个人中心

This commit is contained in:
406803045 2019-06-04 09:24:55 +08:00
parent f97484c725
commit 6014f11ee4
32 changed files with 1114 additions and 33 deletions

View File

@ -3,6 +3,8 @@ NODE_ENV='development'
VUE_APP_ENV = 'development'
#base url
BASE_URL = 'https://www.xxx.com/'
#appid
VUE_APP_WECHAT_APPID='wxc6086549532e9a60'
# base api
VUE_APP_BASE_API = '/dev-api'
VUE_CLI_BABEL_TRANSPILE_MODULES = true

View File

@ -3,6 +3,8 @@ NODE_ENV='production'
VUE_APP_ENV = 'production'
#base url
BASE_URL = https://www.top1buyer.com/
#appid
VUE_APP_WECHAT_APPID='wx6bb2125514b4c1ff'
# base api
VUE_APP_BASE_API = '/prod-api'

View File

@ -2,8 +2,9 @@ NODE_ENV='production'
# must start with VUE_APP_
VUE_APP_ENV = 'staging'
#base url
#base url
BASE_URL = https://www.top1buyer.com/
#appid
VUE_APP_WECHAT_APPID='wx6bb2125514b4c1ff'
# base api
VUE_APP_BASE_API = '/stage-api'

View File

@ -224,7 +224,7 @@ module.exports = {
}
],
'space-before-blocks': [2, 'always'],
'space-before-function-paren': [2, 'never'],
'space-before-function-paren':0,
'space-in-parens': [2, 'never'],
'space-infix-ops': 2,
'space-unary-ops': [

View File

@ -15,12 +15,15 @@
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
},
"dependencies": {
"@chenfengyuan/vue-qrcode": "^1.0.0",
"axios": "0.18.0",
"crypto-js": "^3.1.9-1",
"good-storage": "^1.1.0",
"js-cookie": "^2.2.0",
"lib-flexible": "^0.3.2",
"normalize.css": "7.0.0",
"vant": "^1.6.19",
"vue": "2.6.10",
"vue": "^2.6.10",
"vue-router": "3.0.6",
"vuex": "3.1.0"
},

View File

@ -1,14 +1,80 @@
import qs from 'qs'
import request from '@/utils/request'
import { api } from '@/config'
// 签名
import _bale from '@/utils/package'
// api
const { common_api } = api
// 登录
export function login(params) {
return request({
url: common_api + '/ruleCommon/queryrule',
url: common_api + '/wechat/login.do',
method: 'post',
data: qs.stringify(params)
data: qs.stringify(_bale('login', params))
})
}
/**
* 登录接口请求token与userinfo
* @param params
* 入参 code:"021gj0OV1om5PU0k9VNV1VMQNV1gj0OK"
* 返回 {
* accessToken:'xxx',
* refreshToken:'xxx',
* userInfo:{}
* }
*/
export function loginByCode(params) {
return request({
url: common_api + '/wechat/auth2',
method: 'post',
data: qs.stringify(_bale('auth2', params))
})
}
/**
* 获取登录用户信息
* @param params
*/
export function getUserInfo(params) {
return request({
url: common_api + '/user/get_user',
method: 'post',
data: qs.stringify(_bale('get_user', params))
})
}
/**
* 公众号会员中心
* @param params
*/
export function getAccountInfo(params) {
return request({
url: common_api + '/wechat/selectVipUserInfo',
method: 'post',
data: qs.stringify(_bale('selectVipUserInfo', params))
})
}
/**
* 发送手机验证码
* @param params
*/
export function sendCode(params) {
return request({
url: common_api + '/wechat/send_phone_code',
method: 'post',
data: qs.stringify(_bale('send_phone_code', params))
})
}
/**
* 微信公众号添加手机号
* @param params
*/
export function bindPhoneNumber(params) {
return request({
url: common_api + '/wechat/addPhoneNumber',
method: 'post',
data: qs.stringify(_bale('addPhoneNumber', params))
})
}

View File

@ -1,12 +1,69 @@
@import './variables.scss';
@import './mixin.scss';
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 {
padding: 0;
margin: 0;
list-style: none;
font-style: normal;
text-decoration: none;
border: none;
font-weight: normal;
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
-webkit-font-smoothing: antialiased;
&:hover {
outline: none;
}
}
input[type='text'],
input[type='button'],
input[type='submit'],
input[type='search'],
input[type='reset'] {
-webkit-appearance: none;
}
textarea {
-webkit-appearance: none;
}
body {
height: 100%;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB,
Microsoft YaHei, Arial, sans-serif;
}
label {
@ -20,6 +77,7 @@ html {
#app {
height: 100%;
background: #f4f5f7;
}
*,
@ -50,7 +108,7 @@ div:focus {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
content: ' ';
clear: both;
height: 0;
}
@ -58,5 +116,30 @@ div:focus {
// main-container global css
.app-container {
padding: 20px;
}
.van-hairline--top-bottom::after {
border-width: 0!important;
}
.van-cell {
line-height: 30px !important;
.van-cell__title {
font-size: 15px;
color: #333333;
font-weight: 500;
}
.van-cell__left-icon,
.van-cell__right-icon {
line-height: 30px;
color: #999999;
}
&:not(:last-child)::after {
left: 0 !important;
}
}
//模态框
.modal-popup {
width: 100%;
height: 100%;
max-width: 10rem;
min-width: 10rem;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -0,0 +1,184 @@
<!-- verificationCode.vue -->
<template>
<div class="app-container">
<van-popup v-model="visible" class="modal-popup" position="right">
<div class="verification-code-container">
<div class="cell-box">
<div class="cell-title">绑定手机号</div>
<div class="cell-item">
<div class="item-left">+86</div>
<input v-model="verifyCodeForm.account" type="tel" placeholder="请输入手机号">
</div>
<div class="cell-item">
<div class="item-left"> 验证码</div>
<div class="code-cell-warp">
<input v-model="verifyCodeForm.code" type="tel" placeholder="请输入验证码">
<div :class="['registered-get-code',codeCountdown?'disabled-btn':'']" :disabled="codeCountdown" @click="sendMsgCode">
{{ codeCountdown ? `${codeCountdown}后重新发送` : '发送验证码' }}
</div>
</div>
</div>
<div :class="['cell-btn',codeDisable?'disabled-btn':'']" @click="submit"> </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: 'MsgCode',
components: {
'van-popup': Popup
},
props: {
visible: {
type: Boolean,
default: false
}
},
data () {
return {
codeCountdown: 0,
verifyCodeForm: {
code: '',
type: 3, // 1/ 2/ 3:
account: ''
},
errMessage: ''
}
},
computed: {
codeDisable () {
return this.verifyCodeForm.account === '' || this.verifyCodeForm.code === ''
}
},
methods: {
submit () { //
if (!this.valid(true)) { //
return Toast({
message: this.errMessage
})
}
//
bindPhoneNumber(this.verifyCodeForm).then(res => {
//
this.visible = false
}).catch(() => {
})
},
sendMsgCode () { //
if (!this.valid()) { //
return Toast({
message: this.errMessage
})
}
//
sendCode({
account: this.verifyCodeForm.account,
type: this.verifyCodeForm.type
}).then(res => {
this.codeCountdown = 60
const timer = setInterval(() => {
if (this.codeCountdown <= 0) {
this.codeCountdown = null
clearInterval(timer)
} else {
this.codeCountdown--
}
}, 1000)
}).catch(() => {
})
},
//
valid (validAll) {
if (!Validate.mobile(this.verifyCodeForm.account)) {
this.errMessage = '请输入正确的手机号'
return false
}
// validAlltrue
if (validAll) {
if (!Validate.number(this.verifyCodeForm.code)) {
this.errMessage = '请输入4位数字验证码'
return false
}
}
return true
}
}
}
</script>
<style lang='scss' scoped>
.verification-code-container {
padding: 25px;
// height: 100%;
// background: #fff;
.cell-box {
padding-top: 81px;
.cell-title {
line-height: 31px;
font-size: 22px;
font-weight: 600;
margin-bottom: 40px;
}
.cell-item {
border-bottom: 1px solid #ececec;
display: flex;
align-items: center;
font-size: 17px;
height: 55px;
.item-left {
margin-left: 12px;
width: 73px;
}
input {
flex: 1;
width: 100%;
line-height: 44px;
}
.code-cell-warp {
display: flex;
justify-content: space-between;
align-items: center;
flex: 1;
// .code-cell {
// background: #fff;
// position: relative;
// }
.registered-get-code {
width: 95px;
background: #ff3d3e;
color: #fff;
font-size: 14px;
text-align: center;
margin-left: 10px;
border-radius: 15px;
line-height: 30px;
height: 30px;
}
}
}
.cell-btn {
width: 315px;
height: 45px;
line-height: 45px;
margin-top: 80px;
background: #ff3d3e;
color: #ffffff;
text-align: center;
border-radius: 23px;
font-size: 18px;
}
.disabled-btn {
background: #d9d9d9 !important;
color: #fff !important;
pointer-events: none;
cursor: default;
}
}
}
</style>

View File

@ -1,9 +1,10 @@
// 本地
module.exports = {
title: 'vue-h5-template',
baseUrl:'http://localhost:9018',
api: {
base_api: 'https://xxx.xxx.com/admin',
common_api: 'https://xxx.xxx.com/common'
base_api: 'https://test.top1buyer.com/wx',
common_api: 'https://test.top1buyer.com/wx'
},
// package appid,appSecret
APPID: 'Pc690487e95992c395633866b',

View File

@ -1,6 +1,7 @@
// 正式
module.exports = {
title: 'vue-h5-template',
baseUrl:'http://localhost:9018',
api: {
base_api: 'https://xxx.xxx.com/admin',
common_api: 'https://xxx.xxx.com/common'

View File

@ -1,7 +1,8 @@
module.exports = {
title: 'vue-h5-template',
baseUrl: 'https://test.top1buyer.com',
api: {
base_api: 'https://xxx.xxx.com/admin',
base_api: 'https://test.top1buyer.com/wx',
common_api: 'https://xxx.xxx.com/common'
},
// package appid,appSecret

34
src/filters/filter.js Normal file
View File

@ -0,0 +1,34 @@
// 转为unicode 编码
exports.encodeUnicode = str => {
var res = []
for (var i = 0; i < str.length; i++) {
res[i] = ('00' + str.charCodeAt(i).toString(16)).slice(-4)
}
return '\\u' + res.join('\\u')
}
// 解码
exports.decodeUnicode = str => {
if (str === undefined || '') {
return
}
str = str.replace(/\\/g, '%')
return unescape(str)
}
/*
* 格式化金钱
*/
exports.formatMoney = value => {
return Number(value).toFixed(2)
}
exports.formatCentMoney = value => {
if (value === undefined || '') {
return
}
return Number(value / 100).toFixed(2)
}
// 昵称解码
exports.formatName = nickname => {
if (!nickname) return ''
return decodeURIComponent(nickname)
}

10
src/filters/index.js Normal file
View File

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

View File

@ -7,6 +7,12 @@ import 'lib-flexible/flexible.js'
import App from './App'
import store from './store'
import router from './router'
import '@/filters' // filters
import '@/permission' // permission control
import wechatAuth from './plugins/wechatAuth' // 微信登录插件
Vue.use(wechatAuth, {
appid: process.env.VUE_APP_WECHAT_APPID
})
Vue.config.productionTip = false
new Vue({

57
src/permission.js Normal file
View File

@ -0,0 +1,57 @@
import router from './router'
import store from './store'
import getPageTitle from '@/utils/get-page-title'
import wechatAuth from './plugins/wechatAuth' // 微信登录插件
const qs = require('qs')
router.beforeEach((to, from, next) => {
// next()
console.log(store.getters.loginStatus)
const loginStatus = Number(store.getters.loginStatus)
// console.log(loginStatus === 1)
document.title = getPageTitle(to.meta.title)
if (loginStatus === 0) {
// 微信未授权登录跳转到授权登录页面
const url = window.location.href
// 解决重复登录url添加重复的code与state问题
const parseUrl = qs.parse(url.split('?')[1])
let loginUrl
if (parseUrl.code && parseUrl.state) {
delete parseUrl.code
delete parseUrl.state
loginUrl = `${url.split('?')[0]}?${qs.stringify(parseUrl)}`
} else {
loginUrl = url
}
wechatAuth.redirect_uri = loginUrl
store.dispatch('user/setLoginStatus', 1)
window.location.href = wechatAuth.authUrl
} else if (loginStatus === 1) {
// 微信已经授权回调获取code
try {
wechatAuth.returnFromWechat(to.fullPath)
} catch (err) {
store.dispatch('user/setLoginStatus', 0)
next()
}
// 重新赋值不然获取不到code
const code = wechatAuth.code
store
.dispatch('user/loginWechatAuth', code)
.then(res => {
console.log(res)
if (res.status === 200) {
store.dispatch('user/setLoginStatus', 2)
} else {
store.dispatch('user/setLoginStatus', 0)
}
next()
})
.catch(() => {
store.dispatch('user/setLoginStatus', 0)
next()
})
} else {
//
next()
}
})

108
src/plugins/wechatAuth.js Normal file
View File

@ -0,0 +1,108 @@
const qs = require('qs')
// 应用授权作用域snsapi_base 不弹出授权页面直接跳转只能获取用户openidsnsapi_userinfo 弹出授权页面可通过openid拿到昵称、性别、所在地。并且即使在未关注的情况下只要用户授权也能获取其信息
const SCOPES = ['snsapi_base', 'snsapi_userinfo']
class VueWechatAuthPlugin {
constructor() {
this.appid = null
this.redirect_uri = null
this.scope = SCOPES[1]
this._code = null
this._redirect_uri = null
}
install(Vue, options) {
const wechatAuth = this
this.setAppId(options.appid)
Vue.mixin({
created: function() {
this.$wechatAuth = wechatAuth
}
})
}
static makeState() {
return (
Math.random()
.toString(36)
.substring(2, 15) +
Math.random()
.toString(36)
.substring(2, 15)
)
}
setAppId(appid) {
this.appid = appid
}
set redirect_uri(redirect_uri) {
this._redirect_uri = encodeURIComponent(redirect_uri)
}
get redirect_uri() {
return this._redirect_uri
}
get state() {
return localStorage.getItem('wechat_auth:state')
}
set state(state) {
localStorage.setItem('wechat_auth:state', state)
}
get authUrl() {
if (this.appid === null) {
throw new Error('appid must not be null')
}
if (this.redirect_uri === null) {
throw new Error('redirect uri must not be null')
}
this.state = VueWechatAuthPlugin.makeState()
return `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${
this.appid
}&redirect_uri=${this.redirect_uri}&response_type=code&scope=${
this.scope
}&state=${this.state}#wechat_redirect`
}
returnFromWechat(redirect_uri) {
const parsedUrl = qs.parse(redirect_uri.split('?')[1])
if (process.env.NODE_ENV === 'development') {
this.state = null
this._code = parsedUrl.code
} else {
if (this.state === null) {
throw new Error("You did't set state")
}
if (parsedUrl.state === this.state) {
this.state = null
this._code = parsedUrl.code
} else {
this.state = null
throw new Error(`Wrong state: ${parsedUrl.state}`)
}
}
}
get code() {
if (this._code === null) {
throw new Error('Not get the code from wechat server!')
}
// console.log(this)
// console.log('this._code: ' + this._code)
const code = this._code
this._code = null
// console.log('code: ' + code)
return code
}
}
const vueWechatAuthPlugin = new VueWechatAuthPlugin()
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(VueWechatAuthPlugin)
}
export default vueWechatAuthPlugin

View File

@ -5,6 +5,29 @@ Vue.use(Router)
export const constantRoutes = [
{
path: '/',
redirect: '/account'
},
{
path: '/account',
name: 'account',
component: () => import('@/views/account/index'),
meta: {
title: '个人中心',
keepAlive: false
}
},
{
path: '/coupon',
name: 'coupon',
component: () => import('@/views/account/coupon'),
meta: {
title: '优惠券',
keepAlive: false
}
},
{
path: '/home',
name: 'home',
component: () => import('@/views/home/index'),
meta: {
keepAlive: false
@ -14,7 +37,8 @@ export const constantRoutes = [
const createRouter = () =>
new Router({
// mode: 'history', // require service support
mode: 'history', // require service support
base: '/',
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes
})

View File

@ -1,3 +1,7 @@
const getters = {
// user
token: state => state.user.token,
userInfo: state => state.user.userInfo,
loginStatus: state => state.user.loginStatus
}
export default getters

View File

@ -2,12 +2,14 @@ import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import app from './modules/app'
import user from './modules/user'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
app
app,
user
},
getters
})

82
src/store/modules/user.js Normal file
View File

@ -0,0 +1,82 @@
import { login } from '@/api/user'
import { loginByCode } from '@/api/user'
import {
saveToken,
saveLoginStatus,
saveUserInfo,
removeToken,
removeUserInfo,
loadLoginStatus,
loadToken,
loadUserInfo
} from '@/utils/cache'
const state = {
loginStatus: loadLoginStatus(), // 登录状态
token: loadToken(), // token
userInfo: loadUserInfo() // 用户登录信息
}
const mutations = {
SET_USERINFO: (state, userInfo) => {
state.userInfo = userInfo
},
SET_LOGIN_STATUS: (state, loginStatus) => {
state.loginStatus = loginStatus
},
SET_TOKEN: (state, token) => {
state.token = token
}
}
const actions = {
loginUrl({ commit }, path) {
// const url = baseUrl + path
return new Promise((resolve, reject) => {
login({ redirectUri: path })
.then(response => {
resolve(response)
})
.catch(error => {
reject(error)
})
})
},
// 登录相关
loginWechatAuth({ commit, state }, code) {
const data = {
code: code
}
return new Promise((resolve, reject) => {
loginByCode(data)
.then(res => {
console.log(res)
commit('SET_USERINFO', saveUserInfo(res.data.user))
commit('SET_TOKEN', saveToken(res.data.token))
resolve(res)
})
.catch(error => {
reject(error)
})
})
},
// 设置状态
setLoginStatus({ commit, state }, query) {
if (query === 0 || query === 1) {
// 上线打开注释,本地调试注释掉
removeToken()
removeUserInfo()
}
commit('SET_LOGIN_STATUS', saveLoginStatus(query))
},
// 保存用户个人信息
setUserInfo({ commit, state }, query) {
commit('SET_USERINFO', saveUserInfo(query))
}
}
export default {
namespaced: true,
state,
mutations,
actions
}

48
src/utils/cache.js Normal file
View File

@ -0,0 +1,48 @@
import cookies from 'js-cookie'
import storage from 'good-storage'
const LoginStatusKey = 'Login-Status' // 登录态 0未授权未登录 1授权未登录 2 登陆成功
const TokenKey = 'Access-Token' // token
const UserInfoKey = 'User-Info' // 用户信息 {} {...}
export function loadLoginStatus() {
return cookies.get(LoginStatusKey) || 0
}
export function saveLoginStatus(status) {
cookies.set(LoginStatusKey, status, { expires: 7 })
return status
}
export function removeLoginStatus() {
cookies.remove(LoginStatusKey)
return ''
}
export function loadToken() {
return storage.get(TokenKey, '')
}
export function saveToken(token) {
storage.set(TokenKey, token)
return token
}
export function removeToken() {
storage.remove(TokenKey)
return ''
}
export function loadUserInfo() {
return storage.get(UserInfoKey, {})
}
export function saveUserInfo(userInfo) {
storage.set(UserInfoKey, userInfo)
return userInfo
}
export function removeUserInfo() {
storage.remove(UserInfoKey)
return {}
}

View File

@ -0,0 +1,7 @@
const title = '蚁小宝'
export default function getPageTitle(pageTitle) {
if (pageTitle) {
return `${pageTitle} - ${title}`
}
return `${title}`
}

View File

@ -17,10 +17,10 @@ export function parseTime(time, cFormat) {
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
time = parseInt(time)
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
if (typeof time === 'number' && time.toString().length === 10) {
time = time * 1000
}
date = new Date(time)
@ -37,7 +37,9 @@ export function parseTime(time, cFormat) {
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
if (key === 'a') {
return ['日', '一', '二', '三', '四', '五', '六'][value]
}
if (result.length > 0 && value < 10) {
value = '0' + value
}
@ -108,3 +110,15 @@ export function param2Obj(url) {
'"}'
)
}
// 获取 localStorage
export function getStorage(key) {
return window.localStorage.getItem(key)
}
// 设置 localStorage
export function setStorage(key, value) {
return window.localStorage.setItem(key, value)
}
// 删除 localStorage
export function removeStorage(key) {
return Cookies.remove(key)
}

View File

@ -20,7 +20,7 @@ service.interceptors.request.use(
})
}
if (store.getters.token) {
config.headers['X-Token'] = ''
config.headers['ukey'] = store.getters.token
}
return config
},
@ -34,22 +34,17 @@ service.interceptors.request.use(
service.interceptors.response.use(
response => {
Toast.clear()
// 如果是数据流
if (response.config.responseType === 'arraybuffer') {
return response.data
} else {
const res = response.data
if (res.status !== 200) {
// 登录超时,重新登录
if (res.status === 401) {
store.dispatch('FedLogOut').then(() => {
location.reload()
})
}
return Promise.reject(res || 'error')
} else {
return Promise.resolve(res)
const res = response.data
if (res.status && res.status !== 200) {
// 登录超时,重新登录
if (res.status === 401) {
store.dispatch('FedLogOut').then(() => {
location.reload()
})
}
return Promise.reject(res || 'error')
} else {
return Promise.resolve(res)
}
},
error => {

View File

@ -18,3 +18,14 @@ export function validUsername(str) {
const valid_map = ['admin', 'editor']
return valid_map.indexOf(str.trim()) >= 0
}
/* 手机号*/
export function mobile(str) {
const reg = /^[1][3,4,5,6,7,8,9][0-9]{9}$/
return reg.test(str)
}
/* 数字 */
export function number(str) {
const reg = /^\d{4}$/
return reg.test(str)
}

View File

@ -0,0 +1,129 @@
<!-- coupon.vue -->
<template>
<div class="app-container">
<van-nav-bar title="我的优惠券" left-arrow @click-left="()=>{this.$router.push('/account')}" />
<van-tabs v-model="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-tabs>
<div class="coupon-list-warpper">
<div class="coupon-item">
<div class="coupon-amount-warp">
<div class="coupon-amount-number">
<span class="rmb">¥</span>
<span>50</span>
</div>
<p><span>¥199可用</span></p>
</div>
<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 class="coupon-item">
<div class="coupon-amount-warp">
<div class="coupon-amount-number">
<span class="rmb">¥</span>
<span>50</span>
</div>
<p><span>¥199可用</span></p>
</div>
<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>
</template>
<script>
import { NavBar, Tab, Tabs } from 'vant'
export default {
components: {
'van-nav-bar': NavBar,
'van-tabs': Tabs,
'van-tab': Tab
},
data () {
return {
tabActive: 0
}
},
computed: {},
mounted () { },
methods: {}
}
</script>
<style lang='scss'>
.coupon-tabs {
.van-tab {
font-size: 17px !important;
font-weight: 500 !important;
}
}
.coupon-list-warpper {
padding: 12px;
.coupon-item {
display: flex;
width: 100%;
height: 100px;
margin-bottom: 12px;
.coupon-amount-warp {
width: 118px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: url("../../assets/images/coupon/s-coupon-item@2x.png")
no-repeat center center;
background-size: contain;
color: #fff;
.coupon-amount-number {
font-size: 30px;
.rmb {
font-size: 15px;
}
}
}
.coupon-info-warp {
display: flex;
flex-direction: column;
background: #fff;
flex: 1;
.coupon-info {
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 12px 11px 9px 11px;
height: 70px;
border-bottom: 2px dotted #ddd;
.coupon-title {
font-size: 16px;
line-height: 23px;
color: #333333;
}
.off-date {
font-size: 10px;
line-height: 14px;
color: #999999;
}
}
.coupon-detail {
color: #666666;
line-height: 30px;
padding-left: 11px;
}
}
}
}
</style>

204
src/views/account/index.vue Normal file
View File

@ -0,0 +1,204 @@
<!-- home.vue -->
<template>
<div class="app-container">
<div class="user-info-container">
<div class="user-info-warpper">
<div class="user-info">
<img class="user-avatar" :src="vipInfo.userImg" alt="用户头像">
<div class="user-info-center">
<div class="user-name">{{ vipInfo.nickName|formatName }}<span class="user-level"></span></div>
<div class="user-code">代购编号{{ vipInfo.userCode }}</div>
</div>
<div class="qrcode-warp" @click="showQrcode($event)">
<qrcode :value="vipInfo.userCode" tag="img" :options="{ width: 45,margin:0 }"></qrcode>
</div>
</div>
<div class="account-info">
<div class="account-info-item">
<span class="account-amount"> ¥{{ vipInfo.availableBalance|formatCentMoney }}</span>
<span class="account-label">余额</span>
</div>
<div class="account-info-item">
<span class="account-amount">{{ vipInfo.userGold }}</span>
<span class="account-label">金币</span>
</div>
</div>
</div>
</div>
<div>
<van-cell-group>
<van-cell is-link>
<div slot="title" class="account-cell">
<span class="icon-ticket"></span>
<span class="custom-text">我的入场券 </span>
</div>
</van-cell>
<van-cell value-class="coupon-number" :value="vipInfo.couponAvailable+'张'" is-link to="/coupon">
<div slot="title" class="account-cell">
<span class="icon-coupon"></span>
<span class="custom-text">优惠券 </span>
</div>
</van-cell>
</van-cell-group>
</div>
<!-- 绑定手机 -->
<msg-code :visible="codeVisible"></msg-code>
<!-- 二维码大图 -->
<van-popup v-model="qrcodeVisible" style="padding:10px;border-radius:10px">
<div class="popup-container">
<img :src="qrSrc" alt="">
</div>
</van-popup>
</div>
</template>
<script>
import VueQrcode from '@chenfengyuan/vue-qrcode'
import { Popup, Cell, CellGroup } from 'vant'
import VerificationCode from '@/components/VerificationCode'
import { getAccountInfo } from '@/api/user'
export default {
components: {
'van-cell-group': CellGroup,
'van-cell': Cell,
'msg-code': VerificationCode,
'qrcode': VueQrcode,
'van-popup': Popup
},
data () {
return {
qrcodeVisible: false,
codeVisible: false,
vipInfo: {},
qrSrc: ''
}
},
computed: {},
created () {
this.init()
},
methods: {
//
async init () {
//
const { data } = await getAccountInfo()
this.vipInfo = data.vipUserInfo
},
showQrcode (event) {
this.qrSrc = event.target.currentSrc
this.qrcodeVisible = true
}
}
}
</script>
<style lang='scss' scoped>
h1 {
background: red;
width: 375px;
}
.user-info-container {
padding: 12px 12px 35px 12px;
background: #ea1314;
position: relative;
overflow: hidden;
.user-info-warpper {
position: relative;
z-index: 100;
border-radius: 10px;
background: #fff;
display: flex;
flex-direction: column;
padding: 18px 16px 20px 16px;
box-shadow: 0px 6px 12px 0px rgba(231, 231, 231, 0.5);
.user-info {
display: flex;
border-bottom: 1px solid #e9e9e9;
padding-bottom: 18px;
.user-avatar {
width: 50px;
height: 50px;
border-radius: 10px;
background: #ddd;
}
.user-info-center {
margin-left: 15px;
display: flex;
flex-direction: column;
justify-content: space-between;
flex: 1;
.user-name {
font-size: 17px;
font-weight: 500;
line-height: 24px;
}
.user-code {
font-size: 13px;
}
}
.qr-code {
width: 50px;
height: 50px;
}
}
.account-info {
display: flex;
padding-top: 15px;
.account-info-item {
width: 50%;
display: flex;
align-items: center;
flex-direction: column;
.account-amount {
font-size: 17px;
line-height: 24px;
color: #ea1314;
font-weight: 500;
}
.account-label {
color: #666666;
font-size: 14px;
line-height: 20px;
}
}
}
}
&:after {
content: "";
width: 425px;
height: 75px;
background: #fff;
position: absolute;
bottom: 0;
left: -25px;
z-index: 1;
border-top-right-radius: 50px;
border-top-left-radius: 50px;
}
}
.account-cell {
display: flex;
align-items: center;
.icon-ticket {
width: 22px;
height: 19px;
margin-right: 10px;
display: inline-block;
background: url("../../assets/images/account/s-ticket@2x.png") no-repeat
center center;
background-size: cover;
}
.icon-coupon {
width: 22px;
height: 15px;
margin-right: 10px;
display: inline-block;
background: url("../../assets/images/account/s-conpon@2x.png") no-repeat
center center;
background-size: cover;
}
.coupon-number {
color: #ea1314;
font-weight: 500;
}
}
</style>

View File

@ -47,7 +47,8 @@ module.exports = {
overlay: {
warnings: false,
errors: true
}
},
disableHostCheck: true
},
configureWebpack: config => {
// 为生产环境修改配置...
@ -88,6 +89,7 @@ module.exports = {
if (process.env.NODE_ENV === 'development') {
args[0].cdn = cdn.dev
}
console.log(args)
return args
})