├── public ├── favicon.ico └── index.html ├── src ├── assets │ ├── logo.png │ ├── css │ │ ├── reset.less │ │ ├── base.css │ │ └── normalize.css │ └── img │ │ ├── common │ │ ├── normal.svg │ │ ├── yes.svg │ │ ├── check.svg │ │ ├── down.svg │ │ ├── back.svg │ │ ├── success.svg │ │ ├── warning.svg │ │ ├── heart.svg │ │ ├── shop_bag.svg │ │ ├── message.svg │ │ ├── vip.svg │ │ ├── cust.svg │ │ ├── fail.svg │ │ ├── un_heart.svg │ │ ├── shop.svg │ │ ├── setting.svg │ │ ├── code.svg │ │ ├── GitHub.svg │ │ └── top.svg │ │ ├── profile │ │ ├── in.svg │ │ ├── w_send.svg │ │ ├── phone.svg │ │ ├── w_pay.svg │ │ ├── w_comment.svg │ │ ├── w_recovery.svg │ │ ├── portrait.svg │ │ ├── w_refund.svg │ │ ├── foot.svg │ │ └── coupon.svg │ │ └── tabbar │ │ ├── home.svg │ │ ├── home_active.svg │ │ ├── profile.svg │ │ ├── profile_active.svg │ │ ├── category.svg │ │ ├── category_active.svg │ │ ├── shopcart.svg │ │ └── shopcart_active.svg ├── components │ ├── common │ │ ├── swiper │ │ │ ├── index.js │ │ │ ├── SwiperItem.vue │ │ │ └── Swiper.vue │ │ ├── tabbar │ │ │ ├── TabBar.vue │ │ │ └── TabBarItem.vue │ │ ├── navbar │ │ │ └── NavBar.vue │ │ ├── toast │ │ │ ├── index.js │ │ │ ├── CustToast.vue │ │ │ └── Toast.vue │ │ └── scroll │ │ │ └── scroll.vue │ ├── content │ │ ├── backTop │ │ │ └── BackTop.vue │ │ ├── tabControl │ │ │ └── TabControl.vue │ │ └── mainTabbar │ │ │ └── MainTabBar.vue │ └── HelloWorld.vue ├── api │ ├── detail.js │ ├── index.js │ ├── category.js │ └── home.js ├── utils │ ├── url.js │ ├── public.js │ └── request.js ├── common │ └── mixin.js ├── config │ └── index.js ├── main.js ├── views │ ├── detail │ │ ├── childComps │ │ │ ├── GoodsStyleInfo.vue │ │ │ ├── SaleInfo.vue │ │ │ ├── DetailSwiper.vue │ │ │ ├── LogisticsInfo.vue │ │ │ ├── GoodsDisplay.vue │ │ │ ├── DiscountInfo.vue │ │ │ ├── BaseInfo.vue │ │ │ ├── DetailTabBar.vue │ │ │ ├── ShopInfo.vue │ │ │ ├── BottomBar.vue │ │ │ └── CommentInfo.vue │ │ └── Detail.vue │ ├── category │ │ ├── childComps │ │ │ ├── GoodsLIst.vue │ │ │ ├── LeftBar.vue │ │ │ ├── TabBar.vue │ │ │ └── GoodListItem.vue │ │ └── Category.vue │ ├── home │ │ ├── childComps │ │ │ ├── GoodsLIst.vue │ │ │ ├── FeatureView.vue │ │ │ ├── HomeSwiper.vue │ │ │ ├── RecommendView.vue │ │ │ ├── SwiperView.vue │ │ │ └── GoodListItem.vue │ │ └── Home.vue │ ├── profile │ │ ├── Profile.vue │ │ └── childComps │ │ │ ├── TabView.vue │ │ │ ├── UserView.vue │ │ │ ├── FeatureView.vue │ │ │ └── OrderView.vue │ ├── shopcart │ │ ├── childComps │ │ │ ├── DelBottomBar.vue │ │ │ ├── SettleBottomBar.vue │ │ │ └── ShopListItem.vue │ │ └── ShopCart.vue │ └── demo │ │ └── Demo.vue ├── App.vue ├── router │ └── index.js └── store │ └── index.js ├── babel.config.js ├── vue.config.js ├── .gitignore ├── package.json ├── DEPLOYMENT.md └── README.md /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxy2077/Mall/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wxy2077/Mall/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/components/common/swiper/index.js: -------------------------------------------------------------------------------- 1 | import Swiper from './Swiper' 2 | import SwiperItem from './SwiperItem' 3 | 4 | export { 5 | Swiper, SwiperItem 6 | } 7 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | 2 | // 参考 https://cli.vuejs.org/zh/guide/deployment.html#github-pages 3 | module.exports = { 4 | publicPath: process.env.NODE_ENV === 'production' 5 | ? '/app/mall/' 6 | : '/', 7 | }; 8 | -------------------------------------------------------------------------------- /src/api/detail.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * 请求详情页内容 5 | * */ 6 | 7 | 8 | import request from '@/utils/request' 9 | 10 | // 获取首页信息 11 | export function GoodsDetail(data) { 12 | return request( 13 | '/goods/detail', 14 | "POST", 15 | data, 16 | ) 17 | } 18 | 19 | 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /src/api/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 汇总所有的接口 3 | * Author: CoderCharm 4 | * Time: 2020/06/11 5 | * 6 | */ 7 | import * as home from './home'; 8 | import * as detail from './detail'; 9 | import * as category from './category'; 10 | 11 | 12 | export default { 13 | 14 | home, //首页 15 | detail, // 详情页 16 | category, // 详情页 17 | } 18 | -------------------------------------------------------------------------------- /src/utils/url.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 封装url路由前缀 4 | * 后面扩展版本区分 可以更改这里 5 | * */ 6 | 7 | import { appConfig } from '@/config' 8 | 9 | export default function concatUrl(e) { 10 | let url; 11 | // url = `${appConfig[process.env.NODE_ENV]}/api/leaf/mall${e}`; 12 | url = `${appConfig[process.env.NODE_ENV]}/api/v1${e}`; 13 | return url; 14 | } 15 | -------------------------------------------------------------------------------- /src/common/mixin.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 4 | * 公共的混入对象 5 | * */ 6 | 7 | 8 | // 返回到顶部 9 | export const backTopMixin = { 10 | data: function () { 11 | return { 12 | showBackTop: false 13 | } 14 | }, 15 | methods: { 16 | backTop: function () { 17 | this.$refs.scroll.scrollTo(0, 0, 300); 18 | } 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/config/index.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 4 | * 自定义的一些配置信息,在main.js 通过prototype挂载到vue对象 5 | * 6 | * */ 7 | 8 | export let appConfig = { 9 | development: 'http://127.0.0.1:8010', 10 | production: '', 11 | cookieTime: { 12 | refreshToken: 60 * 60 * 24 * 7, // refreshToken 存在7天 13 | accessToken: 60 * 60 // accessToken 存在1小时 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /src/components/common/swiper/SwiperItem.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 23 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import store from './store' 4 | import router from './router' 5 | 6 | Vue.config.productionTip = false; 7 | 8 | import showToast from '@/components/common/toast/index' 9 | 10 | Vue.use(showToast); 11 | 12 | new Vue({ 13 | store, 14 | router, 15 | render: h => h(App) 16 | }).$mount('#app'); 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/components/common/tabbar/TabBar.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 27 | -------------------------------------------------------------------------------- /src/api/category.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * 请求分类数据 5 | * */ 6 | 7 | 8 | import request from '@/utils/request' 9 | 10 | // 获取侧边栏分类 11 | export function getCategory() { 12 | return request( 13 | '/category', 14 | "GET", 15 | ) 16 | } 17 | 18 | 19 | // 获取对应分类tabBar 20 | export function getTabBar(data) { 21 | return request( 22 | '/category/tab', 23 | "GET", 24 | data 25 | ) 26 | } 27 | 28 | // 获取对应分类tabBar 29 | export function getGoods(data) { 30 | return request( 31 | '/category/goods', 32 | "POST", 33 | data 34 | ) 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/assets/css/reset.less: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | html, 3 | body, 4 | div, 5 | dl, 6 | dt, 7 | dd, 8 | ul, 9 | ol, 10 | li, 11 | h2, 12 | h2, 13 | h3, 14 | pre, 15 | code, 16 | form, 17 | fieldset, 18 | legend, 19 | input, 20 | button, 21 | textarea, 22 | p, 23 | blockquote, 24 | th, 25 | td, 26 | section, 27 | article, 28 | aside, 29 | header, 30 | footer, 31 | nav, 32 | dialog, 33 | figure, 34 | figcaption, 35 | hgroup { 36 | margin: 0; 37 | padding: 0; 38 | } 39 | 40 | body { 41 | width: 100%; 42 | height: 100%; 43 | position: relative; 44 | overflow-x: hidden; 45 | } 46 | html{ 47 | width: 100%; 48 | height: 100%; 49 | } 50 | ul, 51 | li, 52 | ol { 53 | list-style: none; 54 | } -------------------------------------------------------------------------------- /src/assets/img/common/normal.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/content/backTop/BackTop.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | 19 | 32 | -------------------------------------------------------------------------------- /src/views/detail/childComps/GoodsStyleInfo.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | 19 | 31 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/views/detail/childComps/SaleInfo.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 20 | 21 | 31 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 21 | 22 | 31 | -------------------------------------------------------------------------------- /src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | 21 | 22 | 38 | -------------------------------------------------------------------------------- /src/assets/img/profile/in.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/common/yes.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/api/home.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * 请求首页内容 5 | * */ 6 | 7 | 8 | import request from '@/utils/request' 9 | 10 | // 获取首页信息 11 | export function GetBanner() { 12 | return request( 13 | '/home/banner', 14 | "get", 15 | ) 16 | } 17 | 18 | 19 | // 获取首页 features 信息 20 | export function GetFeatures() { 21 | return request( 22 | '/home/features', 23 | "get", 24 | ) 25 | } 26 | 27 | 28 | // 获取首页 recommends 信息 29 | export function GetRecommends() { 30 | return request( 31 | '/home/recommends', 32 | "get", 33 | ) 34 | } 35 | 36 | 37 | // 获取首页 tab信息 38 | export function GetTab() { 39 | return request( 40 | '/home/tab', 41 | "get", 42 | ) 43 | } 44 | 45 | // 获取首页goods信息 46 | export function GetGoods(data={}) { 47 | return request( 48 | '/home/goods', 49 | "get", 50 | data 51 | ) 52 | } 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/views/category/childComps/GoodsLIst.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 31 | 32 | 41 | -------------------------------------------------------------------------------- /src/views/home/childComps/GoodsLIst.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 29 | 30 | 39 | -------------------------------------------------------------------------------- /src/components/common/navbar/NavBar.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | 18 | 39 | -------------------------------------------------------------------------------- /src/assets/img/common/check.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/public.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 公共的工具类 4 | * */ 5 | 6 | // export function debounce(fn, delay=5000) { 7 | // // 记录上一次的延时器 8 | // var timer = null; 9 | // 10 | // return function () { 11 | // var args = arguments; 12 | // var that = this; 13 | // // 清除上一次延时器 14 | // clearTimeout(timer); 15 | // timer = setTimeout(function () { 16 | // fn.apply(that, args) 17 | // }, delay); 18 | // } 19 | // } 20 | 21 | // 防抖函数 22 | export function debounce(fn, delay) { 23 | 24 | delay = delay || 200; 25 | var timer; 26 | return function () { 27 | var th = this; 28 | var args = arguments; 29 | if (timer) { 30 | clearTimeout(timer); 31 | } 32 | window.console.log("防抖111111"); 33 | timer = setTimeout(function () { 34 | timer = null; 35 | window.console.log("防抖了"); 36 | fn.apply(th, args); 37 | }, delay); 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /src/assets/img/profile/w_send.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/profile/phone.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/tabbar/home.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/tabbar/home_active.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/common/down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/detail/childComps/DetailSwiper.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 35 | 36 | 46 | -------------------------------------------------------------------------------- /src/views/detail/childComps/LogisticsInfo.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 27 | 28 | 45 | -------------------------------------------------------------------------------- /src/components/common/toast/index.js: -------------------------------------------------------------------------------- 1 | // 2 | import vue from 'vue' 3 | 4 | // 导入自定义到Toast组件 5 | import CustToast from './CustToast.vue' 6 | 7 | 8 | // 生成一个扩展实例构造器 9 | const ToastConstructor = vue.extend(CustToast); 10 | 11 | // 定义弹出组件的函数 接收三个参数 消息 toast类型 显示时间 12 | function showToast(message, type="normal", duration = 2000) { 13 | 14 | // 实例化一个 CustToast.vue 15 | const _toast = new ToastConstructor({ 16 | data() { 17 | return { 18 | showToast: true, 19 | type: type, 20 | message: message, 21 | duration: duration 22 | } 23 | } 24 | }); 25 | 26 | // 把实例化的 CustToast.vue 添加到 body 里 27 | let element = _toast.$mount().$el; 28 | document.body.appendChild(element); 29 | 30 | // duration时间到了后隐藏 31 | setTimeout(() => {_toast.showToast = false} ,duration) 32 | } 33 | 34 | // 需要在main.js 里面使用 Vue.use(showToast); 35 | showToast.install = (Vue) => { 36 | // 将组件注册到 vue 的 原型链里去, 37 | // 这样就可以在所有 vue 的实例里面使用 this.$toast() 38 | Vue.prototype.$toast = showToast 39 | }; 40 | 41 | // 导出 42 | export default showToast 43 | -------------------------------------------------------------------------------- /src/assets/img/common/back.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/home/childComps/FeatureView.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 26 | 27 | 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mall", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "axios": "^0.19.2", 12 | "better-scroll": "^1.15.2", 13 | "core-js": "^3.6.5", 14 | "swiper": "^5.4.5", 15 | "vue": "^2.6.11", 16 | "vue-awesome-swiper": "^4.1.1", 17 | "vue-router": "^3.3.1", 18 | "vuex": "^3.4.0" 19 | }, 20 | "devDependencies": { 21 | "@vue/cli-plugin-babel": "^4.4.0", 22 | "@vue/cli-plugin-eslint": "^4.4.0", 23 | "@vue/cli-service": "^4.4.0", 24 | "babel-eslint": "^10.1.0", 25 | "eslint": "^5.1.0", 26 | "eslint-plugin-vue": "^6.2.2", 27 | "vue-template-compiler": "^2.6.11" 28 | }, 29 | "eslintConfig": { 30 | "root": true, 31 | "env": { 32 | "node": true 33 | }, 34 | "extends": [ 35 | "plugin:vue/essential", 36 | "eslint:recommended" 37 | ], 38 | "parserOptions": { 39 | "parser": "babel-eslint" 40 | }, 41 | "rules": {} 42 | }, 43 | "browserslist": [ 44 | "> 1%", 45 | "last 2 versions", 46 | "not dead" 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /src/assets/img/common/success.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/common/warning.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/common/heart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/profile/w_pay.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/category/childComps/LeftBar.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 39 | 40 | 56 | -------------------------------------------------------------------------------- /src/assets/img/common/shop_bag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | 2 | import Vue from 'vue' 3 | import VueRouter from 'vue-router' 4 | 5 | // 路由懒加载 用到才请求 6 | const Home = () => import('@/views/home/Home'); 7 | const Category = () => import('@/views/category/Category'); 8 | const Cart = () => import('@/views/shopcart/ShopCart'); 9 | const Profile = () => import('@/views/profile/Profile'); 10 | 11 | // 1.安装VueRouter 12 | Vue.use(VueRouter); 13 | 14 | // 2.配置路由信息 15 | const routes = [ 16 | { 17 | path: '/', 18 | redirect: '/home' 19 | }, 20 | { 21 | path: '/home', 22 | component: Home, 23 | meta:{ 24 | 25 | } 26 | }, 27 | { 28 | path: '/category', 29 | component: Category 30 | }, 31 | { 32 | path: '/cart', 33 | component: Cart 34 | }, 35 | { 36 | path: '/profile', 37 | component: Profile 38 | }, 39 | { 40 | path: '/demo', 41 | name: "Demo", 42 | component: () => import('@/views/demo/Demo'), 43 | }, 44 | { 45 | path: '/detail', 46 | name: "Detail", 47 | component: () => import('@/views/detail/Detail'), 48 | } 49 | ]; 50 | 51 | // 3.创建路由对象 52 | const router = new VueRouter({ 53 | base: '/app/mall', 54 | routes, 55 | mode: 'history' 56 | }); 57 | 58 | // 路由拦截 59 | router.beforeEach((to, from, next) =>{ 60 | next() 61 | }); 62 | 63 | // 4.导出 64 | export default router 65 | -------------------------------------------------------------------------------- /src/views/home/childComps/HomeSwiper.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 44 | 45 | 56 | -------------------------------------------------------------------------------- /src/assets/img/common/message.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/css/base.css: -------------------------------------------------------------------------------- 1 | @import "./normalize.css"; 2 | 3 | :root { 4 | --color-text: #666; 5 | --color-high-text: #ff5777; 6 | --color-tint: #ff8198; 7 | --color-background: #fff; 8 | --font-size: 14px; 9 | --line-height: 1.5; 10 | } 11 | 12 | *, 13 | *::before, 14 | *::after { 15 | margin: 0; 16 | padding: 0; 17 | box-sizing: border-box; 18 | } 19 | 20 | body { 21 | font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif; 22 | user-select: none; /* 禁止用户鼠标在页面上选中文字/图片等 */ 23 | -webkit-tap-highlight-color: transparent; /* webkit是苹果浏览器引擎,tap点击,highlight背景高亮,color颜色,颜色用数值调节 */ 24 | background: var(--color-background); 25 | color: var(--color-text); 26 | width: 100vw; 27 | } 28 | 29 | a { 30 | color: var(--color-text); 31 | text-decoration: none; 32 | } 33 | 34 | 35 | .clear-fix::after { 36 | clear: both; 37 | content: ''; 38 | display: block; 39 | width: 0; 40 | height: 0; 41 | visibility: hidden; 42 | } 43 | 44 | .clear-fix { 45 | zoom: 1; 46 | } 47 | 48 | .arrow-right { 49 | border-top: 1px solid #999; 50 | border-left: 1px solid #999; 51 | width: 9px; 52 | height: 9px; 53 | background-color: transparent; 54 | transform: rotate(135deg); 55 | display: inline-block; 56 | margin-left: .1rem; 57 | } 58 | 59 | /*.left {*/ 60 | /*float: left;*/ 61 | /*}*/ 62 | 63 | /*.right {*/ 64 | /*float: right;*/ 65 | /*}*/ -------------------------------------------------------------------------------- /src/assets/img/profile/w_comment.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/profile/Profile.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 50 | 51 | 66 | -------------------------------------------------------------------------------- /src/assets/img/profile/w_recovery.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/category/childComps/TabBar.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 40 | 41 | 63 | -------------------------------------------------------------------------------- /src/views/home/childComps/RecommendView.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 29 | 30 | 61 | -------------------------------------------------------------------------------- /src/views/detail/childComps/GoodsDisplay.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 34 | 35 | 65 | -------------------------------------------------------------------------------- /src/components/content/tabControl/TabControl.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 36 | 37 | 65 | -------------------------------------------------------------------------------- /src/assets/img/common/vip.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/common/cust.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/common/fail.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/home/childComps/SwiperView.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 48 | 49 | 59 | -------------------------------------------------------------------------------- /src/views/profile/childComps/TabView.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 37 | 38 | 70 | -------------------------------------------------------------------------------- /src/components/common/tabbar/TabBarItem.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 50 | 51 | 69 | -------------------------------------------------------------------------------- /src/assets/img/profile/portrait.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/detail/childComps/DiscountInfo.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 43 | 44 | 72 | -------------------------------------------------------------------------------- /src/views/home/childComps/GoodListItem.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 47 | 48 | 73 | -------------------------------------------------------------------------------- /src/views/category/childComps/GoodListItem.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 48 | 49 | 74 | -------------------------------------------------------------------------------- /src/views/profile/childComps/UserView.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 34 | 35 | 81 | -------------------------------------------------------------------------------- /src/views/detail/childComps/BaseInfo.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 44 | 45 | 92 | -------------------------------------------------------------------------------- /src/views/detail/childComps/DetailTabBar.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 52 | 53 | 79 | -------------------------------------------------------------------------------- /src/assets/img/common/un_heart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/profile/childComps/FeatureView.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 63 | 64 | 88 | -------------------------------------------------------------------------------- /src/views/detail/childComps/ShopInfo.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 52 | 53 | 84 | -------------------------------------------------------------------------------- /src/views/shopcart/childComps/DelBottomBar.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 41 | 42 | 92 | -------------------------------------------------------------------------------- /src/components/common/scroll/scroll.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 79 | 80 | 83 | -------------------------------------------------------------------------------- /src/assets/img/profile/w_refund.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/tabbar/profile.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/tabbar/profile_active.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/detail/childComps/BottomBar.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 47 | 48 | 99 | -------------------------------------------------------------------------------- /src/components/common/toast/CustToast.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 47 | 48 | 96 | -------------------------------------------------------------------------------- /src/assets/img/common/shop.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/content/mainTabbar/MainTabBar.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 65 | 66 | 76 | -------------------------------------------------------------------------------- /src/assets/img/profile/foot.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/demo/Demo.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 91 | 92 | 98 | -------------------------------------------------------------------------------- /DEPLOYMENT.md: -------------------------------------------------------------------------------- 1 | 2 | ## 关于本项目的线上部署 3 | 4 | > nginx 多静态文件部署。 5 | > 我个人博客项目占用了`nginx`根路径,而我只有一个域名,也不准备用二级域名了, 所以就直接用路径区分开。 6 | > 一个域名,只部署一个静态文件服务,那很简单,只用把打包后文件放上去就行。 7 | > 多个Vue项目主要就是路径问题,静态js文件以及自定义的路由。 8 | 9 | #### 环境 10 | 11 | - vue 2.6.11 12 | - vue-router 3.3.1 13 | - vue-cli 4.4.0 14 | 15 | 由于我用的vue-cli 4,所以项目根路径下默认没有 `vue.config.js`文件, 我在官网看到关于这个vue.config.js是这样描述的. 16 | 17 | - 参考[官网说明](https://cli.vuejs.org/zh/config/)https://cli.vuejs.org/zh/config/: 18 | >有些针对 @vue/cli 的全局配置,例如你惯用的包管理器和你本地保存的 preset,都保存在 home 目录下一个名叫 .vuerc 的 JSON 文件。你可以用编辑器直接编辑这个文件来更改已保存的选项。 19 | 你也可以使用 vue config 命令来审查或修改全局的 CLI 配置 20 | 21 | ## 添加配置`vue.config.js` 文件 静态资源路由js,css等路径 22 | 但是 vue config 是全局的配置,我这个项目直接修改这里不合适,于是我就在项目 `根路径` 下自己手动添加了`vue.config.js` 23 | 24 | ```javascript 25 | 26 | // 参考 https://cli.vuejs.org/zh/guide/deployment.html#github-pages 27 | module.exports = { 28 | publicPath: process.env.NODE_ENV === 'production' 29 | ? '/app/mall/' // /app/mall/ 对应后面`nginx`路径,这里添加的目的是其他静态资源文件统一前缀路径 30 | : '/', 31 | }; 32 | ``` 33 | ## 修改src/router/index.js, 项目页面跳转路由 34 | 35 | ```javascript 36 | const router = new VueRouter({ 37 | base: '/app/mall', // 主要添加这个 /app/mall 注意这个 和上面的一致,为nginx location路径, 也就是请求路径 38 | routes, 39 | mode: 'history' 40 | }); 41 | 42 | ``` 43 | ## 修改 src/config/index.js 项目生产请求地址(非必要) 44 | > 非必要,这个地方每个人封装的都不一样,默认会请求 部署的环境地址。 45 | 46 | ```javascript 47 | export let appConfig = { 48 | development: 'http://127.0.0.1:8010', // 本地开发 (ps:vue-cli会自动帮我们区分生产开发,npm run serve就是开发, 49 | production: 'https://www.your_domain.com', // 生产地址 npm run build 默认为生产 50 | }; 51 | 52 | ``` 53 | 54 | > 然后 我是在src/utils/url.js 文件里面 区分前缀的, 最后在 src/utils/request.js 文件里面使用。 55 | 56 | 修改好之后,可以`npm run serve`看看,区分好之后能否正常访问,是否有改错。 57 | 没有改错可以直接`npm run build` 生成静态文件 58 | 59 | ## 上传静态文件 60 | > 我是通过`FillZilla`上传静态资源文件 61 | 62 | 我是上传到服务器 `/data/mall/` 文件夹下, 记住这个文件路径, 后面nginx会指向这里。 63 | 64 | 65 | ## nginx 文件配置 66 | 67 | > Centos7 nginx默认配置地址为 `/etc/nginx/nginx.conf` 68 | 或者有的版本默认引入 `/etc/nginx/conf.d/default.conf`文件 配置 69 | 70 | ``` 71 | 72 | server { 73 | 74 | listen 80; 75 | server_name localhost; 76 | 77 | #charset koi8-r; 78 | #access_log /var/log/nginx/host.access.log main; 79 | 80 | # ... 81 | location / { 82 | root /usr/share/nginx/html; 83 | index index.html index.htm; 84 | } 85 | 86 | 87 | # 添加文件 88 | # /app/mall 项目访问路径和上面地址对应 89 | location /app/mall/ { 90 | alias /data/mall/; # 项目存放路径 91 | try_files $uri $uri/ /app/mall/index.html; # 内部项目跳转路径 92 | } 93 | 94 | } 95 | 96 | ``` 97 | 98 | 检测配置是否有语法错误 99 | ``` 100 | nginx -t 101 | 102 | ``` 103 | 重新加载配置 104 | ``` 105 | nginx -s reload 106 | 107 | ``` 108 | 109 | ## 个人项目部署地址 110 | 项目进度 111 | - 完成首页,分类页,购物车等页面,还差个人中心(2020/06/30) 112 | 113 | https://www.charmcode.cn/app/mall/home 114 | 115 | > PC端, Google浏览器需要F12 点击切换移动设备 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /src/assets/img/common/setting.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/tabbar/category.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/shopcart/childComps/SettleBottomBar.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 67 | 68 | 121 | -------------------------------------------------------------------------------- /src/assets/img/tabbar/category_active.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/shopcart/ShopCart.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 99 | 100 | 103 | -------------------------------------------------------------------------------- /src/assets/img/common/code.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/common/toast/Toast.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 75 | 76 | 124 | -------------------------------------------------------------------------------- /src/assets/img/common/GitHub.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/detail/childComps/CommentInfo.vue: -------------------------------------------------------------------------------- 1 | 49 | 50 | 58 | 59 | 117 | -------------------------------------------------------------------------------- /src/assets/img/profile/coupon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/profile/childComps/OrderView.vue: -------------------------------------------------------------------------------- 1 | 52 | 53 | 67 | 68 | 123 | -------------------------------------------------------------------------------- /src/utils/request.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | // import store from '@/store' 3 | // import router from '@/router' 4 | import concatUrl from '@/utils/url'; 5 | // import { appConfig } from '@/config' 6 | 7 | 8 | /** 9 | * 刷新token 10 | * 当accessToken过期时调用 11 | * */ 12 | export function getReferToken(){ 13 | const refreshToken = localStorage.getItem("refreshToken"); 14 | if(refreshToken) { 15 | const url = concatUrl('/auth/refresh/token/'); 16 | axios({ 17 | withCredentials: true, 18 | headers: { 19 | 'Content-Type': 'application/json; charset=utf-8', 20 | 'Authorization': refreshToken 21 | }, 22 | method: 'post', 23 | url: url 24 | }).then(({data}) => { 25 | window.console.log("-----"); 26 | window.console.log(data); 27 | window.console.log("-----"); 28 | return data 29 | // $cookies.set("access_token", data.data.access_token, cookieTime['access_token']); 30 | }); 31 | } 32 | } 33 | 34 | 35 | 36 | // 创建axios实例 37 | const service = axios.create({ 38 | // baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url 39 | // baseURL: 'http://127.0.0.1:5000/api', // url = base url + request url 40 | timeout: 50000, 41 | withCredentials: true, 42 | headers: { 43 | 'Content-Type': 'application/json; charset=utf-8' 44 | } 45 | }); 46 | 47 | 48 | // 简单封装一层提示消息 49 | // const tip = (msg, type) => { 50 | // Message({ 51 | // type: type, 52 | // showClose: true, 53 | // message: msg || 'Error', 54 | // duration: 5 * 1000 55 | // }); 56 | // }; 57 | 58 | // axios request拦截器 59 | service.interceptors.request.use( 60 | async config => { 61 | // do something before request is sent 62 | window.console.log("请求拦截"); 63 | // 1.当发送网络请求时, 在页面中添加一个loading组件, 作为动画 64 | 65 | // 2.某些请求要求用户必须登录, 判断用户是否有token, 如果没有token跳转到login页面 66 | 67 | // 3.对请求的参数进行序列化(看服务器是否需要序列化) 68 | // config.data = qs.stringify(config.data) 69 | // console.log(config); 70 | return config 71 | }, 72 | error => { 73 | // do something with request error 74 | window.console.log(error); // for debug 75 | return Promise.reject(error) 76 | } 77 | ); 78 | 79 | // axios response 拦截器 80 | service.interceptors.response.use( 81 | /** 82 | * If you want to get http information such as headers or status 83 | * Please return response => response 84 | */ 85 | response => { 86 | const res = response.data; 87 | // if the custom code is not 20000, it is judged as an error. 88 | if (res.code !== 200) { 89 | // tip(res.data, 'error') 90 | window.console.log(res) 91 | } else { 92 | return res 93 | } 94 | }, 95 | error => { 96 | window.console.log('err' + error); // for debug 97 | // tip("网络错误!", "error"); 98 | return Promise.reject(error) 99 | } 100 | ); 101 | 102 | 103 | export const request = (url, method = 'POST', data = {}) => { 104 | let requestUrl = concatUrl(url); 105 | 106 | window.console.log(requestUrl); 107 | 108 | // 参数配置 如果是get方法就 设置成params 参数, 其他则设置成data参数 109 | let reConfig = method.toLocaleUpperCase() === "GET" ? { 110 | method, 111 | url: requestUrl, 112 | params: data 113 | } : { 114 | method, 115 | url: requestUrl, 116 | data 117 | }; 118 | 119 | return service(reConfig); 120 | }; 121 | 122 | 123 | export default request 124 | -------------------------------------------------------------------------------- /src/views/shopcart/childComps/ShopListItem.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 54 | 55 | 136 | -------------------------------------------------------------------------------- /src/assets/img/tabbar/shopcart.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/tabbar/shopcart_active.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mall 2 | > 练习Vue时写的移动端商城项目,目前已经部署到线上。 3 | 4 | ## 对应API服务 5 | > 使用Python FastAPI构建 6 | 7 | [FastAPI服务地址](https://github.com/CoderCharm/MallAPI): https://github.com/CoderCharm/MallAPI 8 | 9 | 项目进度 10 | - 完成首页,分类页,购物车等页面,还差个人中心(2020/06/30) 11 | 12 | 线上地址 13 | https://www.charmcode.cn/app/mall/home 14 | 15 | > PC端, Google浏览器需要F12 点击切换移动设备 16 | 17 | 18 | ## 存在问题 19 | 20 | > 这些问题,暂时先不解决,整体完成后在解决。 21 | 22 | - 1 内容滚动不自然。 23 | - 2 有时候,首次进入页面,不能滑动内容。(疑似没有重新计算内容高度) 24 | - 3 没有记录滚动高度,切换分类时,滚动高度没有初始化。(应该单独记录每个分类的滚动高度) 25 | - 4 没有Toast弹窗提示。(后续完善) 26 | - 5 详情页,部分样式不完善。(不完善了) 27 | - 6 由于数据问题,商品详情页goodsId只有 123, 456, 789三个可以传。 28 | - 7 swiper 获取元素失败报错问题。(使用vue-awesome-swiper替换) 29 | 30 | ## 部署到线上 (同一个域名,部署多个Vue静态文件服务) 31 | 32 | [地址](DEPLOYMENT.md) DEPLOYMENT.md 个人总结 33 | 34 | #### Src文件目录 35 | ``` 36 | 37 | |____App.vue 38 | |____config 39 | | |____index.js // 简单的自定义配置文件 比如请求的地址 生产和开发地址(ps:vue-cli会自动帮我们区分生产开发,npm run serve就是开发,build生产) 40 | |____utils 41 | | |____request.js // 封装的 axios服务 42 | | |____public.js 43 | | |____url.js // 在这里读取区分 开发生产的根路径 44 | |____main.js 45 | |____components // 全局组件 46 | | |____content // 只能本项目通用的全局组件 47 | | | |____tabControl 48 | | | | |____TabControl.vue 49 | | | |____mainTabbar 50 | | | | |____MainTabBar.vue 51 | | | |____backTop 52 | | | | |____BackTop.vue 53 | | |____HelloWorld.vue 54 | | |____common // 其他地方也能用的全局组件 55 | | | |____navbar 56 | | | | |____NavBar.vue 57 | | | |____swiper 58 | | | | |____Swiper.vue 59 | | | | |____SwiperItem.vue 60 | | | | |____index.js 61 | | | |____scroll 62 | | | | |____scroll.vue 63 | | | |____tabbar 64 | | | | |____TabBarItem.vue 65 | | | | |____TabBar.vue 66 | |____common 67 | | |____mixin.js 68 | |____api // 配合axios 区分的各模块路由 69 | | |____index.js 70 | | |____home.js 71 | | |____category.js 72 | | |____detail.js 73 | |____views // 各模块页面 74 | | |____demo 75 | | | |____Demo.vue 76 | | | |____childComps 77 | | |____home 78 | | | |____Home.vue 79 | | | |____childComps 80 | | | | |____FeatureView.vue 81 | | | | |____HomeSwiper.vue 82 | | | | |____GoodListItem.vue 83 | | | | |____RecommendView.vue 84 | | | | |____GoodsLIst.vue 85 | | |____category 86 | | | |____Category.vue 87 | | | |____childComps 88 | | | | |____LeftBar.vue 89 | | | | |____GoodListItem.vue 90 | | | | |____GoodsLIst.vue 91 | | | | |____TabBar.vue 92 | | |____shopcart 93 | | | |____childComps 94 | | | | |____DelBottomBar.vue 95 | | | | |____ShopListItem.vue 96 | | | | |____SettleBottomBar.vue 97 | | | |____ShopCart.vue 98 | | |____detail 99 | | | |____Detail.vue 100 | | | |____childComps 101 | | | | |____DetailSwiper.vue 102 | | | | |____GoodsDisplay.vue 103 | | | | |____GoodsStyleInfo.vue 104 | | | | |____LogisticsInfo.vue 105 | | | | |____DiscountInfo.vue 106 | | | | |____BottomBar.vue 107 | | | | |____CommentInfo.vue 108 | | | | |____BaseInfo.vue 109 | | | | |____SaleInfo.vue 110 | | | | |____ShopInfo.vue 111 | | | | |____DetailTabBar.vue 112 | | |____profile 113 | | | |____Profile.vue 114 | |____assets 115 | | |____css 116 | | | |____reset.less 117 | | | |____normalize.css 118 | | | |____base.css 119 | | |____img 120 | | | |____tabbar 121 | | | | |____home.svg 122 | | | | |____profile_active.svg 123 | | | | |____home_active.svg 124 | | | | |____category.svg 125 | | | | |____category_active.svg 126 | | | | |____shopcart.svg 127 | | | | |____shopcart_active.svg 128 | | | | |____profile.svg 129 | | | |____common 130 | | | | |____yes.svg 131 | | | | |____shop.svg 132 | | | | |____check.svg 133 | | | | |____back.svg 134 | | | | |____cust.svg 135 | | | | |____top.svg 136 | | | | |____heart.svg 137 | | |____logo.png 138 | |____store 139 | | |____index.js 140 | |____router // vue-router 本地路由文件 141 | | |____index.js 142 | 143 | ``` 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | ## Project setup 156 | ``` 157 | npm install 158 | ``` 159 | 160 | ### Compiles and hot-reloads for development 161 | ``` 162 | npm run serve 163 | ``` 164 | 165 | ### Compiles and minifies for production 166 | ``` 167 | npm run build 168 | ``` 169 | 170 | ### Run your tests 171 | ``` 172 | npm run test 173 | ``` 174 | 175 | ### Lints and fixes files 176 | ``` 177 | npm run lint 178 | ``` 179 | 180 | ### Customize configuration 181 | See [Configuration Reference](https://cli.vuejs.org/config/). 182 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: { 8 | // 演示demo 9 | counter: 123, 10 | info: { 11 | name: "卡卡罗特", 12 | identity: "SuperSaiaMan", 13 | }, 14 | 15 | // 购物车列表 16 | shopList: [], 17 | 18 | }, 19 | mutations: { 20 | // 默认有一个state回调参数 21 | increment(state, params) { 22 | state.counter++; 23 | window.console.log(params) 24 | }, 25 | // 另外一种提交风格 传入对象 26 | decrement(state, payload){ 27 | window.console.log(state.counter); 28 | window.console.log(payload); 29 | state.counter --; 30 | 31 | // mutations里面必须是同步函数 https://vuex.vuejs.org/zh/guide/mutations.html#mutation-%E5%BF%85%E9%A1%BB%E6%98%AF%E5%90%8C%E6%AD%A5%E5%87%BD%E6%95%B0 32 | // setTimeout(() =>{ 33 | // // 虽然相应里面已经更改 但是state 没有追踪到 34 | // state.info.name = "孙悟空"; 35 | // }, 1000); 36 | // window.console.log(payload); 37 | }, 38 | asyncModify(state, payload){ 39 | // actions 来异步修改 40 | state.info.name = payload; 41 | 42 | }, 43 | addInfo(state, params){ 44 | // state.info["son"] = params // 不能响应式 修改 45 | // this.$set(state.info, "son", params) // 报错 46 | // 可参考官网响应式原理 https://cn.vuejs.org/v2/guide/reactivity.html#%E5%AF%B9%E4%BA%8E%E5%AF%B9%E8%B1%A1 47 | Vue.set(state.info, "son", params) 48 | }, 49 | delInfo(state, params){ 50 | // 不能响应式 能删除 51 | // delete state.info[params]; 52 | 53 | // 响应式删除 对象 属性 54 | Vue.delete(state.info, params) 55 | }, 56 | 57 | // 添加到购物车 58 | addShopList(state, params){ 59 | // 判断之前是否添加过 60 | const oldInfo = state.shopList.find(item => item.goodsId === params.goodsId); 61 | // 如果没有 62 | if(!oldInfo){ 63 | // 计数为1 64 | params.count = 1; 65 | // 默认被选中 66 | params.checked = true; 67 | state.shopList.push(params); 68 | }else{ 69 | // 如果有 则+1 70 | oldInfo.count += 1 71 | } 72 | 73 | // window.console.log(state.shopList) 74 | }, 75 | // 购物商品减少 76 | reduceShop(state, params){ 77 | let currentIndex = state.shopList.indexOf(params); 78 | if(currentIndex !== -1){ 79 | if(state.shopList[currentIndex].count > 1){ 80 | state.shopList[currentIndex].count -= 1 81 | } 82 | // else{ 83 | // state.shopList.splice(currentIndex, 1) 84 | // } 85 | } 86 | }, 87 | // 改变单个商品选中状态 88 | changeChecked(state, params){ 89 | // 查找当前对象 90 | const oldInfo = state.shopList.find(item => item.goodsId === params.goodsId); 91 | // 92 | if(oldInfo){ 93 | oldInfo.checked = !oldInfo.checked 94 | } 95 | }, 96 | 97 | // 全选/取消全选 98 | choiceAll(state, choice){ 99 | state.shopList.forEach(el => { 100 | el['checked'] = choice 101 | }) 102 | }, 103 | 104 | // 删除 反向选择没选中的 重新赋值 105 | delChoice(state){ 106 | state.shopList = state.shopList.filter(function(el){ 107 | return !el.checked; 108 | }) 109 | } 110 | 111 | 112 | 113 | }, 114 | actions: { 115 | // 默认 context 上下文 参数回调 116 | aModify(context, payload){ 117 | // 异步修改名字 118 | setTimeout(() =>{ 119 | // actions 依旧要通过 mutations 来修改state 120 | context.commit('asyncModify', payload) 121 | }, 2000) 122 | } 123 | 124 | }, 125 | getters: { 126 | // 相当于计算属性 https://www.bilibili.com/video/BV15741177Eh?p=133 127 | // state, getters 两个固定的默认参数 128 | // fun(state, getters){ 129 | // 如果要接受参数 130 | // return (p) =>{ 131 | // return 操作 132 | // } 133 | // 134 | // } 135 | 136 | cartList(state) { 137 | return state.shopList 138 | }, 139 | cartCount(state) { 140 | return state.shopList.length 141 | } 142 | }, 143 | modules: {} 144 | }) 145 | -------------------------------------------------------------------------------- /src/assets/img/common/top.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/category/Category.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 167 | 168 | 195 | -------------------------------------------------------------------------------- /src/views/detail/Detail.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 195 | 196 | 210 | -------------------------------------------------------------------------------- /src/assets/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in iOS. 9 | */ 10 | 11 | html { 12 | line-height: 1.15; /* 1 */ 13 | -webkit-text-size-adjust: 100%; /* 2 */ 14 | } 15 | 16 | /* Sections 17 | ========================================================================== */ 18 | 19 | /** 20 | * Remove the margin in all browsers. 21 | */ 22 | 23 | body { 24 | margin: 0; 25 | } 26 | 27 | /** 28 | * Render the `main` element consistently in IE. 29 | */ 30 | 31 | main { 32 | display: block; 33 | } 34 | 35 | /** 36 | * Correct the font size and margin on `h1` elements within `section` and 37 | * `article` contexts in Chrome, Firefox, and Safari. 38 | */ 39 | 40 | h1 { 41 | font-size: 2em; 42 | margin: 0.67em 0; 43 | } 44 | 45 | /* Grouping content 46 | ========================================================================== */ 47 | 48 | /** 49 | * 1. Add the correct box sizing in Firefox. 50 | * 2. Show the overflow in Edge and IE. 51 | */ 52 | 53 | hr { 54 | box-sizing: content-box; /* 1 */ 55 | height: 0; /* 1 */ 56 | overflow: visible; /* 2 */ 57 | } 58 | 59 | /** 60 | * 1. Correct the inheritance and scaling of font size in all browsers. 61 | * 2. Correct the odd `em` font sizing in all browsers. 62 | */ 63 | 64 | pre { 65 | font-family: monospace, monospace; /* 1 */ 66 | font-size: 1em; /* 2 */ 67 | } 68 | 69 | /* Text-level semantics 70 | ========================================================================== */ 71 | 72 | /** 73 | * Remove the gray background on active links in IE 10. 74 | */ 75 | 76 | a { 77 | background-color: transparent; 78 | } 79 | 80 | /** 81 | * 1. Remove the bottom border in Chrome 57- 82 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 83 | */ 84 | 85 | abbr[title] { 86 | border-bottom: none; /* 1 */ 87 | text-decoration: underline; /* 2 */ 88 | text-decoration: underline dotted; /* 2 */ 89 | } 90 | 91 | /** 92 | * Add the correct font weight in Chrome, Edge, and Safari. 93 | */ 94 | 95 | b, 96 | strong { 97 | font-weight: bolder; 98 | } 99 | 100 | /** 101 | * 1. Correct the inheritance and scaling of font size in all browsers. 102 | * 2. Correct the odd `em` font sizing in all browsers. 103 | */ 104 | 105 | code, 106 | kbd, 107 | samp { 108 | font-family: monospace, monospace; /* 1 */ 109 | font-size: 1em; /* 2 */ 110 | } 111 | 112 | /** 113 | * Add the correct font size in all browsers. 114 | */ 115 | 116 | small { 117 | font-size: 80%; 118 | } 119 | 120 | /** 121 | * Prevent `sub` and `sup` elements from affecting the line height in 122 | * all browsers. 123 | */ 124 | 125 | sub, 126 | sup { 127 | font-size: 75%; 128 | line-height: 0; 129 | position: relative; 130 | vertical-align: baseline; 131 | } 132 | 133 | sub { 134 | bottom: -0.25em; 135 | } 136 | 137 | sup { 138 | top: -0.5em; 139 | } 140 | 141 | /* Embedded content 142 | ========================================================================== */ 143 | 144 | /** 145 | * Remove the border on images inside links in IE 10. 146 | */ 147 | 148 | img { 149 | border-style: none; 150 | } 151 | 152 | /* Forms 153 | ========================================================================== */ 154 | 155 | /** 156 | * 1. Change the font styles in all browsers. 157 | * 2. Remove the margin in Firefox and Safari. 158 | */ 159 | 160 | button, 161 | input, 162 | optgroup, 163 | select, 164 | textarea { 165 | font-family: inherit; /* 1 */ 166 | font-size: 100%; /* 1 */ 167 | line-height: 1.15; /* 1 */ 168 | margin: 0; /* 2 */ 169 | } 170 | 171 | /** 172 | * Show the overflow in IE. 173 | * 1. Show the overflow in Edge. 174 | */ 175 | 176 | button, 177 | input { /* 1 */ 178 | overflow: visible; 179 | } 180 | 181 | /** 182 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 183 | * 1. Remove the inheritance of text transform in Firefox. 184 | */ 185 | 186 | button, 187 | select { /* 1 */ 188 | text-transform: none; 189 | } 190 | 191 | /** 192 | * Correct the inability to style clickable types in iOS and Safari. 193 | */ 194 | 195 | button, 196 | [type="button"], 197 | [type="reset"], 198 | [type="submit"] { 199 | -webkit-appearance: button; 200 | } 201 | 202 | /** 203 | * Remove the inner border and padding in Firefox. 204 | */ 205 | 206 | button::-moz-focus-inner, 207 | [type="button"]::-moz-focus-inner, 208 | [type="reset"]::-moz-focus-inner, 209 | [type="submit"]::-moz-focus-inner { 210 | border-style: none; 211 | padding: 0; 212 | } 213 | 214 | /** 215 | * Restore the focus styles unset by the previous rule. 216 | */ 217 | 218 | button:-moz-focusring, 219 | [type="button"]:-moz-focusring, 220 | [type="reset"]:-moz-focusring, 221 | [type="submit"]:-moz-focusring { 222 | outline: 1px dotted ButtonText; 223 | } 224 | 225 | /** 226 | * Correct the padding in Firefox. 227 | */ 228 | 229 | fieldset { 230 | padding: 0.35em 0.75em 0.625em; 231 | } 232 | 233 | /** 234 | * 1. Correct the text wrapping in Edge and IE. 235 | * 2. Correct the color inheritance from `fieldset` elements in IE. 236 | * 3. Remove the padding so developers are not caught out when they zero out 237 | * `fieldset` elements in all browsers. 238 | */ 239 | 240 | legend { 241 | box-sizing: border-box; /* 1 */ 242 | color: inherit; /* 2 */ 243 | display: table; /* 1 */ 244 | max-width: 100%; /* 1 */ 245 | padding: 0; /* 3 */ 246 | white-space: normal; /* 1 */ 247 | } 248 | 249 | /** 250 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 251 | */ 252 | 253 | progress { 254 | vertical-align: baseline; 255 | } 256 | 257 | /** 258 | * Remove the default vertical scrollbar in IE 10+. 259 | */ 260 | 261 | textarea { 262 | overflow: auto; 263 | } 264 | 265 | /** 266 | * 1. Add the correct box sizing in IE 10. 267 | * 2. Remove the padding in IE 10. 268 | */ 269 | 270 | [type="checkbox"], 271 | [type="radio"] { 272 | box-sizing: border-box; /* 1 */ 273 | padding: 0; /* 2 */ 274 | } 275 | 276 | /** 277 | * Correct the cursor style of increment and decrement buttons in Chrome. 278 | */ 279 | 280 | [type="number"]::-webkit-inner-spin-button, 281 | [type="number"]::-webkit-outer-spin-button { 282 | height: auto; 283 | } 284 | 285 | /** 286 | * 1. Correct the odd appearance in Chrome and Safari. 287 | * 2. Correct the outline style in Safari. 288 | */ 289 | 290 | [type="search"] { 291 | -webkit-appearance: textfield; /* 1 */ 292 | outline-offset: -2px; /* 2 */ 293 | } 294 | 295 | /** 296 | * Remove the inner padding in Chrome and Safari on macOS. 297 | */ 298 | 299 | [type="search"]::-webkit-search-decoration { 300 | -webkit-appearance: none; 301 | } 302 | 303 | /** 304 | * 1. Correct the inability to style clickable types in iOS and Safari. 305 | * 2. Change font properties to `inherit` in Safari. 306 | */ 307 | 308 | ::-webkit-file-upload-button { 309 | -webkit-appearance: button; /* 1 */ 310 | font: inherit; /* 2 */ 311 | } 312 | 313 | /* Interactive 314 | ========================================================================== */ 315 | 316 | /* 317 | * Add the correct display in Edge, IE 10+, and Firefox. 318 | */ 319 | 320 | details { 321 | display: block; 322 | } 323 | 324 | /* 325 | * Add the correct display in all browsers. 326 | */ 327 | 328 | summary { 329 | display: list-item; 330 | } 331 | 332 | /* Misc 333 | ========================================================================== */ 334 | 335 | /** 336 | * Add the correct display in IE 10+. 337 | */ 338 | 339 | template { 340 | display: none; 341 | } 342 | 343 | /** 344 | * Add the correct display in IE 10. 345 | */ 346 | 347 | [hidden] { 348 | display: none; 349 | } -------------------------------------------------------------------------------- /src/views/home/Home.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 197 | 198 | 239 | -------------------------------------------------------------------------------- /src/components/common/swiper/Swiper.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 212 | 213 | 247 | --------------------------------------------------------------------------------