Browse Source

first import

stanley-king 7 years ago
commit
ff39a24309
100 changed files with 6004 additions and 0 deletions
  1. 30 0
      README.md
  2. 1 0
      api.json
  3. 35 0
      build/build.js
  4. 45 0
      build/check-versions.js
  5. 9 0
      build/dev-client.js
  6. 95 0
      build/dev-server.js
  7. 71 0
      build/utils.js
  8. 17 0
      build/vue-loader.conf.js
  9. 67 0
      build/webpack.base.conf.js
  10. 35 0
      build/webpack.dev.conf.js
  11. 124 0
      build/webpack.prod.conf.js
  12. 24 0
      build/webpack.test.conf.js
  13. 6 0
      config/dev.env.js
  14. 38 0
      config/index.js
  15. 3 0
      config/prod.env.js
  16. 6 0
      config/test.env.js
  17. 16 0
      index.html
  18. 102 0
      package.json
  19. 57 0
      src/App.vue
  20. BIN
      src/assets/address_edit_unselected.png
  21. BIN
      src/assets/address_icon.jpg
  22. BIN
      src/assets/address_icon.png
  23. BIN
      src/assets/authorize_icon.png
  24. BIN
      src/assets/bonus_icon.jpg
  25. BIN
      src/assets/bonus_icon.png
  26. BIN
      src/assets/default_img.jpg
  27. BIN
      src/assets/evaluated_icon.png
  28. BIN
      src/assets/favorites_icon.png
  29. BIN
      src/assets/fcode_icon.png
  30. BIN
      src/assets/feedback_icon.png
  31. BIN
      src/assets/goods_receipt_icon.png
  32. BIN
      src/assets/help_icon.png
  33. BIN
      src/assets/mine_logo_icon.png
  34. BIN
      src/assets/payments_due_icon.png
  35. BIN
      src/assets/refund_terms_icon.png
  36. BIN
      src/assets/search_log.jpg
  37. BIN
      src/assets/seven_icon.png
  38. BIN
      src/assets/shipping_term_icon.png
  39. BIN
      src/assets/small_bonus_icon.png
  40. BIN
      src/assets/small_logo.png
  41. BIN
      src/assets/tabbar_home_default@2x.png
  42. BIN
      src/assets/tabbar_home_hight@2x.png
  43. BIN
      src/assets/tabbar_set_default@2x.png
  44. BIN
      src/assets/tabbar_set_hight@2x.png
  45. BIN
      src/assets/tabbar_shopcar_default@2x.png
  46. BIN
      src/assets/tabbar_shopcar_hight@2x.png
  47. BIN
      src/assets/tabbar_shopguide_default@2x.png
  48. BIN
      src/assets/tabbar_shopguide_hight@2x.png
  49. BIN
      src/assets/wx_icon.jpg
  50. 168 0
      src/components/address/add_address.vue
  51. 117 0
      src/components/address/addressList.vue
  52. 58 0
      src/components/blocks/GridList.vue
  53. 55 0
      src/components/blocks/HomeGrid.vue
  54. 66 0
      src/components/blocks/Image.vue
  55. 104 0
      src/components/blocks/block_list.vue
  56. 29 0
      src/components/blocks/brand.vue
  57. 42 0
      src/components/blocks/divider.vue
  58. 21 0
      src/components/blocks/empty_bock.vue
  59. 101 0
      src/components/blocks/fcode.vue
  60. 189 0
      src/components/blocks/goods_item.vue
  61. 32 0
      src/components/blocks/goods_list.vue
  62. 150 0
      src/components/blocks/goods_top.vue
  63. 42 0
      src/components/blocks/home2.vue
  64. 75 0
      src/components/blocks/horizongoods_item.vue
  65. 121 0
      src/components/blocks/horizongoods_list.vue
  66. 50 0
      src/components/blocks/hot_search.vue
  67. 73 0
      src/components/blocks/search.vue
  68. 40 0
      src/components/blocks/swiper_list.vue
  69. 22 0
      src/components/blocks/title.vue
  70. 260 0
      src/components/blocks/util/blockUtil.js
  71. 88 0
      src/components/blocks/util/proxy.js
  72. 30 0
      src/components/blocks/video.vue
  73. 30 0
      src/components/blocks/webview.vue
  74. 25 0
      src/components/comps/scroller.vue
  75. 234 0
      src/components/confirm_order/confirm_order.vue
  76. 31 0
      src/components/fcode/fcode_list.vue
  77. 515 0
      src/components/goods/goods_detail.vue
  78. 146 0
      src/components/login/login.vue
  79. 25 0
      src/components/main/discovery/brandList.vue
  80. 70 0
      src/components/main/discovery/discovery.vue
  81. 105 0
      src/components/main/discovery/functionList.vue
  82. 35 0
      src/components/main/home/homeList.vue
  83. 99 0
      src/components/main/home/homeTabs.vue
  84. 73 0
      src/components/main/main.vue
  85. 222 0
      src/components/main/mine/mine.vue
  86. 109 0
      src/components/main/shop-car/CarProxy.js
  87. 352 0
      src/components/main/shop-car/shopping_cart.vue
  88. 331 0
      src/components/order/list/OrderList.vue
  89. 64 0
      src/components/order/list/OrderTabs.vue
  90. 85 0
      src/components/other/search.vue
  91. 30 0
      src/components/other/webview.vue
  92. 91 0
      src/components/special/special_list.vue
  93. 36 0
      src/components/util/commonUtil.js
  94. 65 0
      src/lib/Actions.js
  95. 117 0
      src/lib/api.js
  96. 134 0
      src/main.js
  97. 128 0
      src/router/routes.js
  98. 112 0
      src/wechat/WechatShare.js
  99. 26 0
      test/e2e/custom-assertions/elementCount.js
  100. 0 0
      test/e2e/nightwatch.conf.js

+ 30 - 0
README.md

@@ -0,0 +1,30 @@
+# panda_h5_shop
+
+> A Vue.js project
+
+## Build Setup
+
+``` bash
+# install dependencies
+npm install
+
+# serve with hot reload at localhost:8080
+npm run dev
+
+# build for production with minification
+npm run build
+
+# build for production and view the bundle analyzer report
+npm run build --report
+
+# run unit tests
+npm run unit
+
+# run e2e tests
+npm run e2e
+
+# 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).

+ 1 - 0
api.json

@@ -0,0 +1 @@
+{ "firstName": "Brett", "lastName":"McLaughlin", "email": "aaaa" }

+ 35 - 0
build/build.js

@@ -0,0 +1,35 @@
+require('./check-versions')()
+
+process.env.NODE_ENV = 'production'
+
+var ora = require('ora')
+var rm = require('rimraf')
+var path = require('path')
+var chalk = require('chalk')
+var webpack = require('webpack')
+var config = require('../config')
+var webpackConfig = require('./webpack.prod.conf')
+
+var spinner = ora('building for production...')
+spinner.start()
+
+rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
+  if (err) throw err
+  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\n')
+
+    console.log(chalk.cyan('  Build complete.\n'))
+    console.log(chalk.yellow(
+      '  Tip: built files are meant to be served over an HTTP server.\n' +
+      '  Opening index.html over file:// won\'t work.\n'
+    ))
+  })
+})

+ 45 - 0
build/check-versions.js

@@ -0,0 +1,45 @@
+var chalk = require('chalk')
+var semver = require('semver')
+var packageConfig = require('../package.json')
+
+function exec (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()
+  }
+})

+ 95 - 0
build/dev-server.js

@@ -0,0 +1,95 @@
+require('./check-versions')()
+
+var config = require('../config')
+if (!process.env.NODE_ENV) {
+  process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
+}
+
+var opn = require('opn')
+var path = require('path')
+var express = require('express')
+var webpack = require('webpack')
+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
+// automatically open browser, if not set will be false
+var autoOpenBrowser = !!config.dev.autoOpenBrowser
+// 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,
+  quiet: true
+})
+
+var hotMiddleware = require('webpack-hot-middleware')(compiler, {
+  log: () => {}
+})
+// 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(options.filter || 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('./mshop'))
+
+var uri = 'http://localhost:' + port
+
+var _resolve
+var readyPromise = new Promise(resolve => {
+  _resolve = resolve
+})
+
+console.log('> Starting dev server...')
+devMiddleware.waitUntilValid(() => {
+  console.log('> Listening at ' + uri + '\n')
+  // when env is testing, don't need open it
+
+  if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
+    opn(uri)
+  }
+  _resolve()
+})
+
+var server = app.listen(port)
+
+module.exports = {
+  ready: readyPromise,
+  close: () => {
+    server.close()
+  }
+}

+ 71 - 0
build/utils.js

@@ -0,0 +1,71 @@
+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 || {}
+
+  var cssLoader = {
+    loader: 'css-loader',
+    options: {
+      minimize: process.env.NODE_ENV === 'production',
+      sourceMap: options.sourceMap
+    }
+  }
+
+  // generate loader string to be used with extract text plugin
+  function generateLoaders (loader, loaderOptions) {
+    var loaders = [cssLoader]
+    if (loader) {
+      loaders.push({
+        loader: loader + '-loader',
+        options: Object.assign({}, loaderOptions, {
+          sourceMap: options.sourceMap
+        })
+      })
+    }
+
+    // Extract CSS when that option is specified
+    // (which is the case during production build)
+    if (options.extract) {
+      return ExtractTextPlugin.extract({
+        use: loaders,
+        fallback: 'vue-style-loader'
+      })
+    } else {
+      return ['vue-style-loader'].concat(loaders)
+    }
+  }
+
+  // https://vue-loader.vuejs.org/en/configurations/extract-css.html
+  return {
+    css: generateLoaders(),
+    postcss: generateLoaders(),
+    less: generateLoaders('less'),
+    sass: generateLoaders('sass', { indentedSyntax: true }),
+    scss: generateLoaders('sass'),
+    stylus: generateLoaders('stylus'),
+    styl: generateLoaders('stylus')
+  }
+}
+
+// Generate loaders for standalone style files (outside of .vue)
+exports.styleLoaders = function (options) {
+  var output = []
+  var loaders = exports.cssLoaders(options)
+  for (var extension in loaders) {
+    var loader = loaders[extension]
+    output.push({
+      test: new RegExp('\\.' + extension + '$'),
+      use: loader
+    })
+  }
+  return output
+}

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

@@ -0,0 +1,17 @@
+var utils = require('./utils')
+var config = require('../config')
+var isProduction = process.env.NODE_ENV === 'production'
+
+module.exports = {
+  loaders: utils.cssLoaders({
+    sourceMap: isProduction
+      ? config.build.productionSourceMap
+      : config.dev.cssSourceMap,
+    extract: isProduction
+  }),
+  postcss: [
+    require('autoprefixer')({
+      browsers: ['iOS >= 7', 'Android >= 4.1']
+    })
+  ]
+}

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

@@ -0,0 +1,67 @@
+var path = require('path')
+var utils = require('./utils')
+
+var projectRoot = path.resolve(__dirname, '../')
+const vuxLoader = require('vux-loader')
+
+var config = require('../config')
+var vueLoaderConfig = require('./vue-loader.conf')
+
+function resolve (dir) {
+  return path.join(__dirname, '..', dir)
+}
+
+let webpackConfig = {
+  entry: {
+    app: './src/main.js'
+  },
+  output: {
+    path: config.build.assetsRoot,
+    filename: '[name].js',
+    publicPath: process.env.NODE_ENV === 'production'
+      ? config.build.assetsPublicPath
+      : config.dev.assetsPublicPath
+  },
+  resolve: {
+    extensions: ['.js', '.vue', '.json'],
+    alias: {
+      'vue$': 'vue/dist/vue.esm.js',
+      '@': resolve('src')
+    }
+  },
+  module: {
+    rules: [
+      {
+        test: /\.vue$/,
+        loader: 'vue-loader',
+        options: vueLoaderConfig
+      },
+      {
+        test: /\.js$/,
+        loader: 'babel-loader',
+        include: [resolve('src'), resolve('test')]
+      },
+      {
+        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('img/[name].[hash:7].[ext]')
+        }
+      },
+      {
+        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
+        }
+      }
+    ]
+  }
+};
+
+
+module.exports = vuxLoader.merge(webpackConfig, {
+  plugins: ['vux-ui', 'progress-bar', 'duplicate-style']
+});

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

@@ -0,0 +1,35 @@
+var utils = require('./utils')
+var webpack = require('webpack')
+var config = require('../config')
+var merge = require('webpack-merge')
+var baseWebpackConfig = require('./webpack.base.conf')
+var HtmlWebpackPlugin = require('html-webpack-plugin')
+var FriendlyErrorsPlugin = require('friendly-errors-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: {
+    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
+  },
+  // cheap-module-eval-source-map is faster for development
+  devtool: 'source-map',
+  plugins: [
+    new webpack.DefinePlugin({
+      'process.env': config.dev.env
+    }),
+    // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
+    new webpack.HotModuleReplacementPlugin(),
+    new webpack.NoEmitOnErrorsPlugin(),
+    // https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: 'index.html',
+      template: 'index.html',
+      inject: true
+    }),
+    new FriendlyErrorsPlugin()
+  ]
+})

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

@@ -0,0 +1,124 @@
+var path = require('path')
+var utils = require('./utils')
+var webpack = require('webpack')
+var config = require('../config')
+var merge = require('webpack-merge')
+var baseWebpackConfig = require('./webpack.base.conf')
+var CopyWebpackPlugin = require('copy-webpack-plugin')
+var HtmlWebpackPlugin = require('html-webpack-plugin')
+var ExtractTextPlugin = require('extract-text-webpack-plugin')
+var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
+
+var env = process.env.NODE_ENV === 'testing'
+  ? require('../config/test.env')
+  : config.build.env
+
+var webpackConfig = merge(baseWebpackConfig, {
+  module: {
+    rules: 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')
+  },
+  plugins: [
+    // http://vuejs.github.io/vue-loader/en/workflow/production.html
+    new webpack.DefinePlugin({
+      'process.env': env
+    }),
+    new webpack.optimize.UglifyJsPlugin({
+      compress: {
+        warnings: false
+      },
+      sourceMap: true
+    }),
+    // extract css into its own file
+    new ExtractTextPlugin({
+      filename: utils.assetsPath('css/[name].[contenthash].css')
+    }),
+    // Compress extracted CSS. We are using this plugin so that possible
+    // duplicated CSS from different components can be deduped.
+    new OptimizeCSSPlugin({
+      cssProcessorOptions: {
+        safe: true
+      }
+    }),
+    // generate dist index.html with correct asset hash for caching.
+    // you can customize output by editing /index.html
+    // see https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: process.env.NODE_ENV === 'testing'
+        ? 'index.html'
+        : config.build.index,
+      template: 'index.html',
+      inject: true,
+      minify: {
+        removeComments: true,
+        collapseWhitespace: true,
+        removeAttributeQuotes: true
+        // more options:
+        // https://github.com/kangax/html-minifier#options-quick-reference
+      },
+      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
+      chunksSortMode: 'dependency'
+    }),
+    // 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']
+    }),
+    // copy custom static assets
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static'),
+        to: config.build.assetsSubDirectory,
+        ignore: ['.*']
+      }
+    ])
+  ]
+})
+
+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
+    })
+  )
+}
+
+if (config.build.bundleAnalyzerReport) {
+  var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
+  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
+}
+
+module.exports = webpackConfig

+ 24 - 0
build/webpack.test.conf.js

@@ -0,0 +1,24 @@
+// This is the webpack config used for unit tests.
+
+var utils = require('./utils')
+var webpack = require('webpack')
+var merge = require('webpack-merge')
+var baseConfig = require('./webpack.base.conf')
+
+var webpackConfig = merge(baseConfig, {
+  // use inline sourcemap for karma-sourcemap-loader
+  module: {
+    rules: utils.styleLoaders()
+  },
+  devtool: '#inline-source-map',
+  plugins: [
+    new webpack.DefinePlugin({
+      'process.env': require('../config/test.env')
+    })
+  ]
+})
+
+// no need for app entry during tests
+delete webpackConfig.entry
+
+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"'
+})

+ 38 - 0
config/index.js

@@ -0,0 +1,38 @@
+// see http://vuejs-templates.github.io/webpack for documentation.
+var path = require('path')
+
+module.exports = {
+  build: {
+    env: require('./prod.env'),
+    index: path.resolve(__dirname, '../dist/index.html'),
+    assetsRoot: path.resolve(__dirname, '../dist'),
+    assetsSubDirectory: 'mshop',
+    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'],
+    // Run the build command with an extra argument to
+    // View the bundle analyzer report after build finishes:
+    // `npm run build --report`
+    // Set to `true` or `false` to always turn it on or off
+    bundleAnalyzerReport: process.env.npm_config_report
+  },
+  dev: {
+    env: require('./dev.env'),
+    port: 8080,
+    autoOpenBrowser: false,
+    assetsSubDirectory: 'mshop',
+    assetsPublicPath: '/',
+    proxyTable: {},
+    // 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"'
+})

+ 16 - 0
index.html

@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=750,user-scalable=no,target-densitydpi=device-dpi">
+    <meta name="layoutmode" content="standard"/>
+    <meta name="imagemode" content="force"/>
+    <meta name="screen-orientation" content="portrait">
+    <meta name="x5-fullscreen" content="true" />
+    <title>熊猫美妆</title>
+</head>
+<body>
+<div id="app-box"></div>
+<!-- built files will be auto injected -->
+</body>
+</html>

+ 102 - 0
package.json

@@ -0,0 +1,102 @@
+{
+  "name": "panda_h5_shop",
+  "version": "1.0.0",
+  "description": "A Vue.js project",
+  "author": "huanggang <175906368@qq.com>",
+  "private": true,
+  "scripts": {
+    "dev": "node build/dev-server.js",
+    "build": "node build/build.js",
+    "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
+    "e2e": "node test/e2e/runner.js",
+    "test": "npm run unit && npm run e2e"
+  },
+  "dependencies": {
+    "fastclick": "^1.0.6",
+    "vue": "^2.2.2",
+    "vue-resource": "^1.3.1",
+    "vue-router": "^2.2.0",
+    "vuex": "^2.1.1",
+    "vuex-i18n": "^1.3.1",
+    "vux": "^2.2.0"
+  },
+  "devDependencies": {
+    "autoprefixer": "^6.7.2",
+    "babel-core": "^6.22.1",
+    "babel-helper-vue-jsx-merge-props": "^2.0.2",
+    "babel-loader": "^6.2.10",
+    "babel-plugin-istanbul": "^3.1.2",
+    "babel-plugin-syntax-jsx": "^6.18.0",
+    "babel-plugin-transform-runtime": "^6.22.0",
+    "babel-plugin-transform-vue-jsx": "^3.4.3",
+    "babel-preset-env": "^1.2.1",
+    "babel-preset-es2015": "^6.24.1",
+    "babel-preset-stage-2": "^6.22.0",
+    "babel-register": "^6.22.0",
+    "chai": "^3.5.0",
+    "chalk": "^1.1.3",
+    "chromedriver": "^2.27.2",
+    "compression-webpack-plugin": "^0.3.2",
+    "connect-history-api-fallback": "^1.3.0",
+    "copy-webpack-plugin": "^4.0.1",
+    "cross-env": "^3.1.4",
+    "cross-spawn": "^5.0.1",
+    "css-loader": "^0.26.1",
+    "eventsource-polyfill": "^0.9.6",
+    "express": "^4.14.1",
+    "extract-text-webpack-plugin": "^2.0.0",
+    "file-loader": "^0.10.0",
+    "friendly-errors-webpack-plugin": "^1.1.3",
+    "function-bind": "^1.1.0",
+    "html-webpack-plugin": "^2.28.0",
+    "http-proxy-middleware": "^0.17.3",
+    "inject-loader": "^2.0.1",
+    "karma": "^1.4.1",
+    "karma-coverage": "^1.1.1",
+    "karma-mocha": "^1.3.0",
+    "karma-phantomjs-launcher": "^1.0.2",
+    "karma-sinon-chai": "^1.2.4",
+    "karma-sourcemap-loader": "^0.3.7",
+    "karma-spec-reporter": "0.0.26",
+    "karma-webpack": "^2.0.2",
+    "less": "^2.7.1",
+    "less-loader": "^2.2.3",
+    "lolex": "^1.5.2",
+    "mocha": "^3.2.0",
+    "mock": "^0.1.1",
+    "mockjs": "^1.0.1-beta3",
+    "nightwatch": "^0.9.12",
+    "opn": "^4.0.2",
+    "optimize-css-assets-webpack-plugin": "^1.3.0",
+    "ora": "^1.1.0",
+    "phantomjs-prebuilt": "^2.1.14",
+    "rimraf": "^2.6.0",
+    "selenium-server": "^3.0.1",
+    "semver": "^5.3.0",
+    "sinon": "^1.17.7",
+    "sinon-chai": "^2.8.0",
+    "url-loader": "^0.5.7",
+    "vue-loader": "^11.1.4",
+    "vue-style-loader": "^2.0.0",
+    "vue-template-compiler": "^2.2.4",
+    "vue-vid": "^1.0.2",
+    "vue-video": "^0.1.6",
+    "vue-video-player": "^3.0.8",
+    "vuex": "^2.3.1",
+    "vux-loader": "^1.0.56",
+    "webpack": "^2.2.1",
+    "webpack-bundle-analyzer": "^2.2.1",
+    "webpack-dev-middleware": "^1.10.0",
+    "webpack-hot-middleware": "^2.16.1",
+    "webpack-merge": "^2.6.1",
+    "yaml-loader": "^0.4.0"
+  },
+  "engines": {
+    "node": ">= 4.0.0",
+    "npm": ">= 3.0.0"
+  },
+  "browserslist": [
+    "iOS >= 7",
+    "Android >= 4.1"
+  ]
+}

+ 57 - 0
src/App.vue

@@ -0,0 +1,57 @@
+<template>
+    <div id="app">
+        <router-view id="router_view"></router-view>
+        <loading v-model="isLoading"></loading>
+    </div>
+</template>
+
+<script>
+    import {Tabbar, TabbarItem, Loading} from 'vux'
+    import Api from './lib/api'
+    export default {
+        created(){
+            let _self = this;
+            this.$store.commit('updateLoadingStatus', {isLoading: true});
+            this.$http.jsonp(Api.member_info_get()).then(function (res)
+            {
+                if (res.body.code !== 10014)
+                {
+                    _self.$store.commit('updateLogIn');
+                    _self.$store.commit('updateUser', {
+                        'name': res.body.datas.member_nickname,
+                        'image': res.body.datas.member_avatar
+                    });
+                    _self.$http.jsonp(Api.user_bonus()).then(function (res) {
+                        _self.$store.commit('updateUser_bonus', {'array': res.body.datas.bonus_rate});
+                    });
+                    this.$store.commit('updateLoadingStatus', {isLoading: false});
+                }
+            });
+        },
+        name: 'app',
+        components: {
+            Loading
+        },
+        computed: {
+            isLoading(){
+                return this.$store.state.isLoading;
+            }
+        }
+    }
+</script>
+
+<style lang="less">
+    @import '~vux/src/styles/reset.less';
+    @import '~vux/src/styles/1px.less';
+    @import '~vux/src/styles/close.less';
+    body {
+        background-color: #F7F7F7;
+        font-size: 24px;
+    }
+    #router_view {
+        background: #fff;
+    }
+    a {
+        color: inherit;
+    }
+</style>

BIN
src/assets/address_edit_unselected.png


BIN
src/assets/address_icon.jpg


BIN
src/assets/address_icon.png


BIN
src/assets/authorize_icon.png


BIN
src/assets/bonus_icon.jpg


BIN
src/assets/bonus_icon.png


BIN
src/assets/default_img.jpg


BIN
src/assets/evaluated_icon.png


BIN
src/assets/favorites_icon.png


BIN
src/assets/fcode_icon.png


BIN
src/assets/feedback_icon.png


BIN
src/assets/goods_receipt_icon.png


BIN
src/assets/help_icon.png


BIN
src/assets/mine_logo_icon.png


BIN
src/assets/payments_due_icon.png


BIN
src/assets/refund_terms_icon.png


BIN
src/assets/search_log.jpg


BIN
src/assets/seven_icon.png


BIN
src/assets/shipping_term_icon.png


BIN
src/assets/small_bonus_icon.png


BIN
src/assets/small_logo.png


BIN
src/assets/tabbar_home_default@2x.png


BIN
src/assets/tabbar_home_hight@2x.png


BIN
src/assets/tabbar_set_default@2x.png


BIN
src/assets/tabbar_set_hight@2x.png


BIN
src/assets/tabbar_shopcar_default@2x.png


BIN
src/assets/tabbar_shopcar_hight@2x.png


BIN
src/assets/tabbar_shopguide_default@2x.png


BIN
src/assets/tabbar_shopguide_hight@2x.png


BIN
src/assets/wx_icon.jpg


+ 168 - 0
src/components/address/add_address.vue

@@ -0,0 +1,168 @@
+<template>
+    <div>
+        <x-header>添加新地址</x-header>
+        <group class="address_item">
+            <x-input title="收件人:" v-model="user_name" placeholder="请输入收件人"></x-input>
+            <x-input title="手机号码:" v-model="mobile" type="tel" placeholder="请输入手机号码"></x-input>
+            <popup-picker title="地区:" :columns="3" :data="address_list" v-model="area"
+                          show-name style="padding-top: 15px;padding-bottom: 14px;" value-text-align = 'left'></popup-picker>
+            <x-input title="详细地址:" v-model="address" placeholder="请输入详细地址"></x-input>
+        </group>
+        <div class="set_default" @click="set_default">
+            <icon :type="set_default_type"></icon>
+            设为默认地址
+        </div>
+        <div class="submit">
+            <x-button type="warn" text="确认" class="submit_btn" @click.native="commmitAddress"></x-button>
+        </div>
+    </div>
+</template>
+<script>
+    import {XHeader, Group, XInput, XButton, Icon, PopupPicker} from 'vux'
+    import Api from '../../lib/api.js'
+    export default {
+        created(){
+            this.$store.commit('updateLoadingStatus', {isLoading: true});
+            let _self = this;
+            this.$http.jsonp(Api.get_address()).then(function (res) {
+                _self.datas = res.body.datas;
+                this.$store.commit('updateLoadingStatus', {isLoading: false});
+            });
+        },
+        data () {
+            return {
+                datas: '',
+                user_name: '',
+                mobile: '',
+                area: [],
+                address: '',
+                is_default: true,
+                set_default_type: 'success'
+            }
+        },
+        computed: {
+            address_list(){
+                if (!this.datas)return;
+                let new_list = [];
+                let areas = this.datas["areas"];
+                for (let i = 0; i < areas.length; i++) {
+                    let area_item = {};
+                    area_item.name = areas[i]['n'];
+                    area_item.value = areas[i]['aid'];
+                    area_item.parent = areas[i]['pid'];
+                    new_list.push(area_item);
+                }
+                return new_list;
+            }
+        },
+        components: {
+            Group,
+            XInput,
+            XHeader,
+            XButton,
+            Icon,
+            PopupPicker
+        },
+        methods: {
+            set_default(){
+                if (this.set_default_type == 'success') {
+                    this.set_default_type = 'circle';
+                    this.is_default = false;
+                } else {
+                    this.set_default_type = 'success';
+                    this.is_default = true;
+                }
+
+            },
+            commmitAddress(){
+                let msg = {
+                    type: 'text',
+                    text: '',
+                    position: 'middle',
+                    width: '400px'
+                };
+                if (this.user_name == "") {
+                    msg.text = "请填写您的收货人名称";
+                    this.$vux.toast.show(msg);
+                    return;
+                }
+                if (!(/^1(3|4|5|7|8)\d{9}$/.test(this.mobile))) {
+                    msg.text = "请填写您的手机号";
+                    this.$vux.toast.show(msg);
+                    return;
+                }
+                if (this.area.length == 0) {
+                    msg.text = "请选择您的收货地址";
+                    this.$vux.toast.show(msg);
+                    return;
+                }
+                if (this.address == "") {
+                    msg.text = "请填写的您的详细收货地址";
+                    this.$vux.toast.show(msg);
+                    return;
+                }
+                let _self = this;
+                let area = this.$http.jsonp(Api.add_address(this.user_name, this.area[this.area.length - 1], this.address, this.mobile)).then(function (res) {
+                    this.$store.commit('updateLoadingStatus', {isLoading: true});
+                    if(res.status !== 200 || res.body.code !== 200) {
+                        _self.$vux.toast.show({
+                            type: 'text',
+                            text: "网络错误",
+                            position: 'middle',
+                            width: '400px'
+                        });
+                    }
+                    else{
+                        _self.$vux.toast.show({
+                            type: 'text',
+                            text: '提交成功',
+                            position: 'middle',
+                            width: '400px'
+                        });
+                    }
+                    _self.$http.jsonp(Api.set_default_address(res.body.datas["address_id"], this.is_default ? 1 : 0)).then(function (ress) {
+                        this.$store.commit('updateLoadingStatus', {isLoading: false});
+                        this.$router.go(-1);
+                    });
+                });
+            }
+        }
+    }
+</script>
+<style scoped>
+    .address_item {
+        padding-top: 92px;
+        background: #F7F7F7;
+    }
+
+    .submit {
+        width: 100%;
+        margin: 0 auto;
+        background: transparent;
+        position: fixed;
+        bottom: 0;
+        left: 0;
+    }
+
+    .submit_btn {
+        width: 100%;
+        margin: 0 auto;
+    }
+
+    .set_default {
+        background: #F7F7F7;
+        padding: 40px;
+    }
+
+    .weui-icon-circle {
+        font-size: 35px;
+    }
+
+    .weui-icon-success {
+        font-size: 35px;
+    }
+
+    .vux-cell-box > div {
+        padding: 0;
+    }
+</style>

+ 117 - 0
src/components/address/addressList.vue

@@ -0,0 +1,117 @@
+<script>
+    import {XHeader, Flexbox, FlexboxItem} from 'vux'
+    import Api from "../../lib/api"
+    import EditIcon from "../../assets/address_edit_unselected.png"
+    export default {
+        created(){
+            let _self = this;
+            this.$http.jsonp(Api.addressList()).then(function (res) {
+                _self.datas = res.body.datas.address_list;
+                this.$store.commit('updateLoadingStatus', {isLoading: false});
+            })
+        },
+
+        data(){
+            return {
+                datas: []
+            }
+        },
+        methods: {
+            link_add_address(){
+                this.$router.push('/add_address');
+            },
+            check_address(name,tel,address){
+                let address_name = name;
+                let address_tel = tel;
+                let user_address = address;
+                if (this.$route.query.hasOwnProperty("need_goBack")) {
+                    this.$store.commit('update_address', {
+                        'name': address_name,
+                        'mobile': address_tel,
+                        'address': user_address
+                    });
+                    this.$router.go(-1);
+                }
+            }
+        },
+        render(){
+            const list = this.datas.map((item) => {
+                let defaultAdd = ""
+                if (item.is_default == 1) {
+                    defaultAdd = <p class="default_address one_line">[默认地址]</p>;
+                }
+                return <div class="vux-1px-b" onClick={()=>this.check_address(item.true_name,item.mob_phone,item.area_info+item.address)}>
+                    <Flexbox align="stretch" gutter={0}>
+                        <FlexboxItem span={11} class="address_left">
+                            <Flexbox orient="vertical">
+                                <FlexboxItem>
+                                    <p>{item.true_name}&nbsp;{item.mob_phone} </p>
+                                </FlexboxItem>
+                                <FlexboxItem align-self="stretch">
+                                    {defaultAdd}<p class="one_line">
+                                    {item.area_info}{item.address}</p>
+                                </FlexboxItem>
+                            </Flexbox>
+                        </FlexboxItem>
+                    </Flexbox>
+                </div>
+            });
+            return (
+                    <div>
+                        <XHeader title='收货地址' leftOptions={{backText: ""}}/>
+                        <div class="list_container padding_lr">
+                            {list}
+                        </div>
+                        <div class='add_btn' onClick={() => this.link_add_address()}>添加地址</div>
+                    </div>
+            )
+        }
+    }
+</script>
+<style scpoed>
+    .list_container {
+        padding-top: 92px;
+    }
+
+    .default_address {
+        color: red;
+    }
+
+    .padding_lr {
+        padding-left: 20px;
+        padding-right: 20px;
+    }
+
+    .one_line {
+        display: inline;
+        margin-right: 20px;
+    }
+
+    .address_left {
+        padding-top: 20px;
+        padding-bottom: 20px;
+    }
+
+    .address_right {
+        width: 100%;
+        height: 100%;
+    }
+
+    .icon_edit {
+        height: 45px;
+        width: 36px;
+        margin: 0 auto;
+    }
+
+    .add_btn {
+        position: fixed;
+        bottom: 0;
+        left: 0;
+        width: 100%;
+        color: #fff;
+        background: #EB4E4F;
+        line-height: 92px;
+        text-align: center;
+        font-size: 30px;
+    }
+</style>

+ 58 - 0
src/components/blocks/GridList.vue

@@ -0,0 +1,58 @@
+<script>
+    import {Scroller} from 'vux'
+    import BlockUtil from './util/blockUtil'
+
+    function getDividerWidth(special) {
+        let spaceCount;
+        if (special.has_margin) {
+            spaceCount = special.items.length + 1;
+        } else {
+            spaceCount = special.items.length - 1;
+        }
+        let childWidthSum = 0;
+        special.items.forEach(function (item) {
+            childWidthSum += item.width;
+        });
+        let space = (1080 - childWidthSum) / spaceCount;
+        return space*750/1080;
+    }
+
+    export default {
+        components: {
+            Scroller,
+        },
+        props: ['special', 'proxy'],
+        render(createElement){
+
+            let special = this.special;
+            // padding
+            let padding = getDividerWidth(special);
+            let scale = 750 / 1080;
+            //渲染
+            let proxy = this.proxy;
+            let ret = [];
+            let hasMargin = special.has_margin;
+
+            let pos = 0;
+            this.special.items.forEach(function (item) {
+                ret.push(BlockUtil.createBlockItemComp(createElement, special.scale, item, proxy, {
+                    "hasMargin": hasMargin,
+                    "space": padding,
+                    "pos": pos,
+                    "width": item.width * scale,
+                    "level":2
+                }));
+                pos++;
+            });
+
+
+           return createElement(
+                "div",{},
+                ret);
+        }
+    }
+
+</script>
+<style scoped>
+
+</style>

+ 55 - 0
src/components/blocks/HomeGrid.vue

@@ -0,0 +1,55 @@
+<script>
+    import {Flexbox, FlexboxItem} from 'vux'
+    import BlockUtil from "./util/blockUtil"
+    export default {
+        data(){
+            return {}
+        },
+        props: [
+            "special",
+            "proxy"
+        ],
+        render(createElement){
+            let childs = [];
+            let proxy = this.proxy;
+            this.special.items.map(function (item) {
+                let comp = BlockUtil.createBlockItemComp(createElement, "", item, proxy, "");
+                let gridItem = createElement(FlexboxItem, {
+                    props: {
+                        span: 1 / 3
+                    },
+                    'class':{
+                        'vux-1px-l':true,
+                        'vux-1px-b':true,
+                        'brand_list':true
+                    }
+                }, [comp]);
+                childs.push(gridItem);
+            });
+
+            let grid = createElement(Flexbox, {
+                props: {
+                    gutter:0,
+                    wrap: "wrap"
+                }
+            }, childs);
+            return createElement("div", {}, [grid]);
+        },
+        components: {}
+    }
+</script>
+<style>
+    .brand_list:last-child:before {
+        content: " ";
+        position: absolute;
+        left: 100%;
+        top: 0;
+        width: 1px;
+        border-left: 1px solid #C7C7C7;
+        color: #C7C7C7;
+        -webkit-transform-origin: 0 0;
+        transform-origin: 0 0;
+        -webkit-transform: scaleX(0.5);
+        transform: scaleX(0.5);
+    }
+</style>

+ 66 - 0
src/components/blocks/Image.vue

@@ -0,0 +1,66 @@
+<script>
+    import {XImg} from 'vux'
+    import BlockUtil from './util/blockUtil'
+    import Default from '../../assets/default_img.jpg'
+    export default {
+        data(){
+            return {}
+        },
+        props: [
+            "item",
+            "proxy",
+            "attrs",
+            "scale"
+        ],
+        render(createElement){
+            let item = this.item;
+            let router = this.$router;
+            let proxy = this.proxy;
+            let width = '100%';
+            let display = "block";
+
+            if (this.attrs.width) {
+                width = this.attrs.width;
+                display = "inline-block";
+            }
+            let margin;
+            if (this.attrs.pos === 0) {
+                if (this.attrs.hasMargin) {
+                    margin = this.attrs.space + "px";
+                } else {
+                    margin = 0;
+                }
+            } else {
+                margin = this.attrs.space + "px";
+            }
+
+            return createElement(XImg, {
+                    props: {
+                        src: item.image,
+                        defaultSrc: Default
+                    },
+                    style: {
+                        width: width + "px",
+                        height: "auto",
+                        marginLeft: margin,
+                        display: display
+                    },
+                    nativeOn: {
+                        click()
+                        {
+                            BlockUtil.blockClick(router, item, proxy, false)
+                        }
+                    }
+
+                },
+                []
+            )
+                ;
+        },
+        components: {
+            XImg
+        }
+    }
+</script>
+<style>
+</style>

+ 104 - 0
src/components/blocks/block_list.vue

@@ -0,0 +1,104 @@
+<script>
+    import Proxy from './util/proxy.js'
+    import BlockUtil from './util/blockUtil.js'
+    import {LoadMore} from 'vux'
+    export default {
+        props: ['datasLink', "hot_id", "brand_id", "keyword", "special_id"],
+        watch: {
+            datasLink(val, oldVal){
+                if (val !== oldVal) {
+                    let dataMap = this.history.dataMap;
+                    //cache old data
+                    dataMap.set(oldVal, {
+                        proxy: this.proxy,
+                        list: this.special_list,
+                        y: document.body.scrollTop
+                    });
+                    //restore data
+                    if (dataMap.has(val)) {
+                        let data = dataMap.get(val)
+                        this.proxy = data.proxy
+                        this.special_list = data.list
+                        let pos = data.y;
+                        setTimeout(() => {
+                            document.body.scrollTop = pos;
+                        }, 100);
+                    } else {
+                        this.getDatas();
+                    }
+                }
+            }
+        },
+        created: function () {
+            this.getDatas()
+        },
+        methods: {
+            getDatas()
+            {
+                this.$store.commit('updateLoadingStatus', {isLoading: true});
+                let _self = this;
+                this.$http.jsonp(this.datasLink).then(function (res) {
+                    let body = JSON.parse(res.bodyText);
+                    _self.special_list = body.datas['special_list'];
+                    _self.proxy = new Proxy(body.datas);
+                    _self.proxy.setTag("SPECIAL_ID", this.special_id);
+                    _self.proxy.setTag("HOT_ID", this.hot_id);
+                    _self.proxy.setTag("BRAND_ID", this.brand_id);
+                    _self.proxy.setTag("WORDS", this.keyword);
+                    this.$store.commit('updateLoadingStatus', {isLoading: false});
+                },res => {
+                    this.$vux.toast.show({
+                        type:'text',
+                        text: "网络错误",
+                        position:'middle'
+                    });
+                    this.$store.commit('updateLoadingStatus', {isLoading: false});
+                })
+            }
+        },
+        data()
+        {
+            return {
+                special_list: [],
+                proxy: {},
+                history: {
+                    dataMap: new Map(),
+                }
+            }
+        }
+        ,
+        render(createElement)
+        {
+            let _self = this;
+            let specialArray = [];
+
+            if (_self.special_list == null || !_self.special_list) {
+                let loadMore = createElement(LoadMore, {
+                    props: {
+                        showLoading: false,
+                        tip: "暂无数据"
+                    }
+                }, []);
+                return createElement('div', {}, [loadMore]);
+            }
+
+
+            _self.special_list.map(function (special) {
+                let comp = BlockUtil.createBlockComp(createElement, special, _self.proxy);
+                if (comp) {
+                    specialArray.push(comp);
+                }
+            });
+            return createElement('div', {}, specialArray);
+        }
+    }
+</script>
+
+<style>
+    img {
+        vertical-align: middle;
+        width: 100%;
+        height: auto;
+        display: block;
+    }
+</style>

+ 29 - 0
src/components/blocks/brand.vue

@@ -0,0 +1,29 @@
+<script>
+    import {GridItem, XImg} from 'vux'
+    import BlockUtil from "./util/blockUtil"
+    export default {
+        props: [
+            "item",
+            "proxy",
+        ],
+        render(createElement){
+            let image = this.item.image;
+            let router = this.$router;
+            let proxy = this.proxy;
+            let item = this.item;
+            return createElement(XImg, {
+                props: {
+                    src: image,
+                },
+                nativeOn: {
+                    click()
+                    {
+                        BlockUtil.blockClick(router, item, proxy, false)
+                    }
+                }
+            }, []);
+        }
+    }
+</script>
+<style>
+</style>

+ 42 - 0
src/components/blocks/divider.vue

@@ -0,0 +1,42 @@
+<script>
+    import {XImg} from 'vux'
+    export default {
+        data(){
+            return {}
+        },
+        props: [
+            "special",
+            "proxy"
+        ],
+
+        render(createElement){
+            let special = this.special;
+            let height = document.body.clientWidth / special.scale + "px";
+            let img;
+            if (special.bg_type == "image") {
+                img = createElement('img', {
+                    attrs: {
+                        src: special.bg_data,
+                    },
+                    style: {
+                        height: height
+                    }
+
+                }, []);
+            } else {
+                img = createElement('div', {
+                    style: {
+                        height: height,
+                        backgroundColor: special.bg_data
+                    }
+
+                }, []);
+            }
+            return img;
+        }
+        ,
+        components: {
+            XImg
+        }
+    }
+</script>

+ 21 - 0
src/components/blocks/empty_bock.vue

@@ -0,0 +1,21 @@
+<script>
+    import BlockUtil from "./util/blockUtil"
+    export default {
+        data(){
+            return {}
+        },
+        props: [
+            "special",
+            "proxy"
+        ],
+        render(createElement){
+            let proxy = this.proxy;
+            let scale = this.special.scale;
+            let ret = [];
+            this.special.items.forEach(function (item) {
+                ret.push(BlockUtil.createBlockItemComp(createElement, scale, item, proxy,''));
+            });
+            return createElement("div", {}, ret);
+        }
+    }
+</script>

+ 101 - 0
src/components/blocks/fcode.vue

@@ -0,0 +1,101 @@
+<script>
+    import {XHeader, Flexbox, FlexboxItem} from 'vux'
+    import {dateFormat} from 'vux'
+    import BlockUtil from './util/blockUtil'
+
+    export default {
+        computed: {},
+
+        props: [
+            "item",
+            "proxy",
+            "attrs",
+            "scale"
+        ],
+        render(){
+            let self = this;
+            const nativeClickHandler = () => {
+                BlockUtil.blockClick(self.$router, self.item, self.proxy, false)
+            };
+            let leftColor = "red";
+            let fcode = this.proxy.getFcode(this.item.show_data);
+            let goods = this.proxy.getSummery(fcode.goods_id);
+            let left_title = '';
+            switch (fcode.state) {
+                case 0:
+                    leftColor = "#ffa82d";
+                    left_title = "可使用";
+                    break;
+                case 1:
+                    leftColor = "#bfbfbf";
+                    left_title = "已用完";
+                    break;
+                case 2:
+                    leftColor = "#bfbfbf";
+                    left_title = "已过期";
+                    break;
+                case 3:
+                    leftColor = "#7e7e7e";
+                    left_title = '已锁定';
+                    break;
+            }
+            return (
+                    <div class="container">
+                        <div class="item" >
+                            <Flexbox gutter={0} nativeOnClick={() => nativeClickHandler()}>
+                                <FlexboxItem span={1}>
+                                    <p class="title" style={{backgroundColor: leftColor}}>{left_title}</p>
+                                </FlexboxItem>
+                                <FlexboxItem>
+                                    <Flexbox orient="vertical" class="right" gutter={0}>
+                                        <FlexboxItem >
+                                            <p style={{display: "inline-block"}}>{goods.goods_mobile_name}</p>
+                                            <p style={{textAlign: "inline-block", float: "right", marginRight: "20px"}}>
+                                                查看商品 ></p>
+                                        </FlexboxItem>
+                                        <FlexboxItem >
+                                            <p style={{padding:"6px 0 20px 0",borderBottom:"1px dashed #000"}}>{fcode.fcode}</p>
+                                        </FlexboxItem>
+                                        <FlexboxItem >
+                                            <p style={{paddingTop:'10px'}}>有效期至{dateFormat(new Date(fcode.usable_time*1000), 'YYYY.MM.DD')}</p>
+                                        </FlexboxItem>
+                                    </Flexbox>
+                                </FlexboxItem>
+                            </Flexbox>
+                        </div>
+
+                    </div>
+            )
+        }
+    }
+</script>
+<style scoped>
+    .container {
+        background-color: #f7f7f7;
+    }
+
+    .item {
+        margin: 0 20px;
+        background-color: white;
+        border-radius: 5px;
+    }
+
+    .title {
+        width: 50px;
+        font-size: 20px;
+        box-sizing: border-box;
+        padding: 40px 15px;
+        color: white;
+        border-radius: 5px;
+    }
+
+    /*.right {*/
+        /*margin-left: 20px;*/
+    /*}*/
+
+    .top_title {
+        display: inline-block;
+        text-align: right;
+        float: right;
+    }
+</style>

+ 189 - 0
src/components/blocks/goods_item.vue

@@ -0,0 +1,189 @@
+<template>
+    <div class="goods_box">
+        <router-link :to="{ path: '/goods_detail', query: { goods_id: summery['goods_id'] }}">
+            <div class="goods">
+                <div class="goods_img">
+                    <x-img :src="item.image" :default-src="defaultImg"></x-img>
+                </div>
+                <div class="goods_msg">
+                    <p class="text_left goods_name">{{summery["goods_jingle"]}}</p>
+                    <p class="text_left desc">{{item.title}}</p>
+
+                    <div class="price_box" v-if="summery['act_type'] == 0">
+                        <div class="f_left">
+                            <p class="text_left desc">专柜价{{summery["goods_price"]}}</p>
+                            <p class="text_left bonus_price"><span class="bonus_icon"></span>{{summery["bonus_price"]}}</p>
+                        </div>
+                        <div class="f_right discount">
+                            <p class="discounts_box"><span class="discounts">{{discount}}</span></p>
+                            <p class="conserve">立省{{conserve}}元</p>
+                        </div>
+                    </div>
+
+                    <div class="price_box" v-if="summery['act_type'] == 1">
+                        <p class="text_right desc">专柜价{{summery["goods_price"]}}</p>
+                        <div>
+                            <span class="f_left desc" style="margin-top: 20px;">仅剩{{grouptime}}天</span>
+                            <div class="f_right">
+                                <span class="desc groupbuy_label">抢购特价</span>
+                                <span class="bonus_price">¥{{summery["goods_promotion_price"]}}</span>
+                            </div>
+                        </div>
+                    </div>
+
+                    <div class="badge" v-if="summery['act_type'] == 1" style="background:#FF4E4E;">抢</div>
+                    <div class="badge" v-if="summery['is_new']&&summery['act_type'] == 0" style="background:#3CB638;">新</div>
+                    <div class="badge" v-if="summery['act_type'] == 2" style="background:#FF4E4E;">限</div>
+                </div>
+            </div>
+        </router-link>
+    </div>
+</template>
+<script>
+    import { XImg } from 'vux'
+    import DefaultImg from '../../assets/default_img.jpg'
+    export default {
+        data () {
+            return {
+                summery:this.proxy.getSummery(this.item.data),
+                groupbuy:this.getGroupbuy,
+                defaultImg:DefaultImg
+            }
+        },
+        components:{
+            XImg
+        },
+        props:['item','proxy'],
+        computed:{
+            getgroupbuy(){
+                   return this.proxy.getGroupbuy(this.summery["act_id"]);
+            },
+            grouptime(){
+                let groupbuygoods = this.proxy.getGroupbuy(this.summery["act_id"]);
+                let endtime = groupbuygoods["end_time"];
+                let localtime = new Date().getTime()/1000;
+                let surplustime = parseInt((endtime-localtime)/60/60/24);
+                return surplustime;
+            },
+            discount(){
+                let discount;
+                if(this.summery["bonus_price"] / this.summery["goods_price"]*10 == 10) {
+                    discount = "无折扣";
+                }
+                else {
+                    let num = this.summery["bonus_price"] / this.summery["goods_price"]*10;
+                    discount = num.toFixed(2)+'折';
+                }
+                return discount;
+            },
+            conserve(){
+                let num = this.summery["goods_price"]-this.summery["bonus_price"];
+                return num.toFixed(2);
+            }
+        }
+    }
+</script>
+<style>
+    .goods_box {
+        width: 45.5%;
+        position: relative;
+        margin-top: 2.5%;
+        background: #fff;
+        margin-left: 3%;
+    }
+    .goods .conserve {
+        color: #fff;
+        font-size: 18px;
+        padding:4px;
+        border-bottom-right-radius: 11px;
+        border-bottom-left-radius: 11px;
+    }
+    .goods .discounts {
+        font-size: 28px;
+        color: #FF4E4E;
+        vertical-align: middle;
+        line-height: 1.3;
+    }
+    .goods .discounts_box {
+        font-size: 18px;
+        color: #FF4E4E;;
+        text-align: center;
+        background: #fff;
+        border-top-right-radius: 9px;
+        border-top-left-radius: 9px;
+    }
+    .goods .goods_msg {
+        padding: 12px;
+    }
+    .goods .discount {
+        border: 1px solid #FF4E4E;
+        margin-right: 4px;
+        background: #FF4E4E;
+        border-radius: 10px;
+        margin-top: 6px;
+    }
+    .price_box {
+        margin-top: 15px;
+        overflow: hidden;
+    }
+    .goods .badge {
+        position: absolute;
+        top: 22px;
+        right: 22px;
+        border-radius: 100px;
+        padding: 6px 6px;
+        color: #fff;
+        font-size: 22px;
+        width: 30px;
+        height: 30px;
+        text-align: center;
+        line-height: 30px;
+    }
+    .goods_name {
+        font-size: 22px;
+        white-space:nowrap;
+        text-overflow:ellipsis;
+        -o-text-overflow:ellipsis;
+        overflow:hidden;
+    }
+    .desc {
+        font-size: 20px;
+        white-space:nowrap;
+        text-overflow:ellipsis;
+        -o-text-overflow:ellipsis;
+        overflow:hidden;
+        color: #7E7E7E;
+    }
+    .f_left {
+        float: left;
+    }
+    .f_right {
+        float: right;
+    }
+    .goods_img {
+        font-size: 0;
+    }
+    .goods img {
+        width: 100%;
+        max-height: 340px;
+        min-height: 340px;
+    }
+    .bonus_icon {
+        display: inline-block;
+        width: 25px;
+        height: 32px;
+        background: url("../../assets/small_bonus_icon.png") no-repeat;
+        background-size: 100%;
+        margin-right: 4px;
+    }
+    .bonus_price {
+        font-size: 38px;
+        color: #FF4E4E;
+    }
+    .text_right {
+        text-align: right;
+    }
+    .groupbuy_label {
+        color: #FF4E4E;
+    }
+</style>

+ 32 - 0
src/components/blocks/goods_list.vue

@@ -0,0 +1,32 @@
+<template>
+    <div>
+        <titles :special="special"></titles>
+        <div class="goods_items">
+            <goods-item v-for="goods in special.items" :key="goods" :item="goods" :proxy="proxy"></goods-item>
+        </div>
+    </div>
+</template>
+
+<script>
+    import GoodsItem from './goods_item.vue'
+    import Titles from './title.vue'
+    export default {
+        props : ['special','proxy'],
+        components:{
+            GoodsItem,
+            Titles
+        }
+    }
+</script>
+
+
+<style>
+    .goods_items {
+        display: flex;
+        flex-flow: row wrap;
+        color: #000;
+        background: #F7F7F7;
+        margin-top: -2%;
+    }
+
+</style>

+ 150 - 0
src/components/blocks/goods_top.vue

@@ -0,0 +1,150 @@
+<template>
+    <div style="position: relative;">
+        <router-link :to="{ path: '/goods_detail', query: { goods_id: goods['goods_id'] }}">
+            <div class="goods">
+                <flexbox>
+                    <flexbox-item>
+                        <img :src="item.image" style="width: 285px;max-height:285px;min-height: 285px;margin-left: 25px;">
+                    </flexbox-item>
+                    <flexbox-item style="text-align: left;">
+                        <p class="top_goods_title">{{item.title}}</p>
+                        <div class="stars">
+                            <x-icon type="ios-star" size="30" v-for="i in stars" style="color: red;"></x-icon>
+                        </div>
+                        <div class="price_box" v-if="goods['act_type'] == 0">
+                            <div class="f_left">
+                                <p class="text_left desc">专柜价{{goods["goods_price"]}}</p>
+                                <p class="text_left bonus_price"><span class="bonus_icon"></span>{{goods["bonus_price"]}}</p>
+                            </div>
+                            <div class="f_right discount">
+                                <p class="discounts_box"><span class="discounts">{{discount}}</span></p>
+                                <p class="conserve">立省{{(goods["goods_price"] - goods["bonus_price"]).toFixed(2)}}元</p>
+                            </div>
+                        </div>
+                        <div class="price_box" v-if="goods['act_type'] == 1">
+                            <p class="text_right desc">专柜价{{goods["goods_price"]}}</p>
+                            <div>
+                                <span class="f_left desc" style="margin-top: 20px;">仅剩{{grouptime}}天</span>
+                                <div class="f_right">
+                                    <span class="desc groupbuy_label">抢购特价</span>
+                                    <span class="bonus_price">¥{{goods["goods_promotion_price"]}}</span>
+                                </div>
+                            </div>
+                        </div>
+                    </flexbox-item>
+                </flexbox>
+                <div class="letter_info">上榜理由:<span>{{attrs.desc}}</span></div>
+            </div>
+            <div class="top_bg"><img :src="attrs.image"></div>
+        </router-link>
+    </div>
+</template>
+<script>
+    import { Flexbox, FlexboxItem} from 'vux'
+    export default {
+        data () {
+            return {}
+        },
+        props:['item','proxy'],
+        created(){
+
+        },
+        computed:{
+            attrs(){
+               let attrArray = this.item.reserved.split('#');
+               let attrs = {};
+               for(let i = 0;i<attrArray.length;i++) {
+                   let key = decodeURIComponent(attrArray[i].match(/(\S*)=/)[1]);
+                   let val = decodeURIComponent(attrArray[i].match(/=(\S*)/)[1]);
+                   attrs[key] = val;
+               }
+                return attrs;
+            },
+            goods(){
+                return this.proxy.getSummery(this.item.data);
+            },
+            discount(){
+                let discount;
+                if(this.goods.act_type) {
+                    if(this.goods["goods_promotion_price"] / this.goods["goods_price"]*10 == 10) {
+                        discount = "无折扣";
+                    }
+                    else {
+                        let num = this.goods["goods_promotion_price"] / this.goods["goods_price"]*10;
+                        discount = num.toFixed(2)+'折';
+                    }
+                }
+                else {
+                    if(this.goods["bonus_price"] / this.goods["goods_price"]*10 == 10) {
+                        discount = "无折扣";
+                    }
+                    else {
+                        let num = this.goods["bonus_price"] / this.goods["goods_price"]*10;
+                        discount = num.toFixed(2)+'折';
+                    }
+                }
+                return discount;
+            },
+            stars(){
+                return parseInt(this.attrs.stars);
+            },
+            grouptime(){
+                let groupbuygoods = this.proxy.getGroupbuy(this.goods["act_id"]);
+                let endtime = groupbuygoods["end_time"];
+                let localtime = new Date().getTime()/1000;
+                let surplustime = parseInt((endtime-localtime)/60/60/24);
+                return surplustime;
+            },
+        },
+        components: {
+            Flexbox,
+            FlexboxItem
+        }
+    }
+</script>
+<style scoped>
+    .top_bg {
+        position: absolute;
+        top: 10px;
+        left: 25px;
+        width: 67px;
+        height: 25px;
+    }
+    .letter_info {
+        position: relative;
+        width: 660px;
+        margin:0 auto;
+        padding: 20px 0;
+        text-align: justify;
+    }
+    .letter_info:before {
+        content: '';
+        position: absolute;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 0;
+        border-bottom: 1px dashed #c1c1c1;
+        z-index: 100;
+    }
+    .letter_info span {
+        color: #bebebe;
+    }
+    .top_goods_title {
+        font-size: 28px;
+        width: 100%;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        overflow: hidden;
+    }
+    .price_box {
+        margin-right: 50px;
+        margin-top: 30px;
+    }
+    .stars {
+        margin-top: 10px;
+    }
+   .vux-x-icon {
+       color: red;
+   }
+</style>

+ 42 - 0
src/components/blocks/home2.vue

@@ -0,0 +1,42 @@
+<template>
+  <div>
+    <flexbox :gutter="1">
+      <flexbox-item :class="{'vux-1px-r':true}"><router-link :to="blockList[0].url"><img :src="blockList[0].image"/></router-link></flexbox-item>
+      <flexbox-item>
+        <flexbox orient="vertical" :gutter="1">
+          <flexbox-item :class="{'vux-1px-b':true}"><router-link :to="blockList[1].url"><img :src="blockList[1].image"/></router-link></flexbox-item>
+          <flexbox-item><router-link :to="blockList[2].url"><img :src="blockList[2].image"/></router-link></flexbox-item>
+        </flexbox>
+      </flexbox-item>
+    </flexbox>
+  </div>
+</template>
+
+<script>
+  import { Flexbox, FlexboxItem } from 'vux'
+  import BlockUtil from './util/blockUtil'
+  export default {
+      props: ['special', 'proxy'],
+      components: {
+          Flexbox,
+          FlexboxItem
+      },
+      computed:{
+          blockList(){
+              let list=[];
+              let _self = this;
+              for (let i = 0;i<this.special.items.length;i++) {
+                  let item = {};
+                  item.image = this.special.items[i].image;
+                  item.url = BlockUtil.blockClick('',this.special.items[i],_self.proxy,true);
+                  list.push(item);
+              }
+              return list;
+          }
+      }
+  }
+</script>
+
+<style>
+
+</style>

+ 75 - 0
src/components/blocks/horizongoods_item.vue

@@ -0,0 +1,75 @@
+<template>
+    <div class="horizongoods_item" :style="{width:itemWidth,marginLeft:leftMargin}">
+        <router-link :to="{ path: '/goods_detail', query: { goods_id: proxy.getSummery(item.data)['goods_id'] }}">
+            <img :src="item.image">
+            <div>
+                <p class="title">{{item.title}}</p>
+                <p>
+                    <span class="bonus_icon"></span>
+                    <span class="bouns_price">{{goods_price}}</span>
+                </p>
+            </div>
+        </router-link>
+    </div>
+</template>
+<script>
+    export default {
+        data () {
+            return {}
+        },
+        props: ['item', 'proxy', "attrs"],
+        computed: {
+            goods_price(){
+                let act_type = this.proxy.getSummery(this.item.data).act_type;
+                if(act_type == 1) {
+                   return this.proxy.getSummery(this.item.data)["goods_promotion_price"];
+                }
+                else {
+                    return this.proxy.getSummery(this.item.data)["bonus_price"];
+                }
+            },
+            itemWidth(){
+                return this.attrs.width + 'px'
+            },
+            leftMargin(){
+                if (this.attrs.pos == 0) {
+                    if (this.attrs.hasMargin) {
+                        return this.attrs.space + "px";
+                    } else {
+                        return 0;
+                    }
+                } else {
+                    return this.attrs.space + "px";
+                }
+            }
+        }
+    }
+</script>
+<style scoped>
+    .bonus_icon {
+        display: inline-block;
+        width: 20px;
+        height: 24px;
+        background: url("../../assets/small_bonus_icon.png") no-repeat;
+        background-size: 100%;
+        margin-right: 4px;
+    }
+
+    .horizongoods_item {
+        display: inline-block;
+        float: left;
+    }
+
+    .horizongoods_item .title {
+        font-size: 26px;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        -o-text-overflow: ellipsis;
+        overflow: hidden;
+    }
+
+    .bouns_price {
+        color: #FF4E4E;
+        font-size: 28px;
+    }
+</style>

+ 121 - 0
src/components/blocks/horizongoods_list.vue

@@ -0,0 +1,121 @@
+<script>
+    //    import {Scroller} from 'vux'
+    import HorizonGoodsItem from './horizongoods_item.vue'
+    import BlockUtil from './util/blockUtil'
+    import Scroller from '../comps/scroller.vue'
+
+    function getMarginBottom(contentItem) {
+        switch (contentItem.show_type) {
+            case "goods_simple":
+                return 132;          //1080 * 94 / 750 ? 375
+            case "image_text":
+                return 75;           //1080 * 52 / 750
+        }
+        return 0;
+    }
+
+    function getDividerWidth(special) {
+        let padding = special.items.length + 1;
+        return 16 * padding;
+    }
+
+    export default {
+        components: {
+            Scroller,
+            HorizonGoodsItem
+        },
+        props: ['special', 'proxy'],
+        data(){
+            return {
+                widths: []
+            }
+        },
+        created(){
+            this.calculateChildRect();
+        },
+        watch: {
+            special(){
+                this.calculateChildRect()
+            }
+        },
+        methods: {
+            calculateChildRect(){
+                let _self = this;
+                //重新计算每个item的宽高
+                let special = _self.special;
+                let maxMarginToBottom = 0;
+                let contentItems = special.items;
+                contentItems.forEach(function (contentItem) {
+                    let marginBottom = getMarginBottom(contentItem);
+                    if (marginBottom > maxMarginToBottom) {
+                        maxMarginToBottom = marginBottom;
+                    }
+                });
+
+                let minImageHeight = 1080 / special.scale - maxMarginToBottom;
+                contentItems.map(function (contentItem, index) {
+                    let scale = contentItem.width / contentItem.height;
+                    _self.widths[index] = minImageHeight * scale + 0.5;
+                });
+            }
+        },
+        render(createElement){
+
+            let _self = this;
+
+            let special = _self.special;
+            // padding
+            let padding = getDividerWidth(special);
+
+            let items_length = _self.special.items.length;
+            let scale = 750 / 1080;
+            let childSumWidth = 0;
+
+            for (let i = 0; i < items_length; i++) {
+                childSumWidth += parseFloat(_self.widths[i]);
+            }
+            childSumWidth = childSumWidth * scale;
+            let parentWidth = childSumWidth + padding + 'px';
+            //左右边距
+
+            //渲染
+            let proxy = _self.proxy;
+            let ret = [];
+            let hasMargin = true;
+            let space = 16;
+            let pos = 0;
+
+            this.special.items.forEach(function (item) {
+                ret.push(BlockUtil.createBlockItemComp(createElement, special.scale, item, proxy, {
+                    "hasMargin": hasMargin,
+                    "space": space,
+                    "pos": pos,
+                    "width": _self.widths[pos] * scale
+                }));
+                pos++;
+            });
+
+            let div = createElement(
+                "div",
+                {
+                    'class': {
+                        horizongoods: true
+                    },
+                    'style': {
+                        width: parentWidth,
+                        paddingLeft: 0,
+                        paddingRight: 0
+                    }
+                },
+                ret);
+
+            return createElement(Scroller, {}, [div]);
+        }
+    }
+
+</script>
+<style scoped>
+    .horizongoods {
+        position: relative;
+    }
+</style>

+ 50 - 0
src/components/blocks/hot_search.vue

@@ -0,0 +1,50 @@
+<template>
+    <div>
+        <titles :special="special"></titles>
+        <flexbox class="flex-box" wrap="wrap" :gutter="0">
+            <flexbox-item :span="1/4" v-for="item in special.items" :key="item">
+                <div @click="onItemClick($router,item,proxy)" class="flex-item"><p>{{item.title}}</p></div>
+            </flexbox-item>
+        </flexbox>
+    </div>
+</template>
+<script>
+    import {Flexbox, FlexboxItem} from 'vux'
+    import Titles from './title.vue'
+    import BlockUtil from './util/blockUtil'
+    export default {
+        props: [
+            "special",
+            "proxy"
+        ],
+        components: {
+            Flexbox,
+            FlexboxItem,
+            Titles
+        },
+        methods: {
+            onItemClick: (router, item, proxy) => {
+                BlockUtil.blockClick(router, item, proxy, false)
+            }
+        }
+    }
+</script>
+<style scoped>
+    .flex-box {
+        background: #f7f7f7;
+    }
+
+    .flex-item {
+        text-align: center;
+        color: #000;
+        background-color: #f7f7f7;
+        background-clip: padding-box;
+        font-size: 24px;
+        padding: 10px;
+    }
+
+    .flex-item p {
+        background: #fff;
+        border-radius: 5px;
+    }
+</style>

+ 73 - 0
src/components/blocks/search.vue

@@ -0,0 +1,73 @@
+<template>
+    <div class="search_box" @click="toSearch" >
+        <div class="search_logo"></div>
+        <div class="search">
+            <p><i class="weui-icon-search index_search_icon"></i>搜索商品 品牌 功效 规格</p>
+        </div>
+    </div>
+</template>
+<script>
+    import Actions from '../../lib/Actions'
+    export default {
+        props: ["isScroll"],
+        data () {
+            return {}
+        },
+        components: {},
+        methods: {
+            toSearch(){
+                this.$router.push(Actions.toSearch());
+            }
+        },
+        computed: {
+            windowScroll(){
+                if (this.isScroll) {
+                    return true;
+                }
+                else {
+                    return false;
+                }
+            }
+        }
+    }
+</script>
+<style scoped>
+    .search_box {
+        position: fixed;
+        top: 0;
+        left: 0;
+        width: 100%;
+        z-index: 999;
+        opacity: 1;
+        background: #fd4d51;
+        padding: 12px 0;
+    }
+
+    .search {
+        border-radius: 5px;
+        background: #fff;
+        height: 60px;
+        width: 520px;
+        margin: 0 auto;
+        float: right;
+        margin-right: 20px;
+    }
+
+    .search p {
+        line-height: 60px;
+        color: #666;
+        padding-left: 20px;
+    }
+
+    .index_search_icon {
+        color: #666;
+        margin-right: 10px;
+    }
+    .search_logo {
+        float: left;
+        height: 60px;
+        width: 176px;
+        margin-left: 20px;
+        background: url("../../assets/search_log.jpg") no-repeat center;
+    }
+</style>

+ 40 - 0
src/components/blocks/swiper_list.vue

@@ -0,0 +1,40 @@
+<template>
+    <div>
+        <swiper :list="getSwiperListData" :show-desc-mask="false" :height="scale" :loop="true" :auto="true"></swiper>
+    </div>
+</template>
+<script>
+    import { Swiper } from 'vux'
+    import BlockUtil from './util/blockUtil'
+    export default {
+        data () {
+            return {
+
+            }
+        },
+        props:['special','proxy'],
+        components: {
+            Swiper
+        },
+        computed: {
+            getSwiperListData() {
+                let SwiperList = [];
+                let items = this.special.items;
+                let _self = this;
+                for (let i = 0; i < items.length; i++) {
+                    let item = {};
+                    item['img'] = items[i].image;
+                    let link=BlockUtil.blockClick('',items[i],_self.proxy,true);
+                    item['url'] = link;
+                    SwiperList.push(item);
+                }
+                return SwiperList;
+            },
+            scale(){
+                return 750 / parseFloat(this.special.scale)+'px' ;
+            }
+        }
+    }
+</script>
+<style>
+</style>

+ 22 - 0
src/components/blocks/title.vue

@@ -0,0 +1,22 @@
+<template>
+    <div>
+        <h3 v-if="special.item_title!==''">{{special.item_title}}</h3>
+    </div>
+</template>
+<script >
+    export default {
+        props: [
+            "special",
+            "proxy"
+        ]
+    }
+</script>
+<style scoped>
+    h3{
+        padding-left: 20px;
+        padding-bottom: 5px;
+        color: #959595;
+        background: #F7F7F7;
+        font-weight: normal;
+    }
+</style>

+ 260 - 0
src/components/blocks/util/blockUtil.js

@@ -0,0 +1,260 @@
+/**
+ * Created by huanggang on 2017/4/27.
+ */
+import Actions from '../../../lib/Actions'
+import Home2 from '../home2.vue'
+import Divider from '../divider.vue'
+import Image from '../Image.vue'
+import EmptyBlock from '../empty_bock.vue'
+import  Webview from '../webview.vue'
+import Swiper from '../swiper_list.vue'
+import {base64} from 'vux'
+import GoodsList from '../goods_list.vue'
+import GoodsItem from '../goods_item.vue'
+import HorizonGoodsItem from '../horizongoods_item.vue'
+import HorizonGoodsList from '../horizongoods_list.vue'
+import GridList from '../GridList.vue'
+import Video from '../video.vue'
+import Homewords from '../hot_search.vue'
+import HomeGrid from '../HomeGrid.vue'
+import Brand from '../brand.vue'
+import FCode from '../fcode.vue'
+import GoodsTop from '../goods_top.vue'
+class BlockUtil {
+    static createBlockComp(createElement, special, proxy) {
+        let comp;
+        let params = {
+            props: {
+                special: special,
+                proxy: proxy
+            },
+        };
+        try {
+            switch (special['item_type']) {
+                case "adv_list" :
+                    comp = createElement(Swiper, params);
+                    break;
+                case "divider" :
+                    comp = createElement(Divider, params);
+                    break;
+                case "home2" :
+                    comp = createElement(Home2, params);
+                    break;
+                case "home1" :
+                case 'home5':
+                case 'home_type':
+                    comp = createElement(EmptyBlock, params);
+                    break;
+                case 'home_goods' :
+                    comp = createElement(GoodsList, params);
+                    break;
+                case "horizon":
+                case 'horizon_goods' :
+                    comp = createElement(HorizonGoodsList, params);
+                    break;
+                case 'home3' :
+                    comp = createElement(GridList, params);
+                    break;
+                case 'homewords' :
+                    comp = createElement(Homewords, params);
+                    break;
+                case "home_grid":
+                    comp = createElement(HomeGrid, params);
+                    break;
+            }
+        } catch (e) {
+            comp = createElement('div', '<p>创建失败</p>');
+        }
+        return comp;
+    }
+
+    static createBlockItemComp(createElement, scale, item, proxy, attrs) {
+        let comp;
+        let params = {
+            props: {
+                item: item,
+                proxy: proxy,
+                scale: scale,
+                attrs: attrs
+            }
+        };
+        let showType = "";
+        if (item.hasOwnProperty("show_type")) {
+            showType = item.show_type;
+        } else {
+            showType = item.type;
+        }
+        try {
+            switch (showType) {
+                case "image" :
+                    comp = createElement(Image, params);
+                    break;
+                case "webview" :
+                    comp = createElement(Webview, params);
+                    break;
+                case "goods" :
+                    comp = createElement(GoodsItem, params);
+                    break;
+                case "goods_simple" :
+                    comp = createElement(HorizonGoodsItem, params);
+                    break;
+                case 'video' :
+                    comp = createElement(Video, params);
+                    break;
+                case "brand":
+                    comp = createElement(Brand, params);
+                    break;
+                case "fcode":
+                    comp = createElement(FCode, params);
+                    break;
+                case "goods_top":
+                    comp = createElement(GoodsTop,params);
+            }
+        } catch (e) {
+            comp = createElement('div', '<p>创建失败</p>');
+        }
+        return comp;
+    }
+
+    static blockClick(router, item, proxy, is_return) {
+
+        let action_type = item.type;
+        let action_data = item.data;
+        let title = item.title ? item.title : "熊猫美妆";
+
+        if (!item || !proxy || !action_type || !action_data) {
+            return;
+        }
+
+        let special_id = proxy.getTag("SPECIAL_ID");
+        let hot_id = proxy.getTag("HOT_ID");
+        let brand_id = proxy.getTag("BRAND_ID");
+        let keyword = proxy.getTag("WORDS");
+        let path;
+        switch (action_type) {
+            case 'url':
+                path = Actions.getWebViewPath(title, action_data);
+                if (is_return) {
+                    return this.getBlockUrl(path);
+                } else {
+                    router.push(path);
+                }
+                break;
+            case 'goods':
+                path = Actions.getGoodsDetailPath(action_data);
+                if (is_return) {
+                    return this.getBlockUrl(path);
+                } else {
+                    router.push(path);
+                }
+                break;
+            case 'special':
+                path = Actions.getSpecialListPath(action_data, title);
+                if (is_return) {
+                    return this.getBlockUrl(path);
+                } else {
+                    router.push(path);
+                }
+                break;
+            case 'keyword':
+                path = Actions.getFilterBlockListPath(action_data, special_id, hot_id, brand_id, action_data);
+                if (is_return) {
+                    return this.getBlockUrl(path);
+                } else {
+                    router.push(path);
+                }
+                break;
+            case 'brand':
+                path = Actions.getFilterBlockListPath(title, special_id, hot_id, action_data, keyword);
+                if (is_return) {
+                    return this.getBlockUrl(path);
+                } else {
+                    router.push(path);
+                }
+                break;
+            case 'category':
+                path = Actions.getFilterBlockListPath(title, special_id, action_data, brand_id, keyword);
+                if (is_return) {
+                    return this.getBlockUrl(path);
+                } else {
+                    router.push(path);
+                }
+                break;
+            case 'appjump':
+                path = Actions.getSchemaPathPath(action_data);
+                if (is_return) {
+                    return this.getBlockUrl(path);
+                } else {
+                    router.push(path);
+                }
+                break;
+        }
+
+        return "";
+    }
+
+    static getBlockUrl(schema) {
+        if (typeof(schema.path) == "undefined") return;
+        let ret = schema.path;
+        if (schema.query) {
+            let g = 0;
+            for (let i in schema.query) {
+                if (g === 0) {
+                    let value = schema.query[i];
+                    if (i == 'url') {
+                        value = base64.encode(value)
+                    }
+                    ret += "?" + i + "=" + value;
+                } else {
+                    let value = schema.query[i];
+                    if (i == 'url') {
+                        value = base64.encode(value)
+                    }
+                    ret += "&" + i + "=" + value;
+                }
+                g++;
+            }
+        }
+        return ret;
+    }
+
+
+}
+export default BlockUtil
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 88 - 0
src/components/blocks/util/proxy.js

@@ -0,0 +1,88 @@
+class Proxy {
+
+    constructor(json) {
+        if (!json) {
+            throw new Error("data is error");
+        }
+        else {
+            this.datas = json;
+            this.cart_list = json.cart_list;
+            this.bundling_list = json.bundling;
+        }
+        this.tags = new Map();
+    }
+
+    setTag(key, value) {
+        this.tags.set(key, value);
+    }
+
+    getTag(key) {
+        return this.tags.get(key)
+    }
+
+
+    static getGoodsSwiperList(list) {
+        if (!Array.isArray(list)) {
+            return false;
+        }
+        else {
+            let SwiperList = [];
+            let items = list;
+            for (let i = 0; i < items.length; i++) {
+                let item = {};
+                item['img'] = items[i];
+                item['src'] = '#/';
+                SwiperList.push(item);
+            }
+            return SwiperList;
+        }
+    }
+
+    getCart(cart_id) {
+        for (let i = 0; i < this.cart_list.length; i++) {
+            if (this.cart_list[i].cart_id == cart_id)
+                return this.cart_list[i];
+        }
+    }
+
+    getBundling(bl_id) {
+        for (let i = 0; i < this.bundling_list.length; i++) {
+            if (this.bundling_list[i].bl_id == bl_id) {
+                return this.bundling_list[i];
+            }
+        }
+    }
+
+    getSummery(goods_id) {
+        for (let i = 0; i < this.datas.summary.length; i++) {
+            let summery = this.datas.summary[i];
+            if (summery['goods_id'] == goods_id) {
+                return summery;
+            }
+        }
+    }
+
+    getGroupbuy(groupbuy_id) {
+        for (let i = 0; i < this.datas.groupbuy.length; i++) {
+            let groupbuy = this.datas.groupbuy[i];
+            if (groupbuy['groupbuy_id'] == groupbuy_id) {
+                return groupbuy;
+            }
+        }
+    }
+
+    getFcode(fcode_id) {
+        for (let i = 0; i < this.datas.fcodes.length; i++) {
+            let fode = this.datas.fcodes[i];
+            if (fode['fcode_id'] == fcode_id) {
+                return fode;
+            }
+        }
+    }
+
+}
+
+export default Proxy
+
+
+

+ 30 - 0
src/components/blocks/video.vue

@@ -0,0 +1,30 @@
+<template>
+    <video-player  ref="videoPlayer" :options="playerOptions"></video-player>
+</template>
+<script>
+    export default {
+        props: [
+            "item",
+            "proxy",
+            "attrs"
+        ],
+        data() {
+            return {
+                playerOptions: {
+                    // component options
+                    start: 0,
+                    playsinline: false,
+                    // videojs options
+                    muted: false,
+                    language: 'en',
+                    playbackRates: [0.7, 1.0, 1.5, 2.0],
+                    sources: [{
+                        type: "video/mp4",
+                        src: this.item.data
+                    }],
+                    poster: this.item.image,
+                }
+            }
+        }
+    }
+</script>

+ 30 - 0
src/components/blocks/webview.vue

@@ -0,0 +1,30 @@
+<template>
+    <iframe :src="getLink" frameborder="0" width="100%" :height="getHeight" scrolling="no" id="iframe_webview"></iframe>
+</template>
+
+<script>
+    export default {
+        props: [
+            "item",
+            "proxy",
+            "scale"
+        ],
+        data () {
+            return {
+
+            }
+        },
+        computed: {
+            getLink(){
+                return this.item.data;
+            },
+            getHeight(){
+                return  document.body.clientWidth  / this.scale + "px";
+            }
+        }
+    }
+</script>
+
+<style>
+
+</style>

+ 25 - 0
src/components/comps/scroller.vue

@@ -0,0 +1,25 @@
+<template>
+    <div class="scroller_list ::-webkit-scrollbar">
+        <slot></slot>
+    </div>
+</template>
+<script>
+    export default {
+        data () {
+            return {}
+        },
+        components: {}
+    }
+</script>
+<style>
+    .scroller_list {
+        width: 100%;
+        overflow-y: hidden;
+        -webkit-overflow-scrolling: touch;
+        background: #fff;
+    }
+
+    .scroller_list::-webkit-scrollbar {
+        display: none;
+    }
+</style>

+ 234 - 0
src/components/confirm_order/confirm_order.vue

@@ -0,0 +1,234 @@
+<template>
+    <div v-if="datas!==''">
+        <x-header>确认订单</x-header>
+        <div class="check_order">
+            <div @click="link_address">
+                <flexbox style="background: #fff" v-if="datas.address !== null">
+                    <flexbox-item class="address_box"><span class="address_icon"></span></flexbox-item>
+                    <flexbox-item :span="4/5" style="padding: 24px 0;line-height: 50px;">
+                        <p>{{store_address?store_address.name:datas.address.true_name}}&nbsp;&nbsp;&nbsp;{{store_address?store_address.mobile:datas.address.mob_phone}}</p>
+                        <p>{{store_address?store_address.address:datas.address.area_info+datas.address.address}}</p>
+                    </flexbox-item>
+                </flexbox>
+                <div v-else>
+                    <p style="text-align: center;color:#EB4E4F; padding: 50px 0;background: #fff; font-size: 26px;"><icon type="warn"></icon>请填写收货人信息</p>
+                </div>
+            </div>
+            <group>
+                <x-input title="红包支付" class="weui-vcode" style="padding-top: 21px; padding-bottom: 21px;">
+                    <p slot="left"></p>
+                    <icon slot="right" type="success-circle" style="color: #EB4E4F"></icon>
+                </x-input>
+                <x-input title="微信支付" class="weui-vcode" style="padding-top: 21px; padding-bottom: 21px;">
+                    <icon slot="right" type="success-circle" style="color: #EB4E4F"></icon>
+                </x-input>
+            </group>
+            <flexbox style="margin-top: 0.71428571em;" v-for="goods in goods_list" :key="goods">
+                <flexbox-item style="background: #fff; padding:30px;" class="vux-1px-b">
+                    <flexbox>
+                        <flexbox-item :span="2/6">
+                            <img :src="goods.goods_image_url" style="width: 175px;height: 175px;">
+                        </flexbox-item>
+                        <flexbox-item :span="3/6" style="padding-left: 25px;">
+                            <p>{{goods.goods_mobile_name}}</p>
+                            <p style="margin: 20px 0 40px 0;" class="letter_pro">{{goods.goods_spec}}</p>
+                            <p><span class="letter_warn" style="margin-right: 10px; font-size: 30px;">¥{{goods.act_type == 0?goods.bonus_price:goods.goods_promotion_price}}</span><span class="letter_pro">专柜价{{goods.goods_price}}</span></p>
+                        </flexbox-item>
+                        <flexbox-item style="text-align: right;">
+                            <span class="letter_pro">x{{goods.goods_num}}</span>
+                        </flexbox-item>
+                    </flexbox>
+                </flexbox-item>
+            </flexbox>
+            <div style="background: #fff; padding:20px 0 20px 30px;" v-if="datas.payinfo.gap_desc"><span class="letter_pro">{{datas.payinfo.gap_desc}}</span></div>
+            <div class="entry">
+                <flexbox>
+                    <flexbox-item>
+                        <flexbox>
+                            <flexbox-item :span="4/5" style="line-height: 50px;">商品总价</flexbox-item>
+                            <flexbox-item :span="1/5" style="text-align: right"><span class="letter_warn">¥ {{datas.payinfo['goods_amount']}}</span></flexbox-item>
+                        </flexbox>
+                    </flexbox-item>
+                </flexbox>
+                <flexbox>
+                    <flexbox-item>
+                        <flexbox>
+                            <flexbox-item :span="4/5" style="line-height: 50px;">{{datas.payinfo['full_desc']}}</flexbox-item>
+                            <flexbox-item :span="1/5" style="text-align: right" v-if="datas.payinfo['full_desc']"><span class="letter_info">- ¥ {{datas.payinfo['full_discount']}}</span></flexbox-item>
+                        </flexbox>
+                    </flexbox-item>
+                </flexbox>
+                <flexbox>
+                    <flexbox-item>
+                        <flexbox v-for="bonus in datas.payinfo['bonus_rates']" :key="bonus">
+                            <flexbox-item :span="4/5" style="line-height: 50px;"><span class="bonus_bg"><span class="bonus_rate">{{bonus.rate}}%</span></span>红包抵扣</flexbox-item>
+                            <flexbox-item :span="1/5" style="text-align: right"><span class="letter_info">- ¥ {{bonus.total}}</span></flexbox-item>
+                        </flexbox>
+                    </flexbox-item>
+                </flexbox>
+                <flexbox>
+                    <flexbox-item>
+                        <flexbox>
+                            <flexbox-item :span="4/5" style="line-height: 50px;">运费</flexbox-item>
+                            <flexbox-item :span="1/5" style="text-align: right"><span class="letter_warn">+ ¥ {{datas.payinfo['freight']}}</span></flexbox-item>
+                        </flexbox>
+                    </flexbox-item>
+                </flexbox>
+            </div>
+        </div>
+        <div class="order_pay">
+            <div class="default_address" v-if="datas.address !== null">配送至:{{store_address?store_address.address:datas.address.area_info+datas.address.address}}</div>
+            <div class="just_buy">
+                <flexbox>
+                    <flexbox-item :span="4/6" style="text-align: center;line-height: 93px; font-size: 30px;">应付: <span class="letter_warn">¥ {{datas.payinfo['pay_cash_pred']}}</span></flexbox-item>
+                    <flexbox-item :span="2/6"  style="text-align: center;background:#EB4E4F;color: #fff;line-height: 93px; "><p>去付款</p></flexbox-item>
+                </flexbox>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+    import { XHeader,Flexbox,FlexboxItem,Group,CellBox,XSwitch,XInput,Icon} from 'vux'
+    import Api from '../../lib/api'
+    import Proxy from '../blocks/util/proxy'
+
+    export default {
+        created(){
+            this.$store.commit('updateLoadingStatus', {isLoading: true});
+            let goods_id = this.$route.query["goods_id"];
+            let goods_num = this.$route.query['num'];
+            let iscart = this.$route.query["iscart"];
+            let cart_id = this.$route.query["cart_id"]?this.$route.query["cart_id"]:'';
+            this.$http.jsonp(Api.confirm_order(iscart,cart_id,goods_id,goods_num)).then(function(res){
+                if(res.body.datas == null && res.body.code !== '200') {
+                    this.$vux.toast.show({
+                        type:'warn',
+                        text: res.body.message,
+                        position:'middle',
+                        width:'600px'
+                    });
+                    this.$router.go(-1);
+                }
+                else {
+                    this.datas = res.body['datas'];
+                    this.proxy = new Proxy(res.body['datas']);
+                    this.$store.commit('updateLoadingStatus', {isLoading: false});
+                }
+            })
+        },
+        data () {
+            return {
+                datas : '',
+                proxy:{},
+                aid:''
+            }
+        },
+        computed:{
+            default_address(){
+                let address_list = this.datas['address'];
+            },
+            goods_list(){
+                if(!this.datas)return;
+                let goods_list = this.datas.goods_list;
+                let list = [];
+                for(let i =0;i<goods_list.length;i++) {
+                    let goods_item = this.proxy.getSummery(goods_list[i].goods_id);
+                    goods_item.goods_num = goods_list[i].goods_num;
+                    list.push(goods_item);
+                }
+                return list;
+            },
+            store_address(){
+                let address;
+                if(this.$store.state.address['name']) {
+                    address = this.$store.state.address;
+                    return address;
+                }
+                else {
+                    return false;
+                }
+            }
+        },
+        methods:{
+            link_address(){
+                this.$router.push({path:'/address/list',query:{need_goBack:true}});
+            }
+        },
+        components: {
+            XHeader,
+            Flexbox,
+            FlexboxItem,
+            Group,
+            CellBox,
+            XSwitch,
+            XInput,
+            Icon
+        }
+    }
+</script>
+<style scoped>
+    #router_view {
+        background: #F7F7F7;
+    }
+    .address_box {
+        padding:24px 0 24px 40px;
+    }
+    .address_box p {
+        line-height: 50px;
+    }
+    .address_icon {
+        display: inline-block;
+        width: 34px;
+        height: 47px;
+        background: url("../../assets/address_icon.jpg") no-repeat;
+        background-size: 100% 100%;
+    }
+    .check_order {
+        padding-top: 92px;
+        padding-bottom: 133px;
+    }
+    .entry {
+        background: #fff;
+        margin-top: 0.71428571em;
+        padding: 30px;
+    }
+    .order_pay {
+        position: fixed;
+        bottom: 0;
+        left: 0;
+        width: 100%;
+    }
+    .just_buy {
+        height: 93px;
+        background: #fff;
+    }
+    .default_address {
+        font-size: 18px;
+        background: #ffeab8;
+        color: #f2bc73;
+        height: 40px;
+        line-height: 40px;
+        padding-left: 30px;
+    }
+    .bonus_bg {
+        background: #f2bb11;
+        padding: 5px;
+        margin-right: 5px;
+    }
+    .bonus_rate {
+        border: 1px dashed #fff;
+    }
+    .letter_pro {
+        color: #a7a7a7;
+    }
+    .letter_info {
+        color: #09BB07;
+    }
+    .letter_warn {
+        color: #EB4E4F;
+    }
+    .weui-icon-warn {
+        vertical-align: text-bottom;
+        font-size: 34px;
+    }
+</style>

+ 31 - 0
src/components/fcode/fcode_list.vue

@@ -0,0 +1,31 @@
+<script>
+    import BlockList from '../blocks/block_list.vue'
+    import api from '../../lib/api.js'
+    import {XHeader} from 'vux'
+
+    export default {
+        render(createElement)
+        {
+//            return createElement('div',{},[
+//                createElement(XHeader,{
+//                    title:'我的F码',
+//                    showBack:true
+//                }),
+//                createElement(BlockList,{
+//                    props:{
+//                        datasLink:api.getFCode()
+//                    }
+//                })
+//                ]
+//            );
+            return  <div>
+                        <XHeader>我的F码</XHeader>
+                        <div style="paddingTop:92px;">
+                            <BlockList  datasLink={api.getFCode()}></BlockList>
+                        </div>
+                    </div>
+
+        }
+
+    }
+</script>

+ 515 - 0
src/components/goods/goods_detail.vue

@@ -0,0 +1,515 @@
+<template>
+    <div v-if="datas!==null">
+        <div class="goods_detail_content" :class="{scale:showPopup}">
+            <x-header :left-options="{backText: ''}" :class="{display_none:showPopup}">商品详情</x-header>
+            <div class="goods_img">
+                <swiper v-if="datas!=null" :list="getGoodsSwiperList" :show-desc-mask="false" height="720px" :auto="true" :loop="true"></swiper>
+                <div class="collection">已收藏人数&nbsp;&nbsp;{{getOneSummary.goods_collect}}</div>
+            </div>
+            <div v-if="getOneSummary['act_type'] == 1" class="groupbuy_msg">
+                <p><span class="groupbuy_title">抢购特价</span>仅剩<span class="groupbuy_label">{{getOneSummary.goods_storage}}</span>件,不可同享其他优惠。</p>
+            </div>
+            <flexbox style="margin-top: 20px;">
+                <flexbox-item :class="{goods_bonus_price:true}">
+                    <span class="bonus_icon"></span>
+                    <span class="bonus_price">{{getOneSummary['act_type'] !== 1?getOneSummary.bonus_price:datas['groupbuy'][0]['promotion_price']}}</span>
+                    <span class="discount" v-if="getOneSummary['act_type'] !== 1">{{getOneSummary.bonus_price/getOneSummary.goods_price*10 == 10 ? "无折扣" : (getOneSummary.bonus_price/getOneSummary.goods_price*10).toFixed(1)+'折'}}</span>
+                    <span class="discount" v-if="getOneSummary['act_type'] !== 1">立省{{(getOneSummary.goods_price - getOneSummary.bonus_price).toFixed(2)}}元</span>
+                </flexbox-item>
+                <flexbox-item :span="4" style="text-align: right;padding-right: 25px;">已售{{getOneSummary.goods_salenum}}件/库存{{getOneSummary.goods_storage}}件</flexbox-item>
+            </flexbox>
+            <p class="goods_price vux-1px-b">专柜价&nbsp;&nbsp;{{getOneSummary.goods_price}}</p>
+            <div style="overflow: hidden; padding-left: 25px;" v-if="getOneSummary['act_type'] !== 1">
+                <p style="color: #a7a7a7; padding: 25px 0; float: right; width: 726px;" class="vux-1px-b">{{getOneSummary.gap_desc}}</p>
+            </div>
+            <div style="overflow: hidden; padding-left: 25px;" v-if="getOneSummary['is_fcode']">
+                <flexbox class="vux-1px-b" style="padding: 25px 0;">
+                    <flexbox-item style="color: #ef524d;">F码商品</flexbox-item>
+                    <flexbox-item style="color: #a7a7a7;text-align: right; padding-right: 25px;">{{getOneSummary.fcode_desc}}</flexbox-item>
+                </flexbox>
+            </div>
+            <div class="goods_name">
+                <h3>{{getOneSummary.goods_mobile_name}}</h3>
+                <p>{{getOneSummary.goods_jingle}}</p>
+            </div>
+            <div class="stamps">
+                <Flexbox>
+                    <FlexboxItem :class="{stamps_item:true}"><img :src="getOneSummary.brand_country_logo" :class="{goods_logo:true}">{{getOneSummary.brand_country}}品牌</FlexboxItem>
+                    <FlexboxItem :class="{stamps_item:true}"><img src="../../assets/small_logo.png" :class="{goods_logo:true}">熊猫美妆自营</FlexboxItem>
+                </Flexbox>
+                <Flexbox>
+                    <FlexboxItem :class="{stamps_item:true}"><img src="../../assets/authorize_icon.png" :class="{goods_logo:true}">{{getOneSummary.brand_author_desc}}</FlexboxItem>
+                    <FlexboxItem :class="{stamps_item:true}"><img src="../../assets/seven_icon.png" :class="{goods_logo:true}">7天无理由退货</FlexboxItem>
+                </Flexbox>
+            </div>
+            <div class="spec" v-if="datas!=null">
+                <p>选择{{getSpecName}}</p>
+                <button type="button" v-for="(skus,index) in getSkus" :class="{active:skus.active}" @click.prevent="skusChange(getSkus,skus,index)">{{skus.spv_name}}</button>
+            </div>
+            <group>
+                <cell title="产品参数"></cell>
+                <cell-form-preview v-if="datas!=null" :list="attrs"></cell-form-preview>
+            </group>
+            <iframe :src="getGoodsWebView" frameborder="0" width="100%" height="3000" scrolling="no" id="iframe_webview"></iframe>
+            <divider>熊猫美妆</divider>
+            <tabbar :class="{display_none:showPopup}">
+                <tabbar-item link="/main/shopping_cart">
+                    <img slot="icon" src="../../assets/tabbar_shopcar_default@2x.png">
+                    <span slot="label">购物车</span>
+                </tabbar-item>
+                <tabbar-item @on-item-click="is_cart" style="background:#ffa82d;" v-if="getOneSummary.goods_storage">
+                    <span slot="label" style="color: #fff">加入购物车</span>
+                </tabbar-item>
+                <tabbar-item @on-item-click="is_buy" style="background: #ff4e4e;" v-if="getOneSummary.goods_storage">
+                    <span slot="label" style="color: #fff">立即购买</span>
+                </tabbar-item>
+                <tabbar-item style="background: #ff4e4e; font-size: 30px;" v-if="!getOneSummary.goods_storage">
+                    <span slot="label" style="color: #fff">售罄</span>
+                </tabbar-item>
+            </tabbar>
+        </div>
+        <popup  height="800px" v-model="showPopup" :show-mask="true" @on-hide="closepopup">
+            <flexbox :class="{goods_price:true}" style="padding-bottom: 30px;">
+                <flexbox-item>
+                    <div class="goods_small_img">
+                        <img :src="getOneSummary.goods_image_url" style="border-radius: 10px;">
+                    </div>
+                </flexbox-item>
+                <flexbox-item>
+                    <div><span class="bonus_icon popup_bonus_icon" style="vertical-align: text-bottom;"></span><span style="color: #EB4E4F;font-size: 36px;">{{getOneSummary['act_type'] !== 1?getOneSummary.bonus_price:datas['groupbuy'][0]['promotion_price']}}</span>&nbsp;&nbsp;<span style="color: #a7a7a7;">{{getOneSummary.goods_price}}</span></div>
+                    <div>已售{{getOneSummary.goods_salenum}}件/库存{{getOneSummary.goods_storage}}件</div>
+                </flexbox-item>
+            </flexbox>
+            <div>
+                <div class="spec vux-1px-b vux-1px-t" v-if="datas!=null">
+                    <p>选择{{getSpecName}}</p>
+                    <div style="height: 200px;overflow-x: hidden;-webkit-overflow-scrolling: touch; margin: 20px 0;padding-bottom: 30px;">
+                        <button type="button" v-for="(skus,index) in getSkus" :class="{active:skus.active}"
+                                @click="skusChange(getSkus,skus,index)">{{skus.spv_name}}
+                        </button>
+                    </div>
+                </div>
+            </div>
+
+           <flexbox>
+               <flexbox-item :class="{compute_label:true}">
+                   <div>数量</div>
+               </flexbox-item>
+               <flexbox-item>
+                   <div class="goods_numbers">
+                       <span class="compute" @click="goods_num_handle('minus',getOneSummary.goods_storage,getOneSummary['act_type'])">-</span>
+                       <span class="compute_result">{{goodsNumber}}</span>
+                       <span class="compute" @click="goods_num_handle('add',getOneSummary.goods_storage,getOneSummary['act_type'])">+</span>
+                   </div>
+               </flexbox-item>
+           </flexbox>
+            <div class="buy_btn" @click="check_submit(goodsNumber,getOneSummary.goods_id)">确定</div>
+            <div class="close_icon">
+                <span class="vux-close" @click="openBuyPopup(false)"></span>
+            </div>
+        </popup>
+    </div>
+</template>
+<script>
+    import { Swiper , Group , Cell , CellBox , CellFormPreview , Flexbox , FlexboxItem , Divider , XHeader , Popup , Tabbar, TabbarItem } from 'vux'
+    import Api from '../../lib/api.js'
+    import Proxy from '../blocks/util/proxy.js'
+    import WebView from '../blocks/webview.vue'
+    export default {
+        created(){
+            this.$store.commit('updateLoadingStatus', {isLoading: true});
+            let goods_id = this.$route.query["goods_id"];
+            this.$http.jsonp(Api.goodsDetail(goods_id)).then(function(res){
+                this.datas = res.body['datas'];
+                this.summary = res.body['datas'].summary;
+                this.$store.commit('updateLoadingStatus', {isLoading: false});
+
+                this.$store.commit(
+                    'updateWxShareMsg',{
+                        title:'熊猫美妆为您推荐:'+this.getOneSummary['goods_mobile_name'],
+                        desc:'熊猫美妆极速商城',
+                        link:window.location.href,
+                        imgUrl:this.getOneSummary['goods_image_url']
+                    });
+            });
+        },
+        mounted(){
+                document.body.scrollTop = 0;
+        },
+        destroyed(){
+            this.$store.commit(
+                'updateWxShareMsg',{
+                    title:'熊猫美妆测试版',
+                    desc:'熊猫美妆即将上线',
+                    link:window.location.href,
+                    imgUrl:'http://manager.lrlz.com/data/upload/shop/store/goods/6/6_05065318508868273_360.jpg'
+                });
+        },
+        destroyed(){
+            document.body.removeEventListener('touchmove',this.noscroll,false);
+        },
+        data () {
+            return {
+                goods_id:this.$route.query["goods_id"],
+                datas:null,
+                summary:[],
+                showPopup:false,
+                goodsNumber:1,
+                check_goods_id:'',
+                isCart:false
+            }
+        },
+        methods:{
+            closepopup(){
+                document.body.removeEventListener('touchmove',this.noscroll,false);
+            },
+            skusChange(skuss,skus,index){
+                this.check_goods_id = skus.goods_id;
+                for(let i =0;i<skuss.length;i++) {
+                    skuss[i].active = false;
+                }
+                skuss[index].active = true;
+                this.openBuyPopup(true);
+            },
+            noscroll(event){
+                if(event.target.tagName == 'BUTTON') {
+                    return false;
+                }
+               else {
+                    event.preventDefault();
+                }
+            },
+            openBuyPopup(type){
+                if(type == true) {
+                    document.body.addEventListener('touchmove', this.noscroll,false);
+                }
+                this.showPopup=type;
+            },
+            link_shopping(){
+                this.$router.push('/main/shopping_cart');
+            },
+            is_cart(){
+                this.isCart = true;
+                this.openBuyPopup(true);
+            },
+            is_buy(){
+                this.isCart = false;
+                this.openBuyPopup(true);
+            },
+            check_submit(quantity,goods_id){
+                this.$store.commit('updateLoadingStatus', {isLoading: true});
+                if(this.isCart) {
+                    this.$http.jsonp(Api.shopping_cart_add(quantity,goods_id)).then(function(res){
+                        this.$store.commit('updateLoadingStatus', {isLoading: false});
+                        if(res.body.code == 200) {
+                            this.openBuyPopup(false);
+                            this.$vux.toast.show({
+                                type:'text',
+                                text: "添加成功",
+                                position:'middle'
+                            });
+                        }
+                        else {
+                            this.openBuyPopup(false);
+                            this.$vux.toast.show({
+                                type:'text',
+                                text: res.body.message,
+                                position:'middle'
+                            })
+                        }
+                    })
+                }
+                else {
+                    this.$router.push({path:'/confirm_order',query:{goods_id:goods_id,iscart:0,num:this.goodsNumber}})
+                }
+            },
+            goods_num_handle(type,goods_storage,act_type){
+               if(act_type !== 0) {
+                   this.$vux.toast.show({
+                       type:'text',
+                       text: '抢购商品只能购买一件',
+                       position:'middle',
+                       width:'400px'
+                   });
+                   return;
+               }
+               let types = type;
+               let goodStorage = goods_storage;
+               if(types == 'minus') {
+                   if(this.goodsNumber <= 1) {
+                       this.$vux.toast.show({
+                           type:'text',
+                           text: '客官!不能再少了!',
+                           position:'middle',
+                           width:'400px'
+                       });
+                   }
+                   else {
+                       this.goodsNumber--;
+                   }
+               }
+               else {
+                   if(this.goodsNumber >= goodStorage) {
+                       this.$vux.toast.show({
+                           type:'text',
+                           text: '客官!只有这么多了!',
+                           position:'middle',
+                           width:'400px'
+                       });
+                   }
+                   else {
+                       this.goodsNumber=this.goodsNumber+1;
+                   }
+               }
+            }
+        },
+        computed:{
+            getGoodsSwiperList(){
+              return  Proxy.getGoodsSwiperList(this.datas.common_info.images);
+            },
+            attrs(){
+                let list = this.datas.common_info.attrs;
+                let new_list = [];
+                 for(let i=0;i<list.length;i++) {
+                     let cell = {};
+                      cell.label = list[i].name;
+                      cell.value = list[i].value;
+                      new_list.push(cell);
+                 }
+                 return new_list;
+            },
+            getSpecName(){
+                return this.datas.common_info.spec_name;
+            },
+            getGoodsWebView(){
+                return Api.goodsContent(this.goods_id);
+            },
+            getOneSummary(){
+                if(!this.summary)return;
+                let goods_id=this.check_goods_id?this.check_goods_id:this.goods_id;
+                let summary = this.summary;
+                let new_summary = {};
+                for(let i =0;i<summary.length;i++) {
+                    if(summary[i].goods_id == goods_id) {
+                        new_summary = summary[i];
+                    }
+                }
+                return new_summary;
+            },
+            getSkus(){
+                if(!this.datas)return;
+                let newSkusArray = [];
+                let skusArray = this.datas.common_info.skus;
+                for(let i=0;i<skusArray.length;i++) {
+                    let skus = {};
+                    skus.active=false;
+                    skus.spv_name = skusArray[i].spv_name;
+                    skus.goods_id = skusArray[i].goods_id;
+                    newSkusArray.push(skus);
+                }
+                newSkusArray[0].active=true;
+                return newSkusArray;
+            }
+        },
+        components: {
+            Swiper,
+            Group,
+            CellBox,
+            Flexbox,
+            FlexboxItem,
+            CellFormPreview,
+            Cell,
+            Divider,
+            XHeader,
+            Popup,
+            Tabbar,
+            TabbarItem
+        }
+    }
+</script>
+<style scoped>
+    .hanle_box {
+        position: fixed;
+        bottom: 0;
+        left: 0;
+        width: 100%;
+        height: 92px;
+        line-height: 92px;
+        text-align: center;
+        z-index: 999;
+        background: #fff;
+        border-top: 1px solid #D9D9D9;
+    }
+    .add_collection {
+        text-align: center;
+        color: #fff;
+        background: #9b9b9b;
+    }
+    .shopping_cart {
+        text-align: center;
+        color: #9b9b9b;
+    }
+    .compute_result {
+        display: inline-block;
+        width: 200px;
+        text-align: center;
+        font-size: 30px;
+    }
+    .compute {
+        color: #EB4E4F;
+        font-size: 40px;
+        display: inline-block;
+        width: 50px;
+        text-align: center;
+    }
+    .compute_label {
+        padding-left: 38px;
+    }
+    .compute {
+        display: inline-block;
+    }
+    .vux-popup-dialog, .vux-popup {
+        background: #fff;
+    }
+   .popup_bonus_icon {
+        width: 22px;
+    }
+    .goods_img {
+        padding-top: 92px;
+        position: relative;
+    }
+    .collection {
+        position: absolute;
+        bottom: 96px;
+        right: 19px;
+        color: #fff;
+        padding:10px 30px;
+        background: #7e7e7e;
+        border-radius: 50px;
+        font-size: 24px;
+    }
+    .stamps {
+        background: #fbfbfb;
+        padding: 15px 0;
+    }
+    .stamps_item {
+        padding-left: 25px;
+        color: #a7a7a7;
+    }
+    .goods_logo {
+        width: 28px;
+        height: 28px;
+        display: inline;
+        padding-right: 22px;
+        vertical-align: middle;
+    }
+    .goods_name {
+        padding-left: 25px;
+        padding-bottom: 20px;
+    }
+    .goods_name h3 {
+        line-height: 80px;
+        font-size: 30px;
+    }
+    .goods_name p {
+        font-size: 26px;
+        color: #a7a7a7;
+    }
+    .bonus_price {
+        font-size: 42px;
+        color: #ef524d;
+        vertical-align: middle;
+        font-weight: bold;
+    }
+    .goods_bonus_price {
+        padding-left: 25px;
+    }
+    .goods_price {
+        padding-left: 25px;
+        line-height: 64px;
+        color: #a7a7a7;
+    }
+    .discount {
+        border: 1px solid #f6495a;
+        color: #ef524d;
+        padding: 2px 5px;
+        display: inline-block;
+        font-size: 22px;
+    }
+    .bonus_icon {
+        vertical-align: middle;
+    }
+    .spec {
+        padding-left: 25px;
+        padding-top: 15px;
+    }
+    .spec button {
+        background: #fff;
+        color: #000;
+        border-radius: 5px;
+        padding: 10px 15px;
+        font-size: 24px;
+        border: 1px solid #000;
+        margin-right: 15px;
+        margin-bottom: 15px;
+    }
+    .spec button.active {
+        background: #f74d49;
+        color: #fff;
+        border: 1px solid #fff;
+    }
+    .spec p {
+        margin-bottom: 15px;
+    }
+    .goods_detail_content {
+        position: relative;
+        -webkit-transition: all 0.6s;
+        transition: all 0.6s;
+    }
+    .scale {
+        -webkit-transform: scale(0.95,0.95);
+    }
+    .goods_small_img {
+        width: 228px;
+        height: 228px;
+        margin-top: -50px;
+        margin-left: 5px;
+    }
+    .buy_btn {
+        position: absolute;
+        bottom: 0;
+        left: 0;
+        width: 750px;
+        background: #EB4E4F;
+        color: #fff;
+        border: none;
+        text-align: center;
+        height: 90px;
+        line-height: 90px;
+        font-size: 30px;
+    }
+    .groupbuy_msg {
+        background: #fee3c6;
+        height: 67px;
+    }
+    .groupbuy_msg p {
+        height: 67px;
+        line-height: 67px;
+        padding-left: 25px;
+    }
+    .groupbuy_title {
+        background: #ff4e4e;
+        color: #fff;
+        padding: 5px 5px;
+        border-radius: 5px;
+        margin-right: 10px;
+    }
+    .display_none {
+        display: none;
+    }
+    .close_icon {
+        position: absolute;
+        right: 15px;
+        top: 15px;
+    }
+</style>

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

@@ -0,0 +1,146 @@
+<template>
+    <div>
+        <x-header :left-options="{backText: ''}">登录</x-header>
+        <div class="login_input">
+            <input type="tel" name="userMobile" class="input_mobile" v-model="userMobile" placeholder="请输入您的手机号码">
+            <flexbox :gutter="20">
+                <flexbox-item>
+                    <input type="number" name="code" v-model="code" class="input_mobile" placeholder="请输入验证码">
+                </flexbox-item>
+                <flexbox-item>
+                    <button type="button" @click="getCode" class="get_code_btn">{{isCount?countTime:btn_msg}}</button>
+                </flexbox-item>
+            </flexbox>
+            <button type="button" class="button_submit" @click="login_submit">立即登录</button>
+        </div>
+    </div>
+</template>
+<script>
+    import {XHeader , Flexbox , FlexboxItem , Divider} from 'vux';
+    import Api from '../../lib/api';
+    export default {
+        data () {
+            return {
+                userMobile:'',
+                btn_msg:'获取验证码',
+                code:'',
+                countTime:60,
+                isCount:false
+            }
+        },
+        components: {
+            XHeader,
+            Flexbox,
+            FlexboxItem,
+            Divider
+        },
+        methods:{
+            login_submit(){
+                let mobile = this.userMobile;
+                let code = this.code;
+                if(!mobile ||  !/^1(3|4|5|7|8)\d{9}$/.test(mobile)) {
+                    this.$vux.toast.show({
+                        type:'text',
+                        text: '请输入正确的电话号码',
+                        position:'middle'
+                    })
+                }
+                else if(!code ) {
+                    this.$vux.toast.show({
+                        type:'text',
+                        text: '请输入验证码',
+                        position:'middle'
+                    })
+                }
+                else {
+                    this.$store.commit('updateLoadingStatus', {isLoading: true});
+                    this.$http.jsonp(Api.logInWeb(mobile,code)).then(function(res)
+                    {
+                       if(res.body.code == 200)
+                       {
+                           this.$http.jsonp(Api.member_info_get()).then(function(res){
+                               if(res.body.code !== 10014) {
+                                   this.$store.commit('updateLogIn');
+                                   this.$store.commit('updateUser',{'name':res.body.datas.member_nickname,'image':res.body.datas.member_avatar});
+                                   this.$http.jsonp(Api.user_bonus()).then(function(res){
+                                       this.$store.commit('updateUser_bonus',{'array':res.body.datas.bonus_rate});
+                                   });
+                                   this.$store.commit('updateLoadingStatus', {isLoading: false});
+                                   this.$router.push('/');
+                               }
+                           });
+                       }
+                       else {
+                           this.$vux.toast.show({
+                               type:'text',
+                               text: res.body.message,
+                               position:'middle'
+                           })
+                       }
+                    });
+                }
+            },
+            getCode(){
+                let mobile = this.userMobile;
+                if(!mobile || !/^1(3|4|5|7|8)\d{9}$/.test(mobile)) {
+                    this.$vux.toast.show({
+                        type:'text',
+                        text: '请输入正确的电话号码',
+                        position:'middle'
+                    })
+                }
+                else {
+                    if(this.isCount) return;
+                    this.isCount = true;
+                    let _self = this;
+                    let count = setInterval(function(){
+                        _self.countTime = _self.countTime -1;
+                        if(_self.countTime<=0) {
+                            clearInterval(count);
+                            _self.isCount = false;
+                        }
+                    },1000);
+                    this.$http.jsonp(Api.getCode(mobile)).then(function(res){
+                        if(!res.body.code == 200) {
+                            this.$vux.toast.show({
+                                type:'text',
+                                text: res.body.message,
+                                position:'middle'
+                            })
+                        }
+                    });
+                }
+            }
+        }
+    }
+</script>
+<style scoped>
+    .login_input {
+        padding-top: 112px;
+        width: 600px;
+        margin: 0 auto;
+    }
+    .input_mobile {
+        display: block;
+        width: 98%;
+        height:72px;
+        padding-left: 2%;
+        font-size: 24px;
+        background: #f2f2f2;
+        border: none;
+        border-radius: 5px;
+        margin-bottom: 20px;
+    }
+    .button_submit,.get_code_btn {
+        display: block;
+        width: 100%;
+        height: 72px;
+        text-align: center;
+        background: #EB4E4F;
+        color: #fff;
+        border: none;
+        border-radius: 5px;
+        font-size: 24px;
+        margin-bottom: 20px;
+    }
+</style>

+ 25 - 0
src/components/main/discovery/brandList.vue

@@ -0,0 +1,25 @@
+<script>
+    import BlockList from '../../blocks/block_list.vue'
+    import api from '../../../lib/api.js'
+
+    export default {
+        render(createElement)
+        {
+            return createElement(BlockList,{
+                props:{
+                    datasLink:api.brands(),
+                }
+            })
+        }
+
+    }
+</script>
+
+<style>
+    img {
+        vertical-align: middle;
+        width: 100%;
+        height: auto;
+        display: block;
+    }
+</style>

+ 70 - 0
src/components/main/discovery/discovery.vue

@@ -0,0 +1,70 @@
+<template>
+    <div>
+        <tab :class="{tab:true}">
+            <tab-item :selected="routerPath('/main/discovery')" @on-item-click="onItemClick(0)">品牌</tab-item>
+            <tab-item :selected="routerPath('/main/discovery/function')" @on-item-click="onItemClick(1)">功效</tab-item>
+        </tab>
+        <div class="discovery_content">
+            <keep-alive>
+                <router-view></router-view>
+            </keep-alive>
+        </div>
+    </div>
+</template>
+<script>
+    import {Tab, TabItem, Divider} from 'vux'
+    import BrandList from  "./brandList.vue"
+    import FunctionList from  "./functionList.vue"
+
+    export  default{
+        components: {
+            Tab, TabItem, BrandList, FunctionList, Divider
+        },
+        data(){
+            return {
+                index: 0
+            }
+        },
+        methods: {
+            onItemClick(index){
+                this.index = index;
+                if (index == 0) {
+                    this.$router.push({path: "/main/discovery"})
+                } else {
+                    this.$router.push({path: "/main/discovery/function"})
+                }
+            },
+            routerPath(path){
+                if (path == this.$route.path) {
+                    return true;
+                }
+                else {
+                    return false;
+                }
+            }
+        }
+    }
+</script>
+<style>
+    .weui-grid__icon {
+        width: 100%;
+        height: 100%;
+    }
+
+    .weui-grid {
+        padding: 0;
+    }
+
+    .tab {
+        position: fixed;
+        width: 100%;
+        z-index: 999;
+    }
+
+    .discovery_content {
+        padding-top: 88px;
+        padding-bottom: 101px;
+        background: #F7F7F7;
+    }
+</style>
+

+ 105 - 0
src/components/main/discovery/functionList.vue

@@ -0,0 +1,105 @@
+<template>
+    <div class="function_list">
+        <div class="function_items" v-for="data in datas">
+            <masker :opacity = "0" class="function_item_img">
+                <x-img :src="data.img" :key="data.img" :default-src="defalutImg"></x-img>
+                <div slot="content" class="m-title" :key="data.name">
+                    <span class="rounds"></span>{{data.name}}<span class="rounds"></span>
+                    </br>
+                    <span class="look_btn"  @click="link(data.name)">查看全部</span>
+                </div>
+            </masker>
+            <flexbox :gutter="0" wrap="wrap" class="function_item">
+                <flexbox-item :span="1/4" v-for="item in data.subitem" class="subitem" :key="item">
+                     <div @click="link(item.name)">
+                         <x-img :src="item.img" :key="item.img" :default-src="defalutImg"></x-img>
+                         <p class="item_title">{{item.name}}</p>
+                     </div>
+                </flexbox-item>
+            </flexbox>
+        </div>
+    </div>
+</template>
+<script>
+
+    import { Masker , Flexbox , FlexboxItem , XImg } from 'vux'
+    import Api from '../../../lib/api.js'
+    import Actions from '../../../lib/Actions'
+    import DefaultImg from '../../../assets/default_img.jpg'
+
+    export default {
+        components:{
+            Masker,
+            Flexbox,
+            FlexboxItem,
+            XImg
+        },
+        created(){
+            let _self = this;
+            this.$http.jsonp(Api.functionList()).then(function(res){
+                _self.datas = res.body.datas["items"];
+            })
+        },
+        mounted(){
+            this.$store.commit('updateLoadingStatus', {isLoading: false});
+        },
+        data(){
+            return {
+                datas:[],
+                defalutImg:DefaultImg
+            }
+        },
+        methods:{
+            link(title){
+                let action = Actions.getFilterBlockListPath(title, "", "", "", title);
+                this.$router.push(action);
+            }
+        }
+    }
+
+</script>
+<style scoped>
+    .m-title {
+        color: #fff;
+        font-size: 50px;
+        text-align: center;
+        text-shadow: 0 0 2px rgba(0, 0, 0, .5);
+        font-weight: 500;
+        position: absolute;
+        left: 0;
+        right: 0;
+        width: 100%;
+        top: 50%;
+        transform: translateY(-50%);
+    }
+    .rounds {
+        display: inline-block;
+        width: 24px;
+        height: 24px;
+        background: #fff;
+        border-radius: 50px;
+        margin: 0 10px;
+        line-height: 15px;
+    }
+    .look_btn {
+        font-size: 28px;
+        border: 1px solid #fff;
+        border-radius: 10px;
+        padding: 10px 20px;
+    }
+    .item_title {
+        font-size: 24px;
+        text-align: center;
+        margin-bottom: 40px;
+    }
+    .function_items:first-child {
+        margin-top: 0;
+    }
+    .function_item_img {
+        margin-bottom: 40px;
+    }
+    .function_item img {
+        width: 70%;
+        margin: 0 auto;
+    }
+</style>

+ 35 - 0
src/components/main/home/homeList.vue

@@ -0,0 +1,35 @@
+<script>
+    import BlockList from '../../blocks/block_list.vue'
+    import Api from "../../../lib/api.js"
+
+    export default {
+        created(){
+            this.getLink();
+        },
+        datas(){
+            return {url: ""}
+        },
+        methods: {
+            getLink(){
+                let arr = this.$route.path.split("\/");
+                let special_id = arr [arr.length - 1];
+                this.url = Api.special(special_id);
+            }
+        },
+        watch: {
+            '$route' (to, from) {
+                if (to.path !== from.path) {
+                    this.getLink();
+                }
+            }
+        },
+        render(){
+            return (
+                    <BlockList datasLink={this.url}></BlockList>
+            )
+        }
+    }
+</script>
+<style>
+
+</style>

+ 99 - 0
src/components/main/home/homeTabs.vue

@@ -0,0 +1,99 @@
+<script>
+    import BlockList from '../../blocks/block_list.vue'
+    import Api from "../../../lib/api.js"
+    import Search from "../../blocks/search"
+    import {XHeader, Tab, TabItem, Divider, Flexbox, FlexboxItem} from 'vux'
+    export default {
+        created() {
+            let _self = this;
+            let arr = this.$route.path.split("\/");
+            this.current_id = arr [arr.length - 1];
+
+            this.$http.jsonp(Api.homeTabs(),).then(function (res) {
+                _self.tabs = res.body.datas.tabs;
+            })
+        },
+        data(){
+            return {
+                tabs: null,
+                current_id: 0,
+            }
+        },
+        methods: {
+            isSelected(item, special_id){
+                return item.special_id == special_id;
+            }
+        }
+        ,
+        render(createElement)
+        {
+            if (!this.tabs)
+                return
+            let nativeClickHandler = (index, item) => {
+                this.current_id = item.special_id;
+                this.$router.push({path: "" + this.current_id})
+            }
+            //tab
+            const top_tabs = this.tabs.map((item, index) => {
+                return <TabItem class="tab_item" selected={this.isSelected(item, this.current_id)}
+                                nativeOnClick={ () => nativeClickHandler(index, item)}>{ item.name}</TabItem>
+            })
+
+            return (
+                    <div>
+                        <Search isScroll={this.isScroll}></Search>
+                        <div>
+                            <div class="list_tab" style = {{position:'fixed'}}>
+                                <Tab style={{width: 150 * this.tabs.length + "px",background:"#fff"}}
+                                customBarWidth="150px">{top_tabs}</Tab>
+                                    </div>
+                        </div>
+                        <div class="list_container">
+
+                                <router-view></router-view>
+                        </div>
+                    </div>
+            )
+        }
+
+    }
+</script>
+
+<style scoped>
+    img {
+        vertical-align: bottom;
+        width: 100%;
+        height: auto;
+        display: block;
+    }
+
+    .list_tab {
+        position: relative;
+        z-index: 1000;
+        width: 100%;
+        margin-top: 83px;
+        overflow-y : auto;
+        -webkit-box-sizing : border-box;
+        -webkit-overflow-scrolling: touch;
+        background: #fff;
+    }
+
+    .list_tab::-webkit-scrollbar {
+        display: none;
+    }
+
+    .list_container {
+        padding-top: 170px;
+        padding-bottom: 101px;
+    }
+
+    .tab_item {
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+    }
+
+    .list_hide {
+        display: none;
+    }
+</style>

+ 73 - 0
src/components/main/main.vue

@@ -0,0 +1,73 @@
+<template>
+    <div>
+        <keep-alive>
+            <router-view></router-view>
+        </keep-alive>
+        <tabbar>
+            <tabbar-item link="/main/index" :selected="routerPath('/main/index')">
+                <img slot="icon" src="../../assets/tabbar_home_default@2x.png">
+                <img slot="icon-active" src="../../assets/tabbar_home_hight@2x.png">
+                <span slot="label">首页</span>
+            </tabbar-item>
+            <tabbar-item link="/main/discovery" :selected="routerPath('/main/discovery')">
+                <img slot="icon" src="../../assets/tabbar_shopguide_default@2x.png">
+                <img slot="icon-active" src="../../assets/tabbar_shopguide_hight@2x.png">
+                <span slot="label">发现</span>
+            </tabbar-item>
+            <tabbar-item link="/main/shopping_cart" :selected="routerPath('/main/shopping_cart')">
+                <img slot="icon" src="../../assets/tabbar_shopcar_default@2x.png">
+                <img slot="icon-active" src="../../assets/tabbar_shopcar_hight@2x.png">
+                <span slot="label">购物车</span>
+            </tabbar-item>
+            <tabbar-item link="/main/mine" :selected="routerPath('/main/mine')">
+                <img slot="icon" src="../../assets/tabbar_set_default@2x.png">
+                <img slot="icon-active" src="../../assets/tabbar_set_hight@2x.png">
+                <span slot="label">我的</span>
+            </tabbar-item>
+        </tabbar>
+    </div>
+</template>
+<script>
+    import {Tabbar, TabbarItem} from 'vux'
+    export default {
+        data () {
+            return {}
+        },
+        components: {
+            Tabbar,
+            TabbarItem,
+        },
+        methods: {
+            routerPath(path){
+                if (this.$route.path.indexOf(path) >= 0) {
+                    return true;
+                }
+                else {
+                    return false;
+                }
+            }
+        }
+    }
+</script>
+<style>
+    .slide-fade-enter-active {
+        -webkit-transform: translateY(0);
+        transition: all .4s;
+        -webkit-transition: all .4s;
+    }
+
+    .slide-fade-enter {
+        transform: translateY(-400px);
+        -webkit-transform: translateY(-400px);
+        opacity: 0;
+    }
+
+    .slide-fade-leave-active {
+        opacity: 0;
+    }
+
+    .weui-tabbar {
+        position: fixed;
+        background: #fff;
+    }
+</style>

+ 222 - 0
src/components/main/mine/mine.vue

@@ -0,0 +1,222 @@
+<template>
+    <div class="mine">
+        <group>
+            <cell :title="userMsg.userName" :link="loginPath" style="padding: 30px 40px;">
+                <img slot="icon" v-bind:src="userMsg.userImg"
+                     style="max-width: 122px; max-height: 118px; vertical-align: middle; margin-right: 32px;border-radius: 10px;" alt="logo"/>
+            </cell>
+        </group>
+        <group>
+            <cell title="我的订单" value="查看全部订单" is-link link="/order_tabs?index=0" style="padding: 50px 40px;"></cell>
+            <cell class="order_classify">
+                <flexbox>
+                    <flexbox-item>
+                        <router-link :to="{ path: '/order_tabs', query: { index: 1}}">
+                            <div class="order_classify_icon order_classify_icon_1"></div>
+                            <p class="item_title">待付款</p>
+                        </router-link>
+                    </flexbox-item>
+                    <flexbox-item>
+                        <router-link :to="{ path: '/order_tabs', query: { index: 2}}">
+                            <div class="order_classify_icon order_classify_icon_2"></div>
+                            <p class="item_title">待发货</p>
+                        </router-link>
+                    </flexbox-item>
+                    </flexbox-item>
+                    <flexbox-item>
+                        <router-link :to="{ path: '/order_tabs', query: { index: 3}}">
+                            <div class="order_classify_icon order_classify_icon_3">
+                            </div>
+                            <p class="item_title">待收货</p>
+                        </router-link>
+                    </flexbox-item>
+                    </flexbox-item>
+                    <flexbox-item>
+                        <router-link :to="{ path: '/order_tabs', query: { index: 4}}">
+                            <div class="order_classify_icon order_classify_icon_4"></div>
+                            <p class="item_title">待评价</p>
+                        </router-link>
+                    </flexbox-item>
+                    <flexbox-item>
+                        <div class="order_classify_icon order_classify_icon_5"></div>
+                        <p class="item_title">退款/售后</p>
+                    </flexbox-item>
+                </flexbox>
+            </cell>
+        </group>
+        <group>
+            <cell title="熊猫红包" is-link :border-intent="false" @click.native="showBonus = !showBonus">
+                <img slot="icon" src="../../../assets/bonus_icon.png"
+                     style="width: 32px;vertical-align: text-bottom;margin-right: 25px;"/>
+            </cell>
+            <div class="bonus_slide" v-for="bonus in userbonus" :class="showBonus?'animate':''">
+                <div style="overflow: hidden;">
+                    <span class="bonus_label">{{bonus.rate+'%'}}</span>
+                    <span class="bonus_value">{{bonus.total}}元</span>
+                </div>
+                <br/>
+            </div>
+            <cell title="收货地址" is-link link="/address/list">
+                <img slot="icon" src="../../../assets/address_icon.png"
+                     style="width: 34px;vertical-align: text-bottom;margin-right: 25px;"/>
+            </cell>
+            <cell title="我的F码" is-link link="/fcodelist">
+                <img slot="icon" src="../../../assets/fcode_icon.png"
+                     style="width: 34px;vertical-align: text-bottom;margin-right: 25px;"/>
+            </cell>
+            <cell title="帮助中心" is-link link="http://p.lrlz.com/hfive/feed_back/question_answer.html">
+                <img slot="icon" src="../../../assets/help_icon.png"
+                     style="width: 34px;vertical-align: text-bottom;margin-right: 25px;"/>
+            </cell>
+        </group>
+    </div>
+</template>
+
+<script>
+    import {Group, Cell, Flexbox, FlexboxItem, Divider} from 'vux'
+    import DefaultImg from '../../../assets/mine_logo_icon.png'
+    export default {
+        created(){
+            if (this.$store.state.isLogIn) {
+                this.userName = this.$store.state.user['name'];
+                this.userImg = this.$store.state.user['image'];
+            }
+            else {
+                this.userName = '登录';
+                this.userImg = this.DefaultImg;
+            }
+        },
+        mounted(){
+            this.$store.commit('updateLoadingStatus', {isLoading: false});
+        },
+        components: {
+            Group,
+            Cell,
+            Flexbox,
+            FlexboxItem,
+            Divider
+        },
+        data(){
+            return {
+                DefaultImg: DefaultImg,
+                showContent002: true,
+                user_bonus: this.$store.state.user_bonus,
+                showBonus: false
+            }
+        },
+        computed: {
+            userMsg () {
+                let user = {};
+                if (this.$store.state.isLogIn) {
+                    user.userName = this.$store.state.user['name'];
+                    user.userImg = this.$store.state.user['image'];
+                }
+                else {
+                    user.userName = '登录';
+                    user.userImg = this.DefaultImg;
+                }
+                return user;
+            },
+            loginPath (){
+                let path = '';
+                if (!this.$store.state.isLogIn) {
+                    path = '/login';
+                }
+                return path;
+            },
+            userbonus(){
+                return this.$store.state.user_bonus;
+            }
+        }
+    }
+</script>
+
+<style>
+    .bonus_label {
+        float: left;
+        height: 50px;
+        line-height: 50px;
+        margin-left: 20px;
+    }
+
+    .bonus_value {
+        float: right;
+        line-height: 30px;
+        margin-right: 20px;
+        color: #EB4E4F;
+    }
+
+    .bonus_slide {
+        padding: 0 20px;
+        overflow: hidden;
+        max-height: 0;
+        transition: max-height .5s cubic-bezier(0, 1, 0, 1) -.1s;
+    }
+
+    .animate {
+        max-height: 9999px;
+        transition-timing-function: cubic-bezier(0.5, 0, 1, 0);
+        transition-delay: 0s;
+    }
+    .mine {
+        background: #F7F7F7;
+    }
+    .mine .weui-cells {
+        font-size: 30px;
+    }
+
+    .mine .shop_state {
+        position: absolute;
+    }
+
+    .mine .order_classify_icon {
+        width: 100%;
+        height: 40px;
+    }
+
+    .mine .order_classify_icon img {
+        width: 40%;
+        margin: 0 auto;
+    }
+
+    .mine .vux-flexbox-item p {
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        -o-text-overflow: ellipsis;
+        overflow: hidden;
+        text-align: center;
+        font-size: 20px;
+        margin-top: 10px;
+        height: 30px;
+        line-height: 30px;
+    }
+
+    .order_classify .weui-cell__ft {
+        width: 100%;
+    }
+
+    .order_classify_icon_1 {
+        background: url("../../../assets/payments_due_icon.png") no-repeat center;
+        background-size: 30%;
+    }
+
+    .order_classify_icon_2 {
+        background: url("../../../assets/shipping_term_icon.png") no-repeat center;
+        background-size: 30%;
+    }
+
+    .order_classify_icon_3 {
+        background: url("../../../assets/goods_receipt_icon.png") no-repeat center;
+        background-size: 30%;
+    }
+
+    .order_classify_icon_4 {
+        background: url("../../../assets/evaluated_icon.png") no-repeat center;
+        background-size: 28%;
+    }
+
+    .order_classify_icon_5 {
+        background: url("../../../assets/refund_terms_icon.png") no-repeat center;
+        background-size: 30%;
+    }
+</style>

+ 109 - 0
src/components/main/shop-car/CarProxy.js

@@ -0,0 +1,109 @@
+/**
+ * Created by Dell on 2017/5/25.
+ */
+import Proxy from '../../blocks/util/proxy.js'
+
+class CarProxy extends Proxy {
+    constructor(json) {
+        //调用实现父类的构造函数
+        super(json);
+        this.infos = json.free_info
+
+    }
+
+    getGoodsPrice(goods_id, need_bonus) {
+        let price = 0;
+        let summary = this.getSummery(goods_id);
+        if (summary == null) return price;
+
+        if (summary.act_type == 1) {
+            let act = this.getGroupbuy(summary.act_id);
+            if (act == null) return price;
+            price = act.promotion_price;
+        } else if (summary.act_type == 2) {
+            price = summary.promotion_price;
+        } else {
+            if (need_bonus) {
+                price = summary.bonus_price;
+            } else {
+                price = summary.goods_price;
+            }
+        }
+        return price;
+    }
+
+    unit_price(cart_id, use_bonus) {
+        let item = this.getCart(cart_id);
+
+        if (item == null) return 0.00;
+        if (item.bl_id > 0) {
+            let bl_id = item.bl_id;
+            let bl = this.getBundling(bl_id);
+            return bl.bl_price;
+        } else {
+            return this.getGoodsPrice(item.goods_id, use_bonus);
+        }
+    }
+
+    amountWithOutBonus(cart_id) {
+        let item = this.getCart(cart_id);
+        return this.unit_price(cart_id, false) * item.goods_num;
+    }
+
+    normal(cart_id) {
+        let cart = this.getCart(cart_id);
+        if (cart.is_bl > 0) {
+            return false;
+        } else {
+            let summary = this.getSummery(cart.goods_id);
+            return summary == null || summary.act_type == 0;
+        }
+    }
+
+    total_amount(selectedCarts, ratios, isLogin) {
+        if (selectedCarts.length == 0)
+            return 0;
+        let normal_amount = 0;
+        let special_amount = 0;
+        selectedCarts.map(item => {
+            if (this.normal(item.cart_id)) {
+                let price = this.amountWithOutBonus(item.cart_id);
+                normal_amount += price;
+            } else {
+                let price = this.amountWithOutBonus(item.cart_id);
+                special_amount += price;
+            }
+        })
+
+        if (!isLogin) {
+            return normal_amount + special_amount;
+        }
+
+        let disc = 0;
+        let left = normal_amount * 100;
+        let count = ratios.length;
+
+        for (let i = 0; i < count; ++i) {
+            let cur = ratios[i];
+            let rate = cur.rate;
+            let total = cur.total * 100;
+
+            if (left == 0) break;
+            if (rate > 100) continue;
+            if (total <= 0) continue;
+
+            let max_rate = left * rate / 100.00;
+            if (total >= max_rate) {
+                disc += max_rate;
+                left = 0;
+            } else {
+                disc += total;
+                left -= total * 100 / rate;
+            }
+        }
+        let cur_price = normal_amount * 100 - disc;
+        let total_price = cur_price / 100.00 + special_amount;
+        return total_price.toFixed(2);
+    }
+}
+export default CarProxy

+ 352 - 0
src/components/main/shop-car/shopping_cart.vue

@@ -0,0 +1,352 @@
+<template>
+    <div class="shopping_cart">
+        <div class="free_info">
+            <group>
+                <cell-box v-for="info in proxy.infos" :key="info"
+                          style="overflow: hidden;text-overflow: ellipsis;white-space: nowrap;width: 100%;">
+                    <span class="info_label">{{info.title}}</span>
+                    <span class="info_data">{{info.value}}</span>
+                </cell-box>
+            </group>
+        </div>
+        <div class="cart_list">
+            <swipeout>
+                <swipeout-item class="vux-1px-b cart_item" transition-mode="follow" v-for="cart in cart_l_items"
+                               :key="cart">
+                    <div slot="right-menu" @click="del_cart(cart)">
+                        <swipeout-button type="warn" :width="150">删除</swipeout-button>
+                    </div>
+                    <div slot="content" class="demo-content">
+                        <flexbox :gutter="17">
+                            <flexbox-item :span="1">
+                                <div @click="cart_select(cart)">
+                                    <icon :type="cart.active"></icon>
+                                </div>
+                            </flexbox-item>
+                            <flexbox-item :span="2.5">
+                                <img :src="proxy.getSummery(cart.goods_id).goods_image_url" class="goods_img"
+                                     @click="link_goods_detail(proxy.getSummery(cart.goods_id).goods_id)">
+                            </flexbox-item>
+                            <flexbox-item :span='8'>
+                                <div @click="link_goods_detail(proxy.getSummery(cart.goods_id).goods_id)">
+                                    <div class="goods_name">
+                                        <p>{{proxy.getSummery(cart.goods_id).goods_mobile_name}}</p>
+                                    </div>
+                                    <div class="goods_spec">{{proxy.getSummery(cart.goods_id).goods_spec}}</div>
+                                </div>
+                                <div class="goods_price">
+                                    <flexbox>
+                                        <flexbox-item :span="6">
+                                            <span class="bonus_icon"></span><span class="bonus_price">{{proxy.getSummery(cart.goods_id).act_type != 0?proxy.getSummery(cart.goods_id).goods_promotion_price:proxy.getSummery(cart.goods_id).bonus_price}}</span>专柜价<span
+                                                style="color: #929292">{{proxy.getSummery(cart.goods_id).goods_price}}</span>
+                                        </flexbox-item>
+                                        <flexbox-item>
+                                            <div class="handle"><span class="handle_btn minus"
+                                                                      @click="goods_num_handle(cart,cart.goods_num-1,cart.cart_id)">-</span><span
+                                                    class="handle_number">{{cart.goods_num}}</span><span
+                                                    class="handle_btn add"
+                                                    @click="goods_num_handle(cart,cart.goods_num+1,cart.cart_id)">+</span>
+                                            </div>
+                                        </flexbox-item>
+                                    </flexbox>
+                                </div>
+                            </flexbox-item>
+                        </flexbox>
+                    </div>
+                </swipeout-item>
+            </swipeout>
+        </div>
+        <div class="all_handle vux-1px-t">
+            <flexbox>
+                <flexbox-item>
+                    <div class="all_check" @click="all_handle">
+                        <icon :type="all_select"></icon>
+                        全选
+                    </div>
+                </flexbox-item>
+                <flexbox-item class="price">
+                    <p class="info_data">红包抵扣后</p>
+                    <p class="bonus_price">应付:¥{{total()}}</p>
+                </flexbox-item>
+                <flexbox-item>
+                    <div class="buy_btn" @click="just_buy">
+                        立即购买
+                    </div>
+                </flexbox-item>
+            </flexbox>
+        </div>
+    </div>
+</template>
+<script>
+    import {Group, CellBox, Divider, Swipeout, SwipeoutItem, SwipeoutButton, Flexbox, FlexboxItem, Icon} from 'vux'
+    import Api from '../../../lib/api'
+    import CarProxy from './CarProxy'
+    import Actions from '../../../lib/Actions'
+    export default {
+        created(){
+            this.$store.commit('updateLoadingStatus', {isLoading: true});
+            this.$http.jsonp(Api.shoppingCart()).then(function (res) {
+                this.proxy = new CarProxy(res.body.datas);
+                this.cart_l_items = this.cart_items();
+                this.$store.commit('updateLoadingStatus', {isLoading: false});
+            })
+        },
+        data(){
+            return {
+                proxy: {},
+                cart_l_items: [],
+                all_select: 'circle'
+            }
+        },
+        watch: {
+            'cart_l_items'(oldVal, newVal){
+                this.total();
+            }
+        },
+        methods: {
+            total(){
+                try {
+                    return this.proxy.total_amount(this.getSelectedCar(), this.$store.state.user_bonus, this.$store.state.isLogIn);
+                } catch (e) {
+                    return 0;
+                }
+            },
+
+            getSelectedCar(){
+                let ret = [];
+                this.cart_l_items.map(item => {
+                    if (item.active == "success") {
+                        ret.push(item);
+                    }
+                });
+                return ret;
+            },
+            cart_items(){
+                let cart_lists = this.proxy.cart_list;
+                let cart_items = [];
+                for (let i = 0; i < cart_lists.length; i++) {
+                    let cart = {};
+                    cart.active = 'circle';
+                    cart.bl_id = cart_lists[i].bl_id;
+                    cart.cart_id = cart_lists[i].cart_id;
+                    cart.goods_id = cart_lists[i].goods_id;
+                    cart.goods_num = cart_lists[i].goods_num;
+                    cart_items.push(cart);
+                }
+                return cart_items;
+            },
+            link_goods_detail(goods_id){
+                this.$router.push(Actions.getGoodsDetailPath(goods_id));
+            },
+            goods_num_handle(cart, quantity, cart_id){
+                if (quantity <= 0) {
+                    quantity = 1;
+                }
+                let _self = this;
+                this.$http.jsonp(Api.shopping_cart_edit(quantity, cart_id)).then(function (res) {
+
+                    if (res.body.code == 200) {
+                        _self.proxy.getCart(cart.cart_id).goods_num = res.body.datas.goods_num;
+                        cart.goods_num = res.body.datas.goods_num;
+                    }
+                    else {
+                        this.$vux.toast.show({
+                            type: 'text',
+                            text: res.body.message,
+                            position: 'middle'
+                        })
+                    }
+                })
+            },
+            del_cart(cart){
+                let _self = this;
+                this.$http.jsonp(Api.shopping_cart_edit(0, cart.cart_id)).then(function (res) {
+                    if (res.body.message == '成功') {
+                        let cart_item = cart;
+                        let cart_id = cart_item.cart_id;
+                        for (let i = 0; i < this.cart_l_items.length; i++) {
+                            if (cart_id == this.cart_l_items[i].cart_id) {
+                                _self.proxy.cart_list.splice(i, 1);
+                                this.cart_l_items.splice(i, 1);
+                                break;
+                            }
+                        }
+                    }
+                    else {
+                        this.$vux.toast.show({
+                            type: 'text',
+                            text: '删除失败',
+                            position: 'middle'
+                        })
+                    }
+                })
+            },
+            cart_select(cart){
+                if (cart.active == 'circle') {
+                    cart.active = 'success';
+                }
+                else {
+                    cart.active = 'circle';
+                }
+            },
+            all_handle(){
+                if (this.all_select == 'circle') {
+                    this.all_select = 'success';
+                    let cart_l_items = this.cart_l_items;
+                    for (let i = 0; i < cart_l_items.length; i++) {
+                        cart_l_items[i].active = 'success';
+                    }
+                }
+                else {
+                    this.all_select = 'circle';
+                    let cart_l_items = this.cart_l_items;
+                    for (let i = 0; i < cart_l_items.length; i++) {
+                        cart_l_items[i].active = 'circle';
+                    }
+                }
+            },
+            just_buy(){
+               let carts = this.getSelectedCar().map(function(item){
+                   return  item.cart_id;
+                });
+               let carts_items = carts.join(",");
+               if(!carts_items) {
+                   this.$vux.toast.show({
+                       type:'warn',
+                       text: '请至少选择一件商品',
+                       position:'middle',
+                       width:'600px'
+                   });
+               }
+               else {
+                   this.$router.push({path:'/confirm_order',query:{iscart:1,cart_id:carts_items}});
+               }
+            }
+        },
+        components: {
+            Divider,
+            Group,
+            CellBox,
+            Swipeout,
+            SwipeoutItem,
+            SwipeoutButton,
+            Icon,
+            Flexbox,
+            FlexboxItem
+        }
+    }
+</script>
+<style scoped>
+    .shopping_cart {
+        padding-bottom: 190px;
+        min-height: 100%;
+        background: #F7F7F7;
+    }
+
+    .info_label, .info_data {
+        font-size: 22px;
+    }
+
+    .info_data {
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        width: 78%;
+        height: 21px;
+        display: inline-block;
+        line-height: 21px;
+    }
+
+    .info_label {
+        color: #f57d86;
+        border: 1px solid #f57d86;
+        border-radius: 5px;
+        padding: 5px;
+        margin-right: 24px;
+    }
+
+    .bonus_icon {
+        width: 18px;
+        height: 25px;
+        vertical-align: text-bottom;
+    }
+
+    .weui-icon-circle {
+        font-size: 35px;
+    }
+
+    .weui-icon-success {
+        font-size: 35px;
+    }
+
+    .cart_item {
+        padding: 20px;
+        background: #fff;
+    }
+
+    .vux-swipeout-button {
+        font-size: 28px;
+    }
+
+    .goods_img {
+        border: 1px solid #C7C7C7;
+    }
+
+    .goods_spec {
+        font-size: 22px;
+        color: #929292;
+    }
+
+    .goods_price {
+        font-size: 22px;
+        margin-top: 30px;
+    }
+
+    .bonus_price {
+        font-size: 22px;
+        margin-right: 10px;
+    }
+
+    .handle_btn {
+        color: #FF4E4E;
+        display: inline-block;
+        width: 90px;
+        text-align: center;
+        font-size: 30px;
+        transform: scale(1.3);
+        -webkit-transform: scale(1.3);
+    }
+
+    .all_handle {
+        position: fixed;
+        width: 100%;
+        bottom: 100px;
+        left: 0;
+        background: #fff;
+        z-index: 100;
+    }
+
+    .buy_btn {
+        background: #FF4E4E;
+        color: #fff;
+        text-align: center;
+        font-size: 30px;
+        height: 92px;
+        line-height: 92px;
+    }
+
+    .price {
+        text-align: right;
+    }
+
+    .all_check {
+        margin-left: 20px;
+    }
+
+    .handle_number {
+        font-size: 30px;
+        display: inline-block;
+        width: 40px;
+        text-align: center;
+    }
+</style>

+ 331 - 0
src/components/order/list/OrderList.vue

@@ -0,0 +1,331 @@
+<script>
+    import {Flexbox, FlexboxItem, Divider} from 'vux'
+    import {dateFormat} from 'vux'
+    import Api from "../../../lib/api"
+    import WebView from "../../other/webview.vue"
+    import Actions from  "../../../lib/Actions"
+    export default {
+        data(){
+            return {
+                datas: null,
+                action_id: -1
+            }
+        },
+        props: [
+            "page",
+        ],
+        render(h){
+
+            let getParamState = (state) => {
+                if (state === 10) {
+                    return "state_new";
+                } else if (state === 20) {
+                    return "state_pay";
+                } else if (state === 30) {
+                    return "state_send";
+                } else if (state === 40) {
+                    return "state_success";
+                } else {
+                    return "";
+                }
+            }
+
+            let _self = this;
+            if (_self.page.isUpdate) {
+                this.$store.commit('updateLoadingStatus', {isLoading: true});
+                this.$http.jsonp(Api.orderList(), {params: {state_type: getParamState(_self.page.state)}}).then(function (res) {
+                    _self.datas = res.body.datas;
+                    _self.page.isUpdate = false;
+                    this.$store.commit('updateLoadingStatus', {isLoading: false});
+                })
+            }
+            let ui;
+            if (!_self.datas || (this.datas.hasOwnProperty("orders") && this.datas.orders.length === 0)) {
+                ui = <p style="text-align:center;margin-top:200px">您不存在{this.page.title}的订单</p>
+            } else {
+
+                let getOrderIndex = (order_id) => {
+                    return this.datas.orders.findIndex((value, index, arr) => {
+                        if (value.order_info.order_id === order_id) {
+                            return true
+                        }
+                    });
+                }
+
+                let hasOrder = (order_id) => {
+                    return getOrderIndex(order_id) >= 0;
+                }
+
+                let removeOrder = (order_id) => {
+                    let index = this.datas.orders.findIndex((value, index, arr) => {
+                        if (value.order_info.order_id === order_id) {
+                            return true
+                        }
+                    });
+                    this.datas.orders.splice(index, 1);
+                }
+
+                let addItem = (order, order_state, index) => {
+                    let order_id = order.order_info.order_id;
+                    let start = getOrderIndex(order_id);
+                    if ((order_state === 0 || order_state === order.order_info.order_state) && start < 0) {
+                        this.datas.orders.splice(index, 0, order);
+                    }
+                }
+
+                let editItem = (order) => {
+                    let order_id = order.order_info.order_id;
+                    let start = getOrderIndex(order_id);
+                    if (start >= 0) {
+                        removeOrder(order_id);
+                        let order_state = this.page.state;
+                        addItem(order, order_state, start);
+                    }
+                }
+
+                //退款
+                let refundOrder = (order_sn) => {
+                    this.$store.commit('updateLoadingStatus', {isLoading: true});
+                    this.$http.jsonp(Api.refundOrder(), {
+                        params: {
+                            order_sn: order_sn,
+                        }
+                    }).then(function (res) {
+                        _self.$store.commit("updateOrderState", {action: res.body.datas, action_id: new Date()});
+                        this.$store.commit('updateLoadingStatus', {isLoading: false});
+                    })
+                }
+
+                //通知服务器改变状态
+                let changeState = (action, order_id) => {
+                    this.$store.commit('updateLoadingStatus', {isLoading: true});
+                    this.$http.jsonp(Api.changeOrderState(), {
+                        params: {
+                            act_type: action,
+                            order_id: order_id
+                        }
+                    }).then(function (res) {
+                        _self.$store.commit("updateOrderState", {action: res.body.datas, action_id: new Date()});
+                        this.$store.commit('updateLoadingStatus', {isLoading: false});
+                    })
+                }
+                //处理服务器返回的状态
+                let global_state = _self.$store.state.changeState;
+                if (global_state.action && global_state.action_id !== this.action_id) {
+                    let action = global_state.action;
+                    let order = action.order;
+                    let order_id = action.order_id;
+                    if (!order) {
+                        removeOrder(order_id);
+                    } else {
+                        let order_state = this.page.state;
+                        let orderInfo = order.order_info;
+                        let has_order = hasOrder(order_id);
+
+                        let hasSameState = order_state === 0 || (order_state === orderInfo.order_state);
+                        if ((has_order && hasSameState) || order_state === 0) {
+                            editItem(order);
+                        } else if (has_order) {
+                            removeOrder(order_id);
+                        } else {
+                            addItem(order, order_state, 0);
+                        }
+                    }
+                    this.action_id = global_state.action_id;
+                }
+                //处理点击事件
+                let onActionClick = (action, item) => {
+                    let order_id = item.order_info.order_id;
+                    let order_sn = item.order_info.order_sn;
+                    switch (action) {
+                        case "if_delete": {
+                            _self.$vux.confirm.show({
+                                title: '订单操作',
+                                content: '您确定要删除该订单吗?',
+                                confirmText: "删除",
+                                cancelText: "不删除",
+                                onCancel () {
+                                },
+                                onConfirm () {
+                                    changeState(action, order_id);
+                                }
+                            })
+                            break
+                        }
+                        case "if_cancel": {
+                            _self.$vux.confirm.show({
+                                title: '订单操作',
+                                content: '您确定要取消该订单吗?',
+                                confirmText: "取消",
+                                cancelText: "不取消",
+                                onCancel () {
+                                },
+                                onConfirm () {
+                                    changeState(action, order_id);
+                                }
+                            })
+                            break
+                        }
+                        case "if_receive": {
+                            _self.$vux.confirm.show({
+                                title: '订单操作',
+                                content: '您确定要确定已收到货吗?',
+                                confirmText: "确定",
+                                cancelText: "不确定",
+                                onCancel () {
+                                },
+                                onConfirm () {
+                                    changeState(action, order_id);
+                                }
+                            })
+                            break
+                        }
+                        case "if_payment": {
+                            alert("立即付款还没做")
+                            break
+                        }
+                        case "if_deliver": {
+                            let path = Actions.getWebViewPath("物流信息", Api.deliverInfo(order_id))
+                            _self.$router.push(path);
+                            break
+                        }
+                        case "if_refund_cancel": {
+                            _self.$vux.confirm.show({
+                                title: '订单操作',
+                                content: '在本订单使用的红包不予退回,确认退款?',
+                                confirmText: "确定",
+                                cancelText: "不确定",
+                                onCancel () {
+                                },
+                                onConfirm () {
+                                    refundOrder(order_sn);
+                                }
+                            })
+                            break
+                        }
+                    }
+                }
+
+                ui = this.datas.orders.map((item, index) => {
+                    let addTime = dateFormat(item.order_info.add_time * 1000, "YYYY-MM-DD");
+                    let goods = item.order_goods.map((goods_item) => {
+                        return <FlexboxItem>
+                            <Flexbox orient="vertical">
+                                <Flexbox align="flex-start">
+                                    <FlexboxItem span={1 / 6}>
+                                        <img class="leftPadding" src={goods_item.goods_image}></img>
+                                    </FlexboxItem>
+                                    <FlexboxItem span={4 / 6}>
+                                        <p class="leftPadding">{goods_item.goods_name}</p>
+                                    </FlexboxItem>
+                                    <Flexbox orient="vertical">
+                                        <FlexboxItem >
+                                            <p class="titleRight">${goods_item.getGoodsPrice}</p>
+                                        </FlexboxItem>
+                                        <FlexboxItem >
+                                            <p class="titleRight">x{goods_item.goods_num}</p>
+                                        </FlexboxItem>
+                                    </Flexbox>
+                                </Flexbox>
+                                <FlexboxItem >
+                                    <div class="divider"></div>
+                                </FlexboxItem>
+                            </Flexbox>
+                        </FlexboxItem>
+                    })
+
+                    let buttons = item.actions.map((action) => {
+                        if (action.action !== ("if_evaluation")) {
+                            return <p class="button"
+                                      onClick={() => onActionClick(action.action, item)}>{action.title}</p>
+                        }
+                    })
+
+                    const bottom = () => {
+                        return <Flexbox orient="vertical">
+                            <FlexboxItem>
+                                <p class="titleRight">共{item.order_goods.length}件
+                                    合计:${item.order_info.order_amount}
+                                    (含运费${item.order_info.shipping_fee})
+                                </p>
+                            </FlexboxItem>
+                            <FlexboxItem >
+                                <div class="divider"></div>
+                            </FlexboxItem>
+                            <FlexboxItem >
+                                {buttons}
+                            </FlexboxItem>
+
+                        </Flexbox>
+                    };
+                    return <div>
+                        <Flexbox orient="vertical">
+                            <FlexboxItem>
+                                <div class="divider2"></div>
+                            </FlexboxItem>
+                            <FlexboxItem>
+                                <Flexbox>
+                                    <FlexboxItem><p class="titleLeft">{addTime}</p></FlexboxItem>
+                                    <FlexboxItem><p class="titleRight">{item.order_info.state_desc}</p></FlexboxItem>
+                                </Flexbox>
+                            </FlexboxItem>
+                            <FlexboxItem>
+                                <div class="divider"></div>
+                            </FlexboxItem>
+                            <FlexboxItem >
+                                <Flexbox orient="vertical">{goods}</Flexbox>
+                            </FlexboxItem>
+                            <FlexboxItem>
+                                {bottom}
+                            </FlexboxItem>
+                        </Flexbox>
+                    </div>
+                })
+            }
+            return (
+                    <div>{ui}</div>
+            );
+        }
+    }
+</script>
+<style scoped>
+    .titleLeft {
+        text-align: left;
+        padding-left: 15px;
+    }
+
+    .titleRight {
+        text-align: right;
+        padding-right: 15px;
+    }
+
+    .divider {
+        background-color: #000;
+        height: 1px;
+    }
+
+    .divider2 {
+        background-color: rgba(222, 94, 170, 0.55);
+        height: 15px;
+    }
+
+    .leftPadding {
+        padding-left: 15px;
+    }
+
+    .rightPadding {
+        padding-right: 15px;
+    }
+
+    .button {
+        color: grey;
+        border: solid black 1px;
+        border-radius: 4px;
+        padding: 0 10px;
+        margin: 15px;
+        font-size: 22px;
+        display: inline-block;
+        float: right;
+    }
+
+</style>

+ 64 - 0
src/components/order/list/OrderTabs.vue

@@ -0,0 +1,64 @@
+<script>
+    import {XHeader, Tab, TabItem, Swiper, SwiperItem} from 'vux'
+    import OrderList from  "./OrderList.vue"
+
+    export default {
+        computed: {
+            current_index: function () {
+                return this.selected_page === -1 ? parseInt(this.$route.query["index"]) : this.selected_page;
+            }
+        },
+        data(){
+            return {
+                pages: [
+                    {isUpdate: true, title: "全部", index: 0, state: 0},
+                    {isUpdate: true, title: "待付款", index: 1, state: 10},
+                    {isUpdate: true, title: "待发货", index: 2, state: 20},
+                    {isUpdate: true, title: "待收货", index: 3, state: 30},
+                    {isUpdate: true, title: "待评价", index: 4, state: 40},
+
+                ],
+                selected_page: -1
+            }
+        },
+        render(h){
+            let _self = this;
+            let nativeClickHandler = (index = 0) => {
+                _self.selected_page = index;
+                _self.pages[index].isUpdate = true;
+            };
+            //tab
+            const tabs = this.pages.map((item, index) => {
+                return <TabItem selected={this.current_index === index}
+                                nativeOnClick={ () => nativeClickHandler(index)}>{ this.pages[index].title}</TabItem>
+            })
+            //pages
+            const lists = this.pages.map((item, index) => {
+                return <OrderList page={item} class={{order_hide: this.current_index !== index}}/>
+            })
+
+            return (
+                    <div>
+                        <XHeader title='我的订单' leftOptions={{backText: ""}}/>
+                        <Tab>{tabs}</Tab>
+                        <div class="order_container">{lists}</div>
+                    </div>
+            )
+        }
+    }
+</script>
+<style scoped>
+    .order_hide {
+        display: none;
+    }
+
+    .vux-tab {
+        position: fixed;
+        margin-top: 94px;
+        width: 100%;
+    }
+
+    .order_container {
+        padding-top: 181px;
+    }
+</style>

+ 85 - 0
src/components/other/search.vue

@@ -0,0 +1,85 @@
+<script>
+    import BlockList from '../blocks/block_list.vue'
+    import {Search, Divider , XHeader} from 'vux'
+    import api from "../../lib/api.js"
+    import Actions from  "../../lib/Actions"
+
+
+    export default {
+        methods:{
+            change(value){
+                let ret = [];
+                if(!value) return;
+                this.value = value;
+                this.$store.commit('updateLoadingStatus', {isLoading: true});
+                this.$http.jsonp(api.search()+'&keyword='+value).then(function(res){
+                    let i=0;
+                    if(res.body.datas.words !== null) {
+                        res.body.datas.words.map(function(word){
+                            let words={};
+                            words.title= word;
+                            words.other=i;
+                            i++;
+                            ret.push(words);
+                        });
+                        this.results = ret;
+                    }
+                    this.$store.commit('updateLoadingStatus', {isLoading: false});
+                })
+            },
+            go(item) {
+                let action = Actions.getFilterBlockListPath(item.title, "", "", "", item.title);
+                this.$router.push(action);
+            },
+            searchSubmit(value){
+                if(!this.value) return;
+                document.activeElement.blur();
+                let action = Actions.getFilterBlockListPath(this.value, "", "", "", this.value);
+                this.$router.push(action);
+            }
+        },
+        data(){
+            return {
+                results: [],
+                value:''
+            }
+        },
+        render(createElement)
+        {
+            let _self = this;
+            let xHeader = createElement(XHeader, {
+                props: {
+                    title: '搜索',
+                    leftOptions: {
+                        backText: ''
+                    }
+                }
+            });
+            let search = createElement(Search,{
+                props: {
+                    placeholder: '搜索商品 品牌 功效 规格',
+                    results: this.results
+                },
+
+                on: {
+                    'on-change': this.change,
+                    "on-result-click": this.go,
+                    'on-submit' : this.searchSubmit
+                },
+                style: {
+                    color: '#9e9e9e',
+                    paddingTop:'92px'
+                },
+                ref:'search',
+                key: 'search'
+            });
+            let blockList = createElement(BlockList, {
+                props: {
+                    datasLink: api.searchSuggest(),
+                }
+            });
+            return createElement('div', {}, [xHeader,search, blockList]);
+        }
+
+    }
+</script>

+ 30 - 0
src/components/other/webview.vue

@@ -0,0 +1,30 @@
+<template>
+    <!--<x-header></x-header>-->
+    <iframe :src="link" frameborder="0" width="100%" :height="changeFrameHeight"  scrolling="no" id="iframe_webview"></iframe>
+</template>
+
+<script>
+    import { base64 } from 'vux'
+    export default {
+        data(){
+            return {
+                title : this.$route.query.title
+            }
+        },
+        computed:{
+            link(){
+                return base64.decode(this.$route.query.url);
+            },
+           changeFrameHeight(){
+                return document.documentElement.clientHeight;
+            }
+        },
+        components:{
+//            Xheader
+        }
+    }
+</script>
+
+<style>
+
+</style>

+ 91 - 0
src/components/special/special_list.vue

@@ -0,0 +1,91 @@
+<script>
+    import BlockList from '../blocks/block_list.vue'
+    import {Divider, XHeader} from 'vux'
+    import api from "../../lib/api.js"
+
+    export default {
+        created() {
+            this.getNewData();
+        },
+        data(){
+            return {
+                order: 'desc',
+                sort: '',
+                url: '',
+                path: ""
+            }
+        },
+        watch: {
+            '$route' (to, from) {
+                if (to.query.title != from.query.title) {
+                    this.getNewData();
+                    setTimeout(() => {
+                        document.body.scrollTop = 0;
+                    }, 100);
+
+                }
+            }
+        },
+        methods: {
+            getNewData(){
+                let params = {};
+                params.is_special = this.$route.query["is_special"];
+                params.special_id = this.$route.query["special_id"];
+                params.hot_id = this.$route.query["hot_id"];
+                params.brand_id = this.$route.query["brand_id"];
+                params.keyword = this.$route.query["keyword"];
+                let title = this.$route.query["title"];
+                if (params.is_special == false || params.is_special == 'false') {
+                    this.url = 'http://p.lrlz.com/mobile/index.php?act=search&op=index&client_type=ajax&title=' + title
+                    for (let i in params) {
+                        if (params[i] !== 'undefined') {
+                            this.url += "&" + i + '=' + params[i] + "";
+                        }
+                    }
+                }
+                else {
+                    this.url = api.special(params.special_id);
+                }
+            }
+        },
+        render(createElement)
+        {
+            let xHeader = createElement(XHeader, {
+                props: {
+                    title: this.$route.query["title"] || this.$route.query["keyword"],
+                    leftOptions: {
+                        backText: ''
+                    }
+                }
+            });
+            let special_id = this.$route.query["special_id"];
+            let hot_id = this.$route.query["hot_id"];
+            let brand_id = this.$route.query["brand_id"];
+            let keyword = this.$route.query["keyword"];
+
+            let blockList = createElement(BlockList, {
+                props: {
+                    datasLink: this.url,
+                    hot_id: hot_id,
+                    brand_id: brand_id,
+                    keyword: keyword,
+                    special_id: special_id,
+                },
+                style: {
+                    paddingTop: '92px'
+                }
+            });
+
+            return createElement('div', {}, [xHeader, blockList]);
+        }
+    }
+</script>
+
+<style>
+    img {
+        vertical-align: middle;
+        width: 100%;
+        height: auto;
+        display: block;
+    }
+</style>

+ 36 - 0
src/components/util/commonUtil.js

@@ -0,0 +1,36 @@
+/**
+ * Created by Dell on 2017/5/27.
+ */
+class CommonUtil {
+    static parseURL(url) {
+        let a = document.createElement('a');
+        a.href = url;
+        return {
+            source: url,
+            protocol: a.protocol.replace(':', ''),
+            host: a.hostname,
+            port: a.port,
+            query: a.search,
+            params: (function () {
+                let ret = {},
+                    seg = a.search.replace(/^\?/, '').split('&'),
+                    len = seg.length, i = 0, s;
+                for (; i < len; i++) {
+                    if (!seg[i]) {
+                        continue;
+                    }
+                    s = seg[i].split('=');
+                    ret[s[0]] = s[1];
+                }
+                return ret;
+            })(),
+            file: (a.pathname.match(/\/([^\/?#]+)$/i) || [, ''])[1],
+            hash: a.hash.replace('#', ''),
+            path: a.pathname.replace(/^([^\/])/, '/$1'),
+            relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [, ''])[1],
+            segments: a.pathname.replace(/^\//, '').split('/')
+        };
+    }
+}
+
+export default CommonUtil

+ 65 - 0
src/lib/Actions.js

@@ -0,0 +1,65 @@
+/**
+ * Created by Dell on 2017/4/27.
+ */
+import {base64} from 'vux'
+class Actions {
+    static getWebViewPath(title, url) {
+        return {
+            path: "/web",
+            query: {
+                url: url,
+                title: title
+            }
+        };
+    }
+
+    static getGoodsDetailPath(goods_id) {
+        return {
+            path: "/goods_detail",
+            query: {
+                goods_id: goods_id
+            }
+        }
+    }
+
+    static getSpecialListPath(special_id, title) {
+        return {
+            path: "/special",
+            query: {
+                special_id: special_id,
+                is_special: true,
+                title: title
+            }
+        };
+    }
+
+    static getFilterBlockListPath(title, special_id, hot_id, brand_id, keyword) {
+        return {
+            path: "/special",
+            query: {
+                special_id: special_id,
+                is_special: false,
+                title: title,
+                hot_id: hot_id,
+                brand_id: brand_id,
+                keyword: keyword
+            }
+        };
+    }
+
+    static getSchemaPathPath(schema) {
+        return {
+            path: "/schema",
+            query: {schema: base64.encode(schema)}
+        };
+    }
+
+    static toSearch() {
+        return {
+            path: "/search",
+            query: {}
+        };
+    }
+}
+
+export default Actions;

+ 117 - 0
src/lib/api.js

@@ -0,0 +1,117 @@
+class Api {
+
+    static host() {
+        return "http://p.lrlz.com";
+        //return "http://192.168.0.200";
+    }
+
+    static special(special_id) {
+        return this.host() + "/mobile/index.php?act=special&op=index&page=10&curpage=1&client_type=ajax&special_id=" + special_id;
+    }
+
+    static brands() {
+        return this.host() + "/mobile/index.php?act=brand&op=home&client_type=ajax";
+    }
+
+    static functionList() {
+        return this.host() + "/mobile/index.php?client_type=ajax&act=category&op=index"
+    }
+
+    static goodsDetail(goods_id) {
+        return this.host() + `/mobile/index.php?goods_id=${goods_id}&act=goods_common&op=index&client_type=ajax`
+    }
+
+    static searchSuggest() {
+        return this.host() + '/mobile/index.php?act=search&op=history&curpage=1&client_type=ajax'
+    }
+
+    static search() {
+        return this.host() + '/mobile/index.php?act=search&op=suggest_words&client_type=ajax'
+    }
+
+    static goodsContent(goods_id) {
+        return this.host() + `/mobile/index.php?act=goods&op=detail&goods_id=${goods_id}&client_type=ajax`
+    }
+
+    static member_info_get() {
+        return this.host() + '/mobile/index.php?act=member_info&op=get&client_type=ajax'
+    }
+
+    static shoppingCart() {
+        return this.host() + '/mobile/index.php?minest_cartid=0&act=cart&op=list&client_type=ajax'
+    }
+
+    static changeOrderState() {
+        return this.host() + '/mobile/index.php?act=member_order&op=change_state&client_type=ajax'
+    }
+
+    static orderList() {
+        return this.host() + '/mobile/index.php?act=member_order&page=30&op=list&curpage=1&client_type=ajax'
+    }
+
+    static refundOrder() {
+        return this.host() + '/mobile/index.php?act=member_refund&op=order_refund&client_type=ajax'
+    }
+
+    static deliverInfo(order_id) {
+        return this.host() + '/mobile/index.php?act=member_order&op=search_deliver&order_id=' + order_id
+    }
+
+    static shopping_cart_edit(quantity, cart_id) {
+        return this.host() + `/mobile/index.php?quantity=${quantity}&act=cart&op=edit&cart_id=${cart_id}&client_type=ajax`
+    }
+
+    static shopping_cart_add(quantity, goods_id) {
+        return this.host() + `/mobile/index.php?quantity=${quantity}&act=cart&goods_id=${goods_id}&op=addex&client_type=ajax`
+    }
+
+    static user_bonus() {
+        return this.host() + '/mobile/index.php?act=cart&op=rate_money&client_type=ajax'
+    }
+
+    static addressList() {
+        return this.host() + '/mobile/index.php?act=member_address&op=address_list&client_type=ajax';
+    }
+
+    static homeTabs() {
+        return this.host() + "/mobile/index.php?act=index&op=tabs&client_type=ajax"
+    }
+
+    static logInWeb(mobile, code) {
+        return this.host() + `/mobile/index.php?act=login&op=bind_mobile&client_type=ajax&mobile=${mobile}&code=${code}`;
+    }
+
+    static getCode(mobile) {
+        return this.host() + `/mobile/index.php?act=login&op=getcodex&client_type=ajax&mobile=${mobile}&type=register`;
+    }
+
+    static getFCode() {
+        return this.host() + '/mobile/index.php?act=member_fcode&op=list&curpage=1&client_type=ajax';
+    }
+
+    static confirm_order(ifcart, cart_id, goods_id, num) {
+        let goods_datas = '';
+        if (ifcart == 0) {
+            goods_datas = goods_id + '|' + num;
+        }
+        else {
+            goods_datas = cart_id;
+        }
+        return this.host() + `/mobile/index.php?act=member_buy&op=step_first&curpage=1&ifcart=${ifcart}&cart_id=${goods_datas}&client_type=ajax`;
+    }
+
+    static get_address() {
+        return this.host() + '/mobile/index.php?act=app_update&op=area&curpage=1&client_type=ajax';
+    }
+
+    static add_address(true_name, area_id, address, mobile) {
+        return this.host() + `/mobile/index.php?act=member_address&op=address_add&curpage=1&client_type=ajax&true_name=${true_name}&area_id=${area_id}&address=${address}&mob_phone=${mobile}`;
+    }
+
+    static set_default_address(id, is_default) {
+        return this.host() + `/mobile/index.php?act=member_address&op=set_default&curpage=1&client_type=ajax&address_id=${id}&is_default=${is_default}`;
+    }
+}
+
+export default Api
+

+ 134 - 0
src/main.js

@@ -0,0 +1,134 @@
+// The Vue build version to load with the `import` command
+// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
+import Vue from 'vue'
+import FastClick from 'fastclick'
+import VueRouter from 'vue-router'
+import VueResource from 'vue-resource'
+import App from './App.vue'
+import Vuex from 'vuex'
+import  {ConfirmPlugin} from 'vux'
+import VueVideoPlayer from 'vue-video-player'
+import  {ToastPlugin} from 'vux'
+import routes from './router/routes'
+import WechatShare from './wechat/WechatShare'
+import {base64} from 'vux'
+import CommonUtil from './components/util/commonUtil.js'
+import Actions from './lib/Actions'
+
+Vue.use(VueRouter);
+Vue.use(VueResource);
+Vue.use(Vuex);
+Vue.use(VueVideoPlayer);
+Vue.use(ToastPlugin);
+Vue.use(ConfirmPlugin);
+
+
+const store = new Vuex.Store({
+    state: {
+        isLoading: false,
+        isLogIn: false,
+        user: {
+            name: '',
+            image: ''
+        },
+        changeState: {
+            action: null,
+            action_id: -1
+        },
+        user_bonus: [],
+        address:{
+            aid:'',
+            name:'',
+            mobile:'',
+            address:''
+        },
+        wechatShareMsg:{
+            title:'熊猫美妆测试版',
+            desc:'熊猫美妆即将上线',
+            link:window.location.host,
+            imgUrl:'http://manager.lrlz.com/data/upload/shop/store/goods/6/6_05065318508868273_360.jpg'
+        }
+    },
+    mutations: {
+        updateLoadingStatus (state, payload) {
+            state.isLoading = payload.isLoading
+        },
+        updateLogIn(state){
+            state.isLogIn = true
+        },
+        updateUser(state, payload){
+            state.user['name'] = payload.name;
+            state.user['image'] = payload.image;
+        },
+        updateOrderState(state, payload){
+            state.changeState.action = payload.action;
+            state.changeState.action_id = payload.action_id;
+        },
+
+        updateUser_bonus(state, payload) {
+            state.user_bonus = payload.array;
+        },
+        update_address(state, payload){
+            state.address.name = payload.name;
+            state.address.mobile = payload.mobile;
+            state.address.address = payload.address;
+        },
+        updateWxShareMsg(state,payload){
+            state.wechatShareMsg.title = payload.title;
+            state.wechatShareMsg.desc = payload.desc;
+            state.wechatShareMsg.link = payload.link;
+            state.wechatShareMsg.imgUrl = payload.imgUrl;
+        }
+    }
+});
+
+const scrollBehavior = (to, from, savedPosition) => {
+    if (savedPosition) {
+        setTimeout(() => {
+            document.body.scrollTop = savedPosition.y
+        }, 100);
+        return savedPosition
+    }
+    return {
+        x: 0,
+        y: 0
+    };
+};
+
+const router = new VueRouter({
+    routes,
+    scrollBehavior,
+    mode: 'history',
+});
+
+router.beforeEach(function (to, from, next) {
+    if (to.path.indexOf("schema") !== -1) {
+        let schema = base64.decode(to.query.schema);
+        let urlObj = CommonUtil.parseURL(schema);
+        let path = urlObj.path;
+        switch (path) {
+            case "//p.lrlz.com/web/web":
+                let title = decodeURI(urlObj.params.title);
+                let url = base64.encode(decodeURIComponent(urlObj.params.url));
+                next(Actions.getWebViewPath(title, url));
+                break
+        }
+    } else {
+        next();
+    }
+});
+
+let Wechat_share = new WechatShare(store.state.wechatShareMsg);
+
+FastClick.attach(document.body);
+
+Vue.config.productionTip = false;
+
+/* eslint-disable no-new */
+new Vue({
+    store,
+    router,
+    render: h => h(App)
+}).$mount('#app-box');
+
+

+ 128 - 0
src/router/routes.js

@@ -0,0 +1,128 @@
+import SpecialList from '../components/special/special_list.vue'
+import GoodsDetail from '../components/goods/goods_detail.vue'
+import WebView from '../components/other/webview.vue'
+import Main from '../components/main/main.vue'
+import SearchPage from '../components/other/search.vue'
+import OrderTabs from  '../components/order/list/OrderTabs.vue'
+import OrderList from  '../components/order/list/OrderList.vue'
+import LogIn from '../components/login/login.vue'
+import {base64} from 'vux'
+import AddressList from  "../components/address/addressList.vue"
+import FcodeList from '../components/fcode/fcode_list.vue'
+import ConfirmOrder from '../components/confirm_order/confirm_order.vue'
+import addAddress from '../components/address/add_address.vue'
+import homeTabs from '../components/main/home/homeTabs.vue'
+import homeList from '../components/main/home/homeList.vue'
+import shoppingCart from '../components/main/shop-car/shopping_cart.vue'
+import Mine from '../components/main/mine/mine.vue'
+import Discovery from '../components/main/discovery/discovery.vue'
+import BrandList from '../components/main/discovery/brandList.vue'
+import functionList from '../components/main/discovery/functionList.vue'
+import brandList from '../components/main/discovery/brandList.vue'
+
+export default [
+    {
+        path: '/main',
+        component: Main,
+        children: [
+            {
+                path: '/',
+                redirect: '/index'
+            },
+            {
+                path: 'index',
+                component: homeTabs,
+                children: [
+                    {
+                        path: '/',
+                        redirect: '/main/index/list/0'
+                    },
+                    {
+                        path: 'list/:special_id',
+                        component: homeList,
+                    }
+                ]
+            },
+            {
+                path: 'shopping_cart',
+                component: shoppingCart
+            },
+            {
+                path: 'mine',
+                component: Mine
+            },
+            {
+                path: 'discovery',
+                component: Discovery,
+                children: [
+                    {
+                        path: 'brand',
+                        component: BrandList
+                    },
+                    {
+                        path: 'function',
+                        component: functionList
+                    }, {
+                        path: '/',
+                        component: brandList
+                    }
+                ]
+            }
+        ]
+    },
+    {
+        path: "/special",
+        component: SpecialList
+    },
+    {
+        path: '/goods_detail',
+        component: GoodsDetail
+    },
+    {
+        path: '/web',
+        component: WebView,
+        beforeEnter: (to, from, next) => {
+            let url = base64.decode(to.query.url)
+            window.location.href = url
+            next(false);
+        }
+    },
+    {
+        path: '/search',
+        component: SearchPage
+    },
+    {
+        path: '/order_tabs',
+        component: OrderTabs,
+        children: [
+            {
+                path: 'list',
+                component: OrderList
+            }
+        ]
+    },
+    {
+        path: '/',
+        redirect: '/main/index/list/0'
+    },
+    {
+        path: '/login',
+        component: LogIn
+    },
+    {
+        path: '/address/list',
+        component: AddressList
+    },
+    {
+        path: '/fcodelist',
+        component: FcodeList
+    },
+    {
+        path: '/confirm_order',
+        component: ConfirmOrder
+    },
+    {
+        path: '/add_address',
+        component: addAddress
+    }
+]

+ 112 - 0
src/wechat/WechatShare.js

@@ -0,0 +1,112 @@
+/**
+ * Created by huanggang on 2017/5/26.
+ */
+import Vue from 'vue'
+import {WechatPlugin} from 'vux'
+Vue.use(WechatPlugin);
+
+class WechatShare {
+
+    constructor(shareData) {
+        this.url = "http://lrlz.sinaapp.com/lrlz?uri=" + encodeURIComponent((window.location.href + "#").substring(0, (window.location.href + "#").indexOf('#'))) + "&callback=";
+        this.getJSONP(this.url, function (data) {
+            let timestamp = data.timestamp,
+                nonceStr = data.nonceStr,
+                signature = data.signature,
+                appid = data.appid;
+            Vue.wechat.config({
+                debug: false,
+                appId: appid,
+                timestamp: timestamp,
+                nonceStr: nonceStr,
+                signature: signature,
+                jsApiList: [
+                    'checkJsApi',
+                    'onMenuShareTimeline',
+                    'onMenuShareAppMessage',
+                    'onMenuShareQQ',
+                    'onMenuShareWeibo',
+                    'onMenuShareQZone',
+                    'getNetworkType',
+                    'hideOptionMenu',
+                    'showOptionMenu',
+                    'chooseWXPay'
+                ]
+            });
+        });
+        this.ready(shareData);
+    }
+
+    getJSONP(url, callback) {
+        let cn = "callback" + (+new Date()),
+            s = document.createElement("script");
+        s.type = "text/javascript";
+        s.charset = "UTF-8";
+        s.src = url + cn;
+        let heads = document.head || document.getElementsByTagName('head')[0];
+        let sp = heads.appendChild(s);
+
+        setTimeout(function () { //利用计时器 变成异步
+            window[cn] = function (data) {
+                try {
+                    s.onload = s.onreadystatechange = function () {
+                        callback && callback(data);
+                    }
+                }
+                catch (e) {
+                }
+                finally {
+                    setTimeout(function () {
+                        if (heads && s.parentNode) {
+                            sp.parentNode.removeChild(sp);   //最重销毁
+                        }
+                        window[cn] = null;
+                    }, 3000);
+                }
+            };
+        }, 0);
+    }
+
+    ready(data) {
+        Vue.wechat.ready(function () {
+            //分享朋友圈  / 2.2 监听“分享到朋友圈”按钮点击、自定义分享内容及分享结果接口
+            Vue.wechat.onMenuShareTimeline({
+                title: data.title, // 分享标
+                desc: data.desc,
+                link: data.link,    //cookie读取
+                imgUrl: data.img,// 分享图标
+                trigger: function (res) { 	// 不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回
+                },
+                success: function (res) {
+                },
+                cancel: function (res) {
+                    //alert('已取消'+res);
+                },
+                fail: function (res) {
+                    //alert(JSON.stringify(res));
+                }
+            });
+
+            //分享给好友
+            Vue.wechat.onMenuShareAppMessage({
+                title: data.title, // 分享标
+                desc: data.desc,
+                link: data.link,    //cookie读取
+                imgUrl: data.img,// 分享图标
+                trigger: function (res) {
+                    // 不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回
+                },
+                success: function (res) {
+                }
+            });
+
+            Vue.wechat.error(function (res) {
+                alert(res.errMsg);
+            });
+        })
+    }
+
+}
+
+
+export default WechatShare;

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

@@ -0,0 +1,26 @@
+// A custom Nightwatch assertion.
+// the name of the method is the filename.
+// can be used in tests like this:
+//
+//   browser.assert.elementCount(selector, count)
+//
+// for how to write custom assertions see
+// http://nightwatchjs.org/guide#writing-custom-assertions
+exports.assertion = function (selector, count) {
+  this.message = 'Testing if element <' + selector + '> has count: ' + count
+  this.expected = count
+  this.pass = function (val) {
+    return val === this.expected
+  }
+  this.value = function (res) {
+    return res.value
+  }
+  this.command = function (cb) {
+    var self = this
+    return this.api.execute(function (selector) {
+      return document.querySelectorAll(selector).length
+    }, [selector], function (res) {
+      cb.call(self, res)
+    })
+  }
+}

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


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