Commit 3113c659 authored by qinhaitao's avatar qinhaitao

feat: 🎸 init

parent 3373bfd2
No preview for this file type
// babel-preset-taro 更多选项和默认值:
// https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md
/* eslint-disable*/
module.exports = {
presets: [
[ 'taro', {
framework: 'react',
ts: false
} ]
]
}
# 新增requestType 说明
## 背景
淘宝需求涉及下单、会员等需要跟淘宝进行强交互的需求时,目前在自测阶段只能通过服务端自测,但是对于
客户端开发的同学,此刻只能通过`yapi`这种静态数据mock的方式获取数据进行展示,其中的不足是无法与
整个业务衔接
## 目的
客户端通过像配置`yapi`那样配置风驰台开放的接口,就能模拟下单、会员等强交互的业务,且整个业务的数
据完成跟服务端实际做的一致,在自测阶段即可模拟淘宝测试环境将强交互的业务需求打通
## 配合
要做到能正常调用风驰台接口,必须服务端同学配合,可以理解成风驰台只是一个中介,真正处理业务逻辑是
在服务端同学的本地机器启动的服务上
## ☑️忽略 Webview 域名合法性检查(仅在本地模拟、预览和远程调试时生效)
具体可在`淘宝开发工具`-> 右上角`详情`-> 页面最底下
### 如何获取到`tornadoAPI`地址?
最简单的方法是:
- 直接问服务端,让其开启服务,并直接给到tornadoAPI地址
### 如何查看表数据?
- 访问地址:https://tornado.duibadev.com.cn/#/data 选择项目,您想要获取的数据均可以在`基础表`查看到
### 如何模拟下单、会员
- 访问地址:https://tornado.duibadev.com.cn/#/data 选择项目,拉到页面最底下,`淘宝api`接口等着你设置模拟数据
const app = getApp();
const { tbcc, cloudName, cloud, requestType } = app;
const { utils: { generateAPI }, request: tbccRequest, capi } = tbcc;
const request = tbccRequest({
cloud,
cloudName,
requestType
});
// api handle 列表
const apiList = {
...capi,
getActivityBaseInfoById: {
handle: 'getActivityBaseInfoById',
method: 'GET',
isShowLoading: true
},
login: 'login'
};
// 生成API
const API = generateAPI(apiList, request);
export default API;
import cloud from '@tbmp/mp-cloud-sdk';
import tbcc from './tbcc-sdk';
cloud.init({
env: 'test' // 云函数环境 test 测试环境 online 线上环境
});
App({
requestType: 'yapi', // cloud: 云函数, yapi: yapi 接口, ams: ams接口,tornadoAPI: 风驰台接口
cloudName: 'CommonToC', // 主云函数项目名
tornadoAPI: 'https://tornado.duibadev.com.cn/tbServer/api?db=xxx&proxyIp=xxx', // 新增:当requestType: 'tornadoAPI'时,找服务端提供地址
cloud,
tbcc,
onLaunch(options) {
this.handleQuery(options.query || {});
},
onShow(options) {
// 从后台被 scheme 重新打开
},
/**
* 处理链接参数
* @param {object} query 小程序链接上的参数
*/
handleQuery(query) {
const { activityId } = query;
this.activityId = activityId || '5f8550a3964f97be3647a635';
}
});
{
"pages": [
"pages/index/index",
"pages/tbccDemo/tbccDemo",
"pages/myprize/myprize",
"pages/collectGoods/collectGoods",
"pages/browseGoods/browseGoods",
"pages/orderGoods/orderGoods",
"pages/endPage/endPage"
],
"window": {
"allowsBounceVertical": "NO",
"defaultTitle": "C端模板",
"enableSkia":"true"
}
}
### 注意!
assets文件夹只做本地开发测试使用
因为淘宝打包有体积限制,最大不超过1M,所以打包提审请同步图片等资源到CDN上,然后删除assets文件夹再打包提审。
\ No newline at end of file
.common-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 20;
}
.common-modal__shade {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.75);
}
.common-modal__container {
position: absolute;
width: 500rpx;
height: 300rpx;
background: #4eafd7;
background-size: 100% 100%;
left: 50%;
top: 50%;
margin-left: -250rpx;
margin-top: -150rpx;
padding: 30rpx;
}
.common-modal__bd {
font-size: 28rpx;
text-align: center;
color: #fff;
padding: 50rpx 0;
}
.common-modal__close {
position: absolute;
right: -20rpx;
top: -80rpx;
height: 86rpx;
width: 86rpx;
background: url('../../assets/modal_close_btn.png') center center no-repeat;
background-size: 48rpx 48rpx;
z-index: 9;
}
.common-modal__confirm-btn {
margin: 0 auto;
text-align: center;
width: 341rpx;
height: 77rpx;
line-height: 77rpx;
background: #FFF;
background-size: 100% 100%;
font-size: 30rpx;
color: #0077a9;
font-weight: bold;
}
<view class="common-modal">
<view class="common-modal__shade modal-animate-fade-in" />
<view class="common-modal__container modal-animate-zoom-in">
<view class="common-modal__close" onTap="onModalClose" />
<view class="common-modal__bd">
<text>{{data.content}}</text>
</view>
<view class="common-modal__ft" a:if="{{data.btnText}}">
<view onTap="onModalConfirm" class="common-modal__confirm-btn">{{data.btnText}}</view>
</view>
</view>
</view>
Component({
methods: {
onModalClose() {
const { onModalClose } = this.props;
onModalClose && onModalClose();
},
onModalConfirm () {
const { onModalConfirm } = this.props;
onModalConfirm && onModalConfirm();
}
}
});
\ No newline at end of file
{
"component": true
}
\ No newline at end of file
.countdown {
position: fixed;
width: 144rpx;
height: 162rpx;
right: 0;
top: 600rpx;
z-index: 20;
}
.countdown-box-clock {
position: absolute;
top: 0;
left: 50%;
margin-left: -41rpx;
width: 82rpx;
height: 88rpx;
background-size: 100% 100%;
z-index: 10;
}
.countdown-box-clock__time {
position: absolute;
top: 5rpx;
left: 0;
width: 82rpx;
height: 88rpx;
display: flex;
align-items: center;
justify-content: center;
}
.countdown-box-clock image {
width: 100%;
height: 100%;
}
.countdown-box {
position: absolute;
width: 130rpx;
height: 18rpx;
border-radius: 10rpx;
border: 1px solid #a6fffe;
background: #111192;
box-shadow: 0 0 4rpx 1rpx #7fd5ff;
top: 25rpx;
bottom: 20rpx;
left: 0;
right: 0;
margin: auto;
overflow: hidden;
z-index: 11;
}
@keyframes countdownAnimation {
to {
transform: translateX(0);
}
}
.countdown-box .countdown-time {
height: 100%;
width: 100%;
background: linear-gradient(#3996f8, #19dffd);
}
.countdown-txt {
position: absolute;
width: 147rpx;
height: 91rpx;
bottom: 0rpx;
z-index: 1;
}
.countdown-txt image {
width: 100%;
height: 100%;
}
.countdown-txt__content {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
line-height: 25rpx;
color: #2A2A2A;
text-align: center;
font-size: 20rpx;
}
.countdown-txt .txt:first-child{
margin-top: 25rpx;
}
.countdown-two {
position: absolute;
width: 554rpx;
height: 104rpx;
left: 50%;
margin-left: -277rpx;
bottom: 70rpx;
}
.countdown-two-clock {
position: absolute;
right: -10rpx;
bottom: 10rpx;
width: 104rpx;
height: 95rpx;
z-index: 15;
}
.countdown-two-clock image {
width: 100%;
height: 100%;
}
.countdown-two-clock .countdown-box-clock__time {
position: absolute;
top: 5rpx;
left: 0;
width: 104rpx;
height: 95rpx;
display: flex;
align-items: center;
justify-content: center;
}
.countdown-two-clock .countdown-box{
position: absolute;
left: 0;
bottom: -20rpx;
}
.countdown-two-txt {
position: absolute;
right: 90rpx;
top: 5rpx;
width: 323rpx;
height: 60rpx;
z-index: 14;
background: url('//yun.dui88.com/taobaomini/clientCTest/count_bg_02@2x.png');
background-size: 100% 100%;
color: #2A2A2A;
text-align: center;
font-size: 20rpx;
line-height: 60rpx;
}
.countdown-two-txt image {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 14;
}
.countdown-two__content {
position: absolute;
left: 0;
top: 0;
width: 323rpx;
height: 60rpx;
color: #2A2A2A;
text-align: center;
font-size: 20rpx;
}
\ No newline at end of file
<view class="countdown" a:if="{{styleType == '01'}}">
<view class="countdown-box-clock">
<image mode="scaleToFill" src="{{data.clock_bg}}"/>
<view class="countdown-box-clock__time" style="{{{'color':data.countTxtColor,'fontSize':data.countTxtSize+'rpx'}}}">{{countTime}}</view>
</view>
<view class="countdown-box" style="{{{'height':data.progressHeight+'rpx','width':data.progressWidth+'rpx'}}}">
<view class="countdown-time" style="transform: translateX({{percent*10 -100}}%);animation: countdownAnimation {{countTime-percent}}s forwards linear;"></view>
</view>
<view class="countdown-txt">
<image mode="scaleToFill" src="{{data.textBg}}"/>
<view class="countdown-txt__content">
<view class="txt">{{data.text.split('\n')[0]}}</view>
<view class="txt">{{data.text.split('\n')[1]}}</view>
</view>
</view>
</view>
<view class="countdown-two" a:else>
<view class="countdown-two-clock">
<image mode="scaleToFill" src="{{data.clock_bg}}"/>
<view class="countdown-box-clock__time" style="{{{'color':data.countTxtColor,'fontSize':42+'rpx'}}}">{{countTime}}</view>
</view>
<view class="countdown-box" style="{{{'height':55+'rpx','width':490+'rpx'}}}">
<view class="countdown-time" style="transform: translateX({{percent*10 -100}}%);animation: countdownAnimation {{countTime-percent}}s forwards linear;"></view>
</view>
<view class="countdown-two-txt" style="{{{'background':`url(${data.textBg}) no-repeat`,'backgroundSize': '100% 100%'}}}">
{{data.text}}
</view>
</view>
/**
* data: {
* text: //文案描述
* textBg: //文案背景
* progressWidth: //进度条宽
* progressHeight: //进度条高
* clock_bg: //时钟icon
* countTxt: //时钟文案
* countTxtColor: //时钟文字颜色
* countTxtSize: //时钟文字大小
* }
* hasPercent: //任务完成剩余时长
* countTime: //倒计时长
* styleType: //样式(01——右对齐,02—底部)
*/
Component({
mixins: [],
data: {},
props: {
percent: 10,
hasPercent:10,
countTime: 10,
styleType: '01',//browse_txt_bg count_bg_02
data: {
text: '浏览商品10s\n获得任务奖励',
textBg: '//yun.duiba.com.cn/taobaomini/clientCTest/browse_txt_bg@2x.png',
progressWidth:'130',
progressHeight:'18',
clock_bg: '//yun.duiba.com.cn/taobaomini/clientCTest/clock@2x.png',
countTxt: '10',
countTxtColor: '#1CA4ED',
countTxtSize: '32'
}
},
didMount() {},
didUpdate() {},
didUnmount() { },
methods: {},
});
{
"component": true
}
\ No newline at end of file
.end-modal-container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
min-height: 100vh;
/* background: url('//yun.dui88.com/taobaomini/clientCTest/end_bg@2x.png') no-repeat; */
background-size: 750rpx 1624rpx;
overflow: hidden;
z-index: 20;
}
.end-modal-container-content {
position: absolute;
width: 750rpx;
height: 480rpx;
top: 50%;
left: 0;
margin-top: -240rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
}
.end-modal-container-content__icon {
width: 478rpx;
height: 340rpx;
}
.end-modal-container-content__icon image {
width: 100%;
height: 100%;
}
.end-modal-container-content__desc {
font-size: 36rpx;
font-family: Source Han Sans CN;
font-weight: 400;
color: #ADADAD;
}
\ No newline at end of file
<view class="end-modal-container" style="{{{'background':`url(${data.image.bg}) no-repeat`}}}">
<view class="end-modal-container-content">
<view class="end-modal-container-content__icon">
<image mode="scaleToFill" src="{{data.image.end_icon}}"/>
</view>
<view class="end-modal-container-content__desc">{{data.text}}</view>
</view>
</view>
\ No newline at end of file
Component({
mixins: [],
data: {},
props: {
data: {
image: {
bg: '//yun.dui88.com/taobaomini/clientCTest/end_bg@2x.png',
end_icon: '//yun.dui88.com/taobaomini/clientCTest/end_icon@2x.png',
},
text: '活动未开始'
}
},
didMount() {},
didUpdate() {},
didUnmount() {},
methods: {},
});
{
"component": true
}
\ No newline at end of file
<view class="goods-modal-container" style="{{{'background':`url(${task.image.bg}) no-repeat`,'backgroundSize': '750rpx 1624rpx'}}}">
<view a:if="{{task.type == '01'}}"
style="{{{'background':`url(${task.image.title}) no-repeat`,'backgroundSize': '100% 100%'}}}"
class="goods-modal-container__title">
</view>
<view a:else class="goods-modal-container__banner" style="{{{'background':`url(${task.image.banner}) no-repeat`,'backgroundSize': '100% 100%','height':task.bannerHeight+'rpx'}}}"></view>
<view class="goods-modal-container__content" style="{{{'top':task.type == '02'?task.bannerHeight+'rpx':'209rpx'}}}">
<scroll-view scroll-y="{{true}}" class="goods-modal-container__scroll">
<view class="goods-modal-container__content-list">
<view class="goods-modal-container__content-item" a:for="{{goodsList}}" a:for-item="item">
<view class="goods-modal-container__content-item__img" data-item="{{item}}" onTap="goToGoodsDetail">
<image mode="scaleToFill" src="{{item.image}}"/>
</view>
<view class="goods-modal-container__content-item__name" style="{{{'color':task.color}}}">{{item.name}}</view>
<view class="goods-modal-container__content-item__price">
<view class="goods-modal-container__content-item__price-num"><text>¥</text>{{item.price}}</view>
<view a:if="{{task.taskType == 'collectGoods'}}"
data-item="{{item}}"
onTap="goToCollectGoods"
class="{{item.collected ? 'goods-modal-container__content-item__price-collect' : 'goods-modal-container__content-item__price-no-collect'}}"
>
<image mode="scaleToFill" src="{{item.collected?task.image.collect:task.image.no_collect}}"/>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
\ No newline at end of file
/**
* 任务类型(taskType):collectGoods、orderGoods、browseGoods、
*/
const app = getApp();
const { tbcc } = app;
const { openDetail, collectGoods, checkGoodsCollectedStatus, commonToast } = tbcc.tb;
Component({
mixins: [],
data: {
},
props: {
isBackFlag: 0,
goodsList: [1,2,3],
task: {
itemId: '617724147979,617724563528,617300295119',
taskType: 'browseGoods',
type: '02',
bannerHeight: 430,
image: {
bg: '//yun.dui88.com/taobaomini/clientCTest/goods_bg@2x.png',
banner: '//yun.dui88.com/taobaomini/clientCTest/goods_banner@2x.png',
title: '//yun.dui88.com/taobaomini/clientCTest/goods_title@2x.png',
collect: '//yun.dui88.com/taobaomini/clientCTest/goods_collection@2x.png',
no_collect: '//yun.dui88.com/taobaomini/clientCTest/collection_no_collect@2x.png',
img: '//yun.dui88.com/taobaomini/clientCTest/goods_img@2x.png',
},
color: '#181818'
},
onCompleteTask: () => {},
onOpenDetail: () => {}
},
didMount() {
},
didUpdate(prevProps,prevData) {
},
didUnmount() {
},
methods: {
/**
* 跳转商品详情页
*/
async goToGoodsDetail(e) {
const { taskType } = this.props.task
const { itemId } = e.target.dataset.item
if(taskType == 'browseGoods') {
this.props.onOpenDetail(itemId)
}
await openDetail(String(itemId))
},
/**
* 收藏商品
*/
async goToCollectGoods(e) {
let { itemId, collected } = e.target.dataset.item
const { taskType } = this.props.task
if(collected) {
commonToast('您已收藏过该商品了')
return;
}
//判断是否活动外已收藏商品
let isCollected = await checkGoodsCollectedStatus(+itemId)
if(isCollected) {
this.props.onCompleteTask(taskType, itemId)
return;
}
let result = await collectGoods(+itemId)
if(result) {
this.props.onCompleteTask(taskType, itemId)
}
}
},
});
<view class="logistics-modal-container">
<view class="logistics-modal-container__content"
style="{{prizeItem.shipStatus == 3 ?`background: url(${task.shipped}) no-repeat;background-size: 100% 100%;`
:`background: url(${task.notShipped}) no-repeat;background-size: 100% 100%;`}}">
<view class="logistics-modal-container__close" onTap="closeModal"></view>
<view class="logistics-modal-container__content-item">
<view class="logistics-modal-container__content-left">
<image mode="scaleToFill" src="{{prizeItem.image}}"/>
</view>
<view class="logistics-modal-container__content-right">
<view class="logistics-modal-container__content-name">{{prizeItem.name}}</view>
<view class="logistics-modal-container__content-copy">
<view class="logistics-modal-container__content-copy__code">奖品编码:{{prizeItem._id}}</view>
<view class="logistics-modal-container__content-copy__img" onTap="copyPrizeId"></view>
</view>
<view class="logistics-modal-container__content-desc">{{prizeItem.shipStatus== 3?'发货状态:已发货':'发货状态:未发货'}}</view>
<view class="logistics-modal-container__content-desc" a:if="{{prizeItem.shipStatus == 2}}">温馨提示:奖品将在活动结束7天内陆续发出</view>
<view class="logistics-modal-container__content-copy" a:else>
<view class="logistics-modal-container__content-copy__code">{{'物流单号:'+prizeItem.shipNum}}</view>
<view class="logistics-modal-container__content-copy__img" style="{{`background: url(${task.copyIcon}) no-repeat;background-size: 100% 100%;`}}" onTap="copyCode"></view>
</view>
</view>
</view>
</view>
</view>
\ No newline at end of file
const app = getApp();
const { tbcc } = app;
const { commonToast } = tbcc.tb;
Component({
mixins: [],
data: {},
props: {
prizeItem: {},
onCloseModal: () => { },
task: {
shipped: '',
notShipped: '',
}
},
didMount() { },
didUpdate() { },
didUnmount() { },
methods: {
closeModal() {
this.props.onCloseModal()
},
//复制运单号
copyCode() {
let { shipNum } = this.props.prizeItem
my.setClipboard({
text: shipNum + '',
success: () => {
commonToast('复制成功')
}
});
},
copyPrizeId() {
let { _id } = this.props.prizeItem
my.setClipboard({
text: _id + '',
success: () => {
commonToast('复制成功')
}
});
}
},
});
{
"component": true
}
\ No newline at end of file
.rule-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 20;
}
.rule-modal__shade {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.75);
}
.rule-modal__container {
position: absolute;
width: 500rpx;
height: 600rpx;
background: #4eafd7;
background-size: 100% 100%;
left: 50%;
top: 50%;
margin-left: -250rpx;
margin-top: -300rpx;
}
.rule-modal__bd {
position: absolute;
top: 40rpx;
left: 40rpx;
right: 40rpx;
bottom: 40rpx;
overflow-x: hidden;
font-size: 24rpx;
color: #fff;
line-height: 1.5;
}
.rule-modal__close {
position: absolute;
right: -20rpx;
top: -80rpx;
height: 86rpx;
width: 86rpx;
background: url('../../assets/modal_close_btn.png') center center no-repeat;
background-size: 48rpx 48rpx;
z-index: 9;
}
<view class="rule-modal">
<view class="rule-modal__shade modal-animate-fade-in" />
<view class="rule-modal__container modal-animate-zoom-in">
<view class="rule-modal__close" onTap="onModalClose" />
<view class="rule-modal__bd">
<slot />
</view>
</view>
</view>
Component({
methods: {
onModalClose() {
const { onModalClose } = this.props;
onModalClose && onModalClose();
}
}
});
\ No newline at end of file
const { tbcc } = getApp();
const { constants: tbccConstants } = tbcc;
// 分享配置
export const SHARE_CONFIG = {
title: '分享测试标题',
desc: '分享测试描述',
imageUrl: '', // 分享图片URL
path: 'pages/index/index'
};
// 链接配置
export const REDIRECT_URL = {
'testpage': '/pages/testPage/testPage',
'tmall': 'https://www.tmall.com'
};
export const DRAW_STATUS = tbccConstants.DRAW_STATUS;
export const PRIZE_TYPE = tbccConstants.PRIZE_TYPE;
{
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@tbmp/mp-cloud-sdk": {
"version": "1.3.4",
"resolved": "https://registry.npm.taobao.org/@tbmp/mp-cloud-sdk/download/@tbmp/mp-cloud-sdk-1.3.4.tgz",
"integrity": "sha1-+1HCYryYfVc0kze0SOWv1oL6xgk="
},
"crypto": {
"version": "1.0.1",
"resolved": "https://registry.npm.taobao.org/crypto/download/crypto-1.0.1.tgz",
"integrity": "sha1-KvG3ytgXXSTIobB3glV5SiGAMDc="
}
}
}
{
"name": "",
"version": "1.0.0",
"main": "",
"license": "MIT",
"dependencies": {
"@tbmp/mp-cloud-sdk": "*",
"crypto": "^1.0.1"
}
}
<view class="goods-page-container">
<goods-modal
isBackFlag="{{isBackFlag}}"
goodsList="{{goodsList}}"
task="{{task}}"
onCompleteTask="onCompleteTask" />
<!-- 倒计时 -->
<count-down-modal a:if="{{isShowCountdown && completeType == 1}}" percent="{{percent}}" hasPercent="{{(10 - usePercent)}}" styleType="{{'01'}}" />
</view>
\ No newline at end of file
/**
* 任务类型(taskType): collectGoods 、 orderGoods 、 browseGoods 、exchangeCredits
* task: {
* itemId: //商品ID
* taskType: //任务类型
* type: //头部样式(02——banner,01——标题)
* image: {//图片
* bg: //集合页背景
* banner: //头部banner
* title: //头部title
* collect: //商品以收藏
* no_collect: //商品未收藏
* }
* color: //商品名称:文字颜色
* }
*/
import API from '../../api';
const app = getApp();
const { tbcc } = app;
const { commonToast } = tbcc.tb;
const completeType = 1 // 1 浏览完成 2 点击任意商品完成
const completeTime = 10 // 浏览时间(s)
const taskConfig = {
taskType: 'browseGoods',
type: '01',
image: {
bg: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_bg@2x.png',
banner: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_banner@2x.png',
title: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_title@2x.png',
collect: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_collection@2x.png',
no_collect: '//yun.duiba.com.cn/taobaomini/clientCTest/collection_no_collect@2x.png',
img: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_img@2x.png',
},
color: '#181818'
}
Page({
timer: null,
data: {
completeType: completeType,
usePercent: 0, // 已进行的进度条
percent: 0,// 进度条
isShowCountdown: false, // 是否显示倒计时
query: {},
task: taskConfig,
goodsList: []
},
onLoad(query) {
this.query = query || {}
this.initPage()
},
onShow() {
//重新回到页面开启倒计时
if(!this.timer && completeType == 1) {
this.setData({ usePercent: this.data.percent })
this.browseGoodsTimes()
}
},
onHide() {
// 页面隐藏
if (this.timer) {
clearInterval(this.timer)
}
},
onUnload() {
// 页面被关闭
if (this.timer) {
clearInterval(this.timer)
}
},
async initPage() {
const { activityId } = app
const { itemIds } = this.query
const { success, data } = await API.getItemListByItemIds({ activityId, itemIds }).catch(res => {
res && commonToast(res)
}) || {}
if(success) {
this.setData({ goodsList: data.list || [] })
}
},
//任务完成
async onCompleteTask(itemId) {
const { activityId } = app
let browsResult = await API.doBrowseGoodsTask({ activityId, itemId }).catch(res => {
res && commonToast(res)
}) || {}
if(browsResult.success) {
commonToast('浏览成功')
}
},
/**
* 跳转商品详情页
*/
onOpenDetail(itemId = '') {
if(completeType == 1) {
this.setData({
percent: this.data.usePercent,
}, () => {
clearInterval(this.timer)
})
}
else {
this.onCompleteTask(itemId)
}
},
/**
* 浏览商品倒计时
*/
browseGoodsTimes() {
this.timer = setInterval(() => {
this.setData({
usePercent: this.data.usePercent + 1
}, () => {
if (this.data.usePercent >= completeTime) {
this.onCompleteTask()
clearInterval(this.timer)
}
})
},1000)
}
});
{
"usingComponents": {
"goods-modal": "/components/goodsModal/goodsModal",
"count-down-modal": "/components/countDownModal/countDownModal"
}
}
\ No newline at end of file
<view class="goodsPage">
<goods-modal
goodsList="{{goodsList}}"
task="{{task}}"
onCompleteTask="onCompleteTask" />
</view>
\ No newline at end of file
/**
* 任务类型(taskType):collectGoods、 orderGoods 、 browseGoods 、exchangeCredits
* task: {
* itemId: //商品ID
* taskType: //任务类型
* type: //头部样式(02——banner,01——标题)
* image: {//图片
* bg: //集合页背景
* banner: //头部banner
* title: //头部title
* collect: //商品以收藏
* no_collect: //商品未收藏
* }
* color: //商品名称:文字颜色
* }
*/
import API from '../../api';
const app = getApp();
const { tbcc } = app;
const { commonToast } = tbcc.tb;
const taskConfig = {
taskType: 'collectGoods',
type: '01',
image: {
bg: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_bg@2x.png',
banner: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_banner@2x.png',
title: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_title@2x.png',
collect: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_collection@2x.png',
no_collect: '//yun.duiba.com.cn/taobaomini/clientCTest/collection_no_collect@2x.png',
img: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_img@2x.png',
},
color: '#181818'
}
Page({
data: {
query: {},
task: taskConfig,
goodsList: []
},
onLoad(query) {
this.query = query || {}
this.initPage()
},
onShow() {
},
async initPage() {
const { activityId } = app
const { itemIds } = this.query
const { success, data } = await API.getCollectGoodsList({ activityId, itemIds }).catch(res => {
res && commonToast(res)
}) || {}
if(success) {
this.setData({ goodsList: data.list || [] })
}
},
//任务完成 616444374083
async onCompleteTask(taskType,itemId) {
console.log(taskType,itemId)
const { activityId } = app
let { success, data } = await API.doCollectGoodsTask({ activityId, itemId }).catch(res => {
res && commonToast(res)
}) || {}
if(success) {
commonToast('收藏成功')
this.initPage()
}
}
});
{
"usingComponents": {
"goods-modal": "/components/goodsModal/goodsModal"
}
}
\ No newline at end of file
<view class="end-page-container">
<end-modal data="{{endData}}" />
</view>
\ No newline at end of file
Page({
data: {
endData: {
image: {
bg: '//yun.dui88.com/taobaomini/clientCTest/end_bg@2x.png',
end_icon: '//yun.dui88.com/taobaomini/clientCTest/end_icon@2x.png',
},
text: '活动未开始'
}
},
onLoad() {},
});
{
"usingComponents": {
"end-modal": "/components/endModal/endModal"
}
}
\ No newline at end of file
.index-page {
min-height: 100vh;
}
.index-rule-btn, .index-myprize-btn {
position: absolute;
top: 30rpx;
width: 126rpx;
height: 48rpx;
line-height: 48rpx;
font-size: 22rpx;
background: rgba(0, 0, 0, 0.8);
color: #FFF;
text-align: center;
z-index: 5;
}
.index-rule-btn {
left: 0;
border-radius: 0 48rpx 48rpx 0;
}
.index-myprize-btn {
right: 0;
border-radius: 48rpx 0 0 48rpx;
}
.index-page__title {
font-size: 50rpx;
text-align: center;
padding: 100rpx 0 50rpx 0;
}
.index-page__content {
text-align: center;
font-size: 30rpx;
}
.index-page__tips {
padding-top: 40rpx;
text-align: center;
font-size: 22rpx;
}
.index-start-btn {
background: #0077a9;
color: #FFF;
width: 300rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
border-radius: 10rpx;
margin: 100rpx auto 50rpx auto;
font-size: 30rpx;
}
.index-tbcc-btn {
background: #0077a9;
color: #FFF;
width: 300rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
border-radius: 10rpx;
margin: 0 auto;
font-size: 30rpx;
}
.index-link-btn {
text-align: center;
color: #333;
font-size: 26rpx;
}
\ No newline at end of file
<view class="index-page">
<view class="index-rule-btn" onTap="showRuleModal">活动规则</view>
<navigator open-type="navigate" url="/pages/myprize/myprize" class="index-myprize-btn">我的奖品</navigator>
<view class="index-page__title">{{title}}</view>
<view class="index-page__content">{{content}}</view>
<view class="index-page__tips">mock接口只支持PC端预览,手机淘宝预览需要使用云函数模式</view>
<view class="index-start-btn" onTap="doStart">开始</view>
<navigator open-type="navigate" url="/pages/tbccDemo/tbccDemo" class="index-tbcc-btn">tbcc demo</navigator>
<navigator style="{{{'marginTop':'16rpx'}}}" open-type="navigate" url="/pages/goodsPage/goodsPage?itemId=617724147979,617724563528" class="index-tbcc-btn">商品集合页面</navigator>
</view>
<rule-modal a:if="{{ruleModalVisible}}" onModalClose="closeRuleModal"><text>{{rule}}</text></rule-modal>
<common-modal a:if="{{commonModalVisible}}" data="{{commonModalData}}" onModalClose="closeCommonModal" onModalConfirm="confirmCommonModal" />
\ No newline at end of file
import { SHARE_CONFIG, REDIRECT_URL } from '../../constants';
import API from '../../api';
const app = getApp();
const { tbcc } = app;
const { commonToast, getAuthUserInfo, navigateToOutside } = tbcc.tb;
Page({
data: {
title: '',
content: '',
rule: '',
commonModalData: {
content: '',
btnText: ''
},
commonModalConfirm: null,
commonModalVisible: false,
ruleModalVisible: false
},
onLoad(query) {
// 页面加载
this.getAuth();
console.info(`Page onLoad with query: ${JSON.stringify(query)}`);
},
init() {
this.getActivityBaseInfoById();
},
onReady() {
// 页面加载完成
},
onShow() {
// 页面显示
},
onHide() {
// 页面隐藏
},
onUnload() {
// 页面被关闭
},
onTitleClick() {
// 标题被点击
},
onPullDownRefresh() {
// 页面被下拉
},
onReachBottom() {
// 页面被拉到底部
},
/**
* 获取用户授权信息
*/
async getAuth() {
const userInfo = await getAuthUserInfo().catch(err => {
console.log('未授权成功', err);
});
if (userInfo) {
console.log('userInfo', userInfo);
const { nickName, avatar } = userInfo;
app.nickName = nickName;
app.avatar = avatar;
this.login();
}
},
/**
* 获取主接口
*/
async getActivityBaseInfoById() {
const { success, data } = await API.getActivityBaseInfoById().catch(res => {
commonToast(res && res.message);
}) || {};
if (success && data) {
const { title, content, rule } = data;
this.setData({ title, content, rule });
}
},
/**
* 登录接口
*/
async login() {
const { nickName, avatar } = app;
const { success } = await API.login({
userNick: nickName, avatar, isFollow: true,entry:1
}).catch(res => {
commonToast(res && res.message);
}) || {};
if (success) {
this.init();
}
},
/**
* 显示规则弹层
*/
showRuleModal() {
this.setData({
ruleModalVisible: true
});
},
/**
* 通用弹出确认按钮回调
*/
confirmCommonModal() {
const { commonModalConfirm } = this.data;
commonModalConfirm && commonModalConfirm();
},
/**
* 执行开始
*/
doStart() {
this.showCommonModal({
content: '活动还未开始\n请稍后',
btnText: '我知道了',
commonModalVisible: true,
confirmCallback: () => {
this.closeCommonModal();
}
});
},
/**
* 显示通用弹层
* @param {object} options content: 通用弹层内容 btnText:按钮文案 confirmCallback: 按钮确认回调
*/
showCommonModal({ content, btnText, confirmCallback }) {
this.setData({
commonModalConfirm: confirmCallback || null,
commonModalVisible: true,
commonModalData: { content, btnText }
});
},
/**
* 跳转到天猫页面
*/
goTmallPage() {
navigateToOutside(REDIRECT_URL['tmall']);
},
/**
* 关闭通用弹层
*/
closeCommonModal() {
this.setData({
commonModalVisible: false
});
},
/**
* 关闭活动规则弹层
*/
closeRuleModal() {
this.setData({
ruleModalVisible: false
});
},
onShareAppMessage(res) {
console.log(res)
// 返回自定义分享信息
return SHARE_CONFIG;
},
});
{
"usingComponents": {
"rule-modal": "/components/ruleModal/ruleModal",
"common-modal": "/components/commonModal/commonModal"
}
}
\ No newline at end of file
.my-prize-container {
width: 100vw;
height: 100vh;
opacity: 1;
position: absolute;
top: 0;
left: 0;
overflow: hidden;
/* background: url('') no-repeat center top/cover; */
}
.my-prize-container .my-prize-label {
width: 208rpx;
height: 36rpx;
margin: 140rpx auto 40rpx;
background: url('//yun.dui88.com/duiba-components-c-myprizeprize_title.png') no-repeat;
background-size: 100% 100%;
}
.my-prize-content {
position: absolute;
left: 0;
top: 363rpx;
bottom: 0;
width: 100%;
}
.my-prize-content__scroll {
width: 100%;
height: 100%;
padding-top: 30rpx;
}
.my-prize-container .my-prize-list {
width: 100%;
height: auto;
}
.my-prize-container .my-prize-item__empty {
text-align: center;
color: #fff;
margin-top: 30rpx;
}
.my-prize-list .my-prize-item {
display: flex;
justify-content: center;
align-items: center;
padding: 0 28rpx 0 26rpx;
margin: 0 auto 24rpx;
position: relative;
box-sizing: border-box;
width: 700rpx;
height: 161rpx;
background-color: #ffffff;
box-shadow: 2rpx 3rpx 18rpx 0 rgba(230, 146, 255, 0.6);
border-radius: 10rpx;
box-sizing: border-box;
}
.my-prize-list .my-prize-item .my-prize-item__avatar {
width: 120rpx;
height: 120rpx;
}
.my-prize-list .my-prize-item .my-prize-item__content {
flex: 1;
min-width: 0;
padding: 0 20rpx;
display: flex;
flex-direction: column;
height: 130rpx;
align-items: flex-start;
justify-content: center
}
.my-prize-list .my-prize-item .my-prize-item__subtitle {
display: flex;
justify-content: center;
align-items: center;
}
.my-prize-list .my-prize-item .my-prize-item__copy {
width: 20rpx;
height: 20rpx;
margin-left: 18rpx;
}
.my-prize-list .my-prize-item .prize-item__status {
width: 150rpx;
}
.prize-item__status>image {
width: 100%;
}
.my-prize-list .my-prize-item .my-prize-item__name {
width: 100%;
font-size: 28rpx;
color: #2d2d2d;
letter-spacing: -1rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.my-prize-list .my-prize-item .my-prize-item__tip {
margin-top: 8rpx;
font-size: 20rpx;
line-height: 20rpx;
letter-spacing: 0;
color: #8a8a8a;
}
.my-prize-list .my-prize-item .my-prize-item__code {
width: 290rpx;
font-family: PingFangSC-Regular;
font-size: 20rpx;
color: #8a8a8a;
letter-spacing: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
\ No newline at end of file
<view class="my-prize-container" style="{{{'background':`url(${prizeConfig.bg}) no-repeat center top/cover`}}}">
<view class="my-prize-label" style="{{{'background':`url(${prizeConfig.title}) no-repeat`,'backgroundSize':'100% 100%'}}}"></view>
<view class="my-prize-content" style="{{ {'top': prizeConfig.contentTop }}}">
<scroll-view scroll-y="{{true}}" class="my-prize-content__scroll">
<view class="my-prize-list" a:if="{{myPrizeList && myPrizeList.length > 0}}">
<view class="my-prize-item" a:for="{{myPrizeList}}">
<image class="my-prize-item__avatar" src="{{item.image}}"/>
<view class="my-prize-item__content">
<label class="my-prize-item__name">{{item.name}}</label>
<view class="my-prize-item__subtitle" a:if="{{isShowPirzeId}}">
<label class="my-prize-item__code">奖品编码:{{item.id || item._id}}</label>
<!-- 复制按钮 -->
<image class="my-prize-item__copy" data-text="{{item.id || item._id}}" onTap="handleCopyId" src="{{prizeConfig.copyIcon}}" />
</view>
<view class="my-prize-item__tip" a:if="{{item.drawStatus === DRAW_STATUS.FAIL}}">{{item.remark}}</view>
<view class="my-prize-item__tip" a:if="{{item.drawStatus === DRAW_STATUS.RETRY || (item.drawStatus === DRAW_STATUS.WAITAWARD)}}">请于{{endTime}}前领取</view>
<view class="my-prize-item__tip" a:if="{{item.drawStatus === DRAW_STATUS.EXPIRED }}">奖品已过期失效</view>
</view>
<view class="prize-item__status" style="{{prizeConfig.btnStyle}}">
<!-- 实物奖品状态 -->
<block a:if="{{item.type === PRIZE_TYPE.OBJECT}}">
<image onTap="handleClick" data-item="{{item}}" src="{{objectStatus[item.drawStatus]}}" mode="widthFix" />
</block>
<!-- 非实物奖品状态 -->
<block a:else>
<image onTap="handleClick" data-item="{{item}}" src="{{(item.drawStatus === 3 && !item.useUrl) ? prizeConfig['receiveBtn'] : enameStatus[item.drawStatus]}}" mode="widthFix" />
</block>
</view>
</view>
</view>
<view a:if="{{myPrizeList && myPrizeList.length === 0}}" class="my-prize-item__empty">{{prizeConfig.blankTxt}}</view>
</scroll-view>
</view>
</view>
<logistics-modal
a:if="{{isLogisticModal}}"
prizeItem="{{currentPrize}}"
onCloseModal="closeLogisticModal"
task="{{logisticsImage}}" />
\ No newline at end of file
'use strict';
import API from "/api";
const app = getApp();
const { tbcc } = app;
const { commonToast, navigateToOutside, getUserAddress, setClipboard } = tbcc.tb;
const { dateFormatter, throttleHandle } = tbcc.utils;
const { PRIZE_TYPE, DRAW_STATUS } = tbcc.constants;
// 我的奖品配置项
const prizeConfig = {
bg: '//yun.duiba.com.cn/aurora/assets/e13fab3165d3a6bb4cbe8827e8f0fd41084b0091.png', //背景图
title: '//yun.dui88.com/duiba-components-c-myprizeprize_title.png',//标题
copyIcon: '//yun.dui88.com/duiba-components-c-myprizemyprize_copy_code.png', //复制
receiveBtn: '//yun.duiba.com.cn/bestoreBox/prize_received.png', //已领取 权益类奖品未配置 uesUrl使用
commonStatus: {
6: '//yun.dui88.com/duiba-components-c-myprizeget_again.png', //重新领取
5: '//yun.duiba.com.cn/spark/assets/914ba8643c8abb8d710a3126ec312aecf6c4daf0.png', //已失效
4: '//yun.duiba.com.cn/spark/assets/8267375232058a6d0140270b1594427fb08d6ae3.png', //领取失败
},
objectStatus: {
1: '//yun.dui88.com/duiba-components-c-myprizeto_address.png', //填写地址(实物奖品)
3: '//yun.dui88.com/duiba-components-c-myprizelogistics.png', //查看物流
},
enameStatus: {
1: '//yun.dui88.com/duiba-components-c-myprizeget_now.png', //立即领取(除实物奖品外)
3: '//yun.duiba.com.cn/bestoreBox/prize_received.png', //去使用(除实物奖品外)
},
logisticsImage: {
shipped: '//yun.dui88.com/duiba-components-c-myprizeprize_shipped.png', // 已发货物理弹窗
notShipped: '//yun.dui88.com/duiba-components-c-myprizenotshipped.png', // 待发货物流弹窗
},
btnStyle: {
width: '150rpx'
},
contentTop: '210rpx',
blankTxt: '暂无奖品',
hasLogistic: true, // 是否展示物流信息
isShowPirzeId: true // 是否显示奖品编号
}
Page({
data: {
PRIZE_TYPE,
DRAW_STATUS,
myPrizeList: [], //我的奖品
endTime: null,//到期时间
prizeConfig: prizeConfig,
objectStatus: {
...prizeConfig.commonStatus,
...prizeConfig.objectStatus
},
enameStatus: {
...prizeConfig.commonStatus,
...prizeConfig.enameStatus
},
logisticsImage: {
...prizeConfig.logisticsImage,
copyIcon: prizeConfig.copyIcon
},
hasLogistic: prizeConfig.hasLogistic,
isShowPirzeId: prizeConfig.isShowPirzeId,
isLogisticModal: false,
currentPrize: {}
},
onLoad() {
this.getMyPrize();
},
onShow() {
},
// 到期时间
getEndTime(endTime) {
if (!endTime) return null
this.setData({
endTime: dateFormatter(endTime, 'yyyy/MM/dd hh:mm:ss')
})
console.log(this.data.endTime)
},
/**
* 获取奖品列表
* @param {string}
*/
async getMyPrize() {
const { activityId } = app;
const { success, data } = await API.getMyPrizeList({ activityId }).catch(res => {
commonToast(res && res.message);
}) || {};
if (success && data) {
const { list } = data
this.setData({
myPrizeList: list
})
const endTime = list && list[0] && list[0].expiredTime
this.getEndTime(endTime)
}
},
//领取奖品
handleClick: throttleHandle(async function(e) {
const { type, drawStatus, useUrl = '', id, _id } = e.target.dataset.item
const { hasLogistic } = this.data
if(drawStatus == 3) {
if(type == PRIZE_TYPE.OBJECT && hasLogistic) {
this.setData({ currentPrize: e.target.dataset.item, isLogisticModal: true })
return;
}
if(type != PRIZE_TYPE.OBJECT && useUrl) {
navigateToOutside(useUrl)
return;
}
}
if(!(drawStatus == 6 || drawStatus == 1)) {
return;
}
// 领取权益
const prizeId = id || _id
if (type === PRIZE_TYPE.ENAME) return this.handleGetEquity(prizeId)
// 领取实物
if (type === PRIZE_TYPE.OBJECT) return this.handleGetEntity(prizeId)
// 领取积分
if (type === PRIZE_TYPE.CREDITS) return this.handleGetIntergation(prizeId)
},2000),
// 优惠券
async handleGetEquity(id) {
const { activityId } = app;
const { success, data, message } = await API.receiveEnamePrize({ activityId, id }).catch(res => {
this.getMyPrize()
commonToast(res && res.message)
}) || {};
if (success && data) {
commonToast('领取成功')
this.getMyPrize()
}
},
// 领取实物前询问
async handleGetEntity(id) {
const { activityId } = app;
const userAddress = await getUserAddress().catch(err => {
commonToast(err && err.errorMessage);
});
if (!userAddress) return
const { name, telNumber, provinceName, cityName, cityCode, countyName, detailInfo, streetName } = userAddress || {};
const params = {
activityId,
name,
phone: telNumber,
addressDetail: detailInfo,
cityCode,
city: cityName,
province: provinceName,
area: countyName,
streetName,
id
}
my.confirm({
title: '提示',
content: '确认使用该收货地址:' + name + telNumber + userAddress.duibaAddress.address,
confirmButtonText: '确定',
cancelButtonText: '取消',
success: (result) => {
console.log(result)
const { confirm } = result;
if (confirm) {
this.receiveObjectPrize(params);
}
},
});
},
// 领取实物
async receiveObjectPrize(params) {
const { success, data } = await API.receiveObjectPrize(params).catch(res => {
commonToast(res && res.message)
}) || {}
if (success && data) {
commonToast('领取成功')
}
this.getMyPrize()
},
// 积分
async handleGetIntergation(id) {
// 判断是否为会员
const { isVip, url } = await this.getUserInfo() || {}
if (!isVip) {
commonToast('需加入会员才能领取成功哦', 2000,() => { navigateToOutside(url) });
return;
}
const { activityId } = app;
const { success, data } = await API.receiveEnamePrize({ activityId, id }).catch(res => {
commonToast(res && res.message)
}) || {};
if (success && data) {
commonToast('领取成功')
}
this.getMyPrize()
},
//获取用户vip信息
async getUserInfo() {
const { activityId } = app;
const { success, data } = await API.getVipInfo({ activityId }).catch(res => {
commonToast(res && res.message)
}) || {};
if (success && data) {
return data
}
},
// 复制编码
async handleCopyId(e) {
const { text } = e.target.dataset || {}
await setClipboard({ text })
commonToast('复制成功')
},
/**
* 关闭物流弹窗
*/
closeLogisticModal() {
this.setData({ isLogisticModal: false })
},
});
\ No newline at end of file
{
"usingComponents": {
"logistics-modal": "/components/logisticsModal/logisticsModal"
}
}
\ No newline at end of file
<view class="goodsPage">
<goods-modal
goodsList="{{goodsList}}"
task="{{task}}"
onCompleteTask="onCompleteTask" />
</view>
\ No newline at end of file
/**
* 任务类型(taskType): collectGoods 、 orderGoods 、 browseGoods 、exchangeCredits
* task: {
* itemId: //商品ID
* taskType: //任务类型
* type: //头部样式(02——banner,01——标题)
* image: {//图片
* bg: //集合页背景
* banner: //头部banner
* title: //头部title
* collect: //商品以收藏
* no_collect: //商品未收藏
* }
* color: //商品名称:文字颜色
* }
*/
import API from '../../api';
const app = getApp();
const { tbcc } = app;
const { commonToast } = tbcc.tb;
const taskConfig = {
taskType: 'orderGoods',
type: '01',
image: {
bg: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_bg@2x.png',
banner: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_banner@2x.png',
title: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_title@2x.png',
collect: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_collection@2x.png',
no_collect: '//yun.duiba.com.cn/taobaomini/clientCTest/collection_no_collect@2x.png',
img: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_img@2x.png',
},
color: '#181818'
}
Page({
data: {
query: {},
task: taskConfig,
goodsList: []
},
onLoad(query) {
this.query = query || {}
this.initPage()
},
onShow() {
},
async initPage() {
const { activityId } = app
const { itemIds } = this.query
const { success, data } = await API.getItemListByItemIds({ activityId, itemIds }).catch(res => {
res && commonToast(res)
}) || {}
if(success) {
this.setData({ goodsList: data.list || [] })
}
},
});
{
"usingComponents": {
"goods-modal": "/components/goodsModal/goodsModal"
}
}
\ No newline at end of file
.tbcc-demo-page {
text-align: center;
padding: 60rpx 20rpx 20rpx;
}
.tbcc-demo-page button {
margin-bottom: 10rpx;
}
.tbcc-demo-page .upload-image {
width: 200rpx;
height: 200rpx;
}
.tbcc-demo-page .input-item {
width: 684rpx;
height: 80rpx;
}
.text-risk-item {
margin: 30rpx auto;
}
.tbcc-demo__title {
padding: 20rpx;
font-weight: bold;
font-size: 36rpx;
}
\ No newline at end of file
<view class="tbcc-demo-page">
<view class="tbcc-demo__title">常用tb方法</view>
<button onTap="showCommomToast">弱提示</button>
<button onTap="showCommomConfirm">通用确认弹层</button>
<button onTap="getAuth">获取用户授权</button>
<button onTap="getSystemInfo">获取系统信息</button>
<button onTap="getServerTime">获取系统时间</button>
<button onTap="favorShop">关注店铺(百草味为例需预览)</button>
<button onTap="checkShopFavoredStatus">店铺关注状态(百草味为例需预览)</button>
<button onTap="getUserAddress">获取收货地址(需预览)</button>
<button onTap="collectGoods">收藏商品(iPad Pro为例)</button>
<button onTap="checkGoodsCollectedStatus">商品收藏状态(iPad Pro为例)</button>
<button onTap="openDetail">跳转淘宝商品页(iPad Pro为例)</button>
<button onTap="navigateToOutside">跳转外部链接(需预览)</button>
<button onTap="navigateTo">跳转内部链接(新开窗口)</button>
<button onTap="redirectTo">跳转内部链接(不新开窗口)</button>
<button onTap="uploadImage">选择上传图片</button>
<view a:if="{{uploadImgUrl}}">
<image mode="aspectFit" src="{{uploadImgUrl}}" class="upload-image"></image>
</view>
<button onTap="chattingSendCard">分享到群聊卡片(需预览)</button>
<view class="text-risk-item">
<input type="text" onInput="bindKeyInput" data-name="riskInput" placeholder="请输入校验文本" class="input-item" />
<button onTap="textRiskIdentification">文本风险检测(需预览)</button>
</view>
<view class="tbcc-demo__title">通用API方法</view>
<button onTap="receiveObjectPrize">领取实物奖励</button>
<button onTap="receiveEnamePrize">领取权益奖励</button>
</view>
import API from '../../api';
const app = getApp();
const { tbcc } = app;
const {
commonToast,
simpleAlert,
commonConfirm,
getAuthUserInfo,
getSystemInfo,
getServerTime,
favorShop,
checkShopFavoredStatus,
getUserAddress,
collectGoods,
checkGoodsCollectedStatus,
openDetail,
navigateToOutside,
navigateTo,
redirectTo,
uploadImage,
chattingSendCard,
textRiskIdentification
} = tbcc.tb;
const { dateFormatter, versionCompare } = tbcc.utils;
Page({
data: {
uploadImgUrl: '',
riskInput: ''
},
onLoad() {},
bindKeyInput(e) {
const { value } = e.detail;
const { name } = e.target.dataset;
this.setData({
[name]: value,
});
},
/**
* toast提示
*/
showCommomToast() {
commonToast('tbcc', 3000, () => {
console.log('tbcc toast loaded');
});
},
/**
* confirm弹窗
*/
showCommomConfirm() {
commonConfirm('提示', '内容', () => {
commonToast('保存成功');
});
},
/**
* 获取用户授权信息
*/
async getAuth() {
const userInfo = await getAuthUserInfo().catch(err => {
console.log('未授权成功', err);
});
if (userInfo) {
console.log('userInfo', userInfo);
const { nickName, avatar } = userInfo;
app.nickName = nickName;
app.avatar = avatar;
simpleAlert(nickName);
}
},
/**
* 获取系统信息
*/
async getSystemInfo() {
const systemInfo = await getSystemInfo().catch(err => {
console.log('获取系统信息失败', err);
});
if (systemInfo) {
console.log('systemInfo', systemInfo);
const { version } = systemInfo;
const compareVersion = '9.8.0';
const isNewVersion = versionCompare(version, compareVersion);
simpleAlert('当前版本:' + version + `${isNewVersion ? '为新版本': '为老版本'}`);
}
},
/**
* 获取系统时间
*/
async getServerTime() {
const severTime = await getServerTime().catch(err => {
console.log('获取系统时间失败', err);
});
simpleAlert('系统时间:' + dateFormatter(severTime, 'yyyy/MM/dd hh:mm:ss'));
},
/**
* 关注店铺
*/
async favorShop() {
const sellerId = 628189716; // 百草味sellerId
const favorStatus = await favorShop(sellerId).catch(err => {
console.log('关注店铺失败', err);
});
commonToast(favorStatus ? '关注成功' : '关注失败');
},
/**
* 查询店铺关注状态
*/
async checkShopFavoredStatus() {
const sellerId = 628189716; // 百草味sellerId
const favoredStatus = await checkShopFavoredStatus(sellerId).catch(err => {
console.log('查询关注店铺状态失败', err);
});
simpleAlert(favoredStatus ? '已关注店铺' : '未关注店铺');
},
/**
* 获取用户收货地址
*/
async getUserAddress() {
const userAddress = await getUserAddress().catch(err => {
commonToast(err.errorMessage);
});
if (userAddress) {
console.log('userAddress', userAddress);
const { name, phone, address } = userAddress.duibaAddress;
commonConfirm('提示', '确认使用该收货地址:' + name + phone + address, () => {
// TODO 保存接口
commonToast('保存成功');
});
}
},
/**
* 收藏商品
*/
async collectGoods() {
const itemId = 622499363381; // iPad Pro 为例
const collectGoodsStatus = await collectGoods(itemId).catch(err => {
console.log('收藏商品失败', err);
});
commonToast(collectGoodsStatus ? '收藏商品成功' : '收藏商品失败');
},
/**
* 收藏商品
*/
async checkGoodsCollectedStatus() {
const itemId = 622499363381; // iPad Pro 为例
const goodscollectedStatus = await checkGoodsCollectedStatus(itemId).catch(err => {
console.log('查询商品收场状态失败', err);
});
simpleAlert(goodscollectedStatus ? '商品已收藏' : '商品未收藏');
},
/**
* 跳转到淘宝商品页
*/
async openDetail() {
const itemId = '622499363381'; // iPad Pro 为例
await openDetail(itemId).catch(err => {
console.log('打开淘宝商品页失败', err);
});
},
/**
* 跳转到外部链接
*/
navigateToOutside() {
const url = 'https://www.tmall.com';
navigateToOutside(url);
},
/**
* 跳转到小程序内部链接(新开窗口)
*/
navigateTo() {
const url = '/pages/index/index';
navigateTo(url);
},
/**
* 跳转到小程序内部链接(不新开窗口)
*/
redirectTo() {
const url = '/pages/index/index';
redirectTo(url);
},
/**
* 选择上传图片
*/
async uploadImage() {
const uploadImageResultUrl = await uploadImage().catch(err => {
commonToast(err && (err.errorMessage || '上传图片失败'));
});
if (uploadImageResultUrl) {
this.setData({
uploadImgUrl: uploadImageResultUrl
});
}
},
/**
* 分享到群聊卡片
*/
async chattingSendCard() {
// 测试请求参数(格式示例)
const requestInfo = {
"biz_ext_map": "{\"activityId\":\"028fe748-9413-4df1-ad15-7d21d9ca16c9\",\"shareId\":\"AAGrFC1BALWl0Axwb3svqUyd\"}",
"card_template_id": 1588754547060,
"group_id": "0_G_2207623078795_1591670473134",
"weex_data_json": "{\"url\":\"https://m.duanqu.com?_ariver_appid=3000000005188064&nbsv=0.1.2007151634.12&nbsource=debug&nbsn=DEBUG&_mp_code=tb&query=activityId%3D028fe748-9413-4df1-ad15-7d21d9ca16c9%26groupId%3D0_G_2207623078795_1591670473134%26shareId%3DAAGrFC1BALWl0Axwb3svqUyd\",\"act_title\":\"一起出道赢大奖\",\"share\":\"@pilee0827 多度\",\"picture3\":\"https://yun.duiba.com.cn/upload/ltuwhbf39x.jpg\"}",
"message_id": "5f0fcdfeef507182be6c2247",
"app_secret": "appSecret",
"app_code": "30123678"
};
const messageResult = await chattingSendCard(requestInfo).catch(err => {
commonToast(err && err.errorMessage);
});
if (messageResult) {
const { result: { value, success } } = messageResult;
commonToast(value && success ? '分享到群聊成功' : '分享到群聊失败');
}
},
/**
* 文本风险校验
*/
async textRiskIdentification() {
const { riskInput } = this.data;
const success = await textRiskIdentification(riskInput).catch(err => {
console.log(err);
});
simpleAlert(`检测文本"${riskInput}",校验结果${success ? '通过' : '不通过'}`);
},
/**
* 领取实物奖励
*/
async receiveObjectPrize() {
const _id = '123'; // 测试奖品ID
const success = await API.fn.receiveObjectPrize({ _id });
if (success) {
commonToast('保存成功');
}
},
/**
* 领取权益奖励
*/
async receiveEnamePrize() {
const _id = '123'; // 测试奖品ID
const success = await API.fn.receiveEnamePrize({ _id });
if (success) {
commonToast('领取成功');
}
}
});
import * as tb from './lib/tb';
import * as tbMock from './lib/tbMock';
import * as utils from './lib/utils';
import * as constants from './lib/constants';
import capi from './lib/capi';
import request from './lib/request';
const isH5 = 'undefined' !== typeof window;
// 如果为h5环境走mock数据
let _tb = {};
Object.entries(tb).forEach(([key, value]) => {
_tb[key] = isH5 ? tbMock[key] : value;
});
export default {
capi,
tb: _tb,
utils,
request,
constants
};
\ No newline at end of file
const capi = {
getMyPrizeList: {
method: 'GET',
handle: 'getMyPrizeList',
isShowLoading: true
},
receiveObjectPrize: { method: 'GET', handle: 'receiveObjectPrize' },
receiveEnamePrize: { method: 'GET', handle: 'receiveEnamePrize' },
getItemListByItemIds: { method: 'GET', handle: 'getItemListByItemIds' },
getCollectGoodsList: { method: 'GET', handle: 'getCollectGoodsList' },
doCollectGoodsTask: { method: 'GET', handle: 'doCompleteTask', defaultParams: { taskType: 'collectGoods'} },
doBrowseGoodsTask: { method: 'GET', handle: 'doCompleteTask', defaultParams: { taskType: 'browseGoods'} },
doJumpLinkTask: { method: 'GET', handle: 'doCompleteTask', defaultParams: { taskType: 'jumpLink'} },
doExchangeCreditsTask: { method: 'GET', handle: 'doCompleteTask', defaultParams: { taskType: 'exchangeCredits'} },
getVipInfo: { method: 'GET', handle: 'getVipInfo' }
};
export default capi;
import { getUserAddress, commonToast } from './tb';
import { getRequestParams } from './utils';
// 通用API封装方法
const capiFn = (apiList, request) => {
return {
async receiveObjectPrize(params) {
const { handle, method, ext } = getRequestParams(apiList['receiveObjectPrize']);
const userAddress = await getUserAddress().catch(err => {
commonToast(err.errorMessage);
});
if (userAddress) {
const { name, phone, address } = userAddress.duibaAddress;
commonConfirm('提示', '确认使用该收货地址:' + name + phone + address, async () => {
params = {
name,
phone,
address,
...params
}
const result = await request(handle, method, params, ext).catch(res => {
commonToast(res && res.message);
});
if (result && result.success) {
return true;
}
});
}
return false;
},
async receiveEnamePrize(params) {
const { handle, method, ext } = getRequestParams(apiList['receiveEnamePrize']);
const result = await request(handle, method, params, ext).catch(res => {
commonToast(res && res.message);
});
if (result && result.success) {
return true;
}
return false;
}
}
};
export default capiFn;
/**
* 通用toast
* @param {string} content toast内容
* @param {number} duration 显示时长
* @param {function} successCb 成功回调
*/
export const commonToast = (content, duration = 3000, successCb) => {
return '暂不支持toast';
};
/**
* 简易alert
* @param {string} content toast内容
*/
export const simpleAlert = content => {
alert(content);
};
/**
* 通用确认弹窗
* @param {string} title 标题
* @param {string} content 内容
* @param {function} successCb 成功回调
*/
export const commonConfirm = (title = '提示', content, successCb) => {
my.confirm({
title,
content,
confirmButtonText: '确定',
cancelButtonText: '取消',
success: result => {
const { confirm } = result;
if (confirm) {
successCb && successCb();
}
}
});
};
/**
* 获取授权用户信息
* @returns {object} 用户登录信息
*/
export const getAuthUserInfo = async () => {
return {
nickName: 'nickName',
avatar: '123',
mixNick: '1234'
};
};
/**
* 获取系统信息
* @returns {object} 小程序系统信息
*/
export const getSystemInfo = async () => {
return {
version: '9.5.0'
};
};
/**
* 执行关注店铺
* @param {number} sellerId 店铺归属的卖家Id
* @returns {boolean} 关注状态
*/
export const favorShop = async sellerId => {
if (!sellerId) {
console.error('favorShop: 请传入卖家Id');
return false;
}
return {};
};
/**
* 判断是否关注店铺
* @param {number} sellerId 店铺归属的卖家Id
* @returns {boolean} 关注状态
*/
export const checkShopFavoredStatus = async sellerId => {
if (!sellerId) {
console.error('checkShopFavoredStatus: 请传入卖家Id');
return false;
}
return true;
};
/**
* 跳转到外部链接
* @param {string} url 跳转链接
*/
export const navigateToOutside = url => {
if (!url) {
console.error('navigateToOutside: 请传入url');
return false;
}
window.location = url;
};
/**
* 跳转到内部链接(新开窗口)
* @param {string} url 跳转链接
*/
export const navigateTo = url => {
if (!url) {
console.error('navigateTo: 请传入url');
return false;
}
window.location = url;
};
/**
* 跳转到内部链接(不新开窗口)
* @param {string} url 跳转链接
*/
export const redirectTo = url => {
if (!url) {
console.error('redirectTo: 请传入url');
return false;
}
window.replace(url);
};
/**
* 获取服务器时间
* @returns {number} 服务器时间戳
*/
export const getServerTime = async () => {
return +new Date();
};
/**
* 收藏商品
* @param {number} 商品id
* @returns {object} 收藏结果
*/
export const collectGoods = async id => {
if (!id) {
console.error('collectGoods: 请传入商品id');
return false;
}
return {};
};
/**
* 查询商品收藏状态
* @param {number} 商品id
* @returns {boolean} 商品收场状态
*/
export const checkGoodsCollectedStatus = async id => {
if (!id) {
console.error('checkGoodsCollectedStatus: 请传入商品id');
return false;
}
return true;
};
/**
* 跳转到淘宝商品页
* @param {string} itemId 商品ID
*/
export const openDetail = async itemId => {
if (!itemId) {
console.error('openDetail: 请传入商品id');
return false;
}
window.location = 'https://item.taobao.com/item.htm?id=' + itemId;
};
/**
* 获取淘宝用户收货地址
*/
export const getUserAddress = async (opts = {}) => {
return {
name: 1,
phone: 1,
address: 123
}
};
/**
* 选择图片
*/
export const chooseImage = async (opts = {}) => {
let defaults = {
count: 1,
sourceType: ['camera', 'album'],
compressLevel: 1
};
let _opts = Object.assign({}, defaults, opts);
const { count, sourceType, compressLevel } = _opts;
return new Promise((resolve, reject) => {
if (!my.chooseImage) {
reject({
noSupport: true,
errorMessage: '当前版本不支持选择收货地址,请升级到最新版本'
});
return false;
}
my.chooseImage({
count,
sourceType,
success: res => {
my.compressImage({
apFilePaths: res.apFilePaths,
level: compressLevel,
success: data => {
resolve(data);
},
fail: err => {
resolve(err);
}
});
},
fail: err => {
// error code 2001 为摄像头授权取消、ios 11为用户取消操作不认作失败场景 、安卓 为1
if (err.error !== 2001 && err.error !== 11 && err.error !== 1) {
reject(err);
} else {
resolve(false);
}
}
});
});
};
/**
* 获取图片信息
* @param {string} src 图片地址
*/
export const getImageInfo = async src => {
return new Promise((resolve, reject) => {
my.getImageInfo({
src,
success: res => {
resolve(res);
},
fail: err => {
reject(err);
}
});
});
};
/**
* 获取图片信息
* @param {*} opts chooseImage配置参数
*/
export const uploadImage = async (opts = {}) => {
const { cloud } = getApp();
if (!cloud) {
console.error('chattingSendCard: 请传入app.cloud');
return false;
}
const chooseImageResult = await chooseImage(opts).catch(err => {
commonToast(err && (err.errorMessage || '选择图片失败'));
});
if (chooseImageResult) {
const { apFilePaths } = chooseImageResult;
const filePath = apFilePaths[0];
const imageInfo = await getImageInfo(filePath).catch(err => {
commonToast(err && err.errorMessage);
});
// 找不到图片信息
if (!imageInfo) {
return false;
}
const { path: imagePath } = imageInfo;
const { url } = await cloud.file.uploadFile({
filePath,
fileType: 'image',
fileName: imagePath.split('/').pop()
});
return url;
}
return false;
}
/**
* 分享到群聊卡片
* @param {object} requestInfo 分享到群聊卡片所需请求参数
*/
export const chattingSendCard = async (requestInfo) => {
alert('无法模拟');
return false;
};
/**
* 分享到群聊卡片
* @param {*} cloud 小程序云
* @param {object} requestInfo 分享到群聊卡片所需请求参数
*/
export const textRiskIdentification = async (text) => {
return true;
}
\ No newline at end of file
module.exports = {
env: {
NODE_ENV: '"development"'
},
defineConstants: {
},
mini: {},
h5: {
esnextModules: [ 'taro-ui' ]
}
}
// eslint-disable-next-line import/no-commonjs
const path = require('path')
const config = {
projectName: 'taro-test',
date: '2021-2-25',
designWidth: 750,
deviceRatio: {
640: 2.34 / 2,
750: 1,
828: 1.81 / 2
},
sourceRoot: 'src',
outputRoot: 'dist',
alias: {
'@': path.resolve(__dirname, '..', 'src')
},
plugins: [
[
'@pluve/taro-plugin-mars', // taro暂时未支持阿里小程序自定义插件 需要引用插件的页面需配置
{
usingComponents: {
enable: true,
include: [ 'pages/index/index', 'pages/myPrize/myPrize' ]
}
}
]
],
defineConstants: {
},
copy: {
patterns: [
],
options: {
}
},
framework: 'react',
mini: {
postcss: {
pxtransform: {
enable: true,
config: {
}
},
url: {
enable: true,
config: {
limit: 1024 // 设定转换尺寸上限
}
},
cssModules: {
enable: true, // 默认为 false,如需使用 css modules 功能,则设为 true
config: {
namingPattern: 'module', // 转换模式,取值为 global/module
generateScopedName: '[name]__[local]___[hash:base64:5]'
}
}
},
webpackChain(chain, webpack) {
chain.module
.rule('less')
.pre()
.use('style-resources')
.loader('style-resources-loader')
.options({
patterns: path.resolve(__dirname, '../src/styles/index.less'),
injector: 'append'
})
}
},
h5: {
publicPath: '/',
staticDirectory: 'static',
postcss: {
autoprefixer: {
enable: true,
config: {
}
},
cssModules: {
enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
config: {
namingPattern: 'module', // 转换模式,取值为 global/module
generateScopedName: '[name]__[local]___[hash:base64:5]'
}
}
}
}
}
module.exports = function(merge) {
if (process.env.NODE_ENV === 'development') {
return merge({}, config, require('./dev'))
}
return merge({}, config, require('./prod'))
}
module.exports = {
env: {
NODE_ENV: '"production"'
},
defineConstants: {
},
mini: {},
h5: {
/**
* 如果h5端编译后体积过大,可以使用webpack-bundle-analyzer插件对打包体积进行分析。
* 参考代码如下:
* webpackChain (chain) {
* chain.plugin('analyzer')
* .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
* }
*/
}
}
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
],
}
}
}
\ No newline at end of file
{
"cloud": {
"type": "CLOUDAPP"
},
"exclude": [
"server/**"
]
}
{
"name": "taro-test",
"version": "1.0.0",
"private": true,
"description": "测试项目",
"templateInfo": {
"name": "taro-ui",
"typescript": false,
"css": "less"
},
"scripts": {
"build:weapp": "taro build --type weapp",
"build:swan": "taro build --type swan",
"build:alipay": "taro build --type alipay",
"build:tt": "taro build --type tt",
"build:h5": "taro build --type h5",
"build:rn": "taro build --type rn",
"build:qq": "taro build --type qq",
"build:jd": "taro build --type jd",
"build:quickapp": "taro build --type quickapp",
"dev:weapp": "npm run build:weapp -- --watch",
"dev:swan": "npm run build:swan -- --watch",
"dev:alipay": "npm run build:alipay -- --watch",
"dev:tt": "npm run build:tt -- --watch",
"dev:h5": "npm run build:h5 -- --watch",
"dev:rn": "npm run build:rn -- --watch",
"dev:qq": "npm run build:qq -- --watch",
"dev:jd": "npm run build:jd -- --watch",
"dev:quickapp": "npm run build:quickapp -- --watch"
},
"browserslist": [
"last 3 versions",
"Android >= 4.1",
"ios >= 8"
],
"author": "",
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.13.0",
"@babel/runtime": "^7.7.7",
"@pluve/taro-plugin-mars": "^1.1.1",
"@tarojs/cli": "v3.2.0-canary.9",
"@tarojs/components": "v3.2.0-canary.9",
"@tarojs/react": "v3.2.0-canary.9",
"@tarojs/runtime": "v3.2.0-canary.9",
"@tarojs/taro": "v3.2.0-canary.9",
"@tbmp/mp-cloud-sdk": "^1.4.2",
"classnames": "^2.2.6",
"hox": "^1.1.2",
"lodash": "4.17.15",
"react": "^16.10.0",
"react-dom": "^16.10.0",
"taro-ui": "^3.0.0-alpha.3"
},
"devDependencies": {
"@babel/core": "^7.8.0",
"@tarojs/mini-runner": "v3.2.0-canary.9",
"@tarojs/webpack-runner": "v3.2.0-canary.9",
"@types/react": "^16.0.0",
"@types/webpack-env": "^1.13.6",
"babel-eslint": "^10.1.0",
"babel-preset-taro": "v3.2.0-canary.9",
"eslint": "^7.12.1",
"eslint-config-standard": "^16.0.2",
"eslint-config-standard-jsx": "^10.0.0",
"eslint-config-standard-react": "^11.0.1",
"eslint-config-taro": "v3.2.0-canary.9",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.3.1",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^1.6.1",
"style-resources-loader": "^1.4.1",
"stylelint": "9.3.0"
}
}
{
"miniprogramRoot": "dist/",
"projectname": "taro-test",
"description": "测试项目",
"appid": "touristappid",
"setting": {
"urlCheck": true,
"es6": false,
"postcss": false,
"preloadBackgroundData": false,
"minified": false,
"newFeature": true,
"autoAudits": false,
"coverView": true,
"showShadowRootInWxmlPanel": false,
"scopeDataCheck": false,
"useCompilerModule": false
},
"compileType": "miniprogram",
"simulatorType": "alipay",
"simulatorPluginLibVersion": {},
"condition": {}
}
import cloud from '@tbmp/mp-cloud-sdk'
import tbRequest from './utils/request'
import conifg from './config'
const { cloudName, requestType } = conifg
const request = tbRequest({
cloud,
requestType,
cloudName
})
/**
const defaultConfig = {
method: 'GET',
showLoading: false, 请求是否loading
toastError: true success为false时是否toastMessage
}
* **/
export const login = (params, config) => request('login', params, config)
export const getActivityBaseInfoById = (params, config) => request('getActivityBaseInfoById', params, config)
export const getVipInfo = (params, config) => request('getVipInfo', params, config)
export const getShareInfo = (params, config) => request('getShareInfo', params, config)
export const getMyPrizeList = (params, config) => request('getMyPrizeList', params, config)
export const receiveEnamePrize = (params, config) => request('receiveEnamePrize', params, config)
export const receiveObjectPrize = (params, config) => request('receiveObjectPrize', params, config)
export const doHelp = (params, config) => request('doHelp', params, config)
export const drawLotteryPrize = (params, config) => request('drawLotteryPrize', params, config)
export const resetTest = (params, config) => request('resetTest', params, config)
export const addStat = (params, config) => request('addStat', params, config)
export const receiveTaskRewards = (params, config) => request('receiveTaskRewards', params, config)
export const getTaskList = (params, config) => request('getTaskList', params, config)
export default {
pages: [
'pages/index/index',
'pages/myPrize/myPrize',
'pages/browseGoods/browseGoods'
],
window: {
enableSkia: 'true',
allowsBounceVertical: 'NO',
navigationBarTitleText: '活动名称'
},
plugins: {
cemMember: {
version: '*',
provider: '3000000026642582'
}
}
}
import cloud from '@tbmp/mp-cloud-sdk'
import { Component } from 'react'
import './app.less'
// 使用taro-ui 按需引入的组件样式
import './taro-ui.scss'
import config from './config'
import './utils/mdProxy'
const { env } = config
cloud.init({ env })
class App extends Component {
componentDidMount() {}
onLaunch(options) {
console.log(options, 'options')
const { activityId } = options.query || {}
const app = getApp()
app.activityId = activityId || config.defaultActivityId
}
componentDidShow() {}
componentDidHide() {}
componentDidCatchError() { }
onGlobalClick(e) {
console.log('onGlobalClick :>> ', e)
}
// this.props.children 是将要会渲染的页面
render() {
return this.props.children
}
}
// export default withModel(useAuthInfoModel, authInfo => ({
// authInfo
// }))(App);
export default App
@import "./styles/animate.less";
.position-build();
.border-build();
.flex-build();
.text-build();
.layout-build();
.position-build();
import React from 'react'
import { View, Image } from '@tarojs/components'
import './DeliveryModal.less'
export default function DeliveryModal(props) {
const { prizeItem, task, onClose = () => {} } = props
return (
<View className='logistics-modal-container'>
<View
className='logistics-modal-container__content'
style={prizeItem.shipStatus === 3
? `background: url(${task.shipped}) no-repeat;background-size: 100% 100%;`
: `background: url(${task.notShipped}) no-repeat;background-size: 100% 100%;`}
>
<View className='logistics-modal-container__close' onTap={onClose} />
<View className='logistics-modal-container__content-item'>
<View className='logistics-modal-container__content-left'>
<Image mode='scaleToFill' src={prizeItem.image} />
</View>
<View className='logistics-modal-container__content-right'>
<View className='logistics-modal-container__content-name'>{prizeItem.name}</View>
<View className='logistics-modal-container__content-copy'>
<View className='logistics-modal-container__content-copy__code'>奖品编码:{prizeItem._id}</View>
<View className='logistics-modal-container__content-copy__img' onTap='copyPrizeId' />
</View>
<View className='logistics-modal-container__content-desc'>{prizeItem.shipStatus === 3 ? '发货状态:已发货' : '发货状态:未发货'}</View>
{prizeItem.shipStatus === 2 && <View className='logistics-modal-container__content-desc'>温馨提示:奖品将在活动结束7天内陆续发出</View>}
{
prizeItem.shipStatus !== 2 &&
<View className='logistics-modal-container__content-copy'>
<View className='logistics-modal-container__content-copy__code'>{'物流单号:' + prizeItem.shipNum}</View>
<View className='logistics-modal-container__content-copy__img' style={`background: url(${task.copyIcon}) no-repeat;background-size: 100% 100%;`} onTap={onClose} />
</View>
}
</View>
</View>
</View>
</View>
)
}
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
} }
.logistics-modal-container { .logistics-modal-container {
position: absolute; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
...@@ -13,20 +13,20 @@ ...@@ -13,20 +13,20 @@
background: rgba(0, 0, 0, 0.7); background: rgba(0, 0, 0, 0.7);
z-index: 20; z-index: 20;
opacity: 0; opacity: 0;
animation: modalAnimation .3s forwards; animation: modalAnimation 0.3s forwards;
} }
@keyframes containerAnimation { @keyframes containerAnimation {
to { to {
transform: scale(1) transform: scale(1);
} }
} }
.logistics-modal-container__content { .logistics-modal-container__content {
width: 646rpx; width: 646px;
height: 244rpx; height: 244px;
padding: 0 46rpx; padding: 0 46px;
position: absolute; position: fixed;
left: 0; left: 0;
top: 0; top: 0;
right: 0; right: 0;
...@@ -35,27 +35,28 @@ ...@@ -35,27 +35,28 @@
display: flex; display: flex;
align-items: center; align-items: center;
transform: scale(0); transform: scale(0);
animation: containerAnimation .3s forwards; animation: containerAnimation 0.3s forwards;
} }
.logistics-modal-container__close { .logistics-modal-container__close {
position: absolute; position: absolute;
right: -10rpx; right: -10px;
top: -70rpx; top: -70px;
height: 48rpx; height: 48px;
width: 48rpx; width: 48px;
background: url("//yun.duiba.com.cn/taobaomini/aupresViva/tc_close.png") no-repeat; background: url("//yun.duiba.com.cn/taobaomini/aupresViva/tc_close.png")
no-repeat;
background-size: 100% 100%; background-size: 100% 100%;
} }
.logistics-modal-container__content-item { .logistics-modal-container__content-item {
border-radius: 10rpx; border-radius: 10px;
display: flex; display: flex;
} }
.logistics-modal-container__content-left { .logistics-modal-container__content-left {
width: 144rpx; width: 144px;
height: 144rpx; height: 144px;
background: rgba(244, 244, 244, 1); background: rgba(244, 244, 244, 1);
} }
...@@ -65,12 +66,12 @@ ...@@ -65,12 +66,12 @@
} }
.logistics-modal-container__content-right { .logistics-modal-container__content-right {
height: 144rpx; height: 144px;
margin-left: 24rpx; margin-left: 24px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
font-size: 20rpx; font-size: 20px;
color: #959595; color: #959595;
flex: 1; flex: 1;
overflow: hidden; overflow: hidden;
...@@ -78,31 +79,31 @@ ...@@ -78,31 +79,31 @@
} }
.logistics-modal-container__content-name { .logistics-modal-container__content-name {
font-size: 28rpx; font-size: 28px;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
} }
.logistics-modal-container__content-desc { .logistics-modal-container__content-desc {
font-size: 20rpx; font-size: 20px;
} }
.logistics-modal-container__content-ids { .logistics-modal-container__content-ids {
margin-bottom: 20rpx; margin-bottom: 20px;
} }
.logistics-modal-container__content-copy { .logistics-modal-container__content-copy {
display: flex; display: flex;
align-items: center; align-items: center;
height: 30rpx; height: 30px;
} }
.logistics-modal-container__content-copy__code { .logistics-modal-container__content-copy__code {
font-size: 20rpx; font-size: 20px;
} }
.logistics-modal-container__content-copy__img { .logistics-modal-container__content-copy__img {
width: 20rpx; width: 20px;
height: 20rpx; height: 20px;
background-size: 100% 100%; background-size: 100% 100%;
margin-left: 20rpx; margin-left: 20px;
} }
\ No newline at end of file
import { View } from '@tarojs/components'
import React, { useState } from 'react'
import { useThrottle } from '@/hooks/useThrottle'
import classnames from 'classnames'
import Modal from '@/components/_base/Modal/Modal'
import styles from './DoHelpModal.module.less'
import { doHelp } from '@/api'
import { checkIsMember, commonToast } from '@/utils/tb'
import { showToast } from '@tarojs/taro'
import { useLoginInfoModel } from '@/store'
export default function RuleModal(props) {
const { bg = '', width = 300, height = 300, onClose = () => {}, closePostion, top = '40%', newVip = false } = props
const [ memberModalVisible, setMemberModalVisible ] = useState(false)
const { loginInfo: { inviteId } } = useLoginInfoModel()
const onAuthSuccess = async() => {
const helpRes = await doHelp({ inviteId })
if (helpRes?.success) {
showToast({ title: '助力成功' })
} else {
commonToast(helpRes?.message)
}
onClose()
setMemberModalVisible(false)
}
const onAuthFail = () => {
setMemberModalVisible(false)
}
const onDoHelp = useThrottle(async() => {
// 条件不是需要新会员的情况下,直接助力
if (!newVip) {
const helpRes = await doHelp({ inviteId })
if (helpRes?.success) {
showToast({ title: '助力成功' })
} else {
commonToast(helpRes?.message)
}
onClose()
}
// 助力条件为新会员
const res = await checkIsMember()
const isVip = res?.businessSuccess && res?.data?.isMember
// 已经是会员
if (isVip) {
commonToast('新会员才能助力哦~')
} else {
// 还不是会员,弹出会员弹窗
setMemberModalVisible(true)
}
}, 2000)
return (
<View>
<Modal onClose={onClose} closePostion={closePostion} top={top}>
<View className={styles.content} style={{ width: `${width / 100}rem`, height: `${height / 100}rem`, backgroundImage: `url(${bg})` }}>
<View className={styles.title}>接受邀请</View>
<View className={styles.image} />
<View className={styles.desc}>是一段描述</View>
<View className={styles.help_button} onTap={onDoHelp}>助力按钮</View>
</View>
</Modal>
{
memberModalVisible &&
<member-modal
onClose={() => setMemberModalVisible(false)}
onAuthFail={onAuthFail}
onAuthSuccess={onAuthSuccess}
/>
}
</View>
)
}
.content {
.image-property();
background-color: #fff;
overflow: hidden;
padding: 30px;
display: flex;
flex-direction: column;
align-items: center;
}
.title {
font-size: 40px;
text-align: center;
.image-property();
}
.image {
.wh(200px, 200px);
.image("//yun.duiba.com.cn/taobaomini/template/250x250.png");
margin: 40px 0 0 0;
}
.desc {
font-size: 24px;
margin: 30px 0 0 0;
text-align: center;
}
.help_button {
text-align: center;
.wh(150px, 40px);
margin: 40px 0 0 0;
border: 1px solid #000;
}
import { View, ScrollView, Image } from '@tarojs/components'
import classnames from 'classnames'
import React, { useEffect, useState } from 'react'
import './GoodsPage.less'
export default function GoodsPage(props) {
const {
isBackFlag = 0, goodsList = [], task = {
itemId: '617724147979,617724563528,617300295119',
taskType: 'browseGoods',
type: '02',
bannerHeight: 430,
image: {
bg: '//yun.dui88.com/taobaomini/clientCTest/goods_bg@2x.png',
banner: '//yun.dui88.com/taobaomini/clientCTest/goods_banner@2x.png',
title: '//yun.dui88.com/taobaomini/clientCTest/goods_title@2x.png',
collect: '//yun.dui88.com/taobaomini/clientCTest/goods_collection@2x.png',
no_collect: '//yun.dui88.com/taobaomini/clientCTest/collection_no_collect@2x.png',
img: '//yun.dui88.com/taobaomini/clientCTest/goods_img@2x.png'
},
color: '#181818'
}
} = props
return (
<View className='goods-modal-container' style={{ background: `url(${task.image.bg}) no-repeat`, backgroundSize: '750rpx 1624rpx' }}>
{task.type === '01'
? <View
style={{ background: `url(${task.image.title}) no-repeat`, backgroundSize: '100% 100%' }}
className='goods-modal-container__title'
/>
: <View className='goods-modal-container__banner' style={{ background: `url(${task.image.banner}) no-repeat`, backgroundSize: '100% 100%', height: task.bannerHeight + 'rpx' }} />}
<View className='goods-modal-container__content' style={{ top: task.type === '02' ? task.bannerHeight + 'rpx' : '209rpx' }}>
<ScrollView scrollY className='goods-modal-container__scroll'>
<View className='goods-modal-container__content-list'>
{
goodsList.map((item, i) => {
return (
<View className='goods-modal-container__content-item' key={item}>
<View className='goods-modal-container__content-item__img' data-item={item} onTap='goToGoodsDetail'>
<Image mode='scaleToFill' src={item.image} />
</View>
<View className='goods-modal-container__content-item__name' style={{ color: task.color }}>{item.name}</View>
<View className='goods-modal-container__content-item__price'>
<View className='goods-modal-container__content-item__price-num'><text>¥</text>{item.price}</View>
{
task.taskType === 'collectGoods' &&
<View
data-item={item}
onTap='goToCollectGoods'
className={item.collected ? 'goods-modal-container__content-item__price-collect' : 'goods-modal-container__content-item__price-no-collect'}
>
<Image mode='scaleToFill' src={item.collected ? task.image.collect : task.image.no_collect} />
</View>
}
</View>
</View>
)
})
}
</View>
</ScrollView>
</View>
</View>
)
}
.goods-modal-container { .goods-modal-container {
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
width: 100%; width: 100%;
min-height: 100vh; min-height: 100vh;
/* background: url('//yun.dui88.com/taobaomini/clientCTestgoods_bg@2x.png') no-repeat; */ /* background: url('//yun.dui88.com/taobaomini/clientCTestgoods_bg@2x.png') no-repeat; */
background-size: 750rpx 1624rpx; background-size: 750px 1624px;
overflow: hidden; overflow: hidden;
z-index: 20; z-index: 20;
} }
.goods-modal-container__title { .goods-modal-container__title {
width: 750rpx; width: 750px;
height: 120rpx; height: 120px;
margin: 96rpx 0 11rpx; margin: 96px 0 11px;
} }
.goods-modal-container__banner { .goods-modal-container__banner {
width: 750rpx; width: 750px;
height: 400rpx; height: 400px;
margin-bottom: 30rpx; margin-bottom: 30px;
background: #E1E1E1; background: #e1e1e1;
} }
.goods-modal-container__content { .goods-modal-container__content {
position: absolute; position: absolute;
top: 190rpx; top: 190px;
left: 0; left: 0;
bottom: 0; bottom: 0;
padding-top: 41rpx; padding-top: 41px;
padding: 41rpx 25rpx 0; padding: 41px 25px 0;
width: 750rpx; width: 750px;
/* height: inherit; */ /* height: inherit; */
} }
.goods-modal-container__scroll { .goods-modal-container__scroll {
height: 100%; height: 100%;
} }
.goods-modal-container__content-list { .goods-modal-container__content-list {
width: 100%; width: 100%;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
} }
.goods-modal-container__content-item { .goods-modal-container__content-item {
width: 340rpx; width: 340px;
height: 510rpx; height: 510px;
background: #fff; background: #fff;
margin-bottom: 20rpx; margin-bottom: 20px;
} }
.goods-modal-container__content-item:nth-child(even) { .goods-modal-container__content-item:nth-child(even) {
margin-left: 20rpx; margin-left: 20px;
} }
.goods-modal-container__content-item__img { .goods-modal-container__content-item__img {
width: 340rpx; width: 340px;
height: 340rpx; height: 340px;
background: #E1E1E1; background: #e1e1e1;
} }
.goods-modal-container__content-item__img image { .goods-modal-container__content-item__img image {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.goods-modal-container__content-item__name { .goods-modal-container__content-item__name {
margin: 25rpx 0 31rpx 21rpx; margin: 25px 0 31px 21px;
width:310rpx; width: 310px;
height:60rpx; height: 60px;
font-size:24rpx; font-size: 24px;
font-family:PingFang SC; font-family: PingFang SC;
font-weight:400; font-weight: 400;
color:rgba(24,24,24,1); color: rgba(24, 24, 24, 1);
line-height:30rpx; line-height: 30px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.goods-modal-container__content-item__price { .goods-modal-container__content-item__price {
padding: 0 31rpx 0 24rpx; padding: 0 31px 0 24px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
.goods-modal-container__content-item__price-num { .goods-modal-container__content-item__price-num {
font-size:32rpx; font-size: 32px;
font-family:PingFang SC; font-family: PingFang SC;
font-weight:600; font-weight: 600;
color:rgba(255,42,0,1); color: rgba(255, 42, 0, 1);
} }
.goods-modal-container__content-item__price-num text { .goods-modal-container__content-item__price-num text {
font-size: 24rpx; font-size: 24px;
margin-right: 2rpx; margin-right: 2px;
} }
.goods-modal-container__content-item__price-collect { .goods-modal-container__content-item__price-collect {
width: 118rpx; width: 118px;
height: 31rpx; height: 31px;
background-size: 100% 100%; background-size: 100% 100%;
} }
.goods-modal-container__content-item__price-collect image { .goods-modal-container__content-item__price-collect image {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.goods-modal-container__content-item__price-no-collect { .goods-modal-container__content-item__price-no-collect {
width: 30rpx; width: 30px;
height: 30rpx; height: 30px;
} }
.goods-modal-container__content-item__price-no-collect image { .goods-modal-container__content-item__price-no-collect image {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.goods-modal-timer { .goods-modal-timer {
width: 147rpx; width: 147px;
height: 223rpx; height: 223px;
position: absolute; position: absolute;
top: 502rpx; top: 502px;
right: 19rpx; right: 19px;
background: #eee; background: #eee;
} }
\ No newline at end of file
import { View, ScrollView, Text } from '@tarojs/components'
import React from 'react'
import classnames from 'classnames'
import Modal from '@/components/_base/Modal/Modal'
import styles from './RuleModal.module.less'
export default function RuleModal(props) {
const { bg = '', width = 300, height = 300, rule = '', onClose, closePostion, top = '40%' } = props
return (
<Modal onClose={onClose} closePostion={closePostion} top={top}>
<View className={styles.content} style={{ width: `${width / 100}rem`, height: `${height / 100}rem`, backgroundImage: `url(${bg})` }}>
<View className={styles.title}>活动规则</View>
<ScrollView className={styles.scroll} scrollY>
<Text className={styles.rule}>{rule}</Text>
</ScrollView>
</View>
</Modal>
)
}
.content {
.image-property();
background-color: #fff;
overflow: hidden;
padding: 30px;
display: flex;
flex-direction: column;
align-items: center;
}
.scroll {
width: 100%;
height: 100%;
margin-top: 20px;
}
.title {
font-size: 40px;
text-align: center;
.image-property();
}
.rule {
font-size: 24px;
line-height: 30px;
flex: 1;
}
import { View } from '@tarojs/components'
import React, { useState } from 'react'
import Popup from '@/components/_base/Popup/Popup'
import { noopFn } from '@/utils/util'
import { useTasks } from '@/hooks/useTasks'
export default function TasksModal(props) {
const { onClose = noopFn } = props
const { taskList } = useTasks()
return (
<Popup onClose={onClose}>4433443</Popup>
)
}
import { View } from '@tarojs/components'
import classnames from 'classnames'
import React, { useState } from 'react'
import Overlay from '../Overlay/Overlay'
import styles from './modal.module.less'
export default function Modal(props) {
const { closePostion = 'top-right', onClose = () => { }, top = '40%', hideCloseButton = false, closeOnClickOverlay = false } = props
const closeBtnClass = classnames(styles.closeButton, {
[`${styles.closeButton_bottom}`]: closePostion === 'bottom',
[`${styles.closeButton_right_top}`]: closePostion === 'top-right'
})
const [ isShowAnimateOut, setShowAnimate ] = useState(false)
const modalClass = classnames(styles.modal, {
'animate-fade-out': isShowAnimateOut
})
const onCloseModal = () => {
setShowAnimate(true)
setTimeout(() => {
setShowAnimate(false)
onClose()
}, 350)
}
const onClickOverlay = () => {
closeOnClickOverlay && onCloseModal()
}
return (
<View className={modalClass}>
<Overlay onTap={onClickOverlay} />
<View className={`${styles.content} animate-zoom-in`} style={{ top }}>
{!hideCloseButton && <View className={closeBtnClass} onTap={onCloseModal} />}
{props.children}
</View>
</View>
)
}
.modal {
.cover(fixed);
z-index: 20;
}
.content {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%) scale(0);
z-index: 22;
}
.closeButton {
.wh(50px, 50px);
.image("//yun.duiba.com.cn/taobaomini/template/modal_close_btn.png", contain);
}
.closeButton_right_top {
.pos-top-right(absolute, -50px);
}
.closeButton_bottom {
.pos-bottom();
left: 50%;
bottom: -80px;
transform: translateX(-50%);
}
.overlay {
.cover();
z-index: 21;
opacity: 0;
background-color: rgba(0, 0, 0, 0.7);
}
import { noopFn } from '@/utils/util'
import { View } from '@tarojs/components'
import React from 'react'
import styles from './OverLay.module.less'
export default function Overlay(props) {
const { onClick = noopFn, onTap = noopFn } = props
return (
<View className={`${styles.overlay} animate-fade-in`} onClick={onClick} onTap={onTap} />
)
}
import { noopFn } from '@/utils/util'
import { View } from '@tarojs/components'
import classnames from 'classnames'
import React, { useEffect, useState } from 'react'
import Overlay from '../Overlay/Overlay'
import styles from './Popup.module.less'
export default function Popup(props) {
const { onClose = noopFn, position = 'bottom', height = '30%', width = '100%', closeOnClickOverlay = true } = props
const [ isShowAnimate, setShowAnimate ] = useState(false)
const onCloseModal = () => {
setShowAnimate(true)
setTimeout(() => {
setShowAnimate(false)
onClose()
}, 350)
}
const onClickOverlay = () => {
closeOnClickOverlay && onCloseModal()
}
const wrapClass = classnames(styles.wrap, {
'animate-fade-out': isShowAnimate
})
const contentClass = classnames(styles.content, styles[position], {
'animate-fly-in': true,
'animate-fly-out': isShowAnimate
})
return (
<View className={wrapClass}>
<Overlay onTap={onClickOverlay} />
<View className={contentClass} style={{ width, height }}>
{props.children}
</View>
</View>
)
}
.content {
position: fixed;
max-height: 100%;
overflow-y: auto;
background-color: #fff;
// transition: transform 0.3s;
z-index: 24;
}
.bottom {
width: 100%;
bottom: 0;
left: 0;
transition: transform 0.3s;
}
.show {
transform: translateY(0);
transition: transform 0.3s;
}
<member-shop-center expend="{{true}}" onClose="onMemberModalClose" onAuthFail="onAuthFail" onAuthSuccess="onAuthSuccess"/>
Component({
data: {
visible: true
},
methods: {
onMemberModalClose() {
// console.log('this.props :>> ', this.props);
// 原生支付宝小程序组件无法调用 props 传入的函数 https://github.com/NervJS/taro/issues/7938
this.props.onClose({
type: 'close',
target: {
id: this.props.id
}
})
},
onAuthFail() {
this.props.onAuthFail({
type: 'authfail',
target: {
id: this.props.id
}
})
},
onAuthSuccess() {
this.props.onAuthSuccess({
type: 'authsuccess',
target: {
id: this.props.id
}
})
}
}
})
{ {
"component": true, "component": true,
"usingComponents": { "usingComponents": {
"count-down-modal": "/components/countDownModal/countDownModal" "member-shop-center": "plugin://cemMember/member-shop-center"
} }
} }
\ No newline at end of file
export default {
env: 'test', // 云函数环境 test 测试环境 online 线上环境
requestType: 'cloud', // cloud: 云函数, yapi: yapi 接口, ams: ams接口,tornadoAPI: 风驰台接口
cloudName: 'richMan', // 主云函数项目名
sellerId: '2207644377875',
tornadoAPI: 'https://tornado.duibadev.com.cn/tbServer/api?db=xxx&proxyIp=xxx', // 新增:当requestType: 'tornadoAPI'时,找服务端提供地址
defaultActivityId: '5ff7e0a3e5c0d7dafd30dce5' // 默认活动id
}
// 分享配置
export const SHARE_CONFIG = {
title: '分享测试标题',
desc: '分享测试描述',
imageUrl: '' // 分享图片URL
}
// 领取奖品状态 // 领取奖品状态
export const DRAW_STATUS = { export const DRAW_STATUS = {
// 待领取 // 待领取
...@@ -13,11 +20,36 @@ export const DRAW_STATUS = { ...@@ -13,11 +20,36 @@ export const DRAW_STATUS = {
// 重新领取 // 重新领取
RETRY: 6 RETRY: 6
} }
// 奖品类型 // 奖品类型
export const PRIZE_TYPE = { export const PRIZE_TYPE = {
ENAME: 1, ENAME: 1,
CREDITS: 2, CREDITS: 2,
OBJECT: 3, OBJECT: 3,
THANKS: 5 THANKS: 5
}; }
\ No newline at end of file
// 我的奖品状态
export const PRIZE_DRAW_STATUS_MAP = {
RECEIVE: 1, // 待领取
DEALING: 2, // 处理中
SUCCESS: 3, // 领取成功
FAILURE: 4, // 领取失败
FINISHED: 5, // 已过期
REPEAT: 6 // 重新领取
}
// 奖品类型
export const PRIZE_TYPE_MAP = {
EQUITY: 1, // 权益
INTEGRATION: 2, // 积分
ENTITY: 3, // 实物
THANKS: 5 // 谢谢参与
}
// 活动状态
export const ACTIVITY_STATUS = {
NO_EXIST: 0, // 不存在
PROCESSING: 1, // 正在进行
NO_START: 2, // 未开始
END: 3 // 已经结束
}
import { useState, useEffect } from 'react'
import { getAuthUserInfo } from '../utils/tb'
export function useAuthInfo() {
const [ authInfo, setAuthInfo ] = useState({
nickName: '',
avatar: ''
})
// 是否授权完成
const [ authorized, setAuthorizeStatus ] = useState(false)
const authUserInfo = async() => {
const info = await getAuthUserInfo().catch(err => {
console.log('未授权成功', err)
})
console.log('authInfo :>> ', info)
if (info) {
setAuthInfo(info)
setAuthorizeStatus(true)
}
}
useEffect(() => {
authUserInfo()
}, [])
return {
authInfo,
authorized
}
}
import { useRef, useEffect, useCallback } from 'react'
export function useDebounce(fn, delay, dep = []) {
const { current } = useRef({ fn, timer: null })
useEffect(function() {
current.fn = fn
}, [ fn ])
return useCallback(function f(...args) {
if (current.timer) {
clearTimeout(current.timer)
}
current.timer = setTimeout(() => {
current.fn.call(this, ...args)
}, delay)
}, dep)
}
import { useEffect, useState } from 'react'
import { useRouter } from '@tarojs/taro'
import { useLoginInfoModel } from '../store/loginInfo'
import { login } from '../api'
import { checkShopFavoredStatus } from '../utils/tb'
import config from '../config'
import { useAuthInfo } from './useAuth'
import { noopFn } from '@/utils/util'
export function useLogin(callback = noopFn) {
const { authorized, authInfo } = useAuthInfo()
const { loginInfo, setLoginInfo } = useLoginInfoModel()
const [ isLogin, setLoginStatus ] = useState(false)
const { params } = useRouter()
const doLogin = async() => {
const isFollow = await checkShopFavoredStatus(config.sellerId)
const { nickName: userNick, avatar } = authInfo
const { inviteId } = params
const res = await login({ userNick, avatar, isFollow, inviteId })
if (res?.success) {
setLoginInfo({
...res.data,
userNick,
avatar,
inviteId
})
setLoginStatus(true)
console.log('isLogin :>> ', true)
callback(loginInfo)
}
}
useEffect(() => {
if (authorized) {
doLogin()
}
console.log('authorized:>> ', authorized)
}, [ authorized ])
return { isLogin }
}
export function useLoginFromShare(callback = noopFn) {
const { loginInfo } = useLoginInfoModel()
// 只执行一次
const [ executed, setExecuted ] = useState(false)
useEffect(() => {
if (loginInfo.inviteId && !executed) {
callback(loginInfo.inviteId)
setExecuted(true)
}
}, [ loginInfo ])
return {
inviteId: loginInfo.inviteId
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment