lin-xin 9 роки тому
коміт
1dea6811e8
48 змінених файлів з 3051 додано та 0 видалено
  1. 5 0
      .babelrc
  2. 9 0
      .editorconfig
  3. 5 0
      .gitignore
  4. 24 0
      README.md
  5. 36 0
      build/build.js
  6. 45 0
      build/check-versions.js
  7. 9 0
      build/dev-client.js
  8. 74 0
      build/dev-server.js
  9. 61 0
      build/utils.js
  10. 82 0
      build/webpack.base.conf.js
  11. 34 0
      build/webpack.dev.conf.js
  12. 102 0
      build/webpack.prod.conf.js
  13. 6 0
      config/dev.env.js
  14. 54 0
      config/index.js
  15. 3 0
      config/prod.env.js
  16. 6 0
      config/test.env.js
  17. 27 0
      index.html
  18. 71 0
      package.json
  19. 5 0
      src/App.vue
  20. BIN
      src/assets/logo.png
  21. BIN
      src/assets/mlogo.png
  22. BIN
      src/assets/om-logo.png
  23. BIN
      src/assets/omw.png
  24. 39 0
      src/components/Hello.vue
  25. 231 0
      src/components/admin/addpro.vue
  26. 22 0
      src/components/admin/admin.vue
  27. 228 0
      src/components/admin/edit.vue
  28. 71 0
      src/components/admin/header.vue
  29. 271 0
      src/components/admin/info.vue
  30. 126 0
      src/components/admin/list.vue
  31. 164 0
      src/components/admin/login.vue
  32. 190 0
      src/components/admin/regist.vue
  33. 64 0
      src/components/admin/sidebar.vue
  34. 20 0
      src/components/index/footer.vue
  35. 69 0
      src/components/index/header.vue
  36. 30 0
      src/components/index/index.vue
  37. 73 0
      src/components/index/info.vue
  38. 41 0
      src/main.js
  39. 0 0
      static/.gitkeep
  40. 133 0
      static/css/admin.css
  41. 43 0
      static/css/common.css
  42. 7 0
      static/css/index.css
  43. 461 0
      static/css/normalize.css
  44. 0 0
      static/css/normalize.min.css
  45. 9 0
      test/unit/.eslintrc
  46. 13 0
      test/unit/index.js
  47. 75 0
      test/unit/karma.conf.js
  48. 13 0
      test/unit/specs/Hello.spec.js

+ 5 - 0
.babelrc

@@ -0,0 +1,5 @@
+{
+  "presets": ["es2015", "stage-2"],
+  "plugins": ["transform-runtime"],
+  "comments": false
+}

+ 9 - 0
.editorconfig

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

+ 5 - 0
.gitignore

@@ -0,0 +1,5 @@
+.DS_Store
+node_modules/
+dist/
+npm-debug.log
+test/unit/coverage

+ 24 - 0
README.md

@@ -0,0 +1,24 @@
+# h5works
+
+> 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
+
+# run unit tests
+npm run unit
+
+# run all tests
+npm test
+```
+
+For detailed explanation on how things work, checkout the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).

+ 36 - 0
build/build.js

@@ -0,0 +1,36 @@
+// https://github.com/shelljs/shelljs
+require('./check-versions')()
+require('shelljs/global')
+env.NODE_ENV = 'production'
+
+var path = require('path')
+var config = require('../config')
+var ora = require('ora')
+var webpack = require('webpack')
+var webpackConfig = require('./webpack.prod.conf')
+
+console.log(
+  '  Tip:\n' +
+  '  Built files are meant to be served over an HTTP server.\n' +
+  '  Opening index.html over file:// won\'t work.\n'
+)
+
+var spinner = ora('building for production...')
+spinner.start()
+
+var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory)
+rm('-rf', assetsPath)
+mkdir('-p', assetsPath)
+cp('-R', 'static/*', assetsPath)
+
+webpack(webpackConfig, function (err, stats) {
+  spinner.stop()
+  if (err) throw err
+  process.stdout.write(stats.toString({
+    colors: true,
+    modules: false,
+    children: false,
+    chunks: false,
+    chunkModules: false
+  }) + '\n')
+})

+ 45 - 0
build/check-versions.js

@@ -0,0 +1,45 @@
+var semver = require('semver')
+var chalk = require('chalk')
+var packageConfig = require('../package.json')
+var exec = function (cmd) {
+  return require('child_process')
+    .execSync(cmd).toString().trim()
+}
+
+var versionRequirements = [
+  {
+    name: 'node',
+    currentVersion: semver.clean(process.version),
+    versionRequirement: packageConfig.engines.node
+  },
+  {
+    name: 'npm',
+    currentVersion: exec('npm --version'),
+    versionRequirement: packageConfig.engines.npm
+  }
+]
+
+module.exports = function () {
+  var warnings = []
+  for (var i = 0; i < versionRequirements.length; i++) {
+    var 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 (var i = 0; i < warnings.length; i++) {
+      var warning = warnings[i]
+      console.log('  ' + warning)
+    }
+    console.log()
+    process.exit(1)
+  }
+}

+ 9 - 0
build/dev-client.js

@@ -0,0 +1,9 @@
+/* eslint-disable */
+require('eventsource-polyfill')
+var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
+
+hotClient.subscribe(function (event) {
+  if (event.action === 'reload') {
+    window.location.reload()
+  }
+})

+ 74 - 0
build/dev-server.js

@@ -0,0 +1,74 @@
+require('./check-versions')()
+var config = require('../config')
+if (!process.env.NODE_ENV) process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
+var path = require('path')
+var express = require('express')
+var webpack = require('webpack')
+var opn = require('opn')
+var proxyMiddleware = require('http-proxy-middleware')
+var webpackConfig = process.env.NODE_ENV === 'testing'
+  ? require('./webpack.prod.conf')
+  : require('./webpack.dev.conf')
+
+// default port where dev server listens for incoming traffic
+var port = process.env.PORT || config.dev.port
+// Define HTTP proxies to your custom API backend
+// https://github.com/chimurai/http-proxy-middleware
+var proxyTable = config.dev.proxyTable
+
+var app = express()
+var compiler = webpack(webpackConfig)
+
+var devMiddleware = require('webpack-dev-middleware')(compiler, {
+  publicPath: webpackConfig.output.publicPath,
+  stats: {
+    colors: true,
+    chunks: false
+  }
+})
+
+var hotMiddleware = require('webpack-hot-middleware')(compiler)
+// force page reload when html-webpack-plugin template changes
+compiler.plugin('compilation', function (compilation) {
+  compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
+    hotMiddleware.publish({ action: 'reload' })
+    cb()
+  })
+})
+
+// proxy api requests
+Object.keys(proxyTable).forEach(function (context) {
+  var options = proxyTable[context]
+  if (typeof options === 'string') {
+    options = { target: options }
+  }
+  app.use(proxyMiddleware(context, options))
+})
+
+// handle fallback for HTML5 history API
+app.use(require('connect-history-api-fallback')())
+
+// serve webpack bundle output
+app.use(devMiddleware)
+
+// enable hot-reload and state-preserving
+// compilation error display
+app.use(hotMiddleware)
+
+// serve pure static assets
+var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
+app.use(staticPath, express.static('./static'))
+
+module.exports = app.listen(port, function (err) {
+  if (err) {
+    console.log(err)
+    return
+  }
+  var uri = 'http://localhost:' + port
+  console.log('Listening at ' + uri + '\n')
+
+  // when env is testing, don't need open it
+  if (process.env.NODE_ENV !== 'testing') {
+    opn(uri)
+  }
+})

+ 61 - 0
build/utils.js

@@ -0,0 +1,61 @@
+var path = require('path')
+var config = require('../config')
+var ExtractTextPlugin = require('extract-text-webpack-plugin')
+
+exports.assetsPath = function (_path) {
+  var assetsSubDirectory = process.env.NODE_ENV === 'production'
+    ? config.build.assetsSubDirectory
+    : config.dev.assetsSubDirectory
+  return path.posix.join(assetsSubDirectory, _path)
+}
+
+exports.cssLoaders = function (options) {
+  options = options || {}
+  // generate loader string to be used with extract text plugin
+  function generateLoaders (loaders) {
+    var sourceLoader = loaders.map(function (loader) {
+      var extraParamChar
+      if (/\?/.test(loader)) {
+        loader = loader.replace(/\?/, '-loader?')
+        extraParamChar = '&'
+      } else {
+        loader = loader + '-loader'
+        extraParamChar = '?'
+      }
+      return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '')
+    }).join('!')
+
+    // Extract CSS when that option is specified
+    // (which is the case during production build)
+    if (options.extract) {
+      return ExtractTextPlugin.extract('vue-style-loader', sourceLoader)
+    } else {
+      return ['vue-style-loader', sourceLoader].join('!')
+    }
+  }
+
+  // http://vuejs.github.io/vue-loader/en/configurations/extract-css.html
+  return {
+    css: generateLoaders(['css']),
+    postcss: generateLoaders(['css']),
+    less: generateLoaders(['css', 'less']),
+    sass: generateLoaders(['css', 'sass?indentedSyntax']),
+    scss: generateLoaders(['css', 'sass']),
+    stylus: generateLoaders(['css', 'stylus']),
+    styl: generateLoaders(['css', 'stylus'])
+  }
+}
+
+// Generate loaders for standalone style files (outside of .vue)
+exports.styleLoaders = function (options) {
+  var output = []
+  var loaders = exports.cssLoaders(options)
+  for (var extension in loaders) {
+    var loader = loaders[extension]
+    output.push({
+      test: new RegExp('\\.' + extension + '$'),
+      loader: loader
+    })
+  }
+  return output
+}

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

@@ -0,0 +1,82 @@
+var path = require('path')
+var config = require('../config')
+var utils = require('./utils')
+var projectRoot = path.resolve(__dirname, '../')
+
+var env = process.env.NODE_ENV
+// check env & config/index.js to decide weither to enable CSS Sourcemaps for the
+// various preprocessor loaders added to vue-loader at the end of this file
+var cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap)
+var cssSourceMapProd = (env === 'production' && config.build.productionSourceMap)
+var useCssSourceMap = cssSourceMapDev || cssSourceMapProd
+
+module.exports = {
+  entry: {
+    app: './src/main.js'
+  },
+  output: {
+    path: config.build.assetsRoot,
+    publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath,
+    filename: '[name].js'
+  },
+  resolve: {
+    extensions: ['', '.js', '.vue'],
+    fallback: [path.join(__dirname, '../node_modules')],
+    alias: {
+      'vue$': 'vue/dist/vue',
+      'src': path.resolve(__dirname, '../src'),
+      'assets': path.resolve(__dirname, '../src/assets'),
+      'components': path.resolve(__dirname, '../src/components'),
+      'static': path.resolve(__dirname, '../static')
+    }
+  },
+  resolveLoader: {
+    fallback: [path.join(__dirname, '../node_modules')]
+  },
+  module: {
+    loaders: [
+      {
+        test: require.resolve('jquery'),
+        loader: 'expose?jQuery!expose?$'
+      },
+      {
+        test: /\.vue$/,
+        loader: 'vue'
+      },
+      {
+        test: /\.js$/,
+        loader: 'babel',
+        include: projectRoot,
+        exclude: /node_modules/
+      },
+      {
+        test: /\.json$/,
+        loader: 'json'
+      },
+      {
+        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
+        loader: 'url',
+        query: {
+          limit: 10000,
+          name: utils.assetsPath('img/[name].[hash:7].[ext]')
+        }
+      },
+      {
+        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
+        loader: 'url',
+        query: {
+          limit: 10000,
+          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
+        }
+      }
+    ]
+  },
+  vue: {
+    loaders: utils.cssLoaders({ sourceMap: useCssSourceMap }),
+    postcss: [
+      require('autoprefixer')({
+        browsers: ['last 2 versions']
+      })
+    ]
+  }
+}

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

@@ -0,0 +1,34 @@
+var config = require('../config')
+var webpack = require('webpack')
+var merge = require('webpack-merge')
+var utils = require('./utils')
+var baseWebpackConfig = require('./webpack.base.conf')
+var HtmlWebpackPlugin = require('html-webpack-plugin')
+
+// add hot-reload related code to entry chunks
+Object.keys(baseWebpackConfig.entry).forEach(function (name) {
+  baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
+})
+
+module.exports = merge(baseWebpackConfig, {
+  module: {
+    loaders: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
+  },
+  // eval-source-map is faster for development
+  devtool: '#eval-source-map',
+  plugins: [
+    new webpack.DefinePlugin({
+      'process.env': config.dev.env
+    }),
+    // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
+    new webpack.optimize.OccurenceOrderPlugin(),
+    new webpack.HotModuleReplacementPlugin(),
+    new webpack.NoErrorsPlugin(),
+    // https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: 'index.html',
+      template: 'index.html',
+      inject: true
+    })
+  ]
+})

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

@@ -0,0 +1,102 @@
+var path = require('path')
+var config = require('../config')
+var utils = require('./utils')
+var webpack = require('webpack')
+var merge = require('webpack-merge')
+var baseWebpackConfig = require('./webpack.base.conf')
+var ExtractTextPlugin = require('extract-text-webpack-plugin')
+var HtmlWebpackPlugin = require('html-webpack-plugin')
+var env = process.env.NODE_ENV === 'testing'
+  ? require('../config/test.env')
+  : config.build.env
+
+var webpackConfig = merge(baseWebpackConfig, {
+  module: {
+    loaders: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true })
+  },
+  devtool: config.build.productionSourceMap ? '#source-map' : false,
+  output: {
+    path: config.build.assetsRoot,
+    filename: utils.assetsPath('js/[name].[chunkhash].js'),
+    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
+  },
+  vue: {
+    loaders: utils.cssLoaders({
+      sourceMap: config.build.productionSourceMap,
+      extract: true
+    })
+  },
+  plugins: [
+    // http://vuejs.github.io/vue-loader/en/workflow/production.html
+    new webpack.DefinePlugin({
+      'process.env': env
+    }),
+    new webpack.optimize.UglifyJsPlugin({
+      compress: {
+        warnings: false
+      }
+    }),
+    new webpack.optimize.OccurenceOrderPlugin(),
+    // extract css into its own file
+    new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')),
+    // generate dist index.html with correct asset hash for caching.
+    // you can customize output by editing /index.html
+    // see https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: process.env.NODE_ENV === 'testing'
+        ? 'index.html'
+        : config.build.index,
+      template: 'index.html',
+      inject: true,
+      minify: {
+        removeComments: true,
+        collapseWhitespace: true,
+        removeAttributeQuotes: true
+        // more options:
+        // https://github.com/kangax/html-minifier#options-quick-reference
+      },
+      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
+      chunksSortMode: 'dependency'
+    }),
+    // split vendor js into its own file
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'vendor',
+      minChunks: function (module, count) {
+        // 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',
+      chunks: ['vendor']
+    })
+  ]
+})
+
+if (config.build.productionGzip) {
+  var 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
+    })
+  )
+}
+
+module.exports = webpackConfig

+ 6 - 0
config/dev.env.js

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

+ 54 - 0
config/index.js

@@ -0,0 +1,54 @@
+// see http://vuejs-templates.github.io/webpack for documentation.
+var path = require('path')
+var domain = 'http://www.api.com';
+// var domain = 'http://api.omwteam.com/api';
+module.exports = {
+  build: {
+    env: require('./prod.env'),
+    index: path.resolve(__dirname, '../dist/index.html'),
+    assetsRoot: path.resolve(__dirname, '../dist'),
+    assetsSubDirectory: 'static',
+    assetsPublicPath: '/',
+    productionSourceMap: true,
+    // 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']
+  },
+  dev: {
+    env: require('./dev.env'),
+    port: 8080,
+    assetsSubDirectory: 'static',
+    assetsPublicPath: '/',
+    proxyTable: {
+      '/info':{
+        target: domain,
+        changeOrigin:true,
+      },
+      '/user':{
+        target: domain,
+        changeOrigin:true,
+      },
+      '/menu':{
+        target: domain,
+        changeOrigin:true,
+      },
+      '/demand':{
+        target: domain,
+        changeOrigin:true,
+      },
+      '/upload':{
+        target: domain,
+        changeOrigin:true,
+      }
+    },
+    // CSS Sourcemaps off by default because relative paths are "buggy"
+    // with this option, according to the CSS-Loader README
+    // (https://github.com/webpack/css-loader#sourcemaps)
+    // In our experience, they generally work as expected,
+    // just be aware of this issue when enabling this option.
+    cssSourceMap: false
+  }
+}

+ 3 - 0
config/prod.env.js

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

+ 6 - 0
config/test.env.js

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

+ 27 - 0
index.html

@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
+    <title>h5works</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script>
+      /**
+       * 获取当前环境,返回请求的域名
+       */
+      function getENV() {
+        var url = window.location.href;
+        if (url.indexOf("localhost") > 0) {
+          return "";
+        } else if (url.indexOf("omwteam.") > 0) {
+          return "http://api.omwteam.com";
+        } else {
+          return "";
+        }
+      }
+      let server = getENV();
+    </script>
+  </body>
+</html>

+ 71 - 0
package.json

@@ -0,0 +1,71 @@
+{
+  "name": "h5works",
+  "version": "1.0.0",
+  "description": "A Vue.js project",
+  "author": "lin-xin <2981207131@qq.com>",
+  "private": true,
+  "scripts": {
+    "dev": "node build/dev-server.js",
+    "build": "node build/build.js",
+    "unit": "karma start test/unit/karma.conf.js --single-run",
+    "test": "npm run unit"
+  },
+  "dependencies": {
+    "vue": "^2.0.1",
+    "jquery": "^3.1.0",
+    "simditor":"^2.3.6"
+  },
+  "devDependencies": {
+    "autoprefixer": "^6.4.0",
+    "babel-core": "^6.0.0",
+    "babel-loader": "^6.0.0",
+    "babel-plugin-transform-runtime": "^6.0.0",
+    "babel-preset-es2015": "^6.0.0",
+    "babel-preset-stage-2": "^6.0.0",
+    "babel-register": "^6.0.0",
+    "chai": "^3.5.0",
+    "chalk": "^1.1.3",
+    "connect-history-api-fallback": "^1.1.0",
+    "css-loader": "^0.25.0",
+    "element-ui": "^1.0.0",
+    "eventsource-polyfill": "^0.9.6",
+    "expose-loader": "^0.7.1",
+    "express": "^4.13.3",
+    "extract-text-webpack-plugin": "^1.0.1",
+    "file-loader": "^0.9.0",
+    "function-bind": "^1.0.2",
+    "html-webpack-plugin": "^2.8.1",
+    "http-proxy-middleware": "^0.17.2",
+    "inject-loader": "^2.0.1",
+    "isparta-loader": "^2.0.0",
+    "json-loader": "^0.5.4",
+    "karma": "^1.3.0",
+    "karma-coverage": "^1.1.1",
+    "karma-mocha": "^1.2.0",
+    "karma-phantomjs-launcher": "^1.0.0",
+    "karma-sinon-chai": "^1.2.0",
+    "karma-sourcemap-loader": "^0.3.7",
+    "karma-spec-reporter": "0.0.26",
+    "karma-webpack": "^1.7.0",
+    "lolex": "^1.4.0",
+    "mocha": "^3.1.0",
+    "opn": "^4.0.2",
+    "ora": "^0.3.0",
+    "semver": "^5.3.0",
+    "shelljs": "^0.7.4",
+    "sinon": "^1.17.3",
+    "sinon-chai": "^2.8.0",
+    "url-loader": "^0.5.7",
+    "vue-loader": "^9.4.0",
+    "vue-resource": "^1.0.3",
+    "vue-style-loader": "^1.0.0",
+    "webpack": "^1.13.2",
+    "webpack-dev-middleware": "^1.8.3",
+    "webpack-hot-middleware": "^2.12.2",
+    "webpack-merge": "^0.14.1"
+  },
+  "engines": {
+    "node": ">= 4.0.0",
+    "npm": ">= 3.0.0"
+  }
+}

+ 5 - 0
src/App.vue

@@ -0,0 +1,5 @@
+<template>
+  <div id="app">
+    <router-view></router-view>
+  </div>
+</template>

BIN
src/assets/logo.png


BIN
src/assets/mlogo.png


BIN
src/assets/om-logo.png


BIN
src/assets/omw.png


+ 39 - 0
src/components/Hello.vue

@@ -0,0 +1,39 @@
+<template>
+  <div class="hello">
+    <h1>{{ msg }}</h1>
+    <h2>Essential Links</h2>
+    <router-link :to="{ path: 'hello' }">hello</router-link>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'hello',
+  data () {
+    return {
+      msg: 'Welcome to Your Vue.js App'
+    }
+  }
+}
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+h1, h2 {
+  font-weight: normal;
+}
+
+ul {
+  list-style-type: none;
+  padding: 0;
+}
+
+li {
+  display: inline-block;
+  margin: 0 10px;
+}
+
+a {
+  color: #42b983;
+}
+</style>

+ 231 - 0
src/components/admin/addpro.vue

@@ -0,0 +1,231 @@
+<template>
+    <div class="content">
+        <div class="widget-box">
+            <div class="widget-title">
+                <h5>H5模块信息</h5>
+            </div>
+            <div class="widget-content">
+                <el-form label-width="90px" ref="ruleForm" :model="ruleForm" :rules="rules" class="demo-ruleForm">
+                    <el-form-item label="项目名称" prop="name">
+                        <el-input placeholder="请输入项目名" v-model="ruleForm.name"></el-input>
+                    </el-form-item>
+                    <el-form-item label="起始时间">
+                        <el-date-picker v-model="ruleForm.start_time"
+                                type="date"
+                                placeholder="开始时间"
+                                style="width:217px">
+                        </el-date-picker>
+                        <span class="line">-</span>
+                        <el-date-picker v-model="ruleForm.uptime"
+                                type="date"
+                                placeholder="结束时间"
+                                style="width:217px">
+                        </el-date-picker>
+                    </el-form-item>
+                    <el-form-item label="所属项目">
+                        <el-select v-model="ruleForm.project">
+                            <el-option :value="p.id" v-for="p in prolist" :label="p.name">{{p.name}}</el-option>
+                        </el-select>
+                    </el-form-item>
+                    <el-form-item label="参与者">
+                        <el-select v-model="ruleForm.engineer">
+                            <el-option :value="u.id" v-for="u in userlist" :label="u.name">{{u.name}}</el-option>
+                        </el-select>
+                    </el-form-item>
+                    <el-form-item label="缩略图">
+                        <img :src="ruleForm.shuticon" class="small-img" v-if="showimg" @click="delShuticon">
+                        <el-upload action="/info/upload" :on-success="handleSuccess" :on-remove="handleRemove">
+                            <el-button size="small" type="primary">点击上传</el-button>
+                            <span class="el-upload__tip" slot="tip">&nbsp;只能上传jpg/png文件,且不超过500kb</span>
+                        </el-upload>
+                    </el-form-item>
+                    <el-form-item label="版本号" prop="version">
+                        <el-input placeholder="请输入版本号" v-model="ruleForm.version"></el-input>
+                    </el-form-item>
+                    <el-form-item label="product" prop="pro">
+                        <el-input placeholder="请输入生产环境地址" v-model="ruleForm.pro"></el-input>
+                    </el-form-item>
+                    <el-form-item label="develop" prop="dev">
+                        <el-input placeholder="请输入开发环境地址" v-model="ruleForm.dev"></el-input>
+                    </el-form-item>
+                    <el-form-item label="module" prop="module">
+                        <el-input placeholder="请输入模块环境地址" v-model="ruleForm.module"></el-input>
+                    </el-form-item>
+                    <el-form-item label="alpha" prop="alpha">
+                        <el-input placeholder="请输入集成环境地址" v-model="ruleForm.alpha"></el-input>
+                    </el-form-item>
+                    <el-form-item label="beta" prop="beta">
+                        <el-input placeholder="请输入灰度环境地址" v-model="ruleForm.beta"></el-input>
+                    </el-form-item>
+                    <el-form-item label="关键技术">
+                        <el-input placeholder="请输入关键技术 用'/'隔开" v-model="ruleForm.technology"></el-input>
+                    </el-form-item>
+                    <el-form-item label="ideas">
+                        <el-input placeholder="请输入ideas" v-model="ruleForm.ideas"></el-input>
+                    </el-form-item>
+                    <el-form-item label="其他备注">
+                        <el-input placeholder="备注" v-model="ruleForm.mark"></el-input>
+                    </el-form-item>
+                    <el-form-item label="">
+                        <el-button type="primary" @click="handleSubmit()">提交</el-button>
+                        <el-button @click="handleReset()">重置</el-button>
+                    </el-form-item>
+                </el-form>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+    export default {
+        data() {
+            return {
+                info_id:'',prolist: [], userlist: [],showimg: false,
+                ruleForm: {
+                    name: '', project: 0, engineer: 0, technology: '', version: '', mark: '', ideas: '',
+                    start_time: new Date(),uptime:new Date(), pro:'', dev:'', module:'', alpha:'', beta:'',shuticon:''
+                },
+                rules: {
+                    name: [{ required: true, message: '请输入项目名', trigger: 'blur' }],
+//                    start_time: [{ type: 'date', required: true, message: '请输入开始时间', trigger: 'change' }],
+//                    uptime: [{ type: 'date', required: true, message: '请输入结束时间', trigger: 'change' }],
+//                    project: [{required: true, message: '请选择所属项目', trigger: 'change' }],
+//                    engineer: [{ required: true, message: '请选择参与者', trigger: 'change' }],
+                    pro: [{ required: true, message: '请输入正式环境地址', trigger: 'blur' }],
+                    dev: [{ required: true, message: '请输入开发环境地址', trigger: 'blur' }],
+                    module: [{ required: true, message: '请输入模块环境地址', trigger: 'blur' }],
+                    alpha: [{ required: true, message: '请输入集成环境地址', trigger: 'blur' }],
+                    beta: [{ required: true, message: '请输入灰度环境地址', trigger: 'blur' }],
+                    version: [{ required: true, message: '请输入版本号', trigger: 'blur' }]
+                }
+            }
+        },
+        methods: {
+            handleSubmit() {
+                this.$refs.ruleForm.validate((valid) => {
+                    if (valid) {
+                        this.addInfo();
+                    } else {
+                        return false;
+                    }
+                });
+            },
+            handleReset() {
+                this.$refs.ruleForm.resetFields();
+            },
+            getInfoById(id) {
+                let self = this;
+                self.$http.get(server+'/info/show?id='+id).then( (res) => {
+                    if(res.ok){
+                        let response = res.data,info,urlobj;
+                        if(response.code=='200'){
+                            info = response.result[0];
+                            urlobj = JSON.parse(info.env_url)
+                            self.ruleForm = {
+                                name : info.name,
+                                project : info.project,
+                                engineer : info.engineer,
+                                start_time : new Date(parseInt(info.start_time)),
+                                uptime : new Date(parseInt(info.uptime)),
+                                shuticon : info.shuticon,
+                                technology : info.technology,
+                                ideas  : info.ideas ,
+                                version : info.version,
+                                mark : info.mark,
+                                pro : urlobj.pro,
+                                dev: urlobj.dev,
+                                module: urlobj.module,
+                                alpha: urlobj.alpha,
+                                beta: urlobj.beta
+                            }
+                            if(self.ruleForm.shuticon){ self.showimg = true; }
+                        }
+                    }
+                })
+            },
+            getOnceMenu() {
+                let self = this;
+                self.$http.get(server+'/menu/get_list').then( (res) => {
+                    if(res.ok){
+                        let response = res.data;
+                        if(response.code=='200'){
+                            self.prolist = response.result.list;
+                            if(!self.info_id){self.ruleForm.project = self.prolist[0].id;}
+                        }
+                    }
+                })
+            },
+            addInfo() {
+                let self = this, params, url = '/info/add';
+                params = self.ruleForm;
+                params.start_time = Date.parse(params.start_time);
+                params.uptime = Date.parse(params.uptime);
+                params.env_url = {
+                    pro : params.pro, dev : params.dev, module: params.module, alpha: params.alpha, beta: params.beta
+                };
+                params.env_url = JSON.stringify(params.env_url)
+                delete params.pro; delete params.dev; delete params.module; delete params.alpha; delete params.beta;
+                if(self.info_id){
+                    params.id = self.info_id;
+                    url = '/info/update';
+                }
+                console.log(params)
+                self.$http.post(server + url, params).then( (res) => {
+                    if (res.ok) {
+                        let response = res.data;
+                        if (response.code == '200') {
+                            self.$message.success(response.msg);
+                        } else {
+                            self.$message.error(response.msg);
+                        }
+                    }else {
+                        self.$message.error('网络出错');
+                    }
+                })
+            },
+            getUserName() {
+                let self = this;
+                self.$http.get(server+'/user/get_list').then( (res) => {
+                    if(res.ok){
+                        let response = res.data;
+                        if(response.code=='200'){
+                            self.userlist = response.result;
+                            if(!self.info_id){self.ruleForm.engineer = self.userlist[0].id;}
+                        }
+                    }
+                })
+            },
+            handleSuccess(file){
+                this.ruleForm.shuticon = file.msg;
+                this.showimg = true;
+            },
+            delShuticon() {
+                this.showimg = false;
+                this.ruleForm.shuticon = '';
+            }
+        },
+        created() {
+            this.info_id = this.$route.query.id;
+            this.getOnceMenu();
+            this.getUserName();
+            this.info_id && this.getInfoById(this.info_id);
+        }
+    }
+</script>
+<style scoped>
+    .el-input{
+        width:500px;
+    }
+    .line {
+        text-align: center;
+    }
+    label{
+        width: 70px;
+        display: inline-block;
+        font-size:14px;
+    }
+    .small-img{
+        width:50px;
+        height:50px;
+        vertical-align: middle;
+    }
+</style>

+ 22 - 0
src/components/admin/admin.vue

@@ -0,0 +1,22 @@
+<template>
+    <div class="wrapper">
+        <v-head></v-head>
+        <v-sidebar></v-sidebar>
+        <router-view></router-view>
+    </div>
+</template>
+
+<script>
+    import header from './header.vue';
+    import sidebar from './sidebar.vue';
+    export default {
+        components:{
+            'vHead':header,
+            'vSidebar':sidebar
+        }
+    }
+
+</script>
+<style>
+    @import "../../../static/css/admin.css";
+</style>

+ 228 - 0
src/components/admin/edit.vue

@@ -0,0 +1,228 @@
+<template>
+    <div class="content">
+        <div class="widget-box">
+            <div class="widget-title">
+                <h5>新增菜单</h5>
+            </div>
+            <div class="widget-content nopadding">
+                <el-select v-model="select_type" style="width: 120px;">
+                    <el-option label="一级菜单" value="1">一级菜单</el-option>
+                    <el-option label="二级菜单" value="2">二级菜单</el-option>
+                </el-select>
+                <el-select v-if="isShow" v-model="select_id" style="width: 120px;">
+                    <el-option :value="m.id" v-for="m in menu" :label="m.name">{{m.name}}</el-option>
+                </el-select>
+                <el-input placeholder="菜单名称" v-model="menu_name" style="width:200px"></el-input>
+                <el-button type="primary" @click="addMenu()">提交</el-button>
+            </div>
+        </div>
+        <div class="widget-box">
+            <div class="widget-title">
+                <h5>新增内容</h5>
+            </div>
+            <div class="widget-content">
+                <el-form label-width="70px" class="demo-ruleForm" v-show="!isedit">
+                    <el-form-item label="所属项目">
+                        <el-select v-model="add_once" style="width: 150px;">
+                            <el-option v-for="(m,i) in first_menu" :value="m.id" :label="m.name">{{m.name}}</el-option>
+                        </el-select>
+                        <span class="line"> - </span>
+                        <el-select v-model="add_second" style="width: 150px;">
+                            <el-option v-for="m in second_menu" :value="m.id" :label="m.name">{{m.name}}</el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-form>
+                <el-form label-width="70px" class="demo-ruleForm" v-show="isedit">
+                    <el-form-item label="所属项目">
+                        <el-input v-model="name" style="width:100%" :disabled="true"></el-input>
+                    </el-form-item>
+                </el-form>
+                <el-input class="mgb20" placeholder="请输入标题" v-model="title" style="width:100%"></el-input>
+                <textarea id="editor" placeholder="请输入详细内容" autofocus v-model="content"></textarea>
+                <el-button type="primary" @click="clickEvent()" style="margin-top: 20px">提交</el-button>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+    import $ from 'expose?$!jquery';
+    import Simditor from 'simditor';
+    export default {
+        data () {
+            return {
+                first_menu:'', second_menu:'', add_once: '', add_second: '', title : '', content : '', menu_name : '',
+                isShow : false, menu : [], select_type : '1', select_id : '', isedit : false, param_id :'', name:''
+            }
+        },
+        created () {
+            this.getOnceMenu();
+            self.param_id = this.$route.query.id;
+            if(self.param_id){
+                this.getCnt(self.param_id);
+
+            }
+        },
+        mounted () {
+            let editor = new Simditor({
+                textarea: $('#editor')
+            });
+        },
+        watch:{
+            'select_type' : function () {
+                if(this.select_type == "2"){
+                    this.isShow = true;
+                }else{
+                    this.isShow = false;
+                }
+            },
+            'add_once' : function(){
+                this.getSubMenuByPid(this.add_once)
+            }
+        },
+        methods: {
+            addMenu() {
+                let self = this,param = {
+                    name:self.menu_name,
+                    type:self.select_type
+                };
+                if(param.type == '2'){ param.p_id = self.select_id; }
+                if(self.menu_name.length){
+                    self.$http.post(server+'/menu/add',param).then( (res) => {
+                        if(res.ok){
+                            let response = res.data;
+                            console.log(response);
+                            if(response.code=='200'){
+                                self.$message.success('提交成功');
+                            }else{
+                                self.$message.error('提交失败');
+                            }
+                        }else {
+                            self.$message.error('网络出错');
+                        }
+                    })
+                }else{
+                    self.$message.warning('请输入菜单名');
+                }
+            },
+            getOnceMenu() {
+                let self = this;
+                self.$http.get(server+'/menu/get_list').then((res) => {
+                    if(res.ok){
+                        let response = res.data;
+                        if(response.code=='200'){
+                            self.menu = self.first_menu = response.result.list;
+                            self.select_id = self.add_once = self.first_menu[0].id;
+                        }
+                    }else {
+                        self.$message.error('获取一级菜单失败');
+                    }
+                })
+            },
+            getSubMenuByPid(pid) {
+                let self = this;
+                self.$http.get(server+'/menu/get_list?pid='+ pid).then((res) => {
+                    if(res.ok){
+                        let response = res.data;
+                        if(response.code=='200'){
+                            self.second_menu = response.result.list;
+                            if(self.second_menu.length){
+                                self.add_second = self.second_menu[0].id;
+                            }else{
+                                self.add_second = '';
+                            }
+                        }
+                    }else {
+                        self.$message.error('获取二级菜单失败');
+                    }
+                })
+            },
+            clickEvent() {
+                if(self.param_id){
+                    this.editCnt(self.param_id)
+                }else{
+                    this.addCnt();
+                }
+            },
+            addCnt() {
+                let self = this,
+                    html = document.querySelector('.simditor-body').innerHTML,
+                    param = {};
+                self.content = (html == '<p><br></p>')?'':html;
+                param = {m_id:self.add_second,title:self.title,content:self.content}
+                if(param.title.length && param.content.length) {
+                    self.$http.post(server+'/demand/add', param).then( (res) => {
+                        if(res.ok){
+                            let response = res.data;
+                            if(response.code=='200'){
+                                self.$message.success('提交成功');
+                                self.title = self.content = '';
+                                document.querySelector('.simditor-body').innerHTML = '';
+                            }else{
+                                self.$message.error('提交失败');
+                            }
+                        }else {
+                            self.$message.error('网络出错');
+                        }
+                    })
+                }else {
+                    self.$message.warning('标题或详细内容不得为空');
+                }
+            },
+            editCnt(id) {
+                let self = this,
+                    html = document.querySelector('.simditor-body').innerHTML,
+                    param = {};
+                self.content = (html == '<p><br></p>')?'':html;
+                param = {id: id,title:self.title,content:self.content}
+                if(self.title.length && self.content.length) {
+                    self.$http.post(server+'/demand/update', param).then( (res) => {
+                        if (res.ok) {
+                            let response = res.data;
+                            if (response.code == '200') {
+                                self.$message.success('修改成功');
+                                self.title = self.content = '';
+                                document.querySelector('.simditor-body').innerHTML = '';
+                            } else {
+                                self.$message.error('修改失败');
+                            }
+                        }else {
+                            self.$message.error('网络出错');
+                        }
+                    })
+                }else {
+                    self.$message.warning('标题和详细内容不得为空');
+                }
+            },
+            getCnt(id) {
+                let self = this;
+                self.$http.get(server+'/demand/show?id='+id).then( (res) => {
+                    if(res.ok){
+                        let response = res.data;
+                        if(response.code=='200'){
+                            self.getMenuName(response.result[0].m_id)
+                            self.title = response.result[0].title;
+                            self.content = response.result[0].content;
+                            document.querySelector('.simditor-placeholder').style.display = 'none';
+                            document.querySelector('.simditor-body').innerHTML = self.content;
+                            self.isedit = true;
+                        }
+                    }else {
+                        self.$message.error('网络出错');
+                    }
+                })
+            },
+            getMenuName(id) {
+                let self = this;
+                self.$http.get(server+'/menu/show?id='+id).then((res) => {
+                    if(res.ok){
+                        let response = res.data;
+                        if(response.code=='200'){
+                            self.name = response.result[0].name;
+                        }
+                    }
+                })
+            }
+        }
+    }
+</script>
+<style src="simditor/styles/simditor.css"></style>

+ 71 - 0
src/components/admin/header.vue

@@ -0,0 +1,71 @@
+<template>
+    <div class="header">
+        H5团队信息管理系统
+        <div class="m-logbar">
+            <span class="logInfo" admin="admin">{{sName}}</span>您好 |
+            <span class="logout" @click="handleLogout">退出</span>
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    data() {
+       return {
+        sName: 'abcd'
+       }
+    },
+    created() {
+       let arr = document.cookie.split('; ');
+       let bArr = arr[arr.length - 1];
+       let sArr = bArr.split('=');
+       let sUserName = sArr[0];
+       this.sName = sUserName;
+    },
+    methods: {
+        handleLogout() {
+            let self = this,param = {
+                username: 'admin',
+                password: 'admin'
+            }
+            self.$http.post(server + '/user/logout',param).then((res) => {
+                if(res.ok){
+                    let response = res.data;
+                    if(response.code == '200') {
+                        location.href = '#/login';
+                    }else {
+                        location.href = '#/login';
+                    }
+                }
+            })
+        },
+        removeCookie(name) {
+            setCookie(name, '1', -1);
+        }
+    }
+}
+</script>
+<style scoped>
+.header {
+    position: relative;
+    -webkit-box-sizing: border-box;
+       -moz-box-sizing: border-box;
+            box-sizing: border-box;
+    padding-left: 50px;
+    width: 100%;
+    height: 70px;
+    font-size: 22px;
+    line-height: 70px;
+    color: #fff;
+    background-color: #242f42;
+}
+.m-logbar {
+    float: right;
+    padding-right: 20px;
+    font-size: 16px;
+    color: #fff;
+}
+.m-logbar .logout {
+    text-decoration: underline;
+}
+</style>

+ 271 - 0
src/components/admin/info.vue

@@ -0,0 +1,271 @@
+<template>
+    <div class="content">
+        <div class="widget-box">
+            <div class="widget-title">
+                <h5>H5模块信息</h5>
+            </div>
+            <div class="widget-content">
+                <table class="el-table el-table--fit el-table--striped el-table--border">
+                    <thead>
+                    <tr>
+                        <th width="50">#</th>
+                        <th>项目名</th>
+                        <th>所属项目</th>
+                        <th width="150">参与者</th>
+                        <th width="150">版本</th>
+                        <th width="150">操作</th>
+                    </tr>
+                    </thead>
+                    <tbody>
+                    <tr v-for="(item,index) in list">
+                        <td>{{(page-1)*pagesize+index+1}}</td>
+                        <td>{{item.name}}</td>
+                        <td>{{getNameById(item.project,pro_list)}}</td>
+                        <td>{{getNameById(item.engineer,user_list)}}</td>
+                        <td>{{item.version}}</td>
+                        <td>
+                            <el-button size="mini" type="info" @click="viewDetail(index)">详情</el-button>
+                            <router-link :to="{path:'addProject',query:{id:item.id}}">
+                                <el-button size="mini" type="warning">编辑</el-button>
+                            </router-link>
+                            <el-tooltip class="item" effect="dark" content="删除不可恢复" placement="top">
+                                <el-button size="mini" type="danger" @click="delInfo(item.id, index)">删除</el-button>
+                            </el-tooltip>
+                        </td>
+                    </tr>
+                    </tbody>
+                </table>
+                <div class="page-box">
+                    <el-pagination
+                            @current-change="handleCurrentChange"
+                            :current-page="1"
+                            :page-size="pagesize"
+                            layout="total, prev, pager, next"
+                            :total="total">
+                    </el-pagination>
+                </div>
+            </div>
+
+            <div class="dialog" :class="{'dialog-show':dialog}">
+                <div class="dialog-wrapper">
+                    <div class="dialog-header">
+                        <span class="dialog-header-title">信息详情</span>
+                        <span class="dialog-close" @click="closeDialog()"><i class="el-icon-close"></i></span>
+                    </div>
+                    <div class="dialog-content">
+                        <div class="mgb5 div-cell"><label>项目名</label><span>{{detail.name}}</span></div>
+                        <div class="mgb5 div-cell"><label>版本号</label><span>{{detail.version}}</span></div>
+                        <div class="mgb5 div-cell"><label>所属项目</label><span>{{getNameById(detail.project,pro_list)}}</span></div>
+                        <div class="mgb5 div-cell"><label>参与者</label><span>{{getNameById(detail.engineer,user_list)}}</span></div>
+                        <div class="mgb5 div-cell"><label>开始时间</label><span>{{getTime(detail.start_time)}}</span></div>
+                        <div class="mgb5 div-cell"><label>结束时间</label><span>{{getTime(detail.uptime)}}</span></div>
+                        <div class="mgb5" v-if="detail.technology"><label>所用技术</label>
+                            <el-tag type="success" v-for="item in detail.technology.split('/')">{{item}}</el-tag>
+                        </div>
+                        <div class="mgb5" v-if="detail.ideas"><label>ideas </label><span>{{detail.ideas}}</span></div>
+                        <div class="mgb5" v-if="detail.shuticon"><label>缩略图</label><span class="small-img">
+                            <img :src="detail.shuticon" title="查看大图" @click="viewBigImg(detail.shuticon)"/></span></div>
+                        <div class="mgb5" v-if="detail.env_url.pro"><label>product</label><span>{{detail.env_url.pro}}</span></div>
+                        <div class="mgb5" v-if="detail.env_url.dev"><label>develop</label><span>{{detail.env_url.dev}}</span></div>
+                        <div class="mgb5" v-if="detail.env_url.module"><label>module</label><span>{{detail.env_url.module}}</span></div>
+                        <div class="mgb5" v-if="detail.env_url.alpha"><label>alpha</label><span>{{detail.env_url.alpha}}</span></div>
+                        <div class="mgb5" v-if="detail.env_url.beta"><label>beta</label><span>{{detail.env_url.beta}}</span></div>
+                        <div class="mgb5" v-if="detail.mark"><label>备注</label><span>{{detail.mark}}</span></div>
+                        <div class="clearfix"></div>
+                        <div class="big-img" v-show="showbig">
+                            <img ref="bigimg" @click="showbig = false" title="关闭大图">
+                            <span></span>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+    export default {
+        data(){
+            return {
+                list:[],user_list:[],pro_list:[],detail:{env_url:{}},dialog:false,page:1,pagesize: 15,total:0,showbig:false
+            }
+        },
+        methods:{
+            handleCurrentChange(val) {
+                this.page = val;
+                this.getInfoList();
+            },
+            viewDetail(index){
+                this.detail = {
+                    name : this.list[index].name,
+                    start_time : this.list[index].start_time,
+                    uptime : this.list[index].uptime,
+                    project : this.list[index].project,
+                    engineer : this.list[index].engineer,
+                    shuticon  : this.list[index].shuticon ,
+                    env_url  : eval("("+this.list[index].env_url+")"),
+                    technology  : this.list[index].technology ,
+                    ideas   : this.list[index].ideas  ,
+                    version   : this.list[index].version  ,
+                    mark   : this.list[index].mark  ,
+                }
+                this.openDialog();
+            },
+            getNameById(id,list){
+                let i, len = list.length;
+                for(i = 0; i < len; i++){
+                    if(list[i].id == id){
+                        return list[i].name;
+                    }
+                }
+            },
+            getInfoList(){
+                let self = this;
+                self.$http.get(server+'/info/index?page='+self.page+'&pagesize='+self.pagesize).then( (res) => {
+                    if(res.ok){
+                        let response = res.data;
+                        if(response.code=='200'){
+                            self.list = response.result.list;
+                            self.total = response.result.total;
+                        }else {
+                            self.$message.error('加载失败');
+                        }
+                    }else {
+                        self.$message.error('网络出错');
+                    }
+                })
+            },
+            getOnceMenu(){
+                let self = this;
+                self.$http.get(server+'/menu/get_list').then( (res) => {
+                    if(res.ok){
+                        let response = res.data;
+                        if(response.code=='200'){
+                            self.pro_list = response.result.list;
+//                            callback && callback()
+                        }
+                    }
+                })
+            },
+            delInfo(id, index) {
+                let self = this;
+                self.$http.post(server+'/info/del',{id:id}).then( (res) => {
+                    if(res.ok){
+                        let response = res.data;
+                        if(response.code=='200'){
+                            self.$message.success('删除成功');
+                            self.list.splice(index,1)
+                        }else{
+                            self.$message.error('删除失败');
+                        }
+                    }
+                })
+            },
+            openDialog(){
+                this.dialog =true;
+            },
+            closeDialog() {
+                this.dialog = false;
+            },
+            getTime(str) {
+                let d = new Date(parseInt(str))
+                return d.getFullYear() + '年' + (d.getMonth()+1) + '月' +d.getDate() + '日';
+            },
+            viewBigImg(url) {
+                this.$refs.bigimg.setAttribute('src',url);
+                this.showbig = true;
+                this.$refs.bigimg.classList.add('showbig')
+            },
+            getUserName() {
+                let self = this;
+                self.$http.get(server+'/user/get_list').then( (res) => {
+                    if(res.ok){
+                        let response = res.data;
+                        if(response.code=='200'){
+                            self.user_list = response.result;
+                        }
+                    }
+                })
+            },
+        },
+        created(){
+            this.getUserName();
+            this.getOnceMenu();
+        }
+    }
+</script>
+<style scoped>
+    .el-tag{
+        margin-right: 10px;
+    }
+    .dialog-content .mgb5{
+        margin-bottom: 10px;
+    }
+    .dialog-content .mgb5 label{
+        display: inline-block;
+        width:80px;
+        line-height: 30px;
+        background: #20A0FF;
+        color: #fff;
+        border-radius: 3px;
+        padding-left: 10px;
+        margin-right: 20px;
+    }
+    .dialog-content .mgb5:nth-child(2n) label,.dialog-content .mgb5:nth-child(5n) label{
+        background: #13CE66;
+    }
+    .dialog-content .mgb5:nth-child(3n) label{
+        background: #F7BA2A;
+    }
+    .dialog-content .mgb5:nth-child(4n) label{
+        background: #FF4949;
+    }
+    .div-cell{
+        display: inline-block;
+        width:50%;
+        float: left;
+    }
+    .clearfix{
+        content: '';
+        display: table;
+        clear: both;
+    }
+    .small-img{
+        display: inline-block;
+        width:50px;
+        height: 50px;
+        overflow: hidden;
+        vertical-align: middle;
+    }
+    .small-img img{
+        width:50px;
+        cursor: pointer;
+    }
+    .big-img{
+        position: absolute;
+        left:0;
+        top:0;
+        width:100%;
+        height:100%;
+        text-align: center;
+        background: rgba(0,0,0,.4);
+    }
+    .big-img img{
+        max-width:100%;
+        vertical-align: middle;
+        cursor: pointer;
+    }
+    .big-img img.showbig{
+        animation: scale .5s ease;
+    }
+    .big-img span{
+        display: inline-block;
+        width:0;
+        height:100%;
+        vertical-align: middle;
+    }
+    @keyframes scale {
+        from{
+            transform: scale(0.1);
+        }
+    }
+</style>

+ 126 - 0
src/components/admin/list.vue

@@ -0,0 +1,126 @@
+<template>
+    <div class="content">
+        <div class="widget-box">
+            <div class="widget-title">
+                <h5>H5模块信息</h5>
+            </div>
+            <div class="widget-content">
+                <table class="el-table el-table--fit el-table--striped el-table--border">
+                    <thead>
+                    <tr>
+                        <th width="50">#</th>
+                        <th width="250">标题</th>
+                        <th>内容</th>
+                        <th width="150">所属项目</th>
+                        <th width="90">状态</th>
+                        <th width="150">操作</th>
+                    </tr>
+                    </thead>
+                    <tbody>
+                    <tr v-for="(value,i) in list">
+                        <td>{{i+1}}</td>
+                        <td>{{value.title}}</td>
+                        <td v-html="value.content"></td>
+                        <td>{{getMenuName(value.m_id)}}</td>
+                        <td v-html="getstatus(value.status)"></td>
+                        <td>
+                            <el-button size="mini" type="info" :data-status="value.status"
+                                       @click="clickFinishBtn(value.id, i)">完成</el-button>
+                            <router-link :to="{path:'taskEdit',query:{id:value.id}}">
+                                <el-button size="mini" type="warning">编辑</el-button>
+                            </router-link>
+                            <el-tooltip class="item" effect="dark" content="删除不可恢复" placement="top">
+                                <el-button size="mini" type="danger" @click="clickDeleteBtn(value.id, i)">删除</el-button>
+                            </el-tooltip>
+                        </td>
+                    </tr>
+                    </tbody>
+                </table>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+export default {
+    data: function () {
+        return {
+            list: [], menu_list: {}
+        }
+    },
+    methods: {
+        getstatus(status) {
+            switch (status){
+                case '0':
+                    return '<span class="el-tag el-tag--primary">未完成</span>';
+                    break;
+                case '1':
+                    return '<span class="el-tag el-tag--success">已完成</span>';
+                    break;
+                case '2':
+                    return '<span class="el-tag">不做了</span>';
+                    break;
+            }
+        },
+        getMenuList(callback) {
+            let self = this;
+            self.$http.get(server+'/menu/index').then((res) => {
+                callback && callback();
+                if(res.ok){
+                    let response = res.data, list, sublist, i, j;
+                    if(response.code=='200'){
+                        list = response.result.list;
+                        for(i=0 ; i<list.length; i++){
+                            if(list[i].subMenu.length){
+                                sublist = list[i].subMenu;
+                                for(j = 0; j<sublist.length; j++){
+                                    self.menu_list[sublist[j].id] = sublist[j].name;
+                                }
+                            }
+                        }
+                    }
+                }else {
+                    self.$message.error('获取一级菜单失败');
+                }
+            })
+        },
+        getMenuName(id) {
+            return this.menu_list[id];
+        },
+        getListItem() {
+            let self = this;
+            self.$http.get(server+"/demand/get_list").then( (res) => {
+                let response = res.data;
+                if (response.code == '200') {
+                    self.list = response.result;
+                }
+            })
+        },
+        clickFinishBtn(id,index) {
+            let status = event.currentTarget.getAttribute('data-status'), self = this ,obj = self.list[index];
+            status = status == '0'? '1': '0';
+            self.$http.post(server+"demand/update", {id: id, status: status}).then( (res) => {
+                let response = res.data;
+                if (response.code == '200') {
+                    status == '0'? self.$message.success('成功标为未完成'): self.$message.success('成功标为已完成');
+                    obj.status = status;
+                }
+            })
+        },
+        clickDeleteBtn(id, index) {
+            let self = this;
+            self.$http.post(server+"/demand/del", {id: id}).then( (res) => {
+                let response = res.data;
+                if (response.code == '200') {
+                    self.$message.success('删除成功');
+                    self.list.splice(index,1)
+                }else{
+                    self.$message.error('删除失败');
+                }
+            })
+        }
+    },
+    created() {
+        this.getMenuList(this.getListItem);
+    }
+}
+</script>

+ 164 - 0
src/components/admin/login.vue

@@ -0,0 +1,164 @@
+<template>
+    <div class="m-form">
+    <div class="login">
+        <el-form :model="ruleForm2" :rules="rules2" ref="ruleForm2" class="demo-ruleForm">
+            <div class="error" v-if="ruleForm2.bHasErrorShow">{{ruleForm2.sErrorTips}}</div>
+            <el-form-item label="用户名:" prop="name">
+                <el-input type="text" v-model="ruleForm2.name" auto-complete="off"></el-input>
+                <span class="el-icon-circle-close close" v-if="ruleForm2.bHasCloseName" @click="clearDataName"></span>
+            </el-form-item>
+            <el-form-item label="密码:" prop="pass">
+                <el-input type="password" v-model="ruleForm2.pass" auto-complete="off"></el-input>
+                <span class="el-icon-circle-close close" v-if="ruleForm2.bHasClosePass" @click="clearDataPass"></span>
+            </el-form-item>
+            <el-form-item>
+                <el-button type="primary" @click="handleSubmit" class="w100">登 陆</el-button>
+                <div class="fr u-line"><a @click="handleRegist">注册新账号</a> | <a>忘记密码?</a> </div>
+            </el-form-item>
+        </el-form>
+    </div>
+    </div>
+</template>
+<script>
+   export default {
+    data() {
+      let validatePass = (rule, value, callback) => {
+        if (value === '') {
+          callback(new Error('请输入用户名'));
+        } else {
+          if (this.ruleForm2.pass !== '') {
+            this.$refs.ruleForm2.validateField('pass');
+          }
+          callback();
+        }
+      };
+      let validatePass2 = (rule, value, callback) => {
+        if (value === '') {
+          callback(new Error('请输入密码'));
+        } else {
+          callback();
+        }
+      };
+      return {
+        ruleForm2: {
+          name: '',
+          pass: '',
+          bHasErrorShow: false,
+          bHasCloseName: false,
+          bHasClosePass: false,
+          sErrorTips: '请输入正确的帐号!'
+        },
+        rules2: {
+          name: [
+            { validator: validatePass, trigger: 'blur' }
+          ],
+          pass: [
+            { validator: validatePass2, trigger: 'blur' }
+          ]
+        }
+      };
+    },
+    watch: {
+        'ruleForm2.name': function() {
+            if(this.ruleForm2.name != ''){
+                this.ruleForm2.bHasCloseName = true;
+            }else{
+                this.ruleForm2.bHasCloseName = false;
+            }
+        },
+        'ruleForm2.pass': function() {
+             if(this.ruleForm2.pass != ''){
+                this.ruleForm2.bHasClosePass = true;
+            }else{
+                this.ruleForm2.bHasClosePass = false;
+            }
+        }
+    },
+    methods: {
+        clearDataName() {
+            this.ruleForm2.name = '';
+          },
+        clearDataPass() {
+            this.ruleForm2.pass = '';
+          },
+        handleSubmit(ev) {
+         let self = this,param = {
+            username: self.ruleForm2.name,
+            password: self.ruleForm2.pass
+        }
+        self.$refs.ruleForm2.validate((valid) => {
+          if (valid) {
+            self.$http.post(server + '/user/login',param).then((res) => {
+                if(res.ok) {
+                    let response = res.data;
+                    if(response.code == '200') {
+                        console.log(response);
+                        let sUsername = response.result.username;
+                        let sUserId = response.result.user_id;
+                        let sExpireTime = 3;
+                        self.setCookie(sUsername,sUserId,sExpireTime);
+                        location.href = '#/admin/';
+                    }else {
+                        self.ruleForm2.bHasErrorShow = true;
+                    }
+                }
+            })
+          } else {
+            console.log('不能为空');
+            return false;
+          }
+        });
+        },
+          setCookie(name, value, iDay) {
+            let oDate = new Date();
+            oDate.setDate(oDate.getDate() + iDay);
+            document.cookie = name + '=' + value + ';expires=' + oDate;
+          },
+          handleRegist() {
+            location.href = '#/regist/';
+          }
+    }
+  }
+</script>
+<style>
+    body {
+        margin: 0;
+        padding: 0;
+    }
+    .m-form {
+        position: absolute;
+        width: 100%;
+        height: 100%;
+        background-color: #555f6e;
+    }
+    .m-form .login {
+        position: relative;
+        top: 30%;
+        margin: 0 auto;
+        border-radius: 8px;
+        padding: 10px 20px 0 20px;
+        width: 16%;
+        background-color: #fff;
+    }
+    .m-form .close {
+        float: right;
+        position: absolute;
+        top: 48px;
+        right: 8px;
+        color: #acb0b7;
+    }
+    .m-form .w100 {
+        width: 100%;
+        background-color: #63738e;
+    }
+    .m-form .error {
+        font-size: 12px;
+        color: red;
+    }
+    .fr {
+        float: right;
+    }
+    .u-line a:hover {
+        text-decoration: underline;
+    }
+</style>

+ 190 - 0
src/components/admin/regist.vue

@@ -0,0 +1,190 @@
+<template>
+    <div class="m-form">
+    <div class="regist" id="regist">
+    <el-form :model="ruleForm2" :rules="rules2" ref="ruleForm2" class="demo-ruleForm">
+            <div class="error" v-if="ruleForm2.bHasErrorShow">{{ruleForm2.sErrorTips}}</div>
+        <el-form-item label="用户名:" prop="username">
+            <el-input v-model.number="ruleForm2.username"></el-input>
+        </el-form-item>
+        <el-form-item label="昵称:" prop="name">
+            <el-input v-model.number="ruleForm2.name"></el-input>
+        </el-form-item>
+        <el-form-item label="密码:" prop="pass">
+            <el-input type="password" v-model="ruleForm2.pass" auto-complete="off"></el-input>
+        </el-form-item>
+        <el-form-item label="确认密码:" prop="checkPass">
+            <el-input type="password" v-model="ruleForm2.checkPass" auto-complete="off"></el-input>
+        </el-form-item>
+        <el-form-item>
+            <el-button type="primary" @click="handleSubmit2" class="w100">立即注册</el-button>
+        </el-form-item>
+    </el-form>
+    </div>
+        <div class="regist-success" style="display: none">
+          <div class="content">
+            <div class="el-icon-check">注册成功</div>
+            <div>账号是:{{ruleForm2.name}}</div>
+            <div id="login" @click="returnLogin">立即登录</div>
+         </div>
+        </div>
+    </div>
+</template>
+<script>
+  export default {
+    data() {
+      let checkUsername = (rule, value, callback) => {
+     if(value === ''){
+           return callback(new Error('用户名不能为空'));
+        }else{
+          callback();
+        }
+      };
+      let checkName = (rule, value, callback) => {
+        if(value === ''){
+           return callback(new Error('昵称不能为空'));
+        }else{
+          callback();
+        }
+      };
+      let validatePass = (rule, value, callback) => {
+        if (value === '') {
+          callback(new Error('请输入密码'));
+        } else {
+          if (this.ruleForm2.checkPass !== '') {
+            this.$refs.ruleForm2.validateField('checkPass');
+          }
+          callback();
+        }
+      };
+      let validatePass2 = (rule, value, callback) => {
+        if (value === '') {
+          callback(new Error('再次输入密码'));
+        } else if (value !== this.ruleForm2.pass) {
+          callback(new Error('两次输入密码不一致!'));
+        } else {
+          callback();
+        }
+      };
+      return {
+        ruleForm2: {
+          pass: '',
+          checkPass: '',
+          bHasErrorShow: false,
+          sErrorTips: '该用户已存在',
+          name: '',
+          username: '',
+        },
+        rules2: {
+          pass: [
+            { validator: validatePass, trigger: 'blur' }
+          ],
+          checkPass: [
+            { validator: validatePass2, trigger: 'blur' }
+          ],
+          name: [
+            { validator: checkName, trigger: 'blur' }
+          ],
+          username: [
+            { validator: checkUsername, trigger: 'blur' }
+          ]
+        }
+      };
+    },
+    methods: {
+    handleReset() {
+        this.$refs.ruleForm2.resetFields();
+      },
+      handleSubmit2(ev) {
+        let self = this,param = {
+            username: self.ruleForm2.username,
+            name: self.ruleForm2.name,
+            password: self.ruleForm2.checkPass
+            }
+        self.$refs.ruleForm2.validate((valid) => {
+          if (valid) {
+            console.log(param);
+            self.$http.post(server + '/user/register',param).then((res) => {
+                if(res.ok) {
+                    let response = res.data;
+                    if(response.code == '200') {
+                        document.querySelector('.regist').style.display = 'none';
+                        document.querySelector('.regist-success').style.display = 'block';
+                    }else {
+                        self.ruleForm2.bHasErrorShow = true;
+                        setTimeout( function(){
+                            self.handleReset();
+                        },1000)
+                    }
+                }
+            })
+          } else {
+            console.log('不能为空');
+            return false;
+          }
+        });
+      },
+      returnLogin() {
+        location.href = '#/login/';
+      }
+    }
+  }
+</script>
+<style>
+    body {
+        margin: 0;
+        padding: 0;
+    }
+    .m-form {
+        position: absolute;
+        width: 100%;
+        height: 100%;
+        background-color: #555f6e;
+    }
+    .m-form .regist {
+        position: relative;
+        top: 20%;
+        margin: 0 auto;
+        border-radius: 8px;
+        padding: 10px 20px 1px 20px;
+        width: 16%;
+        background-color: #fff;
+    }
+    .m-form .w100 {
+        width: 100%;
+        background-color: #63738e;
+    }
+    .m-form .error {
+        font-size: 12px;
+        color: red;
+    }
+    .regist-success {
+        position: absolute;
+        margin: auto;
+        height: 100%;
+        width: 100%;
+        top: 0;
+        bottom: 0;
+        left: 0;
+        right: 0;
+        text-align: center;
+        color: #000;
+        background-color: #fff
+    }
+    .regist-success div {
+        padding: 10px;
+        font-weight: bold;
+        font-size: 20px;
+    }
+    .regist-success .content {
+        position: relative;
+        top: 30%;
+    }
+    .regist-success #login {
+        padding: 6px;
+        margin: 0 auto;
+        font-size: 20px;
+        width: 100px;
+        background: #3499da;
+        color: #fff;
+    }
+</style>

+ 64 - 0
src/components/admin/sidebar.vue

@@ -0,0 +1,64 @@
+<template>
+    <div class="sidebar">
+        <ul class="el-menu el-menu--dark">
+            <li class="el-menu-item">
+                <router-link to="/admin/taskList"><i class="el-icon-upload"></i> 进入前台</router-link>
+            </li>
+            <li class="el-menu-item" :class="{'is-active':isNav('/taskList')}">
+                <router-link to="/admin/taskList"><i class="el-icon-document"></i> 任务列表</router-link>
+            </li>
+            <li class="el-menu-item" :class="{'is-active':isNav('/taskEdit')}">
+                <router-link to="/admin/taskEdit"><i class="el-icon-plus"></i> 添加任务</router-link>
+            </li>
+            <li class="el-menu-item":class="{'is-active':isNav('/moduleInfo')}">
+                <router-link to="/admin/moduleInfo"><i class="el-icon-menu"></i> 项目列表</router-link>
+            </li>
+            <li class="el-menu-item":class="{'is-active':isNav('/addProject')}">
+                <router-link to="/admin/addProject"><i class="el-icon-setting"></i> 添加项目</router-link>
+            </li>
+        </ul>
+    </div>
+</template>
+<script>
+    export default {
+        methods:{
+            isNav: function(str){
+                return this.$route.path.indexOf(str)>-1;
+            }
+        }
+    }
+</script>
+<style scoped>
+    .sidebar{
+        display: block;
+        position: absolute;
+        width: 220px;
+        left: 0;
+        top: 70px;
+        bottom:0;
+        background: #2E363F;
+    }
+    .sidebar > ul {
+        list-style: none;
+        margin: 0 0 0;
+        padding: 0;
+        position: absolute;
+        width: 220px;
+        height:100%;
+    }
+    .sidebar > ul > li{
+        transition: all .5s ease;
+    }
+    .sidebar > ul > li:hover{
+        padding-left: 40px;
+    }
+    .sidebar > ul > li > a {
+        display: block;
+        color: #c0ccda;;
+        cursor: pointer;
+        text-decoration: none;
+    }
+    .sidebar > ul > li.is-active a {
+        color: #20a0ff;
+    }
+</style>

+ 20 - 0
src/components/index/footer.vue

@@ -0,0 +1,20 @@
+<template>
+    <div class="container-fluid footer">
+        <div class="container">
+                <h3>版权归小天才移动web团队</h3>
+        </div>
+    </div>
+</template>
+<script>
+    export default {
+    }
+</script>
+
+<style scoped>
+    .footer {
+        background-color: #324057;
+        color: #a4aebd;
+        height: 120px;
+        line-height: 120px;
+    }
+</style>

+ 69 - 0
src/components/index/header.vue

@@ -0,0 +1,69 @@
+<template>
+    <div class="container-fluid nav-bar">
+        <div class="container">
+            <!--<el-row :gutter="20">-->
+                <!--<el-col :span="4"><div class="grid-content bg-purple"></div></el-col>-->
+                <!--<el-col :span="16"><div class="grid-content bg-purple"></div></el-col>-->
+                <!--<el-col :span="4"><div class="grid-content bg-purple"></div></el-col>-->
+            <!--</el-row>-->
+            <el-row :gutter="20">
+                <el-col :span="4"><div class="grid-content bg-purple"><img src="../../assets/omw.png" alt="" class="logo"></div></el-col>
+                <el-col :span="16">
+                    <el-menu default-active="1" class="el-menu-demo clearfix" mode="horizontal" @select="handleSelect">
+                        <!--<el-menu-item><img src="../../assets/omw.png" alt="" class="logo"></el-menu-item>-->
+                        <el-menu-item index="1">
+                            <router-link to="proInfo"><!--<i class="el-icon-document"></i>--> 项目信息</router-link>
+                        </el-menu-item>
+                        <el-menu-item index="2">功能需求</el-menu-item>
+                        <!--<el-submenu index="2">-->
+                            <!--<template slot="title">功能需求</template>-->
+                            <!--<el-menu-item index="2-1">选项1</el-menu-item>-->
+                            <!--<el-menu-item index="2-2">选项2</el-menu-item>-->
+                            <!--<el-menu-item index="2-3">选项3</el-menu-item>-->
+                        <!--</el-submenu>-->
+                        <el-menu-item index="3">关于平台</el-menu-item>
+                    </el-menu>
+                </el-col>
+            </el-row>
+
+        </div>
+    </div>
+</template>
+
+
+<script>
+    export default {
+        data () {
+            return {
+
+            }
+        },
+        methods: {
+            handleSelect (key, keyPath) {
+                console.log(key, keyPath);
+            }
+        },
+        getMenuList () {
+            let self = this;
+            self.$http.get(server+'/info/index?page='+self.page+'&pagesize='+self.pagesize).then( (res) => {
+                if(res.ok){
+                    let response = res.data;
+                    if(response.code=='200'){
+                        self.list = response.result.list;
+                        self.total = response.result.total;
+                    }else {
+                        self.$message.error('加载失败');
+                    }
+                }else {
+                    self.$message.error('网络出错');
+                }
+            })
+        },
+    }
+</script>
+<style>
+    .logo {
+        height: 60px;
+        width: 120px;
+    }
+</style>

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

@@ -0,0 +1,30 @@
+<template>
+    <div class="wrapper">
+        <v-head></v-head>
+        <div class="container" style="min-height: 800px;">
+            <v-content></v-content>
+        </div>
+        <v-footer></v-footer>
+        <router-view></router-view>
+    </div>
+
+</template>
+
+<script>
+    import header from './header.vue';
+    import footer from './footer.vue';
+//    import main from './main.vue';
+    export default {
+        components:{
+            'vHead':header,
+            'vFooter':footer,
+//            'vContent': main
+        }
+    }
+
+</script>
+<style>
+    @import "../../../static/css/normalize.css";
+    @import "../../../static/css/common.css";
+    @import "../../../static/css/index.css";
+</style>

+ 73 - 0
src/components/index/info.vue

@@ -0,0 +1,73 @@
+<template>
+    <el-table
+        :data="tableData3"
+        border
+        style="width: 100%"
+        @selection-change="handleSelectionChange">
+        <el-table-column
+            type="selection"
+            width="50">
+        </el-table-column>
+        <el-table-column
+            inline-template
+            label="日期"
+            width="120">
+            <div>{{ row.date }}</div>
+        </el-table-column>
+        <el-table-column
+            prop="name"
+            label="姓名"
+            width="120">
+        </el-table-column>
+        <el-table-column
+            prop="address"
+            label="地址"
+            show-overflow-tooltip>
+        </el-table-column>
+    </el-table>
+</template>
+
+<script>
+    export default {
+        data() {
+            return {
+                tableData3: [{
+                    date: '2016-05-03',
+                    name: '王小虎',
+                    address: '上海市普陀区金沙江路 1518 弄'
+                }, {
+                    date: '2016-05-02',
+                    name: '王小虎',
+                    address: '上海市普陀区金沙江路 1518 弄'
+                }, {
+                    date: '2016-05-04',
+                    name: '王小虎',
+                    address: '上海市普陀区金沙江路 1518 弄'
+                }, {
+                    date: '2016-05-01',
+                    name: '王小虎',
+                    address: '上海市普陀区金沙江路 1518 弄'
+                }, {
+                    date: '2016-05-08',
+                    name: '王小虎',
+                    address: '上海市普陀区金沙江路 1518 弄'
+                }, {
+                    date: '2016-05-06',
+                    name: '王小虎',
+                    address: '上海市普陀区金沙江路 1518 弄'
+                }, {
+                    date: '2016-05-07',
+                    name: '王小虎',
+                    address: '上海市普陀区金沙江路 1518 弄'
+                }],
+                multipleSelection: []
+            }
+        },
+
+        methods: {
+            handleSelectionChange(val) {
+                this.multipleSelection = val;
+            }
+        }
+    }
+</script>

+ 41 - 0
src/main.js

@@ -0,0 +1,41 @@
+import Vue from 'vue'
+import VueRouter from 'vue-router'
+import VueResource from 'vue-resource'
+import ElementUI from 'element-ui'
+import 'element-ui/lib/theme-default/index.css'
+import App from './App'
+Vue.use(VueRouter);
+Vue.use(VueResource);
+Vue.use(ElementUI);
+Vue.http.options.emulateJSON = true;
+const router = new VueRouter({
+  routes:[
+      //重定向到admin页面
+    { path: '/', redirect: '/admin/taskList' },
+    { path: '/login', component: resolve => require(['./components/admin/login.vue'], resolve) },
+    { path: '/regist', component: resolve => require(['./components/admin/regist.vue'], resolve) },
+    { path: '/admin', component: resolve => require(['./components/admin/admin.vue'], resolve) ,
+
+      //子路由
+      children:[
+        { path: 'taskList', component: resolve => require(['./components/admin/list.vue'], resolve) },
+        { path: 'taskEdit', component: resolve => require(['./components/admin/edit.vue'], resolve) },
+        { path: 'moduleInfo', component: resolve => require(['./components/admin/info.vue'], resolve) },
+        { path: 'addProject', component: resolve => require(['./components/admin/addpro.vue'], resolve) },
+      ]
+    },
+    { path: '/index', component: function (resolve) { require(['./components/index/index.vue'], resolve) },
+        children:[
+            { path: 'proInfo', component: function (resolve) { require(['./components/admin/list.vue'], resolve) } },
+            { path: 'demand', component: function (resolve) { require(['./components/admin/edit.vue'], resolve) } },
+            { path: 'aboutus', component: function (resolve) { require(['./components/admin/info.vue'], resolve) } },
+        ]
+    }
+  ]
+});
+
+const app = new Vue({
+  router,
+  render: h => h(App),
+}).$mount('#app');
+

+ 0 - 0
static/.gitkeep


+ 133 - 0
static/css/admin.css

@@ -0,0 +1,133 @@
+*{margin:0;padding:0;}
+body{
+    font-family:"Helvetica Neue",Helvetica, "microsoft yahei", arial, STHeiTi, sans-serif;
+}
+a{text-decoration: none}
+.wrapper{
+    background: #2E363F;
+}
+.content{
+    background: none repeat scroll 0 0 #f3f3f3;
+    position: absolute;
+    left: 220px;
+    right: 0;
+    top: 70px;
+    bottom:0;
+    width: auto;
+    padding:30px;
+    box-sizing: border-box;
+    overflow-y: scroll;
+}
+.widget-box{
+    max-width: 1200px;
+    background: none repeat scroll 0 0 #F9F9F9;
+    border-left: 1px solid #F0F8FF;
+    border-top: 2px solid #27a9e3;
+    border-right: 2px solid #cdcdcd;
+    clear: both;
+    margin:0 auto 30px;
+    position: relative;
+}
+.widget-box:hover{
+    box-shadow: 0 2px 7px rgba(0,0,0,.15);
+}
+.widget-title{
+    background: #efefef;
+    border-bottom: 1px solid #F0F8FF;
+    height: 40px;
+}
+.widget-title h5 {
+    color: #666;
+    padding: 12px;
+    margin: 0;
+    font-weight: normal;
+}
+.widget-content {
+    padding:25px;
+    border-bottom: 2px solid #cdcdcd;
+}
+.widget-box .el-select,
+.widget-box .el-input,
+.widget-box .el-upload,
+.widget-box .el-tooltip{
+    display: inline-block;
+}
+.el-button+.el-tooltip {
+    margin-left: 10px;
+}
+.el-table td,.el-table th{
+    padding:5px 18px;
+}
+.el-table tr:hover{
+    background: #f6faff;
+}
+.page-box{
+    margin-top: 20px;
+    text-align: right;
+}
+.page-box .el-pagination{
+    background: #f9f9f9;
+}
+.mgb20{
+    margin-bottom: 15px;
+}
+.mgb5{
+    margin-bottom: 5px;
+}
+.dialog{
+    position: fixed;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    overflow: auto;
+    background: rgba(0,0,0,.4);
+    z-index:-1;
+    opacity: 0;
+    transition: all .5s ease;
+}
+.dialog.dialog-show{
+    opacity: 1;
+    z-index: 1000;
+}
+.dialog .dialog-wrapper{
+    position: absolute;
+    left: 50%;
+    top: 15%;
+    width: 50%;
+    transform: translateX(-50%);
+    background: #fff;
+    border-radius: 2px;
+    box-shadow: 0 1px 3px rgba(0,0,0,.3);
+    box-sizing: border-box;
+    transition: top .5s ease;
+}
+.dialog.dialog-show .dialog-wrapper{
+    top: 17%;
+}
+.dialog .dialog-header{
+    padding: 20px 20px 0;
+}
+.dialog .dialog-content{
+    padding: 30px 20px;
+    color: #475669;
+    font-size: 14px;
+}
+.dialog .dialog-close{
+    float: right;
+    cursor: pointer;
+    color: #c0ccda;
+}
+.dialog .dialog-close:hover{
+    color: #20a0ff;
+    animation: close-rotate .3s ease;
+
+}
+@keyframes close-rotate {
+    from{
+        transform: rotate(-360deg);
+    }
+}
+.el-upload__files{
+    display: none;
+}

+ 43 - 0
static/css/common.css

@@ -0,0 +1,43 @@
+.container {
+    padding-right: 15px;
+    padding-left: 15px;
+    margin-right: auto;
+    margin-left: auto;
+}
+@media (min-width: 768px) {
+    .container {
+        width: 750px;
+    }
+}
+@media (min-width: 992px) {
+    .container {
+        width: 970px;
+    }
+}
+@media (min-width: 1200px) {
+    .container {
+        width: 1170px;
+    }
+}
+.container-fluid {
+    /*padding-right: 15px;*/
+    /*padding-left: 15px;*/
+    margin-right: auto;
+    margin-left: auto;
+}
+.clearfix:before, .clearfix:after {
+    content: " ";
+    display: table;
+}
+.clearfix:after {
+    clear: both;
+}
+.pull-left {
+    float: left;
+}
+.pull-right {
+    float: right;
+}
+.text-center {
+    text-align: center;
+}

+ 7 - 0
static/css/index.css

@@ -0,0 +1,7 @@
+.nav-bar {
+    background-color: #eff2f7;
+}
+
+.logo {
+
+}

+ 461 - 0
static/css/normalize.css

@@ -0,0 +1,461 @@
+/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */
+
+/**
+ * 1. Change the default font family in all browsers (opinionated).
+ * 2. Correct the line height in all browsers.
+ * 3. Prevent adjustments of font size after orientation changes in
+ *    IE on Windows Phone and in iOS.
+ */
+
+/* Document
+   ========================================================================== */
+
+html {
+    font-family: sans-serif; /* 1 */
+    line-height: 1.15; /* 2 */
+    -ms-text-size-adjust: 100%; /* 3 */
+    -webkit-text-size-adjust: 100%; /* 3 */
+}
+
+/* Sections
+   ========================================================================== */
+
+/**
+ * Remove the margin in all browsers (opinionated).
+ */
+
+body {
+    margin: 0;
+}
+
+/**
+ * Add the correct display in IE 9-.
+ */
+
+article,
+aside,
+footer,
+header,
+nav,
+section {
+    display: block;
+}
+
+/**
+ * Correct the font size and margin on `h1` elements within `section` and
+ * `article` contexts in Chrome, Firefox, and Safari.
+ */
+
+h1 {
+    font-size: 2em;
+    margin: 0.67em 0;
+}
+
+/* Grouping content
+   ========================================================================== */
+
+/**
+ * Add the correct display in IE 9-.
+ * 1. Add the correct display in IE.
+ */
+
+figcaption,
+figure,
+main { /* 1 */
+    display: block;
+}
+
+/**
+ * Add the correct margin in IE 8.
+ */
+
+figure {
+    margin: 1em 40px;
+}
+
+/**
+ * 1. Add the correct box sizing in Firefox.
+ * 2. Show the overflow in Edge and IE.
+ */
+
+hr {
+    box-sizing: content-box; /* 1 */
+    height: 0; /* 1 */
+    overflow: visible; /* 2 */
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+pre {
+    font-family: monospace, monospace; /* 1 */
+    font-size: 1em; /* 2 */
+}
+
+/* Text-level semantics
+   ========================================================================== */
+
+/**
+ * 1. Remove the gray background on active links in IE 10.
+ * 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
+ */
+
+a {
+    background-color: transparent; /* 1 */
+    -webkit-text-decoration-skip: objects; /* 2 */
+}
+
+/**
+ * Remove the outline on focused links when they are also active or hovered
+ * in all browsers (opinionated).
+ */
+
+a:active,
+a:hover {
+    outline-width: 0;
+}
+
+/**
+ * 1. Remove the bottom border in Firefox 39-.
+ * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+ */
+
+abbr[title] {
+    border-bottom: none; /* 1 */
+    text-decoration: underline; /* 2 */
+    text-decoration: underline dotted; /* 2 */
+}
+
+/**
+ * Prevent the duplicate application of `bolder` by the next rule in Safari 6.
+ */
+
+b,
+strong {
+    font-weight: inherit;
+}
+
+/**
+ * Add the correct font weight in Chrome, Edge, and Safari.
+ */
+
+b,
+strong {
+    font-weight: bolder;
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+code,
+kbd,
+samp {
+    font-family: monospace, monospace; /* 1 */
+    font-size: 1em; /* 2 */
+}
+
+/**
+ * Add the correct font style in Android 4.3-.
+ */
+
+dfn {
+    font-style: italic;
+}
+
+/**
+ * Add the correct background and color in IE 9-.
+ */
+
+mark {
+    background-color: #ff0;
+    color: #000;
+}
+
+/**
+ * Add the correct font size in all browsers.
+ */
+
+small {
+    font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` elements from affecting the line height in
+ * all browsers.
+ */
+
+sub,
+sup {
+    font-size: 75%;
+    line-height: 0;
+    position: relative;
+    vertical-align: baseline;
+}
+
+sub {
+    bottom: -0.25em;
+}
+
+sup {
+    top: -0.5em;
+}
+
+/* Embedded content
+   ========================================================================== */
+
+/**
+ * Add the correct display in IE 9-.
+ */
+
+audio,
+video {
+    display: inline-block;
+}
+
+/**
+ * Add the correct display in iOS 4-7.
+ */
+
+audio:not([controls]) {
+    display: none;
+    height: 0;
+}
+
+/**
+ * Remove the border on images inside links in IE 10-.
+ */
+
+img {
+    border-style: none;
+}
+
+/**
+ * Hide the overflow in IE.
+ */
+
+svg:not(:root) {
+    overflow: hidden;
+}
+
+/* Forms
+   ========================================================================== */
+
+/**
+ * 1. Change the font styles in all browsers (opinionated).
+ * 2. Remove the margin in Firefox and Safari.
+ */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+    font-family: sans-serif; /* 1 */
+    font-size: 100%; /* 1 */
+    line-height: 1.15; /* 1 */
+    margin: 0; /* 2 */
+}
+
+/**
+ * Show the overflow in IE.
+ * 1. Show the overflow in Edge.
+ */
+
+button,
+input { /* 1 */
+    overflow: visible;
+}
+
+/**
+ * Remove the inheritance of text transform in Edge, Firefox, and IE.
+ * 1. Remove the inheritance of text transform in Firefox.
+ */
+
+button,
+select { /* 1 */
+    text-transform: none;
+}
+
+/**
+ * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
+ *    controls in Android 4.
+ * 2. Correct the inability to style clickable types in iOS and Safari.
+ */
+
+button,
+html [type="button"], /* 1 */
+[type="reset"],
+[type="submit"] {
+    -webkit-appearance: button; /* 2 */
+}
+
+/**
+ * Remove the inner border and padding in Firefox.
+ */
+
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner {
+    border-style: none;
+    padding: 0;
+}
+
+/**
+ * Restore the focus styles unset by the previous rule.
+ */
+
+button:-moz-focusring,
+[type="button"]:-moz-focusring,
+[type="reset"]:-moz-focusring,
+[type="submit"]:-moz-focusring {
+    outline: 1px dotted ButtonText;
+}
+
+/**
+ * Change the border, margin, and padding in all browsers (opinionated).
+ */
+
+fieldset {
+    border: 1px solid #c0c0c0;
+    margin: 0 2px;
+    padding: 0.35em 0.625em 0.75em;
+}
+
+/**
+ * 1. Correct the text wrapping in Edge and IE.
+ * 2. Correct the color inheritance from `fieldset` elements in IE.
+ * 3. Remove the padding so developers are not caught out when they zero out
+ *    `fieldset` elements in all browsers.
+ */
+
+legend {
+    box-sizing: border-box; /* 1 */
+    color: inherit; /* 2 */
+    display: table; /* 1 */
+    max-width: 100%; /* 1 */
+    padding: 0; /* 3 */
+    white-space: normal; /* 1 */
+}
+
+/**
+ * 1. Add the correct display in IE 9-.
+ * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
+ */
+
+progress {
+    display: inline-block; /* 1 */
+    vertical-align: baseline; /* 2 */
+}
+
+/**
+ * Remove the default vertical scrollbar in IE.
+ */
+
+textarea {
+    overflow: auto;
+}
+
+/**
+ * 1. Add the correct box sizing in IE 10-.
+ * 2. Remove the padding in IE 10-.
+ */
+
+[type="checkbox"],
+[type="radio"] {
+    box-sizing: border-box; /* 1 */
+    padding: 0; /* 2 */
+}
+
+/**
+ * Correct the cursor style of increment and decrement buttons in Chrome.
+ */
+
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+    height: auto;
+}
+
+/**
+ * 1. Correct the odd appearance in Chrome and Safari.
+ * 2. Correct the outline style in Safari.
+ */
+
+[type="search"] {
+    -webkit-appearance: textfield; /* 1 */
+    outline-offset: -2px; /* 2 */
+}
+
+/**
+ * Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
+ */
+
+[type="search"]::-webkit-search-cancel-button,
+[type="search"]::-webkit-search-decoration {
+    -webkit-appearance: none;
+}
+
+/**
+ * 1. Correct the inability to style clickable types in iOS and Safari.
+ * 2. Change font properties to `inherit` in Safari.
+ */
+
+::-webkit-file-upload-button {
+    -webkit-appearance: button; /* 1 */
+    font: inherit; /* 2 */
+}
+
+/* Interactive
+   ========================================================================== */
+
+/*
+ * Add the correct display in IE 9-.
+ * 1. Add the correct display in Edge, IE, and Firefox.
+ */
+
+details, /* 1 */
+menu {
+    display: block;
+}
+
+/*
+ * Add the correct display in all browsers.
+ */
+
+summary {
+    display: list-item;
+}
+
+/* Scripting
+   ========================================================================== */
+
+/**
+ * Add the correct display in IE 9-.
+ */
+
+canvas {
+    display: inline-block;
+}
+
+/**
+ * Add the correct display in IE.
+ */
+
+template {
+    display: none;
+}
+
+/* Hidden
+   ========================================================================== */
+
+/**
+ * Add the correct display in IE 10-.
+ */
+
+[hidden] {
+    display: none;
+}

Різницю між файлами не показано, бо вона завелика
+ 0 - 0
static/css/normalize.min.css


+ 9 - 0
test/unit/.eslintrc

@@ -0,0 +1,9 @@
+{
+  "env": {
+    "mocha": true
+  },
+  "globals": {
+    "expect": true,
+    "sinon": true
+  }
+}

+ 13 - 0
test/unit/index.js

@@ -0,0 +1,13 @@
+// Polyfill fn.bind() for PhantomJS
+/* eslint-disable no-extend-native */
+Function.prototype.bind = require('function-bind')
+
+// require all test files (files that ends with .spec.js)
+const testsContext = require.context('./specs', true, /\.spec$/)
+testsContext.keys().forEach(testsContext)
+
+// require all src files except main.js for coverage.
+// you can also change this to match only the subset of files that
+// you want coverage for.
+const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/)
+srcContext.keys().forEach(srcContext)

+ 75 - 0
test/unit/karma.conf.js

@@ -0,0 +1,75 @@
+// This is a karma config file. For more details see
+//   http://karma-runner.github.io/0.13/config/configuration-file.html
+// we are also using it with karma-webpack
+//   https://github.com/webpack/karma-webpack
+
+var path = require('path')
+var merge = require('webpack-merge')
+var baseConfig = require('../../build/webpack.base.conf')
+var utils = require('../../build/utils')
+var webpack = require('webpack')
+var projectRoot = path.resolve(__dirname, '../../')
+
+var webpackConfig = merge(baseConfig, {
+  // use inline sourcemap for karma-sourcemap-loader
+  module: {
+    loaders: utils.styleLoaders()
+  },
+  devtool: '#inline-source-map',
+  vue: {
+    loaders: {
+      js: 'isparta'
+    }
+  },
+  plugins: [
+    new webpack.DefinePlugin({
+      'process.env': require('../../config/test.env')
+    })
+  ]
+})
+
+// no need for app entry during tests
+delete webpackConfig.entry
+
+// make sure isparta loader is applied before eslint
+webpackConfig.module.preLoaders = webpackConfig.module.preLoaders || []
+webpackConfig.module.preLoaders.unshift({
+  test: /\.js$/,
+  loader: 'isparta',
+  include: path.resolve(projectRoot, 'src')
+})
+
+// only apply babel for test files when using isparta
+webpackConfig.module.loaders.some(function (loader, i) {
+  if (loader.loader === 'babel') {
+    loader.include = path.resolve(projectRoot, 'test/unit')
+    return true
+  }
+})
+
+module.exports = function (config) {
+  config.set({
+    // to run in additional browsers:
+    // 1. install corresponding karma launcher
+    //    http://karma-runner.github.io/0.13/config/browsers.html
+    // 2. add it to the `browsers` array below.
+    browsers: ['PhantomJS'],
+    frameworks: ['mocha', 'sinon-chai'],
+    reporters: ['spec', 'coverage'],
+    files: ['./index.js'],
+    preprocessors: {
+      './index.js': ['webpack', 'sourcemap']
+    },
+    webpack: webpackConfig,
+    webpackMiddleware: {
+      noInfo: true
+    },
+    coverageReporter: {
+      dir: './coverage',
+      reporters: [
+        { type: 'lcov', subdir: '.' },
+        { type: 'text-summary' }
+      ]
+    }
+  })
+}

+ 13 - 0
test/unit/specs/Hello.spec.js

@@ -0,0 +1,13 @@
+import Vue from 'vue'
+import Hello from 'src/components/Hello'
+
+describe('Hello.vue', () => {
+  it('should render correct contents', () => {
+    const vm = new Vue({
+      el: document.createElement('div'),
+      render: (h) => h(Hello)
+    })
+    expect(vm.$el.querySelector('.hello h1').textContent)
+      .to.equal('Welcome to Your Vue.js App')
+  })
+})

Деякі файли не було показано, через те що забагато файлів було змінено