├── static ├── .gitkeep ├── 1.jpg ├── icon.png ├── logo.png ├── ok-2.png ├── 九号平衡车.jpg ├── 小米体重秤.jpg ├── 小米米家对讲机.jpg ├── 小米路由器 3.jpg ├── 小米魔方控制器.jpg ├── 平衡车plus.jpg ├── 米兔儿童手表Q.jpg ├── 米兔定位电话.jpg ├── 米兔智能故事机.jpg ├── 米家IH电饭煲.jpg ├── 米家声波电动牙刷.jpg ├── 米家恒温电水壶.jpg ├── 米家扫地机器人.jpg ├── 米家智能摄像机.jpg ├── 小米空气净化器 2.jpg ├── 米兔儿童电话手表2.jpg ├── 米家PM2.5检测仪.jpg ├── 米家小白智能摄像机.jpg ├── 米家空气净化器Pro.jpg ├── 米家飞利浦台灯二代.jpg ├── Yeelight床头灯.jpg ├── iHealth智能血压计.jpg ├── 九号平衡车!220x220.jpg ├── 小方摄像机!220x220.jpg ├── 小方智能摄像机 1080P.jpg ├── 米家压力 IH 电饭煲.jpg ├── 小米AI音箱!220x220.jpg ├── 小米路由器3C!220x220.jpg ├── 米家 iHealth 血压计.jpg ├── 米家感应夜灯!220x220.jpg ├── 米家行车记录仪!220x220.jpg ├── 米家iHealth血压计!220x220.jpg └── loading-svg │ ├── loading-bars.svg │ └── loading-spinning-bubbles.svg ├── config ├── prod.env.js ├── dev.env.js └── index.js ├── resource ├── img │ ├── 1.jpg │ ├── icon.png │ ├── logo.png │ └── ok-2.png ├── css │ ├── nav-bread.css │ ├── nav-footer.css │ ├── nav-header.css │ ├── login.css │ └── goods-list.css └── views │ ├── 下单成功页.html │ ├── 商品列表页.html │ ├── 订单确认.html │ ├── 收货地址选择页.html │ └── 购物车页面.html ├── src ├── assets │ ├── logo.png │ └── css │ │ ├── nav-bread.css │ │ ├── nav-footer.css │ │ ├── nav-header.css │ │ ├── login.css │ │ └── goods-list.css ├── App.vue ├── util │ └── currency.js ├── components │ ├── Modal.vue │ ├── NavBread.vue │ ├── NavFooter.vue │ ├── HelloWorld.vue │ └── NavHeader.vue ├── router │ └── index.js ├── main.js └── views │ ├── OrderSuccess.vue │ ├── GoodsList.vue │ ├── OrderConfirm.vue │ ├── Address.Vue │ └── Cart.vue ├── shop-server ├── views │ ├── error.ejs │ └── index.ejs ├── public │ └── stylesheets │ │ └── style.css ├── routes │ ├── index.js │ ├── goods.js │ └── users.js ├── models │ ├── goods.js │ └── users.js ├── package.json ├── util │ └── date_format.js ├── bin │ └── www └── app.js ├── .babelrc ├── .postcssrc.js ├── server ├── index.js ├── package.json └── package-lock.json ├── index.html ├── mock └── goods.json ├── package.json └── README.md /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/1.jpg -------------------------------------------------------------------------------- /static/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/icon.png -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/logo.png -------------------------------------------------------------------------------- /static/ok-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/ok-2.png -------------------------------------------------------------------------------- /static/九号平衡车.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/九号平衡车.jpg -------------------------------------------------------------------------------- /static/小米体重秤.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/小米体重秤.jpg -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /resource/img/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/resource/img/1.jpg -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /static/小米米家对讲机.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/小米米家对讲机.jpg -------------------------------------------------------------------------------- /static/小米路由器 3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/小米路由器 3.jpg -------------------------------------------------------------------------------- /static/小米魔方控制器.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/小米魔方控制器.jpg -------------------------------------------------------------------------------- /static/平衡车plus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/平衡车plus.jpg -------------------------------------------------------------------------------- /static/米兔儿童手表Q.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米兔儿童手表Q.jpg -------------------------------------------------------------------------------- /static/米兔定位电话.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米兔定位电话.jpg -------------------------------------------------------------------------------- /static/米兔智能故事机.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米兔智能故事机.jpg -------------------------------------------------------------------------------- /static/米家IH电饭煲.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米家IH电饭煲.jpg -------------------------------------------------------------------------------- /static/米家声波电动牙刷.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米家声波电动牙刷.jpg -------------------------------------------------------------------------------- /static/米家恒温电水壶.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米家恒温电水壶.jpg -------------------------------------------------------------------------------- /static/米家扫地机器人.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米家扫地机器人.jpg -------------------------------------------------------------------------------- /static/米家智能摄像机.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米家智能摄像机.jpg -------------------------------------------------------------------------------- /resource/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/resource/img/icon.png -------------------------------------------------------------------------------- /resource/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/resource/img/logo.png -------------------------------------------------------------------------------- /resource/img/ok-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/resource/img/ok-2.png -------------------------------------------------------------------------------- /static/小米空气净化器 2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/小米空气净化器 2.jpg -------------------------------------------------------------------------------- /static/米兔儿童电话手表2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米兔儿童电话手表2.jpg -------------------------------------------------------------------------------- /static/米家PM2.5检测仪.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米家PM2.5检测仪.jpg -------------------------------------------------------------------------------- /static/米家小白智能摄像机.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米家小白智能摄像机.jpg -------------------------------------------------------------------------------- /static/米家空气净化器Pro.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米家空气净化器Pro.jpg -------------------------------------------------------------------------------- /static/米家飞利浦台灯二代.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米家飞利浦台灯二代.jpg -------------------------------------------------------------------------------- /static/Yeelight床头灯.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/Yeelight床头灯.jpg -------------------------------------------------------------------------------- /static/iHealth智能血压计.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/iHealth智能血压计.jpg -------------------------------------------------------------------------------- /static/九号平衡车!220x220.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/九号平衡车!220x220.jpg -------------------------------------------------------------------------------- /static/小方摄像机!220x220.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/小方摄像机!220x220.jpg -------------------------------------------------------------------------------- /static/小方智能摄像机 1080P.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/小方智能摄像机 1080P.jpg -------------------------------------------------------------------------------- /static/米家压力 IH 电饭煲.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米家压力 IH 电饭煲.jpg -------------------------------------------------------------------------------- /static/小米AI音箱!220x220.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/小米AI音箱!220x220.jpg -------------------------------------------------------------------------------- /static/小米路由器3C!220x220.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/小米路由器3C!220x220.jpg -------------------------------------------------------------------------------- /static/米家 iHealth 血压计.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米家 iHealth 血压计.jpg -------------------------------------------------------------------------------- /static/米家感应夜灯!220x220.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米家感应夜灯!220x220.jpg -------------------------------------------------------------------------------- /static/米家行车记录仪!220x220.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米家行车记录仪!220x220.jpg -------------------------------------------------------------------------------- /shop-server/views/error.ejs: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /static/米家iHealth血压计!220x220.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/worldsong/vue2-shop-2019/HEAD/static/米家iHealth血压计!220x220.jpg -------------------------------------------------------------------------------- /shop-server/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /shop-server/routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | /* GET home page. */ 5 | router.get('/', function(req, res, next) { 6 | res.render('index', { title: 'Express' }); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-vue-jsx", "transform-runtime"] 12 | } 13 | -------------------------------------------------------------------------------- /shop-server/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 | 8 |

<%= title %>

9 |

Welcome to <%= title %>

10 | 11 | 12 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | "postcss-import": {}, 6 | "postcss-url": {}, 7 | // to edit target browsers: use "browserslist" field in package.json 8 | "autoprefixer": {} 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | 4 | const router = express.Router(); 5 | const goodsData = require('./../mock/goods.json'); 6 | 7 | router.get("/goods", function (req, res, next) { 8 | res.json(goodsData); 9 | }) 10 | 11 | app.use(router); 12 | 13 | app.listen(4000); 14 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | vue2-shop 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue2-shop-server", 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": "Song", 10 | "license": "ISC", 11 | "dependencies": { 12 | "express": "^4.17.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /shop-server/models/goods.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | var Schema = mongoose.Schema; 3 | 4 | var productSchema = new Schema({ 5 | "productId":{type:String}, 6 | "productName":String, 7 | "salePrice":Number, 8 | "checked":String, 9 | "productNum":Number, 10 | "productImage":String 11 | }); 12 | 13 | module.exports = mongoose.model('Good', productSchema); 14 | -------------------------------------------------------------------------------- /shop-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shop-server", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "cookie-parser": "~1.4.4", 10 | "debug": "~2.6.9", 11 | "ejs": "~2.6.1", 12 | "express": "~4.16.1", 13 | "http-errors": "~1.6.3", 14 | "mongoose": "^5.6.0", 15 | "morgan": "~1.9.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/assets/css/nav-bread.css: -------------------------------------------------------------------------------- 1 | .nav-breadcrumb-wrap { 2 | background: #f0f0f0; 3 | } 4 | 5 | 6 | /** 面包屑 **/ 7 | .nav-breadcrumb { 8 | padding: 10px 0; 9 | line-height: 25px; 10 | font-size: 14px; 11 | } 12 | 13 | .nav-breadcrumb a { 14 | position: relative; 15 | margin-right: 16px; 16 | color: #999; 17 | } 18 | 19 | .nav-breadcrumb a:after { 20 | position: absolute; 21 | top: 3px; 22 | right: -12px; 23 | content: "/"; 24 | line-height: 1.2; 25 | } 26 | 27 | .nav-breadcrumb a:hover { 28 | color: #ee7a23; 29 | } 30 | 31 | .nav-breadcrumb span { 32 | color: #ee7a23; 33 | } 34 | -------------------------------------------------------------------------------- /shop-server/util/date_format.js: -------------------------------------------------------------------------------- 1 | Date.prototype.Format = function (fmt) { 2 | var o = { 3 | "M+": this.getMonth() + 1, //月份 4 | "d+": this.getDate(), //日 5 | "h+": this.getHours(), //小时 6 | "m+": this.getMinutes(), //分 7 | "s+": this.getSeconds(), //秒 8 | "q+": Math.floor((this.getMonth() + 3) / 3), //季度 9 | "S": this.getMilliseconds() //毫秒 10 | }; 11 | if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); 12 | for (var k in o) 13 | if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); 14 | return fmt; 15 | } 16 | 17 | module.exports = {}; 18 | -------------------------------------------------------------------------------- /shop-server/models/users.js: -------------------------------------------------------------------------------- 1 | var mongoose = require('mongoose'); 2 | 3 | var userSchema = new mongoose.Schema({ 4 | "userId":String, 5 | "userName":String, 6 | "userPwd":String, 7 | "orderList":Array, 8 | "cartList":[ 9 | { 10 | "productId":String, 11 | "productName":String, 12 | "salePrice":String, 13 | "productImage":String, 14 | "checked":String, 15 | "productNum":String 16 | } 17 | ], 18 | "addressList":[ 19 | { 20 | "addressId": String, 21 | "userName": String, 22 | "streetName": String, 23 | "postCode": Number, 24 | "tel": Number, 25 | "isDefault": Boolean 26 | } 27 | ] 28 | }) 29 | 30 | module.exports = mongoose.model("User", userSchema); 31 | -------------------------------------------------------------------------------- /resource/css/nav-bread.css: -------------------------------------------------------------------------------- 1 | /** the page max width **/ 2 | .container { 3 | max-width: 1280px; 4 | margin: 0 auto; 5 | padding: 0 10px; 6 | } 7 | 8 | .nav-breadcrumb-wrap { 9 | background: #f0f0f0; 10 | } 11 | 12 | 13 | /** 面包屑 **/ 14 | .nav-breadcrumb { 15 | padding: 10px 0; 16 | line-height: 25px; 17 | font-size: 14px; 18 | } 19 | 20 | .nav-breadcrumb a { 21 | position: relative; 22 | margin-right: 16px; 23 | color: #999; 24 | } 25 | 26 | .nav-breadcrumb a:after { 27 | position: absolute; 28 | top: 3px; 29 | right: -12px; 30 | content: "/"; 31 | line-height: 1.2; 32 | } 33 | 34 | .nav-breadcrumb a:hover { 35 | color: #ee7a23; 36 | } 37 | 38 | .nav-breadcrumb span { 39 | color: #ee7a23; 40 | } 41 | -------------------------------------------------------------------------------- /src/util/currency.js: -------------------------------------------------------------------------------- 1 | const digitsRE = /(\d{3})(?=\d)/g 2 | 3 | export function currency (value, currency, decimals) { 4 | value = parseFloat(value) 5 | if (!isFinite(value) || (!value && value !== 0)) return '' 6 | currency = currency != null ? currency : '¥' 7 | decimals = decimals != null ? decimals : 2 8 | var stringified = Math.abs(value).toFixed(decimals) 9 | var _int = decimals 10 | ? stringified.slice(0, -1 - decimals) 11 | : stringified 12 | var i = _int.length % 3 13 | var head = i > 0 14 | ? (_int.slice(0, i) + (_int.length > 3 ? ',' : '')) 15 | : '' 16 | var _float = decimals 17 | ? stringified.slice(-1 - decimals) 18 | : '' 19 | var sign = value < 0 ? '-' : '' 20 | return sign + currency + head + 21 | _int.slice(i).replace(digitsRE, '$1,') + 22 | _float 23 | } 24 | -------------------------------------------------------------------------------- /src/components/Modal.vue: -------------------------------------------------------------------------------- 1 | 21 | 36 | 39 | -------------------------------------------------------------------------------- /resource/css/nav-footer.css: -------------------------------------------------------------------------------- 1 | .footer { 2 | background-color: #f5f7fc; 3 | color: #333; 4 | position: relative; 5 | } 6 | 7 | .footer__wrap { 8 | max-width: 1280px; 9 | margin: 0 auto; 10 | } 11 | 12 | .footer__secondary { 13 | border-top: 1px solid rgba(51, 51, 51, 0.15); 14 | } 15 | 16 | .footer__secondary .footer__inner { 17 | height: 100px; 18 | } 19 | 20 | .footer__inner { 21 | max-width: 1280px; 22 | margin: 0 auto; 23 | position: relative; 24 | } 25 | 26 | .footer__secondary__nav { 27 | position: absolute; 28 | top: 50%; 29 | -webkit-transform: translateY(-50%); 30 | -ms-transform: translateY(-50%); 31 | transform: translateY(-50%); 32 | right: 36px; 33 | } 34 | 35 | .footer__secondary__nav a, 36 | .footer__secondary__nav span { 37 | color: #ada9a5; 38 | margin-left: 3.3125em; 39 | } 40 | 41 | .footer__secondary__nav a:first-child, 42 | .footer__secondary__nav span:first-child { 43 | margin-left: 0; 44 | } 45 | -------------------------------------------------------------------------------- /src/assets/css/nav-footer.css: -------------------------------------------------------------------------------- 1 | .footer { 2 | background-color: #f5f7fc; 3 | color: #333; 4 | position: relative; 5 | } 6 | 7 | .footer__wrap { 8 | max-width: 1280px; 9 | margin: 0 auto; 10 | } 11 | 12 | .footer__secondary { 13 | border-top: 1px solid rgba(51, 51, 51, 0.15); 14 | } 15 | 16 | .footer__secondary .footer__inner { 17 | height: 100px; 18 | } 19 | 20 | .footer__inner { 21 | max-width: 1280px; 22 | margin: 0 auto; 23 | position: relative; 24 | } 25 | 26 | .footer__secondary__nav { 27 | position: absolute; 28 | top: 50%; 29 | -webkit-transform: translateY(-50%); 30 | -ms-transform: translateY(-50%); 31 | transform: translateY(-50%); 32 | right: 36px; 33 | } 34 | 35 | .footer__secondary__nav a, 36 | .footer__secondary__nav span { 37 | color: #ada9a5; 38 | margin-left: 3.3125em; 39 | } 40 | 41 | .footer__secondary__nav a:first-child, 42 | .footer__secondary__nav span:first-child { 43 | margin-left: 0; 44 | } 45 | -------------------------------------------------------------------------------- /src/components/NavBread.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 24 | 25 | 26 | 69 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import GoodsList from '@/views/GoodsList.vue' 4 | import Cart from '@/views/Cart.vue' 5 | import Address from '@/views/Address.vue' 6 | import OrderConfirm from '@/views/OrderConfirm.vue' 7 | import OrderSuccess from '@/views/OrderSuccess.vue' 8 | 9 | import axios from 'axios'; 10 | 11 | Vue.use(Router) 12 | 13 | const router = new Router({ 14 | routes: [ 15 | { 16 | path: '/', 17 | redirect: '/goods' 18 | }, 19 | { 20 | path: '/goods', 21 | name: 'GoodsList', 22 | component: GoodsList 23 | }, 24 | { 25 | path: '/cart', 26 | name: 'Cart', 27 | component: Cart 28 | }, 29 | { 30 | path: '/address', 31 | name: 'Address', 32 | component: Address 33 | }, 34 | { 35 | path: '/OrderConfirm', 36 | name: 'OrderConfirm', 37 | component: OrderConfirm 38 | }, 39 | { 40 | path: '/OrderSuccess', 41 | name: 'OrderSuccess', 42 | component: OrderSuccess 43 | }, 44 | ] 45 | }); 46 | 47 | // 判断是否需要登录权限以及是否登录 48 | router.beforeEach((to, from, next) => { 49 | axios.get("http://localhost:3000/users/checkLogin").then(response => { 50 | var res = response.data; 51 | if(res.status == '0'){ 52 | next(); 53 | } else { 54 | if(to.fullPath == '/goods'){ 55 | next() 56 | } else { 57 | next({path: '/goods'}) 58 | } 59 | } 60 | }) 61 | }) 62 | 63 | export default router; 64 | -------------------------------------------------------------------------------- /mock/goods.json: -------------------------------------------------------------------------------- 1 | { 2 | "status":"0", 3 | "result":[ 4 | { 5 | "productId":"10001", 6 | "productName":"小米空气净化器 2", 7 | "salePrice":"699", 8 | "productImage":"小米空气净化器 2.jpg" 9 | }, 10 | { 11 | "productId":"10002", 12 | "productName":"米家空气净化器Pro", 13 | "salePrice":"1499", 14 | "productImage":"米家空气净化器Pro.jpg" 15 | }, 16 | { 17 | "productId":"10003", 18 | "productName":"米家PM2.5检测仪", 19 | "salePrice":"399", 20 | "productImage":"米家PM2.5检测仪.jpg" 21 | }, 22 | { 23 | "productId":"10004", 24 | "productName":"九号平衡车", 25 | "salePrice":"1999", 26 | "productImage":"九号平衡车.jpg" 27 | }, 28 | { 29 | "productId":"10005", 30 | "productName":"小米路由器 3", 31 | "salePrice":"139", 32 | "productImage":"小米路由器 3.jpg" 33 | }, 34 | { 35 | "productId":"10006", 36 | "productName":"米家压力 IH 电饭煲", 37 | "salePrice":"999", 38 | "productImage":"米家压力 IH 电饭煲.jpg" 39 | }, 40 | { 41 | "productId":"10007", 42 | "productName":"米家IH电饭煲", 43 | "salePrice":"399", 44 | "productImage":"米家IH电饭煲.jpg" 45 | }, 46 | { 47 | "productId":"10008", 48 | "productName":"米家恒温电水壶", 49 | "salePrice":"199", 50 | "productImage":"米家恒温电水壶.jpg" 51 | }, 52 | { 53 | "productId":"10009", 54 | "productName":"米家小白智能摄像机", 55 | "salePrice":"399", 56 | "productImage":"米家小白智能摄像机.jpg" 57 | }, 58 | { 59 | "productId":"10010", 60 | "productName":"Yeelight床头灯", 61 | "salePrice":"249", 62 | "productImage":"Yeelight床头灯.jpg" 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /resource/css/nav-header.css: -------------------------------------------------------------------------------- 1 | .header { 2 | width: 100%; 3 | background-color: white; 4 | font-family: "moderat",sans-serif; 5 | font-size: 16px; 6 | } 7 | 8 | .goods-svg { 9 | position: absolute; 10 | width: 0; 11 | height: 0; 12 | overflow: hidden; 13 | } 14 | .navbar { 15 | display: flex; 16 | justify-content: space-between; 17 | align-content: center; 18 | width: 100%; 19 | height: 70px; 20 | max-width: 1280px; 21 | margin: 0 auto; 22 | padding: 5px 20px 10px 20px; 23 | } 24 | .navbar-left-container { 25 | display: flex; 26 | justify-content: flex-start; 27 | align-items: center; 28 | margin-left: -20px; 29 | } 30 | .header a, .footer a { 31 | color: #666; 32 | text-decoration: none; 33 | } 34 | .navbar-brand-logo { 35 | /*width: 120px;*/ 36 | margin-top: 10px; 37 | } 38 | a { 39 | -webkit-transition: color .3s ease-out; 40 | transition: color .3s ease-out; 41 | } 42 | .navbar-right-container { 43 | display: flex; 44 | justify-content: flex-start; 45 | align-items: center; 46 | } 47 | .navbar-menu-container { 48 | display: flex; 49 | justify-content: flex-end; 50 | align-items: center; 51 | padding-top: 10px; 52 | } 53 | .navbar-link { 54 | padding-left: 15px; 55 | } 56 | .navbar-cart-container { 57 | position: relative; 58 | } 59 | .navbar-cart-count { 60 | justify-content: center; 61 | align-items: center; 62 | position: absolute; 63 | top: -9px; 64 | right: -11px; 65 | width: 20px; 66 | border-radius: 10px; 67 | color: white; 68 | background-color: #eb767d; 69 | font-size: 16px; 70 | font-weight: bold; 71 | text-align: center; 72 | } 73 | .navbar-cart-logo { 74 | width: 25px; 75 | height: 25px; 76 | transform: scaleX(-1); 77 | } 78 | -------------------------------------------------------------------------------- /src/assets/css/nav-header.css: -------------------------------------------------------------------------------- 1 | .header { 2 | width: 100%; 3 | background-color: white; 4 | font-family: "moderat",sans-serif; 5 | font-size: 16px; 6 | } 7 | 8 | .goods-svg { 9 | position: absolute; 10 | width: 0; 11 | height: 0; 12 | overflow: hidden; 13 | } 14 | .navbar { 15 | display: flex; 16 | justify-content: space-between; 17 | align-content: center; 18 | width: 100%; 19 | height: 70px; 20 | max-width: 1280px; 21 | margin: 0 auto; 22 | padding: 5px 20px 10px 20px; 23 | } 24 | .navbar-left-container { 25 | display: flex; 26 | justify-content: flex-start; 27 | align-items: center; 28 | margin-left: -20px; 29 | } 30 | .header a, .footer a { 31 | color: #666; 32 | text-decoration: none; 33 | } 34 | .navbar-brand-logo { 35 | /*width: 120px;*/ 36 | margin-top: 10px; 37 | } 38 | a { 39 | -webkit-transition: color .3s ease-out; 40 | transition: color .3s ease-out; 41 | } 42 | .navbar-right-container { 43 | display: flex; 44 | justify-content: flex-start; 45 | align-items: center; 46 | } 47 | .navbar-menu-container { 48 | display: flex; 49 | justify-content: flex-end; 50 | align-items: center; 51 | padding-top: 10px; 52 | } 53 | .navbar-link { 54 | padding-left: 15px; 55 | } 56 | .navbar-cart-container { 57 | position: relative; 58 | } 59 | .navbar-cart-count { 60 | justify-content: center; 61 | align-items: center; 62 | position: absolute; 63 | top: -9px; 64 | right: -11px; 65 | width: 20px; 66 | border-radius: 10px; 67 | color: white; 68 | background-color: #eb767d; 69 | font-size: 16px; 70 | font-weight: bold; 71 | text-align: center; 72 | } 73 | .navbar-cart-logo { 74 | width: 25px; 75 | height: 25px; 76 | transform: scaleX(-1); 77 | } 78 | -------------------------------------------------------------------------------- /static/loading-svg/loading-bars.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /shop-server/bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('shop-server:server'); 9 | var http = require('http'); 10 | 11 | /** 12 | * Get port from environment and store in Express. 13 | */ 14 | 15 | var port = normalizePort(process.env.PORT || '3000'); 16 | app.set('port', port); 17 | 18 | /** 19 | * Create HTTP server. 20 | */ 21 | 22 | var server = http.createServer(app); 23 | 24 | /** 25 | * Listen on provided port, on all network interfaces. 26 | */ 27 | 28 | server.listen(port); 29 | server.on('error', onError); 30 | server.on('listening', onListening); 31 | 32 | /** 33 | * Normalize a port into a number, string, or false. 34 | */ 35 | 36 | function normalizePort(val) { 37 | var port = parseInt(val, 10); 38 | 39 | if (isNaN(port)) { 40 | // named pipe 41 | return val; 42 | } 43 | 44 | if (port >= 0) { 45 | // port number 46 | return port; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | /** 53 | * Event listener for HTTP server "error" event. 54 | */ 55 | 56 | function onError(error) { 57 | if (error.syscall !== 'listen') { 58 | throw error; 59 | } 60 | 61 | var bind = typeof port === 'string' 62 | ? 'Pipe ' + port 63 | : 'Port ' + port; 64 | 65 | // handle specific listen errors with friendly messages 66 | switch (error.code) { 67 | case 'EACCES': 68 | console.error(bind + ' requires elevated privileges'); 69 | process.exit(1); 70 | break; 71 | case 'EADDRINUSE': 72 | console.error(bind + ' is already in use'); 73 | process.exit(1); 74 | break; 75 | default: 76 | throw error; 77 | } 78 | } 79 | 80 | /** 81 | * Event listener for HTTP server "listening" event. 82 | */ 83 | 84 | function onListening() { 85 | var addr = server.address(); 86 | var bind = typeof addr === 'string' 87 | ? 'pipe ' + addr 88 | : 'port ' + addr.port; 89 | debug('Listening on ' + bind); 90 | } 91 | -------------------------------------------------------------------------------- /resource/css/login.css: -------------------------------------------------------------------------------- 1 | .regi_form_input{ 2 | position: relative; 3 | height: 42px; 4 | line-height: 42px; 5 | background: none; 6 | margin-bottom: 15px; 7 | font-size: 14px; 8 | overflow: hidden; 9 | border:1px solid #ccc; 10 | padding-bottom: 0; 11 | } 12 | .regi_form_input .icon { 13 | display: inline-block; 14 | float: left; 15 | width: 25px; 16 | height: 29px; 17 | margin: 6px 0 0 14px; 18 | background-position: 4px 5px; 19 | background-image: url("/static/icon.png"); 20 | background-repeat: no-repeat; 21 | } 22 | .regi_form_input .IconPwd { 23 | background-position: -198px 3px; 24 | } 25 | .regi_form_input .regi_login_input{ 26 | position: absolute; 27 | left:45px; 28 | top:0; 29 | padding: 9px 0 10px; 30 | width: 270px; 31 | font-size: 14px; 32 | zoom: 1; 33 | border: none; 34 | color: #333; 35 | /*height: 23px;*/ 36 | line-height: 23px; 37 | background: 0 0!important; 38 | } 39 | .md-title{ 40 | position: absolute; 41 | top: 14px; 42 | line-height: 24px; 43 | padding: 8px 0; 44 | color: #333; 45 | font-size: 18px; 46 | font-weight: 400; 47 | font-style: normal; 48 | } 49 | .login-wrap{ 50 | margin-top:30px; 51 | } 52 | .md-content .btn-login{ 53 | display: block; 54 | height: 38px; 55 | line-height: 38px; 56 | border: 2px solid #009de6; 57 | background: #009de6; 58 | color: #fff; 59 | font-size: 18px; 60 | text-align: center; 61 | } 62 | .btn-login:hover { 63 | background: #61b1ef; 64 | border: 2px solid #61b1ef; 65 | } 66 | .error-wrap .error{ 67 | font-size: 12px; 68 | color: #d31723; 69 | visibility: hidden; 70 | display: block; 71 | padding: 0 0 7px 17px; 72 | line-height: 16px; 73 | height: 16px; 74 | text-align: left; 75 | background: url("/static/icon.png") 0 -100px no-repeat; 76 | } 77 | .md-content .error-wrap .error-show{ 78 | visibility: visible; 79 | height: auto; 80 | } 81 | -------------------------------------------------------------------------------- /src/assets/css/login.css: -------------------------------------------------------------------------------- 1 | .regi_form_input{ 2 | position: relative; 3 | height: 42px; 4 | line-height: 42px; 5 | background: none; 6 | margin-bottom: 15px; 7 | font-size: 14px; 8 | overflow: hidden; 9 | border:1px solid #ccc; 10 | padding-bottom: 0; 11 | } 12 | .regi_form_input .icon { 13 | display: inline-block; 14 | float: left; 15 | width: 25px; 16 | height: 29px; 17 | margin: 6px 0 0 14px; 18 | background-position: 4px 5px; 19 | background-image: url("/static/icon.png"); 20 | background-repeat: no-repeat; 21 | } 22 | .regi_form_input .IconPwd { 23 | background-position: -198px 3px; 24 | } 25 | .regi_form_input .regi_login_input{ 26 | position: absolute; 27 | left:45px; 28 | top:0; 29 | padding: 9px 0 10px; 30 | width: 270px; 31 | font-size: 14px; 32 | zoom: 1; 33 | border: none; 34 | color: #333; 35 | /*height: 23px;*/ 36 | line-height: 23px; 37 | background: 0 0!important; 38 | } 39 | .md-title{ 40 | position: absolute; 41 | top: 14px; 42 | line-height: 24px; 43 | padding: 8px 0; 44 | color: #333; 45 | font-size: 18px; 46 | font-weight: 400; 47 | font-style: normal; 48 | } 49 | .login-wrap{ 50 | margin-top:30px; 51 | } 52 | .md-content .btn-login{ 53 | display: block; 54 | height: 38px; 55 | line-height: 38px; 56 | border: 2px solid #009de6; 57 | background: #009de6; 58 | color: #fff; 59 | font-size: 18px; 60 | text-align: center; 61 | } 62 | .btn-login:hover { 63 | background: #61b1ef; 64 | border: 2px solid #61b1ef; 65 | } 66 | .error-wrap .error{ 67 | font-size: 12px; 68 | color: #d31723; 69 | visibility: hidden; 70 | display: block; 71 | padding: 0 0 7px 17px; 72 | line-height: 16px; 73 | height: 16px; 74 | text-align: left; 75 | background: url("/static/icon.png") 0 -100px no-repeat; 76 | } 77 | .md-content .error-wrap .error-show{ 78 | visibility: visible; 79 | height: auto; 80 | } 81 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App' 5 | import axios from 'axios' 6 | import Vuex from 'vuex' 7 | import router from './router' 8 | import VueLazyLoad from 'vue-lazyload' 9 | import infiniteScroll from 'vue-infinite-scroll' 10 | 11 | axios.defaults.withCredentials = true; 12 | 13 | import {currency} from './util/currency' 14 | Vue.filter("currency",currency); 15 | 16 | Vue.use(infiniteScroll); 17 | Vue.use(Vuex); 18 | Vue.config.productionTip = false 19 | 20 | Vue.use(VueLazyLoad, { 21 | loading: 'static/loading-svg/loading-bars.svg', 22 | attempt: 3, // default 1 23 | }) 24 | 25 | const store = new Vuex.Store({ 26 | state: { 27 | nickName: '', 28 | cartCount: 0 29 | }, 30 | mutations: { 31 | // 更新用户信息 32 | updateUserInfo(state, nickName){ 33 | state.nickName = nickName; 34 | }, 35 | updateCartCount(state, cartCount){ 36 | state.cartCount += cartCount; 37 | }, 38 | clearCartCount(state){ 39 | state.cartCount = 0; 40 | } 41 | } 42 | }) 43 | /* eslint-disable no-new */ 44 | new Vue({ 45 | el: '#app', 46 | store, 47 | router, 48 | components: { App }, 49 | template: '', 50 | mounted(){ 51 | this.checkLogin(); 52 | this.getCartCount(); 53 | }, 54 | methods:{ 55 | checkLogin(){ 56 | axios.get("http://localhost:3000/users/checkLogin").then(res=> { 57 | var res = res.data; 58 | if (res.status == "0") { 59 | this.$store.commit("updateUserInfo", res.result); 60 | }else{ 61 | if(this.$route.path!="/goods"){ 62 | this.$router.push("/goods"); 63 | } 64 | } 65 | }); 66 | }, 67 | getCartCount(){ 68 | axios.get("http://localhost:3000/users/getCartCount").then(res=>{ 69 | var res = res.data; 70 | if(res.status=="0"){ 71 | this.$store.commit("updateCartCount",res.result); 72 | } 73 | }); 74 | } 75 | } 76 | }) 77 | -------------------------------------------------------------------------------- /src/components/NavFooter.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 36 | 37 | 38 | 85 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue2-shop", 3 | "version": "1.0.0", 4 | "description": "vue2全栈商城项目", 5 | "author": "worldsong <772083612@qq.com>", 6 | "private": true, 7 | "scripts": { 8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", 9 | "start": "npm run dev", 10 | "build": "node build/build.js" 11 | }, 12 | "dependencies": { 13 | "axios": "^0.19.0", 14 | "vue": "^2.5.2", 15 | "vue-infinite-scroll": "^2.0.2", 16 | "vue-lazyload": "^1.2.6", 17 | "vue-router": "^3.0.1", 18 | "vuex": "^3.1.1" 19 | }, 20 | "devDependencies": { 21 | "autoprefixer": "^7.1.2", 22 | "babel-core": "^6.22.1", 23 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 24 | "babel-loader": "^7.1.1", 25 | "babel-plugin-syntax-jsx": "^6.18.0", 26 | "babel-plugin-transform-runtime": "^6.22.0", 27 | "babel-plugin-transform-vue-jsx": "^3.5.0", 28 | "babel-preset-env": "^1.3.2", 29 | "babel-preset-stage-2": "^6.22.0", 30 | "chalk": "^2.0.1", 31 | "copy-webpack-plugin": "^4.0.1", 32 | "css-loader": "^0.28.0", 33 | "extract-text-webpack-plugin": "^3.0.0", 34 | "file-loader": "^1.1.4", 35 | "friendly-errors-webpack-plugin": "^1.6.1", 36 | "html-webpack-plugin": "^2.30.1", 37 | "node-notifier": "^5.1.2", 38 | "optimize-css-assets-webpack-plugin": "^3.2.0", 39 | "ora": "^1.2.0", 40 | "portfinder": "^1.0.13", 41 | "postcss-import": "^11.0.0", 42 | "postcss-loader": "^2.0.8", 43 | "postcss-url": "^7.2.1", 44 | "rimraf": "^2.6.0", 45 | "semver": "^5.3.0", 46 | "shelljs": "^0.7.6", 47 | "uglifyjs-webpack-plugin": "^1.1.1", 48 | "url-loader": "^0.5.8", 49 | "vue-loader": "^13.3.0", 50 | "vue-style-loader": "^3.0.1", 51 | "vue-template-compiler": "^2.5.2", 52 | "webpack": "^3.6.0", 53 | "webpack-bundle-analyzer": "^2.9.0", 54 | "webpack-dev-server": "^2.9.1", 55 | "webpack-merge": "^4.1.0" 56 | }, 57 | "engines": { 58 | "node": ">= 6.0.0", 59 | "npm": ">= 3.0.0" 60 | }, 61 | "browserslist": [ 62 | "> 1%", 63 | "last 2 versions", 64 | "not ie <= 8" 65 | ] 66 | } 67 | -------------------------------------------------------------------------------- /shop-server/app.js: -------------------------------------------------------------------------------- 1 | var createError = require('http-errors'); 2 | var express = require('express'); 3 | var path = require('path'); 4 | var cookieParser = require('cookie-parser'); 5 | var logger = require('morgan'); 6 | 7 | var indexRouter = require('./routes/index'); 8 | var usersRouter = require('./routes/users'); 9 | var goodsRouter = require('./routes/goods'); 10 | 11 | var app = express(); 12 | 13 | // view engine setup 14 | app.set('views', path.join(__dirname, 'views')); 15 | app.set('view engine', 'ejs'); 16 | 17 | app.use(logger('dev')); 18 | app.use(express.json()); 19 | app.use(express.urlencoded({ extended: false })); 20 | app.use(cookieParser()); 21 | app.use(express.static(path.join(__dirname, 'public'))); 22 | 23 | app.all('*', function(req, res, next) { 24 | // CORS配置 25 | res.header("Access-Control-Allow-Origin", "http://localhost:8080"); 26 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); 27 | res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS"); 28 | res.header("Access-Control-Allow-Credentials", true); 29 | res.header("X-Powered-By",' 3.2.1') 30 | res.header("Content-Type", "application/json;charset=utf-8"); 31 | next(); 32 | }); 33 | 34 | // 登录拦截,限制服务端接口或页面的访问 35 | app.use(function (req, res, next) { 36 | if(req.cookies.userId){ 37 | next(); 38 | }else { 39 | console.log("url: " + req.originalUrl); 40 | if(req.originalUrl =='/users/login' || req.originalUrl=='/users/logout' || req.originalUrl.indexOf('/goods/list')> -1 ){ 41 | next(); 42 | } else { 43 | res.json({ 44 | status: '10001', 45 | msg: '当前未登录', 46 | result: '' 47 | }) 48 | } 49 | } 50 | }) 51 | app.use('/', indexRouter); 52 | app.use('/users', usersRouter); 53 | app.use('/goods', goodsRouter); 54 | 55 | // catch 404 and forward to error handler 56 | app.use(function(req, res, next) { 57 | next(createError(404)); 58 | }); 59 | 60 | // error handler 61 | app.use(function(err, req, res, next) { 62 | // set locals, only providing error in development 63 | res.locals.message = err.message; 64 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 65 | 66 | // render the error page 67 | res.status(err.status || 500); 68 | res.render('error'); 69 | }); 70 | 71 | module.exports = app; 72 | -------------------------------------------------------------------------------- /static/loading-svg/loading-spinning-bubbles.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | // Template version: 1.3.1 3 | // see http://vuejs-templates.github.io/webpack for documentation. 4 | 5 | const path = require('path') 6 | 7 | module.exports = { 8 | dev: { 9 | 10 | // Paths 11 | assetsSubDirectory: 'static', 12 | assetsPublicPath: '/', 13 | proxyTable: { 14 | '/goods': {// 8080/goods 15 | target: 'http://localhost:3000' 16 | }, 17 | '/users': {// 8080/users 18 | target: 'http://localhost:3000' 19 | } 20 | }, 21 | 22 | // Various Dev Server settings 23 | host: 'localhost', // can be overwritten by process.env.HOST 24 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 25 | autoOpenBrowser: false, 26 | errorOverlay: true, 27 | notifyOnErrors: true, 28 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 29 | 30 | 31 | /** 32 | * Source Maps 33 | */ 34 | 35 | // https://webpack.js.org/configuration/devtool/#development 36 | devtool: 'cheap-module-eval-source-map', 37 | 38 | // If you have problems debugging vue-files in devtools, 39 | // set this to false - it *may* help 40 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 41 | cacheBusting: true, 42 | 43 | cssSourceMap: true 44 | }, 45 | 46 | build: { 47 | // Template for index.html 48 | index: path.resolve(__dirname, '../dist/index.html'), 49 | 50 | // Paths 51 | assetsRoot: path.resolve(__dirname, '../dist'), 52 | assetsSubDirectory: 'static', 53 | assetsPublicPath: '/', 54 | 55 | /** 56 | * Source Maps 57 | */ 58 | 59 | productionSourceMap: true, 60 | // https://webpack.js.org/configuration/devtool/#production 61 | devtool: '#source-map', 62 | 63 | // Gzip off by default as many popular static hosts such as 64 | // Surge or Netlify already gzip all static assets for you. 65 | // Before setting to `true`, make sure to: 66 | // npm install --save-dev compression-webpack-plugin 67 | productionGzip: false, 68 | productionGzipExtensions: ['js', 'css'], 69 | 70 | // Run the build command with an extra argument to 71 | // View the bundle analyzer report after build finishes: 72 | // `npm run build --report` 73 | // Set to `true` or `false` to always turn it on or off 74 | bundleAnalyzerReport: process.env.npm_config_report 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 85 | 86 | 96 | 97 | 98 | 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue2-shop 2 | 3 | > vue2全栈商城项目 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # Node.js 环境搭建 9 | 使用 nvm 安装 node.js 10 | nvm install 8.16.0 11 | 12 | # Vue项目的开发环境 13 | npm i vue-cli -g 14 | 15 | # project init (项目初始化) 16 | vue init webpack vue2-shop 17 | 18 | # install dependencies(安装依赖) 19 | npm install 20 | 21 | # serve with hot reload at localhost:8080 (试运行)【开发环境】 22 | npm run dev 23 | 24 | # build for production with minification (项目构建打包)【生产环境】 25 | npm run build 26 | 27 | # build for production and view the bundle analyzer report 【可视化打包文件分析】 28 | npm run build --report 29 | ``` 30 | 31 | For a detailed explanation on how things work, 联系:772083612@qq.com 32 | 33 | ### 全栈商城项目实战 34 | Vue2商城需求分析及框架搭建,商品列表实现 35 | 36 | 商品列表模块实现的两种方式 37 | 38 | 商城后台框架搭建与商品列表滚动分页 39 | 40 | 业务功能开发(一)(分页查询及登录拦截) 41 | 42 | 业务功能开发(二)(购物车) 43 | 44 | 业务功能开发(三)(购物车及收货详情) 45 | 46 | 业务功能开发(四)(订单处理) 47 | 48 | Vue项目打包优化及部署上线+课程总结 49 | 50 | ### 环境资源 51 | Webstorm、git、nvm、vue-devtools、mongodb 52 | 53 | 链接:https://pan.baidu.com/s/192FcnHjvm3cZc6b6mUlUmw 54 | 提取码:ite8 55 | 56 | ### 作业 57 | 第一课作业: 58 | 59 | 样式引入到组件内,Vue页面实现和原型html页面有出入;没有做到像素级同步,为什么? 60 | 61 | 目的:考查对项目的熟悉程度,对样式的熟练程度。 62 | 63 | 第一节课笔记: 64 | 65 | 1、[全栈商城项目课程简介](http://note.youdao.com/noteshare?id=0e6c11f3488fd15e683ab988c268f9bd&sub=A5F0609EAADE4A84B9CA3F6C80C9F745) 66 | 67 | 2、[第一讲:需求分析及框架搭建,商品列表实现(2019-6-12)](http://note.youdao.com/noteshare?id=896962ddba20d4f819d3aa737c77ebfc&sub=19ED3C0159CF4D1EAA1A6D44B890A587) 68 | 69 | 第二节课笔记: 70 | 71 | 1、[第二讲:商品列表模块实现的两种方式及价格过滤组件的实现(含移动端适配)(2019-6-15)](http://note.youdao.com/noteshare?id=7691af7561a89a5c9fa893cc508c59ca&sub=EC8150ED8BAE457A962429D10B535002) 72 | 73 | 第三节课笔记: 74 | 75 | 1、[第三讲:商城后台框架搭建与商品列表滚动分页及价格过滤功能实现(2019-06-16)](http://note.youdao.com/noteshare?id=e88d3c4a173662250250bf92c7d5793f&sub=A31204453D4044FABD271477A95F37B3) 76 | 77 | 第四节课笔记: 78 | 79 | 1、[第四讲:业务功能开发(一)商品加入购物车及用户登录登出(2019-06-19)](http://note.youdao.com/noteshare?id=a90a8d9247ac081998fb99227e96713a&sub=21CBB2CAB7EF4AFEA021136F79739537) 80 | 81 | 第五次课笔记: 82 | 83 | 1、[第五讲:业务功能开发(二)(登录相关的高级功能及购物车列表功能实现)(2019-06-22)](http://note.youdao.com/noteshare?id=4db8bc44b5ad8d7a3d36380abd583a71&sub=4A63B853907A4145B7BED0D3DC5E3B3D) 84 | 85 | 第六节课笔记: 86 | 87 | 1、[第六讲:业务功能开发(三)(购物车及收货地址页面功能)(2019-06-23)](http://note.youdao.com/noteshare?id=dbc4bc7329441d829a9e438cf26136c3&sub=49F7E133B387467186DFC9DD5668DF94) 88 | 89 | 第七节课笔记: 90 | 91 | 1、[第七讲:业务功能开发(四)(收货地址及订单处理)(2019-06-26)](http://note.youdao.com/noteshare?id=ce2bd0f972b426f23d0f1f53cd252b9a&sub=E03F950D1E6D48C5B27BEC59C58DF56D) 92 | 93 | 第八次课笔记: 94 | 95 | 1、[第八讲:Vuex引入及Vue项目打包优化及部署上线+课程总结(2019-6-29)](http://note.youdao.com/noteshare?id=96a37a14b0a44282ccec477ad559f055&sub=2AA14C5144CC4F7DA5F4ABFE93674D02) 96 | -------------------------------------------------------------------------------- /src/views/OrderSuccess.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 97 | 98 | 99 | 102 | -------------------------------------------------------------------------------- /shop-server/routes/goods.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var mongoose = require('mongoose'); 4 | var Goods = require('../models/goods'); 5 | 6 | /* 连接MongoDB 数据库 */ 7 | mongoose.connect('mongodb://127.0.0.1:27017/vue_shop'); 8 | 9 | mongoose.connection.on("connected", function () { 10 | console.log("MongoDB connected success."); 11 | }); 12 | 13 | mongoose.connection.on("error", function () { 14 | console.log("MongoDB connected fail."); 15 | }); 16 | 17 | mongoose.connection.on("disconnected", function () { 18 | console.log("MongoDB connected disconnected."); 19 | }); 20 | /* 21 | * 商品列表分页 http://localhost:3000/goods?page=2&pageSize=8&sort=1&priceLevel=all 22 | * */ 23 | router.get("/list", function (req, res, next) { 24 | let page = parseInt(req.param("page")); 25 | let pageSize = parseInt(req.param("pageSize")); 26 | let sort = req.param("sort"); // 1 表示升序, -1 表示降序 27 | let priceLevel = req.param("priceLevel"); 28 | let skip = (page - 1) * pageSize; 29 | let priceGt = ''; 30 | let priceLte = ''; 31 | let params = {}; 32 | if(priceLevel != 'all'){ 33 | switch(priceLevel){ 34 | case '0':priceGt = 0;priceLte=100;break; 35 | case '1':priceGt = 100;priceLte=500;break; 36 | case '2':priceGt = 500;priceLte=1000;break; 37 | case '3':priceGt = 1000;priceLte=2000;break; 38 | case '4':priceGt = 2000;priceLte=3000;break; 39 | case '5':priceGt = 3000;priceLte=6000;break; 40 | } 41 | params = { 42 | salePrice: { 43 | $gt: priceGt, 44 | $lte: priceLte 45 | } 46 | } 47 | } 48 | let goodsModel = Goods.find(params).skip(skip).limit(pageSize); 49 | goodsModel.sort({'salePrice': sort}); 50 | goodsModel.exec(function (err, doc) { 51 | if(err){ 52 | res.json({ 53 | status: '1', 54 | msg: err.message 55 | }); 56 | } else { 57 | res.json({ 58 | status: '0', 59 | msg: '', 60 | result: { 61 | count: doc.length, 62 | list: doc 63 | } 64 | }) 65 | } 66 | }) 67 | }); 68 | 69 | /* 70 | * 商品加入购物车 71 | * */ 72 | router.post("/addCart", function (req, res, next) { 73 | var userId = '100000077', productId = req.body.productId; 74 | var User = require('../models/users'); 75 | User.findOne({userId: userId}, function (err, userDoc) { 76 | if(err){ 77 | res.json({ 78 | status: "1", 79 | msg: err.message 80 | }) 81 | } else { 82 | console.log("userDoc: " + userDoc ); 83 | if(userDoc){ 84 | var goodsItem = ''; 85 | userDoc.cartList.forEach(function (item) { // 遍历购物车,是否早已存在该商品 86 | if(item.productId == productId){ // 购物车中存在该商品,令其数量加1 87 | goodsItem = item; 88 | item.productNum ++; 89 | } 90 | }); 91 | if(goodsItem){ // 购物车存在该商品,保存更新的购物车数据 92 | userDoc.save(function (err2, doc2) { 93 | if(err2){ 94 | res.json({ 95 | status: '1', 96 | msg: err2.message 97 | }) 98 | } else { 99 | res.json({ 100 | status: '0', 101 | msg: '', 102 | result: 'success' 103 | }) 104 | } 105 | }) 106 | } else { 107 | Goods.findOne({productId: productId}, function (err1, doc) { 108 | if(err1){ 109 | res.json({ 110 | status: "1", 111 | msg: err1.message 112 | }) 113 | } else { 114 | if(doc){ // 新添加一种商品到购物车 115 | doc.productNum = 1; 116 | doc.checked = 1; 117 | userDoc.cartList.push(doc); 118 | userDoc.save(function (err2, doc2) { 119 | if(err2){ 120 | res.json({ 121 | status: "1", 122 | msg: err2.message 123 | }) 124 | } else { 125 | res.json({ 126 | status: "0", 127 | msg: '', 128 | result: 'success' 129 | }) 130 | } 131 | }) 132 | } 133 | } 134 | }) 135 | } 136 | } 137 | } 138 | }) 139 | }) 140 | 141 | module.exports = router; 142 | -------------------------------------------------------------------------------- /resource/views/下单成功页.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 下单成功 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 | 17 | 18 | arrow-short 19 | 20 | 21 | 22 | status-ok 23 | 24 | 25 | 26 | 27 | cart 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 59 |
60 | 68 |
69 |
70 |

订单提交成功,请尽快付款!

71 |
72 | 73 |
74 |
    75 |
  • 确认 收货地址
  • 76 |
  • 核对 订单信息
  • 77 |
  • 支付 方式
  • 78 |
  • 成功提交 订单
  • 79 |
80 |
81 | 82 |
83 |
84 |
85 |

恭喜!
订单提交成功,请尽快付款!

86 |

87 | 订单号:100000001 88 | 应付金额:¥40390 89 |

90 |
91 |
92 | 购物车列表 93 |
94 |
95 | 商品列表 96 |
97 |
98 |
99 |
100 |
101 | 121 |
122 | 123 | 124 | -------------------------------------------------------------------------------- /resource/views/商品列表页.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 商品列表 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | arrow-short 19 | 20 | 21 | 22 | status-ok 23 | 24 | 25 | 26 | 27 | cart 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 59 |
60 | 68 |
69 |
70 |
71 | 排序: 72 | 默认 73 | 价格 74 | 筛选 75 |
76 |
77 | 78 |
79 |
80 |
价格区间:
81 |
选择价格
82 |
83 | ¥ 0 - 100 元 84 |
85 |
86 |
87 | 88 | 89 |
90 |
91 |
    92 |
  • 93 |
    94 | 95 |
    96 |
    97 |
    小米电视4 55英寸
    98 |
    3999
    99 |
    100 | 加入购物车 101 |
    102 |
    103 |
  • 104 |
105 |
106 |
107 |
108 |
109 |
110 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /src/views/GoodsList.vue: -------------------------------------------------------------------------------- 1 | 79 | 80 | 228 | 229 | 230 | 233 | -------------------------------------------------------------------------------- /src/views/OrderConfirm.vue: -------------------------------------------------------------------------------- 1 | 130 | 131 | 197 | 198 | 199 | 202 | -------------------------------------------------------------------------------- /src/components/NavHeader.vue: -------------------------------------------------------------------------------- 1 | 84 | 85 | 160 | 161 | 162 | 242 | -------------------------------------------------------------------------------- /shop-server/routes/users.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | var User = require('./../models/users'); 4 | require('./../util/date_format.js') 5 | 6 | /* 用户登录接口 */ 7 | router.post('/login', function(req, res, next) { 8 | var param = { 9 | userName: req.body.userName, 10 | userPwd: req.body.userPwd 11 | } 12 | User.findOne(param, function (err, doc) { 13 | if(err){ 14 | res.json({ 15 | status: "1", 16 | msg: err.message 17 | }) 18 | } else { 19 | if(doc){ 20 | res.cookie("userId", doc.userId, { 21 | path: '/', 22 | maxAge: 1000 * 60 * 60 23 | }); 24 | res.cookie("userName", doc.userName, { 25 | path: '/', 26 | maxAge: 1000 * 60 * 60 27 | }) 28 | res.json({ 29 | status: "0", 30 | msg: '', 31 | result: { 32 | userName: doc.userName 33 | } 34 | }) 35 | } 36 | } 37 | }) 38 | }); 39 | 40 | /* 用户登出接口 */ 41 | router.post('/logout', function (req, res, next) { 42 | res.cookie("userId", "", { 43 | path: '/', 44 | maxAge: -1 45 | }) 46 | res.json({ 47 | status: "0", 48 | msg: '', 49 | result: '' 50 | }) 51 | }); 52 | 53 | /* 检查登录状态 */ 54 | router.get('/checkLogin', function (req, res, next) { 55 | if(req.cookies.userId){ 56 | res.json({ 57 | status: '0', 58 | msg: '', 59 | result: req.cookies.userName 60 | }) 61 | } else { 62 | res.json({ 63 | status: '1', 64 | msg: '未登录', 65 | result: '' 66 | }) 67 | } 68 | }); 69 | 70 | /* 查询当前用户的购物车数据 */ 71 | router.get("/cartlist", function (req, res, next) { 72 | var userId = req.cookies.userId; 73 | User.findOne({userId:userId}, function (err, doc) { 74 | if(err){ 75 | res.json({ 76 | status: '1', 77 | msg: err.message, 78 | result: '' 79 | }) 80 | } else { 81 | if(doc){ 82 | res.json({ 83 | status: '0', 84 | msg: '', 85 | result: doc.cartList 86 | }) 87 | } 88 | } 89 | }) 90 | }); 91 | 92 | /* 购物车商品删除 */ 93 | router.post("/cartDel", function (req, res, next) { 94 | var userId = req.cookies.userId, productId = req.body.productId; 95 | User.update({ 96 | userId:userId 97 | },{ 98 | $pull: { 99 | 'cartList':{ 100 | 'productId':productId 101 | } 102 | } 103 | }, function (err, doc) { 104 | if(err){ 105 | res.json({ 106 | status: "1", 107 | msg: err.msg, 108 | result: '' 109 | }) 110 | } else { 111 | res.json({ 112 | status: '0', 113 | msg: '', 114 | result: 'success' 115 | }) 116 | } 117 | }) 118 | }) 119 | 120 | /* 修改商品数量 */ 121 | router.post("/cartEdit", function (req, res, next) { 122 | var userId = req.cookies.userId, 123 | productId = req.body.productId, 124 | productNum = req.body.productNum, 125 | checked = req.body.checked; 126 | 127 | User.update({"userId": userId, "cartList.productId": productId},{ 128 | "cartList.$.productNum": productNum, 129 | "cartList.$.checked": checked 130 | }, function (err, doc) { 131 | if(err){ 132 | res.json({ 133 | status: "1", 134 | msg: err.msg, 135 | result: '' 136 | }) 137 | } else { 138 | res.json({ 139 | status: '0', 140 | msg: '', 141 | result: 'success' 142 | }) 143 | } 144 | }) 145 | }); 146 | 147 | /* 购物车全选功能 */ 148 | router.post("/editCheckAll", function (req, res, next) { 149 | var userId = req.cookies.userId; 150 | var checkAll = req.body.checkAll ? '1': '0'; 151 | User.findOne({userId: userId}, function (err, user) { 152 | if(err){ 153 | res.json({ 154 | status: '1', 155 | msg: err.message, 156 | result: '' 157 | }) 158 | } else { 159 | if(user){ 160 | user.cartList.forEach((item) => { 161 | item.checked = checkAll; 162 | }) 163 | user.save(function (err1, doc) { 164 | if(err){ 165 | res.json({ 166 | status: '1', 167 | msg: err.message, 168 | result: '' 169 | }) 170 | } else { 171 | res.json({ 172 | status: '0', 173 | msg: '', 174 | result: 'success' 175 | }) 176 | } 177 | }) 178 | } 179 | } 180 | }) 181 | }); 182 | 183 | /* 查询用户地址接口 */ 184 | router.get("/addressList", function (req, res, next) { 185 | var userId = req.cookies.userId; 186 | User.findOne({userId: userId}, function (err, doc) { 187 | if(err){ 188 | res.json({ 189 | status: '1', 190 | msg: err.message, 191 | result: '' 192 | }) 193 | } else { 194 | res.json({ 195 | status: '0', 196 | msg: '', 197 | result: doc.addressList 198 | }) 199 | } 200 | }) 201 | }); 202 | 203 | /* 设置默认地址接口 */ 204 | router.post("/setDefault", function (req, res, next) { 205 | var userId = req.cookies.userId; 206 | var addressId = req.body.addressId; 207 | if(!addressId){ 208 | res.json({ 209 | status: '1003', 210 | msg: 'addressId is null', 211 | result: '' 212 | }) 213 | } else { 214 | User.findOne({userId: userId}, function (err, doc) { 215 | if(err){ 216 | res.json({ 217 | status: '1', 218 | msg: err.message, 219 | result: '' 220 | }) 221 | } else { 222 | var addressList = doc.addressList; 223 | addressList.forEach((item)=> { 224 | if(item.addressId == addressId){ 225 | item.isDefault = true; 226 | } else { 227 | item.isDefault = false; 228 | } 229 | }); 230 | doc.save(function (err1, doc1) { 231 | if(err){ 232 | res.json({ 233 | status: '1', 234 | msg: err1.message, 235 | result: '' 236 | }) 237 | }else { 238 | res.json({ 239 | status: '0', 240 | msg: '', 241 | result: 'success' 242 | }) 243 | } 244 | }) 245 | } 246 | }) 247 | } 248 | }); 249 | 250 | /* 删除地址接口 */ 251 | router.post("/delAddress", function (req, res, next) { 252 | var userId = req.cookies.userId; 253 | var addressId = req.body.addressId; 254 | User.update({ 255 | userId: userId 256 | }, { 257 | $pull:{ 258 | 'addressList':{ 259 | 'addressId':addressId 260 | } 261 | } 262 | }, function (err, doc) { 263 | if(err){ 264 | res.json({ 265 | status: '1', 266 | msg: err.message, 267 | result: '' 268 | }) 269 | } else { 270 | res.json({ 271 | status: '0', 272 | msg: '', 273 | result: 'success' 274 | }) 275 | } 276 | }) 277 | }); 278 | 279 | /* 付款结算功能的实现*/ 280 | router.post("/payMent", function (req, res, next) { 281 | var userId = req.cookies.userId; 282 | var addressId = req.body.addressId; 283 | var orderTotal = req.body.orderTotal; 284 | User.findOne({userId: userId}, function (err, doc) { 285 | if(err){ 286 | res.json({ 287 | status: '1', 288 | msg: err.message, 289 | result: '' 290 | }) 291 | } else { 292 | var address = '', goodsList = []; 293 | // 获取当前用户的地址信息 294 | doc.addressList.forEach((item) => { 295 | if(addressId == item.addressId){ 296 | address = item; 297 | } 298 | }); 299 | // 获取用户购买的商品 300 | doc.cartList.filter((item) => { 301 | if(item.checked == '1'){ 302 | goodsList.push(item); 303 | } 304 | }) 305 | 306 | var platform = '622'; 307 | var r1 = Math.floor(Math.random()* 10); 308 | var r2 = Math.floor(Math.random()* 10); 309 | 310 | var sysDate = new Date().Format('yyyyMMddhhmmss'); 311 | var createDate = new Date().Format('yyyy-MM-dd hh:mm:ss'); 312 | var orderId = platform + r1 + sysDate + r2; 313 | 314 | var order = { 315 | orderId: orderId, 316 | orderTotal: orderTotal, 317 | addressInfo: address, 318 | goodsList: goodsList, 319 | orderStatus: '1', 320 | createDate: createDate 321 | } 322 | 323 | doc.orderList.push(order); 324 | 325 | doc.save(function (err1, doc1) {{ 326 | if(err1){ 327 | res.json({ 328 | status: '1', 329 | msg: err.message, 330 | result: '' 331 | }) 332 | } else { 333 | res.json({ 334 | status: '0', 335 | msg: '', 336 | result: { 337 | orderId: order.orderId, 338 | orderTotal: order.orderTotal 339 | } 340 | }) 341 | } 342 | } 343 | 344 | }) 345 | } 346 | }) 347 | }); 348 | 349 | /* 根据订单Id 查询订单信息 */ 350 | router.post("/orderDetail", function (req, res, next) { 351 | var userId = req.cookies.userId; 352 | var orderId = req.body.orderId; 353 | var orderTotal = ''; 354 | User.findOne({ userId: userId}, function (err, userInfo) { 355 | if(err){ 356 | res.json({ 357 | status: '1', 358 | msg: err.msg, 359 | result: '' 360 | }) 361 | } else { 362 | var orderList = userInfo.orderList; 363 | if(orderList.length > 0){ 364 | orderList.forEach((item) => { 365 | if(item.orderId == orderId){ 366 | orderTotal =item.orderTotal; 367 | } 368 | }); 369 | if(orderTotal > 0){ 370 | res.json({ 371 | status: '0', 372 | msg: '', 373 | result: { 374 | orderId: orderId, 375 | orderTotal: orderTotal 376 | } 377 | }) 378 | } else { 379 | res.json({ 380 | status: '12001', 381 | msg: '无此订单', 382 | result: '' 383 | }) 384 | } 385 | } else { 386 | res.json({ 387 | status: '13001', 388 | msg: '当前用户未创建订单', 389 | result: '' 390 | }) 391 | } 392 | } 393 | }) 394 | }); 395 | 396 | /* 获取购车商品数量 */ 397 | router.get("/getCartCount", function (req,res,next) { 398 | if(req.cookies && req.cookies.userId){ 399 | console.log("userId:"+req.cookies.userId); 400 | var userId = req.cookies.userId; 401 | User.findOne({"userId":userId}, function (err,doc) { 402 | if(err){ 403 | res.json({ 404 | status:"0", 405 | msg:err.message 406 | }); 407 | }else{ 408 | let cartList = doc.cartList; 409 | let cartCount = 0; 410 | cartList.map(function(item){ 411 | cartCount += parseFloat(item.productNum); 412 | }); 413 | res.json({ 414 | status:"0", 415 | msg:"", 416 | result:cartCount 417 | }); 418 | } 419 | }); 420 | }else{ 421 | res.json({ 422 | status:"0", 423 | msg:"当前用户不存在" 424 | }); 425 | } 426 | }); 427 | module.exports = router; 428 | -------------------------------------------------------------------------------- /resource/views/订单确认.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 订单确认 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 | 17 | 18 | arrow-short 19 | 20 | 21 | 22 | status-ok 23 | 24 | 25 | 26 | 27 | cart 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 59 |
60 | 68 | 69 | 70 | 71 | add2 72 | 73 | 74 | 75 | ok 76 | 77 | 78 | 79 | edit 80 | 81 | 82 | 83 | delete 84 | 85 | 86 | 87 | clock 88 | 89 | 90 | 91 | 92 |
93 |
94 | 95 |
96 |
    97 |
  • 确认 收货地址
  • 98 |
  • 核对 订单信息
  • 99 |
  • 支付 方式
  • 100 |
  • 成功提交 订单
  • 101 |
102 |
103 | 104 | 105 |
106 |

填写并核对订单信息

107 |
108 |
109 |
110 |
111 |
    112 |
  • 商品
  • 113 |
  • 单价
  • 114 |
  • 数量
  • 115 |
  • 小计
  • 116 |
117 |
118 |
    119 |
  • 120 |
    121 |
    122 | 123 |
    124 |
    125 |
    小米电视4 55英寸
    126 | 127 |
    128 |
    129 |
    130 |
    3999
    131 |
    132 |
    133 |
    134 |
    135 |
    136 | ×10 137 |
    138 |
    139 |
    有货
    140 |
    141 |
    142 |
    143 |
    ¥39990
    144 |
    145 |
  • 146 |
147 |
148 |
149 | 150 | 151 |
152 |
153 |
    154 |
  • 155 | 商品总额: 156 | ¥39990 157 |
  • 158 |
  • 159 | 运费: 160 | ¥100 161 |
  • 162 |
  • 163 | 优惠: 164 | ¥100 165 |
  • 166 |
  • 167 | 纳税: 168 | ¥400 169 |
  • 170 |
  • 171 | 应付总额: 172 | ¥40390 173 |
  • 174 |
175 |
176 |
177 | 178 |
179 |
180 | 181 |
182 |
183 | 184 |
185 |
186 |
187 |
188 | 208 |
209 | 210 | 211 | -------------------------------------------------------------------------------- /src/views/Address.Vue: -------------------------------------------------------------------------------- 1 | 147 | 148 | 240 | 241 | 242 | 245 | -------------------------------------------------------------------------------- /resource/views/收货地址选择页.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 收货地址 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | arrow-short 18 | 19 | 20 | 21 | status-ok 22 | 23 | 24 | 25 | 26 | cart 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 58 |
59 |
60 | 61 | 62 | 63 | add 64 | 65 | 66 | 67 | ok 68 | 69 | 70 | 71 | edit 72 | 73 | 74 | 75 | delete 76 | 77 | 78 | 79 | 80 | 81 | 82 | clock 83 | 84 | 85 | 86 | question 87 | 88 | 89 | 90 | 91 | 92 | 93 |
94 |
95 |
96 |

check out

97 |
98 | 99 |
100 |
    101 |
  • 确认 收货地址
  • 102 |
  • 核对 订单信息
  • 103 |
  • 支付 方式
  • 104 |
  • 成功提交 订单
  • 105 |
106 |
107 | 108 | 109 |
110 |

收货人信息

111 |
112 |
113 |
114 |
    115 |
  • 116 |
    117 |
    XXX(用户名)
    118 |
    湖南省长沙市麓谷企业广场
    119 |
    772083612@qq.com
    120 |
    121 |
    122 | 123 | 124 | 125 |
    126 |
    127 | Set default 128 |
    129 |
    默认地址
    130 |
  • 131 |
  • 132 |
    133 | 134 | 135 | 136 |

    新增收货地址

    137 |
    138 |
  • 139 |
140 |
141 | 142 | 151 |
152 | 153 | 154 |
155 |

货运方式

156 |
157 | 160 |
161 |
162 |
    163 |
  • 164 |
    京东快递
    165 |
    包邮
    166 |
    167 |

    自签收后7天内退货,15天内换货,可享1次上门取件服务

    168 |
    169 |
  • 170 |
171 |
172 |
173 |
174 | 去结算 175 |
176 |
177 |
178 |
179 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /resource/views/购物车页面.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Cart 9 | 10 | 11 | 12 | 13 | 14 | 15 | 39 | 40 | 41 |
42 | 43 | 44 | 45 | arrow-short 46 | 47 | 48 | 49 | status-ok 50 | 51 | 52 | 53 | 54 | cart 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 86 |
87 | 95 | 97 | 98 | 99 | add2 100 | 102 | 103 | 104 | ok 105 | 107 | 108 | 109 | edit 110 | 112 | 113 | 114 | delete 115 | 117 | 119 | 121 | 123 | 124 | 125 | clock 126 | 128 | 130 | 131 | 132 | 133 |
134 |
135 |
136 |

我的购物车

137 |
138 |
139 |
140 |
141 |
    142 |
  • 商品
  • 143 |
  • 单价
  • 144 |
  • 数量
  • 145 |
  • 小计
  • 146 |
  • 操作
  • 147 |
148 |
149 |
    150 |
  • 151 |
    152 | 159 |
    160 | 161 |
    162 |
    163 |
    小米电视4 55英寸
    164 |
    165 |
    166 |
    167 |
    3999
    168 |
    169 |
    170 |
    171 |
    172 |
    173 | - 174 | 10 175 | + 176 |
    177 |
    178 |
    179 |
    180 |
    181 |
    39990
    182 |
    183 |
    184 | 191 |
    192 |
  • 193 |
194 |
195 |
196 |
197 |
198 | 208 |
209 |
210 | 总价: 39990 211 |
212 |
213 | 去结算 214 |
215 |
216 |
217 |
218 |
219 |
220 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /src/views/Cart.vue: -------------------------------------------------------------------------------- 1 | 144 | 145 | 276 | 277 | 278 | 302 | -------------------------------------------------------------------------------- /resource/css/goods-list.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | .accessory-list > ul:after { 4 | visibility: hidden; 5 | display: block; 6 | content: " "; 7 | clear: both; 8 | } 9 | 10 | .accessory-list > ul > li { 11 | float: left; 12 | width: 23.80952%; 13 | margin-right: 1.5873%; 14 | margin-bottom: 1.5873%; 15 | background: #fff; 16 | border: 2px solid #e9e9e9; 17 | -webkit-transition: all .5s ease-out; 18 | transition: all .5s ease-out; 19 | } 20 | 21 | .accessory-list > ul > li:hover { 22 | border-color: #ee7a23; 23 | -webkit-transform: translateY(-5px); 24 | -ms-transform: translateY(-5px); 25 | transform: translateY(-5px); 26 | -webkit-box-shadow: 0 0 10px #999; 27 | box-shadow: 0 0 10px #999; 28 | -webkit-transition: all .5s ease-out; 29 | transition: all .5s ease-out; 30 | } 31 | 32 | .accessory-list.col-4 > ul > li { 33 | width: 23.80952%; 34 | margin-right: 1.5873%; 35 | margin-bottom: 1.5873%; 36 | } 37 | 38 | .accessory-list.col-4 > ul > li:nth-child(4n) { 39 | margin-right: 0; 40 | } 41 | 42 | .accessory-list.col-5 > ul > li { 43 | width: 18.73016%; 44 | margin-right: 1.5873%; 45 | margin-bottom: 1.5873%; 46 | } 47 | 48 | .accessory-list.col-5 > ul > li:nth-child(5n) { 49 | margin-right: 0; 50 | } 51 | 52 | .accessory-list .pic { 53 | overflow: hidden; 54 | } 55 | 56 | .accessory-list .pic a { 57 | display: block; 58 | width: 100%; 59 | height: 100%; 60 | } 61 | 62 | .accessory-list .pic img { 63 | width: 100%; 64 | } 65 | 66 | .accessory-list .main { 67 | padding: 10px 10px 10px 10px; 68 | } 69 | 70 | .accessory-list .main .name { 71 | height: 2em; 72 | line-height: 1.25em; 73 | padding-bottom: 10px; 74 | font-family: "moderat", sans-serif; 75 | font-weight: bold; 76 | overflow: hidden; 77 | } 78 | 79 | .accessory-list .main .price { 80 | float: left; 81 | line-height: 30px; 82 | color: #ee7a23; 83 | font-size: 1.25em; 84 | } 85 | 86 | .accessory-list .main .quantity { 87 | float: right; 88 | } 89 | 90 | .accessory-list .main .btn-area { 91 | clear: both; 92 | padding-top: 10px; 93 | } 94 | 95 | .accessory-list .main .btn-area .btn { 96 | width: 100%; 97 | } 98 | 99 | @media only screen and (max-width: 991px) { 100 | .accessory-list.col-4 > ul > li { 101 | width: 32.27513%; 102 | margin-right: 1.5873%; 103 | margin-bottom: 1.5873%; 104 | } 105 | 106 | .accessory-list.col-4 > ul > li:nth-child(4n) { 107 | margin-right: 1.5873%; 108 | } 109 | 110 | .accessory-list.col-4 > ul > li:nth-child(3n) { 111 | margin-right: 0; 112 | } 113 | 114 | .accessory-list.col-5 > ul > li { 115 | width: 23.80952%; 116 | margin-right: 1.5873%; 117 | margin-bottom: 1.5873%; 118 | } 119 | 120 | .accessory-list.col-5 > ul > li:nth-child(5n) { 121 | margin-right: 20px; 122 | } 123 | 124 | .accessory-list.col-5 > ul > li:nth-child(4n) { 125 | margin-right: 0; 126 | } 127 | 128 | .accessory-list .main .name { 129 | font-size: 1.3rem; 130 | } 131 | 132 | .accessory-list .main .btn-area .btn { 133 | font-size: 1.2rem; 134 | } 135 | } 136 | 137 | @media only screen and (max-width: 767px) { 138 | .accessory-list { 139 | font-size: 1.2rem; 140 | } 141 | 142 | .accessory-list.col-4 > ul > li, .accessory-list.col-5 > ul > li { 143 | width: 100%; 144 | margin-bottom: 10px; 145 | padding: 10px; 146 | border: none; 147 | border-top: 1px solid #e9e9e9; 148 | border-bottom: 1px solid #e9e9e9; 149 | } 150 | 151 | .accessory-list.col-4 > ul > li:hover, .accessory-list.col-5 > ul > li:hover { 152 | border-color: none; 153 | -webkit-transform: none; 154 | -ms-transform: none; 155 | transform: none; 156 | -webkit-box-shadow: none; 157 | box-shadow: none; 158 | -webkit-transition: none .5s ease-out; 159 | transition: none .5s ease-out; 160 | } 161 | 162 | .accessory-list .pic { 163 | float: left; 164 | width: 110px; 165 | height: 72px; 166 | border: 1px solid #e9e9e9; 167 | } 168 | 169 | .accessory-list .pic a { 170 | display: block; 171 | width: 100%; 172 | height: 100%; 173 | padding-bottom: 0; 174 | } 175 | 176 | .accessory-list .main { 177 | padding: 0 0 0 90px; 178 | } 179 | 180 | .accessory-list .main:after { 181 | visibility: hidden; 182 | display: block; 183 | content: " "; 184 | clear: both; 185 | } 186 | 187 | .accessory-list .main .price { 188 | float: none; 189 | } 190 | 191 | .accessory-list .main .quantity { 192 | float: left; 193 | } 194 | 195 | .accessory-list .main .name { 196 | height: auto; 197 | min-height: 30px; 198 | } 199 | 200 | .accessory-list .main .btn-area { 201 | padding: 0; 202 | clear: none; 203 | float: right; 204 | } 205 | 206 | .accessory-list .main .btn-area .btn { 207 | height: 30px; 208 | line-height: 30px; 209 | padding-left: .8em; 210 | padding-right: .8em; 211 | font-size: 1rem; 212 | letter-spacing: .1em; 213 | } 214 | } 215 | 216 | .filter { 217 | width: 230px; 218 | padding: 0 20px 0 20px; 219 | color: #605F5F; 220 | -webkit-transition: right .5s ease-out; 221 | transition: right .5s ease-out; 222 | } 223 | 224 | .filter dl { 225 | min-height: 180px; 226 | margin-bottom: 50px; 227 | } 228 | 229 | .filter dt { 230 | margin-bottom: 30px; 231 | font-family: "moderat", sans-serif; 232 | letter-spacing: .25em; 233 | text-transform: uppercase; 234 | font-weight: bold; 235 | } 236 | 237 | .filter dd { 238 | line-height: 1.2em; 239 | margin: 20px 0; 240 | } 241 | 242 | .filter dd a { 243 | display: block; 244 | padding: 5px 0; 245 | -webkit-transition: padding-left .3s ease-out; 246 | transition: padding-left .3s ease-out; 247 | } 248 | 249 | .filter dd a:hover, .filter dd .cur { 250 | color: #ee7a23; 251 | } 252 | 253 | .filter .filter-category ul { 254 | display: none; 255 | padding-left: 20px; 256 | } 257 | 258 | .filter .filter-category li { 259 | line-height: 1.2em; 260 | margin: 10px 0; 261 | } 262 | 263 | .filter .filter-category ul > li > ul { 264 | margin-bottom: 20px; 265 | } 266 | 267 | .filter .filter-category ul > li > ul .icon-arrow-down { 268 | display: none; 269 | } 270 | 271 | .filter .filter-category a:hover .icon-arrow-down { 272 | fill: #ee7a23; 273 | -webkit-transition: -webkit-transform .3s ease-out; 274 | transition: -webkit-transform .3s ease-out; 275 | transition: transform .3s ease-out; 276 | transition: transform .3s ease-out, -webkit-transform .3s ease-out; 277 | } 278 | 279 | .filter .filter-category .icon-arrow-down { 280 | width: 11px; 281 | height: 11px; 282 | fill: #605F5F; 283 | vertical-align: middle; 284 | margin-left: 3px; 285 | -webkit-transition: -webkit-transform .3s ease-out; 286 | transition: -webkit-transform .3s ease-out; 287 | transition: transform .3s ease-out; 288 | transition: transform .3s ease-out, -webkit-transform .3s ease-out; 289 | } 290 | 291 | .filter .filter-category .open ~ ul { 292 | display: block; 293 | } 294 | 295 | .filter .filter-category .open .icon-arrow-down { 296 | -webkit-transform: rotate(180deg); 297 | -ms-transform: rotate(180deg); 298 | transform: rotate(180deg); 299 | } 300 | 301 | .filter .filter-price dd a:hover, .filter .filter-price dd .cur { 302 | -webkit-transition: padding-left .3s ease-out; 303 | transition: padding-left .3s ease-out; 304 | border-left: 2px solid #ee7a23; 305 | padding-left: 15px; 306 | } 307 | 308 | .filter .filter-price dd .cur { 309 | font-weight: bold; 310 | } 311 | 312 | .filter.filterby-show { 313 | right: 0; 314 | -webkit-transition: right .5s ease-out; 315 | transition: right .5s ease-out; 316 | } 317 | 318 | @media only screen and (max-width: 767px) { 319 | .filter { 320 | position: fixed; 321 | right: -230px; 322 | top: 0; 323 | z-index: 201; 324 | height: 100%; 325 | background: #fff; 326 | padding: 0; 327 | margin: 0; 328 | overflow: auto; 329 | } 330 | 331 | .filter dl { 332 | min-height: 0; 333 | margin: 0; 334 | } 335 | 336 | .filter dt { 337 | background: #f0f0f0; 338 | height: 55px; 339 | line-height: 55px; 340 | margin: 0; 341 | padding-left: 20px; 342 | } 343 | 344 | .filter dd { 345 | margin: 0; 346 | } 347 | 348 | .filter dd a { 349 | padding: 12px 10px 12px 15px; 350 | border-bottom: 1px solid #e9e9e9; 351 | } 352 | 353 | .filter dd li { 354 | margin: 0; 355 | } 356 | 357 | .filter .filter-category ul { 358 | padding-left: 0; 359 | } 360 | 361 | .filter .filter-category ul li > a { 362 | padding-left: 30px; 363 | } 364 | 365 | .filter .filter-category ul li > ul a { 366 | padding-left: 45px; 367 | } 368 | 369 | .filter .filter-category .icon-arrow-down { 370 | float: right; 371 | } 372 | } 373 | 374 | /** search resule **/ 375 | /** 搜索结果页 **/ 376 | .search-result-wrap { 377 | background: #f0f0f0; 378 | } 379 | 380 | .search-result-wrap .search-result-num { 381 | padding: 10px 0; 382 | line-height: 25px; 383 | } 384 | 385 | .search-result-wrap .search-result-num strong { 386 | color: #d1434a; 387 | } 388 | 389 | .accessory-result { 390 | display: -webkit-box; 391 | display: -webkit-flex; 392 | display: -ms-flexbox; 393 | display: flex; 394 | } 395 | 396 | .accessory-result .filter { 397 | margin-right: 25px; 398 | } 399 | 400 | .accessory-result .accessory-list-wrap { 401 | -webkit-box-flex: 1; 402 | -webkit-flex: 1; 403 | -ms-flex: 1; 404 | flex: 1; 405 | } 406 | 407 | .filter-nav { 408 | height: 55px; 409 | line-height: 55px; 410 | margin: 60px 0 30px 0; 411 | padding: 0 20px; 412 | background: #fff; 413 | text-align: right; 414 | overflow: hidden; 415 | } 416 | 417 | .filter-nav a { 418 | margin: 0 10px; 419 | } 420 | 421 | .filter-nav a.cur, .filter-nav a:hover { 422 | color: #ee7a23; 423 | } 424 | 425 | .filter-nav .sort-up .icon-arrow-short { 426 | -webkit-transform: rotate(180deg); 427 | -ms-transform: rotate(180deg); 428 | transform: rotate(180deg); 429 | } 430 | 431 | .filter-nav .filterby { 432 | display: none; 433 | letter-spacing: .25em; 434 | text-transform: uppercase; 435 | font-size: 12px; 436 | } 437 | 438 | @media only screen and (max-width: 767px) { 439 | .search-result-wrap { 440 | background: none; 441 | padding: 0 10px; 442 | } 443 | 444 | .filter-nav { 445 | margin: 0; 446 | padding: 0 10px; 447 | background: #f0f0f0; 448 | border-top: 1px solid #e9e9e9; 449 | text-align: left; 450 | } 451 | 452 | .filter-nav:after { 453 | visibility: hidden; 454 | display: block; 455 | content: " "; 456 | clear: both; 457 | } 458 | 459 | .filter-nav a { 460 | float: left; 461 | } 462 | 463 | .filter-nav .filterby { 464 | display: block; 465 | float: right; 466 | } 467 | 468 | .filter-nav .sortby { 469 | display: none; 470 | } 471 | 472 | .accessory-result .filter { 473 | margin: 0; 474 | } 475 | } 476 | 477 | .icon-arrow-short { 478 | width: 11px; 479 | height: 11px; 480 | } 481 | 482 | .page-title-accessory { 483 | display: none; 484 | } 485 | 486 | .accessory-title:after { 487 | visibility: hidden; 488 | display: block; 489 | content: " "; 490 | clear: both; 491 | } 492 | 493 | .accessory-title h2 { 494 | float: left; 495 | } 496 | 497 | .accessory-title .accessory-more-link { 498 | float: right; 499 | line-height: 30px; 500 | } 501 | 502 | .accessory-title .accessory-more-link .icon-arrow-short { 503 | margin-left: 15px; 504 | -webkit-transform: rotate(-90deg); 505 | -ms-transform: rotate(-90deg); 506 | transform: rotate(-90deg); 507 | } 508 | 509 | .accessory-title .accessory-more-link:hover { 510 | color: #ee7a23; 511 | } 512 | 513 | .accessory-title .accessory-more-link:hover .icon-arrow-short { 514 | fill: #ee7a23; 515 | } 516 | 517 | .accessory-classify { 518 | position: relative; 519 | } 520 | 521 | .accessory-result-page .view-more-normal { 522 | height: 120px; 523 | margin-top: 40px; 524 | } 525 | 526 | .accessory-result-page .view-more-normal .view-more-btn { 527 | top: 0; 528 | padding-top: 40px; 529 | } 530 | 531 | @media only screen and (max-width: 767px) { 532 | .page-title-accessory { 533 | display: block; 534 | } 535 | 536 | .accessory-classify { 537 | padding-bottom: 55px; 538 | } 539 | 540 | .accessory-title { 541 | padding-left: 10px; 542 | padding-right: 10px; 543 | border: none; 544 | } 545 | 546 | .accessory-title .accessory-more-link { 547 | position: absolute; 548 | bottom: 10px; 549 | left: 0; 550 | width: 100%; 551 | height: 45px; 552 | line-height: 45px; 553 | background: #fff; 554 | border-top: 1px solid #e9e9e9; 555 | border-bottom: 1px solid #e9e9e9; 556 | } 557 | 558 | .accessory-result-page { 559 | font-size: 1.2rem; 560 | } 561 | 562 | .accessory-result-page .btn-wrap { 563 | margin: 0; 564 | } 565 | 566 | .accessory-result-page .btn-wrap .btn { 567 | /*line-height: 1.2em;*/ 568 | white-space: normal; 569 | } 570 | 571 | .accessory-result-page .view-more-normal { 572 | height: 60px; 573 | margin-top: 10px; 574 | } 575 | 576 | .accessory-result-page .view-more-normal .view-more-btn { 577 | top: 0; 578 | padding-top: 20px; 579 | } 580 | } 581 | -------------------------------------------------------------------------------- /src/assets/css/goods-list.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | .accessory-list > ul:after { 4 | visibility: hidden; 5 | display: block; 6 | content: " "; 7 | clear: both; 8 | } 9 | 10 | .accessory-list > ul > li { 11 | float: left; 12 | width: 23.80952%; 13 | margin-right: 1.5873%; 14 | margin-bottom: 1.5873%; 15 | background: #fff; 16 | border: 2px solid #e9e9e9; 17 | -webkit-transition: all .5s ease-out; 18 | transition: all .5s ease-out; 19 | } 20 | 21 | .accessory-list > ul > li:hover { 22 | border-color: #ee7a23; 23 | -webkit-transform: translateY(-5px); 24 | -ms-transform: translateY(-5px); 25 | transform: translateY(-5px); 26 | -webkit-box-shadow: 0 0 10px #999; 27 | box-shadow: 0 0 10px #999; 28 | -webkit-transition: all .5s ease-out; 29 | transition: all .5s ease-out; 30 | } 31 | 32 | .accessory-list.col-4 > ul > li { 33 | width: 23.80952%; 34 | margin-right: 1.5873%; 35 | margin-bottom: 1.5873%; 36 | } 37 | 38 | .accessory-list.col-4 > ul > li:nth-child(4n) { 39 | margin-right: 0; 40 | } 41 | 42 | .accessory-list.col-5 > ul > li { 43 | width: 18.73016%; 44 | margin-right: 1.5873%; 45 | margin-bottom: 1.5873%; 46 | } 47 | 48 | .accessory-list.col-5 > ul > li:nth-child(5n) { 49 | margin-right: 0; 50 | } 51 | 52 | .accessory-list .pic { 53 | overflow: hidden; 54 | } 55 | 56 | .accessory-list .pic a { 57 | display: block; 58 | width: 100%; 59 | height: 100%; 60 | } 61 | 62 | .accessory-list .pic img { 63 | width: 100%; 64 | } 65 | 66 | .accessory-list .main { 67 | padding: 10px 10px 10px 10px; 68 | } 69 | 70 | .accessory-list .main .name { 71 | height: 2em; 72 | line-height: 1.25em; 73 | padding-bottom: 10px; 74 | font-family: "moderat", sans-serif; 75 | font-weight: bold; 76 | overflow: hidden; 77 | } 78 | 79 | .accessory-list .main .price { 80 | float: left; 81 | line-height: 30px; 82 | color: #ee7a23; 83 | font-size: 1.25em; 84 | } 85 | 86 | .accessory-list .main .quantity { 87 | float: right; 88 | } 89 | 90 | .accessory-list .main .btn-area { 91 | clear: both; 92 | padding-top: 10px; 93 | } 94 | 95 | .accessory-list .main .btn-area .btn { 96 | width: 100%; 97 | } 98 | 99 | @media only screen and (max-width: 991px) { 100 | .accessory-list.col-4 > ul > li { 101 | width: 32.27513%; 102 | margin-right: 1.5873%; 103 | margin-bottom: 1.5873%; 104 | } 105 | 106 | .accessory-list.col-4 > ul > li:nth-child(4n) { 107 | margin-right: 1.5873%; 108 | } 109 | 110 | .accessory-list.col-4 > ul > li:nth-child(3n) { 111 | margin-right: 0; 112 | } 113 | 114 | .accessory-list.col-5 > ul > li { 115 | width: 23.80952%; 116 | margin-right: 1.5873%; 117 | margin-bottom: 1.5873%; 118 | } 119 | 120 | .accessory-list.col-5 > ul > li:nth-child(5n) { 121 | margin-right: 20px; 122 | } 123 | 124 | .accessory-list.col-5 > ul > li:nth-child(4n) { 125 | margin-right: 0; 126 | } 127 | 128 | .accessory-list .main .name { 129 | font-size: 1.3rem; 130 | } 131 | 132 | .accessory-list .main .btn-area .btn { 133 | font-size: 1.2rem; 134 | } 135 | } 136 | 137 | @media only screen and (max-width: 767px) { 138 | .accessory-list { 139 | font-size: 1.2rem; 140 | } 141 | 142 | .accessory-list.col-4 > ul > li, .accessory-list.col-5 > ul > li { 143 | width: 100%; 144 | margin-bottom: 10px; 145 | padding: 10px; 146 | border: none; 147 | border-top: 1px solid #e9e9e9; 148 | border-bottom: 1px solid #e9e9e9; 149 | } 150 | 151 | .accessory-list.col-4 > ul > li:hover, .accessory-list.col-5 > ul > li:hover { 152 | border-color: none; 153 | -webkit-transform: none; 154 | -ms-transform: none; 155 | transform: none; 156 | -webkit-box-shadow: none; 157 | box-shadow: none; 158 | -webkit-transition: none .5s ease-out; 159 | transition: none .5s ease-out; 160 | } 161 | 162 | .accessory-list .pic { 163 | float: left; 164 | width: 110px; 165 | height: 72px; 166 | border: 1px solid #e9e9e9; 167 | } 168 | 169 | .accessory-list .pic a { 170 | display: block; 171 | width: 100%; 172 | height: 100%; 173 | padding-bottom: 0; 174 | } 175 | 176 | .accessory-list .main { 177 | padding: 0 0 0 90px; 178 | } 179 | 180 | .accessory-list .main:after { 181 | visibility: hidden; 182 | display: block; 183 | content: " "; 184 | clear: both; 185 | } 186 | 187 | .accessory-list .main .price { 188 | float: none; 189 | } 190 | 191 | .accessory-list .main .quantity { 192 | float: left; 193 | } 194 | 195 | .accessory-list .main .name { 196 | height: auto; 197 | min-height: 30px; 198 | } 199 | 200 | .accessory-list .main .btn-area { 201 | padding: 0; 202 | clear: none; 203 | float: right; 204 | } 205 | 206 | .accessory-list .main .btn-area .btn { 207 | height: 30px; 208 | line-height: 30px; 209 | padding-left: .8em; 210 | padding-right: .8em; 211 | font-size: 1rem; 212 | letter-spacing: .1em; 213 | } 214 | } 215 | 216 | .filter { 217 | width: 230px; 218 | padding: 0 20px 0 20px; 219 | color: #605F5F; 220 | -webkit-transition: right .5s ease-out; 221 | transition: right .5s ease-out; 222 | } 223 | 224 | .filter dl { 225 | min-height: 180px; 226 | margin-bottom: 50px; 227 | } 228 | 229 | .filter dt { 230 | margin-bottom: 30px; 231 | font-family: "moderat", sans-serif; 232 | letter-spacing: .25em; 233 | text-transform: uppercase; 234 | font-weight: bold; 235 | } 236 | 237 | .filter dd { 238 | line-height: 1.2em; 239 | margin: 20px 0; 240 | } 241 | 242 | .filter dd a { 243 | display: block; 244 | padding: 5px 0; 245 | -webkit-transition: padding-left .3s ease-out; 246 | transition: padding-left .3s ease-out; 247 | } 248 | 249 | .filter dd a:hover, .filter dd .cur { 250 | color: #ee7a23; 251 | } 252 | 253 | .filter .filter-category ul { 254 | display: none; 255 | padding-left: 20px; 256 | } 257 | 258 | .filter .filter-category li { 259 | line-height: 1.2em; 260 | margin: 10px 0; 261 | } 262 | 263 | .filter .filter-category ul > li > ul { 264 | margin-bottom: 20px; 265 | } 266 | 267 | .filter .filter-category ul > li > ul .icon-arrow-down { 268 | display: none; 269 | } 270 | 271 | .filter .filter-category a:hover .icon-arrow-down { 272 | fill: #ee7a23; 273 | -webkit-transition: -webkit-transform .3s ease-out; 274 | transition: -webkit-transform .3s ease-out; 275 | transition: transform .3s ease-out; 276 | transition: transform .3s ease-out, -webkit-transform .3s ease-out; 277 | } 278 | 279 | .filter .filter-category .icon-arrow-down { 280 | width: 11px; 281 | height: 11px; 282 | fill: #605F5F; 283 | vertical-align: middle; 284 | margin-left: 3px; 285 | -webkit-transition: -webkit-transform .3s ease-out; 286 | transition: -webkit-transform .3s ease-out; 287 | transition: transform .3s ease-out; 288 | transition: transform .3s ease-out, -webkit-transform .3s ease-out; 289 | } 290 | 291 | .filter .filter-category .open ~ ul { 292 | display: block; 293 | } 294 | 295 | .filter .filter-category .open .icon-arrow-down { 296 | -webkit-transform: rotate(180deg); 297 | -ms-transform: rotate(180deg); 298 | transform: rotate(180deg); 299 | } 300 | 301 | .filter .filter-price dd a:hover, .filter .filter-price dd .cur { 302 | -webkit-transition: padding-left .3s ease-out; 303 | transition: padding-left .3s ease-out; 304 | border-left: 2px solid #ee7a23; 305 | padding-left: 15px; 306 | } 307 | 308 | .filter .filter-price dd .cur { 309 | font-weight: bold; 310 | } 311 | 312 | .filter.filterby-show { 313 | right: 0; 314 | -webkit-transition: right .5s ease-out; 315 | transition: right .5s ease-out; 316 | } 317 | 318 | @media only screen and (max-width: 767px) { 319 | .filter { 320 | position: fixed; 321 | right: -230px; 322 | top: 0; 323 | z-index: 201; 324 | height: 100%; 325 | background: #fff; 326 | padding: 0; 327 | margin: 0; 328 | overflow: auto; 329 | } 330 | 331 | .filter dl { 332 | min-height: 0; 333 | margin: 0; 334 | } 335 | 336 | .filter dt { 337 | background: #f0f0f0; 338 | height: 55px; 339 | line-height: 55px; 340 | margin: 0; 341 | padding-left: 20px; 342 | } 343 | 344 | .filter dd { 345 | margin: 0; 346 | } 347 | 348 | .filter dd a { 349 | padding: 12px 10px 12px 15px; 350 | border-bottom: 1px solid #e9e9e9; 351 | } 352 | 353 | .filter dd li { 354 | margin: 0; 355 | } 356 | 357 | .filter .filter-category ul { 358 | padding-left: 0; 359 | } 360 | 361 | .filter .filter-category ul li > a { 362 | padding-left: 30px; 363 | } 364 | 365 | .filter .filter-category ul li > ul a { 366 | padding-left: 45px; 367 | } 368 | 369 | .filter .filter-category .icon-arrow-down { 370 | float: right; 371 | } 372 | } 373 | 374 | /** search resule **/ 375 | /** 搜索结果页 **/ 376 | .search-result-wrap { 377 | background: #f0f0f0; 378 | } 379 | 380 | .search-result-wrap .search-result-num { 381 | padding: 10px 0; 382 | line-height: 25px; 383 | } 384 | 385 | .search-result-wrap .search-result-num strong { 386 | color: #d1434a; 387 | } 388 | 389 | .accessory-result { 390 | display: -webkit-box; 391 | display: -webkit-flex; 392 | display: -ms-flexbox; 393 | display: flex; 394 | } 395 | 396 | .accessory-result .filter { 397 | margin-right: 25px; 398 | } 399 | 400 | .accessory-result .accessory-list-wrap { 401 | -webkit-box-flex: 1; 402 | -webkit-flex: 1; 403 | -ms-flex: 1; 404 | flex: 1; 405 | } 406 | 407 | .filter-nav { 408 | height: 55px; 409 | line-height: 55px; 410 | margin: 60px 0 30px 0; 411 | padding: 0 20px; 412 | background: #fff; 413 | text-align: right; 414 | overflow: hidden; 415 | } 416 | 417 | .filter-nav a { 418 | margin: 0 10px; 419 | } 420 | 421 | .filter-nav a.cur, .filter-nav a:hover { 422 | color: #ee7a23; 423 | } 424 | 425 | .filter-nav .sort-up .icon-arrow-short { 426 | -webkit-transform: rotate(180deg); 427 | -ms-transform: rotate(180deg); 428 | transform: rotate(180deg); 429 | } 430 | 431 | .filter-nav .filterby { 432 | display: none; 433 | letter-spacing: .25em; 434 | text-transform: uppercase; 435 | font-size: 12px; 436 | } 437 | 438 | @media only screen and (max-width: 767px) { 439 | .search-result-wrap { 440 | background: none; 441 | padding: 0 10px; 442 | } 443 | 444 | .filter-nav { 445 | margin: 0; 446 | padding: 0 10px; 447 | background: #f0f0f0; 448 | border-top: 1px solid #e9e9e9; 449 | text-align: left; 450 | } 451 | 452 | .filter-nav:after { 453 | visibility: hidden; 454 | display: block; 455 | content: " "; 456 | clear: both; 457 | } 458 | 459 | .filter-nav a { 460 | float: left; 461 | } 462 | 463 | .filter-nav .filterby { 464 | display: block; 465 | float: right; 466 | } 467 | 468 | .filter-nav .sortby { 469 | display: none; 470 | } 471 | 472 | .accessory-result .filter { 473 | margin: 0; 474 | } 475 | } 476 | 477 | .icon-arrow-short { 478 | width: 11px; 479 | height: 11px; 480 | } 481 | 482 | .page-title-accessory { 483 | display: none; 484 | } 485 | 486 | .accessory-title:after { 487 | visibility: hidden; 488 | display: block; 489 | content: " "; 490 | clear: both; 491 | } 492 | 493 | .accessory-title h2 { 494 | float: left; 495 | } 496 | 497 | .accessory-title .accessory-more-link { 498 | float: right; 499 | line-height: 30px; 500 | } 501 | 502 | .accessory-title .accessory-more-link .icon-arrow-short { 503 | margin-left: 15px; 504 | -webkit-transform: rotate(-90deg); 505 | -ms-transform: rotate(-90deg); 506 | transform: rotate(-90deg); 507 | } 508 | 509 | .accessory-title .accessory-more-link:hover { 510 | color: #ee7a23; 511 | } 512 | 513 | .accessory-title .accessory-more-link:hover .icon-arrow-short { 514 | fill: #ee7a23; 515 | } 516 | 517 | .accessory-classify { 518 | position: relative; 519 | } 520 | 521 | .accessory-result-page .view-more-normal { 522 | height: 120px; 523 | margin-top: 40px; 524 | } 525 | 526 | .accessory-result-page .view-more-normal .view-more-btn { 527 | top: 0; 528 | padding-top: 40px; 529 | } 530 | 531 | @media only screen and (max-width: 767px) { 532 | .page-title-accessory { 533 | display: block; 534 | } 535 | 536 | .accessory-classify { 537 | padding-bottom: 55px; 538 | } 539 | 540 | .accessory-title { 541 | padding-left: 10px; 542 | padding-right: 10px; 543 | border: none; 544 | } 545 | 546 | .accessory-title .accessory-more-link { 547 | position: absolute; 548 | bottom: 10px; 549 | left: 0; 550 | width: 100%; 551 | height: 45px; 552 | line-height: 45px; 553 | background: #fff; 554 | border-top: 1px solid #e9e9e9; 555 | border-bottom: 1px solid #e9e9e9; 556 | } 557 | 558 | .accessory-result-page { 559 | font-size: 1.2rem; 560 | } 561 | 562 | .accessory-result-page .btn-wrap { 563 | margin: 0; 564 | } 565 | 566 | .accessory-result-page .btn-wrap .btn { 567 | /*line-height: 1.2em;*/ 568 | white-space: normal; 569 | } 570 | 571 | .accessory-result-page .view-more-normal { 572 | height: 60px; 573 | margin-top: 10px; 574 | } 575 | 576 | .accessory-result-page .view-more-normal .view-more-btn { 577 | top: 0; 578 | padding-top: 20px; 579 | } 580 | } 581 | -------------------------------------------------------------------------------- /server/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue2-shop-server", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.7", 9 | "resolved": "https://registry.npm.taobao.org/accepts/download/accepts-1.3.7.tgz", 10 | "integrity": "sha1-UxvHJlF6OytB+FACHGzBXqq1B80=", 11 | "requires": { 12 | "mime-types": "~2.1.24", 13 | "negotiator": "0.6.2" 14 | } 15 | }, 16 | "array-flatten": { 17 | "version": "1.1.1", 18 | "resolved": "http://registry.npm.taobao.org/array-flatten/download/array-flatten-1.1.1.tgz", 19 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 20 | }, 21 | "body-parser": { 22 | "version": "1.19.0", 23 | "resolved": "https://registry.npm.taobao.org/body-parser/download/body-parser-1.19.0.tgz", 24 | "integrity": "sha1-lrJwnlfJxOCab9Zqj9l5hE9p8Io=", 25 | "requires": { 26 | "bytes": "3.1.0", 27 | "content-type": "~1.0.4", 28 | "debug": "2.6.9", 29 | "depd": "~1.1.2", 30 | "http-errors": "1.7.2", 31 | "iconv-lite": "0.4.24", 32 | "on-finished": "~2.3.0", 33 | "qs": "6.7.0", 34 | "raw-body": "2.4.0", 35 | "type-is": "~1.6.17" 36 | } 37 | }, 38 | "bytes": { 39 | "version": "3.1.0", 40 | "resolved": "http://registry.npm.taobao.org/bytes/download/bytes-3.1.0.tgz", 41 | "integrity": "sha1-9s95M6Ng4FiPqf3oVlHNx/gF0fY=" 42 | }, 43 | "content-disposition": { 44 | "version": "0.5.3", 45 | "resolved": "http://registry.npm.taobao.org/content-disposition/download/content-disposition-0.5.3.tgz", 46 | "integrity": "sha1-4TDK9+cnkIfFYWwgB9BIVpiYT70=", 47 | "requires": { 48 | "safe-buffer": "5.1.2" 49 | } 50 | }, 51 | "content-type": { 52 | "version": "1.0.4", 53 | "resolved": "http://registry.npm.taobao.org/content-type/download/content-type-1.0.4.tgz", 54 | "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=" 55 | }, 56 | "cookie": { 57 | "version": "0.4.0", 58 | "resolved": "https://registry.npm.taobao.org/cookie/download/cookie-0.4.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcookie%2Fdownload%2Fcookie-0.4.0.tgz", 59 | "integrity": "sha1-vrQ35wIrO21JAZ0IhmUwPr6cFLo=" 60 | }, 61 | "cookie-signature": { 62 | "version": "1.0.6", 63 | "resolved": "http://registry.npm.taobao.org/cookie-signature/download/cookie-signature-1.0.6.tgz", 64 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 65 | }, 66 | "debug": { 67 | "version": "2.6.9", 68 | "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz", 69 | "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", 70 | "requires": { 71 | "ms": "2.0.0" 72 | } 73 | }, 74 | "depd": { 75 | "version": "1.1.2", 76 | "resolved": "http://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz", 77 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 78 | }, 79 | "destroy": { 80 | "version": "1.0.4", 81 | "resolved": "http://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz", 82 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 83 | }, 84 | "ee-first": { 85 | "version": "1.1.1", 86 | "resolved": "http://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz", 87 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 88 | }, 89 | "encodeurl": { 90 | "version": "1.0.2", 91 | "resolved": "http://registry.npm.taobao.org/encodeurl/download/encodeurl-1.0.2.tgz", 92 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 93 | }, 94 | "escape-html": { 95 | "version": "1.0.3", 96 | "resolved": "http://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz", 97 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 98 | }, 99 | "etag": { 100 | "version": "1.8.1", 101 | "resolved": "http://registry.npm.taobao.org/etag/download/etag-1.8.1.tgz", 102 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 103 | }, 104 | "express": { 105 | "version": "4.17.1", 106 | "resolved": "https://registry.npm.taobao.org/express/download/express-4.17.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fexpress%2Fdownload%2Fexpress-4.17.1.tgz", 107 | "integrity": "sha1-RJH8OGBc9R+GKdOcK10Cb5ikwTQ=", 108 | "requires": { 109 | "accepts": "~1.3.7", 110 | "array-flatten": "1.1.1", 111 | "body-parser": "1.19.0", 112 | "content-disposition": "0.5.3", 113 | "content-type": "~1.0.4", 114 | "cookie": "0.4.0", 115 | "cookie-signature": "1.0.6", 116 | "debug": "2.6.9", 117 | "depd": "~1.1.2", 118 | "encodeurl": "~1.0.2", 119 | "escape-html": "~1.0.3", 120 | "etag": "~1.8.1", 121 | "finalhandler": "~1.1.2", 122 | "fresh": "0.5.2", 123 | "merge-descriptors": "1.0.1", 124 | "methods": "~1.1.2", 125 | "on-finished": "~2.3.0", 126 | "parseurl": "~1.3.3", 127 | "path-to-regexp": "0.1.7", 128 | "proxy-addr": "~2.0.5", 129 | "qs": "6.7.0", 130 | "range-parser": "~1.2.1", 131 | "safe-buffer": "5.1.2", 132 | "send": "0.17.1", 133 | "serve-static": "1.14.1", 134 | "setprototypeof": "1.1.1", 135 | "statuses": "~1.5.0", 136 | "type-is": "~1.6.18", 137 | "utils-merge": "1.0.1", 138 | "vary": "~1.1.2" 139 | } 140 | }, 141 | "finalhandler": { 142 | "version": "1.1.2", 143 | "resolved": "https://registry.npm.taobao.org/finalhandler/download/finalhandler-1.1.2.tgz", 144 | "integrity": "sha1-t+fQAP/RGTjQ/bBTUG9uur6fWH0=", 145 | "requires": { 146 | "debug": "2.6.9", 147 | "encodeurl": "~1.0.2", 148 | "escape-html": "~1.0.3", 149 | "on-finished": "~2.3.0", 150 | "parseurl": "~1.3.3", 151 | "statuses": "~1.5.0", 152 | "unpipe": "~1.0.0" 153 | } 154 | }, 155 | "forwarded": { 156 | "version": "0.1.2", 157 | "resolved": "http://registry.npm.taobao.org/forwarded/download/forwarded-0.1.2.tgz", 158 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 159 | }, 160 | "fresh": { 161 | "version": "0.5.2", 162 | "resolved": "http://registry.npm.taobao.org/fresh/download/fresh-0.5.2.tgz", 163 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 164 | }, 165 | "http-errors": { 166 | "version": "1.7.2", 167 | "resolved": "http://registry.npm.taobao.org/http-errors/download/http-errors-1.7.2.tgz", 168 | "integrity": "sha1-T1ApzxMjnzEDblsuVSkrz7zIXI8=", 169 | "requires": { 170 | "depd": "~1.1.2", 171 | "inherits": "2.0.3", 172 | "setprototypeof": "1.1.1", 173 | "statuses": ">= 1.5.0 < 2", 174 | "toidentifier": "1.0.0" 175 | } 176 | }, 177 | "iconv-lite": { 178 | "version": "0.4.24", 179 | "resolved": "http://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.24.tgz", 180 | "integrity": "sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=", 181 | "requires": { 182 | "safer-buffer": ">= 2.1.2 < 3" 183 | } 184 | }, 185 | "inherits": { 186 | "version": "2.0.3", 187 | "resolved": "http://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz", 188 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 189 | }, 190 | "ipaddr.js": { 191 | "version": "1.9.0", 192 | "resolved": "http://registry.npm.taobao.org/ipaddr.js/download/ipaddr.js-1.9.0.tgz", 193 | "integrity": "sha1-N9905DCg5HVQ/lSi3v4w2KzZX2U=" 194 | }, 195 | "media-typer": { 196 | "version": "0.3.0", 197 | "resolved": "https://registry.npm.taobao.org/media-typer/download/media-typer-0.3.0.tgz", 198 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 199 | }, 200 | "merge-descriptors": { 201 | "version": "1.0.1", 202 | "resolved": "http://registry.npm.taobao.org/merge-descriptors/download/merge-descriptors-1.0.1.tgz", 203 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 204 | }, 205 | "methods": { 206 | "version": "1.1.2", 207 | "resolved": "http://registry.npm.taobao.org/methods/download/methods-1.1.2.tgz", 208 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 209 | }, 210 | "mime": { 211 | "version": "1.6.0", 212 | "resolved": "https://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz?cache=0&sync_timestamp=1560034758817&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime%2Fdownload%2Fmime-1.6.0.tgz", 213 | "integrity": "sha1-Ms2eXGRVO9WNGaVor0Uqz/BJgbE=" 214 | }, 215 | "mime-db": { 216 | "version": "1.40.0", 217 | "resolved": "http://registry.npm.taobao.org/mime-db/download/mime-db-1.40.0.tgz", 218 | "integrity": "sha1-plBX6ZjbCQ9zKmj2wnbTh9QSbDI=" 219 | }, 220 | "mime-types": { 221 | "version": "2.1.24", 222 | "resolved": "http://registry.npm.taobao.org/mime-types/download/mime-types-2.1.24.tgz", 223 | "integrity": "sha1-tvjQs+lR77d97eyhlM/20W9nb4E=", 224 | "requires": { 225 | "mime-db": "1.40.0" 226 | } 227 | }, 228 | "ms": { 229 | "version": "2.0.0", 230 | "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.0.0.tgz", 231 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 232 | }, 233 | "negotiator": { 234 | "version": "0.6.2", 235 | "resolved": "https://registry.npm.taobao.org/negotiator/download/negotiator-0.6.2.tgz", 236 | "integrity": "sha1-/qz3zPUlp3rpY0Q2pkiD/+yjRvs=" 237 | }, 238 | "on-finished": { 239 | "version": "2.3.0", 240 | "resolved": "http://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz", 241 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 242 | "requires": { 243 | "ee-first": "1.1.1" 244 | } 245 | }, 246 | "parseurl": { 247 | "version": "1.3.3", 248 | "resolved": "http://registry.npm.taobao.org/parseurl/download/parseurl-1.3.3.tgz", 249 | "integrity": "sha1-naGee+6NEt/wUT7Vt2lXeTvC6NQ=" 250 | }, 251 | "path-to-regexp": { 252 | "version": "0.1.7", 253 | "resolved": "http://registry.npm.taobao.org/path-to-regexp/download/path-to-regexp-0.1.7.tgz", 254 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 255 | }, 256 | "proxy-addr": { 257 | "version": "2.0.5", 258 | "resolved": "http://registry.npm.taobao.org/proxy-addr/download/proxy-addr-2.0.5.tgz", 259 | "integrity": "sha1-NMvWSi2B9LH9IedvnwbIpFKZ7jQ=", 260 | "requires": { 261 | "forwarded": "~0.1.2", 262 | "ipaddr.js": "1.9.0" 263 | } 264 | }, 265 | "qs": { 266 | "version": "6.7.0", 267 | "resolved": "http://registry.npm.taobao.org/qs/download/qs-6.7.0.tgz", 268 | "integrity": "sha1-QdwaAV49WB8WIXdr4xr7KHapsbw=" 269 | }, 270 | "range-parser": { 271 | "version": "1.2.1", 272 | "resolved": "https://registry.npm.taobao.org/range-parser/download/range-parser-1.2.1.tgz", 273 | "integrity": "sha1-PPNwI9GZ4cJNGlW4SADC8+ZGgDE=" 274 | }, 275 | "raw-body": { 276 | "version": "2.4.0", 277 | "resolved": "http://registry.npm.taobao.org/raw-body/download/raw-body-2.4.0.tgz", 278 | "integrity": "sha1-oc5vucm8NWylLoklarWQWeE9AzI=", 279 | "requires": { 280 | "bytes": "3.1.0", 281 | "http-errors": "1.7.2", 282 | "iconv-lite": "0.4.24", 283 | "unpipe": "1.0.0" 284 | } 285 | }, 286 | "safe-buffer": { 287 | "version": "5.1.2", 288 | "resolved": "http://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz", 289 | "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" 290 | }, 291 | "safer-buffer": { 292 | "version": "2.1.2", 293 | "resolved": "http://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz", 294 | "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=" 295 | }, 296 | "send": { 297 | "version": "0.17.1", 298 | "resolved": "https://registry.npm.taobao.org/send/download/send-0.17.1.tgz", 299 | "integrity": "sha1-wdiwWfeQD3Rm3Uk4vcROEd2zdsg=", 300 | "requires": { 301 | "debug": "2.6.9", 302 | "depd": "~1.1.2", 303 | "destroy": "~1.0.4", 304 | "encodeurl": "~1.0.2", 305 | "escape-html": "~1.0.3", 306 | "etag": "~1.8.1", 307 | "fresh": "0.5.2", 308 | "http-errors": "~1.7.2", 309 | "mime": "1.6.0", 310 | "ms": "2.1.1", 311 | "on-finished": "~2.3.0", 312 | "range-parser": "~1.2.1", 313 | "statuses": "~1.5.0" 314 | }, 315 | "dependencies": { 316 | "ms": { 317 | "version": "2.1.1", 318 | "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.1.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.1.tgz", 319 | "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=" 320 | } 321 | } 322 | }, 323 | "serve-static": { 324 | "version": "1.14.1", 325 | "resolved": "https://registry.npm.taobao.org/serve-static/download/serve-static-1.14.1.tgz", 326 | "integrity": "sha1-Zm5jbcTwEPfvKZcKiKZ0MgiYsvk=", 327 | "requires": { 328 | "encodeurl": "~1.0.2", 329 | "escape-html": "~1.0.3", 330 | "parseurl": "~1.3.3", 331 | "send": "0.17.1" 332 | } 333 | }, 334 | "setprototypeof": { 335 | "version": "1.1.1", 336 | "resolved": "http://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.1.1.tgz", 337 | "integrity": "sha1-fpWsskqpL1iF4KvvW6ExMw1K5oM=" 338 | }, 339 | "statuses": { 340 | "version": "1.5.0", 341 | "resolved": "http://registry.npm.taobao.org/statuses/download/statuses-1.5.0.tgz", 342 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 343 | }, 344 | "toidentifier": { 345 | "version": "1.0.0", 346 | "resolved": "http://registry.npm.taobao.org/toidentifier/download/toidentifier-1.0.0.tgz", 347 | "integrity": "sha1-fhvjRw8ed5SLxD2Uo8j013UrpVM=" 348 | }, 349 | "type-is": { 350 | "version": "1.6.18", 351 | "resolved": "https://registry.npm.taobao.org/type-is/download/type-is-1.6.18.tgz", 352 | "integrity": "sha1-TlUs0F3wlGfcvE73Od6J8s83wTE=", 353 | "requires": { 354 | "media-typer": "0.3.0", 355 | "mime-types": "~2.1.24" 356 | } 357 | }, 358 | "unpipe": { 359 | "version": "1.0.0", 360 | "resolved": "http://registry.npm.taobao.org/unpipe/download/unpipe-1.0.0.tgz", 361 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 362 | }, 363 | "utils-merge": { 364 | "version": "1.0.1", 365 | "resolved": "http://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz", 366 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 367 | }, 368 | "vary": { 369 | "version": "1.1.2", 370 | "resolved": "http://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz", 371 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 372 | } 373 | } 374 | } 375 | --------------------------------------------------------------------------------