# mimall **Repository Path**: yx102/mimall ## Basic Information - **Project Name**: mimall - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-07-10 - **Last Updated**: 2020-12-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # mimall ## Project setup ``` npm install ``` ### Compiles and hot-reloads for development ``` npm run serve ``` ### Compiles and minifies for production ``` npm run build ``` ## 配置全局变量 在`vue.config.js`文件中配置`style-resource`全局变量 下载: ``` npm i style-resources-loader -D ``` 配置:注【全局变量less_reset文件不能使用config文件中的配置,没有设置为全局文件可以使用】 ``` const path = require('path') module.exports = { chainWebpack: config => { const types = ['vue-modules', 'vue', 'normal-modules', 'normal'] types.forEach(type => addStyleResource(config.module.rule('less').oneOf(type))) // 删除预加载的路由 config.plugins.delete('prefetch') }, } function addStyleResource (rule) { rule.use('style-resource') .loader('style-resources-loader') .options({ patterns: [ // 在此处添加less的mixin和变量 path.resolve(__dirname, './src/assets/less/less_reset.less'), path.resolve(__dirname, './src/assets/less/config.less') ], }) } ``` 方案二:配置局部全局变量,使用的时候每个页面引入 ``` module.exports = { css: { loaderOptions: { less: { // 这里data换成 prependData 并且重启vue项目即可 prependData: [`@import "@/assets/css/base.less";`, `@import "@/assets/css/common.less";`] } } } } ``` ## less函数 ``` .flex(@row:center, @col:center) { display: flex; justify-content: @row; align-items: @col; } 使用 .flex() ``` ## scss函数 ``` @mixin flex($row:center, $col:center){ display: flex; justify-content: $row; align-items: $col; } 使用 @include flex() ``` ul的li平均分布 ```css 方式一: li{ float:left; width:20% } 方式二: ul{ display:flex } li{ flex: 1; } ``` ## 使用技术 ![1594311712631](C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1594311712631.png) 需要掌握的内容 ## navHeader组件 ### logo的切换 a设置width: 110px; a::before{width: 55px;} a::after{width: 55px;} &:hover::before { margin-left: -55px; transition: all 0.5s; } ```html ``` ### 三角形定位 ```html
小米商城App
下载App
``` ### 盒子 悬浮在某一个上面,下面会出现一个大框布局 ```
小米手机
红米手机
电视
``` 竖线 li开启相对定位,通过伪元素定位要实现;去除最后一条也是通过伪元素来实现 ```html
``` ### 悬浮显示隐藏和动画 height ``` .item-menu{ &:hover { .children { height: 220px; transition: all 1s; } } .children { height: 0; overflow: hidden; transition: all 0.5s; } } ``` ### 输入框 `background-size: contain;`让元素充满整个盒子 ### 抽取重复样式 抽取后的样式可以直接使用 ``` .flex(@row:center, @col:center) { display: flex; justify-content: @row; align-items: @col; } 使用 .flex() ``` ## navFooter组件 ## serviceBar组件 ## 配置代理 `vue.config.js`文件配置代理 ```js devServer: { host: 'localhost', port: 8080, proxy: { '/api': { target: 'http://mall-pre.springboot.cn', changeOrigin: true, pathRewrite: { '/api': '' } } } }, ``` `ajax.js`文件封装请求函数 ```js import axios from 'axios' const request = axios.create({ baseURL: '/api', // 超出请求时间设置: timeout: 5000 }) request.interceptors.request.use(function (config) { if (config.headers.needToken) { const token = sessionStorage.getItem('token_key') if (token) { config.headers.Authorization = 'Token ' + token } } return config }, function (error) { return Promise.reject(error); }) // axios接口错误拦截器: request.interceptors.response.use(function (response) { // 获取到接口的返回值 let res = response.data; let path = location.hash; // 只有是0的时候才是成功: if (res.status == 0) { // 成功返回数据: return res.data; // 登录错误的报错码: } else if (res.status == 10) { // 跳转回login并抛出异常: if (path != '/') { window.location.href = '/login'; } return Promise.reject(res) } else { // 弹错其他错误: console.dir(res.msg) return Promise.reject(res); } }, (error) => { let res = error.response; console.dir(res.data.message); return Promise.reject(error); }) export default request ``` 完成navheader组件请求 ``` async getNavHeader () { const result = await reqNavHeaderData('100012') // 只截取前六条数据 if (result.list.length > 6) { this.navHeaderData = result.list.splice(0, 6) } } ``` ## 首页 下载轮播图插件 轮播图和左侧导航栏布局 左侧导航栏通过定位的方式实现压在轮播图上 ```
.nav-menu { position: relative; width: 264px; z-index: 3; .menu-wrap { position: absolute; width: 264px; height: 451px; padding: 26px 0; background-color: #55585a7a; box-sizing: border-box; .menu-item { height: 50px; line-height: 50px; &:hover { background-color: @colorA; } > a { font-size: 16px; color: #fff; padding-left: 30px; display: block; position: relative; &:after { position: absolute; right: 30px; top: 17.5px; content: ''; .bgImg(10px, 15px, '/imgs/icon-arrow.png'); } } } } } ``` 循环二维数组 切割二位数据 【slice不会改变原数组,splice会改变原数组】 ``` // 前六条数据在header.取后八条: res.list=res.list.slice(6,14); // 分割产品到二维数组: this.phoneList=[res.list.slice(0,4),res.list.slice(4,8)]; ``` ## modal做上下动画 ```vue ``` ## btn样式开发 ``` .btn { display: inline-block; width: 110px; height: 30px; line-height: 30px; text-align: center; background-color: #ff6600; color: #ffffff; border: none; cursor: pointer; } // 默认按钮样式: .btn-default { background-color: #b0b0b0; color: #ffffff; border: 1px solid #d7d7d7; } // 大号按钮: .btn-large { width: 202px; height: 50px; line-height: 50px; font-size: 18px; } // 巨大号按钮 .btn-huge { width: 300px; height: 54px; line-height: 54px; font-size: 16px; } // 按钮组: .btn-group { .btn { margin-right: 20px; &:last-child { margin-right: none; } } } 使用:
确定 取消
``` ## 图片懒加载 下载 ``` npm i vue-lazyload -S ``` 引入 ``` import VueLazyLoad from 'vue-lazyload' Vue.use(VueLazyLoad, { loading: '/imgs/loading-svg/loading-bubbles.svg' }) ``` 使用 ``` 动态 静态:需要加引号,否则当成变量解析 ``` ## 登录页面 使用滑块校验:组件slide,存在的bug,出错不会重置 ```vue ``` ## js-cookie保存用户信息 下载 ```js npm install js-cookie --save ``` 引入使用 ```js import Cookies from 'js-cookie' ``` [参考文章](https://www.npmjs.com/package/js-cookie) ## 引入element-ui 按需加载element-ui ```shell npm install babel-plugin-component -D ``` ### 全局注册组件 ```js import Vue from 'vue'; import { form, formItem, Input, Button, Checkbox } from "element-ui"; Vue.use(form); Vue.use(formItem); Vue.use(Input); Vue.use(Button); Vue.use(Checkbox); Vue.use(form); ``` ### 组件局部注册 ```js import { form, formItem, Input, Button, Checkbox } from "element-ui"; components: { [form.name]: form, [formItem.name]: formItem, [Button.name]: Button, [Input.name]: Input, [Checkbox.name]: Checkbox }, ``` ## 表单一行内包含两种内容 ```html
``` ## 导航吸顶 ``` mounted () { window.addEventListener('scroll', this.handleScroll) }, destroyed () { window.removeEventListener('scroll', this.handleScroll, false) }, methods: { handleScroll () { this.$nextTick(() => { // 多版本兼容获取滚动条高度 let scrollTop = document.documentElement.scrollTop || document.body.scrollTop; let offsetTop = this.$refs['navbar'].offsetTop; if (scrollTop > offsetTop) { this.searchBarFixed = true; } else { this.searchBarFixed = false; } }) } } ``` ## 点击弹出视频 top:-50%;opacity:0;transition: all .6s;和 top:50%;opacity:1;是重点 ```

60帧超慢动作摄影
慢慢回味每一瞬间的精彩

后置960帧电影般超慢动作视频,将眨眼间的美妙展现得淋漓尽致!
更能AI 精准分析视频内容,15个场景智能匹配背景音效。

``` ## 退出后刷新登录没有获取到购物车数据 解决办法:在app.vue和navHeader.vue中都发送请求 但是这样会重复发送两次请求,可以在navheader.vue组件添加拦截,只请求登录页面跳转过来数据,login.vue只能用name,用path不会进行请求 `navheader.vue` ``` created () { // 判断是否从登录页面跳转过来,如果是就发送请求,如果不是就不发送请求 let params = this.$route.params if (params && params.from == 'login') { this.getCartCount() } }, ``` `login.vue` ``` this.$router.replace({ name: 'Indexpage', params: { from: 'login' } }) ``` ## cookies会话期间有效 注:会话期间是`expiress`,设置时间用`expires` ``` Cookies.set("uid", uid, { expiress: "Session" }) ``` ## 总结 如果是export const导出,需要用{}来引入 如果是export default 则不需要用{}来引入 ``` export const reqLogin = () => {} import {reqLogin} from './a.js' export default [] import data from './a.js' ``` ## 判断是否有选择地址 ``` let addr = this.addressList[this.checkIndex] if (!addr) { return this.$message.error("请选择一个收货地址") } ``` ## 携带参数跳转到结算页面 ``` const result = await reqSubmitOrder(addr.id) this.$router.push({ path: '/order/pay', query: { orderNo: result.orderNo } }) ``` ## 结算页面 订单展示 有支付宝支付和微信支付两种支付接口 ## 订单列表是否显示 添加变量:showDetail ```

订单详情

订单号:
5190702816411009
data:{ showDetail:true } ``` ## 在js中打开新窗口 ``` window.open('地址', '_blank') window.open('/order/alipay?orderId=' + this.orderNo, '_blank') ``` ## 阿里支付页面布局 ``` ``` ## loading通用组件 ``` > ``` ## 微信生成支付二维码 后台返回一个链接,需要将链接生成二维码 ``` const result = await reqPay(this.orderNo, 'Vue高仿商城', 0.01, 2) // console.log(result) // content: "weixin://wxpay/bizpayurl?pr=kD5oENL" 方式一: QRCode.toDataURL(result.content) .then(url => { this.payImg = url }).catch(() => { this.$message.error("微信二维码生成失败"); }); 方式二: try { this.payImg = await QRCode.toDataURL(result.content) this.payByWechat = true } catch (err) { this.$message.error("微信二维码生成失败,请稍后重试"); } ``` ## 微信轮询支付状态 如果已经支付需要清除定时器,在生成二维码的时候就需要开启轮询状态,检测到支付成功清除定时器,跳转页面;点击关闭二维码弹框也需要清除定时器 ``` 一:轮询 //轮询当前订单支付状态,如果支付完毕则自动关闭(状态20) loopOrderState () { this.timeId = setInterval(async () => { const result = await reqOrderDetail(this.orderNo) if (result.status === 20) { // 清除定时器 clearInterval(this.timeId) // 跳转页面 this.goOrderList() } }, 1000) }, 二:生成二维码时开启轮询【this.loopOrderState()】 async paySubmit (type) { if (type === 1) { this.payInfo = 1 window.open('/order/alipay?orderId=' + this.orderNo, '_blank') } else { this.payInfo = 2 const result = await reqPay(this.orderNo, 'Vue高仿商城', 0.01, 2) try { this.payImg = await QRCode.toDataURL(result.content) this.payByWechat = true this.loopOrderState() } catch (err) { this.$message.error("微信二维码生成失败,请稍后重试"); } } }, 三:关闭二维码弹框清除定时器【clearInterval(this.timeId)】 closeWxpay () { this.payByWechat = false this.showPayModal = true clearInterval(this.timeId) }, ``` ## 修复跳转title不生效bug 由于只是跳转没有刷新页面,在mounted中检测URL只会检测一次,因此需要给每个子组件都添加orderheader而不是在父组件中定义通过mounted来改变,通过this.$router.push()跳转的会检测不到URL的变化,因此会不生效 ## 分页切换 ``` async getOrderList () { this.loading = true const result = await reqOrderList(this.pageNum, this.pageSize) this.orderList = [...this.orderList, ...result.list] this.total = result.total this.loading = false }, LoadingMore () { this.loading = true // 计算总页数 let totalPage = Math.ceil(this.total / this.pageSize) if (this.pageNum >= totalPage) { this.$message.warning('没有更多数据') } else { this.pageNum++ this.getOrderList() } this.loading = false } ``` ``` ```