微信公众号,授权,券

This commit is contained in:
406803045 2019-06-06 09:17:06 +08:00
parent 6014f11ee4
commit ca44e7447a
29 changed files with 627 additions and 195 deletions

12
.babelrc Normal file
View File

@ -0,0 +1,12 @@
{
"presets": [
[ "@babel/preset-env", {
"targets": {
"browsers": [ "last 1 version", "ie >= 11" ]
}
}]
],
"plugins": [
"@babel/plugin-transform-runtime"
]
}

View File

@ -4,6 +4,7 @@ VUE_APP_ENV = 'staging'
#base url #base url
BASE_URL = https://www.top1buyer.com/ BASE_URL = https://www.top1buyer.com/
#appid #appid
VUE_APP_WECHAT_APPID='wx6bb2125514b4c1ff' VUE_APP_WECHAT_APPID='wx6bb2125514b4c1ff'
# base api # base api
VUE_APP_BASE_API = '/stage-api' VUE_APP_BASE_API = '/stage-api'

View File

@ -1,5 +1,6 @@
process.env.VUE_CLI_BABEL_TRANSPILE_MODULES = true
module.exports = { module.exports = {
presets: ['@vue/app'], presets: [['@vue/app', { useBuiltIns: 'entry' }]],
plugins: [ plugins: [
[ [
'import', 'import',

View File

@ -3,7 +3,7 @@ const chalk = require('chalk')
// const config = require('../vue.config.js') // const config = require('../vue.config.js')
const rawArgv = process.argv.slice(2) const rawArgv = process.argv.slice(2)
const args = rawArgv.join(' ') const args = rawArgv.join(' ')
// npm run preview -- --report
if (process.env.npm_config_preview || rawArgv.includes('--preview')) { if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
const report = rawArgv.includes('--report') const report = rawArgv.includes('--report')

View File

@ -7,7 +7,7 @@
"scripts": { "scripts": {
"dev": "vue-cli-service serve", "dev": "vue-cli-service serve",
"build:prod": "vue-cli-service build", "build:prod": "vue-cli-service build",
"build:stage": "vue-cli-service build --mode staging", "build:sit": "vue-cli-service build --mode staging",
"preview": "node build/index.js --preview", "preview": "node build/index.js --preview",
"lint": "eslint --ext .js,.vue src", "lint": "eslint --ext .js,.vue src",
"test:unit": "jest --clearCache && vue-cli-service test:unit", "test:unit": "jest --clearCache && vue-cli-service test:unit",
@ -15,8 +15,10 @@
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml" "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
}, },
"dependencies": { "dependencies": {
"@babel/polyfill": "^7.4.4",
"@chenfengyuan/vue-qrcode": "^1.0.0", "@chenfengyuan/vue-qrcode": "^1.0.0",
"axios": "0.18.0", "axios": "0.18.0",
"core-js": "^2.6.9",
"crypto-js": "^3.1.9-1", "crypto-js": "^3.1.9-1",
"good-storage": "^1.1.0", "good-storage": "^1.1.0",
"js-cookie": "^2.2.0", "js-cookie": "^2.2.0",
@ -52,6 +54,7 @@
"script-loader": "0.7.2", "script-loader": "0.7.2",
"serve-static": "^1.13.2", "serve-static": "^1.13.2",
"svgo": "1.2.2", "svgo": "1.2.2",
"vconsole": "^2.5.2",
"vue-template-compiler": "2.6.10" "vue-template-compiler": "2.6.10"
}, },
"engines": { "engines": {

27
public/index.1.html Normal file
View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<% for (var i in
htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
<% } %>
<title><%= webpackConfig.name %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= webpackConfig.name %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- 使用CDN加速的JS文件配置在vue.config.js下 -->
<% for (var i in
htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
<!-- built files will be auto injected -->
</body>
</html>

View File

@ -5,11 +5,6 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> <link rel="icon" href="<%= BASE_URL %>favicon.ico">
<% for (var i in
htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
<% } %>
<title><%= webpackConfig.name %></title> <title><%= webpackConfig.name %></title>
</head> </head>
<body> <body>
@ -18,10 +13,6 @@
</noscript> </noscript>
<div id="app"></div> <div id="app"></div>
<!-- 使用CDN加速的JS文件配置在vue.config.js下 --> <!-- 使用CDN加速的JS文件配置在vue.config.js下 -->
<% for (var i in
htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
<!-- built files will be auto injected --> <!-- built files will be auto injected -->
</body> </body>
</html> </html>

15
src/api/coupon.js Normal file
View File

@ -0,0 +1,15 @@
import qs from 'qs'
import request from '@/utils/request'
import { api } from '@/config'
// 签名
import _bale from '@/utils/package'
// api
// 查询我的优惠券
export function getCouponList(params) {
return request({
url: '/coupon/selectMyCoupon',
method: 'post',
data: qs.stringify(_bale('selectMyCoupon', params))
})
}

View File

@ -4,12 +4,10 @@ import { api } from '@/config'
// 签名 // 签名
import _bale from '@/utils/package' import _bale from '@/utils/package'
// api // api
const { common_api } = api
// 登录 // 登录
export function login(params) { export function login(params) {
return request({ return request({
url: common_api + '/wechat/login.do', url: '/wechat/login.do',
method: 'post', method: 'post',
data: qs.stringify(_bale('login', params)) data: qs.stringify(_bale('login', params))
}) })
@ -17,16 +15,10 @@ export function login(params) {
/** /**
* 登录接口请求token与userinfo * 登录接口请求token与userinfo
* @param params * @param params
* 入参 code:"021gj0OV1om5PU0k9VNV1VMQNV1gj0OK"
* 返回 {
* accessToken:'xxx',
* refreshToken:'xxx',
* userInfo:{}
* }
*/ */
export function loginByCode(params) { export function loginByCode(params) {
return request({ return request({
url: common_api + '/wechat/auth2', url: '/wechat/auth2',
method: 'post', method: 'post',
data: qs.stringify(_bale('auth2', params)) data: qs.stringify(_bale('auth2', params))
}) })
@ -37,7 +29,7 @@ export function loginByCode(params) {
*/ */
export function getUserInfo(params) { export function getUserInfo(params) {
return request({ return request({
url: common_api + '/user/get_user', url: '/user/get_user',
method: 'post', method: 'post',
data: qs.stringify(_bale('get_user', params)) data: qs.stringify(_bale('get_user', params))
}) })
@ -49,7 +41,7 @@ export function getUserInfo(params) {
*/ */
export function getAccountInfo(params) { export function getAccountInfo(params) {
return request({ return request({
url: common_api + '/wechat/selectVipUserInfo', url: '/wechat/selectVipUserInfo',
method: 'post', method: 'post',
data: qs.stringify(_bale('selectVipUserInfo', params)) data: qs.stringify(_bale('selectVipUserInfo', params))
}) })
@ -61,7 +53,7 @@ export function getAccountInfo(params) {
*/ */
export function sendCode(params) { export function sendCode(params) {
return request({ return request({
url: common_api + '/wechat/send_phone_code', url: '/wechat/send_phone_code',
method: 'post', method: 'post',
data: qs.stringify(_bale('send_phone_code', params)) data: qs.stringify(_bale('send_phone_code', params))
}) })
@ -73,7 +65,7 @@ export function sendCode(params) {
*/ */
export function bindPhoneNumber(params) { export function bindPhoneNumber(params) {
return request({ return request({
url: common_api + '/wechat/addPhoneNumber', url: '/wechat/addPhoneNumber',
method: 'post', method: 'post',
data: qs.stringify(_bale('addPhoneNumber', params)) data: qs.stringify(_bale('addPhoneNumber', params))
}) })

View File

@ -118,7 +118,7 @@ div:focus {
.app-container { .app-container {
} }
.van-hairline--top-bottom::after { .van-hairline--top-bottom::after {
border-width: 0!important; border-width: 0 !important;
} }
.van-cell { .van-cell {
line-height: 30px !important; line-height: 30px !important;
@ -143,3 +143,26 @@ div:focus {
max-width: 10rem; max-width: 10rem;
min-width: 10rem; min-width: 10rem;
} }
.my-header {
position: relative;
width: 100%;
background: #fff;
border-bottom:1px solid #E9E9E9;
.back-icon {
position: absolute;
width: 44px;
height: 44px;
box-sizing: border-box;
background: url('../../assets/images/arrow-left.png') no-repeat center
center;
background-size: 70% 70%;
}
.header-title {
font-size: 17px;
font-weight: 600;
height: 44px;
line-height: 44px;
text-align: center;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,35 @@
<!-- ListEmpty.vue -->
<template>
<div class="list-empty">
<slot name='emptyImg'></slot>
<div class="empty-tips">{{tips}}</div>
</div>
</template>
<script>
export default {
name: 'ListEmpty',
props: {
tips: {
type: String,
default: ''
}
},
}
</script>
<style lang='scss' scoped>
.list-empty {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 55px;
.empty-tips {
color: #999999;
font-size: 17px;
line-height: 24px;
padding-top: 35px;
}
}
</style>

View File

@ -0,0 +1,134 @@
<!-- verificationCode.vue -->
<template>
<div class="app-container">
<!-- 二维码大图 -->
<van-popup v-model="visible" class="modal-popup">
<div class="qrcode-popup-container">
<div class="qrcode-close" @click="closePopup">
取消
</div>
<div class="qrcode-top">
{{title}}
</div>
<div class="qrcode-bottom">
<img class="qrcode-img" :src="src" alt="">
<div class="qrcode-tips" v-html="tips">
</div>
</div>
</div>
</van-popup>
</div>
</template>
<script>
import { Popup, Toast } from 'vant'
import * as Validate from '@/utils/validate'
import { bindPhoneNumber, sendCode } from '@/api/user'
export default {
name: 'QrCodePopup',
components: {
'van-popup': Popup
},
props: {
visible: {
type: Boolean,
default: false
},
src: {
type: String,
default: ''
},
title: {
type: String,
default: ''
},
tips: {
type: String,
default: ''
}
},
data () {
return {
}
},
methods: {
closePopup () {
this.$emit('close')
}
}
}
</script>
<style lang='scss' scoped>
.modal-popup {
width: 100%;
height: 100%;
background: #d43f36;
.qrcode-popup-container {
position: relative;
margin: 25px 27px;
background: #fff;
border-radius: 10px;
.qrcode-close {
color: #d43f36;
font-size: 16px;
position: absolute;
left: 0px;
top: 0px;
padding: 20px;
z-index: 100;
}
.qrcode-top {
position: relative;
height: 115px;
border-bottom: 2px dotted #dbdbdb;
display: flex;
justify-content: center;
align-items: center;
color: #333333;
font-size: 24px;
font-weight: 500;
&::before {
content: "";
width: 32px;
height: 32px;
background: #d43f36;
position: absolute;
bottom: -16px;
right: -16px;
border-radius: 50%;
}
&::after {
content: "";
width: 32px;
height: 32px;
background: #d43f36;
position: absolute;
bottom: -16px;
left: -16px;
border-radius: 50%;
}
}
.qrcode-bottom {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 25px 0 41px 0;
.qrcode-img {
height: 190px;
width: 190px;
}
.qrcode-tips {
font-size: 15px;
color: #666666;
width: 210px;
text-align: center;
margin-top: 17px;
line-height: 21px;
}
}
}
}
</style>

View File

@ -7,12 +7,12 @@
<div class="cell-title">绑定手机号</div> <div class="cell-title">绑定手机号</div>
<div class="cell-item"> <div class="cell-item">
<div class="item-left">+86</div> <div class="item-left">+86</div>
<input v-model="verifyCodeForm.account" type="tel" placeholder="请输入手机号"> <input v-reset-page v-model="verifyCodeForm.account" type="tel" placeholder="请输入手机号">
</div> </div>
<div class="cell-item"> <div class="cell-item">
<div class="item-left"> 验证码</div> <div class="item-left"> 验证码</div>
<div class="code-cell-warp"> <div class="code-cell-warp">
<input v-model="verifyCodeForm.code" type="tel" placeholder="请输入验证码"> <input v-reset-page v-model="verifyCodeForm.code" type="tel" placeholder="请输入验证码">
<div :class="['registered-get-code',codeCountdown?'disabled-btn':'']" :disabled="codeCountdown" @click="sendMsgCode"> <div :class="['registered-get-code',codeCountdown?'disabled-btn':'']" :disabled="codeCountdown" @click="sendMsgCode">
{{ codeCountdown ? `${codeCountdown}后重新发送` : '发送验证码' }} {{ codeCountdown ? `${codeCountdown}后重新发送` : '发送验证码' }}
</div> </div>
@ -66,7 +66,10 @@ export default {
// //
bindPhoneNumber(this.verifyCodeForm).then(res => { bindPhoneNumber(this.verifyCodeForm).then(res => {
// //
this.visible = false Toast({
message: '绑定成功'
})
this.$emit('close')
}).catch(() => { }).catch(() => {
}) })
}, },

View File

@ -3,7 +3,7 @@ module.exports = {
baseUrl: 'https://test.top1buyer.com', baseUrl: 'https://test.top1buyer.com',
api: { api: {
base_api: 'https://test.top1buyer.com/wx', base_api: 'https://test.top1buyer.com/wx',
common_api: 'https://xxx.xxx.com/common' common_api: 'https://test.top1buyer.com/wx'
}, },
// package appid,appSecret // package appid,appSecret
APPID: 'Pc690487e95992c395633866b', APPID: 'Pc690487e95992c395633866b',

View File

@ -1,5 +1,42 @@
/**
*格式化时间
*yyyy-MM-dd hh:mm:ss
*/
export function formatDate(time, fmt) {
if (time === undefined || '') {
return
}
const date = new Date(time)
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(
RegExp.$1,
(date.getFullYear() + '').substr(4 - RegExp.$1.length)
)
}
const o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds()
}
for (const k in o) {
if (new RegExp(`(${k})`).test(fmt)) {
const str = o[k] + ''
fmt = fmt.replace(
RegExp.$1,
RegExp.$1.length === 1 ? str : padLeftZero(str)
)
}
}
return fmt
}
function padLeftZero(str) {
return ('00' + str).substr(str.length)
}
// 转为unicode 编码 // 转为unicode 编码
exports.encodeUnicode = str => { export function encodeUnicode(str) {
var res = [] var res = []
for (var i = 0; i < str.length; i++) { for (var i = 0; i < str.length; i++) {
res[i] = ('00' + str.charCodeAt(i).toString(16)).slice(-4) res[i] = ('00' + str.charCodeAt(i).toString(16)).slice(-4)
@ -8,7 +45,7 @@ exports.encodeUnicode = str => {
} }
// 解码 // 解码
exports.decodeUnicode = str => { export function decodeUnicode(str) {
if (str === undefined || '') { if (str === undefined || '') {
return return
} }
@ -18,17 +55,26 @@ exports.decodeUnicode = str => {
/* /*
* 格式化金钱 * 格式化金钱
*/ */
exports.formatMoney = value => { export function formatMoney(value) {
return Number(value).toFixed(2) return Number(value).toFixed(2)
} }
exports.formatCentMoney = value => { export function formatCentMoney(value) {
if (value === undefined || '') { if (value === undefined || '') {
return return
} }
return Number(value / 100).toFixed(2) return Number(value / 100).toFixed(2)
} }
// 昵称解码 // 昵称解码
exports.formatName = nickname => { export function formatName(nickname) {
if (!nickname) return '' if (!nickname) return ''
return decodeURIComponent(nickname) return decodeURIComponent(nickname)
} }
// 折扣转换
export function formatPercent(percent_off) {
if (!percent_off) return ''
if (percent_off == 1) {
return '原价'
}
// 解决精度丢失问题
return Math.round(percent_off * 100) / 10
}

View File

@ -1,10 +1,12 @@
import Vue from 'vue' import Vue from 'vue'
import filter from './filter' import * as filter from './filter'
Object.keys(filter).forEach(k => Vue.filter(k, filter[k])) Object.keys(filter).forEach(k => Vue.filter(k, filter[k]))
Vue.prototype.$formatDate = Vue.filter('formatDate')
Vue.prototype.$encodeUnicode = Vue.filter('encodeUnicode') Vue.prototype.$encodeUnicode = Vue.filter('encodeUnicode')
Vue.prototype.$decodeUnicode = Vue.filter('decodeUnicode') Vue.prototype.$decodeUnicode = Vue.filter('decodeUnicode')
Vue.prototype.$formatMoney = Vue.filter('formatMoney') Vue.prototype.$formatMoney = Vue.filter('formatMoney')
Vue.prototype.$formatCentMoney = Vue.filter('formatCentMoney') Vue.prototype.$formatCentMoney = Vue.filter('formatCentMoney')
Vue.prototype.$formatName = Vue.filter('formatName') Vue.prototype.$formatName = Vue.filter('formatName')
Vue.prototype.$formatPercent = Vue.filter('formatPercent')

View File

@ -1,5 +1,5 @@
import Vue from 'vue' import Vue from 'vue'
import '@babel/polyfill'
import 'normalize.css/normalize.css' // A modern alternative to CSS resets import 'normalize.css/normalize.css' // A modern alternative to CSS resets
import '@/assets/css/index.scss' // global css import '@/assets/css/index.scss' // global css
// 移动端适配 // 移动端适配
@ -8,11 +8,14 @@ import App from './App'
import store from './store' import store from './store'
import router from './router' import router from './router'
import '@/filters' // filters import '@/filters' // filters
import '@/utils/directives' // directives
import '@/permission' // permission control import '@/permission' // permission control
import wechatAuth from './plugins/wechatAuth' // 微信登录插件 import wechatAuth from './plugins/wechatAuth' // 微信登录插件
Vue.use(wechatAuth, { Vue.use(wechatAuth, {
appid: process.env.VUE_APP_WECHAT_APPID appid: process.env.VUE_APP_WECHAT_APPID
}) })
import VCconsole from 'vconsole'
Vue.use(VCconsole)
Vue.config.productionTip = false Vue.config.productionTip = false
new Vue({ new Vue({

View File

@ -5,9 +5,14 @@ import wechatAuth from './plugins/wechatAuth' // 微信登录插件
const qs = require('qs') const qs = require('qs')
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
// next() // next()
console.log(store.getters.loginStatus) // store.dispatch('user/fedLogOut').then(() => {
// // location.reload()
// })
// store.dispatch('user/setLoginStatus', 0)
// alert(store.getters.loginStatus)
// return false
// alert(store.getters.loginStatus)
const loginStatus = Number(store.getters.loginStatus) const loginStatus = Number(store.getters.loginStatus)
// console.log(loginStatus === 1)
document.title = getPageTitle(to.meta.title) document.title = getPageTitle(to.meta.title)
if (loginStatus === 0) { if (loginStatus === 0) {
// 微信未授权登录跳转到授权登录页面 // 微信未授权登录跳转到授权登录页面
@ -22,7 +27,9 @@ router.beforeEach((to, from, next) => {
} else { } else {
loginUrl = url loginUrl = url
} }
// alert(loginUrl)
wechatAuth.redirect_uri = loginUrl wechatAuth.redirect_uri = loginUrl
// 无论拒绝还是授权都设置成1
store.dispatch('user/setLoginStatus', 1) store.dispatch('user/setLoginStatus', 1)
window.location.href = wechatAuth.authUrl window.location.href = wechatAuth.authUrl
} else if (loginStatus === 1) { } else if (loginStatus === 1) {
@ -31,27 +38,32 @@ router.beforeEach((to, from, next) => {
wechatAuth.returnFromWechat(to.fullPath) wechatAuth.returnFromWechat(to.fullPath)
} catch (err) { } catch (err) {
store.dispatch('user/setLoginStatus', 0) store.dispatch('user/setLoginStatus', 0)
next() location.reload()
// next()
} }
// 同意授权 to.fullPath 携带code参数拒绝授权没有code参数
// alert(to.fullPath)
// 重新赋值不然获取不到code // 重新赋值不然获取不到code
const code = wechatAuth.code const code = wechatAuth.code
// alert(code)
if (code) {
store store
.dispatch('user/loginWechatAuth', code) .dispatch('user/loginWechatAuth', code)
.then(res => { .then(res => {
console.log(res)
if (res.status === 200) {
store.dispatch('user/setLoginStatus', 2) store.dispatch('user/setLoginStatus', 2)
} else {
store.dispatch('user/setLoginStatus', 0)
}
next() next()
}) })
.catch(() => { .catch(() => {
store.dispatch('user/setLoginStatus', 0) store.dispatch('user/setLoginStatus', 0)
next() location.reload()
}) })
} else { } else {
// store.dispatch('user/setLoginStatus', 0)
location.reload()
}
} else {
// alert(to.fullPath)
next() next()
} }
}) })

View File

@ -38,7 +38,7 @@ export const constantRoutes = [
const createRouter = () => const createRouter = () =>
new Router({ new Router({
mode: 'history', // require service support mode: 'history', // require service support
base: '/', base: '/antpublic',
scrollBehavior: () => ({ y: 0 }), scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes routes: constantRoutes
}) })

View File

@ -71,6 +71,12 @@ const actions = {
// 保存用户个人信息 // 保存用户个人信息
setUserInfo({ commit, state }, query) { setUserInfo({ commit, state }, query) {
commit('SET_USERINFO', saveUserInfo(query)) commit('SET_USERINFO', saveUserInfo(query))
},
// 登出
fedLogOut({ commit, state }, query) {
removeToken()
removeUserInfo()
commit('SET_LOGIN_STATUS', saveLoginStatus(0))
} }
} }

33
src/utils/directives.js Normal file
View File

@ -0,0 +1,33 @@
import Vue from 'vue'
Vue.directive('focus', {
inserted: function(el) {
// 获取焦点
el.focus()
}
})
Vue.directive('numberOnly', {
inserted: function(el) {
// 获取焦点
el.handler = function() {
el.value = el.value.replace(/[^\d]/g, '')
}
el.addEventListener('input', el.handler)
}
})
Vue.directive('resetPage', {
inserted: function(el) {
// 监听键盘收起事件
document.body.addEventListener('focusout', () => {
if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
// 软键盘收起的事件处理
setTimeout(() => {
const scrollHeight =
document.documentElement.scrollTop || document.body.scrollTop || 0
window.scrollTo(0, Math.max(scrollHeight - 1, 0))
}, 100)
}
})
}
})

View File

@ -35,10 +35,14 @@ service.interceptors.response.use(
response => { response => {
Toast.clear() Toast.clear()
const res = response.data const res = response.data
if (res.status && res.status !== 200) { if (res.status && res.status !== 200) {
Toast({
message: res.info
})
// 登录超时,重新登录 // 登录超时,重新登录
if (res.status === 401) { if (res.status === 401) {
store.dispatch('FedLogOut').then(() => { store.dispatch('user/fedLogOut').then(() => {
location.reload() location.reload()
}) })
} }

View File

@ -1,84 +1,134 @@
<!-- coupon.vue --> <!-- coupon.vue -->
<template> <template>
<div class="app-container"> <div class="app-container">
<van-nav-bar title="我的优惠券" left-arrow @click-left="()=>{this.$router.push('/account')}" /> <div class="fixed">
<van-tabs v-model="tabActive" class="coupon-tabs" :line-width="44" :line-height="1" color="#333333"> <div class="my-header">
<div class="back-icon" @click="()=>{this.$router.push('/account')}"></div>
<div class="header-title">我的优惠券</div>
</div>
<van-tabs v-model="tabActive" @click="tabClick(tabActive)" class="coupon-tabs" :line-width="44" :line-height="1" color="#333333">
<van-tab title="未使用"></van-tab> <van-tab title="未使用"></van-tab>
<van-tab title="已使用"></van-tab> <van-tab title="已使用"></van-tab>
<van-tab title="已失效"></van-tab> <van-tab title="已失效"></van-tab>
</van-tabs> </van-tabs>
</div>
<div class="coupon-list-warpper"> <div class="coupon-list-warpper">
<div class="coupon-item"> <!-- foreach -->
<div class="coupon-amount-warp"> <div v-for="(item,index) in couponList" :key="index" class="coupon-item">
<div class="coupon-main">
<div class="coupon-left">
<div class="coupon-amount-number"> <div class="coupon-amount-number">
<span class="rmb">¥</span> <template v-if="item.couponType===3">
<span>50</span> <span>{{ item.percentOff|formatPercent }}</span><span class="rmb"></span>
</template>
<template v-else>
<span class="rmb">¥</span><span>{{ item.couponAmount|formatCentMoney }}</span>
</template>
</div> </div>
<p><span>¥199可用</span></p> <div>
<span v-if="item.couponType===1">无门槛</span>
<span v-if="item.couponType===2">¥{{ item.availableAmount|formatCentMoney }}可用</span>
</div> </div>
<div class="coupon-info-warp"> </div>
<div class="coupon-right">
<div class="coupon-info"> <div class="coupon-info">
<div class="coupon-title">全场限时优惠</div> <div class="coupon-title">{{ item.couponName }}</div>
<div class="off-date">2018.12.18 10:00:00</div> <div v-if="item.endType === 0" class="coupon-off-date">无使用期限</div>
<div v-else class="coupon-off-date"> {{ item.couponStart|formatDate("yyyy.MM.dd hh:mm:ss") }}-{{ item.couponEnd|formatDate("yyyy.MM.dd hh:mm:ss") }}</div>
</div> </div>
<div class="coupon-detail">详细信息<span></span></div> <div class="coupon-detail" @click="showExpain(index)">
<span>详细信息</span>
<span :class="item.explainVisible?'coupon-arrow-up':'coupon-arrow-down'"></span>
</div> </div>
</div> </div>
<div class="coupon-item">
<div class="coupon-amount-warp">
<div class="coupon-amount-number">
<span class="rmb">¥</span>
<span>50</span>
</div> </div>
<p><span>¥199可用</span></p> <div v-if="item.explainVisible" class="coupon-info-explain">
</div> {{ item.couponExplain }}
<div class="coupon-info-warp">
<div class="coupon-info">
<div class="coupon-title">全场限时优惠</div>
<div class="off-date">2018.12.18 10:00:00</div>
</div>
<div class="coupon-detail">详细信息<span></span></div>
</div> </div>
</div> </div>
<!-- foreach end-->
<!-- empty -->
<list-empty v-if="couponList.length===0" tips="您没有可用的优惠券哦">
<div slot="emptyImg" class="coupon_empty">
</div>
</list-empty>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { NavBar, Tab, Tabs } from 'vant' import { Tab, Tabs } from 'vant'
import { getCouponList } from '@/api/coupon'
import ListEmpty from '@/components/ListEmpty'
export default { export default {
components: { components: {
'van-nav-bar': NavBar,
'van-tabs': Tabs, 'van-tabs': Tabs,
'van-tab': Tab 'van-tab': Tab,
'list-empty': ListEmpty
}, },
data () { data () {
return { return {
tabActive: 0 tabActive: 0,
couponList: [],
params: {
couponStatus: 0 // 0使1使2
},
explainShow: false
} }
}, },
computed: {}, computed: {},
mounted () { }, mounted () {
methods: {} this.fetchData()
},
methods: {
fetchData () {
getCouponList(this.params).then(res => {
this.couponList = res.data.couponList
})
},
//
showExpain (index) {
if (this.couponList[index].explainVisible) {
this.couponList[index].explainVisible = !this.couponList[index].explainVisible
} else {
this.$set(this.couponList[index], 'explainVisible', true)
}
},
// tab
tabClick (index) {
this.params.couponStatus = index
//
this.fetchData()
}
}
} }
</script> </script>
<style lang='scss'> <style lang='scss'>
.coupon-tabs { .fixed {
position: fixed;
z-index: 9999;
width: 100%;
.coupon-tabs {
.van-tab { .van-tab {
font-size: 17px !important; font-size: 17px !important;
font-weight: 500 !important; font-weight: 500 !important;
} }
}
} }
.coupon-list-warpper { .coupon-list-warpper {
padding: 12px; padding: 98px 12px 0 12px;
height: 100vh;
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
.coupon-item { .coupon-item {
margin-bottom: 12px;
.coupon-main {
display: flex; display: flex;
width: 100%; width: 100%;
height: 100px; .coupon-left {
margin-bottom: 12px;
.coupon-amount-warp {
width: 118px; width: 118px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -90,12 +140,13 @@ export default {
color: #fff; color: #fff;
.coupon-amount-number { .coupon-amount-number {
font-size: 30px; font-size: 30px;
line-height: 42px;
.rmb { .rmb {
font-size: 15px; font-size: 15px;
} }
} }
} }
.coupon-info-warp { .coupon-right {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background: #fff; background: #fff;
@ -112,7 +163,7 @@ export default {
line-height: 23px; line-height: 23px;
color: #333333; color: #333333;
} }
.off-date { .coupon-off-date {
font-size: 10px; font-size: 10px;
line-height: 14px; line-height: 14px;
color: #999999; color: #999999;
@ -121,9 +172,40 @@ export default {
.coupon-detail { .coupon-detail {
color: #666666; color: #666666;
line-height: 30px; line-height: 30px;
padding-left: 11px; padding: 0 11px;
display: flex;
justify-content: space-between;
align-items: center;
} }
.coupon-arrow-down {
width: 22px;
height: 22px;
background: url("../../assets/images/coupon/s-coupon-detail-down@2x.png")
no-repeat center center;
background-size: contain;
}
.coupon-arrow-up {
width: 22px;
height: 22px;
background: url("../../assets/images/coupon/s-coupon-detail-up@2x.png")
no-repeat center center;
background-size: contain;
}
}
}
.coupon-info-explain {
background: #ffffff;
margin-top: 1px;
padding: 8px 12px;
color: #999999;
} }
} }
} }
.coupon_empty {
width: 140px;
height: 105px;
background: url("../../assets/images/coupon/s-coupon-empty@2x.png") no-repeat
center center;
background-size: cover;
}
</style> </style>

View File

@ -9,8 +9,8 @@
<div class="user-name">{{ vipInfo.nickName|formatName }}<span class="user-level"></span></div> <div class="user-name">{{ vipInfo.nickName|formatName }}<span class="user-level"></span></div>
<div class="user-code">代购编号{{ vipInfo.userCode }}</div> <div class="user-code">代购编号{{ vipInfo.userCode }}</div>
</div> </div>
<div class="qrcode-warp" @click="showQrcode($event)"> <div class="qrcode-warp" @click="showUserQrcode($event)">
<qrcode :value="vipInfo.userCode" tag="img" :options="{ width: 45,margin:0 }"></qrcode> <qrcode v-if="vipInfo.userPhone!==''" :value="vipInfo.userPhone" tag="img" :options="{ width: 145,margin:0 }"></qrcode>
</div> </div>
</div> </div>
<div class="account-info"> <div class="account-info">
@ -42,49 +42,60 @@
</van-cell-group> </van-cell-group>
</div> </div>
<!-- 绑定手机 --> <!-- 绑定手机 -->
<msg-code :visible="codeVisible"></msg-code> <msg-code :visible="codeVisible" @close="()=>{ codeVisible = false }"></msg-code>
<!-- 二维码大图 --> <!-- 二维码 -->
<van-popup v-model="qrcodeVisible" style="padding:10px;border-radius:10px"> <qrcode-popup :visible="qrcodeVisible" :src="qrSrc" :title="qrTitle" :tips="qrTips" @close="()=>{ qrcodeVisible = false }">
<div class="popup-container"> </qrcode-popup>
<img :src="qrSrc" alt="">
</div>
</van-popup>
</div> </div>
</template> </template>
<script> <script>
import VueQrcode from '@chenfengyuan/vue-qrcode' import VueQrcode from '@chenfengyuan/vue-qrcode'
import { Popup, Cell, CellGroup } from 'vant' import { Cell, CellGroup } from 'vant'
import VerificationCode from '@/components/VerificationCode' import VerificationCode from '@/components/VerificationCode'
import QrCodePopup from '@/components/QrCodePopup'
import { getAccountInfo } from '@/api/user' import { getAccountInfo } from '@/api/user'
export default { export default {
components: { components: {
'van-cell-group': CellGroup, 'van-cell-group': CellGroup,
'van-cell': Cell, 'van-cell': Cell,
'msg-code': VerificationCode, 'msg-code': VerificationCode,
'qrcode': VueQrcode, 'qrcode-popup': QrCodePopup,
'van-popup': Popup 'qrcode': VueQrcode
}, },
data () { data () {
return { return {
qrcodeVisible: false, qrcodeVisible: false,
codeVisible: false, codeVisible: false,
vipInfo: {}, vipInfo: {},
qrSrc: '' qrSrc: '',
qrTitle: '',
qrTips: ''
} }
}, },
computed: {}, computed: {
created () { },
mounted () {
this.init() this.init()
}, },
methods: { methods: {
// //
async init () { init () {
// //
const { data } = await getAccountInfo() getAccountInfo().then(res => {
this.vipInfo = data.vipUserInfo this.vipInfo = res.data.vipUserInfo
//
if (this.vipInfo && this.vipInfo.userPhone === '') {
this.codeVisible = true
}
})
}, },
showQrcode (event) { //
showUserQrcode (event) {
this.qrSrc = event.target.currentSrc this.qrSrc = event.target.currentSrc
this.qrTitle = '小蚁货仓会员码'
this.qrTips = '<p>会员码用于会员储值及支付</p> <p>请勿随意泄漏给</p>'
this.qrcodeVisible = true this.qrcodeVisible = true
} }
} }
@ -135,9 +146,13 @@ h1 {
font-size: 13px; font-size: 13px;
} }
} }
.qr-code { .qrcode-warp {
width: 50px; width: 50px;
height: 50px; height: 50px;
img {
width: 100%;
height: 100%;
}
} }
} }
.account-info { .account-info {

View File

@ -1,17 +1,15 @@
'use strict' 'use strict'
const path = require('path') const path = require('path')
const defaultSettings = require('./src/config/index.js')
function resolve(dir) { function resolve(dir) {
return path.join(__dirname, dir) return path.join(__dirname, dir)
} }
const name = defaultSettings.title || 'vue mobile template' // page title
const port = 9018 // dev port const port = 9018 // dev port
const externals = { const externals = {
vue: 'Vue', vue: 'Vue',
'vue-router': 'VueRouter', 'vue-router': 'VueRouter',
vuex: 'Vuex', vuex: 'Vuex',
'mint-ui': 'MINT', vant: 'vant',
axios: 'axios', axios: 'axios',
'crypto-js': 'CryptoJS' 'crypto-js': 'CryptoJS'
} }
@ -26,6 +24,7 @@ const cdn = {
build: { build: {
css: ['https://cdn.jsdelivr.net/npm/vant@beta/lib/index.css'], css: ['https://cdn.jsdelivr.net/npm/vant@beta/lib/index.css'],
js: [ js: [
// 'https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.4.4/polyfill.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.0.6/vue-router.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.0.6/vue-router.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js',
@ -36,7 +35,7 @@ const cdn = {
} }
} }
module.exports = { module.exports = {
publicPath: '/', publicPath: process.env.NODE_ENV === 'production' ? '/antpublic/' : '/',
outputDir: 'dist', outputDir: 'dist',
assetsDir: 'static', assetsDir: 'static',
lintOnSave: process.env.NODE_ENV === 'development', lintOnSave: process.env.NODE_ENV === 'development',
@ -50,29 +49,22 @@ module.exports = {
}, },
disableHostCheck: true disableHostCheck: true
}, },
configureWebpack: config => { // configureWebpack: config => {
// 为生产环境修改配置... // // 为生产环境修改配置...
if (process.env.NODE_ENV === 'production') { // if (process.env.NODE_ENV === 'production') {
// externals里的模块不打包 // // externals里的模块不打包
Object.assign(config, { // Object.assign(config, {
externals: externals // externals: externals
}) // })
}
// 为开发环境修改配置...
if (process.env.NODE_ENV === 'development') {
}
},
// configureWebpack: {
// name: name,
// resolve: {
// alias: {
// '@': resolve('src')
// } // }
// // 为开发环境修改配置...
// if (process.env.NODE_ENV === 'development') {
// } // }
// }, // },
chainWebpack(config) { chainWebpack(config) {
config.plugins.delete('preload') // TODO: need test config.plugins.delete('preload') // TODO: need test
config.plugins.delete('prefetch') // TODO: need test config.plugins.delete('prefetch') // TODO: need test
config
// alias // alias
config.resolve.alias config.resolve.alias
.set('@', resolve('src')) .set('@', resolve('src'))
@ -82,16 +74,16 @@ module.exports = {
/** /**
* 添加CDN参数到htmlWebpackPlugin配置中 详见public/index.html 修改 * 添加CDN参数到htmlWebpackPlugin配置中 详见public/index.html 修改
*/ */
config.plugin('html').tap(args => { // config.plugin('html').tap(args => {
if (process.env.NODE_ENV === 'production') { // if (process.env.NODE_ENV === 'production') {
args[0].cdn = cdn.build // args[0].cdn = cdn.build
} // }
if (process.env.NODE_ENV === 'development') { // if (process.env.NODE_ENV === 'development') {
args[0].cdn = cdn.dev // args[0].cdn = cdn.dev
} // }
console.log(args) // console.log(args)
return args // return args
}) // })
// set preserveWhitespace // set preserveWhitespace
config.module config.module