├── .gitignore
├── README.md
├── client
├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .postcssrc.js
├── README.md
├── build
│ ├── build.js
│ ├── check-versions.js
│ ├── dev-client.js
│ ├── dev-server.js
│ ├── utils.js
│ ├── vue-loader.conf.js
│ ├── webpack.base.conf.js
│ ├── webpack.dev.conf.js
│ ├── webpack.prod.conf.js
│ └── webpack.test.conf.js
├── config
│ ├── dev.env.js
│ ├── index.js
│ ├── prod.env.js
│ └── test.env.js
├── index.html
├── package.json
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ ├── Hello.vue
│ │ ├── cart
│ │ │ └── cart.vue
│ │ ├── cate
│ │ │ ├── cate.vue
│ │ │ └── detail.vue
│ │ ├── center
│ │ │ └── center.vue
│ │ ├── com
│ │ │ ├── header.vue
│ │ │ ├── jam.js
│ │ │ ├── loading.vue
│ │ │ ├── localDB.js
│ │ │ ├── sidebar.vue
│ │ │ └── swiper.vue
│ │ └── page
│ │ │ └── index.vue
│ ├── css
│ │ ├── base.scss
│ │ ├── cart.scss
│ │ ├── cate.scss
│ │ ├── center.scss
│ │ ├── detail.scss
│ │ └── index.scss
│ ├── main.js
│ ├── router
│ │ └── router.js
│ └── store
│ │ └── store.js
├── static
│ ├── .gitkeep
│ └── data
│ │ ├── cart.json
│ │ ├── cate.json
│ │ └── index.json
└── test
│ ├── e2e
│ ├── custom-assertions
│ │ └── elementCount.js
│ ├── nightwatch.conf.js
│ ├── runner.js
│ └── specs
│ │ └── test.js
│ └── unit
│ ├── .eslintrc
│ ├── index.js
│ ├── karma.conf.js
│ └── specs
│ └── Hello.spec.js
├── s1_serverNodeBegin
├── index.js
├── requestHandlers.js
├── router.js
└── server.js
├── s2_serverExpress
├── index.js
├── package.json
└── routes
│ └── routers.js
├── s3_Mongodb
└── test.js
├── s4_mongoose
├── package.json
└── test.js
├── s5_server
├── api.js
├── db.js
├── index.js
├── newDb.js
└── package.json
└── server
├── api.js
├── db.js
├── index.js
├── initCarts.json
├── initGoods.json
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | npm-debug.log
4 | .idea/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-node-proj
2 | my first project of learning vue-node
3 |
4 | **简介:**
5 |
6 | 之前刚入门vue并做好了一个简而全的纯vue2全家桶的项目,数据都是本地 json 模拟请求的;详情请移步这里:[vue-proj-demo](https://github.com/gjincai/vue-proj-demo)
7 |
8 | 为了真正做到数据库的真实存取,于是又开始入门了 node+express+mongoose 、并以此来为之前的vue页面写后台数据接口。
9 |
10 | ## 代码目录说明:
11 |
12 | ```
13 | |--vue-node-proj
14 | |--client //前端vue页面:http://gjincai.github.io/tags/vue/
15 | |--s1_serverNodeBegin //《Node入门》学习练习代码,地址:https://www.nodebeginner.org/index-zh-cn.html
16 | |--s2_serverExpress //express入门学习练习
17 | |--s3_Mongodb //mongodb入门学习练习:http://gjincai.github.io/tags/mongodb/
18 | |--s4_mongoose //mongoose入门学习练习:http://gjincai.github.io/tags/mongodb/
19 | |--s5_server //express与mongoose整合,实现注册登录的数据在mongodb的存取
20 | |--server //前端client页面的正式后台:
21 | |--api.js //所有接口
22 | |--db.js //数据库初始化、Schema数据模型
23 | |--index.js //后台服务启动入口
24 | |--initCarts.json //首次连接数据库,购物车数据的初始化
25 | |--initGoods.json //首次连接数据库,所有商品数据的初始化
26 | |--package.json //安装依赖:express,mongoose
27 | ```
28 |
29 | ## 项目说明:
30 |
31 | 前端:`client` 目录;主要技术:`vue-cli + vue2 + vue-router2 + vuex2 + axios + es6 + sass + eslint`
32 |
33 | 后台:`server` 目录;主要技术:`express+mongoose`
34 |
35 | (前后端分离,路由跳转在前端通过 vue-router 控制,后台只负责数据接口)
36 |
37 | ## 项目运行:
38 | ### 环境配置:
39 | **node.js 与 express 入门:**
40 |
41 | 学习练习代码:参考本项目中的文件夹 `vue-node-proj/s1_serverNodeBegin` 和 `vue-node-proj/s2_serverExpress`;
42 |
43 | **mongodb的安装与配置、mongoose的基本使用:**
44 |
45 | blog学习笔记:[http://gjincai.github.io/categories/mongodb/](http://gjincai.github.io/categories/mongodb/);
46 |
47 | 学习练习代码:参考本项目中的文件夹 `vue-node-proj/s3_Mongodb` 和 `vue-node-proj/s3_Mongodb`;
48 |
49 | ### 运行顺序:
50 |
51 | 新建命令行窗口1,开启本地mongodb服务:
52 |
53 | ```
54 | mongod
55 | ```
56 |
57 | 新建命令行窗口2,开启本地后台node服务器:
58 |
59 | ```
60 | cd vue-node-proj/server
61 | cnpm install --save
62 | node index.js
63 | ```
64 |
65 | 新建命令行窗口3,开启本地前端vue的dev模式:
66 |
67 | ```
68 | cd vue-node-proj/client
69 | cnpm install --save
70 | npm run dev --color
71 | ```
72 |
73 | 然后在浏览器打开:
74 |
75 | ```
76 | localhost:8080
77 | ```
78 |
79 | ## 相关学习笔记
80 | [express+mongoose 实现简易后台数据接口](http://gjincai.github.io/2017/07/26/express-mongoose-%E5%AE%9E%E7%8E%B0%E7%AE%80%E6%98%93%E5%90%8E%E5%8F%B0%E6%95%B0%E6%8D%AE%E6%8E%A5%E5%8F%A3/)
81 |
82 | ## 效果呈现:
83 |
84 | 

85 |
86 | 

87 |
88 |
--------------------------------------------------------------------------------
/client/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", { "modules": false }],
4 | "stage-2"
5 | ],
6 | "plugins": ["transform-runtime"],
7 | "comments": false,
8 | "env": {
9 | "test": {
10 | "presets": ["env", "stage-2"],
11 | "plugins": [ "istanbul" ]
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/client/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/client/.eslintignore:
--------------------------------------------------------------------------------
1 | build/*.js
2 | config/*.js
3 |
--------------------------------------------------------------------------------
/client/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // http://eslint.org/docs/user-guide/configuring
2 |
3 | module.exports = {
4 | root: true,
5 | parser: 'babel-eslint',
6 | parserOptions: {
7 | sourceType: 'module'
8 | },
9 | env: {
10 | browser: true,
11 | },
12 | // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
13 | extends: 'standard',
14 | // required to lint *.vue files
15 | plugins: [
16 | 'html'
17 | ],
18 | // add your custom rules here
19 | 'rules': {
20 | // allow paren-less arrow functions
21 | 'arrow-parens': 0,
22 | // allow async-await
23 | 'generator-star-spacing': 0,
24 | // allow debugger during development
25 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/client/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | test/unit/coverage
8 | test/e2e/reports
9 | selenium-debug.log
10 |
--------------------------------------------------------------------------------
/client/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | // to edit target browsers: use "browserlist" field in package.json
6 | "autoprefixer": {}
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | # client
2 |
3 | > A Vue.js project width Node
4 |
5 | ## Build Setup
6 |
7 | ``` bash
8 | # install dependencies
9 | npm install
10 |
11 | # serve with hot reload at localhost:8080
12 | npm run dev
13 |
14 | # build for production with minification
15 | npm run build
16 |
17 | # build for production and view the bundle analyzer report
18 | npm run build --report
19 |
20 | # run unit tests
21 | npm run unit
22 |
23 | # run e2e tests
24 | npm run e2e
25 |
26 | # run all tests
27 | npm test
28 | ```
29 |
30 | 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).
31 |
--------------------------------------------------------------------------------
/client/build/build.js:
--------------------------------------------------------------------------------
1 | require('./check-versions')()
2 |
3 | process.env.NODE_ENV = 'production'
4 |
5 | var ora = require('ora')
6 | var rm = require('rimraf')
7 | var path = require('path')
8 | var chalk = require('chalk')
9 | var webpack = require('webpack')
10 | var config = require('../config')
11 | var webpackConfig = require('./webpack.prod.conf')
12 |
13 | var spinner = ora('building for production...')
14 | spinner.start()
15 |
16 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
17 | if (err) throw err
18 | webpack(webpackConfig, function (err, stats) {
19 | spinner.stop()
20 | if (err) throw err
21 | process.stdout.write(stats.toString({
22 | colors: true,
23 | modules: false,
24 | children: false,
25 | chunks: false,
26 | chunkModules: false
27 | }) + '\n\n')
28 |
29 | console.log(chalk.cyan(' Build complete.\n'))
30 | console.log(chalk.yellow(
31 | ' Tip: built files are meant to be served over an HTTP server.\n' +
32 | ' Opening index.html over file:// won\'t work.\n'
33 | ))
34 | })
35 | })
36 |
--------------------------------------------------------------------------------
/client/build/check-versions.js:
--------------------------------------------------------------------------------
1 | var chalk = require('chalk')
2 | var semver = require('semver')
3 | var packageConfig = require('../package.json')
4 | var shell = require('shelljs')
5 | function exec (cmd) {
6 | return require('child_process').execSync(cmd).toString().trim()
7 | }
8 |
9 | var versionRequirements = [
10 | {
11 | name: 'node',
12 | currentVersion: semver.clean(process.version),
13 | versionRequirement: packageConfig.engines.node
14 | },
15 | ]
16 |
17 | if (shell.which('npm')) {
18 | versionRequirements.push({
19 | name: 'npm',
20 | currentVersion: exec('npm --version'),
21 | versionRequirement: packageConfig.engines.npm
22 | })
23 | }
24 |
25 | module.exports = function () {
26 | var warnings = []
27 | for (var i = 0; i < versionRequirements.length; i++) {
28 | var mod = versionRequirements[i]
29 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
30 | warnings.push(mod.name + ': ' +
31 | chalk.red(mod.currentVersion) + ' should be ' +
32 | chalk.green(mod.versionRequirement)
33 | )
34 | }
35 | }
36 |
37 | if (warnings.length) {
38 | console.log('')
39 | console.log(chalk.yellow('To use this template, you must update following to modules:'))
40 | console.log()
41 | for (var i = 0; i < warnings.length; i++) {
42 | var warning = warnings[i]
43 | console.log(' ' + warning)
44 | }
45 | console.log()
46 | process.exit(1)
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/client/build/dev-client.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | require('eventsource-polyfill')
3 | var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
4 |
5 | hotClient.subscribe(function (event) {
6 | if (event.action === 'reload') {
7 | window.location.reload()
8 | }
9 | })
10 |
--------------------------------------------------------------------------------
/client/build/dev-server.js:
--------------------------------------------------------------------------------
1 | require('./check-versions')()
2 |
3 | var config = require('../config')
4 | if (!process.env.NODE_ENV) {
5 | process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV)
6 | }
7 |
8 | var opn = require('opn')
9 | var path = require('path')
10 | var express = require('express')
11 | var webpack = require('webpack')
12 | var proxyMiddleware = require('http-proxy-middleware')
13 | var webpackConfig = process.env.NODE_ENV === 'testing'
14 | ? require('./webpack.prod.conf')
15 | : require('./webpack.dev.conf')
16 |
17 | // default port where dev server listens for incoming traffic
18 | var port = process.env.PORT || config.dev.port
19 | // automatically open browser, if not set will be false
20 | var autoOpenBrowser = !!config.dev.autoOpenBrowser
21 | // Define HTTP proxies to your custom API backend
22 | // https://github.com/chimurai/http-proxy-middleware
23 | var proxyTable = config.dev.proxyTable
24 |
25 | var app = express()
26 | var compiler = webpack(webpackConfig)
27 |
28 | var devMiddleware = require('webpack-dev-middleware')(compiler, {
29 | publicPath: webpackConfig.output.publicPath,
30 | quiet: true
31 | })
32 |
33 | var hotMiddleware = require('webpack-hot-middleware')(compiler, {
34 | log: () => {}
35 | })
36 | // force page reload when html-webpack-plugin template changes
37 | compiler.plugin('compilation', function (compilation) {
38 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
39 | hotMiddleware.publish({ action: 'reload' })
40 | cb()
41 | })
42 | })
43 |
44 | // proxy api requests
45 | Object.keys(proxyTable).forEach(function (context) {
46 | var options = proxyTable[context]
47 | if (typeof options === 'string') {
48 | options = { target: options }
49 | }
50 | app.use(proxyMiddleware(options.filter || context, options))
51 | })
52 |
53 | // handle fallback for HTML5 history API
54 | app.use(require('connect-history-api-fallback')())
55 |
56 | // serve webpack bundle output
57 | app.use(devMiddleware)
58 |
59 | // enable hot-reload and state-preserving
60 | // compilation error display
61 | app.use(hotMiddleware)
62 |
63 | // serve pure static assets
64 | var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory)
65 | app.use(staticPath, express.static('./static'))
66 |
67 | var uri = 'http://localhost:' + port
68 |
69 | var _resolve
70 | var readyPromise = new Promise(resolve => {
71 | _resolve = resolve
72 | })
73 |
74 | console.log('> Starting dev server...')
75 | devMiddleware.waitUntilValid(() => {
76 | console.log('> Listening at ' + uri + '\n')
77 | // when env is testing, don't need open it
78 | if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
79 | opn(uri)
80 | }
81 | _resolve()
82 | })
83 |
84 | var server = app.listen(port)
85 |
86 | module.exports = {
87 | ready: readyPromise,
88 | close: () => {
89 | server.close()
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/client/build/utils.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var config = require('../config')
3 | var ExtractTextPlugin = require('extract-text-webpack-plugin')
4 |
5 | exports.assetsPath = function (_path) {
6 | var assetsSubDirectory = process.env.NODE_ENV === 'production'
7 | ? config.build.assetsSubDirectory
8 | : config.dev.assetsSubDirectory
9 | return path.posix.join(assetsSubDirectory, _path)
10 | }
11 |
12 | exports.cssLoaders = function (options) {
13 | options = options || {}
14 |
15 | var cssLoader = {
16 | loader: 'css-loader',
17 | options: {
18 | minimize: process.env.NODE_ENV === 'production',
19 | sourceMap: options.sourceMap
20 | }
21 | }
22 |
23 | // generate loader string to be used with extract text plugin
24 | function generateLoaders (loader, loaderOptions) {
25 | var loaders = [cssLoader]
26 | if (loader) {
27 | loaders.push({
28 | loader: loader + '-loader',
29 | options: Object.assign({}, loaderOptions, {
30 | sourceMap: options.sourceMap
31 | })
32 | })
33 | }
34 |
35 | // Extract CSS when that option is specified
36 | // (which is the case during production build)
37 | if (options.extract) {
38 | return ExtractTextPlugin.extract({
39 | use: loaders,
40 | fallback: 'vue-style-loader'
41 | })
42 | } else {
43 | return ['vue-style-loader'].concat(loaders)
44 | }
45 | }
46 |
47 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html
48 | return {
49 | css: generateLoaders(),
50 | postcss: generateLoaders(),
51 | less: generateLoaders('less'),
52 | sass: generateLoaders('sass', { indentedSyntax: true }),
53 | scss: generateLoaders('sass'),
54 | stylus: generateLoaders('stylus'),
55 | styl: generateLoaders('stylus')
56 | }
57 | }
58 |
59 | // Generate loaders for standalone style files (outside of .vue)
60 | exports.styleLoaders = function (options) {
61 | var output = []
62 | var loaders = exports.cssLoaders(options)
63 | for (var extension in loaders) {
64 | var loader = loaders[extension]
65 | output.push({
66 | test: new RegExp('\\.' + extension + '$'),
67 | use: loader
68 | })
69 | }
70 | return output
71 | }
72 |
--------------------------------------------------------------------------------
/client/build/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | var utils = require('./utils')
2 | var config = require('../config')
3 | var isProduction = process.env.NODE_ENV === 'production'
4 |
5 | module.exports = {
6 | loaders: utils.cssLoaders({
7 | sourceMap: isProduction
8 | ? config.build.productionSourceMap
9 | : config.dev.cssSourceMap,
10 | extract: isProduction
11 | })
12 | }
13 |
--------------------------------------------------------------------------------
/client/build/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var utils = require('./utils')
3 | var config = require('../config')
4 | var vueLoaderConfig = require('./vue-loader.conf')
5 |
6 | function resolve (dir) {
7 | return path.join(__dirname, '..', dir)
8 | }
9 |
10 | module.exports = {
11 | entry: {
12 | app: './src/main.js'
13 | },
14 | output: {
15 | path: config.build.assetsRoot,
16 | filename: '[name].js',
17 | publicPath: process.env.NODE_ENV === 'production'
18 | ? config.build.assetsPublicPath
19 | : config.dev.assetsPublicPath
20 | },
21 | resolve: {
22 | extensions: ['.js', '.vue', '.json'],
23 | alias: {
24 | 'vue$': 'vue/dist/vue.esm.js',
25 | '@': resolve('src')
26 | }
27 | },
28 | module: {
29 | rules: [
30 | {
31 | test: /\.(js|vue)$/,
32 | loader: 'eslint-loader',
33 | enforce: 'pre',
34 | include: [resolve('src'), resolve('test')],
35 | options: {
36 | formatter: require('eslint-friendly-formatter')
37 | }
38 | },
39 | {
40 | test: /\.vue$/,
41 | loader: 'vue-loader',
42 | options: {
43 | loaders: {
44 | // Since sass-loader (weirdly) has SCSS as its default parse mode, we map
45 | // the "scss" and "sass" values for the lang attribute to the right configs here.
46 | // other preprocessors should work out of the box, no loader config like this necessary.
47 | 'scss': 'vue-style-loader!css-loader!sass-loader',
48 | 'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax'
49 | // 'scss': 'sass-loader',
50 | // 'sass': 'sass-loader?indentedSyntax'
51 | }
52 | // other vue-loader options go here
53 | }
54 | },
55 | // {
56 | // test: /\.vue$/,
57 | // loader: 'vue-loader',
58 | // options: vueLoaderConfig
59 | // },
60 | {
61 | test: /\.js$/,
62 | loader: 'babel-loader',
63 | include: [resolve('src'), resolve('test')]
64 | },
65 | {
66 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
67 | loader: 'url-loader',
68 | options: {
69 | limit: 10000,
70 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
71 | }
72 | },
73 | {
74 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
75 | loader: 'url-loader',
76 | options: {
77 | limit: 10000,
78 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
79 | }
80 | }
81 | ]
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/client/build/webpack.dev.conf.js:
--------------------------------------------------------------------------------
1 | var utils = require('./utils')
2 | var webpack = require('webpack')
3 | var config = require('../config')
4 | var merge = require('webpack-merge')
5 | var baseWebpackConfig = require('./webpack.base.conf')
6 | var HtmlWebpackPlugin = require('html-webpack-plugin')
7 | var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
8 |
9 | // add hot-reload related code to entry chunks
10 | Object.keys(baseWebpackConfig.entry).forEach(function (name) {
11 | baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name])
12 | })
13 |
14 | module.exports = merge(baseWebpackConfig, {
15 | module: {
16 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap })
17 | },
18 | // cheap-module-eval-source-map is faster for development
19 | devtool: '#cheap-module-eval-source-map',
20 | plugins: [
21 | new webpack.DefinePlugin({
22 | 'process.env': config.dev.env
23 | }),
24 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
25 | new webpack.HotModuleReplacementPlugin(),
26 | new webpack.NoEmitOnErrorsPlugin(),
27 | // https://github.com/ampedandwired/html-webpack-plugin
28 | new HtmlWebpackPlugin({
29 | filename: 'index.html',
30 | template: 'index.html',
31 | inject: true
32 | }),
33 | new FriendlyErrorsPlugin()
34 | ]
35 | })
36 |
--------------------------------------------------------------------------------
/client/build/webpack.prod.conf.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var utils = require('./utils')
3 | var webpack = require('webpack')
4 | var config = require('../config')
5 | var merge = require('webpack-merge')
6 | var baseWebpackConfig = require('./webpack.base.conf')
7 | var CopyWebpackPlugin = require('copy-webpack-plugin')
8 | var HtmlWebpackPlugin = require('html-webpack-plugin')
9 | var ExtractTextPlugin = require('extract-text-webpack-plugin')
10 | var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
11 |
12 | var env = process.env.NODE_ENV === 'testing'
13 | ? require('../config/test.env')
14 | : config.build.env
15 |
16 | var webpackConfig = merge(baseWebpackConfig, {
17 | module: {
18 | rules: utils.styleLoaders({
19 | sourceMap: config.build.productionSourceMap,
20 | extract: true
21 | })
22 | },
23 | devtool: config.build.productionSourceMap ? '#source-map' : false,
24 | output: {
25 | path: config.build.assetsRoot,
26 | filename: utils.assetsPath('js/[name].[chunkhash].js'),
27 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
28 | },
29 | plugins: [
30 | // http://vuejs.github.io/vue-loader/en/workflow/production.html
31 | new webpack.DefinePlugin({
32 | 'process.env': env
33 | }),
34 | new webpack.optimize.UglifyJsPlugin({
35 | compress: {
36 | warnings: false
37 | },
38 | sourceMap: true
39 | }),
40 | // extract css into its own file
41 | new ExtractTextPlugin({
42 | filename: utils.assetsPath('css/[name].[contenthash].css')
43 | }),
44 | // Compress extracted CSS. We are using this plugin so that possible
45 | // duplicated CSS from different components can be deduped.
46 | new OptimizeCSSPlugin({
47 | cssProcessorOptions: {
48 | safe: true
49 | }
50 | }),
51 | // generate dist index.html with correct asset hash for caching.
52 | // you can customize output by editing /index.html
53 | // see https://github.com/ampedandwired/html-webpack-plugin
54 | new HtmlWebpackPlugin({
55 | filename: process.env.NODE_ENV === 'testing'
56 | ? 'index.html'
57 | : config.build.index,
58 | template: 'index.html',
59 | inject: true,
60 | minify: {
61 | removeComments: true,
62 | collapseWhitespace: true,
63 | removeAttributeQuotes: true
64 | // more options:
65 | // https://github.com/kangax/html-minifier#options-quick-reference
66 | },
67 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin
68 | chunksSortMode: 'dependency'
69 | }),
70 | // split vendor js into its own file
71 | new webpack.optimize.CommonsChunkPlugin({
72 | name: 'vendor',
73 | minChunks: function (module, count) {
74 | // any required modules inside node_modules are extracted to vendor
75 | return (
76 | module.resource &&
77 | /\.js$/.test(module.resource) &&
78 | module.resource.indexOf(
79 | path.join(__dirname, '../node_modules')
80 | ) === 0
81 | )
82 | }
83 | }),
84 | // extract webpack runtime and module manifest to its own file in order to
85 | // prevent vendor hash from being updated whenever app bundle is updated
86 | new webpack.optimize.CommonsChunkPlugin({
87 | name: 'manifest',
88 | chunks: ['vendor']
89 | }),
90 | // copy custom static assets
91 | new CopyWebpackPlugin([
92 | {
93 | from: path.resolve(__dirname, '../static'),
94 | to: config.build.assetsSubDirectory,
95 | ignore: ['.*']
96 | }
97 | ])
98 | ]
99 | })
100 |
101 | if (config.build.productionGzip) {
102 | var CompressionWebpackPlugin = require('compression-webpack-plugin')
103 |
104 | webpackConfig.plugins.push(
105 | new CompressionWebpackPlugin({
106 | asset: '[path].gz[query]',
107 | algorithm: 'gzip',
108 | test: new RegExp(
109 | '\\.(' +
110 | config.build.productionGzipExtensions.join('|') +
111 | ')$'
112 | ),
113 | threshold: 10240,
114 | minRatio: 0.8
115 | })
116 | )
117 | }
118 |
119 | if (config.build.bundleAnalyzerReport) {
120 | var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
121 | webpackConfig.plugins.push(new BundleAnalyzerPlugin())
122 | }
123 |
124 | module.exports = webpackConfig
125 |
--------------------------------------------------------------------------------
/client/build/webpack.test.conf.js:
--------------------------------------------------------------------------------
1 | // This is the webpack config used for unit tests.
2 |
3 | var utils = require('./utils')
4 | var webpack = require('webpack')
5 | var merge = require('webpack-merge')
6 | var baseConfig = require('./webpack.base.conf')
7 |
8 | var webpackConfig = merge(baseConfig, {
9 | // use inline sourcemap for karma-sourcemap-loader
10 | module: {
11 | rules: utils.styleLoaders()
12 | },
13 | devtool: '#inline-source-map',
14 | resolveLoader: {
15 | alias: {
16 | // necessary to to make lang="scss" work in test when using vue-loader's ?inject option
17 | // see discussion at https://github.com/vuejs/vue-loader/issues/724
18 | 'scss-loader': 'sass-loader'
19 | }
20 | },
21 | plugins: [
22 | new webpack.DefinePlugin({
23 | 'process.env': require('../config/test.env')
24 | })
25 | ]
26 | })
27 |
28 | // no need for app entry during tests
29 | delete webpackConfig.entry
30 |
31 | module.exports = webpackConfig
32 |
--------------------------------------------------------------------------------
/client/config/dev.env.js:
--------------------------------------------------------------------------------
1 | var merge = require('webpack-merge')
2 | var prodEnv = require('./prod.env')
3 |
4 | module.exports = merge(prodEnv, {
5 | NODE_ENV: '"development"'
6 | })
7 |
--------------------------------------------------------------------------------
/client/config/index.js:
--------------------------------------------------------------------------------
1 | // see http://vuejs-templates.github.io/webpack for documentation.
2 | var path = require('path')
3 |
4 | module.exports = {
5 | build: {
6 | env: require('./prod.env'),
7 | index: path.resolve(__dirname, '../dist/index.html'),
8 | assetsRoot: path.resolve(__dirname, '../dist'),
9 | assetsSubDirectory: 'static',
10 | assetsPublicPath: '/',
11 | productionSourceMap: true,
12 | // Gzip off by default as many popular static hosts such as
13 | // Surge or Netlify already gzip all static assets for you.
14 | // Before setting to `true`, make sure to:
15 | // npm install --save-dev compression-webpack-plugin
16 | productionGzip: false,
17 | productionGzipExtensions: ['js', 'css'],
18 | // Run the build command with an extra argument to
19 | // View the bundle analyzer report after build finishes:
20 | // `npm run build --report`
21 | // Set to `true` or `false` to always turn it on or off
22 | bundleAnalyzerReport: process.env.npm_config_report
23 | },
24 | dev: {
25 | env: require('./dev.env'),
26 | port: 8080,
27 | autoOpenBrowser: false,
28 | assetsSubDirectory: 'static',
29 | assetsPublicPath: '/',
30 | proxyTable: {
31 | // proxy all requests starting with /api to jsonplaceholder
32 | '/api': {
33 | target: 'http://127.0.0.1:8889/api',
34 | changeOrigin: true,
35 | pathRewrite: {
36 | '^/api': '' // 若target中没有/api、这里又为空,则404;
37 | }
38 | }
39 | },
40 | // CSS Sourcemaps off by default because relative paths are "buggy"
41 | // with this option, according to the CSS-Loader README
42 | // (https://github.com/webpack/css-loader#sourcemaps)
43 | // In our experience, they generally work as expected,
44 | // just be aware of this issue when enabling this option.
45 | cssSourceMap: true
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/client/config/prod.env.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | NODE_ENV: '"production"'
3 | }
4 |
--------------------------------------------------------------------------------
/client/config/test.env.js:
--------------------------------------------------------------------------------
1 | var merge = require('webpack-merge')
2 | var devEnv = require('./dev.env')
3 |
4 | module.exports = merge(devEnv, {
5 | NODE_ENV: '"testing"'
6 | })
7 |
--------------------------------------------------------------------------------
/client/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | client
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "1.0.0",
4 | "description": "A Vue.js project width Node",
5 | "author": "Jam ",
6 | "private": true,
7 | "scripts": {
8 | "dev": "node build/dev-server.js",
9 | "start": "node build/dev-server.js",
10 | "build": "node build/build.js",
11 | "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
12 | "e2e": "node test/e2e/runner.js",
13 | "test": "npm run unit && npm run e2e",
14 | "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs"
15 | },
16 | "dependencies": {
17 | "axios": "^0.16.2",
18 | "vue": "^2.3.3",
19 | "vue-infinite-loading": "^2.1.3",
20 | "vue-lazyload": "^1.0.6",
21 | "vue-router": "^2.3.1",
22 | "vue-swipe": "^2.0.3",
23 | "vuex": "^2.3.1"
24 | },
25 | "devDependencies": {
26 | "autoprefixer": "^6.7.2",
27 | "babel-core": "^6.22.1",
28 | "babel-eslint": "^7.1.1",
29 | "babel-loader": "^6.2.10",
30 | "babel-plugin-istanbul": "^4.1.1",
31 | "babel-plugin-transform-runtime": "^6.22.0",
32 | "babel-preset-env": "^1.3.2",
33 | "babel-preset-stage-2": "^6.22.0",
34 | "babel-register": "^6.22.0",
35 | "chai": "^3.5.0",
36 | "chalk": "^1.1.3",
37 | "chromedriver": "^2.27.2",
38 | "connect-history-api-fallback": "^1.3.0",
39 | "copy-webpack-plugin": "^4.0.1",
40 | "cross-env": "^4.0.0",
41 | "cross-spawn": "^5.0.1",
42 | "css-loader": "^0.28.0",
43 | "eslint": "^3.19.0",
44 | "eslint-config-standard": "^6.2.1",
45 | "eslint-friendly-formatter": "^2.0.7",
46 | "eslint-loader": "^1.7.1",
47 | "eslint-plugin-html": "^2.0.0",
48 | "eslint-plugin-promise": "^3.4.0",
49 | "eslint-plugin-standard": "^2.0.1",
50 | "eventsource-polyfill": "^0.9.6",
51 | "express": "^4.14.1",
52 | "extract-text-webpack-plugin": "^2.0.0",
53 | "file-loader": "^0.11.1",
54 | "friendly-errors-webpack-plugin": "^1.1.3",
55 | "html-webpack-plugin": "^2.28.0",
56 | "http-proxy-middleware": "^0.17.3",
57 | "inject-loader": "^3.0.0",
58 | "karma": "^1.4.1",
59 | "karma-coverage": "^1.1.1",
60 | "karma-mocha": "^1.3.0",
61 | "karma-phantomjs-launcher": "^1.0.2",
62 | "karma-phantomjs-shim": "^1.4.0",
63 | "karma-sinon-chai": "^1.3.1",
64 | "karma-sourcemap-loader": "^0.3.7",
65 | "karma-spec-reporter": "0.0.30",
66 | "karma-webpack": "^2.0.2",
67 | "lolex": "^1.5.2",
68 | "mocha": "^3.2.0",
69 | "nightwatch": "^0.9.12",
70 | "node-sass": "^4.5.3",
71 | "opn": "^4.0.2",
72 | "optimize-css-assets-webpack-plugin": "^1.3.0",
73 | "ora": "^1.2.0",
74 | "phantomjs-prebuilt": "^2.1.14",
75 | "rimraf": "^2.6.0",
76 | "sass-loader": "^6.0.5",
77 | "selenium-server": "^3.0.1",
78 | "semver": "^5.3.0",
79 | "shelljs": "^0.7.6",
80 | "sinon": "^2.1.0",
81 | "sinon-chai": "^2.8.0",
82 | "url-loader": "^0.5.8",
83 | "vue-loader": "^12.1.0",
84 | "vue-style-loader": "^3.0.1",
85 | "vue-template-compiler": "^2.3.3",
86 | "webpack": "^2.6.1",
87 | "webpack-bundle-analyzer": "^2.2.1",
88 | "webpack-dev-middleware": "^1.10.0",
89 | "webpack-hot-middleware": "^2.18.0",
90 | "webpack-merge": "^4.1.0"
91 | },
92 | "engines": {
93 | "node": ">= 4.0.0",
94 | "npm": ">= 3.0.0"
95 | },
96 | "browserslist": [
97 | "> 1%",
98 | "last 2 versions",
99 | "not ie <= 8"
100 | ]
101 | }
102 |
--------------------------------------------------------------------------------
/client/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
31 |
32 |
37 |
--------------------------------------------------------------------------------
/client/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flydoing/vue-node-proj/f6c4f5ef1c6d2b7a2497ce73c7a3c0fc7f3aaf98/client/src/assets/logo.png
--------------------------------------------------------------------------------
/client/src/components/Hello.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ msg }}
4 |
Essential Links
5 |
测试改改
6 |
14 |
Ecosystem
15 |
21 |
22 |
23 |
24 |
34 |
35 |
36 |
55 |
--------------------------------------------------------------------------------
/client/src/components/cart/cart.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 |
9 |
10 |
11 |
{{cart.brand_name}}
12 |
17 |
18 | ¥{{cart.brand_price*cart.cart_num}}
19 | 删除
20 |
21 | 购物车为空
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
总计 :¥{{totalNowPrice}}
32 |
33 |
去结算
34 |
35 |
36 |
37 |
38 |
162 |
163 |
166 |
--------------------------------------------------------------------------------
/client/src/components/cate/cate.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
24 |
25 |
26 | -
27 |
28 |
29 |
30 | {{brand.brand_name}}
31 | {{brand.brand_price}}
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
129 |
130 |
133 |
--------------------------------------------------------------------------------
/client/src/components/cate/detail.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{detailData.brand_name}}
6 |
¥{{detailData.brand_price}}
7 |
12 |
13 |
17 |
18 |
19 |
20 |
124 |
125 |
128 |
--------------------------------------------------------------------------------
/client/src/components/center/center.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
用户:{{dataLogin.name}}
6 |
注册
7 |
登录
8 |
9 |
12 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
31 |
36 |
37 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
53 |
54 |
58 |
59 |
60 |
61 |
62 |
208 |
209 |
212 |
--------------------------------------------------------------------------------
/client/src/components/com/header.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
33 |
34 |
71 |
--------------------------------------------------------------------------------
/client/src/components/com/jam.js:
--------------------------------------------------------------------------------
1 | export default class jam {
2 | // constructor () {
3 | // }
4 | locDbSet (name, obj) {
5 | if (!name || !obj) {
6 | console.log('name,obj:参数不能为空!')
7 | return
8 | }
9 | window.localStorage.setItem(name, JSON.stringify(obj))
10 | }
11 | locDbGet (name) {
12 | if (!name) {
13 | console.log('locDbGet(name): name参数不能为空!')
14 | return
15 | }
16 | // return JSON.parse(window.localStorage.getItem(name)) || {error: '不存在'}
17 | return JSON.parse(window.localStorage.getItem(name))
18 | }
19 | isPhone (str) {
20 | var pattern1 = /^(0|86|17951)?(13[0-9]|15[012356789]|17[0-9]|18[0-9]|14[57])[0-9]{8}$/
21 | if (pattern1.test(str)) {
22 | return true
23 | } else {
24 | return false
25 | }
26 | }
27 | isPass (str) {
28 | var pattern2 = /^\w{6}$/
29 | if (pattern2.test(str)) {
30 | return true
31 | } else {
32 | return false
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/client/src/components/com/loading.vue:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flydoing/vue-node-proj/f6c4f5ef1c6d2b7a2497ce73c7a3c0fc7f3aaf98/client/src/components/com/loading.vue
--------------------------------------------------------------------------------
/client/src/components/com/localDB.js:
--------------------------------------------------------------------------------
1 | export default class todoDb {
2 | constructor (name) {
3 | this.name = name
4 | if (JSON.stringify(this.get(this.name)) === '{}') {
5 | this.set([])
6 | }
7 | }
8 | set (val) {
9 | window.localStorage.setItem(this.name, JSON.stringify(val))
10 | }
11 | get () {
12 | return JSON.parse(window.localStorage.getItem(this.name)) || {}
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/client/src/components/com/sidebar.vue:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
40 |
41 |
115 |
--------------------------------------------------------------------------------
/client/src/components/com/swiper.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
38 |
39 |
52 |
--------------------------------------------------------------------------------
/client/src/components/page/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 特卖
8 |
9 | 更多 >
10 |
11 |
12 |
13 | {{brand.brand_name}}
14 | ¥{{brand.brand_price}}
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | 热销
24 |
25 | 更多 >
26 |
27 |
28 |
32 | {{brand.brand_name}}
33 | {{brand.brand_desc}}
34 |
35 |
36 |
37 |
38 |
42 |
43 | {{brand.brand_name}}
44 | {{brand.brand_desc}}
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | 精品
56 |
57 | 更多 >
58 |
59 |
60 |
61 | -
62 |
63 |
64 |
65 | {{brand.brand_name}}
66 | ¥{{brand.brand_price}}
67 |
68 |
69 |
70 |
71 | 没有更多数据了
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
157 |
158 |
161 |
--------------------------------------------------------------------------------
/client/src/css/base.scss:
--------------------------------------------------------------------------------
1 | /*base*/
2 | body,div,ul,li,h3,h4,h5,form,input,button,textarea,p {margin: 0;padding: 0;font-family: "Microsoft YaHei";font-size: 12px;border:none;}
3 | address,em,strong,h3,h4,h5,i{font-style: normal;font-weight: normal;}
4 | ul,li{list-style: none;}
5 | a{color: black;text-decoration: none;}
6 | img{border: 0;}
7 | html{font-size: 40px;background: #ffffff;-webkit-overflow-scrolling: touch;}
8 | body{position:relative;max-width: 640px;min-width: 320px;margin:0 auto;background: #ffffff;}
9 | .shop{
10 | position:relative;
11 | padding:50px 0;
12 | }
13 | //路由的active
14 |
--------------------------------------------------------------------------------
/client/src/css/cart.scss:
--------------------------------------------------------------------------------
1 | .s-cart{
2 | position: relative;
3 | }
4 | .cart-cont{
5 | margin: .25rem 0 .5rem;
6 | padding: 0 .25rem;
7 | border-top: 1px solid #eee;
8 | background: #ffffff;
9 | .cont-one{
10 | height: 2rem;
11 | overflow: hidden;
12 | padding: .25rem;
13 | border-bottom: 1px solid #eee;
14 | .goods-checkbox{
15 | float: left;
16 | height: 2rem;
17 | line-height: 2rem;
18 | }
19 | .goods-a{
20 | float: left;
21 | width: 1.8rem;
22 | height: 2rem;
23 | margin: 0 .25rem;
24 | .goods-img{
25 | display: inline-block;
26 | width: 1.8rem;
27 | height: 2rem;
28 | }
29 | }
30 | .goods-info{
31 | float: left;
32 | width: 7.2rem;
33 | // background: yellow;
34 | .goods-name{
35 | min-width: 7.5rem;
36 | overflow: hidden;
37 | white-space: nowrap;
38 | text-overflow: ellipsis;
39 | font-size: .38rem;
40 | margin-bottom: .2rem;
41 | }
42 | .goods-counter{
43 | a{
44 | padding: .125rem .8rem;
45 | }
46 | .goods-num{
47 | width: 1.4rem;
48 | border: 1px solid #eee;
49 | }
50 | }
51 | }
52 | .goods-price{
53 | display: inline-block;
54 | height: 2rem;
55 | line-height: 2rem;
56 | font-size: .5rem;
57 | color: #fc8637;
58 | }
59 | .goods-delete{
60 | float: right;
61 | height: 2rem;
62 | padding: 0 .3rem;
63 | font-size: 0.4rem;
64 | line-height: 2rem;
65 | }
66 | }
67 | }
68 | .cart-counter{
69 | position: fixed;
70 | bottom: 0;
71 | z-index: 100;
72 | height: 50px;
73 | line-height: 50px;
74 | width:16rem;
75 | max-width: 16rem;
76 | min-width: 320px;
77 | margin:0 auto;
78 | border-top: 1px solid #eee;
79 | .all-checkbox,.all-price,.btn-counter{
80 | display: inline-block;
81 | height: 50px;
82 | font-size: .4rem;
83 | }
84 | .all-checkbox{
85 | padding-left: .8rem;
86 | }
87 | .all-price .price-p{
88 | margin-left: .8rem;
89 | font-size: .4rem;
90 | .price{
91 | color: #fc8637;
92 | }
93 | }
94 | .btn-counter{
95 | float: right;
96 | color: red;
97 | margin-right: .8rem;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/client/src/css/cate.scss:
--------------------------------------------------------------------------------
1 | .cate-nav{
2 | position: fixed;
3 | width: 4.5rem;
4 | height: 8.5rem; //动态变动
5 | overflow: hidden;
6 | background: #f6f6f6;
7 | .nav-out{
8 | // width: 4.5rem;
9 | width: 4.8rem;
10 | height: 8.5rem; //动态变动
11 | overflow-x: hidden;
12 | overflow-y: auto;
13 | border-top: 1px solid #eee;
14 | border-bottom: 1px solid #eee;
15 | .nav-a{
16 | display: block;
17 | height: 1.5rem;
18 | line-height: 1.5rem;
19 | text-align: center;
20 | font-size: .625rem;
21 | color: #666666;
22 | border-bottom: 1px solid #eee;
23 | }
24 | .nav-a-act{
25 | background: #ffffff;
26 | &:before{
27 | content: '';
28 | float: left;
29 | width: 1px;
30 | height: 100%;
31 | border-right: 5px solid #282828;
32 | }
33 | }
34 | }
35 | }
36 | .cate-cont{
37 | position: relative;
38 | overflow: hidden;
39 | margin-top: .25rem;
40 | margin-left: 4.5rem;
41 | background: #ffffff;
42 | li{
43 | display: inline-block;
44 | float: left;
45 | width: 33.3%;
46 | }
47 | .cont-li{
48 | display: inline-block;
49 | margin-bottom: .4rem;
50 | margin-left: .2rem;
51 | padding: .275rem;
52 | border-radius: .2rem;
53 | border: 1px solid #eee;
54 | background: #ffffff;
55 | .pic{
56 | display: block;
57 | width: 2.8rem;
58 | height: 3rem;
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/client/src/css/center.scss:
--------------------------------------------------------------------------------
1 | $border:#ccc;
2 | div{
3 | // color: red;
4 | }
5 | fieldset{
6 | text-align: center;
7 | color: #dbdbdb;
8 | border: none;
9 | border-top: 1px solid #dbdbdb;
10 | }
11 |
12 | .s-center{
13 | position: relative;
14 | }
15 | .ban{
16 | height: 5rem;
17 | text-align: center;
18 | background: rgba(0,0,0,0.1);
19 | .uname{
20 | padding-top: 2rem;
21 | font-size: .4rem;
22 | color: #555;
23 | span{
24 | display: inline-block;
25 | padding: 0 .8rem;
26 | cursor: pointer;
27 | font-size: .8rem;
28 | }
29 | }
30 | }
31 | .cont-center{
32 | .item{
33 | margin: .375rem 0 .75rem;
34 | border-top: 1px solid $border;
35 | border-bottom: 1px solid $border;
36 | a{
37 | display: block;
38 | margin: 0 .375rem;
39 | padding: .375rem 0;
40 | font-size: .8rem;
41 | border-bottom: 1px solid $border;
42 | i{
43 | float: right;
44 | }
45 | }
46 | &>a:last-child{
47 | border-bottom: none;
48 | }
49 | }
50 | }
51 | .form-item{
52 | position: relative;
53 | height: 1.8rem;
54 | line-height: 1.8rem;
55 | padding: 0 .5rem;
56 | border-bottom: 1px solid $border;
57 | label,input,button,p{
58 | font-size: .8rem;
59 | outline: none;
60 | }
61 | input{
62 | width: 7rem;
63 | padding: 0 0 0 .375rem;
64 | border: none;
65 | }
66 | .form-tips{
67 | color: red;
68 | }
69 | .btn-get{
70 | position: absolute;
71 | right: 0;
72 | width: 5.2rem;
73 | height: 1.8rem;
74 | text-align: center;
75 | line-height: 1.8rem;
76 | font-size: .8rem;
77 | color: #4a90e2;
78 | background-color: #ececec;
79 | }
80 | }
81 | .btn-div{
82 | text-align: center;
83 | .btn-a{
84 | display: inline-block;
85 | height: 1.8rem;
86 | line-height: 1.8rem;
87 | margin: 1.2rem 0 .8rem;
88 | padding: 0 2.5rem;
89 | color: #4a90e2;
90 | font-size: .8rem;
91 | border: 1px solid #4a90e2;
92 | border-radius: .425rem;
93 | }
94 | }
95 | .form-fieldset{
96 | margin: .5rem 0 .8rem;
97 | legend{
98 | margin-bottom: .25rem;
99 | }
100 | }
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/client/src/css/detail.scss:
--------------------------------------------------------------------------------
1 | .s-detail{
2 | position: relative;
3 | }
4 | .s-detail{
5 | .ban{
6 | height: auto;
7 | width: 100%;
8 | img{
9 | width: 100%;
10 | height: auto;
11 | }
12 | }
13 | }
14 | .cont{
15 | margin: .3rem 0;
16 | padding: 0 .375rem;
17 | .name{
18 | font-size: .45rem;
19 | }
20 | .price{
21 | text-align: center;
22 | font-size: .5rem;
23 | color: red;
24 | }
25 | .goods-counter{
26 | height: .75rem;
27 | a{
28 | display: inline-block;
29 | height: .75rem;
30 | line-height: .75rem;
31 | font-size: .3rem;
32 | padding: 0 .8rem;
33 | border: 1px solid #eee;
34 | }
35 | .goods-num{
36 | height: .75rem;
37 | line-height: .75rem;
38 | width: 1.4rem;
39 | text-align: center;
40 | font-size: .4rem;
41 | border: 1px solid #eee;
42 | }
43 | }
44 | }
45 | .bot{
46 | position: fixed;
47 | bottom: 0;
48 | z-index: 100;
49 | height: 50px;
50 | width:16rem;
51 | max-width: 16rem;
52 | min-width: 320px;
53 | margin:0 auto;
54 | color: #fff;
55 | border-top: 1px solid #eee;
56 | background: #ffffff;
57 | .add-cart{
58 | float: right;
59 | height: 34px;
60 | margin-top: 8px;
61 | margin-right: .5rem;
62 | padding: 0 15px;
63 | line-height: 34px;
64 | font-size: .5rem;
65 | color: red;
66 | border: 1px solid red;
67 | border-radius: 4px;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/client/src/css/index.scss:
--------------------------------------------------------------------------------
1 | .s-index{
2 | position: relative;
3 | background-color: #f6f6f6;
4 | }
5 | .cont{
6 | margin-top: .4rem;
7 | }
8 | .cont-head{
9 | height: 1.5rem;
10 | line-height: 1.5rem;
11 | padding: 0 .25rem;
12 | font-size: .7rem;
13 | border-top: 1px solid #eee;
14 | border-bottom: 1px solid #eee;
15 | background-color: #ffffff;
16 | &:before{
17 | content: '';
18 | width: 1px;
19 | height: 100%;
20 | margin-right: .125rem;
21 | border-right: 5px solid #282828;
22 | }
23 | .head-title{
24 | // font-size: .4rem;
25 | }
26 | .head-right{
27 | float: right;
28 | color: #666;
29 | // font-size: .4rem;
30 | }
31 | }
32 | .cont-main{
33 | background-color: #ffffff;
34 | .name{
35 | font-size: .7rem;
36 | }
37 | .desc{
38 | font-size: .6rem;
39 | color: #666;
40 | }
41 | .price{
42 | font-size: .7rem;
43 | color: #fc8637;
44 | }
45 | }
46 | .cont-temai{
47 | overflow: hidden;
48 | padding: .375rem 0;
49 | text-align: center;
50 | .cont-one{
51 | float: left;
52 | width: 33%;
53 | overflow: hidden;
54 | border-right: 1px solid #eee;
55 | span{
56 | display: block;
57 | }
58 | .desc{
59 | margin: .125rem 0 .25rem;
60 | }
61 | .pic{
62 | width: 80%;
63 | }
64 | }
65 | &>:last-child{
66 | border-right: none;
67 | }
68 | }
69 | .cont-rexiao{
70 | height: 9rem;
71 | overflow: hidden;
72 | text-align: center;
73 | .cont-left{
74 | float: left;
75 | width: 5.6rem;
76 | height: 8.6rem;
77 | padding: 0.2rem;
78 | border-right: 1px solid #eee;
79 | span{
80 | display: block;
81 | }
82 | .desc{
83 | margin: .125rem 0 .25rem;
84 | }
85 | .pic{
86 | width: 5rem;
87 | height: 6rem;
88 | }
89 | }
90 | .cont-right{
91 | float: left;
92 | width: 9.2rem;
93 | height: 9rem;
94 | &>:first-child{
95 | border-bottom: 1px solid #eee;
96 | }
97 | .cont-right-one{
98 | display: block;
99 | height: 4.1rem;
100 | padding: 0.2rem;
101 | .text{
102 | float: left;
103 | width: 4.4rem;
104 | text-align: right;
105 | span{
106 | display: block;
107 | }
108 | .name{
109 | margin: .85rem 0 .125rem;
110 | }
111 | }
112 | .pic{
113 | float: right;
114 | width: 4.1rem;
115 | height: 4.1rem;
116 | }
117 | }
118 | }
119 | }
120 | .cont-jingpin{
121 | position: relative;
122 | overflow: hidden;
123 | padding-bottom: .4rem;
124 | // background-color: #f6f6f6;
125 | li{
126 | display: inline-block;
127 | float: left;
128 | width: 50%;
129 | }
130 | .cont-li{
131 | display: inline-block;
132 | margin-top: .4rem;
133 | margin-left: .3rem;
134 | padding: .375rem;
135 | border-radius: .2rem;
136 | border: 1px solid #eee;
137 | background: #ffffff;
138 | .pic{
139 | display: block;
140 | width: 6.5rem;
141 | height: 8.125rem;
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/client/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import router from './router/router.js'
3 | import store from './store/store.js'
4 | import axios from 'axios'
5 | Vue.prototype.$http = axios
6 |
7 | import VueLazyload from 'vue-lazyload'
8 | Vue.use(VueLazyload)
9 |
10 | const app = new Vue({
11 | router,
12 | store
13 | }).$mount('#app')
14 | // const app = new Vue({
15 | // router,
16 | // store,
17 | // render: h => h('.app', App)
18 | // })
19 |
20 | export default app
21 |
--------------------------------------------------------------------------------
/client/src/router/router.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueRouter from 'vue-router'
3 | Vue.use(VueRouter)
4 |
5 | import App from '../App.vue'
6 | import Index from '../components/page/index.vue'
7 | import Cate from '../components/cate/cate.vue'
8 | import Detail from '../components/cate/detail.vue'
9 | import Center from '../components/center/center.vue'
10 | import Cart from '../components/cart/cart.vue'
11 |
12 | export default new VueRouter({
13 | routes: [
14 | {
15 | path: '/',
16 | redirect: '/index',
17 | component: App,
18 | children: [
19 | {path: 'index', name: 'index', component: Index},
20 | {path: 'cate', name: 'cate', component: Cate},
21 | // {path: 'detail', name: 'detail', component: Detail},
22 | {path: 'detail/:id', name: 'detail', component: Detail},
23 | {path: 'center', name: 'center', component: Center},
24 | {path: 'cart', name: 'cart', component: Cart}
25 | ]
26 | }
27 | ],
28 | linkActiveClass: 'footer-act'
29 | })
30 |
--------------------------------------------------------------------------------
/client/src/store/store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | Vue.use(Vuex)
4 | export default new Vuex.Store({
5 | state: {
6 | sideBarState: false,
7 | headerTitle: '默认的头部标题'
8 | },
9 | mutations: {
10 | changeSideBarState (state, boolean) {
11 | state.sideBarState = boolean
12 | },
13 | changeHeaderTitle (state, str) {
14 | state.headerTitle = str
15 | }
16 | },
17 | actions: {
18 | // changeSideBarState (context, status) {
19 | // context.commit('changeSideBarState', status)
20 | // }
21 | // es6解构写法
22 | changeSideBarState ({commit}, status) {
23 | commit('changeSideBarState', status)
24 | },
25 | changeHeaderTitle ({commit}, str) {
26 | commit('changeHeaderTitle', str)
27 | }
28 | },
29 | getters: {
30 | getSideBarState (state) {
31 | return state.sideBarState
32 | },
33 | getHeaderTitle (state) {
34 | return state.headerTitle
35 | }
36 | }
37 | })
38 |
--------------------------------------------------------------------------------
/client/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flydoing/vue-node-proj/f6c4f5ef1c6d2b7a2497ce73c7a3c0fc7f3aaf98/client/static/.gitkeep
--------------------------------------------------------------------------------
/client/static/data/cart.json:
--------------------------------------------------------------------------------
1 | {
2 | "state": 1,
3 | "msg": "success",
4 | "data": {
5 | "allSelect": false,
6 | "allSeclectPrice": 0,
7 | "carts": [
8 | {
9 | "id": 100048,
10 | "type": "type_man",
11 | "isSelect": false,
12 | "cart_img": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg",
13 | "cart_name": "商品名字1",
14 | "cart_num": 1,
15 | "cart_price": 19
16 | },
17 | {
18 | "id": 100048,
19 | "type": "type_man",
20 | "isSelect": false,
21 | "cart_img": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg",
22 | "cart_name": "商品名字2",
23 | "cart_num": 2,
24 | "cart_price": 29
25 | },
26 | {
27 | "id": 100048,
28 | "type": "type_man",
29 | "isSelect": false,
30 | "cart_img": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg",
31 | "cart_name": "商品名字3",
32 | "cart_num": 1,
33 | "cart_price": 39
34 | }
35 |
36 | ]
37 | }
38 | }
--------------------------------------------------------------------------------
/client/static/data/cate.json:
--------------------------------------------------------------------------------
1 | {
2 | "state": 1,
3 | "msg": "success",
4 | "data": {
5 | "types": [
6 | {
7 | "type_name": "全部",
8 | "type_now": "type_all"
9 | },
10 | {
11 | "type_name": "男装",
12 | "type_now": "type_man"
13 | },
14 | {
15 | "type_name": "女装",
16 | "type_now": "type_girl"
17 | },
18 | {
19 | "type_name": "男装",
20 | "type_now": "type_man"
21 | },
22 | {
23 | "type_name": "男装",
24 | "type_now": "type_man"
25 | },
26 | {
27 | "type_name": "女装",
28 | "type_now": "type_girl"
29 | },
30 | {
31 | "type_name": "女装",
32 | "type_now": "type_girl"
33 | }
34 | ],
35 | "allBrand": [
36 | {
37 | "id": 100048,
38 | "type": "type_man",
39 | "brand_price": 79.9,
40 | "brand_name": "商品名字1",
41 | "brand_desc": "商品描述商品描述",
42 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
43 | },
44 | {
45 | "id": 100047,
46 | "type": "type_man",
47 | "brand_price": 89.9,
48 | "brand_name": "商品名字2",
49 | "brand_desc": "商品描述商品描述",
50 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
51 | },
52 | {
53 | "id": 100041,
54 | "type": "type_girl",
55 | "brand_price": 99.9,
56 | "brand_name": "商品名字3",
57 | "brand_desc": "商品描述商品描述",
58 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
59 | },
60 | {
61 | "id": 100048,
62 | "type": "type_girl",
63 | "brand_price": 49.9,
64 | "brand_name": "商品名字4",
65 | "brand_desc": "商品描述商品描述",
66 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
67 | },
68 | {
69 | "id": 100047,
70 | "type": "type_girl",
71 | "brand_price": 59.9,
72 | "brand_name": "商品名字5",
73 | "brand_desc": "商品描述商品",
74 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
75 | },
76 | {
77 | "id": 100041,
78 | "type": "type_girl",
79 | "brand_price": 69.9,
80 | "brand_name": "商品名字6",
81 | "brand_desc": "商品描述商品",
82 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
83 | },
84 | {
85 | "id": 100048,
86 | "type": "type_girl",
87 | "brand_price": 19.9,
88 | "brand_name": "商品名1字",
89 | "brand_desc": "商品描述商品描述",
90 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
91 | },
92 | {
93 | "id": 100047,
94 | "type": "type_girl",
95 | "brand_price": 29.9,
96 | "brand_name": "商品名2字",
97 | "brand_desc": "商品描述商品描述",
98 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
99 | },
100 | {
101 | "id": 100041,
102 | "type": "type_man",
103 | "brand_price": 39.9,
104 | "brand_name": "商品名3字",
105 | "brand_desc": "商品描述商品描述",
106 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
107 | },
108 | {
109 | "id": 100042,
110 | "type": "type_man",
111 | "brand_price": 49.9,
112 | "brand_name": "商品名4字",
113 | "brand_desc": "商品描述商品描述",
114 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
115 | },
116 | {
117 | "id": 100042,
118 | "type": "type_man",
119 | "brand_price": 49.9,
120 | "brand_name": "商品名4字",
121 | "brand_desc": "商品描述商品描述",
122 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
123 | },
124 | {
125 | "id": 100042,
126 | "type": "type_man",
127 | "brand_price": 49.9,
128 | "brand_name": "商品名4字",
129 | "brand_desc": "商品描述商品描述",
130 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
131 | },
132 | {
133 | "id": 100042,
134 | "type": "type_man",
135 | "brand_price": 49.9,
136 | "brand_name": "商品名4字",
137 | "brand_desc": "商品描述商品描述",
138 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
139 | },
140 | {
141 | "id": 100042,
142 | "type": "type_man",
143 | "brand_price": 49.9,
144 | "brand_name": "商品名4字",
145 | "brand_desc": "商品描述商品描述",
146 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
147 | },
148 | {
149 | "id": 100042,
150 | "type": "type_man",
151 | "brand_price": 49.9,
152 | "brand_name": "商品名4字",
153 | "brand_desc": "商品描述商品描述",
154 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
155 | },
156 | {
157 | "id": 100042,
158 | "type": "type_man",
159 | "brand_price": 49.9,
160 | "brand_name": "商品名4字",
161 | "brand_desc": "商品描述商品描述",
162 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
163 | },
164 | {
165 | "id": 100042,
166 | "type": "type_man",
167 | "brand_price": 49.9,
168 | "brand_name": "商品名4字",
169 | "brand_desc": "商品描述商品描述",
170 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
171 | },
172 | {
173 | "id": 100042,
174 | "type": "type_man",
175 | "brand_price": 49.9,
176 | "brand_name": "商品名4字end",
177 | "brand_desc": "商品描述商品描述",
178 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
179 | }
180 | ]
181 | }
182 | }
--------------------------------------------------------------------------------
/client/static/data/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "state": 1,
3 | "msg": "success",
4 | "data": {
5 | "temai": [
6 | {
7 | "id": 100048,
8 | "type": "type_man",
9 | "brand_price": 79.9,
10 | "brand_name": "商品名字1",
11 | "brand_desc": "商品描述商品描述",
12 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
13 | },
14 | {
15 | "id": 100047,
16 | "type": "type_man",
17 | "brand_price": 89.9,
18 | "brand_name": "商品名字2",
19 | "brand_desc": "商品描述商品描述",
20 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
21 | },
22 | {
23 | "id": 100041,
24 | "type": "type_man",
25 | "brand_price": 99.9,
26 | "brand_name": "商品名字3",
27 | "brand_desc": "商品描述商品描述",
28 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
29 | }
30 | ],
31 | "rexiao": [
32 | {
33 | "id": 100048,
34 | "type": "type_man",
35 | "brand_price": 49.9,
36 | "brand_name": "商品名字4",
37 | "brand_desc": "商品描述商品描述",
38 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
39 | },
40 | {
41 | "id": 100047,
42 | "type": "type_man",
43 | "brand_price": 59.9,
44 | "brand_name": "商品名字5",
45 | "brand_desc": "商品描述商品",
46 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
47 | },
48 | {
49 | "id": 100041,
50 | "type": "type_man",
51 | "brand_price": 69.9,
52 | "brand_name": "商品名字6",
53 | "brand_desc": "商品描述商品",
54 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
55 | }
56 | ],
57 | "jingpin": [
58 | {
59 | "id": 100048,
60 | "type": "type_man",
61 | "brand_price": 19.9,
62 | "brand_name": "商品名1字",
63 | "brand_desc": "商品描述商品描述",
64 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
65 | },
66 | {
67 | "id": 100047,
68 | "type": "type_man",
69 | "brand_price": 29.9,
70 | "brand_name": "商品名2字",
71 | "brand_desc": "商品描述商品描述",
72 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
73 | },
74 | {
75 | "id": 100041,
76 | "type": "type_man",
77 | "brand_price": 39.9,
78 | "brand_name": "商品名3字",
79 | "brand_desc": "商品描述商品描述",
80 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
81 | },
82 | {
83 | "id": 100042,
84 | "type": "type_man",
85 | "brand_price": 49.9,
86 | "brand_name": "商品名4字",
87 | "brand_desc": "商品描述商品描述",
88 | "brand_pic_url": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
89 | }
90 | ]
91 | }
92 | }
--------------------------------------------------------------------------------
/client/test/e2e/custom-assertions/elementCount.js:
--------------------------------------------------------------------------------
1 | // A custom Nightwatch assertion.
2 | // the name of the method is the filename.
3 | // can be used in tests like this:
4 | //
5 | // browser.assert.elementCount(selector, count)
6 | //
7 | // for how to write custom assertions see
8 | // http://nightwatchjs.org/guide#writing-custom-assertions
9 | exports.assertion = function (selector, count) {
10 | this.message = 'Testing if element <' + selector + '> has count: ' + count
11 | this.expected = count
12 | this.pass = function (val) {
13 | return val === this.expected
14 | }
15 | this.value = function (res) {
16 | return res.value
17 | }
18 | this.command = function (cb) {
19 | var self = this
20 | return this.api.execute(function (selector) {
21 | return document.querySelectorAll(selector).length
22 | }, [selector], function (res) {
23 | cb.call(self, res)
24 | })
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/client/test/e2e/nightwatch.conf.js:
--------------------------------------------------------------------------------
1 | require('babel-register')
2 | var config = require('../../config')
3 |
4 | // http://nightwatchjs.org/gettingstarted#settings-file
5 | module.exports = {
6 | src_folders: ['test/e2e/specs'],
7 | output_folder: 'test/e2e/reports',
8 | custom_assertions_path: ['test/e2e/custom-assertions'],
9 |
10 | selenium: {
11 | start_process: true,
12 | server_path: require('selenium-server').path,
13 | host: '127.0.0.1',
14 | port: 4444,
15 | cli_args: {
16 | 'webdriver.chrome.driver': require('chromedriver').path
17 | }
18 | },
19 |
20 | test_settings: {
21 | default: {
22 | selenium_port: 4444,
23 | selenium_host: 'localhost',
24 | silent: true,
25 | globals: {
26 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port)
27 | }
28 | },
29 |
30 | chrome: {
31 | desiredCapabilities: {
32 | browserName: 'chrome',
33 | javascriptEnabled: true,
34 | acceptSslCerts: true
35 | }
36 | },
37 |
38 | firefox: {
39 | desiredCapabilities: {
40 | browserName: 'firefox',
41 | javascriptEnabled: true,
42 | acceptSslCerts: true
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/client/test/e2e/runner.js:
--------------------------------------------------------------------------------
1 | // 1. start the dev server using production config
2 | process.env.NODE_ENV = 'testing'
3 | var server = require('../../build/dev-server.js')
4 |
5 | server.ready.then(() => {
6 | // 2. run the nightwatch test suite against it
7 | // to run in additional browsers:
8 | // 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings"
9 | // 2. add it to the --env flag below
10 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox`
11 | // For more information on Nightwatch's config file, see
12 | // http://nightwatchjs.org/guide#settings-file
13 | var opts = process.argv.slice(2)
14 | if (opts.indexOf('--config') === -1) {
15 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js'])
16 | }
17 | if (opts.indexOf('--env') === -1) {
18 | opts = opts.concat(['--env', 'chrome'])
19 | }
20 |
21 | var spawn = require('cross-spawn')
22 | var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' })
23 |
24 | runner.on('exit', function (code) {
25 | server.close()
26 | process.exit(code)
27 | })
28 |
29 | runner.on('error', function (err) {
30 | server.close()
31 | throw err
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/client/test/e2e/specs/test.js:
--------------------------------------------------------------------------------
1 | // For authoring Nightwatch tests, see
2 | // http://nightwatchjs.org/guide#usage
3 |
4 | module.exports = {
5 | 'default e2e tests': function (browser) {
6 | // automatically uses dev Server port from /config.index.js
7 | // default: http://localhost:8080
8 | // see nightwatch.conf.js
9 | const devServer = browser.globals.devServerURL
10 |
11 | browser
12 | .url(devServer)
13 | .waitForElementVisible('#app', 5000)
14 | .assert.elementPresent('.hello')
15 | .assert.containsText('h1', 'Welcome to Your Vue.js App')
16 | .assert.elementCount('img', 1)
17 | .end()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/client/test/unit/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | },
5 | "globals": {
6 | "expect": true,
7 | "sinon": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/client/test/unit/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | Vue.config.productionTip = false
4 |
5 | // require all test files (files that ends with .spec.js)
6 | const testsContext = require.context('./specs', true, /\.spec$/)
7 | testsContext.keys().forEach(testsContext)
8 |
9 | // require all src files except main.js for coverage.
10 | // you can also change this to match only the subset of files that
11 | // you want coverage for.
12 | const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/)
13 | srcContext.keys().forEach(srcContext)
14 |
--------------------------------------------------------------------------------
/client/test/unit/karma.conf.js:
--------------------------------------------------------------------------------
1 | // This is a karma config file. For more details see
2 | // http://karma-runner.github.io/0.13/config/configuration-file.html
3 | // we are also using it with karma-webpack
4 | // https://github.com/webpack/karma-webpack
5 |
6 | var webpackConfig = require('../../build/webpack.test.conf')
7 |
8 | module.exports = function (config) {
9 | config.set({
10 | // to run in additional browsers:
11 | // 1. install corresponding karma launcher
12 | // http://karma-runner.github.io/0.13/config/browsers.html
13 | // 2. add it to the `browsers` array below.
14 | browsers: ['PhantomJS'],
15 | frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'],
16 | reporters: ['spec', 'coverage'],
17 | files: ['./index.js'],
18 | preprocessors: {
19 | './index.js': ['webpack', 'sourcemap']
20 | },
21 | webpack: webpackConfig,
22 | webpackMiddleware: {
23 | noInfo: true
24 | },
25 | coverageReporter: {
26 | dir: './coverage',
27 | reporters: [
28 | { type: 'lcov', subdir: '.' },
29 | { type: 'text-summary' }
30 | ]
31 | }
32 | })
33 | }
34 |
--------------------------------------------------------------------------------
/client/test/unit/specs/Hello.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Hello from '@/components/Hello'
3 |
4 | describe('Hello.vue', () => {
5 | it('should render correct contents', () => {
6 | const Constructor = Vue.extend(Hello)
7 | const vm = new Constructor().$mount()
8 | expect(vm.$el.querySelector('.hello h1').textContent)
9 | .to.equal('Welcome to Your Vue.js App')
10 | })
11 | })
12 |
--------------------------------------------------------------------------------
/s1_serverNodeBegin/index.js:
--------------------------------------------------------------------------------
1 | var server = require('./server')
2 | var router = require('./router')
3 | var requestHandlers = require('./requestHandlers')
4 |
5 | var handles = {
6 | '/': requestHandlers.start,
7 | '/start': requestHandlers.start,
8 | '/getJson': requestHandlers.getJson
9 | }
10 |
11 |
12 |
13 | server.start(router.route, handles)
--------------------------------------------------------------------------------
/s1_serverNodeBegin/requestHandlers.js:
--------------------------------------------------------------------------------
1 | var exec = require('child_process').exec
2 |
3 | function start (res) {
4 | console.log('requestHandlers start was called!')
5 | exec("ls -lah", function(error, stdout, stderr) {
6 | res.writeHead(200, {"Content-Type": "text/plain"})
7 | res.write(stdout)
8 | res.end()
9 | })
10 | }
11 | function getJson (res) {
12 | console.log('requesstHandlers getJson wad called!')
13 | res.writeHead(200, {"Content-Type": "application/json"})
14 | // res.write('hello getJson')
15 | // res.toJSON({'name': 'guojincai'})
16 | res.end(JSON.stringify({'name': 'guojincai', 'age': 99, 'hobby': 'movie'}))
17 | }
18 |
19 | exports.start = start
20 | exports.getJson = getJson
--------------------------------------------------------------------------------
/s1_serverNodeBegin/router.js:
--------------------------------------------------------------------------------
1 | function route (handles, pathname, res) {
2 | console.log('route for: ' + pathname)
3 | if (typeof handles[pathname] === 'function') {
4 | // handles.pathname // 读出来的是 '/start',不存在该方法
5 | handles[pathname](res)
6 | } else {
7 | console.log("No request handler found for " + pathname)
8 | res.writeHead(404, {"Content-Type": "text/plain"})
9 | res.write("404 Not found");
10 | res.end();
11 | }
12 | }
13 |
14 | exports.route = route
--------------------------------------------------------------------------------
/s1_serverNodeBegin/server.js:
--------------------------------------------------------------------------------
1 | var http = require("http");
2 | var url = require('url');
3 |
4 | function start(route, handles) {
5 | function onRequest(req, res) {
6 | var pathname = url.parse(req.url).pathname
7 | console.log("Request received pathname:" + pathname)
8 |
9 | route(handles, pathname, res)
10 |
11 | // res.writeHead(200, {"Content-Type": "text/plain"})
12 | // res.write("Hello node")
13 | // res.end()
14 | }
15 | http.createServer(onRequest).listen(8888);
16 | console.log("Server has started.");
17 | }
18 | exports.start = start;
--------------------------------------------------------------------------------
/s2_serverExpress/index.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var app = express();
3 |
4 | var routers = require('./routes/routers')
5 | routers(app)
6 |
7 | app.listen(8888);
--------------------------------------------------------------------------------
/s2_serverExpress/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server_express",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "express": "^4.15.3"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/s2_serverExpress/routes/routers.js:
--------------------------------------------------------------------------------
1 | module.exports = function (app) {
2 | app.get('/', function (req, res) {
3 | res.end('home page');
4 | });
5 | app.get('/home', function(req, res){
6 | res.end('home page');
7 | });
8 | app.get('/detail/:id?', function(req, res){
9 | res.end('center page: id :' + req.params.id);
10 | });
11 | app.get('/center', function(req, res){
12 | res.end('center page');
13 | });
14 | app.get('/api/send', function(req, res){
15 | res.send({name: 'guojc', age: 99, hobby: 'movie'});
16 | });
17 | app.get('/api/json', function(req, res){
18 | res.json({code: 200, data: {name: 'guojc9', age: 999, hobby: 'movie'}});
19 | // res.status(200).json({name: 'guojc9', age: 999, hobby: 'movie'});
20 | });
21 | app.get('*', function(req, res){
22 | res.end('404');
23 | });
24 | }
--------------------------------------------------------------------------------
/s3_Mongodb/test.js:
--------------------------------------------------------------------------------
1 | // 全局安装了node驱动库:cnpm install mongodb
2 | var MongoClient = require('mongodb').MongoClient;
3 |
4 | var url_test = 'mongodb://localhost:27017/test';
5 | var insertData = function(db){
6 | db.collection('site').insertOne({name: 'guojc1', age: 991, hobby: 'movie1'}, function(err, result){
7 | console.log('inserted successly');
8 | console.log(result);
9 | db.close();
10 | console.log('close');
11 | });
12 | }
13 |
14 | MongoClient.connect(url_test, function(err, db) {
15 | console.log('Connected successly to server.');
16 | insertData(db);
17 | });
--------------------------------------------------------------------------------
/s4_mongoose/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "mongoose": "^4.10.6"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/s4_mongoose/test.js:
--------------------------------------------------------------------------------
1 | var mongoose = require('mongoose');
2 | var db = mongoose.connect('mongodb://127.0.0.1:27017/test');
3 | db.connection.on('error', function(error){
4 | console.log('数据库test连接失败:' + error);
5 | });
6 | db.connection.on('open', function(){
7 | console.log('数据库test连接成功');
8 | });
9 |
10 | var testSchema = new mongoose.Schema({
11 | name: {type: String},
12 | age: {type: Number, default: 0},
13 | email: {type: String},
14 | time: {type: Date, default: Date.now}
15 | });
16 | var testModel = db.model('test1', testSchema); // 集合名称;集合的结构对象
17 | // Document文档(关联数组式的对象) < Collection集合 < 数据库
18 |
19 | // step1: 插入数据
20 | // 插入数据,一次执行完后注释掉 start
21 | // 插入保存一段数据
22 | // testModel.create([
23 | // {name: "test1", age: 8},
24 | // {name: "test2", age: 18},
25 | // {name: "test3", age: 28},
26 | // {name: "test4", age: 38},
27 | // {name: "test5", age: 48},
28 | // {name: "test6", age: 58, email:"tttt@qq.com"},
29 | // {name: "test7", age: 68, email:"ssss@qq.com"},
30 | // {name: "test8", age: 18},
31 | // {name: "test9", age: 18, email:"rrrr@qq.com"},
32 | // {name: "test10",age: 18}
33 | // ], function (error, docs) {
34 | // if(error) {
35 | // console.log(error);
36 | // } else {
37 | // console.log('save ok');
38 | // console.log(docs);
39 | // }
40 | // });
41 | // 插入数据,一次执行完后注释掉 end
42 |
43 | // step2: 查询
44 | // find(Conditions,fields,callback);
45 | // 省略或为空、返回所有记录;只包含name,age字段,去掉默认的_id字段;执行回调函数
46 | testModel.find({}, {name:1, age:1, _id:0}, function(err, docs){
47 | if (err) {
48 | console.log('查询出错:' + err);
49 | } else {
50 | console.log('{}查询结果为:');
51 | console.log(docs);
52 | }
53 | });
54 | // 查询age大于等于28,小于等于48
55 | testModel.find({age: {$gte: 28, $lte: 48}}, {name:1, age:1, _id:0}, function(err, docs){
56 | if (err) {
57 | console.log('查询出错:' + err);
58 | } else {
59 | console.log('$gte,$lte查询结果为:');
60 | console.log(docs);
61 | }
62 | });
63 | // 查询age为58、68的2条数据
64 | testModel.find({age: {$in: [58, 68]}}, {name:1, age:1, _id:0}, function(err, docs){
65 | if (err) {
66 | console.log('查询出错:' + err);
67 | } else {
68 | console.log('$in查询结果为:');
69 | console.log(docs);
70 | }
71 | });
72 | // 查询name为test3、或者age为18的全部数据
73 | testModel.find({$or: [{name: 'test3'}, {age: 18}]}, {name:1, age:1, _id:0}, function(err, docs){
74 | if (err) {
75 | console.log('查询出错:' + err);
76 | } else {
77 | console.log('$or查询结果为:');
78 | console.log(docs);
79 | }
80 | });
81 |
82 | // step3:游标查询
83 | // 查询name为test3、或者age为18的全部数据;但限制只查询2条数据
84 | testModel.find({$or: [{name: 'test3'}, {age: 18}]}, {name:1, age:1, _id:0}, {limit: 2}, function(err, docs){
85 | if (err) {
86 | console.log('查询出错:' + err);
87 | } else {
88 | console.log('limit查询结果为:');
89 | console.log(docs);
90 | }
91 | });
92 | // 查询age大于等于28,小于等于48;降序输出
93 | testModel.find({age: {$gte: 28, $lte: 48}}, {name:1, age:1, _id:0}, {sort: {age: -1}}, function(err, docs){
94 | if (err) {
95 | console.log('查询出错:' + err);
96 | } else {
97 | console.log('sort查询结果为:');
98 | console.log(docs);
99 | }
100 | });
101 |
102 | // step4: 数据更新
103 | var conditions = {name: 'test1'};
104 | var update = {$set: {age: 11 }};
105 | testModel.update(conditions, update, function(error){
106 | if(error) {
107 | console.log(error);
108 | } else {
109 | console.log('Update success!');
110 | testModel.find({name: 'test1'}, {name:1, age:1, _id:0}, function(err, docs){
111 | if (err) {
112 | console.log('查询出错:' + err);
113 | } else {
114 | console.log('更新test1后的查询结果为:');
115 | console.log(docs); // 更新test_update后的查询结果为空数组:[ ];
116 | // 更新test1后的查询结果为: [ { name: 'test1', age: 11 } ]
117 | // 只能更新本来已存在的数据
118 | }
119 | });
120 | }
121 | });
122 |
123 | // step5: 数据删除
124 | var conditions = {name: 'test2'};
125 | testModel.remove(conditions, function(error){
126 | if(error) {
127 | console.log(error);
128 | } else {
129 | console.log('Delete success!');
130 | testModel.find({name: 'test2'}, {name:1, age:1, _id:0}, function(err, docs){
131 | if (err) {
132 | console.log('查询出错:' + err);
133 | } else {
134 | console.log('删除test2后的查询结果为:');
135 | console.log(docs); // 删除test2后的查询结果为空数组:[ ];
136 | }
137 | });
138 | }
139 | });
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/s5_server/api.js:
--------------------------------------------------------------------------------
1 | const db = require('./db')
2 |
3 | module.exports = function (app) {
4 | app.all("*", function(req, res, next) {
5 | // res.writeHead(200, { "Content-Type": "text/plain" })
6 | // db.init(next)
7 | next()
8 | });
9 | app.get('/api/user/login', function (req, res) {
10 | // 对发来的登录数据进行验证
11 | if (!req.query.name) {
12 | res.json({code: 600, msg:'name 不能为空!'})
13 | return
14 | }
15 | if (!req.query.pwd) {
16 | res.json({code: 600, msg:'pwd 不能为空!'})
17 | return
18 | }
19 | db.userModel.findOne({name: req.query.name}, function(err, doc){
20 | if (err) {
21 | console.log('查询出错:' + err);
22 | } else {
23 | if (!doc) {
24 | res.json({code: 700, msg:'不存在该用户名:' + req.query.name})
25 | return
26 | } else {
27 | if (req.query.pwd != doc.pwd) {
28 | res.json({code: 700, msg:'密码不正确!'})
29 | return
30 | } else {
31 | res.json({code: 700, msg:'密码正确,登录成功'})
32 | return
33 | }
34 | }
35 |
36 | }
37 | })
38 | // 查询数据库验证账号、密码
39 | // 返回登录状态
40 | // res.send(JSON.stringify({code: 200, data: {account: 'guojc', pass: 111111}}))
41 | })
42 | app.get('/api/user/register', function (req, res) {
43 | // 对发来的注册数据进行验证
44 | // 查询数据库验证注册账号、密码
45 | console.log(req.url)
46 | db.userModel.create({
47 | name: 'guojctest1',
48 | pwd: 111111
49 | }, function (err, doc) {
50 | if (err) {
51 | res.end('err:' + err)
52 | } else {
53 | // console.log(doc)
54 | res.send(doc)
55 | }
56 | })
57 | // 返回注册状态
58 | // res.send(JSON.stringify({code: 200, data: {account: 'guojcres', pass: 111111}}))
59 | })
60 | app.get('*', function(req, res){
61 | res.end('404')
62 | })
63 | }
--------------------------------------------------------------------------------
/s5_server/db.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 | const Schema = mongoose.Schema
3 |
4 | const database = mongoose.connect('mongodb://127.0.0.1:27017/test_nodeVue')
5 | database.connection.on('error', function(error){
6 | console.log('数据库test_nodeVue连接失败:' + error)
7 | return
8 | })
9 | database.connection.once('open', function(){
10 | console.log('数据库test_nodeVue连接成功')
11 | // callback()
12 | })
13 |
14 | // const init = function (callback) {
15 | // // const mongoose = require('mongoose')
16 | // database = mongoose.connect('mongodb://127.0.0.1:27017/test_nodeVue')
17 | // database.connection.on('error', function(error){
18 | // console.log('数据库test_nodeVue连接失败:' + error)
19 | // // return
20 | // })
21 | // database.connection.on('open', function(){
22 | // console.log('数据库test_nodeVue连接成功')
23 | // callback()
24 | // })
25 | // }
26 |
27 | const userSchema = new Schema({
28 | name: {type: String},
29 | pwd: {type: String},
30 | time: {type: Date, default: Date.now}
31 | })
32 |
33 | const db = {
34 | // init: init,
35 | userModel: database.model('userModel', userSchema)
36 | }
37 |
38 | module.exports = db
39 |
--------------------------------------------------------------------------------
/s5_server/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const app = express()
3 |
4 | const api = require('./api')
5 | api(app)
6 |
7 | app.listen(8889)
--------------------------------------------------------------------------------
/s5_server/newDb.js:
--------------------------------------------------------------------------------
1 | // 全局安装了node驱动库:cnpm install mongodb
2 | const MongoClient = require('mongodb').MongoClient;
3 |
4 | const url_test = 'mongodb://localhost:27017/test_node';
5 |
6 | MongoClient.connect(url_test, function(err, db) {
7 | console.log('Connected successly to server.');
8 | });
--------------------------------------------------------------------------------
/s5_server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "s5_server",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "dependencies": {
7 | "express": "^4.15.3",
8 | "mongoose": "^4.10.6"
9 | },
10 | "devDependencies": {},
11 | "scripts": {
12 | "test": "echo \"Error: no test specified\" && exit 1"
13 | },
14 | "author": "",
15 | "license": "ISC"
16 | }
17 |
--------------------------------------------------------------------------------
/server/api.js:
--------------------------------------------------------------------------------
1 | const db = require('./db')
2 |
3 | module.exports = function (app) {
4 | app.all("*", function(req, res, next) {
5 | // res.writeHead(200, { "Content-Type": "text/plain", "Access-Control-Allow-Origin":"*" })
6 | // res.header('Access-Control-Allow-Origin', '*')
7 | // res.header('Access-Control-Allow-Headers', 'X-Requested-With')
8 | // res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS')
9 | // res.header("X-Powered-By",' 3.2.1')
10 | // res.header("Content-Type", "application/json;charset=utf-8")
11 | // next()
12 | // res.header('Access-Control-Allow-Origin', '*');
13 | // res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
14 | // res.header("Access-Control-Allow-Methods","*");
15 |
16 | next();
17 | // if (req.method == 'OPTIONS') {
18 | // res.send(200);
19 | // }
20 | // else {
21 | // next();
22 | // }
23 | });
24 | // api login
25 | app.get('/api/user/login', function (req, res) {
26 | // 对发来的登录数据进行验证
27 | if (!req.query.name) {
28 | // res.writeHead('Access-Control-Allow-Origin', '*');
29 | // res.writeHead('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild, x-access-token');
30 | // res.writeHead('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
31 | // res.writeHead('Access-Control-Allow-Credentials', 'true');
32 | res.json({code: 600, msg:'name 不能为空!'})
33 | return
34 | }
35 | if (!req.query.pwd) {
36 | res.json({code: 600, msg:'pwd 不能为空!'})
37 | return
38 | }
39 | db.userModel.findOne({name: req.query.name}, function(err, doc){
40 | if (err) {
41 | console.log('查询出错:' + err);
42 | res.json({code: 700, msg:'查询出错:' + err})
43 | return
44 | } else {
45 | if (!doc) {
46 | res.json({code: 700, msg:'不存在该用户名:' + req.query.name})
47 | return
48 | } else {
49 | if (req.query.pwd != doc.pwd) {
50 | res.json({code: 700, msg:'密码不正确!'})
51 | return
52 | } else {
53 | res.json({code: 200, msg:'密码正确,登录成功'})
54 | return
55 | }
56 | }
57 |
58 | }
59 | })
60 | })
61 | // api register
62 | app.get('/api/user/register', function (req, res) {
63 | // 对发来的注册数据进行验证
64 | let name = req.query.name
65 | let pwd = req.query.pwd
66 | if (!name) {
67 | res.json({code: 600, msg:'name 不能为空!'})
68 | return
69 | }
70 | if (!pwd) {
71 | res.json({code: 600, msg:'pwd 不能为空!'})
72 | return
73 | }
74 | // 查询数据库验证注册账号、密码
75 | // 是否存在账号
76 | db.userModel.findOne({name: req.query.name}, function(err, doc){
77 | if (err) {
78 | console.log('查询出错:' + err);
79 | res.json({code: 700, msg:'查询出错:' + err})
80 | return
81 | } else {
82 | if (doc) {
83 | res.json({code: 700, msg:'该用户名名已经被注册:' + name})
84 | return
85 | } else {
86 | db.userModel.create({
87 | name: name,
88 | pwd: pwd
89 | }, function (err, doc) {
90 | if (err) {
91 | res.end('注册失败:' + err)
92 | } else {
93 | res.json({code: 200, msg:'用户注册成功:' + name})
94 | return
95 | }
96 | })
97 | }
98 |
99 | }
100 | })
101 | // 返回注册状态
102 | // res.send(JSON.stringify({code: 200, data: {account: 'guojcres', pass: 111111}}))
103 | })
104 | // api index
105 | app.get('/api/goods/index', function (req, res) {
106 | let temai = [],
107 | rexiao = [],
108 | jingpin = [];
109 | // // 1.temai
110 | // db.goodsModel.find(
111 | // {brand_status: "temai"},
112 | // {brand_id:1, brand_name:1, brand_price:1, brand_pic:1, _id:0},
113 | // {limit: 3},
114 | // function(err, doc){
115 | // if (err) {
116 | // console.log('temai find error!');
117 | // } else {
118 | // if (!doc) {
119 | // temai = [];
120 | // } else {
121 | // temai = doc;
122 | // }
123 | // }
124 | // })
125 | // // 2.rexiao
126 | // db.goodsModel.find(
127 | // {brand_status: "rexiao"},
128 | // {brand_id:1, brand_name:1, brand_desc:1, brand_pic:1, _id:0},
129 | // {limit: 3},
130 | // function(err, doc){
131 | // if (err) {
132 | // console.log('rexiao find error!');
133 | // } else {
134 | // if (!doc) {
135 | // rexiao = [];
136 | // } else {
137 | // rexiao = doc;
138 | // }
139 | // }
140 | // })
141 | // // 3.jingpin
142 | // db.goodsModel.find(
143 | // {brand_status: "jingpin"},
144 | // {brand_id:1, brand_name:1, brand_price:1, brand_pic:1, _id:0},
145 | // {limit: 4},
146 | // function(err, doc){
147 | // if (err) {
148 | // console.log('jingpin find error!');
149 | // } else {
150 | // if (!doc) {
151 | // jingpin = [];
152 | // } else {
153 | // jingpin = doc;
154 | // // res
155 | // res.json({code: 200, msg:'', data: {"temai": temai, "rexiao": rexiao, "jingpin": jingpin}})
156 | // return
157 | // }
158 | // }
159 | // })
160 |
161 | // 异步操作,未解决
162 | // 1.temai
163 | // db.goodsModel.find(
164 | // {brand_status: "temai"},
165 | // {brand_id:1, brand_name:1, brand_price:1, brand_pic:1, _id:0},
166 | // {limit: 3},
167 | // function(err, doc){
168 | // if (err) {
169 | // console.log('temai find error!');
170 | // } else {
171 | // if (!doc) {
172 | // temai = [];
173 | // } else {
174 | // temai = doc;
175 | // }
176 | // }
177 | // })
178 | // .then( () => {
179 | // // 2.rexiao
180 | // db.goodsModel.find(
181 | // {brand_status: "rexiao"},
182 | // {brand_id:1, brand_name:1, brand_desc:1, brand_pic:1, _id:0},
183 | // {limit: 3},
184 | // function(err, doc){
185 | // if (err) {
186 | // console.log('rexiao find error!');
187 | // } else {
188 | // if (!doc) {
189 | // rexiao = [];
190 | // } else {
191 | // rexiao = doc;
192 | // }
193 | // }
194 | // })
195 | // .then( () => {
196 | // // 3.jingpin
197 | // db.goodsModel.find(
198 | // {brand_status: "jingpin"},
199 | // {brand_id:1, brand_name:1, brand_price:1, brand_pic:1, _id:0},
200 | // {limit: 4},
201 | // function(err, doc){
202 | // if (err) {
203 | // console.log('jingpin find error!');
204 | // } else {
205 | // if (!doc) {
206 | // jingpin = [];
207 | // } else {
208 | // jingpin = doc;
209 | // }
210 | // }
211 | // })
212 | // .then( () => {
213 | // // res
214 | // res.json({code: 200, msg:'', data: {"temai": temai, "rexiao": rexiao, "jingpin": jingpin}})
215 | // return
216 | // })
217 | // })
218 | // })
219 | // .catch( (err) => {
220 | // res.json({code: 200, msg:'', data: {"temai": temai, "rexiao": rexiao, "jingpin": jingpin}})
221 | // return
222 | // })
223 |
224 | // promise 解决
225 | // temai
226 | const getTemai = new Promise((resolve,reject) => {
227 | db.goodsModel.find(
228 | {brand_status: "temai"},
229 | {brand_id:1, brand_name:1, brand_price:1, brand_pic:1, brand_status:1, _id:0},
230 | {limit: 3},
231 | function(err, doc){
232 | if (err) {
233 | console.log('temai find error!')
234 | reject('reject temai')
235 | } else {
236 | if (!doc) {
237 | temai = [];
238 | } else {
239 | temai = doc;
240 | }
241 | resolve(temai)
242 | }
243 | })
244 | })
245 | // rexiao
246 | const getRexiao = new Promise((resolve,reject) => {
247 | db.goodsModel.find(
248 | {brand_status: "rexiao"},
249 | {brand_id:1, brand_name:1, brand_desc:1, brand_pic:1, brand_status:1, _id:0},
250 | {limit: 3},
251 | function(err, doc){
252 | if (err) {
253 | console.log('rexiao find error!');
254 | reject('reject rexiao')
255 | } else {
256 | if (!doc) {
257 | rexiao = [];
258 | } else {
259 | rexiao = doc;
260 | }
261 | resolve(rexiao)
262 | }
263 | })
264 | })
265 | // jingpin
266 | const getJingpin = new Promise((resolve,reject) => {
267 | db.goodsModel.find(
268 | {brand_status: "jingpin"},
269 | {brand_id:1, brand_name:1, brand_price:1, brand_pic:1, brand_status:1, _id:0},
270 | {limit: 4},
271 | function(err, doc){
272 | if (err) {
273 | console.log('jingpin find error!')
274 | reject('reject jingpin')
275 | } else {
276 | if (!doc) {
277 | jingpin = []
278 | } else {
279 | jingpin = doc
280 | }
281 | resolve(jingpin)
282 | }
283 | })
284 | })
285 |
286 | const p_all = Promise.all([getTemai, getRexiao, getJingpin])
287 |
288 | p_all.then( (suc) => {
289 | let data = {
290 | "temai": suc[0],
291 | "rexiao": suc[1],
292 | "jingpin": suc[2]
293 | }
294 | res.json({code: 200, msg:'', data: data})
295 | return
296 | }).catch( (err) => {
297 | console.log('err all:' + err)
298 | res.json({code: 600, msg:'查询出错', data: data})
299 | return
300 | })
301 | })
302 | // 精品下拉加载更多api index/jingpin
303 | app.get('/api/goods/index/jingpin', function (req, res) {
304 | let nowLength = parseInt(req.query.nowLength)
305 | db.goodsModel.find(
306 | {brand_status: "jingpin"},
307 | {brand_id: 1, brand_name: 1, brand_price: 1, brand_pic: 1, _id: 0},
308 | {limit: 4, skip:nowLength},
309 | function (err, doc) {
310 | if (err) {
311 | console.log('jingpin find error!');
312 | console.log(err)
313 | } else {
314 | if (!doc) {
315 | // res
316 | res.json({code: 600, msg: '没有了', data: ''})
317 | return
318 | } else {
319 | // res 加载效果,故意延时1s
320 | setTimeout( ()=> {
321 | res.json({code: 200, msg: '', data: doc})
322 | return
323 | }, 1000)
324 | }
325 | }
326 | }
327 | )
328 | })
329 | // api cate
330 | app.get('/api/goods/cate', function (req, res) {
331 | db.goodsModel.find({}, function(err, doc){
332 | if (err) {
333 | console.log('查询出错:' + err);
334 | res.json({code: 700, msg:'查询出错:' + err})
335 | return
336 | } else {
337 | if (!doc) {
338 | res.json({code: 600, msg:'没有商品', data: doc})
339 | return
340 | } else {
341 | res.json({code: 200, msg:'', data: doc})
342 | return
343 | }
344 |
345 | }
346 | })
347 | })
348 | // api detail
349 | app.get('/api/goods/detail', function (req, res) {
350 | let brand_id = req.query.brand_id
351 | db.goodsModel.findOne({brand_id: brand_id}, {__v: 0, _id: 0}, function(err, doc){
352 | if (err) {
353 | console.log('查询出错:' + err);
354 | res.json({code: 700, msg:'查询出错:' + err})
355 | return
356 | } else {
357 | if (!doc) {
358 | res.json({code: 600, msg:'没有商品', data: doc})
359 | return
360 | } else {
361 | res.json({code: 200, msg:'', data: doc})
362 | return
363 | }
364 | }
365 | })
366 | })
367 | // api addToCart
368 | app.get('/api/goods/addToCart', function (req, res) {
369 | let brand_id = req.query.brand_id
370 | let name = req.query.name
371 | let newCart = req.query
372 | db.cartsModel.update({brand_id: brand_id, name: name}, {$set:newCart}, {upsert:true}, function(err){
373 | if (err) {
374 | console.log('加入购物车失败:' + err);
375 | res.json({code: 700, msg:'加入购物车失败:' + err})
376 | return
377 | } else {
378 | // add
379 | res.json({code: 200, msg:'加入购物车成功'})
380 | return
381 | }
382 | })
383 | })
384 | // api carts
385 | app.get('/api/goods/carts', function (req, res) {
386 | let name = req.query.name
387 | db.cartsModel.find({name: name}, {__v: 0, _id: 0}, function(err, doc){
388 | if (err) {
389 | console.log('购物车查询出错:' + err);
390 | res.json({code: 700, msg:'购物车查询出错:' + err})
391 | return
392 | } else {
393 | if (!doc) {
394 | res.json({code: 600, msg:'购物车为空', data: doc})
395 | return
396 | } else {
397 | res.json({code: 200, msg:'购物车返回成功', data: doc})
398 | return
399 | }
400 | }
401 | })
402 | })
403 | // api delectCart
404 | app.get('/api/goods/delectCart', function (req, res) {
405 | let brand_id = req.query.brand_id
406 | let name = req.query.name
407 | db.cartsModel.remove({brand_id: brand_id, name: name}, function(err){
408 | if (err) {
409 | console.log('购物车删除:' + err);
410 | res.json({code: 700, msg:'购物车删除:' + err})
411 | return
412 | } else {
413 | // add
414 | res.json({code: 200, msg:'购物车删除成功'})
415 | return
416 | }
417 | })
418 | })
419 |
420 | app.get('*', function(req, res){
421 | res.end('404')
422 | })
423 | }
--------------------------------------------------------------------------------
/server/db.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose')
2 | const Schema = mongoose.Schema
3 | const initGoods = require('./initGoods.json')
4 | const initCarts = require('./initCarts.json')
5 |
6 | // 用户信息的数据结构模型
7 | const userSchema = new Schema({
8 | name: {type: String},
9 | pwd: {type: String},
10 | time: {type: Date, default: Date.now}
11 | })
12 | // 商品的的数据结构模型
13 | const goodsSchema = new Schema({
14 | brand_id: Number,
15 | brand_cate: String,
16 | brand_cateName: String,
17 | brand_status: String,
18 | brand_name: String,
19 | brand_price: Number,
20 | brand_desc: String,
21 | brand_pic: String
22 | })
23 | // 购物车的的数据结构模型
24 | const cartsSchema = new Schema({
25 | name: String,
26 | brand_id: Number,
27 | brand_cate: String,
28 | brand_name: String,
29 | brand_price: Number,
30 | brand_desc: String,
31 | brand_pic: String,
32 | cart_num: Number,
33 | cart_isSelect: Boolean
34 | });
35 |
36 | mongoose.Promise = global.Promise;
37 | const database = mongoose.connect('mongodb://127.0.0.1:27017/test_nodeVue')
38 | database.connection.on('error', function(error){
39 | console.log('数据库test_nodeVue连接失败:' + error)
40 | return
41 | })
42 | database.connection.once('open', function(){
43 | console.log('数据库test_nodeVue连接成功')
44 | // 初始化数据库
45 | initData();
46 | })
47 |
48 | const db = {
49 | userModel: database.model('userModel', userSchema),
50 | goodsModel: database.model('goodsModel', goodsSchema),
51 | cartsModel: database.model('cartsModel', cartsSchema)
52 | }
53 |
54 | const initData = function () {
55 | // 初始化商品goods
56 | db.goodsModel.find({}, function(err, doc){
57 | if (err) {
58 | console.log('initData出错:' + err);
59 | } else if (!doc.length) {
60 | console.log('db goodsModel open first time');
61 | // 初始化数据,遍历插入;先打印出来看看
62 | initGoods.map(brand => {
63 | db.goodsModel.create(brand)
64 | })
65 | // console.log(initGoods)
66 |
67 | } else {
68 | console.log('db open not first time');
69 | }
70 | })
71 | // 为用户name15011760730初始化购物车内容
72 | db.cartsModel.find({}, function(err, doc){
73 | if (err) {
74 | console.log('initData出错:' + err);
75 | } else if (!doc.length) {
76 | console.log('db cartsModel open first time');
77 | // 初始化数据,遍历插入;先打印出来看看
78 | initCarts.map(brand => {
79 | db.cartsModel.create(brand)
80 | })
81 | // console.log(initGoods)
82 |
83 | } else {
84 | console.log('db open not first time');
85 | }
86 | })
87 | }
88 |
89 | module.exports = db
90 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const app = express()
3 |
4 | const api = require('./api')
5 | api(app)
6 |
7 | app.listen(8889)
--------------------------------------------------------------------------------
/server/initCarts.json:
--------------------------------------------------------------------------------
1 | [
{
"name": "15011760730",
"brand_id": 10002,
"brand_cate": "男装",
"brand_name": "商品名字2",
"brand_price": 120,
"brand_desc": "商品描述商品描述商品描述商品描述",
"brand_pic": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg",
"cart_num": 1,
"cart_isSelect": false
},
{
"name": "15011760730",
"brand_id": 10003,
"brand_cate": "男装",
"brand_name": "商品名字3",
"brand_price": 120,
"brand_desc": "商品描述商品描述商品描述商品描述",
"brand_pic": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg",
"cart_num": 1,
"cart_isSelect": false
}
]
--------------------------------------------------------------------------------
/server/initGoods.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "brand_id": 10001,
4 | "brand_cate": "type_man",
5 | "brand_cateName": "男装",
6 | "brand_status": "temai",
7 | "brand_name": "商品名字1",
8 | "brand_price": 110,
9 | "brand_desc": "商品描述商品描述商品描述商品描述",
10 | "brand_pic": "http://ohe5avf3y.bkt.clouddn.com/pro/vue/vue-shop/vue-proj-goods.jpg"
11 | },
12 | {
13 | "brand_id": 10002,
14 | "brand_cate": "type_man",
15 | "brand_cateName": "男装",
16 | "brand_status": "temai",
17 | "brand_name": "商品名字1",
18 | "brand_price": 120,
19 | "brand_desc": "商品描述商品描述商品描述商品描述",
20 | "brand_pic": "http://s11.mogucdn.com/mlcdn/c45406/170602_52gblji4hd3ad0i9eeghf9kebg482_640x960.jpg_240x308.v1cAC.70.webp"
21 | },
22 | {
23 | "brand_id": 10003,
24 | "brand_cate": "type_man",
25 | "brand_cateName": "男装",
26 | "brand_status": "temai",
27 | "brand_name": "商品名字1",
28 | "brand_price": 130,
29 | "brand_desc": "商品描述商品描述商品描述商品描述",
30 | "brand_pic": "http://s2.mogucdn.com/mlcdn/c45406/170603_2lbjee355ek1675be7jb0j5lcl75a_640x960.png_240x308.v1cAC.70.webp"
31 | },
32 | {
33 | "brand_id": 10004,
34 | "brand_cate": "type_man",
35 | "brand_cateName": "男装",
36 | "brand_status": "rexiao",
37 | "brand_name": "商品名字1",
38 | "brand_price": 140,
39 | "brand_desc": "商品描述商品描述商品描述商品描述",
40 | "brand_pic": "http://s3.mogucdn.com/mlcdn/c45406/170604_37d4jkh07056ce39ig0f978kki6lc_640x960.jpg_240x308.v1cAC.70.webp"
41 | },
42 | {
43 | "brand_id": 10005,
44 | "brand_cate": "type_man",
45 | "brand_cateName": "男装",
46 | "brand_status": "rexiao",
47 | "brand_name": "商品名字1",
48 | "brand_price": 150,
49 | "brand_desc": "商品描述商品描述商品描述商品描述",
50 | "brand_pic": "http://s3.mogucdn.com/mlcdn/17f85e/170506_5j6jff5k457k0h43h33gid14a7j93_640x960.jpg_240x308.v1cAC.70.webp"
51 | },
52 | {
53 | "brand_id": 10006,
54 | "brand_cate": "type_man",
55 | "brand_cateName": "男装",
56 | "brand_status": "rexiao",
57 | "brand_name": "商品名字1",
58 | "brand_price": 160,
59 | "brand_desc": "商品描述商品描述商品描述商品描述",
60 | "brand_pic": "http://s2.mogucdn.com/mlcdn/c45406/170507_14l6dgfe1a33ffj4keiahlc1gaeed_640x960.jpg_240x308.v1cAC.70.webp"
61 | },
62 | {
63 | "brand_id": 10007,
64 | "brand_cate": "type_man",
65 | "brand_cateName": "男装",
66 | "brand_status": "jingpin",
67 | "brand_name": "商品名字1",
68 | "brand_price": 170,
69 | "brand_desc": "商品描述商品描述商品描述商品描述",
70 | "brand_pic": "http://s2.mogucdn.com/mlcdn/c45406/170623_380bf1ela7jb2f65ggadh2d1ll9bg_640x960.jpg_240x308.v1cAC.70.webp"
71 | },
72 | {
73 | "brand_id": 10008,
74 | "brand_cate": "type_man",
75 | "brand_cateName": "男装",
76 | "brand_status": "jingpin",
77 | "brand_name": "商品名字1",
78 | "brand_price": 180,
79 | "brand_desc": "商品描述商品描述商品描述商品描述",
80 | "brand_pic": "http://s3.mogucdn.com/mlcdn/c45406/170427_8ef6fhgg8kl08iil5809dffc1de8c_640x960.jpg_240x308.v1cAC.70.webp"
81 | },
82 | {
83 | "brand_id": 10009,
84 | "brand_cate": "type_man",
85 | "brand_cateName": "男装",
86 | "brand_status": "jingpin",
87 | "brand_name": "商品名字1",
88 | "brand_price": 190,
89 | "brand_desc": "商品描述商品描述商品描述商品描述",
90 | "brand_pic": "http://s2.mogucdn.com/mlcdn/c45406/170622_4ehkgj2ag7el2ckb59d3h586bb0ja_640x960.jpg_240x308.v1cAC.70.webp"
91 | },
92 | {
93 | "brand_id": 10010,
94 | "brand_cate": "type_man",
95 | "brand_cateName": "男装",
96 | "brand_status": "jingpin",
97 | "brand_name": "商品名字1",
98 | "brand_price": 200,
99 | "brand_desc": "商品描述商品描述商品描述商品描述",
100 | "brand_pic": "http://s17.mogucdn.com/mlcdn/c45406/170421_0eg9hh8gh6jj37f07h3j2k5jah9e2_640x960.jpg_240x308.v1cAC.70.webp"
101 | },
102 | {
103 | "brand_id": 10011,
104 | "brand_cate": "type_man",
105 | "brand_cateName": "男装",
106 | "brand_status": "jingpin",
107 | "brand_name": "商品名字1",
108 | "brand_price": 220,
109 | "brand_desc": "商品描述商品描述商品描述商品描述",
110 | "brand_pic": "http://s2.mogucdn.com/mlcdn/c45406/170526_3b6a34eai87154g6i11a71ab5h3kb_640x960.jpg_240x308.v1cAC.70.webp"
111 | },
112 | {
113 | "brand_id": 10012,
114 | "brand_cate": "type_man",
115 | "brand_cateName": "男装",
116 | "brand_status": "jingpin",
117 | "brand_name": "商品名字1",
118 | "brand_price": 230,
119 | "brand_desc": "商品描述商品描述商品描述商品描述",
120 | "brand_pic": "http://s3.mogucdn.com/mlcdn/17f85e/170510_6efkgcc5ec9a9g02d87fl028014k9_640x960.jpg_240x308.v1cAC.70.webp"
121 | },
122 | {
123 | "brand_id": 10013,
124 | "brand_cate": "type_man",
125 | "brand_cateName": "男装",
126 | "brand_status": "jingpin",
127 | "brand_name": "商品名字1",
128 | "brand_price": 240,
129 | "brand_desc": "商品描述商品描述商品描述商品描述",
130 | "brand_pic": "http://s3.mogucdn.com/mlcdn/c45406/170703_4d7d9777201jfi7k5cbfdlg2l2581_640x960.jpg_240x308.v1cAC.70.webp"
131 | },
132 | {
133 | "brand_id": 10014,
134 | "brand_cate": "type_man",
135 | "brand_cateName": "男装",
136 | "brand_status": "jingpin",
137 | "brand_name": "商品名字1",
138 | "brand_price": 250,
139 | "brand_desc": "商品描述商品描述商品描述商品描述",
140 | "brand_pic": "http://s2.mogucdn.com/mlcdn/c45406/170620_5llbg7ba6gjihcg0jedc16cb59j1i_640x960.jpg_240x308.v1cAC.70.webp"
141 | },
142 | {
143 | "brand_id": 10015,
144 | "brand_cate": "type_man",
145 | "brand_cateName": "男装",
146 | "brand_status": "jingpin",
147 | "brand_name": "商品名字1",
148 | "brand_price": 260,
149 | "brand_desc": "商品描述商品描述商品描述商品描述",
150 | "brand_pic": "http://s3.mogucdn.com/mlcdn/c45406/170519_52k6ifbb318a1ii3j1he09ck8f323_800x1200.jpg_240x308.v1cAC.70.webp"
151 | },
152 | {
153 | "brand_id": 10016,
154 | "brand_cate": "type_man",
155 | "brand_cateName": "男装",
156 | "brand_status": "jingpin",
157 | "brand_name": "商品名字1",
158 | "brand_price": 270,
159 | "brand_desc": "商品描述商品描述商品描述商品描述",
160 | "brand_pic": "http://s3.mogucdn.com/mlcdn/c45406/170421_0fb1c9d3j3hf0932i4l61ljb28deh_640x960.jpg_240x308.v1cAC.70.webp"
161 | },
162 | {
163 | "brand_id": 10017,
164 | "brand_cate": "type_man",
165 | "brand_cateName": "男装",
166 | "brand_status": "jingpin",
167 | "brand_name": "商品名字1",
168 | "brand_price": 280,
169 | "brand_desc": "商品描述商品描述商品描述商品描述",
170 | "brand_pic": "http://s11.mogucdn.com/mlcdn/c45406/170627_867h059le4g5ci0b05k5887dlka0d_640x960.jpg_240x308.v1cAC.70.webp"
171 | },
172 | {
173 | "brand_id": 10018,
174 | "brand_cate": "type_man",
175 | "brand_cateName": "男装",
176 | "brand_status": "jingpin",
177 | "brand_name": "商品名字1",
178 | "brand_price": 290,
179 | "brand_desc": "商品描述商品描述商品描述商品描述",
180 | "brand_pic": "http://s3.mogucdn.com/mlcdn/c45406/170623_2c0fe84793fbda75cd0je7gi74fdd_640x960.jpg_240x308.v1cAC.70.webp"
181 | },
182 | {
183 | "brand_id": 10019,
184 | "brand_cate": "type_man",
185 | "brand_cateName": "男装",
186 | "brand_status": "jingpin",
187 | "brand_name": "商品名字1",
188 | "brand_price": 300,
189 | "brand_desc": "商品描述商品描述商品描述商品描述",
190 | "brand_pic": "http://s3.mogucdn.com/mlcdn/c45406/170426_14b064a69k4dgd61kd1fi6k1ig74g_640x960.png_240x308.v1cAC.70.webp"
191 | },
192 | {
193 | "brand_id": 10020,
194 | "brand_cate": "type_man",
195 | "brand_cateName": "男装",
196 | "brand_status": "jingpin",
197 | "brand_name": "商品名字1",
198 | "brand_price": 201,
199 | "brand_desc": "商品描述商品描述商品描述商品描述",
200 | "brand_pic": "http://s2.mogucdn.com/mlcdn/c45406/170601_1c5fkcjibfc38ld6b935066ahd0ch_640x960.jpg_240x308.v1cAC.70.webp"
201 | }
202 | ]
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "s5_server",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "dependencies": {
7 | "express": "^4.15.3",
8 | "mongoose": "^4.10.6"
9 | },
10 | "devDependencies": {},
11 | "scripts": {
12 | "test": "echo \"Error: no test specified\" && exit 1"
13 | },
14 | "author": "",
15 | "license": "ISC"
16 | }
17 |
--------------------------------------------------------------------------------