├── .browserslistrc ├── .gitignore ├── README.md ├── babel.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public └── index.html ├── screenshot ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png └── 6.png ├── src ├── App.vue ├── config │ ├── area.js │ ├── filters.js │ ├── global.js │ └── rem.js ├── images │ └── tabbar │ │ ├── category_default.png │ │ ├── category_selected.png │ │ ├── home_default.png │ │ ├── home_selected.png │ │ ├── mine_default.png │ │ ├── mine_selected.png │ │ ├── shoppingcart_default.png │ │ └── shoppingcart_selected.png ├── main.js ├── plugins │ └── vant.js ├── router │ └── index.js ├── service │ └── api │ │ ├── ajax.js │ │ └── index.js ├── store │ ├── actions.js │ ├── index.js │ ├── mutations-type.js │ ├── mutations.js │ └── state.js ├── style │ └── common.less └── views │ ├── cart │ ├── Cart.vue │ └── images │ │ ├── detail1.jpg │ │ └── shop-icon.png │ ├── category │ ├── Category.vue │ └── components │ │ ├── ContentView.vue │ │ ├── Header.vue │ │ └── ProductItem.vue │ ├── dashboard │ └── DashBoard.vue │ ├── home │ ├── Home.vue │ └── components │ │ ├── flashSale │ │ ├── FlashSale.vue │ │ └── FlashSaleItem.vue │ │ ├── header │ │ └── Header.vue │ │ ├── markPage │ │ └── MarkPage.vue │ │ ├── nav │ │ └── Nav.vue │ │ ├── sowing │ │ └── Sowing.vue │ │ └── youLike │ │ ├── YouLike.vue │ │ └── YouLikeItem.vue │ ├── login │ ├── Login.vue │ ├── SelectLogin.vue │ └── images │ │ ├── captcha.svg │ │ ├── hide_pwd.png │ │ ├── logo_bg.png │ │ └── show_pwd.png │ ├── mine │ ├── Mine.vue │ ├── children │ │ ├── MineOrder.vue │ │ ├── UserCenter.vue │ │ └── components │ │ │ └── MineOrderItem.vue │ └── images │ │ └── avator.jpg │ └── order │ ├── Order.vue │ ├── children │ ├── MyAddress.vue │ ├── OrderDetail.vue │ └── children │ │ ├── AddAddress.vue │ │ └── EditAddress.vue │ └── images │ ├── detail1.jpg │ └── shop-icon.png ├── tests └── unit │ └── example.spec.js └── vue.config.js /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /.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 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-shop 2 | 基于Vue + VantUI 做的生鲜商城demo 线上体验地址: http://www.lrboy.live 3 | 4 | # 技术栈 5 | *** 6 | ### vue2.6 + vue-router + vuex + axios + vantUI + ES6 + less 7 | 8 | ## 预览 9 | *** 10 | ![image](https://github.com/bhb603552916/vue-shop/blob/master/screenshot/1.png) 11 | ![image](https://github.com/bhb603552916/vue-shop/blob/master/screenshot/2.png) 12 | ![image](https://github.com/bhb603552916/vue-shop/blob/master/screenshot/3.png) 13 | ![image](https://github.com/bhb603552916/vue-shop/blob/master/screenshot/4.png) 14 | ![image](https://github.com/bhb603552916/vue-shop/blob/master/screenshot/5.png) 15 | ![image](https://github.com/bhb603552916/vue-shop/blob/master/screenshot/6.png) 16 | 17 | ## 经验 18 | *** 19 | ##### 1.用一个Pubsub-js发布订阅模式插件,可以在任意组件中通信,无需关注父子子孙等组件的通信问题 20 | ##### 2.页面中的返回顶部以及轮播图插件其实可以用其他插件更方便,本项目用的swiper以及scroll配置有点麻烦 21 | 22 | ## Build Setup 23 | *** 24 | ``` 25 | # install dependencies 26 | npm install 27 | 28 | # serve with hot reload at localhost:1322 29 | npm run dev 30 | 31 | # build for production with minification 32 | npm run build 33 | ``` 34 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ], 5 | plugins: [ 6 | ['import', { 7 | libraryName: 'vant', 8 | libraryDirectory: 'es', 9 | style: true 10 | }, 'vant'] 11 | ] 12 | }; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lk-shop", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "test:unit": "vue-cli-service test:unit" 9 | }, 10 | "dependencies": { 11 | "axios": "^0.19.0", 12 | "better-scroll": "^1.15.2", 13 | "core-js": "^2.6.5", 14 | "fastclick": "^1.0.6", 15 | "moment": "^2.24.0", 16 | "pubsub-js": "^1.7.0", 17 | "vant": "^2.1.1", 18 | "vue": "^2.6.10", 19 | "vue-awesome-swiper": "^3.1.3", 20 | "vue-qriously": "^1.1.1", 21 | "vue-router": "^3.0.3", 22 | "vuex": "^3.0.1" 23 | }, 24 | "devDependencies": { 25 | "@vue/cli-plugin-babel": "^3.9.0", 26 | "@vue/cli-plugin-unit-mocha": "^3.9.0", 27 | "@vue/cli-service": "^3.9.0", 28 | "@vue/test-utils": "1.0.0-beta.29", 29 | "babel-plugin-import": "^1.12.0", 30 | "chai": "^4.1.2", 31 | "less": "^3.0.4", 32 | "less-loader": "^4.1.0", 33 | "vue-template-compiler": "^2.6.10" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 生鲜商城 10 | 11 | 12 | 13 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /screenshot/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/screenshot/1.png -------------------------------------------------------------------------------- /screenshot/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/screenshot/2.png -------------------------------------------------------------------------------- /screenshot/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/screenshot/3.png -------------------------------------------------------------------------------- /screenshot/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/screenshot/4.png -------------------------------------------------------------------------------- /screenshot/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/screenshot/5.png -------------------------------------------------------------------------------- /screenshot/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/screenshot/6.png -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /src/config/filters.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | // 人民币过滤器 4 | Vue.filter('moneyFormat', (value)=>{ 5 | return '¥' + Number(value).toFixed(2); 6 | }); -------------------------------------------------------------------------------- /src/config/global.js: -------------------------------------------------------------------------------- 1 | 2 | export const showBack = (callback)=>{ 3 | // 1. 变量 4 | let docB = document.documentElement || document.body; 5 | let oldScrollTop, requestFrame; 6 | 7 | // 2. 监听滚动 8 | document.addEventListener('scroll', ()=>{ 9 | showBackFunc(); 10 | }, false); 11 | 12 | // 3. 监听触摸 13 | document.addEventListener('touchstart', ()=>{ 14 | showBackFunc(); 15 | },{passive:true}); 16 | 17 | document.addEventListener('touchmove', ()=>{ 18 | showBackFunc(); 19 | },{passive:true}); 20 | 21 | document.addEventListener('touchend', ()=>{ 22 | oldScrollTop = docB.scrollTop; 23 | moveEnd(); 24 | },{passive:true}); 25 | 26 | const moveEnd = ()=>{ 27 | /* 28 | 屏幕刷新率 60HZ 1000 / 60 = 16.7ms 29 | 屏幕刷新率 75HZ 1000 / 75 = 13.3ms 30 | 31 | 1) 函数节流 32 | */ 33 | requestFrame = requestAnimationFrame(()=>{ 34 | if(docB.scrollTop !== oldScrollTop){ 35 | oldScrollTop = docB.scrollTop; 36 | moveEnd(); 37 | }else { 38 | cancelAnimationFrame(requestFrame); 39 | } 40 | showBackFunc(); 41 | }); 42 | }; 43 | 44 | 45 | // 判断是否到达目标点 46 | const showBackFunc = ()=>{ 47 | // console.log(docB.scrollTop); 48 | if(docB.scrollTop >= 200){ 49 | callback(true); 50 | }else { 51 | callback(false); 52 | } 53 | } 54 | }; 55 | 56 | /** 57 | * 获取style样式 58 | */ 59 | export const getStyle = (element, attr, NumberMode = 'int') => { 60 | let target; 61 | // scrollTop 获取方式不同,没有它不属于style,而且只有document.body才能用 62 | if (attr === 'scrollTop') { 63 | target = element.scrollTop; 64 | }else if(element.currentStyle){ 65 | target = element.currentStyle[attr]; 66 | }else{ 67 | target = document.defaultView.getComputedStyle(element,null)[attr]; 68 | } 69 | //在获取 opactiy 时需要获取小数 parseFloat 70 | return NumberMode === 'float' ? parseFloat(target) : parseInt(target); 71 | }; 72 | 73 | 74 | /** 75 | * 运动效果 76 | * @param {HTMLElement} element 运动对象,必选 77 | * @param {JSON} target 属性:目标值,必选 78 | * @param {number} duration 运动时间,可选 79 | * @param {string} mode 运动模式,可选 80 | * @param {function} callback 可选,回调函数,链式动画 81 | */ 82 | export const animate = (element, target, duration = 400, mode = 'ease-out', callback) => { 83 | clearInterval(element.timer); 84 | //判断不同参数的情况 85 | if (duration instanceof Function) { 86 | callback = duration; 87 | duration = 400; 88 | }else if(duration instanceof String){ 89 | mode = duration; 90 | duration = 400; 91 | } 92 | 93 | //判断不同参数的情况 94 | if (mode instanceof Function) { 95 | callback = mode; 96 | mode = 'ease-out'; 97 | } 98 | 99 | //获取dom样式 100 | const attrStyle = attr => { 101 | if (attr === "opacity") { 102 | return Math.round(getStyle(element, attr, 'float') * 100); 103 | } else { 104 | return getStyle(element, attr); 105 | } 106 | }; 107 | //根字体大小,需要从此将 rem 改成 px 进行运算 108 | const rootSize = parseFloat(document.documentElement.style.fontSize); 109 | 110 | const unit = {}; 111 | const initState = {}; 112 | 113 | //获取目标属性单位和初始样式值 114 | Object.keys(target).forEach(attr => { 115 | if (/[^\d^\.]+/gi.test(target[attr])) { 116 | unit[attr] = target[attr].match(/[^\d^\.]+/gi)[0] || 'px'; 117 | }else{ 118 | unit[attr] = 'px'; 119 | } 120 | initState[attr] = attrStyle(attr); 121 | }); 122 | 123 | //去掉传入的后缀单位 124 | Object.keys(target).forEach(attr => { 125 | if (unit[attr] === 'rem') { 126 | target[attr] = Math.ceil(parseInt(target[attr])*rootSize); 127 | }else{ 128 | target[attr] = parseInt(target[attr]); 129 | } 130 | }); 131 | 132 | 133 | let flag = true; //假设所有运动到达终点 134 | const remberSpeed = {};//记录上一个速度值,在ease-in模式下需要用到 135 | element.timer = setInterval(() => { 136 | Object.keys(target).forEach(attr => { 137 | let iSpeed = 0; //步长 138 | let status = false; //是否仍需运动 139 | let iCurrent = attrStyle(attr) || 0; //当前元素属性址 140 | let speedBase = 0; //目标点需要减去的基础值,三种运动状态的值都不同 141 | let intervalTime; //将目标值分为多少步执行,数值越大,步长越小,运动时间越长 142 | switch(mode){ 143 | case 'ease-out': 144 | speedBase = iCurrent; 145 | intervalTime = duration*5/400; 146 | break; 147 | case 'linear': 148 | speedBase = initState[attr]; 149 | intervalTime = duration*20/400; 150 | break; 151 | case 'ease-in': 152 | let oldspeed = remberSpeed[attr] || 0; 153 | iSpeed = oldspeed + (target[attr] - initState[attr])/duration; 154 | remberSpeed[attr] = iSpeed; 155 | break; 156 | default: 157 | speedBase = iCurrent; 158 | intervalTime = duration*5/400; 159 | } 160 | if (mode !== 'ease-in') { 161 | iSpeed = (target[attr] - speedBase) / intervalTime; 162 | iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed); 163 | } 164 | //判断是否达步长之内的误差距离,如果到达说明到达目标点 165 | switch(mode){ 166 | case 'ease-out': 167 | status = iCurrent !== target[attr]; 168 | break; 169 | case 'linear': 170 | status = Math.abs(Math.abs(iCurrent) - Math.abs(target[attr])) > Math.abs(iSpeed); 171 | break; 172 | case 'ease-in': 173 | status = Math.abs(Math.abs(iCurrent) - Math.abs(target[attr])) > Math.abs(iSpeed); 174 | break; 175 | default: 176 | status = iCurrent !== target[attr]; 177 | } 178 | 179 | if (status) { 180 | flag = false; 181 | //opacity 和 scrollTop 需要特殊处理 182 | if (attr === "opacity") { 183 | element.style.filter = "alpha(opacity:" + (iCurrent + iSpeed) + ")"; 184 | element.style.opacity = (iCurrent + iSpeed) / 100; 185 | } else if (attr === 'scrollTop') { 186 | element.scrollTop = iCurrent + iSpeed; 187 | }else{ 188 | element.style[attr] = iCurrent + iSpeed + 'px'; 189 | } 190 | } else { 191 | flag = true; 192 | } 193 | 194 | if (flag) { 195 | clearInterval(element.timer); 196 | if (callback) { 197 | callback(); 198 | } 199 | } 200 | }) 201 | }, 20); 202 | }; 203 | 204 | /* 205 | 本地化存储 206 | */ 207 | export const setStore = (name, content) =>{ 208 | if(!name) return; 209 | if(typeof content !== 'string'){ 210 | content = JSON.stringify(content); 211 | } 212 | window.localStorage.setItem(name, content); 213 | }; 214 | 215 | /* 216 | 本地化获取 217 | */ 218 | export const getStore = (name)=>{ 219 | if(!name) return; 220 | return window.localStorage.getItem(name); 221 | }; 222 | 223 | /* 224 | 本地化删除 225 | */ 226 | export const removeStore = (name)=>{ 227 | if(!name) return; 228 | return window.localStorage.removeItem(name); 229 | }; -------------------------------------------------------------------------------- /src/config/rem.js: -------------------------------------------------------------------------------- 1 | (function (doc, win) { 2 | var docEl = doc.documentElement, 3 | resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize', 4 | recalc = function() { 5 | var clientWidth = docEl.clientWidth; 6 | if (!clientWidth) return; 7 | docEl.style.fontSize = 15 * (clientWidth / 320) + 'px'; 8 | }; 9 | if (!doc.addEventListener) return; 10 | win.addEventListener(resizeEvt, recalc, false); 11 | doc.addEventListener('DOMContentLoaded', recalc, false); 12 | })(document, window); -------------------------------------------------------------------------------- /src/images/tabbar/category_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/src/images/tabbar/category_default.png -------------------------------------------------------------------------------- /src/images/tabbar/category_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/src/images/tabbar/category_selected.png -------------------------------------------------------------------------------- /src/images/tabbar/home_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/src/images/tabbar/home_default.png -------------------------------------------------------------------------------- /src/images/tabbar/home_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/src/images/tabbar/home_selected.png -------------------------------------------------------------------------------- /src/images/tabbar/mine_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/src/images/tabbar/mine_default.png -------------------------------------------------------------------------------- /src/images/tabbar/mine_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/src/images/tabbar/mine_selected.png -------------------------------------------------------------------------------- /src/images/tabbar/shoppingcart_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/src/images/tabbar/shoppingcart_default.png -------------------------------------------------------------------------------- /src/images/tabbar/shoppingcart_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/src/images/tabbar/shoppingcart_selected.png -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router/index' 4 | import store from './store/index' 5 | 6 | // 1. 引入fastClick 7 | import FastClick from 'fastclick' 8 | if ('addEventListener' in document) { 9 | document.addEventListener('DOMContentLoaded', function() { 10 | FastClick.attach(document.body); 11 | }, false); 12 | } 13 | 14 | // 2. 引入全局的样式 15 | import '@/style/common.less' 16 | 17 | // 3. 引入全局UI组件库-vant 18 | import '@/plugins/vant' 19 | 20 | // 4. 引入rem 21 | import '@/config/rem.js' 22 | 23 | // 5. 引入全局过滤器 24 | import '@/config/filters' 25 | 26 | // 6. 配置二维码插件 27 | import VueQriously from 'vue-qriously' 28 | Vue.use(VueQriously); 29 | 30 | new Vue({ 31 | router, 32 | store, 33 | render: h => h(App) 34 | }).$mount('#app'); 35 | -------------------------------------------------------------------------------- /src/plugins/vant.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | // 1. 底部的导航 4 | import { Tabbar, TabbarItem } from 'vant'; 5 | Vue.use(Tabbar).use(TabbarItem); 6 | 7 | // 2. Loading 8 | import { Loading } from 'vant'; 9 | Vue.use(Loading); 10 | 11 | //3. 图片懒加载 12 | import { Image } from 'vant'; 13 | Vue.use(Image); 14 | 15 | // 4. 提示 16 | import { Toast } from 'vant'; 17 | Vue.use(Toast); 18 | 19 | // 5. 蒙版提示 20 | import { Dialog } from 'vant'; 21 | Vue.use(Dialog); 22 | 23 | // 6. 导航栏 24 | import { NavBar } from 'vant'; 25 | Vue.use(NavBar); 26 | 27 | // 7. 地址 28 | import { ContactCard, ContactList, ContactEdit } from 'vant'; 29 | Vue.use(ContactCard).use(ContactList).use(ContactEdit); 30 | 31 | import { AddressList } from 'vant'; 32 | Vue.use(AddressList); 33 | 34 | import { AddressEdit } from 'vant'; 35 | Vue.use(AddressEdit); 36 | 37 | // 8. 单元格 38 | import { Cell, CellGroup } from 'vant'; 39 | Vue.use(Cell).use(CellGroup); 40 | 41 | // 9. 提交订单 42 | import { SubmitBar } from 'vant'; 43 | Vue.use(SubmitBar); 44 | 45 | // 10. 宫格 46 | import { Grid, GridItem } from 'vant'; 47 | Vue.use(Grid).use(GridItem); 48 | 49 | // 11. 弹出层 50 | import { Popup } from 'vant'; 51 | Vue.use(Popup); 52 | 53 | // 12. 日期组件 54 | import { DatetimePicker } from 'vant'; 55 | Vue.use(DatetimePicker); 56 | 57 | // 13. 卡片选项 58 | import { Card } from 'vant'; 59 | Vue.use(Card); 60 | 61 | // 14. 选项卡 62 | import { Tab, Tabs } from 'vant'; 63 | Vue.use(Tab).use(Tabs); -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | 4 | // 引入一级组件 5 | import DashBoard from './../views/dashboard/DashBoard' 6 | const Home = ()=> import('./../views/home/Home.vue'); 7 | const Category = ()=> import('./../views/category/Category.vue'); 8 | const Cart = ()=> import('./../views/cart/Cart.vue'); 9 | 10 | // 用户中心 11 | const Mine = ()=> import('./../views/mine/Mine.vue'); 12 | const UserCenter = ()=> import('./../views/mine/children/UserCenter'); 13 | const MineOrder = ()=> import('./../views/mine/children/MineOrder'); 14 | 15 | // 引入组件相关 16 | const Order = ()=> import('./../views/order/Order.vue'); 17 | const OrderDetail = ()=> import('./../views/order/children/OrderDetail.vue'); 18 | const MyAddress = ()=> import('./../views/order/children/MyAddress.vue'); 19 | const AddAddress = ()=> import('./../views/order/children/children/AddAddress.vue'); 20 | const EditAddress = ()=> import('./../views/order/children/children/EditAddress.vue'); 21 | 22 | // 引入登录 23 | const Login = ()=> import('./../views/login/Login.vue'); 24 | 25 | 26 | Vue.use(Router); 27 | 28 | export default new Router({ 29 | routes: [ 30 | {path: '/', redirect: '/dashboard'}, 31 | { 32 | path: '/dashboard', 33 | name: 'dashboard', 34 | component: DashBoard, 35 | children: [ 36 | {path: '/dashboard', redirect: '/dashboard/home'}, 37 | {path: 'home', name:'home', component: Home, meta: { keepAlive: true}}, 38 | {path: 'category', name:'category', component: Category, meta: { keepAlive: true}}, 39 | {path: 'cart', name:'cart', component: Cart}, 40 | { 41 | path: 'mine', 42 | name:'mine', 43 | component: Mine, 44 | children: [ 45 | {path: 'userCenter', component: UserCenter}, // 用户中心 46 | {path: 'mineOrder', component: MineOrder} // 我的订单 47 | ] 48 | } 49 | ] 50 | }, 51 | { 52 | path: '/confirmOrder', 53 | name: 'order', 54 | component: Order, 55 | children: [ 56 | { 57 | path:'myAddress', 58 | name: 'myAddress', 59 | component: MyAddress, 60 | children: [ 61 | // 添加地址 62 | {path: 'addAddress', name:'addAddress', component: AddAddress}, 63 | {path: 'editAddress', name:'editAddress', component: EditAddress}, 64 | ] 65 | }, 66 | { 67 | path: 'orderDetail', 68 | name: 'orderDetail', 69 | component: OrderDetail 70 | } 71 | ] 72 | }, 73 | {path: '/login', name: 'login', component: Login} 74 | ] 75 | }); -------------------------------------------------------------------------------- /src/service/api/ajax.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | export default function ajax(url = '', params = {}, type = 'GET') { 4 | // 1. 变量 5 | let promise; 6 | 7 | // 2. 返回一个promise对象 8 | return new Promise((resolve, reject) => { 9 | // 2.1 判断请求的类型 10 | if (type.toUpperCase() === 'GET') { // get请求 11 | // 2.2 拼接字符串 12 | let paramsStr = ''; 13 | // 2.3 遍历 14 | Object.keys(params).forEach(key => { 15 | paramsStr += key + '=' + params[key] + '&'; 16 | }); 17 | // 2.4 过滤最后的& 18 | /* 19 | 注意:为了防止请求缓存,在尾部加了时间戳 20 | */ 21 | if (paramsStr) { 22 | paramsStr = paramsStr.substr(0, paramsStr.lastIndexOf('&')); 23 | // 2.5 拼接完整路径 24 | if(url.indexOf('47.98.157.152') === -1) { 25 | url += '?' + paramsStr + '&itlike=' + randomCode(20); 26 | }else { 27 | url += '?' + paramsStr; 28 | } 29 | }else { 30 | if(url.indexOf('47.98.157.152') === -1){ 31 | url += '?itlike=' + randomCode(20) 32 | } 33 | } 34 | // 2.6 发起get请求 35 | promise = axios.get(url); 36 | } else if (type.toUpperCase() === 'POST') { // post请求 37 | // 2.7 发起post请求 38 | promise = axios.post(url, params); 39 | } 40 | // 2.8 处理结果并返回 41 | promise.then((response) => { 42 | resolve(response.data); 43 | }).catch(error => { 44 | reject(error); 45 | }) 46 | }); 47 | 48 | } 49 | 50 | /*生成指定长度的随机数*/ 51 | function randomCode(length) { 52 | let chars = ['0','1','2','3','4','5','6','7','8','9']; 53 | let result = ""; 54 | for(let i = 0; i < length ; i ++) { 55 | let index = Math.ceil(Math.random()*9); 56 | result += chars[index]; 57 | } 58 | return result; 59 | } -------------------------------------------------------------------------------- /src/service/api/index.js: -------------------------------------------------------------------------------- 1 | import ajax from './ajax' 2 | 3 | 4 | // 1. 定义基础路径 5 | const BASE_URL = 'http://demo.itlike.com/web/xlmc'; 6 | 7 | export const getHomeData = () => ajax(BASE_URL + '/api/homeApi'); 8 | export const getCategories = () => ajax(BASE_URL + '/api/homeApi/categories'); 9 | export const getCategoriesDetail = (preParams) => ajax(BASE_URL + '/api/homeApi/categoriesdetail' + preParams); 10 | 11 | 12 | 13 | 14 | 15 | // 2. 用户中心接口 16 | // http://localhost:3000/web/xlmc/api/send_code 17 | const LOCAL_BASE_URL = 'http://demo.itlike.com/web/xlmc'; 18 | // const LOCAL_BASE_URL = '/api'; 19 | 20 | // 2.1 获取短信验证码(GET) 21 | export const getPhoneCode = (phone) => ajax(LOCAL_BASE_URL + '/api/send_code', { phone }); 22 | // 2.2 手机验证码登录(POST) 23 | export const phoneCodeLogin = (phone, code) => ajax(LOCAL_BASE_URL + '/api/login_code', { phone, code }, 'POST'); 24 | // 2.3 用户名和密码登录(POST) 25 | export const pwdLogin = (user_name, user_pwd, captcha) => ajax(LOCAL_BASE_URL + '/api/login_pwd', { user_name, user_pwd, captcha }, 'POST'); 26 | // 2.4 自动登录 27 | export const getUserInfo = () => ajax(LOCAL_BASE_URL + '/api/userinfo'); 28 | // 2.5 退出登录 29 | export const getLogOut = () => ajax(LOCAL_BASE_URL + '/api/logout'); 30 | 31 | 32 | 33 | 34 | // 3. 购物车接口 35 | /* 36 | 3.1 添加商品 37 | 请求方式:POST 38 | 参数:{goods_id: String, goods_name: String, goods_price: Number, small_image: String} 39 | */ 40 | export const addGoodsToCart = (user_id, goods_id, goods_name, goods_price, small_image) => ajax(LOCAL_BASE_URL + '/api/cart/add', { user_id, goods_id, goods_name, goods_price, small_image }, 'POST'); 41 | 42 | // 3.2 获取当前用户购物车中的商品 43 | export const getGoodsCart = (user_id) => ajax(LOCAL_BASE_URL + '/api/cart/search/' + user_id); 44 | 45 | // 3.3 修改购物车商品数量 46 | export const changeCartNum = (user_id, goods_id, type) => ajax(LOCAL_BASE_URL + '/api/cart/num', { user_id, goods_id, type }, 'POST'); 47 | 48 | // 3.4 删除当前用户购物车中所有的商品 49 | export const clearAllCart = (user_id) => ajax(LOCAL_BASE_URL + '/api/cart/clear/' + user_id); 50 | 51 | // 3.5 单个商品的选中和取消选中 52 | export const singerGoodsSelect = (user_id, goods_id) => ajax(LOCAL_BASE_URL + '/api/cart/singer_select', { user_id, goods_id }, 'POST'); 53 | 54 | // 3.6 所有商品的选中和取消选中 55 | export const allGoodsSelect = (user_id, flag) => ajax(LOCAL_BASE_URL + '/api/cart/all_select', { user_id, flag }, 'POST'); 56 | 57 | // 3.7 查询所有已经被选中的商品 58 | export const getAllSelectedGoods = (user_id) => ajax(LOCAL_BASE_URL + '/api/cart/selected/' + user_id); 59 | 60 | // 3.8 删除已经生成订单的商品 61 | export const delAllSelectedGoods = (user_id) => ajax(LOCAL_BASE_URL + '/api/cart/del_checked/' + user_id); 62 | 63 | 64 | // 4. 地址接口 65 | 66 | // 4.1 获取当前用户的地址 67 | export const getUserAddress = (user_id) => ajax(LOCAL_BASE_URL + '/api/address/search/' + user_id); 68 | 69 | // 4.2 添加新的地址 70 | export const addUserAddress = (user_id, address_name, address_phone, address_area, address_area_detail, address_post_code, address_tag, province, city, county, areaCode) => ajax(LOCAL_BASE_URL + '/api/address/add', { user_id, address_name, address_phone, address_area, address_area_detail, address_post_code, address_tag, province, city, county, areaCode }, 'POST'); 71 | 72 | // 4.3 编辑用户的地址 73 | export const changeUserAddress = (address_id, user_id, address_name, address_phone, address_area, address_area_detail, address_post_code, address_tag, province, city, county, areaCode) => ajax(LOCAL_BASE_URL + '/api/address/edit', { address_id, user_id, address_name, address_phone, address_area, address_area_detail, address_post_code, address_tag, province, city, county, areaCode }, 'POST'); 74 | 75 | // 4.4 删除用户的地址 76 | export const delUserAddress = (address_id) => ajax(LOCAL_BASE_URL + '/api/address/del/' + address_id); 77 | 78 | // 4.5 获取单条地址 79 | export const getCurrentUserAddress = (user_id, address_id) => ajax(LOCAL_BASE_URL + '/api/address/one', { user_id, address_id }, 'POST'); 80 | 81 | // 5. 订单接口 82 | // 5.1 提交订单 83 | export const postOrder = (user_id, address_id, arrive_time, cart_shop, notice, shop_price, dis_price) => ajax(LOCAL_BASE_URL + '/api/order/post', { user_id, address_id, arrive_time, cart_shop, notice, shop_price, dis_price }, 'POST'); 84 | 85 | // 5.2 订单支付成功 86 | export const orderPaySuccess = (user_id, order_id) => ajax(LOCAL_BASE_URL + '/api/order/change_status', { user_id, order_id }, 'POST'); 87 | 88 | // 5.3 查询订单 89 | export const getOrder = (user_id, status) => ajax(LOCAL_BASE_URL + '/api/order/get', { user_id, status }, 'POST'); // pay will 90 | 91 | 92 | // 6. 微信支付接口部署 93 | 94 | const PAY_URL = 'http://47.98.157.152/WXPayProject/pay'; 95 | // const PAY_URL = '/pay'; 96 | // 6.1 获取支付的URL 97 | export const getWXCode = (outTradeNo, totalFee) => ajax(PAY_URL + '/createNative.do', { outTradeNo, totalFee }); 98 | // 6.2 查询是否支付成功 99 | export const queryPayStatus = (out_trade_no) => ajax(PAY_URL + '/queryPayStatus.do', { out_trade_no }); -------------------------------------------------------------------------------- /src/store/actions.js: -------------------------------------------------------------------------------- 1 | import {USER_INFO} from './mutations-type' 2 | import {getStore} from './../config/global' 3 | import {getUserInfo} from './../service/api/index' 4 | 5 | export default { 6 | // 1. 同步用户信息 7 | syncUserInfo({commit}, userInfo) { 8 | commit(USER_INFO, {userInfo}) 9 | }, 10 | 11 | // 2. 自动登录 12 | async reqUserInfo({commit}){ 13 | // 2.1 从本地获取数据 14 | let userInfo = JSON.parse(getStore('userInfo')); 15 | if(userInfo){ 16 | commit(USER_INFO, {userInfo}); 17 | }else { 18 | // 2.2 从服务器端验证 19 | let result = await getUserInfo(); 20 | // console.log(result); 21 | if(200 === result.success_code){ 22 | commit(USER_INFO, {userInfo: result.data}); 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /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 | 8 | Vue.use(Vuex); 9 | 10 | export default new Vuex.Store({ 11 | state, 12 | mutations, 13 | actions 14 | }) -------------------------------------------------------------------------------- /src/store/mutations-type.js: -------------------------------------------------------------------------------- 1 | export const ADD_GOODS = 'ADD_GOODS'; // 添加商品到购物车 2 | export const INIT_SHOP_CART = 'INIT_SHOP_CART'; //初始化购物车 3 | export const REDUCE_CART = 'REDUCE_CART'; // 把商品移出购物车 4 | export const SELECTED_SINGER_GOODS = 'SELECTED_SINGER_GOODS'; // 单个商品的选中和取消选中 5 | export const SELECTED_All_GOODS = 'SELECTED_All_GOODS'; // 全选和取消全选 6 | export const CLEAR_CART = 'CLEAR_CART'; // 清空购物车 7 | 8 | export const USER_INFO = 'USER_INFO'; // 保存用户信息 9 | export const INIT_USER_INFO = 'INIT_USER_INFO'; // 初始化用户数据 10 | export const RESET_USER_INFO = 'RESET_USER_INFO'; // 重置用户数据 11 | 12 | -------------------------------------------------------------------------------- /src/store/mutations.js: -------------------------------------------------------------------------------- 1 | import { 2 | ADD_GOODS, 3 | INIT_SHOP_CART, 4 | REDUCE_CART, 5 | SELECTED_SINGER_GOODS, 6 | SELECTED_All_GOODS, 7 | CLEAR_CART, 8 | USER_INFO, 9 | INIT_USER_INFO, 10 | RESET_USER_INFO 11 | } from './mutations-type' 12 | 13 | import { getStore, removeStore, setStore } from './../config/global' 14 | import Vue from 'vue' 15 | 16 | export default { 17 | // 1. 往购物车中添加数据 18 | [ADD_GOODS](state, { goodsId, goodsName, smallImage, goodsPrice }) { 19 | let shopCart = state.shopCart; 20 | // 1.1 判断商品是否存在 21 | if (shopCart[goodsId]) { // 存在 22 | shopCart[goodsId]['num']++; 23 | } else { // 不存在 24 | shopCart[goodsId] = { 25 | "num": 1, 26 | "id": goodsId, 27 | "name": goodsName, 28 | "small_image": smallImage, 29 | "price": goodsPrice, 30 | "checked": true 31 | } 32 | } 33 | // 1.2 产生新对象 34 | state.shopCart = {...shopCart }; 35 | // 1.3 存入本地 36 | setStore('shopCart', state.shopCart); 37 | }, 38 | 39 | // 2. 页面初始化,获取购物车的数据(本地) 40 | [INIT_SHOP_CART](state) { 41 | let initCart = getStore('shopCart'); 42 | if (initCart) { 43 | state.shopCart = JSON.parse(initCart); 44 | } 45 | }, 46 | 47 | // 3. 把商品移出购物车 48 | [REDUCE_CART](state, { goodsId }) { 49 | let shopCart = state.shopCart; 50 | let goods = shopCart[goodsId]; 51 | if (goods) { // 找到该商品 52 | if (goods['num'] > 0) { 53 | goods['num']--; 54 | // 3.1 判断是否只有0个 55 | if (goods['num'] === 0) { 56 | delete shopCart[goodsId]; 57 | } 58 | } else { 59 | goods = null; 60 | } 61 | // 3.2 同时数据 62 | state.shopCart = {...shopCart }; 63 | setStore('shopCart', state.shopCart); 64 | } 65 | }, 66 | 67 | // 4. 单个商品选中和取消选中 68 | [SELECTED_SINGER_GOODS](state, { goodsId }) { 69 | let shopCart = state.shopCart; 70 | let goods = shopCart[goodsId]; 71 | if (goods) { 72 | if (goods.checked) { // 存在该属性 73 | goods.checked = !goods.checked; 74 | } else { 75 | Vue.set(goods, 'checked', true); 76 | // goods.checked = true; 77 | } 78 | // 4.1 同时数据 79 | state.shopCart = {...shopCart }; 80 | setStore('shopCart', state.shopCart); 81 | } 82 | }, 83 | 84 | // 5. 所有商品选中和取消选中 85 | [SELECTED_All_GOODS](state, { isSelected }) { 86 | let shopCart = state.shopCart; 87 | Object.values(shopCart).forEach((goods, index) => { 88 | if (goods.checked) { // 存在该属性 89 | goods.checked = !isSelected; 90 | } else { 91 | Vue.set(goods, 'checked', !isSelected); 92 | } 93 | }); 94 | state.shopCart = {...shopCart }; 95 | }, 96 | 97 | // 6. 清空购物车 98 | [CLEAR_CART](state) { 99 | state.shopCart = null; 100 | state.shopCart = {...state.shopCart }; 101 | setStore('shopCart', state.shopCart); 102 | }, 103 | 104 | // 7. 保存用户信息报本地 105 | [USER_INFO](state, { userInfo }) { 106 | state.userInfo = userInfo; 107 | setStore('userInfo', state.userInfo); 108 | }, 109 | 110 | // 8. 获取用户信息 111 | [INIT_USER_INFO](state) { 112 | // 8.1 获取用户信息 113 | let userInfo = getStore('userInfo'); 114 | // 8.2 判断 115 | if (userInfo) { 116 | state.userInfo = JSON.parse(userInfo); 117 | } 118 | }, 119 | 120 | // 9. 退出登录 121 | [RESET_USER_INFO](state) { 122 | state.userInfo = {}; 123 | removeStore('userInfo'); 124 | } 125 | } -------------------------------------------------------------------------------- /src/store/state.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // 购物车的商品 3 | shopCart: {}, 4 | // 用户信息 5 | userInfo: {} 6 | } -------------------------------------------------------------------------------- /src/style/common.less: -------------------------------------------------------------------------------- 1 | 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{ 2 | padding: 0; 3 | margin: 0; 4 | list-style: none; 5 | font-style: normal; 6 | text-decoration: none; 7 | border: none; 8 | font-weight: normal; 9 | font-family: "Microsoft Yahei", serif; 10 | box-sizing: border-box; 11 | -webkit-tap-highlight-color:transparent; 12 | -webkit-font-smoothing: antialiased; 13 | &:hover{ 14 | outline: none; 15 | } 16 | } 17 | 18 | html,body{ 19 | height: 100%; 20 | width: 100%; 21 | background-color: #F5F5F5; 22 | } 23 | 24 | /* 根据dpr显示2x图/3x图 */ 25 | .bg-image(@url){ 26 | background-image:~"url('@{url}@2x.png')"; 27 | @media (-webkit-min-device-pixel-ratio: 3),(min-device-pixel-ratio: 3){ 28 | background-image:~"url('@{url}@3x.png')"; 29 | } 30 | } 31 | 32 | input,textarea { 33 | border: 0; 34 | -webkit-appearance: none; 35 | } -------------------------------------------------------------------------------- /src/views/cart/Cart.vue: -------------------------------------------------------------------------------- 1 | 62 | 63 | 209 | 210 | -------------------------------------------------------------------------------- /src/views/cart/images/detail1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/src/views/cart/images/detail1.jpg -------------------------------------------------------------------------------- /src/views/cart/images/shop-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/src/views/cart/images/shop-icon.png -------------------------------------------------------------------------------- /src/views/category/Category.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 126 | 127 | -------------------------------------------------------------------------------- /src/views/category/components/ContentView.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 25 | 26 | -------------------------------------------------------------------------------- /src/views/category/components/Header.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /src/views/category/components/ProductItem.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 93 | 94 | -------------------------------------------------------------------------------- /src/views/dashboard/DashBoard.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 119 | 120 | -------------------------------------------------------------------------------- /src/views/home/Home.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 150 | 151 | -------------------------------------------------------------------------------- /src/views/home/components/flashSale/FlashSale.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 31 | 32 | -------------------------------------------------------------------------------- /src/views/home/components/flashSale/FlashSaleItem.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 48 | 49 | -------------------------------------------------------------------------------- /src/views/home/components/header/Header.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 60 | 61 | -------------------------------------------------------------------------------- /src/views/home/components/markPage/MarkPage.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 21 | 22 | -------------------------------------------------------------------------------- /src/views/home/components/nav/Nav.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 22 | 23 | -------------------------------------------------------------------------------- /src/views/home/components/sowing/Sowing.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 61 | 62 | -------------------------------------------------------------------------------- /src/views/home/components/youLike/YouLike.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 29 | 30 | -------------------------------------------------------------------------------- /src/views/home/components/youLike/YouLikeItem.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 65 | 66 | -------------------------------------------------------------------------------- /src/views/login/Login.vue: -------------------------------------------------------------------------------- 1 | 69 | 70 | 218 | 219 | -------------------------------------------------------------------------------- /src/views/login/SelectLogin.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /src/views/login/images/captcha.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 10 | 12 | 13 | -------------------------------------------------------------------------------- /src/views/login/images/hide_pwd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/src/views/login/images/hide_pwd.png -------------------------------------------------------------------------------- /src/views/login/images/logo_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/src/views/login/images/logo_bg.png -------------------------------------------------------------------------------- /src/views/login/images/show_pwd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/src/views/login/images/show_pwd.png -------------------------------------------------------------------------------- /src/views/mine/Mine.vue: -------------------------------------------------------------------------------- 1 | 70 | 71 | 96 | 97 | -------------------------------------------------------------------------------- /src/views/mine/children/MineOrder.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 95 | 96 | -------------------------------------------------------------------------------- /src/views/mine/children/UserCenter.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 41 | 42 | -------------------------------------------------------------------------------- /src/views/mine/children/components/MineOrderItem.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 26 | 27 | -------------------------------------------------------------------------------- /src/views/mine/images/avator.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/src/views/mine/images/avator.jpg -------------------------------------------------------------------------------- /src/views/order/Order.vue: -------------------------------------------------------------------------------- 1 | 78 | 79 | 283 | 284 | -------------------------------------------------------------------------------- /src/views/order/children/MyAddress.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 121 | 122 | -------------------------------------------------------------------------------- /src/views/order/children/OrderDetail.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 50 | 51 | -------------------------------------------------------------------------------- /src/views/order/children/children/AddAddress.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 84 | 85 | -------------------------------------------------------------------------------- /src/views/order/children/children/EditAddress.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 121 | 122 | -------------------------------------------------------------------------------- /src/views/order/images/detail1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/src/views/order/images/detail1.jpg -------------------------------------------------------------------------------- /src/views/order/images/shop-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LRboyz/vue-shop/c3a1fdfa9c47b23587072d160ce752102c7fc743/src/views/order/images/shop-icon.png -------------------------------------------------------------------------------- /tests/unit/example.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai' 2 | import { shallowMount } from '@vue/test-utils' 3 | import HelloWorld from '@/components/HelloWorld.vue' 4 | 5 | describe('HelloWorld.vue', () => { 6 | it('renders props.msg when passed', () => { 7 | const msg = 'new message' 8 | const wrapper = shallowMount(HelloWorld, { 9 | propsData: { msg } 10 | }) 11 | expect(wrapper.text()).to.include(msg) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | publicPath: '/', 3 | devServer: { 4 | proxy: { 5 | // '/api': { 6 | // target: 'http://lrboy.live', 7 | // changeOrigin: true, 8 | // pathRewrite: { 9 | // '^/api': '' 10 | // } 11 | // }, 12 | } 13 | } 14 | }; --------------------------------------------------------------------------------