├── web-client ├── src │ ├── store │ │ ├── getters.js │ │ ├── index.js │ │ ├── state.js │ │ ├── mutation-types.js │ │ ├── mutations.js │ │ └── actions.js │ ├── common │ │ ├── img │ │ │ ├── title.ico │ │ │ ├── loading.gif │ │ │ ├── shopping.png │ │ │ └── logo-round.png │ │ ├── fonts │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ └── stylus │ │ │ └── mixins.styl │ ├── pages │ │ ├── Home │ │ │ ├── img │ │ │ │ ├── close.jpg │ │ │ │ ├── common.png │ │ │ │ ├── con_box.png │ │ │ │ ├── footer.png │ │ │ │ ├── meg_top.png │ │ │ │ ├── pro11.jpg │ │ │ │ ├── pro12.jpg │ │ │ │ ├── pro13.jpg │ │ │ │ ├── pro14.jpg │ │ │ │ ├── pro15.jpg │ │ │ │ ├── pro16.jpg │ │ │ │ ├── pro21.jpg │ │ │ │ ├── pro22.jpg │ │ │ │ ├── pro23.jpg │ │ │ │ ├── pro31.jpg │ │ │ │ ├── pro32.jpg │ │ │ │ ├── pro33.jpg │ │ │ │ ├── pro41.jpg │ │ │ │ ├── pro42.jpg │ │ │ │ ├── pro43.jpg │ │ │ │ ├── pro51.jpg │ │ │ │ ├── pro52.jpg │ │ │ │ ├── pro53.jpg │ │ │ │ ├── pro_go.png │ │ │ │ ├── search.png │ │ │ │ ├── title.ico │ │ │ │ ├── con_img1.jpg │ │ │ │ ├── con_img2.jpg │ │ │ │ ├── con_img3.jpg │ │ │ │ ├── con_img4.jpg │ │ │ │ ├── no_login.jpg │ │ │ │ ├── logo-round.png │ │ │ │ ├── meg_showcar.png │ │ │ │ ├── small_logo.png │ │ │ │ └── pro_detail_11.jpg │ │ │ ├── children │ │ │ │ └── DrawerSection │ │ │ │ │ ├── children │ │ │ │ │ └── DrawerItem.vue │ │ │ │ │ └── DrawerSection.vue │ │ │ └── Home.vue │ │ ├── Me │ │ │ ├── images │ │ │ │ └── no_login.jpg │ │ │ ├── Children │ │ │ │ ├── Sales.vue │ │ │ │ ├── EditPwd.vue │ │ │ │ ├── Profile.vue │ │ │ │ ├── EditPhone.vue │ │ │ │ └── Update.vue │ │ │ └── Me.vue │ │ ├── Admin │ │ │ ├── images │ │ │ │ └── no_login.jpg │ │ │ ├── Children │ │ │ │ ├── AdminSales.vue │ │ │ │ ├── AdminUsers.vue │ │ │ │ ├── AdminUpdate.vue │ │ │ │ ├── AdminGoods.vue │ │ │ │ └── AddGoods.vue │ │ │ └── Admin.vue │ │ ├── Login │ │ │ └── images │ │ │ │ ├── hide_pwd.png │ │ │ │ ├── show_pwd.png │ │ │ │ ├── logo-round.png │ │ │ │ └── captcha.svg │ │ ├── AdminLogin │ │ │ ├── images │ │ │ │ ├── hide_pwd.png │ │ │ │ ├── show_pwd.png │ │ │ │ ├── logo-round.png │ │ │ │ └── captcha.svg │ │ │ └── AdminLogin.vue │ │ ├── SearchDetail │ │ │ └── SearchDetail.vue │ │ ├── Search │ │ │ └── Search.vue │ │ ├── ShopCar │ │ │ └── ShopCar.vue │ │ └── Goods │ │ │ └── Goods.vue │ ├── main.js │ ├── App.vue │ ├── api │ │ ├── ajax.js │ │ └── index.js │ ├── config │ │ ├── filters.js │ │ └── utils.js │ ├── components │ │ ├── ProductItem │ │ │ └── ProductItem.vue │ │ ├── HeaderSearch │ │ │ └── HeaderSearch.vue │ │ └── HeaderTop │ │ │ └── HeaderTop.vue │ └── router │ │ └── index.js ├── build │ ├── logo.png │ ├── vue-loader.conf.js │ ├── build.js │ ├── check-versions.js │ ├── webpack.base.conf.js │ ├── utils.js │ ├── webpack.dev.conf.js │ └── webpack.prod.conf.js ├── config │ ├── prod.env.js │ ├── dev.env.js │ └── index.js ├── .editorconfig ├── .gitignore ├── .babelrc ├── .postcssrc.js ├── index.html └── package.json ├── web-server ├── main.js ├── public │ ├── images │ │ └── home │ │ │ ├── s1.png │ │ │ ├── s2.png │ │ │ ├── s3.png │ │ │ ├── s4.png │ │ │ ├── s5.png │ │ │ ├── s6.png │ │ │ ├── s7.png │ │ │ ├── s8.png │ │ │ └── s9.png │ └── avatar_uploads │ │ ├── avatar_default.jpg │ │ └── upload_5a67435fbb680332a6a3aa62014f5aa2.jpg ├── views │ ├── error.ejs │ └── index.ejs ├── .babelrc ├── db │ └── db.js ├── src │ ├── config.js │ └── app.js ├── package.json └── util │ └── sms_util.js ├── .gitignore └── README.md /web-client/src/store/getters.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /web-server/main.js: -------------------------------------------------------------------------------- 1 | // 入口文件 2 | 3 | require('babel-register'); 4 | require('./src/app'); -------------------------------------------------------------------------------- /web-client/build/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/build/logo.png -------------------------------------------------------------------------------- /web-client/config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /web-client/src/common/img/title.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/common/img/title.ico -------------------------------------------------------------------------------- /web-server/public/images/home/s1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-server/public/images/home/s1.png -------------------------------------------------------------------------------- /web-server/public/images/home/s2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-server/public/images/home/s2.png -------------------------------------------------------------------------------- /web-server/public/images/home/s3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-server/public/images/home/s3.png -------------------------------------------------------------------------------- /web-server/public/images/home/s4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-server/public/images/home/s4.png -------------------------------------------------------------------------------- /web-server/public/images/home/s5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-server/public/images/home/s5.png -------------------------------------------------------------------------------- /web-server/public/images/home/s6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-server/public/images/home/s6.png -------------------------------------------------------------------------------- /web-server/public/images/home/s7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-server/public/images/home/s7.png -------------------------------------------------------------------------------- /web-server/public/images/home/s8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-server/public/images/home/s8.png -------------------------------------------------------------------------------- /web-server/public/images/home/s9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-server/public/images/home/s9.png -------------------------------------------------------------------------------- /web-server/views/error.ejs: -------------------------------------------------------------------------------- 1 |

<%= message %>

2 |

<%= error.status %>

3 |
<%= error.stack %>
4 | -------------------------------------------------------------------------------- /web-client/src/common/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/common/img/loading.gif -------------------------------------------------------------------------------- /web-client/src/common/img/shopping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/common/img/shopping.png -------------------------------------------------------------------------------- /web-client/src/common/img/logo-round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/common/img/logo-round.png -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/close.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/close.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/common.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/common.png -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/con_box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/con_box.png -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/footer.png -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/meg_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/meg_top.png -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro11.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro12.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro13.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro14.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro15.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro16.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro21.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro22.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro23.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro31.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro31.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro32.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro33.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro33.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro41.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro41.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro42.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro42.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro43.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro43.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro51.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro51.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro52.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro52.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro53.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro53.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro_go.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro_go.png -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/search.png -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/title.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/title.ico -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/con_img1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/con_img1.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/con_img2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/con_img2.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/con_img3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/con_img3.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/con_img4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/con_img4.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/no_login.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/no_login.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Me/images/no_login.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Me/images/no_login.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Admin/images/no_login.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Admin/images/no_login.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/logo-round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/logo-round.png -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/meg_showcar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/meg_showcar.png -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/small_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/small_logo.png -------------------------------------------------------------------------------- /web-client/src/pages/Login/images/hide_pwd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Login/images/hide_pwd.png -------------------------------------------------------------------------------- /web-client/src/pages/Login/images/show_pwd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Login/images/show_pwd.png -------------------------------------------------------------------------------- /web-client/src/pages/Home/img/pro_detail_11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Home/img/pro_detail_11.jpg -------------------------------------------------------------------------------- /web-client/src/pages/Login/images/logo-round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/Login/images/logo-round.png -------------------------------------------------------------------------------- /web-server/.babelrc: -------------------------------------------------------------------------------- 1 | // .babelrc 转换高阶语法 2 | { 3 | "presets": [ 4 | "env" 5 | ], 6 | "plugins": [ 7 | "transform-runtime" 8 | ] 9 | } -------------------------------------------------------------------------------- /web-client/src/pages/AdminLogin/images/hide_pwd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/AdminLogin/images/hide_pwd.png -------------------------------------------------------------------------------- /web-client/src/pages/AdminLogin/images/show_pwd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/AdminLogin/images/show_pwd.png -------------------------------------------------------------------------------- /web-server/public/avatar_uploads/avatar_default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-server/public/avatar_uploads/avatar_default.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | */node_modules/ 3 | 4 | # Editor directories and files 5 | .idea 6 | .vscode 7 | *.suo 8 | *.ntvs* 9 | *.njsproj 10 | *.sln -------------------------------------------------------------------------------- /web-client/src/pages/AdminLogin/images/logo-round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/pages/AdminLogin/images/logo-round.png -------------------------------------------------------------------------------- /web-client/src/common/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/common/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /web-client/src/common/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/common/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /web-client/src/common/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/common/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /web-client/src/common/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-client/src/common/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /web-server/public/avatar_uploads/upload_5a67435fbb680332a6a3aa62014f5aa2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuiErro/WebShop/HEAD/web-server/public/avatar_uploads/upload_5a67435fbb680332a6a3aa62014f5aa2.jpg -------------------------------------------------------------------------------- /web-client/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 | -------------------------------------------------------------------------------- /web-client/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /web-server/views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= title %> 5 | 6 | 7 |

<%= title %>

8 |

Welcome to <%= title %>

9 | 10 | 11 | -------------------------------------------------------------------------------- /web-client/src/pages/Admin/Children/AdminSales.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /web-client/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | /dist/ 4 | /static/ 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | 9 | # Editor directories and files 10 | .idea 11 | .vscode 12 | *.suo 13 | *.ntvs* 14 | *.njsproj 15 | *.sln 16 | .gitignore -------------------------------------------------------------------------------- /web-client/.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 | -------------------------------------------------------------------------------- /web-client/.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 | -------------------------------------------------------------------------------- /web-client/src/pages/Me/Children/Sales.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 19 | -------------------------------------------------------------------------------- /web-server/db/db.js: -------------------------------------------------------------------------------- 1 | import mysql from 'mysql' 2 | import config from '../src/config' 3 | 4 | const conn = mysql.createConnection({ 5 | host: config.host, // 数据库的地址 6 | user: config.user, // 账号 7 | password: config.password, // 密码 8 | database: config.database, // 数据库名称 9 | multipleStatements: true, // 允许多条sql同时查询 10 | }); 11 | 12 | conn.connect(); 13 | 14 | export default conn; -------------------------------------------------------------------------------- /web-client/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | import state from './state' 5 | import mutations from './mutations' 6 | import actions from './actions' 7 | import getters from './getters' 8 | 9 | // 1. 使用vuex 10 | Vue.use(Vuex); 11 | 12 | // 2. 对外输出vuex的store对象 13 | export default new Vuex.Store({ 14 | state, 15 | mutations, 16 | actions, 17 | getters 18 | }) 19 | -------------------------------------------------------------------------------- /web-client/src/store/state.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // 首页轮播图 3 | homecasual: [], 4 | // 分类数组 5 | categoryList: [], 6 | // 首页的商品列表 7 | homeshoplist: [], 8 | // 推荐的商品列表 9 | recommendshoplist: [], 10 | // 商品详细信息 11 | goodsDetail: [], 12 | // 商品评论 13 | goodsComment: [], 14 | // 用户数据 15 | userInfo: {}, 16 | // 购物车数据 17 | cartgoods: [], 18 | // 搜索结果 19 | searchresults: [] 20 | } 21 | -------------------------------------------------------------------------------- /web-server/src/config.js: -------------------------------------------------------------------------------- 1 | import {join} from 'path' 2 | 3 | export default { 4 | viewsPath: join(__dirname, '../views'), 5 | publicPath: join(__dirname, '../public'), 6 | uploadsGoodsPath: join(__dirname, '../public/uploads'), // 上传的图片所放置的文件夹 7 | uploadsAvatarPath: join(__dirname, '../public/avatar_uploads'), // 上传的头像所放置的文件夹 8 | port: parseInt(process.env.PORT, 10) || 3000, // 端口号 9 | 10 | host: '127.0.0.1', // 数据库的地址 11 | user: 'root', // 账号 12 | password: '****', // 密码 13 | database: 'web_shop', // 数据库名称 14 | 15 | secret: '123456', // session 16 | maxAge: 1000 * 60 * 60 * 6 // session保存6个小时 17 | } -------------------------------------------------------------------------------- /web-client/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App' 3 | import store from './store' 4 | 5 | // 引入路由器 6 | import router from './router/index' 7 | 8 | import ElementUI from 'element-ui'; 9 | import 'element-ui/lib/theme-chalk/index.css'; 10 | Vue.use(ElementUI); 11 | 12 | // 配置图片懒加载 13 | import VueLazyload from 'vue-lazyload' 14 | import loading from './common/img/loading.gif' 15 | Vue.use(VueLazyload, { 16 | loading 17 | }); 18 | 19 | // 引入全局过滤器 20 | import '@/config/filters' 21 | 22 | import 'bootstrap/dist/css/bootstrap.css' 23 | 24 | new Vue({ 25 | el:'#app', 26 | router, 27 | store, 28 | render: h=>h(App) 29 | }); 30 | -------------------------------------------------------------------------------- /web-client/build/vue-loader.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const config = require('../config') 4 | const isProduction = process.env.NODE_ENV === 'production' 5 | const sourceMapEnabled = isProduction 6 | ? config.build.productionSourceMap 7 | : config.dev.cssSourceMap 8 | 9 | module.exports = { 10 | loaders: utils.cssLoaders({ 11 | sourceMap: sourceMapEnabled, 12 | extract: isProduction 13 | }), 14 | cssSourceMap: sourceMapEnabled, 15 | cacheBusting: config.dev.cacheBusting, 16 | transformToRequire: { 17 | video: ['src', 'poster'], 18 | source: 'src', 19 | img: 'src', 20 | image: 'xlink:href' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /web-client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 西二商城 7 | 8 | 9 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /web-client/src/store/mutation-types.js: -------------------------------------------------------------------------------- 1 | export const HOME_CASUAL = 'home_casual'; // 首页轮播图 2 | export const CATEGORY_LIST = 'category_list'; // 首页轮播图 3 | export const HOME_SHOP_LIST = 'home_shop_list'; // 首页商品数据 4 | export const RECOMMEND_SHOP_LIST = 'recommend_shop_list'; // 推荐商品数据 5 | export const GOODS_DETAIL = 'goods_detail'; // 获取商品详细信息 6 | export const GOODS_COMMENT = 'goods_comment'; // 获取商品评论 7 | export const USER_INFO = 'user_info'; // 同步用户信息 8 | export const RESET_USER_INFO = 'reset_user_info'; // 清空用户信息 9 | export const CART_GOODS_LIST = 'cart_goods_list'; // 购物车数据 10 | export const CHANGE_GOODS_COUNT = 'change_goods_count'; // 改变购物车单个物品的数量 11 | export const SELECTED_ALL_GOODS = 'selected_all_goods'; // 是否选中所有商品 12 | export const SELECTED_SINGLE_GOODS = 'selected_single_goods'; // 是否选中单个商品 13 | export const DEL_SINGLE_GOODS = 'del_single_goods'; // 是否删除单个商品 14 | export const DEL_ALL_GOODS = 'del_all_goods'; // 是否删除所有商品 15 | export const SEARCH_KEYWORDS = 'search_goods'; // 关键词搜索 16 | 17 | -------------------------------------------------------------------------------- /web-client/src/App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 33 | 34 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /web-client/src/api/ajax.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | export default function ajax(url = '', params = {}, type = 'GET') { 4 | // 1. 定义promise对象 5 | let promise; 6 | return new Promise((resolve, reject)=>{ 7 | // 2. 判断请求的方式 8 | if('GET' === type){ 9 | // 2.1 拼接请求字符串 10 | let paramsStr = ''; 11 | Object.keys(params).forEach(key =>{ 12 | paramsStr += key + '=' + params[key] + '&' 13 | }); 14 | // 2.2 过滤最后的& 15 | if(paramsStr !== ''){ 16 | paramsStr = paramsStr.substr(0, paramsStr.lastIndexOf('&')) 17 | } 18 | // 2.3 完整路径 19 | url += '?' + paramsStr; 20 | // 2.4 发送get请求 21 | promise = axios.get(url) 22 | }else if('POST' === type){ 23 | promise = axios.post(url, params) 24 | } 25 | // 3. 返回请求的结果 26 | promise.then((response)=>{ 27 | resolve(response.data) 28 | }).catch(error => { 29 | reject(error) 30 | }) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /web-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "dev": "set PORT=3000 && nodemon main.js", 9 | "build": "babel src -d dist", 10 | "start": "node dist/app.js" 11 | }, 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "blueimp-md5": "^2.12.0", 16 | "connect-mongo": "^3.1.2", 17 | "cookie-parser": "^1.4.4", 18 | "cors": "^2.8.5", 19 | "ejs": "^2.7.1", 20 | "express": "^4.17.1", 21 | "express-session": "^1.17.0", 22 | "formidable": "^1.2.1", 23 | "http-errors": "^1.7.3", 24 | "js-base64": "^2.5.1", 25 | "moment": "^2.24.0", 26 | "mongoose": "^5.7.7", 27 | "morgan": "^1.9.1", 28 | "mysql": "^2.17.1", 29 | "request": "^2.88.0", 30 | "svg-captcha": "^1.4.0" 31 | }, 32 | "devDependencies": { 33 | "babel-cli": "^6.26.0", 34 | "babel-plugin-transform-runtime": "^6.23.0", 35 | "babel-preset-env": "^1.7.0", 36 | "babel-register": "^6.26.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /web-client/src/config/filters.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | // 人民币过滤器 4 | Vue.filter('moneyFormat', (value)=>{ 5 | return '¥' + Number(value).toFixed(2); 6 | }); 7 | 8 | // 电话号码过滤器 9 | Vue.filter('phoneFormat', (phone)=>{ 10 | // 1. 转成数组 11 | let phoneArr = [...phone]; 12 | // 2. 遍历 13 | let str = ''; 14 | phoneArr.forEach((item, index)=>{ 15 | if(index === 3 || index === 4 ||index === 5 ||index === 6 ){ 16 | str += "*"; 17 | }else { 18 | str += item; 19 | } 20 | }); 21 | return str; 22 | }); 23 | 24 | // 账号过滤器 25 | Vue.filter('nameFormat', (name)=>{ 26 | // 1. 转成数组 27 | let nameArr = [...name]; 28 | let len = nameArr.length; 29 | // 2. 遍历 30 | let str = ''; 31 | // 判断账号是否是手机号 32 | if(/^[1][3,4,5,7,8][0-9]{9}$/.test(name)){ 33 | nameArr.forEach((item, index)=>{ 34 | if(index === 3 || index === 4 ||index === 5 ||index === 6 ){ 35 | str += "*"; 36 | }else { 37 | str += item; 38 | } 39 | }); 40 | }else{ 41 | nameArr.forEach((item, index)=>{ 42 | if(index != 0 && index != len-1 ){ 43 | str += "*"; 44 | }else { 45 | str += item; 46 | } 47 | }); 48 | } 49 | return str; 50 | }); 51 | -------------------------------------------------------------------------------- /web-client/src/pages/Admin/Children/AdminUsers.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 51 | 52 | 55 | -------------------------------------------------------------------------------- /web-client/build/build.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('./check-versions')() 3 | 4 | process.env.NODE_ENV = 'production' 5 | 6 | const ora = require('ora') 7 | const rm = require('rimraf') 8 | const path = require('path') 9 | const chalk = require('chalk') 10 | const webpack = require('webpack') 11 | const config = require('../config') 12 | const webpackConfig = require('./webpack.prod.conf') 13 | 14 | const spinner = ora('building for production...') 15 | spinner.start() 16 | 17 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { 18 | if (err) throw err 19 | webpack(webpackConfig, (err, stats) => { 20 | spinner.stop() 21 | if (err) throw err 22 | process.stdout.write(stats.toString({ 23 | colors: true, 24 | modules: false, 25 | children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. 26 | chunks: false, 27 | chunkModules: false 28 | }) + '\n\n') 29 | 30 | if (stats.hasErrors()) { 31 | console.log(chalk.red(' Build failed with errors.\n')) 32 | process.exit(1) 33 | } 34 | 35 | console.log(chalk.cyan(' Build complete.\n')) 36 | console.log(chalk.yellow( 37 | ' Tip: built files are meant to be served over an HTTP server.\n' + 38 | ' Opening index.html over file:// won\'t work.\n' 39 | )) 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /web-client/build/check-versions.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const chalk = require('chalk') 3 | const semver = require('semver') 4 | const packageConfig = require('../package.json') 5 | const shell = require('shelljs') 6 | 7 | function exec (cmd) { 8 | return require('child_process').execSync(cmd).toString().trim() 9 | } 10 | 11 | const versionRequirements = [ 12 | { 13 | name: 'node', 14 | currentVersion: semver.clean(process.version), 15 | versionRequirement: packageConfig.engines.node 16 | } 17 | ] 18 | 19 | if (shell.which('npm')) { 20 | versionRequirements.push({ 21 | name: 'npm', 22 | currentVersion: exec('npm --version'), 23 | versionRequirement: packageConfig.engines.npm 24 | }) 25 | } 26 | 27 | module.exports = function () { 28 | const warnings = [] 29 | 30 | for (let i = 0; i < versionRequirements.length; i++) { 31 | const mod = versionRequirements[i] 32 | 33 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 34 | warnings.push(mod.name + ': ' + 35 | chalk.red(mod.currentVersion) + ' should be ' + 36 | chalk.green(mod.versionRequirement) 37 | ) 38 | } 39 | } 40 | 41 | if (warnings.length) { 42 | console.log('') 43 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 44 | console.log() 45 | 46 | for (let i = 0; i < warnings.length; i++) { 47 | const warning = warnings[i] 48 | console.log(' ' + warning) 49 | } 50 | 51 | console.log() 52 | process.exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # webshop 2 | 3 | > A vue & node Project PC端网上商城 4 | 5 | #### 架构 6 | 7 | - 前端:vue全家桶 8 | - 后端: node:express框架 9 | - 数据库:MySQL 10 | 11 | #### 基本功能 12 | 13 | ##### 普通用户 14 | 15 | - 注册、登录(图形验证码) 16 | - 定位 (腾讯地图定位功能)、自主选择所在城市 17 | - 商品 18 | - 分类 19 | - 简单展示商品 20 | - 查看商品详情 21 | - 商品评论 22 | - 分页功能 23 | - 购物车功能 24 | - 加入购物车 25 | - 购物车商品数量增减 26 | - 清空购物车 27 | - 商品结算 28 | - 多关键词模糊搜索商品 (关键词需为商品名称) 29 | - 用户个人中心 30 | + 修改用户信息 (头像、昵称、简介...) 31 | + 修改手机号 32 | + 修改密码 33 | 34 | ##### 管理员 35 | 36 | - 登录(固定账号密码:admin) 37 | - 查看所有用户 38 | - 查看数据库商品信息 39 | - 商品上架(添加商品) 40 | - 删除/修改商品 41 | - 分页功能 42 | 43 | 44 | 45 | #### 运行 46 | 47 | > 项目后端服务器是基于node、MySQL开发,运行前请确认系统已安装相关应用 48 | 49 | - `git clone` 50 | 51 | ##### 服务器端 52 | 53 | + 确认是否全局安装`nodemon` 54 | 55 | + 若无安装: 56 | 57 | + `npm i -g nodemon` 58 | 59 | + 或修改`web-server/package.json` 60 | 61 | ```javascript 62 | "scripts": { 63 | "test": "echo \"Error: no test specified\" && exit 1", 64 | "dev": "set PORT=3000 && node main.js", 65 | "build": "babel src -d dist", 66 | "start": "node dist/app.js" 67 | }, 68 | ``` 69 | 70 | + `cd web-server` 71 | + `mkdir -p public/uploads` 新建uploads文件夹保存商品图片 72 | + 将`web-server`文件夹下的`webshop.sql`导入`MySQL`数据库中 73 | + `npm install` 安装依赖 74 | + 修改`web-server/src/config.js`文件,此文件记录项目的全局变量,在文件中找到`password`、`database`、`user`属性,根据你的数据库信息修改它们的值 75 | + `npm run dev` 在本地运行,启动服务器 76 | 77 | ##### 客户端 78 | 79 | - `cd web-client` 80 | - `mkdir static` 新建static文件夹,防止报错 81 | - `npm install` 安装依赖 82 | - `npm run dev` 在本地运行 83 | - 接着就可以在`http://localhost:8080`下访问到该项目 84 | -------------------------------------------------------------------------------- /web-server/src/app.js: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import config from './config' 3 | import indexRouter from '../routes/index' 4 | 5 | import createError from 'http-errors' 6 | import cookieParser from 'cookie-parser' 7 | import logger from 'morgan' 8 | import session from 'express-session' 9 | 10 | const app = express(); 11 | 12 | // 使用cors解决跨域问题 13 | import cors from 'cors' 14 | app.use(cors()); 15 | 16 | app.use(cookieParser()); 17 | 18 | // 使用express-session保存用户登录状况 19 | app.use(session({ 20 | secret: config.secret, 21 | resave: false, 22 | saveUninitialized: true, 23 | cookie: { 24 | maxAge: config.maxAge, 25 | }, 26 | })); 27 | 28 | 29 | // 请求体 30 | import bodyParser from 'body-parser' 31 | app.use(bodyParser.urlencoded({extended: false})); 32 | 33 | // view engine setup 34 | app.set('views', config.viewsPath); 35 | app.set('view engine', 'ejs'); 36 | 37 | app.use(logger('dev')); 38 | app.use(express.json()); 39 | app.use(express.urlencoded({ extended: false })); 40 | app.use(express.static(config.publicPath)); 41 | 42 | app.use('/', indexRouter); 43 | 44 | // catch 404 and forward to error handler 45 | app.use((req, res, next) => { 46 | next(createError(404)); 47 | }); 48 | 49 | // error handler 50 | app.use((err, req, res, next) => { 51 | // set locals, only providing error in development 52 | res.locals.message = err.message; 53 | res.locals.error = req.app.get('env') === 'development' ? err : {}; 54 | 55 | // render the error page 56 | res.status(err.status || 500); 57 | res.render('error'); 58 | }); 59 | 60 | app.listen(config.port, ()=>{ 61 | console.log(`服务器已经启动, 端口是: ${config.port}`); 62 | }); 63 | 64 | export default app; 65 | -------------------------------------------------------------------------------- /web-client/src/common/stylus/mixins.styl: -------------------------------------------------------------------------------- 1 | // 通用样式类 2 | $bg = #F5F5F5 3 | 4 | body, div, span, header, footer, nav, section, aside, article, ul, dl, dt, dd, li, a, p, h1, h2, h3, h4,h5, h6, i, b, textarea, button, input, select, figure, figcaption 5 | padding 0 6 | margin 0 7 | list-style none 8 | font-style normal 9 | text-decoration none 10 | border none 11 | font-weight normal 12 | font-family "Microsoft Yahei" 13 | box-sizing border-box 14 | -webkit-tap-highlight-colortransparent 15 | -webkit-font-smoothing antialiased 16 | &:hover 17 | outline none 18 | 19 | // 1px上边线 20 | border-top-1px($color) 21 | position relative 22 | &::before 23 | content '' 24 | position absolute 25 | z-index 1000 26 | left 0 27 | top 0 28 | width 100% 29 | height 1px 30 | background-color $color 31 | 32 | // 1px下边线 33 | border-bottom-1px($color) 34 | position relative 35 | border none 36 | &::after 37 | content '' 38 | position absolute 39 | left 0 40 | bottom 0 41 | width 100% 42 | height 1px 43 | background-color $color 44 | transform scaleY(0.5) 45 | 46 | // 根据屏幕分辨率判断使用@2x图片或者@3x图片 47 | bg-image($url) 48 | background-image url($url + "@2x.png") 49 | @media (-webkit-min-device-pixel-ratio: 3), (min-device-pixel-ratio: 3) 50 | background-image url($url + "@3x.png") 51 | 52 | // 根据屏幕分辨率自动缩放1px边框线 53 | @media only screen and (-webkit-min-device-pixel-ratio: 2) 54 | .border-1px 55 | &::before 56 | transform scaleY(.5) 57 | 58 | @media only screen and (-webkit-min-device-pixel-ratio: 3) 59 | .border-1px 60 | &::before 61 | transform scaleY(.3) 62 | 63 | // 清除浮动 64 | clearFix 65 | *zoom 1 66 | &::after 67 | content '' 68 | display block 69 | clear both 70 | -------------------------------------------------------------------------------- /web-client/src/components/ProductItem/ProductItem.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 32 | 33 | 85 | -------------------------------------------------------------------------------- /web-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webshop", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "zoek ", 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.18.0", 14 | "bootstrap": "^4.3.1", 15 | "element-ui": "^2.12.0", 16 | "moment": "^2.22.2", 17 | "vue": "^2.5.2", 18 | "vue-router": "^3.0.1", 19 | "vuex": "^3.0.1" 20 | }, 21 | "devDependencies": { 22 | "autoprefixer": "^7.1.2", 23 | "babel-core": "^6.22.1", 24 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 25 | "babel-loader": "^7.1.1", 26 | "babel-plugin-component": "^1.1.1", 27 | "babel-plugin-syntax-jsx": "^6.18.0", 28 | "babel-plugin-transform-runtime": "^6.22.0", 29 | "babel-plugin-transform-vue-jsx": "^3.5.0", 30 | "babel-preset-env": "^1.3.2", 31 | "babel-preset-stage-2": "^6.22.0", 32 | "chalk": "^2.0.1", 33 | "copy-webpack-plugin": "^4.0.1", 34 | "css-loader": "^0.28.0", 35 | "extract-text-webpack-plugin": "^3.0.0", 36 | "file-loader": "^1.1.4", 37 | "friendly-errors-webpack-plugin": "^1.6.1", 38 | "html-webpack-plugin": "^2.30.1", 39 | "node-notifier": "^5.1.2", 40 | "optimize-css-assets-webpack-plugin": "^3.2.0", 41 | "ora": "^1.2.0", 42 | "portfinder": "^1.0.13", 43 | "postcss-import": "^11.0.0", 44 | "postcss-loader": "^2.0.8", 45 | "postcss-url": "^7.2.1", 46 | "rimraf": "^2.6.0", 47 | "semver": "^5.3.0", 48 | "shelljs": "^0.7.6", 49 | "stylus": "^0.54.5", 50 | "stylus-loader": "^3.0.2", 51 | "uglifyjs-webpack-plugin": "^1.1.1", 52 | "url-loader": "^0.5.8", 53 | "vue-lazyload": "^1.2.6", 54 | "vue-loader": "^13.3.0", 55 | "vue-style-loader": "^3.0.1", 56 | "vue-template-compiler": "^2.5.2", 57 | "webpack": "^3.6.0", 58 | "webpack-bundle-analyzer": "^2.9.0", 59 | "webpack-dev-server": "^2.9.1", 60 | "webpack-merge": "^4.1.0" 61 | }, 62 | "engines": { 63 | "node": ">= 6.0.0", 64 | "npm": ">= 3.0.0" 65 | }, 66 | "browserslist": [ 67 | "> 1%", 68 | "last 2 versions", 69 | "not ie <= 8" 70 | ] 71 | } 72 | -------------------------------------------------------------------------------- /web-server/util/sms_util.js: -------------------------------------------------------------------------------- 1 | import md5 from 'blueimp-md5' 2 | import moment from 'moment' 3 | import {Base64} from 'js-base64' 4 | import request from 'request' 5 | 6 | /*生成指定长度的随机数*/ 7 | function randomCode(length) { 8 | let chars = ['0','1','2','3','4','5','6','7','8','9']; 9 | let result = ""; 10 | for(let i = 0; i < length ; i ++) { 11 | let index = Math.ceil(Math.random()*9); 12 | result += chars[index]; 13 | } 14 | return result; 15 | } 16 | 17 | /*向指定号码发送指定验证码*/ 18 | function sendCode(phone, code, callback) { 19 | let ACCOUNT_SID = '************'; 20 | let AUTH_TOKEN = '************'; 21 | let Rest_URL = 'https://app.cloopen.com:8883'; 22 | let AppID = '************'; 23 | //1. 准备请求url 24 | /* 25 | 使用MD5加密(账户Id + 账户授权令牌 + 时间戳)。其中账户Id和账户授权令牌根据url的验证级别对应主账户。 26 | 时间戳是当前系统时间,格式"yyyyMMddHHmmss"。时间戳有效时间为24小时,如:20140416142030 27 | 2.SigParameter参数需要大写,如不能写成sig=abcdefg而应该写成sig=ABCDEFG 28 | */ 29 | let sigParameter = ''; 30 | let time = moment().format('YYYYMMDDHHmmss'); 31 | sigParameter = md5(ACCOUNT_SID+AUTH_TOKEN+time); 32 | let url = Rest_URL+'/2013-12-26/Accounts/'+ACCOUNT_SID+'/SMS/TemplateSMS?sig='+sigParameter; 33 | 34 | //2. 准备请求体 35 | let body = { 36 | to : phone, 37 | appId : AppID, 38 | templateId : '1', 39 | "datas":[code,"1"] 40 | } 41 | 42 | //3. 准备请求头 43 | /* 44 | a.使用Base64编码(账户Id + 冒号 + 时间戳)其中账户Id根据url的验证级别对应主账户 45 | b.冒号为英文冒号 46 | c.时间戳是当前系统时间,格式"yyyyMMddHHmmss",需与SigParameter中时间戳相同。 47 | */ 48 | let authorization = ACCOUNT_SID + ':' + time; 49 | authorization = Base64.encode(authorization); 50 | let headers = { 51 | 'Accept' :'application/json', 52 | 'Content-Type' :'application/json;charset=utf-8', 53 | 'Content-Length': JSON.stringify(body).length+'', 54 | 'Authorization' : authorization 55 | } 56 | 57 | //4. 发送请求, 并得到返回的结果, 调用callback 58 | request({ 59 | method : 'POST', 60 | url : url, 61 | headers : headers, 62 | body : body, 63 | json : true 64 | }, (error, response, body) => { 65 | console.log(error, response, body); 66 | callback(body.statusCode==='000000'); 67 | }); 68 | } 69 | 70 | export default { 71 | randomCode, 72 | sendCode 73 | } 74 | -------------------------------------------------------------------------------- /web-client/src/pages/Me/Children/EditPwd.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 57 | 58 | 88 | -------------------------------------------------------------------------------- /web-client/build/webpack.base.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const config = require('../config') 5 | const vueLoaderConfig = require('./vue-loader.conf') 6 | 7 | function resolve (dir) { 8 | return path.join(__dirname, '..', dir) 9 | } 10 | 11 | 12 | 13 | module.exports = { 14 | context: path.resolve(__dirname, '../'), 15 | entry: { 16 | app: './src/main.js' 17 | }, 18 | output: { 19 | path: config.build.assetsRoot, 20 | filename: '[name].js', 21 | publicPath: process.env.NODE_ENV === 'production' 22 | ? config.build.assetsPublicPath 23 | : config.dev.assetsPublicPath 24 | }, 25 | resolve: { 26 | extensions: ['.js', '.vue', '.json'], 27 | alias: { 28 | 'vue$': 'vue/dist/vue.esm.js', 29 | '@': resolve('src'), 30 | } 31 | }, 32 | module: { 33 | rules: [ 34 | { 35 | test: /\.vue$/, 36 | loader: 'vue-loader', 37 | options: vueLoaderConfig 38 | }, 39 | { 40 | test: /\.js$/, 41 | loader: 'babel-loader', 42 | include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] 43 | }, 44 | { 45 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 46 | loader: 'url-loader', 47 | options: { 48 | limit: 10000, 49 | name: utils.assetsPath('img/[name].[hash:7].[ext]') 50 | } 51 | }, 52 | { 53 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 54 | loader: 'url-loader', 55 | options: { 56 | limit: 10000, 57 | name: utils.assetsPath('media/[name].[hash:7].[ext]') 58 | } 59 | }, 60 | { 61 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 62 | loader: 'url-loader', 63 | options: { 64 | limit: 10000, 65 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]') 66 | } 67 | } 68 | ] 69 | }, 70 | node: { 71 | // prevent webpack from injecting useless setImmediate polyfill because Vue 72 | // source contains it (although only uses it if it's native). 73 | setImmediate: false, 74 | // prevent webpack from injecting mocks to Node native modules 75 | // that does not make sense for the client 76 | dgram: 'empty', 77 | fs: 'empty', 78 | net: 'empty', 79 | tls: 'empty', 80 | child_process: 'empty' 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /web-client/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 | '/api': { 15 | target: 'http://localhost:3000/', //源地址 16 | changeOrigin: true, //改变源 17 | pathRewrite: { 18 | '^/api': '' //路径重写 19 | } 20 | }, 21 | }, 22 | 23 | // Various Dev Server settings 24 | host: 'localhost', // can be overwritten by process.env.HOST 25 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined 26 | autoOpenBrowser: false, 27 | errorOverlay: true, 28 | notifyOnErrors: true, 29 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- 30 | 31 | 32 | /** 33 | * Source Maps 34 | */ 35 | 36 | // https://webpack.js.org/configuration/devtool/#development 37 | devtool: 'cheap-module-eval-source-map', 38 | 39 | // If you have problems debugging vue-files in devtools, 40 | // set this to false - it *may* help 41 | // https://vue-loader.vuejs.org/en/options.html#cachebusting 42 | cacheBusting: true, 43 | 44 | cssSourceMap: true 45 | }, 46 | 47 | build: { 48 | // Template for index.html 49 | index: path.resolve(__dirname, '../dist/index.html'), 50 | 51 | // Paths 52 | assetsRoot: path.resolve(__dirname, '../dist'), 53 | assetsSubDirectory: 'static', 54 | assetsPublicPath: '/', 55 | 56 | /** 57 | * Source Maps 58 | */ 59 | 60 | productionSourceMap: true, 61 | // https://webpack.js.org/configuration/devtool/#production 62 | devtool: '#source-map', 63 | 64 | // Gzip off by default as many popular static hosts such as 65 | // Surge or Netlify already gzip all static assets for you. 66 | // Before setting to `true`, make sure to: 67 | // npm install --save-dev compression-webpack-plugin 68 | productionGzip: false, 69 | productionGzipExtensions: ['js', 'css'], 70 | 71 | // Run the build command with an extra argument to 72 | // View the bundle analyzer report after build finishes: 73 | // `npm run build --report` 74 | // Set to `true` or `false` to always turn it on or off 75 | bundleAnalyzerReport: process.env.npm_config_report 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /web-client/src/store/mutations.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { 3 | HOME_CASUAL, 4 | CATEGORY_LIST, 5 | HOME_SHOP_LIST, 6 | RECOMMEND_SHOP_LIST, 7 | GOODS_DETAIL, 8 | GOODS_COMMENT, 9 | USER_INFO, 10 | RESET_USER_INFO, 11 | CART_GOODS_LIST, 12 | CHANGE_GOODS_COUNT, 13 | SELECTED_ALL_GOODS, 14 | SELECTED_SINGLE_GOODS, 15 | DEL_SINGLE_GOODS, 16 | DEL_ALL_GOODS, 17 | SEARCH_KEYWORDS 18 | } from './mutation-types' 19 | 20 | export default { 21 | [HOME_CASUAL](state, {homecasual}) { 22 | state.homecasual = homecasual; 23 | }, 24 | 25 | [CATEGORY_LIST](state,{categoryList}){ 26 | state.categoryList = categoryList; 27 | }, 28 | 29 | [HOME_SHOP_LIST](state, {homeshoplist}) { 30 | state.homeshoplist = state.homeshoplist.concat(homeshoplist); 31 | }, 32 | 33 | [RECOMMEND_SHOP_LIST](state, {recommendshoplist}) { 34 | state.recommendshoplist = recommendshoplist; 35 | }, 36 | 37 | [GOODS_DETAIL](state, {goodsDetail}) { 38 | state.goodsDetail = goodsDetail; 39 | }, 40 | 41 | [GOODS_COMMENT](state, {goodsComment}) { 42 | state.goodsComment = goodsComment; 43 | }, 44 | 45 | [USER_INFO](state, {userInfo}) { 46 | state.userInfo = userInfo; 47 | }, 48 | 49 | [RESET_USER_INFO](state) { 50 | state.userInfo = {}; 51 | }, 52 | 53 | [CART_GOODS_LIST](state, {cartgoods}) { 54 | state.cartgoods = cartgoods; 55 | }, 56 | 57 | [CHANGE_GOODS_COUNT](state, {goods,count}) { 58 | const index = state.cartgoods.indexOf(goods); 59 | state.cartgoods[index].buy_count = count; 60 | }, 61 | 62 | [SELECTED_ALL_GOODS](state, {isSelectedAll}){ 63 | state.cartgoods.forEach((goods)=>{ 64 | if(goods.checked){ 65 | // 该属性存在 66 | goods.checked = !isSelectedAll; 67 | }else{ 68 | Vue.set(goods, 'checked', !isSelectedAll); 69 | } 70 | }); 71 | }, 72 | 73 | [SELECTED_SINGLE_GOODS](state, {goods}){ 74 | const index = state.cartgoods.indexOf(goods); 75 | if(!goods.checked){ 76 | //Vue.set(goods, 'checked', true); 77 | state.cartgoods[index].checked = true; 78 | }else{ 79 | state.cartgoods[index].checked = !state.cartgoods[index].checked; 80 | } 81 | }, 82 | 83 | [DEL_SINGLE_GOODS](state, {goods}){ 84 | goods.buy_count = 0; 85 | const index = state.cartgoods.indexOf(goods); 86 | state.cartgoods.splice(index, 1); 87 | }, 88 | 89 | [DEL_ALL_GOODS](state){ 90 | state.cartgoods = []; 91 | }, 92 | 93 | [SEARCH_KEYWORDS](state, {searchresults}) { 94 | state.searchresults = searchresults; 95 | }, 96 | 97 | } 98 | -------------------------------------------------------------------------------- /web-client/src/pages/Me/Children/Profile.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 55 | 56 | 92 | 93 | -------------------------------------------------------------------------------- /web-client/build/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const config = require('../config') 4 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 5 | const packageConfig = require('../package.json') 6 | 7 | exports.assetsPath = function (_path) { 8 | const assetsSubDirectory = process.env.NODE_ENV === 'production' 9 | ? config.build.assetsSubDirectory 10 | : config.dev.assetsSubDirectory 11 | 12 | return path.posix.join(assetsSubDirectory, _path) 13 | } 14 | 15 | exports.cssLoaders = function (options) { 16 | options = options || {} 17 | 18 | const cssLoader = { 19 | loader: 'css-loader', 20 | options: { 21 | sourceMap: options.sourceMap 22 | } 23 | } 24 | 25 | const postcssLoader = { 26 | loader: 'postcss-loader', 27 | options: { 28 | sourceMap: options.sourceMap 29 | } 30 | } 31 | 32 | // generate loader string to be used with extract text plugin 33 | function generateLoaders (loader, loaderOptions) { 34 | const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] 35 | 36 | if (loader) { 37 | loaders.push({ 38 | loader: loader + '-loader', 39 | options: Object.assign({}, loaderOptions, { 40 | sourceMap: options.sourceMap 41 | }) 42 | }) 43 | } 44 | 45 | // Extract CSS when that option is specified 46 | // (which is the case during production build) 47 | if (options.extract) { 48 | return ExtractTextPlugin.extract({ 49 | use: loaders, 50 | fallback: 'vue-style-loader' 51 | }) 52 | } else { 53 | return ['vue-style-loader'].concat(loaders) 54 | } 55 | } 56 | 57 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html 58 | return { 59 | css: generateLoaders(), 60 | postcss: generateLoaders(), 61 | less: generateLoaders('less'), 62 | sass: generateLoaders('sass', { indentedSyntax: true }), 63 | scss: generateLoaders('sass'), 64 | stylus: generateLoaders('stylus'), 65 | styl: generateLoaders('stylus') 66 | } 67 | } 68 | 69 | // Generate loaders for standalone style files (outside of .vue) 70 | exports.styleLoaders = function (options) { 71 | const output = [] 72 | const loaders = exports.cssLoaders(options) 73 | 74 | for (const extension in loaders) { 75 | const loader = loaders[extension] 76 | output.push({ 77 | test: new RegExp('\\.' + extension + '$'), 78 | use: loader 79 | }) 80 | } 81 | 82 | return output 83 | } 84 | 85 | exports.createNotifierCallback = () => { 86 | const notifier = require('node-notifier') 87 | 88 | return (severity, errors) => { 89 | if (severity !== 'error') return 90 | 91 | const error = errors[0] 92 | const filename = error.file && error.file.split('!').pop() 93 | 94 | notifier.notify({ 95 | title: packageConfig.name, 96 | message: severity + ': ' + error.name, 97 | subtitle: filename || '', 98 | icon: path.join(__dirname, 'logo.png') 99 | }) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /web-client/src/router/index.js: -------------------------------------------------------------------------------- 1 | // 1. 引入对应模块 2 | import Vue from 'vue' 3 | import VueRouter from 'vue-router' 4 | 5 | const Home = ()=> import('./../pages/Home/Home'); 6 | const Login = ()=> import('./../pages/Login/Login'); 7 | const Search = ()=> import('./../pages/Search/Search'); 8 | const Goods = ()=> import('./../pages/Goods/Goods'); 9 | const Me = ()=> import('./../pages/Me/Me'); 10 | const ShopCar = ()=> import('./../pages/ShopCar/ShopCar'); 11 | const AdminLogin = ()=> import('./../pages/AdminLogin/AdminLogin'); 12 | const Admin = ()=> import('./../pages/Admin/Admin'); 13 | const SearchDetail = ()=> import('./../pages/SearchDetail/SearchDetail'); 14 | 15 | const Profile = ()=> import('./../pages/Me/Children/Profile'); 16 | const Update = ()=> import('./../pages/Me/Children/Update'); 17 | const EditPwd = ()=> import('./../pages/Me/Children/EditPwd'); 18 | const EditPhone = ()=> import('./../pages/Me/Children/EditPhone'); 19 | const Sales = ()=> import('./../pages/Me/Children/Sales'); 20 | const AdminSales = ()=> import('./../pages/Admin/Children/AdminSales'); 21 | const AddGoods = ()=> import('./../pages/Admin/Children/AddGoods'); 22 | const AdminGoods = ()=> import('./../pages/Admin/Children/AdminGoods'); 23 | const AdminUpdate = ()=> import('./../pages/Admin/Children/AdminUpdate'); 24 | const AdminUsers = ()=> import('./../pages/Admin/Children/AdminUsers'); 25 | 26 | // 2. 声明使用 27 | Vue.use(VueRouter); 28 | 29 | // 3. 输出路由对象 30 | export default new VueRouter({ 31 | // 3.1 配置一级路由 32 | routes: [ 33 | { 34 | path: '/home', 35 | component: Home, 36 | meta: {showHeaderTop: true, showHeaderSearch: true} 37 | }, 38 | { 39 | path: '/login', 40 | component: Login 41 | }, 42 | { 43 | path: '/search/:id/:pageNo', 44 | component: Search, 45 | meta: {showHeaderTop: true, showHeaderSearch: true} 46 | }, 47 | { 48 | path: '/goods/:id', 49 | component: Goods, 50 | meta: {showHeaderTop: true} 51 | }, 52 | { 53 | path: '/me', 54 | component: Me, 55 | children: [ 56 | {path: 'profile', component: Profile}, 57 | {path: 'update', component: Update}, 58 | {path: 'editpwd', component: EditPwd}, 59 | {path: 'editphone', component: EditPhone}, 60 | {path: 'sales', component: Sales}, 61 | {path: '/me',redirect: '/me/profile'} 62 | ], 63 | }, 64 | { 65 | path: '/shopcar', 66 | component: ShopCar, 67 | meta: {showHeaderTop: true} 68 | }, 69 | { 70 | path: '/adminlogin', 71 | component: AdminLogin 72 | }, 73 | { 74 | path: '/admin', 75 | component: Admin, 76 | children: [ 77 | {path: 'adminsales', component: AdminSales}, 78 | {path: 'adminusers', component: AdminUsers}, 79 | {path: 'addgoods', component: AddGoods}, 80 | {path: 'admingoods', component: AdminGoods}, 81 | {path: 'adminupdate', component: AdminUpdate}, 82 | {path: '/admin',redirect: '/admin/admingoods'} 83 | ], 84 | }, 85 | { 86 | path: '/searchdetail', 87 | component: SearchDetail, 88 | meta: {showHeaderTop: true, showHeaderSearch: true} 89 | }, 90 | { 91 | path: '/', 92 | redirect: '/home' 93 | }, 94 | ] 95 | }); 96 | -------------------------------------------------------------------------------- /web-client/build/webpack.dev.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const utils = require('./utils') 3 | const webpack = require('webpack') 4 | const config = require('../config') 5 | const merge = require('webpack-merge') 6 | const path = require('path') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') 11 | const portfinder = require('portfinder') 12 | 13 | const HOST = process.env.HOST 14 | const PORT = process.env.PORT && Number(process.env.PORT) 15 | 16 | const devWebpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) 19 | }, 20 | // cheap-module-eval-source-map is faster for development 21 | devtool: config.dev.devtool, 22 | 23 | // these devServer options should be customized in /config/index.js 24 | devServer: { 25 | clientLogLevel: 'warning', 26 | historyApiFallback: { 27 | rewrites: [ 28 | { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, 29 | ], 30 | }, 31 | hot: true, 32 | contentBase: false, // since we use CopyWebpackPlugin. 33 | compress: true, 34 | host: HOST || config.dev.host, 35 | port: PORT || config.dev.port, 36 | open: config.dev.autoOpenBrowser, 37 | overlay: config.dev.errorOverlay 38 | ? { warnings: false, errors: true } 39 | : false, 40 | publicPath: config.dev.assetsPublicPath, 41 | proxy: config.dev.proxyTable, 42 | quiet: true, // necessary for FriendlyErrorsPlugin 43 | watchOptions: { 44 | poll: config.dev.poll, 45 | } 46 | }, 47 | plugins: [ 48 | new webpack.DefinePlugin({ 49 | 'process.env': require('../config/dev.env') 50 | }), 51 | new webpack.HotModuleReplacementPlugin(), 52 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. 53 | new webpack.NoEmitOnErrorsPlugin(), 54 | // https://github.com/ampedandwired/html-webpack-plugin 55 | new HtmlWebpackPlugin({ 56 | filename: 'index.html', 57 | template: 'index.html', 58 | inject: true 59 | }), 60 | // copy custom static assets 61 | new CopyWebpackPlugin([ 62 | { 63 | from: path.resolve(__dirname, '../static'), 64 | to: config.dev.assetsSubDirectory, 65 | ignore: ['.*'] 66 | } 67 | ]) 68 | ] 69 | }) 70 | 71 | module.exports = new Promise((resolve, reject) => { 72 | portfinder.basePort = process.env.PORT || config.dev.port 73 | portfinder.getPort((err, port) => { 74 | if (err) { 75 | reject(err) 76 | } else { 77 | // publish the new Port, necessary for e2e tests 78 | process.env.PORT = port 79 | // add port to devServer config 80 | devWebpackConfig.devServer.port = port 81 | 82 | // Add FriendlyErrorsPlugin 83 | devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ 84 | compilationSuccessInfo: { 85 | messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], 86 | }, 87 | onErrors: config.dev.notifyOnErrors 88 | ? utils.createNotifierCallback() 89 | : undefined 90 | })) 91 | 92 | resolve(devWebpackConfig) 93 | } 94 | }) 95 | }) 96 | -------------------------------------------------------------------------------- /web-client/src/api/index.js: -------------------------------------------------------------------------------- 1 | import ajax from './ajax' 2 | 3 | // 1. 基础路径 4 | const BASE_URL = '/api'; // 防跨域 5 | // const BASE_URL = 'http://localhost:3000'; // 服务器真实地址 6 | 7 | // 2. 请求方法 8 | 9 | // 请求首页的轮播图 10 | export const getHomeCasual = () => ajax(BASE_URL + '/api/homecasual'); 11 | 12 | // 请求首页的分类数 13 | export const getCategory = () => ajax(BASE_URL + '/api/category'); 14 | 15 | // 请求首页的商品数据 16 | export const getHomeShopList = () => ajax(BASE_URL + '/api/homeshoplist'); 17 | 18 | // 请求推荐的商品数据 19 | export const getRecommendShopList = (params) => ajax(BASE_URL + '/api/recommendshoplist', params); 20 | 21 | // 请求所有商品 22 | export const getAllgoods = () => ajax(BASE_URL + '/api/allgoods'); 23 | 24 | // 请求商品详细数据 25 | export const getGoodsDetail = (params) => ajax(BASE_URL + '/api/goodsdetail', params); 26 | 27 | // 请求商品评价 28 | export const getGoodsComment = (params) => ajax(BASE_URL + '/api/goodscomment', params); 29 | 30 | // 发布评论 31 | export const postComment = (goods_id, comment_detail, comment_rating, user_id) => ajax(BASE_URL + '/api/postcomment', {goods_id, comment_detail, comment_rating, user_id}, 'POST'); 32 | 33 | // 请求短信验证码 34 | export const getPhoneCode = (phone) => ajax(BASE_URL + '/api/send_code', {phone}); 35 | 36 | // 手机验证码登录 37 | export const phoneCodeLogin = (phone, code) => ajax(BASE_URL + '/api/login_code', {phone, code}, 'POST'); 38 | 39 | // 用户名和密码登录 40 | export const pwdLogin = (name, pwd, captcha) => ajax(BASE_URL + '/api/login_pwd', {name, pwd, captcha}, 'POST'); 41 | 42 | // 获取登录的用户信息 43 | export const getUserInfo = (params) => ajax(BASE_URL + '/api/user_info',params); 44 | 45 | // 退出登录 46 | export const getLogout = () => ajax(BASE_URL + '/api/logout'); 47 | 48 | // 修改用户信息 49 | export const changeUserInfo = (params) => ajax(BASE_URL + '/api/change_user_msg', params, 'POST'); 50 | 51 | // 修改用户密码 52 | export const changeUserPwd = (id, oriPwd, newPwd) => ajax(BASE_URL + '/api/change_user_pwd', {id, oriPwd, newPwd}, 'POST'); 53 | 54 | // 修改用户手机 55 | export const changeUserPhone = (id, phone, code) => ajax(BASE_URL + '/api/change_user_phone', {id, phone, code}, 'POST'); 56 | 57 | // 加入购物车 58 | export const addGoodsToCart = (user_id, goods_id, goods_name, thumb_url, price,buy_count, counts) => ajax(BASE_URL + '/api/add_shop_cart', {user_id, goods_id, goods_name, thumb_url, price, buy_count, counts}, 'POST'); 59 | 60 | // 单个商品数量的改变 61 | export const changeGoodsCount = (goods_id, count, user_id) => ajax(BASE_URL + '/api/change_goods_count', {goods_id, count, user_id}, 'POST'); 62 | 63 | // 删除单个商品 64 | export const deleteGoods = (goods_id, user_id) => ajax(BASE_URL + '/api/delete_goods', {goods_id, user_id}, 'POST'); 65 | 66 | // 删除所有商品 67 | export const deleteAllGoods = (user_id) => ajax(BASE_URL + '/api/delete_all_goods', {user_id}, 'POST'); 68 | 69 | // 添加商品到数据库 70 | export const addGoodsToRecom = (params) => ajax(BASE_URL + '/api/add_shop_recom', params, 'POST'); 71 | 72 | // 删除recommend单个商品 73 | export const deleteRecomGoods = (goods_id) => ajax(BASE_URL + '/api/delete_recom_goods', {goods_id}, 'POST'); 74 | 75 | // 修改recommend单个商品 76 | export const changeGoodsInfo = (params) => ajax(BASE_URL + '/api/update_recom_goods', params, 'POST'); 77 | 78 | // 请求购物车的数据 79 | export const getCartsGoods = (params) => ajax(BASE_URL + '/api/cart_goods', params); 80 | 81 | // 关键词搜索 82 | export const searchKeywords = (keywords) => ajax(BASE_URL + '/api/searchgoods', {keywords}, 'POST'); 83 | 84 | // 管理员登录 85 | export const adminLogin = (account, pwd) => ajax(BASE_URL + '/api/admin_login', {account, pwd}, 'POST'); 86 | 87 | // 管理员退出登录 88 | export const adminLogout = () => ajax(BASE_URL + '/api/admin_logout'); 89 | 90 | // 请求所有用户 91 | export const getAllUsers = () => ajax(BASE_URL + '/api/admin_allusers'); 92 | -------------------------------------------------------------------------------- /web-client/src/pages/SearchDetail/SearchDetail.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 37 | 38 | 169 | -------------------------------------------------------------------------------- /web-client/src/components/HeaderSearch/HeaderSearch.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 59 | 60 | 161 | -------------------------------------------------------------------------------- /web-client/src/pages/Home/children/DrawerSection/children/DrawerItem.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 69 | 70 | 153 | -------------------------------------------------------------------------------- /web-client/src/store/actions.js: -------------------------------------------------------------------------------- 1 | import { 2 | getHomeCasual, 3 | getCategory, 4 | getHomeShopList, 5 | getRecommendShopList, 6 | getGoodsDetail, 7 | getGoodsComment, 8 | getUserInfo, 9 | getLogout, 10 | getCartsGoods, 11 | deleteGoods, 12 | changeGoodsCount, 13 | deleteAllGoods, 14 | searchKeywords 15 | } from '../api' 16 | 17 | import { 18 | HOME_CASUAL, 19 | CATEGORY_LIST, 20 | HOME_SHOP_LIST, 21 | RECOMMEND_SHOP_LIST, 22 | GOODS_DETAIL, 23 | GOODS_COMMENT, 24 | USER_INFO, 25 | RESET_USER_INFO, 26 | CART_GOODS_LIST, 27 | CHANGE_GOODS_COUNT, 28 | SELECTED_ALL_GOODS, 29 | SELECTED_SINGLE_GOODS, 30 | DEL_SINGLE_GOODS, 31 | DEL_ALL_GOODS, 32 | SEARCH_KEYWORDS 33 | } from './mutation-types' 34 | 35 | export default { 36 | // 获取首页的轮播图 37 | async reqHomeCasual({commit}) { 38 | const result = await getHomeCasual(); 39 | commit(HOME_CASUAL, {homecasual: result.message}) 40 | }, 41 | 42 | // 获取分类数组 43 | async reqCategory({commit}) { 44 | const result = await getCategory(); 45 | commit(CATEGORY_LIST, {categoryList: result.message}) 46 | }, 47 | 48 | // 获取首页的商品数据 49 | async reqHomeShopList({commit}) { 50 | const result = await getHomeShopList(); 51 | commit(HOME_SHOP_LIST, {homeshoplist: result.message}); 52 | }, 53 | 54 | // 获取推荐的商品数据 55 | async reqRecommendShopList({commit}, params) { 56 | const result = await getRecommendShopList(params); 57 | commit(RECOMMEND_SHOP_LIST, {recommendshoplist: result.message}); 58 | }, 59 | 60 | // 获取商品详细数据 61 | async reqGoodsDetail({commit}, params) { 62 | const result = await getGoodsDetail(params); 63 | commit(GOODS_DETAIL, {goodsDetail: result.message}); 64 | }, 65 | 66 | // 获取商品评论 67 | async reqGoodsComment({commit},params) { 68 | const result = await getGoodsComment(params); 69 | commit(GOODS_COMMENT, {goodsComment: result.message}); 70 | }, 71 | 72 | // 同步用户的信息 73 | syncUserInfo({commit}, userInfo){ 74 | commit(USER_INFO, {userInfo}); 75 | }, 76 | 77 | // 异步获取用户信息 78 | async getUserInfo({commit},params){ 79 | let userInfo = {}; 80 | const result = await getUserInfo(params); 81 | if(result.success_code === 200){ 82 | userInfo = result.message; 83 | window.localStorage.setItem("userInfo",JSON.stringify(userInfo)); 84 | }else{ 85 | userInfo = JSON.parse(window.localStorage.getItem("userInfo")); 86 | } 87 | commit(USER_INFO, {userInfo}); 88 | }, 89 | 90 | // 退出登录 91 | async logOut({commit}){ 92 | const result = await getLogout(); 93 | if(result.success_code === 200){ 94 | commit(RESET_USER_INFO); 95 | } 96 | }, 97 | 98 | // 请求购物车数据 99 | async reqCartsGoods({commit},params) { 100 | const result = await getCartsGoods(params); 101 | if(result.success_code === 200){ 102 | commit(CART_GOODS_LIST, {cartgoods: result.message}) 103 | } 104 | }, 105 | 106 | // 单个商品数量的改变 107 | async updateGoodsCount({commit}, {goods, count, user_id}){ 108 | const result = await changeGoodsCount(goods.goods_id, count, user_id); 109 | if(result.success_code === 200){ 110 | commit(CHANGE_GOODS_COUNT, {goods,count}); 111 | } 112 | }, 113 | 114 | // 是否选中所有商品 115 | selectedAll({commit}, {isSelectedAll}){ 116 | commit(SELECTED_ALL_GOODS, {isSelectedAll}); 117 | }, 118 | 119 | // 是否选中单个商品 120 | singleSelected({commit}, {goods}){ 121 | commit(SELECTED_SINGLE_GOODS, {goods}); 122 | }, 123 | 124 | // 删除单个商品 125 | async delSingleGoods({commit}, {goods, user_id}){ 126 | const result = await deleteGoods(goods.goods_id, user_id); 127 | if(result.success_code === 200){ 128 | commit(DEL_SINGLE_GOODS, {goods}); 129 | } 130 | }, 131 | 132 | // 删除所有商品 133 | async delAllGoods({commit}, {user_id}){ 134 | const result = await deleteAllGoods(user_id); 135 | if(result.success_code === 200){ 136 | commit(DEL_ALL_GOODS); 137 | } 138 | }, 139 | 140 | // 模糊搜索 141 | async reqSearch({commit}, {keywords}){ 142 | const result = await searchKeywords(keywords); 143 | if(result.success_code === 200){ 144 | let searchresults = result.message; 145 | commit(SEARCH_KEYWORDS, {searchresults}); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /web-client/src/pages/Admin/Children/AdminUpdate.vue: -------------------------------------------------------------------------------- 1 | 72 | 73 | 120 | 121 | 172 | 173 | -------------------------------------------------------------------------------- /web-client/src/pages/Me/Children/EditPhone.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 117 | 118 | 178 | 179 | -------------------------------------------------------------------------------- /web-client/src/pages/Admin/Children/AdminGoods.vue: -------------------------------------------------------------------------------- 1 | 65 | 66 | 143 | 144 | 164 | 165 | -------------------------------------------------------------------------------- /web-client/build/webpack.prod.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const utils = require('./utils') 4 | const webpack = require('webpack') 5 | const config = require('../config') 6 | const merge = require('webpack-merge') 7 | const baseWebpackConfig = require('./webpack.base.conf') 8 | const CopyWebpackPlugin = require('copy-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 11 | const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') 12 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 13 | 14 | const env = require('../config/prod.env') 15 | 16 | const webpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ 19 | sourceMap: config.build.productionSourceMap, 20 | extract: true, 21 | usePostCSS: true 22 | }) 23 | }, 24 | devtool: config.build.productionSourceMap ? config.build.devtool : false, 25 | output: { 26 | path: config.build.assetsRoot, 27 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 28 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 29 | }, 30 | plugins: [ 31 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 32 | new webpack.DefinePlugin({ 33 | 'process.env': env 34 | }), 35 | new UglifyJsPlugin({ 36 | uglifyOptions: { 37 | compress: { 38 | warnings: false 39 | } 40 | }, 41 | sourceMap: config.build.productionSourceMap, 42 | parallel: true 43 | }), 44 | // extract css into its own file 45 | new ExtractTextPlugin({ 46 | filename: utils.assetsPath('css/[name].[contenthash].css'), 47 | // Setting the following option to `false` will not extract CSS from codesplit chunks. 48 | // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. 49 | // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 50 | // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 51 | allChunks: true, 52 | }), 53 | // Compress extracted CSS. We are using this plugin so that possible 54 | // duplicated CSS from different components can be deduped. 55 | new OptimizeCSSPlugin({ 56 | cssProcessorOptions: config.build.productionSourceMap 57 | ? { safe: true, map: { inline: false } } 58 | : { safe: true } 59 | }), 60 | // generate dist index.html with correct asset hash for caching. 61 | // you can customize output by editing /index.html 62 | // see https://github.com/ampedandwired/html-webpack-plugin 63 | new HtmlWebpackPlugin({ 64 | filename: config.build.index, 65 | template: 'index.html', 66 | inject: true, 67 | minify: { 68 | removeComments: true, 69 | collapseWhitespace: true, 70 | removeAttributeQuotes: true 71 | // more options: 72 | // https://github.com/kangax/html-minifier#options-quick-reference 73 | }, 74 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 75 | chunksSortMode: 'dependency' 76 | }), 77 | // keep module.id stable when vendor modules does not change 78 | new webpack.HashedModuleIdsPlugin(), 79 | // enable scope hoisting 80 | new webpack.optimize.ModuleConcatenationPlugin(), 81 | // split vendor js into its own file 82 | new webpack.optimize.CommonsChunkPlugin({ 83 | name: 'vendor', 84 | minChunks (module) { 85 | // any required modules inside node_modules are extracted to vendor 86 | return ( 87 | module.resource && 88 | /\.js$/.test(module.resource) && 89 | module.resource.indexOf( 90 | path.join(__dirname, '../node_modules') 91 | ) === 0 92 | ) 93 | } 94 | }), 95 | // extract webpack runtime and module manifest to its own file in order to 96 | // prevent vendor hash from being updated whenever app bundle is updated 97 | new webpack.optimize.CommonsChunkPlugin({ 98 | name: 'manifest', 99 | minChunks: Infinity 100 | }), 101 | // This instance extracts shared chunks from code splitted chunks and bundles them 102 | // in a separate chunk, similar to the vendor chunk 103 | // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk 104 | new webpack.optimize.CommonsChunkPlugin({ 105 | name: 'app', 106 | async: 'vendor-async', 107 | children: true, 108 | minChunks: 3 109 | }), 110 | 111 | // copy custom static assets 112 | new CopyWebpackPlugin([ 113 | { 114 | from: path.resolve(__dirname, '../static'), 115 | to: config.build.assetsSubDirectory, 116 | ignore: ['.*'] 117 | } 118 | ]) 119 | ] 120 | }) 121 | 122 | if (config.build.productionGzip) { 123 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 124 | 125 | webpackConfig.plugins.push( 126 | new CompressionWebpackPlugin({ 127 | asset: '[path].gz[query]', 128 | algorithm: 'gzip', 129 | test: new RegExp( 130 | '\\.(' + 131 | config.build.productionGzipExtensions.join('|') + 132 | ')$' 133 | ), 134 | threshold: 10240, 135 | minRatio: 0.8 136 | }) 137 | ) 138 | } 139 | 140 | if (config.build.bundleAnalyzerReport) { 141 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 142 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 143 | } 144 | 145 | module.exports = webpackConfig 146 | -------------------------------------------------------------------------------- /web-client/src/config/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 获取style样式 3 | */ 4 | export const getStyle = (element, attr, NumberMode = 'int') => { 5 | let target; 6 | // scrollTop 获取方式不同,没有它不属于style,而且只有document.body才能用 7 | if (attr === 'scrollTop') { 8 | target = element.scrollTop; 9 | }else if(element.currentStyle){ 10 | target = element.currentStyle[attr]; 11 | }else{ 12 | target = document.defaultView.getComputedStyle(element,null)[attr]; 13 | } 14 | //在获取 opactiy 时需要获取小数 parseFloat 15 | return NumberMode === 'float' ? parseFloat(target) : parseInt(target); 16 | }; 17 | 18 | /** 19 | * 运动效果 20 | * @param {HTMLElement} element 运动对象,必选 21 | * @param {JSON} target 属性:目标值,必选 22 | * @param {number} duration 运动时间,可选 23 | * @param {string} mode 运动模式,可选 24 | * @param {function} callback 可选,回调函数,链式动画 25 | */ 26 | export const animate = (element, target, duration = 400, mode = 'ease-out', callback) => { 27 | clearInterval(element.timer); 28 | //判断不同参数的情况 29 | if (duration instanceof Function) { 30 | callback = duration; 31 | duration = 400; 32 | }else if(duration instanceof String){ 33 | mode = duration; 34 | duration = 400; 35 | } 36 | 37 | //判断不同参数的情况 38 | if (mode instanceof Function) { 39 | callback = mode; 40 | mode = 'ease-out'; 41 | } 42 | 43 | //获取dom样式 44 | const attrStyle = attr => { 45 | if (attr === "opacity") { 46 | return Math.round(getStyle(element, attr, 'float') * 100); 47 | } else { 48 | return getStyle(element, attr); 49 | } 50 | }; 51 | //根字体大小,需要从此将 rem 改成 px 进行运算 52 | const rootSize = parseFloat(document.documentElement.style.fontSize); 53 | 54 | const unit = {}; 55 | const initState = {}; 56 | 57 | //获取目标属性单位和初始样式值 58 | Object.keys(target).forEach(attr => { 59 | if (/[^\d^\.]+/gi.test(target[attr])) { 60 | unit[attr] = target[attr].match(/[^\d^\.]+/gi)[0] || 'px'; 61 | }else{ 62 | unit[attr] = 'px'; 63 | } 64 | initState[attr] = attrStyle(attr); 65 | }); 66 | 67 | //去掉传入的后缀单位 68 | Object.keys(target).forEach(attr => { 69 | if (unit[attr] === 'rem') { 70 | target[attr] = Math.ceil(parseInt(target[attr])*rootSize); 71 | }else{ 72 | target[attr] = parseInt(target[attr]); 73 | } 74 | }); 75 | 76 | 77 | let flag = true; //假设所有运动到达终点 78 | const remberSpeed = {};//记录上一个速度值,在ease-in模式下需要用到 79 | element.timer = setInterval(() => { 80 | Object.keys(target).forEach(attr => { 81 | let iSpeed = 0; //步长 82 | let status = false; //是否仍需运动 83 | let iCurrent = attrStyle(attr) || 0; //当前元素属性址 84 | let speedBase = 0; //目标点需要减去的基础值,三种运动状态的值都不同 85 | let intervalTime; //将目标值分为多少步执行,数值越大,步长越小,运动时间越长 86 | switch(mode){ 87 | case 'ease-out': 88 | speedBase = iCurrent; 89 | intervalTime = duration*5/400; 90 | break; 91 | case 'linear': 92 | speedBase = initState[attr]; 93 | intervalTime = duration*20/400; 94 | break; 95 | case 'ease-in': 96 | let oldspeed = remberSpeed[attr] || 0; 97 | iSpeed = oldspeed + (target[attr] - initState[attr])/duration; 98 | remberSpeed[attr] = iSpeed; 99 | break; 100 | default: 101 | speedBase = iCurrent; 102 | intervalTime = duration*5/400; 103 | } 104 | if (mode !== 'ease-in') { 105 | iSpeed = (target[attr] - speedBase) / intervalTime; 106 | iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); 107 | } 108 | //判断是否达步长之内的误差距离,如果到达说明到达目标点 109 | switch(mode){ 110 | case 'ease-out': 111 | status = iCurrent !== target[attr]; 112 | break; 113 | case 'linear': 114 | status = Math.abs(Math.abs(iCurrent) - Math.abs(target[attr])) > Math.abs(iSpeed); 115 | break; 116 | case 'ease-in': 117 | status = Math.abs(Math.abs(iCurrent) - Math.abs(target[attr])) > Math.abs(iSpeed); 118 | break; 119 | default: 120 | status = iCurrent !== target[attr]; 121 | } 122 | 123 | if (status) { 124 | flag = false; 125 | //opacity 和 scrollTop 需要特殊处理 126 | if (attr === "opacity") { 127 | element.style.filter = "alpha(opacity:" + (iCurrent + iSpeed) + ")"; 128 | element.style.opacity = (iCurrent + iSpeed) / 100; 129 | } else if (attr === 'scrollTop') { 130 | element.scrollTop = iCurrent + iSpeed; 131 | }else{ 132 | element.style[attr] = iCurrent + iSpeed + 'px'; 133 | } 134 | } else { 135 | flag = true; 136 | } 137 | 138 | if (flag) { 139 | clearInterval(element.timer); 140 | if (callback) { 141 | callback(); 142 | } 143 | } 144 | }) 145 | }, 20); 146 | }; 147 | 148 | /** 149 | * 生成图片Base64编码 150 | */ 151 | export const fileToBase64Url = (file, callback) => { 152 | // 1.1 修改图片的信息 153 | let src = ''; 154 | const reader = new FileReader(); 155 | if(file){ 156 | reader.readAsDataURL(file); 157 | } 158 | // 1.2 阅读器已经解析完毕 159 | reader.onloadend = ()=>{ 160 | src = reader.result; 161 | // 回调返回 162 | callback && callback(src); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /web-client/src/pages/AdminLogin/AdminLogin.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 97 | 98 | 186 | -------------------------------------------------------------------------------- /web-client/src/components/HeaderTop/HeaderTop.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 135 | 136 | 217 | -------------------------------------------------------------------------------- /web-client/src/pages/Admin/Admin.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 103 | 104 | 234 | -------------------------------------------------------------------------------- /web-client/src/pages/Search/Search.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 110 | 111 | 242 | -------------------------------------------------------------------------------- /web-client/src/pages/Home/children/DrawerSection/DrawerSection.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 108 | 109 | 250 | -------------------------------------------------------------------------------- /web-client/src/pages/Me/Me.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 102 | 103 | 247 | -------------------------------------------------------------------------------- /web-client/src/pages/Admin/Children/AddGoods.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 191 | 192 | 201 | 202 | -------------------------------------------------------------------------------- /web-client/src/pages/Home/Home.vue: -------------------------------------------------------------------------------- 1 | 69 | 70 | 121 | 122 | 292 | -------------------------------------------------------------------------------- /web-client/src/pages/Me/Children/Update.vue: -------------------------------------------------------------------------------- 1 | 92 | 93 | 180 | 181 | 267 | 268 | -------------------------------------------------------------------------------- /web-client/src/pages/Login/images/captcha.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 10 | 12 | 13 | -------------------------------------------------------------------------------- /web-client/src/pages/AdminLogin/images/captcha.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 10 | 12 | 13 | -------------------------------------------------------------------------------- /web-client/src/pages/ShopCar/ShopCar.vue: -------------------------------------------------------------------------------- 1 | 74 | 75 | 179 | 180 | 384 | -------------------------------------------------------------------------------- /web-client/src/pages/Goods/Goods.vue: -------------------------------------------------------------------------------- 1 | 112 | 113 | 205 | 206 | 449 | --------------------------------------------------------------------------------