# 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;
}
```
## 使用技术

需要掌握的内容
## 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
```
订单详情
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
}
```
```
```