Authored by ccbikai(👎🏻🍜)

Merge remote-tracking branch 'origin/feature/grow' into release/5.7

'use strict';
const mRoot = '../models';
const headerModel = require('../../../doraemon/models/header'); // 头部model
const gradeNewModel = require(`${mRoot}/grade-new`);
exports.index = (req, res, next) => {
let responseData = {
module: 'home',
page: 'grade-new',
pageHeader: headerModel.setNav({
navTitle: '会员等级'
}),
title: '会员等级',
width750: true,
localCss: true,
pageFooter: true
};
let params = {
uid: req.user.uid,
channel: req.query.channel || 1
};
req.ctx(gradeNewModel).index(params).then(result => {
res.render('grade-new/index', Object.assign(responseData, result));
}).catch(next);
};
exports.content = (req, res, next) => {
let responseData = {
module: 'home',
page: 'grade-new',
pageHeader: headerModel.setNav({
navTitle: '会员等级'
}),
title: '会员等级',
width750: true,
localCss: true,
pageFooter: true
};
let params = {
uid: req.user.uid,
channel: req.query.channel || 1
};
req.ctx(gradeNewModel).index(params).then(result => {
res.render('grade-new/content', Object.assign(responseData, result));
}).catch(next);
};
exports.grow = (req, res, next) => {
let responseData = {
module: 'home',
page: 'grade-new',
pageHeader: headerModel.setNav({
navTitle: '成长记录'
}),
title: '成长记录',
width750: true,
localCss: true,
pageFooter: true
};
let params = {
uid: req.user.uid,
page: 1
};
req.ctx(gradeNewModel).grow(params).then(result => {
res.render('grade-new/grow', Object.assign(responseData, result));
}).catch(next);
};
exports.getHis = (req, res, next) => {
let params = {
uid: req.user.uid,
page: req.query.page
};
req.ctx(gradeNewModel).getHisAjax(params).then(result => {
res.json(result);
}).catch(next);
};
... ...
'use strict';
const api = global.yoho.API;
const helpers = global.yoho.helpers;
const _ = require('lodash');
const moment = require('moment');
// 格式年月日
const _formatDay = (day) => {
return moment(day).format('YYYY-MM-DD');
};
/**
* 会员等级
* @param params
*/
module.exports = class extends global.yoho.BaseModel {
constructor(ctx) {
super(ctx);
}
getGradeGrade(uid, channel) {
let options = {
data: {
method: 'app.passport.vip',
uid: uid,
channel: channel || 1
},
param: {
code: 200
}
};
return this.get(options).then(result => {
return result;
});
}
getGradeUser(uid, channel) {
let options = {
data: {
method: 'app.passport.profile',
uid: uid,
channel: channel || 1
},
param: {
code: 200
}
};
return this.get(options).then(result => {
return result;
});
}
getGrowthVip(uid) {
let options = {
data: {
method: 'app.passport.growthvip',
uid: uid
},
param: {
code: 200
}
};
return this.get(options).then(result => {
return result;
});
}
getHis(params) {
let options = {
data: {
method: 'app.passport.growthhistory',
uid: params.uid,
page: params.page
},
param: {
code: 200
}
};
return this.get(options).then(result => {
return result;
});
}
index(param) {
if (param.uid) {
return api.all([
this.getGradeGrade(param.uid, param.channel),
this.getGradeUser(param.uid, param.channel),
this.getGrowthVip(param.uid)
]).then((result) => {
let resu = {
vipGrade: []
};
let enp = {};
let obj = {
privilege: []
};
if (result[0] && result[0].data) {
_.forEach(result[0].data.enjoy_preferential, function(val) {
enp = {
description: val.description,
pic: val.pic,
title: val.title,
href: val.id === 8
};
obj.privilege.push(enp);
});
switch (result[0].data.current_vip_level) {
case '0': // 普通会员
obj = _.assign(obj, {
vip0: true
});
break;
case '1': // 银卡会员
obj = _.assign(obj, {
vip1: true
});
break;
case '2': // 金卡会员
obj = _.assign(obj, {
vip2: true
});
break;
case '3': // 白金会员
obj = _.assign(obj, {
vip3: true
});
break;
default:
}
obj = _.assign(obj, {
allUrl: helpers.urlFormat('/home/privilege')
});
}
if (result[1] && result[1].data) {
obj = _.assign(obj, {
name: result[1].data.nickname,
headIco: result[1].data.head_ico
});
}
if (result[2] && result[2].data) {
let nowGrowth = parseInt(result[2].data.current_total_growth, 10) || 0;
let nextGrowth = parseInt(result[2].data.upgrade_need_growth, 10) || 0;
obj = _.assign(obj, {
nowGrowth: nowGrowth,
nextGrowth: nextGrowth,
percent: nowGrowth < 7000 ? nowGrowth / 70 : 100
});
}
resu.vipGrade.push(obj);
return resu;
});
} else {
return Promise.resolve({
noUid: true
});
}
}
grow(param) {
return api.all([
this.getGrowthVip(param.uid),
this.getHis(param)
]).then((result) => {
let resu = {
levelHis: [],
detailHis: []
};
if (result) {
if (result[0] && result[0].data) {
let build = [];
if (result[0].data.sliver_start_time) {
build.push({
tip: '普通升级为银卡',
time: _formatDay(result[0].data.sliver_start_time * 1000)
});
}
if (result[0].data.gold_start_time) {
build.push({
tip: '银卡升级为金卡',
time: _formatDay(result[0].data.gold_start_time * 1000)
});
}
if (result[0].data.whitegold_start_time) {
build.push({
tip: '金卡升级为白金',
time: _formatDay(result[0].data.whitegold_start_time * 1000)
});
}
resu.levelHis = build;
}
if (result[1] && result[1].data && result[1].data.data) {
let build = [];
_.forEach(result[1].data.data, function(val) {
build.push({
title: val.typeDesc,
time: _formatDay(val.createTime),
value: val.growthValue
});
});
resu.detailHis = build;
}
}
return resu;
});
}
getHisAjax(param) {
return api.all([
this.getHis(param)
]).then((result) => {
let resu = {
detailHis: []
};
if (result && result[0] && result[0].data) {
let build = [];
_.forEach(result[0].data.data, function(val) {
build.push({
title: val.typeDesc,
time: _formatDay(val.createTime),
value: val.growthValue
});
});
resu.detailHis = build;
}
return resu;
});
}
};
... ...
... ... @@ -28,6 +28,7 @@ const help = require(`${cRoot}/help`);
const suggest = require(`${cRoot}/suggest`);
const message = require(`${cRoot}/message`);
const onlineService = require(`${cRoot}/onlineService`);
const gradeNew = require(`${cRoot}/grade-new`);
// const myDetail = require(`${cRoot}/myDetail);
... ... @@ -63,7 +64,7 @@ router.post('/orders/changeAddress', orderDetailController.changeAddress); //
router.get('/', homeController.index); // 个人中心首页
router.get('/mydetails', auth, homeController.myDetails); // 个人基本资料页面
router.get('/grade', auth, homeController.grade); // 会员等级页
// router.get('/grade', auth, homeController.grade); // 会员等级页
router.get('/privilege', homeController.preferential); // 会员特权列表页
router.get('/mycurrency', auth, currencyController.myCurrency); // yoho币总数
... ... @@ -145,4 +146,9 @@ router.get('/getaddress.json', addressController.newGetAddress); // TODO 模拟
router.get('/logistic', auth, orderDetailController.logistic); // 查看物流页面
router.get('/grade', auth, gradeNew.index); // 会员等级
router.get('/gradeNew/content', auth, gradeNew.content); // app要提供我的等级单独tab部分页面
router.get('/gradeNew/grow', auth, gradeNew.grow); // 成长记录
router.get('/gradeNew/getHis', gradeNew.getHis); // 成长记录
module.exports = router;
... ...
<div class="grade-new-c">
<div class="tab-item">
{{> grade-new/content}}
</div>
</div>
... ...
<div class="grade-new-c">
<div class="tab">
<span class="active">成长值明细</span>
<span>成长记录</span>
</div>
<div class="tab-item">
<div class="ul-detail">
{{# detailHis}}
<div class="li-item">
<div class="text">
<p>{{title}}</p>
<p>{{time}}</p>
</div>
<div class="num">+{{value}}</div>
</div>
{{/ detailHis}}
</div>
</div>
<div class="tab-item hide">
<div class="ul-detail">
{{# levelHis}}
<div class="li-item">
<div class="title">{{tip}}</div>
<div class="date">{{time}}</div>
</div>
{{/ levelHis}}
</div>
</div>
</div>
... ...
<div class="grade-new-c">
<div class="tab">
<span class="active">我的等级</span>
<span>我的权益</span>
</div>
<div class="tab-item">
{{> grade-new/content}}
</div>
<div class="tab-item hide">
{{# vipGrade}}
<div class="vip-privilege-page clearfix">
{{> vip-grade/privilege}}
</div>
<a class="big-top" href="{{allUrl}}">
查看全部VIP特权
<span class="iconfont">&#xe604;</span>
</a>
{{/ vipGrade}}
</div>
</div>
... ...
<div class="vip-privilege-page yoho-page">
<div class="privilege-list clearfix">
{{#each privilege}}
<a{{#if href}} href="//m.yohobuy.com/activity/birthday"{{/if}}>
<img class="icon" src="{{pic}}">
<p>
{{title}}
<span>{{description}}</span>
</p>
<div class="privilege-list clearfix">
{{#each privilege}}
<a{{#if href}} href="//m.yohobuy.com/activity/birthday"{{/if}}>
<img class="icon" src="{{pic}}">
<p>
{{title}}
<span>{{description}}</span>
</p>
{{#if href}}
<span class="iconfont">&#xe604;</span>
{{/if}}
</a>
{{/each}}
</div>
</a>
{{/each}}
</div>
</div>
... ...
<a class="big-top" href='//m.yohobuy.com/home/gradeNew/grow?openby:yohobuy={"action":"go.h5","params":{"url":"http://m.yohobuy.com/home/gradeNew/grow"}}'{{#if noUid}} style="display:none;"{{/if}}>
成长值进度
<span class="iconfont">&#xe604;</span>
<span class="s-title">成长值记录</span>
</a>
<div class="user-info clearfix{{#if noUid}} hide{{/if}}">
<div class="base clearfix">
{{# vipGrade}}
<div class="pic"{{#if headIco}} style="background-image:url({{image headIco 125 125}})"{{/if}}>
{{#if vip0}}
<div class="level-pic vip-0"></div>
{{else if vip1}}
<div class="level-pic vip-1"></div>
{{else if vip2}}
<div class="level-pic vip-2"></div>
{{else if vip3}}
<div class="level-pic vip-3"></div>
{{/if}}
</div>
<div class="intro">
<div class="name">{{name}},您好!</div>
<div class="level">
<span class="now">我的成长值:<span class="val">{{nowGrowth}}</span></span>
{{#if nextGrowth}}
<span class="next">下次升级还需:<span class="val">{{nextGrowth}}</span></span>
{{/if}}
</div>
</div>
{{/ vipGrade}}
</div>
<div class="level-process clearfix">
<div class="line-c">
{{# vipGrade}}
<div class="line"{{#if percent}} style="width:{{percent}}%"{{/if}}></div>
{{/ vipGrade}}
<div class="point">
<div class="point-item">
<div class="level-text">
<p>普通会员</p>
<p>0</p>
</div>
</div>
<div class="point-item">
<div class="level-text">
<p>银卡会员</p>
<p>800</p>
</div>
</div>
<div class="point-item">
<div class="level-text">
<p>金卡会员</p>
<p>3000</p>
</div>
</div>
<div class="point-item">
<div class="level-text">
<p>白金会员</p>
<p>7000</p>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="big-top" href='//m.yohobuy.com/service/chatQaList?openby:yohobuy={"action":"go.sfhome"}'>
会员等级介绍
<span class="iconfont">&#xe604;</span>
<span class="s-title">了解等级规则</span>
</a>
<div class="content">
<p>1.注册成功即为普通会员,各会员等级均按照历史累计有效的成长值升降级,会员等级越高可享受会员权益越多。</p>
<p>2.成长值为有货会员通过购物、评价、登录等获得经验累积值。</p>
<p>3.会员升级后,会员等级有效期一年。每365天进行累计成长值扣减,扣减后的成长值不满足当前级别条件的,直接降级至对应成长值级别。</p>
<p>自成为银卡之日开始,365天内未继续晋级更高级别,则在366天扣减当前级别所需成长值,扣减值为400</p>
<p>自成为金卡之日开始,365天内未继续晋级更高级别,则在366天扣减当前级别所需成长值,扣减值为1500</p>
<p>自成为白金卡之日开始,365天内未继续晋级更高级别,则在366天扣减当前级别所需成长值,扣减值为3500</p>
</div>
<div class="big-top">
成长值获取方法
</div>
<div class="table-c">
<table>
<tr>
<th>场景</th>
<th>获取规则</th>
<th>成长数值</th>
</tr>
<tr>
<td>购物</td>
<td>
按商品实际付款金额1元累计1个
<br />
成长值订单完成后奖励</td>
<td>
等于实际
<br />
付款金额
</td>
</tr>
<tr>
<td>
月度购买
<br />
次数
</td>
<td>
自然月内达到1个购物天数
<br />
且订单已完成
</td>
<td>20</td>
</tr>
<tr>
<td>完善资料</td>
<td>完善个人资料</td>
<td>50</td>
</tr>
<tr>
<td>
手机邮箱
<br />
验证
</td>
<td>完成手机邮箱双验证</td>
<td>20</td>
</tr>
<tr>
<td>VIP登录</td>
<td>VIP每日登录2个成长值</td>
<td>2</td>
</tr>
<tr>
<td>评价</td>
<td>
评价成功且审核通过10个成长值
<br />
一个商品仅限一次
</td>
<td>10</td>
</tr>
</table>
<p>扣除成长值的情况:</p>
<p>发生退货或删除评价,扣除当时获得的成长值。</p>
</div>
... ...
... ... @@ -10,4 +10,4 @@
</div>
</div>
{{> chat/chat-gm}}
</div>
\ No newline at end of file
</div>
... ...
{{# detailHis}}
<div class="li-item">
<div class="text">
<p>{{title}}</p>
<p>{{time}}</p>
</div>
<div class="num">+{{value}}</div>
</div>
{{/ detailHis}}
... ...
'use strict';
import {
Controller
} from 'yoho-mvc';
import {
TabView,
GetContent
} from './view';
import {
globalSearch as search
} from './model';
let hisContent = require('home/grade-new.hbs');
class GradeController extends Controller {
constructor() {
super();
this.tabView = new TabView();
this.content = new GetContent();
this.content.on('search', this.doSearch.bind(this));
this.page = 1;
}
doSearch() {
if (!this.end) {
this.page ++;
this.search(this.page);
}
}
search(page) {
search('//m.yohobuy.com/home/gradeNew/getHis', {page: page}).then(data => {
if (data.detailHis <= 0) {
this.end = true;
} else {
$('.ul-detail:first').append(hisContent(data));
}
}).catch(() => {});
}
}
module.exports = GradeController;
... ...
require('home/grade-new.page.css');
const GradeController = require('./controller');
new GradeController();
... ...
'use strict';
import {
http
} from 'yoho-mvc';
function globalSearch(url, data) {
return http({
url: location.protocol + url,
data: data,
});
}
export {
globalSearch
};
... ...
import {
View
} from 'yoho-mvc';
class TabView extends View {
constructor() {
super('.tab');
this.end = false;
this.on('touchend touchcancel', 'span', this.tabClick.bind(this));
}
tabClick(e) {
let $this = $(e.currentTarget);
if (!$this.hasClass('active')) {
let $index = $this.index();
$this.addClass('active').siblings('span').removeClass('active');
$('.tab-item:eq(' + $index + ')').removeClass('hide').siblings('.tab-item').addClass('hide');
window.rePosFooter();
}
}
}
class GetContent extends View {
constructor() {
super('.grade-new-c');
// srcoll to load more
$(window).scroll(() => {
window.requestAnimationFrame(this.scrollHandler.bind(this));
});
}
scrollHandler() {
if (($(window).scrollTop() + $(window).height() >= $(document).height()) && $('.ul-detail').length > 0) {
this.emit('search');
}
}
}
export {
TabView,
GetContent
};
... ...
.grade-new-c {
background-color: #f0f0f0;
padding-bottom: 30px;
.tab {
height: 80px;
line-height: 80px;
padding: 18px 0;
background-color: #fff;
span {
line-height: 44px;
display: inline-block;
width: 375px;
text-align: center;
float: left;
font-size: 28px;
color: #b0b0b0;
&:first-child {
border-right: solid 1px #e5e5e5;
}
}
span.active {
color: #444;
}
}
.big-top {
display: block;
font-size: 28px;
color: #444;
line-height: 88px;
margin-top: 20px;
background-color: #fff;
padding: 0 30px;
.s-title {
float: right;
padding-right: 10px;
}
.iconfont {
float: right;
}
}
.user-info {
padding: 30px 30px 115px;
background-color: #fff;
border-top: solid 1px #e5e5e5;
.base {
.pic {
width: 125px;
height: 125px;
float: left;
background-size: 100% 100%;
background-position: center;
border-radius: 50%;
border: 1px solid #eee;
position: relative;
background-image: resolve("home/index/user-avatar.png");
.level-pic {
width: 82px;
height: 36px;
background-size: 100% 100%;
position: absolute;
bottom: -18px;
left: 50%;
margin-left: -41px;
}
.vip-3 {
background-image: url("/home/grade-new/vip-3.png");
}
.vip-2 {
background-image: url("/home/grade-new/vip-2.png");
}
.vip-1 {
background-image: url("/home/grade-new/vip-1.png");
}
}
.intro {
padding: 15px 0 15px 20px;
float: left;
width: 560px;
}
.name {
color: #444;
font-size: 32px;
line-height: 60px;
}
.level {
font-size: 24px;
line-height: 40px;
}
.val {
font-size: 32px;
}
.now {
float: left;
}
.next {
float: right;
}
}
.level-process {
.line-c {
width: 600px;
height: 4px;
background-color: #f0f0f0;
margin: 50px auto 0;
position: relative;
}
.line {
width: 0;
height: 100%;
max-width: 100%;
background-color: #444;
}
.point {
width: 619px;
position: absolute;
left: -10px;
top: -9px;
}
.point-item {
width: 22px;
height: 22px;
background-image: url("/home/point.png");
background-size: auto 100%;
background-repeat: no-repeat;
float: left;
position: relative;
}
.point-item:nth-child(1) {
margin-right: 57px;
.level-text {
margin-left: -80px;
}
}
.point-item:nth-child(2) {
margin-right: 167px;
.level-text {
margin-left: -40px;
}
}
.point-item:nth-child(3) {
margin-right: 307px;
}
.level-text {
width: 120px;
position: absolute;
top: 29px;
left: 50%;
margin-left: -60px;
font-size: 24px;
text-align: center;
line-height: 40px;
}
}
}
.content {
background-color: #fff;
padding: 30px;
border-top: solid 1px #e5e5e5;
p {
line-height: 40px;
margin-top: 5px;
}
}
.table-c {
width: 100%;
background-color: #fff;
padding-bottom: 30px;
table {
width: 100%;
}
th {
border: solid 1px #e5e5e5;
font-size: 24px;
text-align: center;
font-weight: bold;
padding: 15px;
line-height: 40px;
}
td {
border: solid 1px #e5e5e5;
font-size: 24px;
text-align: center;
padding: 15px;
line-height: 40px;
}
p {
font-size: 24px;
padding: 0 30px;
margin-top: 20px;
}
}
.vip-privilege-page {
background-color: #fff;
padding: 0 30px;
margin-top: 20px;
}
.privilege-list {
background: #fff;
.icon {
float: left;
display: block;
width: 70px;
height: 70px;
}
a {
width: 690px;
clear: both;
float: left;
padding: 25px 0;
border-bottom: solid 1px #e5e5e5;
&:last-child {
border-bottom: 0;
}
.iconfont {
float: right;
line-height: 70px;
}
}
p {
font-size: 28px;
margin-left: 30px;
min-height: 70px;
width: 560px;
float: left;
}
span {
display: block;
color: #b0b0b0;
font-size: 22px;
}
}
.ul-detail {
margin-top: 20px;
.li-item {
height: 120px;
background-color: #fff;
border-bottom: solid 1px #e5e5e5;
padding: 20px 30px;
line-height: 80px;
}
.text {
width: 600px;
float: left;
p:first-child {
line-height: 40px;
font-size: 28px;
color: #444;
}
p:last-child {
line-height: 40px;
font-size: 25px;
color: #b0b0b0;
}
}
.num {
width: 90px;
float: left;
text-align: right;
line-height: 80px;
font-size: 24px;
}
.title {
float: left;
width: 500px;
font-size: 28px;
color: #444;
}
.date {
width: 190px;
font-size: 28px;
color: #444;
float: right;
}
}
}
... ...