Commit 7e79f207 authored by 王能飞's avatar 王能飞

Merge branch 'c_client_taro-pack' into 'c_client_taro'

新模版合并

See merge request !38
parents 10bc6224 105bc537
No preview for this file type
```
taobao-mini-template
└─ c_client
├─ .editorconfig
├─ .eslintrc.js
├─ .gitignore
├─ babel.config.js
├─ config
│ ├─ default.js
│ ├─ dev.js
│ ├─ index.js
│ └─ prod.js
├─ jsconfig.json
├─ minigame
│ ├─ package-lock.json
│ ├─ package.json
│ └─ yarn.lock
├─ package-lock.json
├─ package.json
├─ plugin
│ └─ changeAppJsonPlugin.js
├─ project.config.json
├─ src
│ ├─ api.js
│ ├─ app.config.js
│ ├─ app.js
│ ├─ app.less
│ ├─ components
│ │ ├─ _base
│ │ │ ├─ Modal
│ │ │ │ ├─ Modal.config.js
│ │ │ │ ├─ Modal.jsx
│ │ │ │ └─ Modal.module.less
│ │ │ ├─ Overlay
│ │ │ │ ├─ OverLay.module.less
│ │ │ │ └─ Overlay.jsx
│ │ │ └─ Popup
│ │ │ ├─ Popup.jsx
│ │ │ └─ Popup.module.less
│ │ ├─ _miniprogram
│ │ │ ├─ game
│ │ │ │ ├─ game.acss
│ │ │ │ ├─ game.axml
│ │ │ │ ├─ game.js
│ │ │ │ ├─ game.json
│ │ │ │ ├─ output.js
│ │ │ │ └─ utils.js
│ │ │ ├─ lottieAnimation
│ │ │ │ ├─ lottieAnimation.acss
│ │ │ │ ├─ lottieAnimation.axml
│ │ │ │ ├─ lottieAnimation.js
│ │ │ │ └─ lottieAnimation.json
│ │ │ ├─ lottieBlank
│ │ │ │ ├─ lottieBlank.acss
│ │ │ │ ├─ lottieBlank.axml
│ │ │ │ ├─ lottieBlank.js
│ │ │ │ └─ lottieBlank.json
│ │ │ └─ memberModal
│ │ │ ├─ memberModal.acss
│ │ │ ├─ memberModal.axml
│ │ │ ├─ memberModal.js
│ │ │ └─ memberModal.json
│ │ ├─ _tb_comps
│ │ │ ├─ CountDown
│ │ │ │ ├─ CountDown.jsx
│ │ │ │ └─ CountDown.less
│ │ │ ├─ GoodsList
│ │ │ │ ├─ GoodsList.jsx
│ │ │ │ └─ GoodsList.module.less
│ │ │ ├─ GoodsTitle
│ │ │ │ ├─ GoodsTitle.jsx
│ │ │ │ └─ GoodsTitle.module.less
│ │ │ ├─ Input
│ │ │ ├─ Progress
│ │ │ │ ├─ Progress.jsx
│ │ │ │ └─ Progress.module.less
│ │ │ ├─ RankList
│ │ │ │ ├─ RankList.jsx
│ │ │ │ └─ RankList.module.less
│ │ │ ├─ RankTitle
│ │ │ │ ├─ RankTitle.jsx
│ │ │ │ └─ RankTitle.module.less
│ │ │ ├─ ScrollXView
│ │ │ │ ├─ ScrollXView.jsx
│ │ │ │ └─ ScrollXView.module.less
│ │ │ └─ Swiper
│ │ │ ├─ Swiper.jsx
│ │ │ └─ Swiper.module.less
│ │ ├─ _tb_custom
│ │ │ └─ Test
│ │ │ ├─ Test.jsx
│ │ │ └─ Test.module.less
│ │ └─ _tb_modal
│ │ ├─ DeliveryModal
│ │ │ ├─ DeliveryModal.jsx
│ │ │ └─ DeliveryModal.module.less
│ │ ├─ DoHelpModal
│ │ │ ├─ DoHelpModal.jsx
│ │ │ └─ DoHelpModal.module.less
│ │ ├─ PrizeModal
│ │ │ ├─ PrizeModal.jsx
│ │ │ └─ PrizeModal.module.less
│ │ ├─ RuleModal
│ │ │ ├─ RuleModal.jsx
│ │ │ └─ RuleModal.module.less
│ │ ├─ TasksModal
│ │ │ ├─ TasksModal.config.js
│ │ │ ├─ TasksModal.jsx
│ │ │ └─ TasksModal.module.less
│ │ └─ ThanksModal
│ │ ├─ ThanksModal.jsx
│ │ └─ ThanksModal.module.less
│ ├─ config.js
│ ├─ const.js
│ ├─ hooks
│ │ ├─ useAuth.js
│ │ ├─ useDebounce.js
│ │ ├─ useLogin.js
│ │ ├─ useRequest.js
│ │ ├─ useTasks.js
│ │ └─ useThrottle.js
│ ├─ index.html
│ ├─ lottie
│ │ ├─ index.js
│ │ └─ lottieData.json
│ ├─ mock.js
│ ├─ pages
│ │ ├─ index
│ │ │ ├─ index.config.js
│ │ │ ├─ index.jsx
│ │ │ └─ index.module.less
│ │ ├─ packageGame
│ │ │ └─ game
│ │ │ ├─ game.config.js
│ │ │ ├─ game.jsx
│ │ │ └─ game.module.less
│ │ ├─ packageGood
│ │ │ ├─ browseGoods
│ │ │ │ ├─ browseGoods.config.js
│ │ │ │ ├─ browseGoods.jsx
│ │ │ │ └─ browseGoods.module.less
│ │ │ ├─ cartGoods
│ │ │ │ ├─ cartGoods.config.js
│ │ │ │ ├─ cartGoods.jsx
│ │ │ │ └─ cartGoods.module.less
│ │ │ ├─ collectGoods
│ │ │ │ ├─ collectGoods.config.js
│ │ │ │ ├─ collectGoods.jsx
│ │ │ │ └─ collectGoods.module.less
│ │ │ └─ orderGoods
│ │ │ ├─ orderGoods.config.js
│ │ │ ├─ orderGoods.jsx
│ │ │ └─ orderGoods.module.less
│ │ └─ packagePrize
│ │ └─ myPrize
│ │ ├─ myPrize.config.js
│ │ ├─ myPrize.jsx
│ │ └─ myPrize.module.less
│ ├─ store
│ │ ├─ activityInfo.js
│ │ ├─ index.js
│ │ └─ loginInfo.js
│ ├─ styles
│ └─ utils
│ ├─ date.js
│ ├─ mdProxy.js
│ └─ util.js
└─ yarn.lock
```
\ No newline at end of file
1.安装依赖
```
npm install
```
2.运行项目(淘宝环境)
```
npm run dev:alipay
```
3.使用淘宝IDE打开dist文件夹关联相应小程序
4.生产环境打包
```
npm run build:alipay
```
5.使用IDE上传版本
export default { export default {
hasGameModule:false, // 是否开启游戏模块自动注入dist hasCanvas: false, // 是否开启游戏模块自动注入dist
hasLottieAnimation: false,// 使用Lottie动画
defaultPlugins: [ defaultPlugins: [
[ [
'@pluve/taro-plugin-mars', // taro暂时未支持阿里小程序自定义插件 需要引用插件的页面需配置 '@pluve/taro-plugin-mars', // taro暂时未支持阿里小程序自定义插件 需要引用插件的页面需配置
{ {
usingComponents: {// 原生组件引入 usingComponents: {// 原生组件引入
enable: true, enable: true,
include: [ 'pages/index/index', 'pages/myPrize/myPrize' ] include: [ 'pages/index/index', 'pages/packagePrize/myPrize/myPrize','pages/packageGame/game/game']
} }
} }
] ]
], ],
addPages: ['pages/games/games'],// 游戏页面(自动注入app.json)'pages/games/games' canvasPages: [
gamePages: [// 拷贝相关页面和依赖到dist目录 {'root': 'pages/packageGame/game/game.json', 'comRoot': 'components/_miniprogram/canvasComp/canvasComp'}, // 游戏组件
{ from: 'src/pages/games', to: 'dist/pages/games' }, // {'root': 'pages/index/index.json', 'comRoot': 'components/_miniprogram/lottieAnimation/lottieAnimation'} // Lottie组件
],// 引入cavas组件页面(更改canvas引用路径)
canvasComps: [// 拷贝相关页面和依赖到dist目录
{ from: 'minigame/node_modules', to: 'dist/node_modules'}, { from: 'minigame/node_modules', to: 'dist/node_modules'},
{ from: 'src/components/_miniprogram/taskModal', to: 'dist/components/taskModal'}, { from: 'src/components/_miniprogram/canvasComp', to: 'dist/components/_miniprogram/canvasComp'}, // 游戏组建引入
// { from: 'src/lottie', to: 'dist/lottie'}, // { from: 'src/components/_miniprogram/lottieAnimation', to: 'dist/components/_miniprogram/lottieAnimation'}, // lottie 动画组件
// { from: 'src/components/_miniprogram/lottieAnimation', to: 'dist/components/lottieAnimation'} // { from: 'src/lottie', to: 'dist/lottie'},// lottie json文件
],
lottiePages: [
{ from: 'minigame/node_modules', to: 'dist/node_modules'},
{ from: 'src/lottie', to: 'dist/lottie'},
{ from: 'src/components/_miniprogram/lottieAnimation', to: 'dist/components/lottieAnimation'}
] ]
} }
\ No newline at end of file
// eslint-disable-next-line import/no-commonjs // eslint-disable-next-line import/no-commonjs
const path = require('path') const path = require('path')
const { hasGameModule, defaultPlugins, addPages, gamePages, hasLottieAnimation, lottiePages } = require('./default').default const { hasCanvas, defaultPlugins, canvasComps } = require('./default').default
const config = { const config = {
projectName: 'taro-test', projectName: 'taro-test',
...@@ -16,22 +16,22 @@ const config = { ...@@ -16,22 +16,22 @@ const config = {
alias: { alias: {
'@': path.resolve(__dirname, '..', 'src/') '@': path.resolve(__dirname, '..', 'src/')
}, },
plugins: hasGameModule || hasLottieAnimation ? [...defaultPlugins,[ // 是否开启原生页面自动注入 plugins: hasCanvas ? [...defaultPlugins,[ // 是否开启原生页面自动注入
path.resolve(__dirname,'..')+'/plugin/changeAppJsonPlugin.js', path.resolve(__dirname,'..')+'/plugin/changeAppJsonPlugin.js'
{ ]
hasGameModule, ] : defaultPlugins,
hasLottieAnimation
}
]] : defaultPlugins,
defineConstants: { defineConstants: {
}, },
copy: { copy: {
patterns: hasGameModule ? gamePages : hasLottieAnimation ? lottiePages : [], patterns: hasCanvas ? canvasComps : [],
options: { options: {
} }
}, },
framework: 'react', framework: 'react',
mini: { mini: {
compile: {
exclude: []
},
postcss: { postcss: {
pxtransform: { pxtransform: {
enable: true, enable: true,
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"fyge": { "fyge": {
"version": "2.0.23", "version": "2.0.31",
"resolved": "https://registry.npmjs.org/fyge/-/fyge-2.0.23.tgz", "resolved": "https://registry.npmjs.org/fyge/-/fyge-2.0.23.tgz",
"integrity": "sha512-5EA6FHrwpp9sAMyctuyE+gyUH10uGT5PPp1O1gZfohJNx1/bkCPcUomnXApa8367tJ5UiPhiVi3dZeqUdJiZVQ==" "integrity": "sha512-5EA6FHrwpp9sAMyctuyE+gyUH10uGT5PPp1O1gZfohJNx1/bkCPcUomnXApa8367tJ5UiPhiVi3dZeqUdJiZVQ=="
} }
......
...@@ -4,6 +4,6 @@ ...@@ -4,6 +4,6 @@
"main": "", "main": "",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"fyge": "*" "fyge": "2.0.31"
} }
} }
...@@ -2,20 +2,7 @@ ...@@ -2,20 +2,7 @@
# yarn lockfile v1 # yarn lockfile v1
duiba-utils@^1.0.0: fyge@2.0.31:
version "1.0.9" version "2.0.31"
resolved "http://npm.dui88.com:80/duiba-utils/-/duiba-utils-1.0.9.tgz#7a1e09e08a8754a1563b18aa6b117cb270f76877" resolved "https://registry.yarnpkg.com/fyge/-/fyge-2.0.31.tgz#49afcdfef92e23b53e42bef6a93f3b2211af281e"
integrity sha512-3YB5w05gNruS4yyN0cx2Qktr0vG0AXYOumfV6WYlzlMx3kGX3AihBLT2/PouERtkLfFk2q4cIikSkgwramHTmg== integrity sha512-HIt26NvgQ5udlI5jJwYgVSqBdfy1qX0+L1TseJNuyUVWgzkNGjQy2rGnXXA2yuWoSgWoDGtKfL94CC9FCAsNyg==
fyge@*:
version "2.0.23"
resolved "http://npm.dui88.com:80/fyge/-/fyge-2.0.23.tgz#36550f93d9578cd3b02ae9772f93aa94730abd25"
integrity sha512-nNUD5HAPyknIMKdxxa50HzriLuBFmk4XLnZw5CgiX3mH7ROJzsh/ToRMTKXcxwH3r3ICid95cgEo0iCPIHSIsQ==
dependencies:
duiba-utils "^1.0.0"
tslib "^2.1.0"
tslib@^2.1.0:
version "2.2.0"
resolved "http://npm.dui88.com:80/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
integrity sha1-+yxHWXfjXiQTEe3iaTzuHsZpj1w=
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -26,7 +26,8 @@ ...@@ -26,7 +26,8 @@
"dev:rn": "npm run build:rn -- --watch", "dev:rn": "npm run build:rn -- --watch",
"dev:qq": "npm run build:qq -- --watch", "dev:qq": "npm run build:qq -- --watch",
"dev:jd": "npm run build:jd -- --watch", "dev:jd": "npm run build:jd -- --watch",
"dev:quickapp": "npm run build:quickapp -- --watch" "dev:quickapp": "npm run build:quickapp -- --watch",
"preview": "taobaodev build-preview --debug"
}, },
"browserslist": [ "browserslist": [
"last 3 versions", "last 3 versions",
...@@ -38,33 +39,33 @@ ...@@ -38,33 +39,33 @@
"@babel/helper-create-class-features-plugin": "^7.13.0", "@babel/helper-create-class-features-plugin": "^7.13.0",
"@babel/runtime": "^7.7.7", "@babel/runtime": "^7.7.7",
"@pluve/taro-plugin-mars": "^1.1.1", "@pluve/taro-plugin-mars": "^1.1.1",
"@tarojs/cli": "v3.2.0-canary.9", "@tarojs/cli": "3.3.2",
"@tarojs/components": "v3.2.0-canary.9", "@tarojs/components": "3.3.2",
"@tarojs/react": "v3.2.0-canary.9", "@tarojs/react": "3.3.2",
"@tarojs/runtime": "v3.2.0-canary.9", "@tarojs/runtime": "3.3.2",
"@tarojs/taro": "v3.2.0-canary.9", "@tarojs/taro": "3.3.2",
"@tbmp/mp-cloud-sdk": "^1.4.2", "@tbmp/mp-cloud-sdk": "^1.4.2",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"hox": "^1.1.2", "hox": "^1.1.2",
"lodash": "4.17.15", "lodash": "4.17.15",
"react": "^16.10.0", "react": "^17.0.0",
"react-dom": "^16.10.0", "react-dom": "^17.0.0",
"taro-ui": "^3.0.0-alpha.3", "taro-ui": "^3.0.0-alpha.3",
"tbcc-sdk-ts": "^1.0.5" "tbcc-sdk-ts": "^1.0.7"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.8.0", "@babel/core": "^7.8.0",
"@tarojs/mini-runner": "v3.2.0-canary.9", "@tarojs/mini-runner": "3.3.2",
"@tarojs/webpack-runner": "v3.2.0-canary.9", "@tarojs/webpack-runner": "3.3.2",
"@types/react": "^16.0.0", "@types/react": "^16.0.0",
"@types/webpack-env": "^1.13.6", "@types/webpack-env": "^1.13.6",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"babel-preset-taro": "v3.2.0-canary.9", "babel-preset-taro": "3.3.2",
"eslint": "^7.12.1", "eslint": "^7.12.1",
"eslint-config-standard": "^16.0.2", "eslint-config-standard": "^16.0.2",
"eslint-config-standard-jsx": "^10.0.0", "eslint-config-standard-jsx": "^10.0.0",
"eslint-config-standard-react": "^11.0.1", "eslint-config-standard-react": "^11.0.1",
"eslint-config-taro": "v3.2.0-canary.9", "eslint-config-taro": "3.3.2",
"eslint-plugin-import": "^2.22.1", "eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0", "eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.3.1", "eslint-plugin-promise": "^4.3.1",
......
const fs = require('fs'); const fs = require('fs');
const path = require('path') const path = require('path')
const { addPages } = require('../config/default').default const { canvasPages, hasCanvas } = require('../config/default').default
export default (ctx, options) => { export default (ctx, options) => {
// plugin 主体 // plugin 主体
const { hasGameModule = false, hasLottieAnimation = false } = options
ctx.onBuildStart(() => { ctx.onBuildStart(() => {
// console.log('编译开始!') // console.log('编译开始!')
}) })
ctx.onBuildFinish(() => { ctx.onBuildFinish(() => {
if(hasGameModule) { if(hasCanvas) {
const dirUrl = path.resolve(__dirname,'..')+'/dist/app.json' for(let i = 0; i < canvasPages.length; i++) {
const rawdata = fs.readFileSync(dirUrl); const itm = canvasPages[i]
const { pages, ...ext } = JSON.parse(rawdata); const dirUrl = path.resolve(__dirname,'..')+`/dist/${itm.root}`
let new_pages = [...pages,...addPages]
const _new_info = {
...ext,
pages: new_pages
}
fs.writeFileSync(dirUrl, JSON.stringify(_new_info));
}
if(hasLottieAnimation) {
const dirUrl = path.resolve(__dirname,'..')+'/dist/pages/index/index.json'
const rawdata = fs.readFileSync(dirUrl); const rawdata = fs.readFileSync(dirUrl);
const { usingComponents, ...ext } = JSON.parse(rawdata); const { usingComponents, ...ext } = JSON.parse(rawdata);
const new_info = { const new_info = {
...ext, ...ext,
usingComponents: { usingComponents: {
...usingComponents, ...usingComponents,
"lottie-modal":"/components/lottieAnimation/lottieAnimation" "canvas-comp": `/${itm.comRoot}`
} }
} }
fs.writeFileSync(dirUrl, JSON.stringify(new_info)); fs.writeFileSync(dirUrl, JSON.stringify(new_info));
} }
}
console.log('文件写入成功!'); console.log('文件写入成功!');
//ctx.writeFileToDist({ filePath: string, content: string }) //ctx.writeFileToDist({ filePath: string, content: string })
}) })
......
import cloud from '@tbmp/mp-cloud-sdk' import cloud from '@tbmp/mp-cloud-sdk'
import tbccTs from 'tbcc-sdk-ts' import tbccTs from 'tbcc-sdk-ts'
import conifg from './config' import conifg from './config/config'
const { utils: { generateAPI }, request: tbccRequest, capi, capiFn } = tbccTs const { utils: { generateAPI }, request: tbccRequest, capi } = tbccTs
const { cloudName, requestType } = conifg const { cloudName, requestType } = conifg
const request = tbccRequest({ const request = tbccRequest({
...@@ -11,7 +10,6 @@ const request = tbccRequest({ ...@@ -11,7 +10,6 @@ const request = tbccRequest({
cloudName, cloudName,
requestType requestType
}) })
/** /**
const defaultConfig = { const defaultConfig = {
method: 'GET', method: 'GET',
...@@ -22,27 +20,36 @@ const request = tbccRequest({ ...@@ -22,27 +20,36 @@ const request = tbccRequest({
// api handle 列表 // api handle 列表
const apiList = { const apiList = {
...capi, ...capi,
receiveObjectPrize: {
handle: 'receiveObjectPrize',
method: 'GET',
isShowLoading: false,
toastError: false
},
receiveEnamePrize: {
handle: 'receiveEnamePrize',
method: 'GET',
isShowLoading: false,
toastError: false
},
getActivityBaseInfoById: { getActivityBaseInfoById: {
handle: 'getActivityBaseInfoById', handle: 'getActivityBaseInfoById',
method: 'GET', method: 'GET',
isShowLoading: false isShowLoading: false
}, },
login: 'login', login: 'login',
getTaskList: 'getTaskList',
doCompleteTask: 'doCompleteTask',
receiveTaskRewards: 'receiveTaskRewards',
addStat: 'addStat', addStat: 'addStat',
doHelp: 'doHelp', drawLottery: 'drawLottery',
drawLotteryPrize: 'drawLotteryPrize',
resetTest: 'resetTest',
getShareInfo: 'getShareInfo', getShareInfo: 'getShareInfo',
getVipInfo: 'getVipInfo', getCollectGoods: 'getCollectGoods',
getCollectGoodsList: 'getCollectGoodsList', updateEnamePrizeReceived: 'updateEnamePrizeReceived', // 会员权益领取
getItemListByItemIds: 'getItemListByItemIds' getRotatePrizeListInfo:'getRotatePrizeListInfo',//获取奖品列表接口,暂未实现
drawRotatePrize:'drawRotatePrize',//大转盘抽奖接口,暂未实现
exchangePrize: 'exchangePrize', // 兑换奖品
getUserInfo: 'getUserInfo', // 获取用户信息
} }
// 生成API // 生成API
const API = generateAPI(apiList, request) const API = generateAPI(apiList, request)
API.fn = capiFn(apiList, request)
export default API export default API
export default { export default {
pages: [ pages: [
'pages/index/index', 'pages/index/index'
'pages/myPrize/myPrize', ],
'pages/browseGoods/browseGoods', subPackages: [
'pages/collectGoods/collectGoods', {
'pages/orderGoods/orderGoods' "root": "pages/packagePrize",
"pages": [
'myPrize/myPrize'
]
},
{
"root": "pages/packageGood",
"pages": [
'browseGoods/browseGoods',
'collectGoods/collectGoods',
'orderGoods/orderGoods',
'cartGoods/cartGoods'
]
},
{
"root": "pages/packageGame",
"pages": [
'game/game'
]
},
{
"root": "pages/packageComs",
"pages": [
'comsTest/comsTest'
]
}
], ],
window: { window: {
enableSkia: 'true', enableSkia: 'true',
allowsBounceVertical: 'NO', allowsBounceVertical: 'NO',
navigationBarTitleText: '活动名称' navigationBarTitleText: '活动名称',
navigationBarForceEnable: false
}, },
plugins: { plugins: {
cemMember: { cemMember: {
......
...@@ -4,7 +4,7 @@ import './app.less' ...@@ -4,7 +4,7 @@ import './app.less'
// 使用taro-ui 按需引入的组件样式 // 使用taro-ui 按需引入的组件样式
import './taro-ui.scss' import './taro-ui.scss'
import config from './config' import config from './config/config'
import './utils/mdProxy' import './utils/mdProxy'
const { env, tornadoAPI, defaultActivityId } = config const { env, tornadoAPI, defaultActivityId } = config
...@@ -20,6 +20,10 @@ class App extends Component { ...@@ -20,6 +20,10 @@ class App extends Component {
app.tornadoAPI = tornadoAPI app.tornadoAPI = tornadoAPI
app.activityId = activityId || defaultActivityId app.activityId = activityId || defaultActivityId
} }
// 页面后台切前台
componentDidShow () {}
// 页面前台切后台
componentDidHide() {}
render() { render() {
return this.props.children return this.props.children
} }
......
import React, { useState, useEffect, useRef } from 'react'
import { View, Text } from '@tarojs/components'
import { useDidShow, useDidHide } from '@tarojs/taro'
import tbcc from 'tbcc-sdk-ts'
import './CountDown.less'
const { getServerTime } = tbcc.tb
export default function CountDown(props) {
const { endTime, startTime, onUpdate, type = 1, color = '#000', bgColor = 'transparent', fontSize = '26rpx', padding = '0' } = props
const [countDown, setCountDown] = useState(type === 1 ? ['00', ':', '00', ':', '00'] : ['00', '天', '00', '时', '00', '分', '00', '秒'])
const [nowTime, setNowTime] = useState(startTime || Date.now())
const countTimer = useRef(null)
const isAccessRender = useRef(false)
useEffect(() => {
countTimeFn()
return () => clearInterval(countTimer.current)
}, [endTime, nowTime])
useDidShow(() => {
if(isAccessRender.current) {
setNowTime(startTime || Date.now())
}
isAccessRender.current = true
})
const countTimeFn = async () => {
const _nowTime = await getServerTime()
let diffTime = endTime - (startTime || _nowTime)
countTimer.current = setInterval(() => {
if (diffTime > 1000) {
let day = Math.floor(diffTime / (3600 * 1000) / 24)
let hour = Math.floor((diffTime / 1000 / 3600) % 24)
let minute = Math.floor((diffTime / 1000 / 60) % 60)
let second = Math.floor(diffTime / 1000 % 60)
day = day < 10 ? '0' + day : day
hour = hour < 10 ? '0' + hour : hour
minute = minute < 10 ? '0' + minute : minute
second = second < 10 ? '0' + second : second
setCountDown(type === 1 ? [hour, ':', minute, ':', second] : [day, '天', hour, '时', minute, '分', second, '秒'])
diffTime -= 1000
} else {
setCountDown(type === 1 ? ['00', ':', '00', ':', '00'] : ['00', '天', '00', '时', '00', '分', '00', '秒'])
clearInterval(countTimer.current)
onUpdate && onUpdate()
}
}, 1000)
}
return (
<View className="count-down">
{
countDown.map((item, i) => {
return (
<Text className="count-down-time" key={i} style={{ backgroundColor: item !== ':' ? bgColor : 'transparent', fontSize, color, padding }}>{item}</Text>
)
})
}
</View>
)
}
\ No newline at end of file
.count-down {
display: flex;
align-items: center;
}
.count-down-time {
border-radius: 4rpx;
}
\ No newline at end of file
import { View } from '@tarojs/components'
import React, { useState } from 'react'
import { useThrottle } from '@/hooks/useThrottle'
import Modal from '@/components/_base/Modal/Modal'
import styles from './DoHelpModal.module.less'
import API from '@/api'
import { showToast } from '@tarojs/taro'
import { useLoginInfoModel } from '@/store'
import { checkIsMember } from 'tbcc-sdk-ts/lib/utils'
import { commonToast } from 'tbcc-sdk-ts/lib/core/tb'
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 API.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 API.doHelp({ inviteId })
if (helpRes?.success) {
showToast({ title: '助力成功' })
} else {
commonToast(helpRes?.message)
}
onClose()
}
// 助力条件为新会员
const isVip = await checkIsMember()
// 已经是会员
if (isVip) {
commonToast('新会员才能助力哦~')
} else {
// 还不是会员,弹出会员弹窗
setMemberModalVisible(true)
}
}, 2000)
const closeMemberModal = () => {
setMemberModalVisible(false)
}
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={() => closeMemberModal()}
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 React from 'react'
import tbccTs from 'tbcc-sdk-ts'
const { openDetail, collectGoods, checkGoodsCollectedStatus, commonToast } = tbccTs.tb
import './GoodsPage.less'
export default function GoodsPage(props) {
const {
isBackFlag = 0, goodsList = [],
task = {
itemId: '617724147979,617724563528,617300295119',
taskType: 'browseGoods',
type: '02', // 01 banner 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'
},
onOpenDetail,
onCompleteTask
} = props
const goToGoodsDetail = async(item) => {
const { taskType } = task
const { itemId } = item
if (taskType === 'browseGoods') {
onOpenDetail && onOpenDetail(itemId)
}
await openDetail(String(itemId))
}
// 收藏商品
const goToCollectGoods = async(item) => {
const { itemId, collected } = item
const { taskType } = task
if (collected) {
commonToast('您已收藏过该商品了')
return
}
// 判断是否活动外已收藏商品
const isCollected = await checkGoodsCollectedStatus(+itemId)
if (isCollected) {
onCompleteTask && onCompleteTask(taskType, itemId)
return
}
const result = await collectGoods(+itemId)
if (result) {
onCompleteTask && onCompleteTask(taskType, itemId)
}
}
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={'goods_'+i}>
<View className='goods-modal-container__content-item__img' onClick={() => goToGoodsDetail(item)}>
<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
onClick={() => goToCollectGoods(item)}
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>
)
}
import { View, ScrollView, Image } from '@tarojs/components'
import React, { useState, useEffect } from 'react'
import Popup from '@/components/_base/Popup/Popup'
import { noopFn } from '@/utils/util'
import { useTasks } from '@/hooks/useTasks'
import tbccTs from 'tbcc-sdk-ts'
import API from '@/api'
import config from '@/config'
import { useThrottle } from '@/hooks/useThrottle'
import { TASK_STATUS, TASK_CONFIG } from '@/const'
import './TasksModal.less'
import { useDidShow } from '@tarojs/taro'
const { commonToast, favorShop, navigateTo, navigateToOutside, showSharePanel } = tbccTs.tb
export default function TasksModal(props) {
const app = getApp()
const { onClose = noopFn, onUpdate, taskFlag } = props
const [ memberShopVisible, setMemberShopVisible ] = useState(false)
const [ currentTaskType, setCurrentTaskType ] = useState('')
const [ preBrowseTime, setPreBrowseTime ] = useState(null)
const { taskList, fetchTaskList } = useTasks()
const { taskIcon, commonTaskTxt, doTaskTxt, browseType, browseTime } = TASK_CONFIG
useDidShow(() => {
if (app.isFlashTask) {
app.isFlashTask = false
if (preBrowseTime && ((Date.now() - preBrowseTime) / 1000) >= browseTime) {
doCompleteTaskHandle(currentTaskType, true)
} else {
commonToast(`未达到${browseTime}秒~`)
fetchTaskList()
}
}
})
const handleTapItem = useThrottle(async(item) => {
const { status, taskType } = item
const tapFn = {
[TASK_STATUS.WAIT_RECEIVE]: async() => {
const { success, data } = await API.receiveTaskRewards({ taskType })
if (success && data) {
const { rewards } = data
commonToast(`领取成功,次数+${rewards}`)
fetchTaskList()
onUpdate && onUpdate()
}
},
[TASK_STATUS.WAIT_DO]: () => goToComplete(item)
}
tapFn[status] && tapFn[status]()
})
const goToComplete = async(item) => {
const { taskType, url, itemIds, keepTime } = item
setCurrentTaskType(taskType)
const completeFn = {
member: () => setMemberShopVisible(true),
invites: () => showSharePanel(),
follow: async() => {
const isFollow = await favorShop(config.sellerId)
if (isFollow) {
doCompleteTaskHandle(taskType)
}
},
// 跳转任务 https://www.feizhu.com
jumpLink: async() => {
navigateToOutside(url)
if (browseType === 2) {
doCompleteTaskHandle(taskType)
} else {
app.isFlashTask = true
setPreBrowseTime(Date.now())
}
},
browseGoods: async() => {
app.isFlashTask = true
if (url) {
navigateToOutside(url)
return
}
navigateTo(`/pages/browseGoods/browseGoods?itemIds=${itemIds}&keepTime=${keepTime}`)
},
orderGoods: async() => {
app.isFlashTask = true
if (url) {
navigateToOutside(url)
return
}
navigateTo(`/pages/orderGoods/orderGoods?itemIds=${itemIds}`)
},
collectGoods: async() => {
app.isFlashTask = true
navigateTo(`/pages/collectGoods/collectGoods?itemIds=${itemIds}`)
},
sign: () => doCompleteTaskHandle(taskType)
}
completeFn[taskType] && completeFn[taskType]()
}
const doCompleteTaskHandle = async(taskType, flag) => {
const { success } = await API.doCompleteTask({ taskType })
if (success) {
await fetchTaskList()
flag && setPreBrowseTime(null)
}
}
const onCloseMemberModal = () => {
setMemberShopVisible(false)
}
const onAuthSuccess = () => {
setMemberShopVisible(false)
fetchTaskList()
}
const onAuthFail = () => {
setMemberShopVisible(false)
}
const onCloseModal = () => {
onClose && onClose()
}
return (
<>
<Popup onClose={onCloseModal} height='700rpx'>
<View className='task-modal-container'>
<View className='task-modal-content'>
<View className='task-modal-content__title'>任务模块</View>
<View className='task-modal-content__list'>
<ScrollView scroll-y='{{true}}' className='task-modal-content__scroll'>
{
taskList.map((item, i) => {
return (
<View className='task-modal-content-item' key={'task_I' + i}>
<View className='task-modal-content-item__left'>
<Image src={taskIcon[item.taskType]} mode='widthFix' />
</View>
<View className='task-modal-content-item__left-label'>
<View className='task-modal-content-item__left-title'>{item.title}</View>
<View className='task-modal-content-item__left-reward'>抽盒次数+{item.rewards}</View>
</View>
<View className={'task-modal-content__list-item__right' + ' ' + 'task-item__status-' + item.status} onClick={() => handleTapItem(item)}>
{
item.status === TASK_STATUS.WAIT_DO ? (doTaskTxt[item.taskType] || '去完成') : commonTaskTxt[item.status]
}
</View>
</View>
)
})
}
</ScrollView>
</View>
</View>
</View>
</Popup>
{memberShopVisible && <member-modal onClose={onCloseMemberModal} onAuthFail={onAuthFail} onAuthSuccess={onAuthSuccess} />}
</>
)
}
import { View } from '@tarojs/components'
import React from 'react'
import styles from './ContainerFit.module.less'
export default function ContainerFit(props) {
const { bg = '', hasFitPsd = false } = props
return (
<View className={styles['page-container']}>
<View className={styles['page-psd-container']} style={{ background: `url(${bg}) no-repeat`, backgroundSize: '7.5rem 16.24rem' }}>
{!hasFitPsd && <View className={styles['page-content']}>
{props.children}
</View>}
{hasFitPsd && props.children}
</View>
</View>
)
}
\ No newline at end of file
### 设计稿适配方案
#### 入参
| 参数 | 类型 | 描述 |
| --- | --- | --- |
|bg| String |背景图(1624px),非必填|
|hasFitPsd| Boolean |内容块是否对标设计稿(750px * 1624px),非必填|
#### 使用
``` jsx
import ContainerFit from '@/components/_base/ContainerFit/ContainerFit'
<ContainerFit hasFitPsd={true} bg={''}>
</ContainerFit>
```
\ No newline at end of file
.page-container {
position: relative;
top: 0;
right: 0;
left: 0;
width: 100vw;
height: 100vh;
min-height: 100vh;
overflow: hidden;
}
.page-psd-container {
position: absolute;
width: 100vw;
height: 1624px;
left: 0;
right: 0;
top: 50%;
transform: translate(0,-50%);
}
.page-content {
position: absolute;
width: 100vw;
height: 100vh;
left: 0;
right: 0;
top: 50%;
transform: translate(0,-50%);
}
\ No newline at end of file
import { ScrollView } from '@tarojs/components'
import React, { useState, useImperativeHandle, forwardRef } from 'react'
import { useThrottle } from '@/hooks/useThrottle'
import styles from './ContainerScroll.module.less'
const ContainerFit = forwardRef((props, ref) => {
const { } = props
// 滚动
const [scrollTop,setScrollTop] = useState(0)
// 滚动指定位置
useImperativeHandle(ref,() => ({ scrollFixTop }))
const scrollFixTop = (topFix) => {
setScrollTop(topFix)
}
// 滑动
const onScroll = useThrottle((e) => {
const { scrollTop: _scrollTop } = e.detail
setScrollTop(_scrollTop)
})
return (
<ScrollView className={styles['scroll-container']}
scrollY scrollTop={scrollTop}
onScroll={(e) => onScroll(e)}
onScrollToUpper={() => console.warn(1)}
scrollWithAnimation
trapScroll={false}
>
{props.children}
</ScrollView>
)
})
export default ContainerFit
\ No newline at end of file
### 滚动容器
#### 入参
| 参数 | 描述 |
| --- | --- |
|ref| ref实例),非必填|
#### 父组件设置滚动具体位置方案
``` jsx
// 滚动 600px
containerRef.current?.scrollFixTop(600)
```
#### 使用
``` jsx
import ContainerScroll from '@/components/_base/ContainerScroll/ContainerScroll'
const containerRef = createRef()
// ref实例
<ContainerFit ref={containerRef}>
</ContainerFit>
```
\ No newline at end of file
.scroll-container {
position: relative;
top: 0;
right: 0;
left: 0;
width: 100vw;
height: 100vh;
min-height: 100vh;
}
\ No newline at end of file
import { View } from '@tarojs/components' import { View } from '@tarojs/components'
import classnames from 'classnames'
import React, { useState } from 'react' import React, { useState } from 'react'
import Overlay from '../Overlay/Overlay' import Overlay from '../Overlay/Overlay'
import styles from './modal.module.less' import styles from './modal.module.less'
import classnames from 'classnames'
export default function Modal(props) { export default function Modal(props) {
const { closePostion = 'top-right', onClose = () => { }, top = '40%', hideCloseButton = false, closeOnClickOverlay = false } = props const { closePostion = 'top-right', onClose = () => { }, top = '40%', hideCloseButton = false, closeOnClickOverlay = false, closePos = { right: 0, top: -30 } } = props
const closeBtnClass = classnames(styles.closeButton, { const closeBtnClass = classnames(styles.closeButton, {
[`${styles.closeButton_bottom}`]: closePostion === 'bottom', [`${styles.closeButton_bottom}`]: closePostion === 'bottom',
...@@ -32,10 +31,10 @@ export default function Modal(props) { ...@@ -32,10 +31,10 @@ export default function Modal(props) {
return ( return (
<View className={modalClass}> <View className={modalClass}>
<Overlay onTap={onClickOverlay} /> <Overlay onClick={() => onClickOverlay()} />
<View className={`${styles.content} animate-zoom-in`} style={{ top }}> <View className={`${styles.content} animate-zoom-in`} style={{ top }}>
{!hideCloseButton && <View className={closeBtnClass} onTap={onCloseModal} />}
{props.children} {props.children}
{!hideCloseButton && <View className={closeBtnClass} onClick={() => onClose()} style={{ top: closePos.top / 100 + 'rem', right: closePos.right / 100 + 'rem' }} />}
</View> </View>
</View> </View>
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
position: absolute; position: absolute;
left: 50%; left: 50%;
top: 50%; top: 50%;
transform: translate(-50%, -50%) scale(0); transform: translate(-50%, -50%);
z-index: 22; z-index: 22;
} }
......
.overlay { .overlay {
.cover(); .cover();
z-index: 21; z-index: 21;
opacity: 0;
background-color: rgba(0, 0, 0, 0.7); background-color: rgba(0, 0, 0, 0.7);
} }
.canvas {
width:100%;
height:100vh;
flex: 1;
display: flex;
flex-direction: column;
background: #f5cccd;
}
.test-btn-box {
position: absolute;
top: 320rpx;
background-color: #000;
}
\ No newline at end of file
<view class="farmer-container-plants canvas">
<view class="page-game">
<canvas id="canvas" type="2d" class="canvas" disable-scroll="true" <canvas id="canvas" type="2d" class="canvas" disable-scroll="true"
onTouchStart="onMouseEvent" onTouchStart="onMouseEvent"
onTouchMove="onMouseEvent" onTouchMove="onMouseEvent"
...@@ -8,5 +6,3 @@ ...@@ -8,5 +6,3 @@
onReady="onCanvasReady" onReady="onCanvasReady"
></canvas> ></canvas>
</view> </view>
\ No newline at end of file
<task-modal />
\ No newline at end of file
import { Main } from './output';
/**
* 直接取,app
*/
const app = getApp()
Component({
isUpdateFlag: true,
data: {},
props: {
data: {}
},
didMount(query) {},
didUpdate(preProps,preData) {
// 实时监听数据更新
const { updateFlag, data } = this.props
if(updateFlag !== 1 && updateFlag !== preProps.updateFlag) {
this.postMessage(data.type, data)
}
},
didUnMount() {
// 页面被关闭
this.main && this.main.destroy();
},
methods: {
onCanvasReady() {
console.log("进canvas了")
// 创建Main,授权登录完后才创建
const self = this;
my.createCanvas({
id: 'canvas',
success: (ccc) => {
const dpr = my.getSystemInfoSync().pixelRatio
const windowWidth = my.getSystemInfoSync().windowWidth;
const windowHeight = my.getSystemInfoSync().windowHeight;
ccc.width = windowWidth * dpr + dpr;//重新修改会有误差
ccc.height = windowHeight * dpr + dpr;
// console.log(Main)
console.log(ccc.width, ccc.height)
if (!this.main) {
try {
this.main = new Main(ccc)
//添加事件,为了和main里的通信
this.main.addGlobalEvent("onMessage", self.onMessage, self)
} catch (err) {
console.error(err)
}
}
}
})
},
//和main通信的方法,接收main的信息
onMessage(e) {
//接口名字
let netName = e.data.netName;
//接口参数
let parameter = e.data.parameter;
if(netName.indexOf("mine") == 0) {
// 和游戏页面通信
this.props.onMessage({
type: 'message',
target: {
id: this.props.id
},
data: {
netName,
parameter
}
})
}else {
this.postMessage(netName, { success: false })
}
},
postMessage(netName, data) {
this.main && this.main.dispatchGlobalEvent({ type: netName }, data)//以防复用事件event串了,就用这种方式
},
//鼠标事件
onMouseEvent(e) {
if (this.main) this.main.stage.onMouseEvent(e)
},
}
});
This source diff could not be displayed because it is too large. You can view the blob instead.
/**
* 通用toast
* @param {string} content toast内容
* @param {number} duration 显示时长
*/
export const commonToast = (content, duration = 3000) => {
my.showToast({
content: content || '换个姿势再试一次~',
duration
});
};
/**
* 获取授权用户信息
* @returns {object} 用户登录信息
*/
export const getAuthUserInfo = () => {
return new Promise((resolve, reject) => {
my.authorize({
scopes: 'scope.userInfo',
success: res => {
my.getAuthUserInfo({
success: userInfo => { resolve(userInfo); }
});
},
fail: err => {
reject(err);
}
});
});
};
/**
* 获取系统信息
* @returns {object} 小程序系统信息
*/
export const getSystemInfo = () => {
return new Promise((resolve, reject) => {
my.getSystemInfo({
success: res => { resolve(res); },
fail: err => { reject(err); }
});
});
};
/**
* 执行关注店铺
* @param {number} sellerId 店铺归属的卖家Id
* @returns {boolean} 关注状态
*/
export const favorShop = sellerId => {
return new Promise((resolve, reject) => {
my.tb.favorShop({
id: sellerId,
success: res => {
console.log(res, '关注店铺');
resolve(res);
},
fail: err => {
console.log(err, '关注店铺失败');
// error code 11 为用户取消操作不认作失败场景
// if (err.error !== 11) {
reject(err);
// }
}
});
})
};
/**
* 判断是否关注店铺
* @param {number} sellerId 店铺归属的卖家Id
* @returns {boolean} 关注状态
*/
export const checkShopFavoredStatus = sellerId => {
return new Promise((resolve, reject) => {
my.tb.checkShopFavoredStatus({
id: sellerId,
success: res => {
resolve(res.isFavor);
},
fail: err => {
reject(err);
}
});
});
};
/**
* 跳转到外部链接
* @param {string} url 跳转链接
*/
export const navigateToOutside = url => my.call("navigateToOutside", { url });
/**
* 跳转到内部链接(新开窗口)
* @param {string} url 跳转链接
*/
export const navigateTo = url => my.navigateTo({ url });
/**
* 跳转到内部链接(不新开窗口)
* @param {string} url 跳转链接
*/
export const redirectTo = url => my.redirectTo({ url });
/**
* 获取服务器时间
* @returns {number} 服务器时间戳
*/
export const getServerTime = () => {
return new Promise((resolve, reject) => {
my.getServerTime({
success: (res) => {
resolve(res.time);
},
fail: err => {
reject(err);
}
});
});
};
/**
* 收藏商品
* @param {number} 商品id
* @returns {object} 收藏结果
*/
export const collectGoods = id => {
return new Promise((resolve, reject) => {
my.tb.collectGoods({
id,
success: res => {
resolve(res);
},
fail: err => {
if (err.errorMessage === '该商品已收藏') {
// 已收藏没有给到errorCode,只能通过message判断
// 返回收藏成功,并且给到标记代表是已收藏的
resolve({ success: true, hasCollected: true, errorMessage: err.errorMessage });
} else if (err.error !== 11) {
// error code 11 为用户取消操作不认作失败场景
reject(err);
}
}
});
});
};
/**
* 查询商品收藏状态
* @param {number} 商品id
* @returns {boolean} 商品收场状态
*/
export const checkGoodsCollectedStatus = id => {
return new Promise((resolve) => {
my.tb.checkGoodsCollectedStatus({
id,
success: res => {
resolve(res.isCollect);
}
});
});
};
/**
* 时间格式化
* @param date 接收可以被new Date()方法转换的内容
* @param format 字符串,需要的格式例如:'yyyy-MM-dd hh:mm:ss'
* @returns {String}
*/
export const dateFormatter = (date, format = 'yyyy-MM-dd') => {
if (!date) return '-';
date = new Date(typeof date === 'string' && isNaN(date) ? date.replace(/-/g, '/') : date);
let o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds(),
'q+': Math.floor((date.getMonth() + 3) / 3),
'S': date.getMilliseconds()
};
if (/(y+)/.test(format)) {
format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
}
for (let k in o) {
if (new RegExp('(' + k + ')').test(format)) {
format = format.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
}
}
return format;
};
/**
* 创建音频类
*/
class AudioModel {
constructor(opts = {}) {
const { src } = opts;
if (!src) {
console.error('请输入音频链接');
return false;
}
try {
const innerAudioContext = my.createInnerAudioContext();
Object.keys(opts).forEach(item => {
innerAudioContext[item] = opts[item];
});
return innerAudioContext;
} catch(e) {
console.error('当前版本不支持音频');
}
}
};
/**
* 创建音频实例
* @param {object} opts 配置对象 例 { src: '音频链接', autoplay: false }
*/
export const createAudio = opts => {
const audioInstance = new AudioModel(opts)
return audioInstance;
};
//暂时只有一个音乐,临时处理
let bgMusic
export const playBgMusic = (isOn,src) => {//待测试
if (isOn) {
if (!bgMusic) {
bgMusic = createAudio({
src: src||"https://yun.duiba.com.cn/db_games/activity/babycare_xiaoxiao/bgm_babycare.mp3",
autoplay: true,
loop: true//暂时无效?
});
//监听结束,因为loop无效
bgMusic.onEnded(() => {
// bgMusic.seek(0);
bgMusic.play();
})
console.log(src)
} else {
bgMusic.play();
}
} else {
if (bgMusic) {
// bgMusic.pause();
bgMusic.stop();
// bgMusic.seek(0);
}
}
}
/**
* 版本比较
* @param {string} cur 当前版本
* @param {string} compare 比较版本
*/
export const versionCompare = (cur, compare) => {
const _cur = cur.split('.');
const _compare = compare.split('.');
if (+_cur[0] < +_compare[0]) {
return false;
} else if ( +_cur[0] === +_compare[0] && +_cur[1] < +_compare[1] ) {
return false;
} else if (+_cur[0] === +_compare[0] && +_cur[1] === +_compare[1] && +_cur[2] < +_compare[2]) {
return false;
}
return true;
}
//暂时只有一个音乐,临时处理
// let bgMusic;
// let gameMusic;
let bgs = {
}
export const playBgMusic1 = (isOn, src) => {//待测试
for (let k in bgs) {
if (bgs[k].isOn) {
bgs[k].stop();
}
// bgs[k].isOn = false; /// TODO 自己记一个开关,因为淘宝的玩意stop之后虽然听不到,但还是在播放,会触发loop播放和onEnded事件
}
const key = src;
if (isOn) {
if (!bgs[key]) {
bgs[key] = createAudio({
src: src,
autoplay: true,
loop: false /// TODO 暂时无效?2020.08.12 目前有效,但stop后还会跑数据,导致到音频结束到时间又自动播放
});
bgs[key].onPlay(() => {
bgs[key].isOn = true;
});
bgs[key].onStop(() => {
bgs[key].isOn = false;
});
/// TODO监听结束,因为loop无效 2020.08.12 loop有效,这个操作是为了解决stop之后还会跑音频数据的问题
bgs[key].onEnded(() => {
if (bgs[key].isOn) { // 判断自己记录的开关
bgs[key].play();
// bgs[key].isOn = true;
}
});
bgs[key].play(); // 安卓有时候autoplay好像莫得用
// console.log(src)
// bgs[key].isOn = true; /// TODO 自己记一个开关,因为淘宝的玩意stop之后虽然听不到,但还是在播放,会触发loop播放和onEnded事件
} else {
bgs[key].play();
// bgs[key].isOn = true; /// TODO 自己记一个开关,因为淘宝的玩意stop之后虽然听不到,但还是在播放,会触发loop播放和onEnded事件
}
} else {
if (bgs[key]) {
// bgs[key].pause(); /// pause 垃圾,不能用
bgs[key].stop();
// bgs[key].isOn = false; /// TODO 自己记一个开关,因为淘宝的玩意stop之后虽然听不到,但还是在播放,会触发loop播放和onEnded事件
}
}
}
// TODO: 确认天猫是否也是9.3.0以下有问题
export const checkSystem = async ()=> {
const { version, app } = await getSystemInfo();
console.log('version', version);
return versionCompare(version, '9.3.0');
}
/**
* 获取淘宝用户收货地址
*/
export const getUserAddress = async (opts = {}) => {
let defaults = {
addAddress: 'show',
searchAddress: 'hide',
locateAddress: 'hide',
joinTag: '' // 分割收货地址符号(淘宝商家有特定发货需求)
};
const isValidVersion = await checkSystem();
if(!isValidVersion) {
commonToast('当前版本不支持选择收货地址,请升级到最新版本');
return;
}
let _opts = Object.assign({}, defaults, opts);
return new Promise((resolve, reject) => {
my.authorize({
scopes: 'scope.addressList',
success: () => {
const { addAddress, searchAddress, locateAddress, joinTag } = _opts;
if (!my.tb.chooseAddress) {
reject({ support: false, errorMessage: '当前版本不支持选择收货地址,请升级到最新版本' });
return false;
}
my.tb.chooseAddress({
addAddress,
searchAddress,
locateAddress
}, res => {
const { error, provinceName, countyName, cityName, streetName, detailInfo, name, telNumber } = res;
// 如果有error为用户取消操作不做操作
if (error) {
resolve(false);
} else {
resolve({
...res,
duibaAddress: {
name,
phone: telNumber,
address: [provinceName, cityName, countyName, streetName, detailInfo].join(joinTag)
}
});
}
}, res => {
reject(res);
})
},
});
});
};
/**
* 打开详情页
* @param {string} itemId
*/
export const openDetail = (itemId) => {
return new Promise((resolve, reject) => {
my.tb.openDetail({
itemId,
success: (res) => {
resolve(res)
},
fail: (res) => {
reject(res)
},
});
})
}
/**
* 自定义埋点的方法
* @param {string}} logkey
*/
export const reportAnalytics = (logkey) => {
my.reportAnalytics('click', {
// 标志某一类事件,方便数据分析进行分组
'logkey': logkey
})
}
export const cloudRequest = async (_cloudName, params, handle, loading = false) => {
const { cloud } = getApp();
loading && my.showLoading()
return new Promise((resolve, reject) => {
cloud.function.invoke(_cloudName, params, handle).then(res => {
console.log(`调用${handle}接口返回结果`, res)
loading && my.hideLoading()
if (res && res.success) {
resolve(res);
} else {
reject(res);
}
return res;
}).catch(() => {
loading && my.hideLoading()
reject();
});
});
}
export const checkMember = async () => {
// @ts-ignore
const memberPlugin = requirePlugin("cemMember");
return new Promise((resolve,reject) => {
memberPlugin.checkMember({
// sellerId,
success: (res) => {
let isMember = !!res.data.isMember && res.data.isMember === 'true'
resolve(isMember)
},
fail: (res) => {
reject(res)
}
})
})
}
\ No newline at end of file
{
"component": true
}
\ No newline at end of file
import * as fyge from "fyge" import * as fyge from "@/components/_miniprogram/canvasComp/node_modules/fyge"
import lottieData from '/lottie/index.js' import lottieData from '/lottie/index.js'
Component({ Component({
...@@ -26,6 +26,7 @@ Component({ ...@@ -26,6 +26,7 @@ Component({
my.createCanvas({ my.createCanvas({
id: 'canvas', id: 'canvas',
success: (ccc) => { success: (ccc) => {
console.warn(JSON.stringify(ccc))
this.canvas = ccc; this.canvas = ccc;
const dpr = my.getSystemInfoSync().pixelRatio const dpr = my.getSystemInfoSync().pixelRatio
//显示尺寸 //显示尺寸
...@@ -37,11 +38,13 @@ Component({ ...@@ -37,11 +38,13 @@ Component({
//兼容小程序 //兼容小程序
fyge.initedByCanvas(ccc) fyge.initedByCanvas(ccc)
var stage = this.stage = new fyge.Stage(ccc, 750, 1624, windowWidth, windowHeight,1); var stage = this.stage = new fyge.Stage(ccc, 750, 1624, windowWidth, windowHeight);
//加载 //加载
console.warn(lottieData[type]) console.warn(lottieData[type])
var l = stage.addChild(new fyge.Lottie(lottieData[type])) var l = stage.addChild(new fyge.Lottie(lottieData[type]))
l.play() l.play(1, () => {
console.warn('动画播放结束')
})
// fyge.GlobalLoader.loadJson((s, json) => { // fyge.GlobalLoader.loadJson((s, json) => {
// console.warn(lottieData) // console.warn(lottieData)
// var l = stage.addChild(new fyge.Lottie(lottieData)) // var l = stage.addChild(new fyge.Lottie(lottieData))
......
<member-shop-center expend="{{true}}" onClose="onMemberModalClose" onAuthFail="onAuthFail" onAuthSuccess="onAuthSuccess"/> <member-shop-center expend="{{expend}}" sellerId="{{sellerId}}" onClose="onMemberModalClose" onAuthFail="onAuthFail" onAuthSuccess="onAuthSuccess"/>
Component({ Component({
data: { data: {},
visible: true props: { expend: false, sellerId: '' },
}, didMount(){},
methods: { methods: {
onMemberModalClose() { onMemberModalClose() {
// console.log('this.props :>> ', this.props); // console.log('this.props :>> ', this.props);
......
.dialog-container {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 20;
}
.dialog-modal__shade {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.75);
}
.dialog-modal__container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.dialog-modal__container-bottom {
animation: bottom-up 1s linear forwards;
}
.dialog-modal__close {
position: absolute;
right: 20rpx;
top: -80rpx;
height: 48rpx;
width: 48rpx;
background: url('//yun.dui88.com/taobaomini/clientCTest/modal_close_btn.png') center center no-repeat;
background-size: 48rpx 48rpx;
}
@keyframes bottom-up {
from {
transform: translate(0,100vh);
}
to {
transform: translate(0,0);
}
}
.task-modal-container {
width: 100%;
height: 700rpx;
position: absolute;
bottom: 0;
left: 0;
background-color: #ffffff;
}
.task-modal-content__title {
text-align: center;
margin: 30rpx 0;
}
.task-modal-content__list {
width: 100%;
height: 600rpx;
padding: 0 16rpx;
}
.task-modal-content__scroll {
width: 100%;
height: 100%;
}
.task-modal-content-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
padding: 16rpx;
border: 1rpx solid #eee;
}
.task-modal-content-item__left {
width: 120rpx;
height: 120rpx;
display: flex;
justify-content: center;
align-items: center;
background-color: #eee;
}
.task-modal-content-item__left image {
width: 80rpx;
}
.task-modal-content-item__left-label {
flex: 1;
flex-direction: column;
align-items: center;
margin: 0 16rpx;
}
.task-modal-content-item__left-title {
}
.task-modal-content-item__left-reward {
margin-top: 10rpx;
}
.task-modal-content__list-item__right {
display: flex;
justify-content: center;
align-items: center;
width: 150rpx;
height: 60rpx;
background-color: #eee;
border-radius: 30rpx;
}
.task-item__status-1 {
}
.task-item__status-2 {
}
.task-item__status-3 {
}
\ No newline at end of file
<view class="dialog-container">
<view class="dialog-modal__shade modal-animate-fade-in" />
<view class="dialog-modal__container {{ 'dialog-modal__container-bottom'}} ">
<view class="task-modal-container">
<view class="dialog-modal__close" onTap="onModalClose" />
<view class="task-modal-content">
<view class="task-modal-content__title">任务模块</view>
<view class="task-modal-content__list">
<scroll-view scroll-y="{{true}}" class="task-modal-content__scroll">
<view class="task-modal-content-item" a:for="{{taskList}}">
<view class="task-modal-content-item__left">
<image src="{{taskIcon[item.taskType]}}" mode="widthFix" />
</view>
<view class="task-modal-content-item__left-label">
<view class="task-modal-content-item__left-title">{{item.title}}</view>
<view class="task-modal-content-item__left-reward">抽盒次数+{{item.rewards}}</view>
</view>
<view class="task-modal-content__list-item__right {{'task-item__status-'+item.status}}" data-item="{{item}}" onTap="handleTapItem">
{{
item.status == 1 ? doTaskTxt[item.taskType] : commonTaskTxt[taskType]
}}
</view>
</view>
</scroll-view>
</view>
</view>
</view>
</view>
</view>
<member-shop
a:if="{{memberShopVisible}}"
expend="{{memberShopVisible}}"
onSuccess="onSuccess"
onFail="closeCommonModal"
onClose="closeCommonModal" />
\ No newline at end of file
import { cloudRequest, throttleHandle, commonToast, favorShop, navigateTo, navigateToOutside, showSharePanel } from './utils';
const app = getApp();
const taskConfig = {
taskStatus: { // 任务状态
1: '//yun.duiba.com.cn/baicaoweiFarmer/task_btn_01.png',
2: '//yun.duiba.com.cn/baicaoweiFarmer/task_btn_02.png',
3: '//yun.duiba.com.cn/baicaoweiFarmer/task_btn_03.png'
},
taskIcon: {// 任务图标
follow: '//yun.duiba.com.cn/baicaoweiFarmer/follow.png',
member: '//yun.duiba.com.cn/baicaoweiFarmer/memberStar1.png',
invites: '//yun.duiba.com.cn/baicaoweiFarmer/invites.png',
jumpLink: '//yun.duiba.com.cn/baicaoweiFarmer/browseGoodsLink.png',
browseGoods: '//yun.duiba.com.cn/baicaoweiFarmer/browseGoods.png',
orderGoods: '//yun.duiba.com.cn/baicaoweiFarmer/orderGoods.png',
collectGoods: '//yun.duiba.com.cn/baicaoweiFarmer/orderGoods.png',
sign: '//yun.duiba.com.cn/baicaoweiFarmer/orderGoods.png',
},
commonTaskTxt: {
1: '去完成',
2: '待领取',
3: '已完成'
},
doTaskTxt: {
follow: '立即关注',
invites: '去邀请',
member: '1秒入会',
sign: '立即签到',
jumpLink: '去完成',
browseGoods: '去完成',
orderGoods: '去完成',
collectGoods: '去完成',
},
browseType: 1, // 1 跳转即算完成 2 跳转浏览15s 才算完成
browseTime: 15, // 浏览时间
}
const cloudName = 'luolai'
const GO_TO_INVITE = 2 // 数据埋点
const SELLER_ID = 2374579403 // 商家sellerId
Component({
mixins: [],
data: {
taskStatus: taskConfig.taskStatus,
taskIcon: taskConfig.taskIcon,
commonTaskTxt: taskConfig.commonTaskTxt,
doTaskTxt: taskConfig.doTaskTxt,
taskList: [
{
taskType: 'follow',
title: '关注店铺',
taskRateType: 1,
times: 1,
completeTimes: 0,
status: 1,
rewards: 1
},
{
taskType: 'member',
title: '加入会员',
taskRateType: 1,
times: 1,
completeTimes: 0,
status: 1,
rewards: 1
},
{
taskType: 'invites',
title: '邀请好友入会',
taskRateType: 1,
times: 3,
completeTimes: 0,
status: 1,
rewards: 1
},
{
taskType: 'jumpLink',
title: '浏览店铺15s',
taskRateType: 2,
times: 1,
completeTimes: 0,
status: 1,
rewards: 1
},
{
taskType: 'browseGoods',
title: '浏览商品',
taskRateType: 2,
times: 1,
completeTimes: 0,
status: 1,
rewards: 1
},
{
taskType: 'orderGoods',
title: '下单任务',
taskRateType: 2,
times: 1,
completeTimes: 0,
status: 1,
rewards: 1
},
{
taskType: 'collectGoods',
title: '收藏商品',
taskRateType: 2,
times: 1,
todayCompleteTimes: 0,
status: 1,
rewards: 1
},
{
taskType: 'sign',
title: '每日签到',
taskRateType: 2,
times: 1,
todayCompleteTimes: 0,
status: 1,
rewards: 1
}
],
memberShopVisible: false,
currentTaskType: null,
preBrowseTime: null
},
props: {
taskFlag: 1,
dataName: '',
onModalClose: () => {},
onUpdate: () => {}
},
didMount() {
this.getTaskList()
},
didUpdate(preProps,preData) {
const { taskFlag } = this.props
const { isFlashTask = false } = app
const { browseTime } = taskConfig
const { preBrowseTime, currentTaskType } = this.data
if(preProps.taskFlag !== taskFlag && isFlashTask) {
// console.log((Date.now() - preBrowseTime)/1000)
if(preBrowseTime && ((Date.now() - preBrowseTime) / 1000) >= browseTime) {
this.doCompleteTask(currentTaskType,true)
}
else {
if(currentTaskType === 'jumpLink') {
this.setData({ preBrowseTime: null, currentTaskType: null})
}
this.getTaskList(true)
}
}
},
didUnmount() {
},
methods: {
// 关闭弹窗
onModalClose() {
const { onModalClose, dataName } = this.props;
onModalClose && onModalClose(dataName);
},
// 按钮
handleTapItem: throttleHandle(async function(e) {
const { activityId } = app
const { status, taskType, url, rewards, itemIds, keepTime } = e.target.dataset.item
if(status == 3) return;
if(status == 2) {
const { success, data } = await cloudRequest(cloudName,{ activityId, taskType }, 'receiveTaskRewards').catch(res => {
commonToast(res && res.message);
}) || {};
if (success && data) {
const { rewards, receiveTimes } = data
let message = taskType == 'invites' ?
`成功邀请${receiveTimes}名好友,抽盒次数+${rewards}`:`抽盒次数+${rewards}`
commonToast(message)
this.getTaskList()
this.props.onUpdate()
}
}
else {
this.goToComplete(taskType,url,itemIds,keepTime)
}
},2000),
// 做任务
async goToComplete(taskType,url,itemIds,keepTime) {
const { activityId } = app
const { browseType } = taskConfig
switch(taskType) {
case 'member':
// 去加入会员
this.setData({ memberShopVisible: true })
break;
case 'follow':
let isFollow = await favorShop(SELLER_ID)
if(isFollow) {
this.doCompleteTask(taskType)
}
break;
case 'invites':
// 邀请好友
showSharePanel();
await cloudRequest(cloudName,{ activityId, type: GO_TO_INVITE }, 'addStat')
break;
case 'jumpLink':
// 跳转任务
navigateToOutside(url || 'https://www.feizhu.com')
if(browseType === 1) {
this.doCompleteTask(taskType)
}
else {
app.isFlashTask = true
this.setData({ preBrowseTime: Date.now(), currentTaskType: taskType})
}
break;
case 'browseGoods':
// 跳转浏览页面
app.isFlashTask = true
navigateTo(`/pages/browseGoods/browseGoods?itemIds=${itemIds}&keepTime=${keepTime}`)
break;
case 'orderGoods':
// 跳转下单页面
app.isFlashTask = true
navigateTo(`/pages/orderGoods/orderGoods?itemIds=${itemIds}`)
break;
case 'collectGoods':
// 跳转收藏页面
app.isFlashTask = true
navigateTo(`/pages/collectGoods/collectGoods?itemIds=${itemIds}`)
break;
case 'sign':
// 签到
this.doCompleteTask(taskType)
break;
}
},
// 完成任务
async doCompleteTask(taskType, flag) {
const { activityId } = app
let { success, data } = await cloudRequest(cloudName,{ activityId, taskType }, 'doCompleteTask') || {}
if(success) {
this.getTaskList()
flag && this.setData({ preBrowseTime: null })
}
},
// 获取任务列表
async getTaskList(flag) {
const { activityId } = app
const { success, data } = await cloudRequest(cloudName,{ activityId }, 'getTaskList').catch(res => {
commonToast(res && res.message);
}) || {};
if (success && data) {
const { list } = data
app.isFlashTask = flag ? false : true
// this.setData({ taskList: list })
}
},
// 关闭入会组件
closeCommonModal() {
this.setData({ memberShopVisible: false })
},
// 入会成功
onSuccess() {
this.getTaskList()
this.setData({ memberShopVisible: false })
}
},
});
{
"component": true,
"usingComponents": {
"member-shop":"/components/_miniprogram/memberModal/memberModal"
}
}
\ No newline at end of file
/**
* 获取任务数据
* @param {*} _cloudName
* @param {*} params
* @param {*} loading
*/
export const cloudRequest = async (_cloudName, params, handle, loading = false) => {
const { cloud } = getApp();
loading && my.showLoading()
return new Promise((resolve, reject) => {
cloud.function.invoke(_cloudName, params, handle).then(res => {
console.log(`调用${handle}接口返回结果`, res)
loading && my.hideLoading()
if (res && res.success) {
resolve(res);
} else {
reject(res);
}
return res;
}).catch(() => {
loading && my.hideLoading()
reject();
});
});
}
/**
* 函数节流,普通防连点
* @param {fun} fun 函数
* @param {delay} delay 时间
*/
export const throttleHandle = (fun, delay = 1000) => {
let last, deferTimer;
return function () {
let now = +new Date();
if (last && now < last + delay) {
clearTimeout(deferTimer);
deferTimer = setTimeout(() => {
last = now;
}, delay);
} else {
last = now;
fun.apply(this, arguments);
}
};
}
/**
* toast
* @param {*} content
* @param {*} duration
* @param {*} successCb
*/
export const commonToast = (content, duration, successCb) => {
my.showToast({
content: content || '换个姿势再试一次~',
duration: duration || 3000,
success: successCb
})
};
/**
* 执行关注店铺
* @param {number} sellerId 店铺归属的卖家Id
* @returns {boolean} 关注状态
*/
export const favorShop = async (sellerId) => {
if (!sellerId) {
return false;
}
return new Promise((resolve, reject) => {
// @ts-ignore
my.tb.favorShop({
id: +sellerId,
success: res => {
if(res.error === 11) resolve(true)
resolve(res)
},
fail: err => {
reject(err)
}
})
})
};
/**
* 跳转到外部链接
* @param {string} url 跳转链接
*/
export const navigateToOutside = url => {
if (!url) {
console.error('navigateToOutside: 请传入url');
return false;
}
my.call('navigateToOutside',{ url });
};
/**
* 跳转到内部链接(新开窗口)
* @param {string} url 跳转链接
*/
export const navigateTo = url => {
if (!url) {
console.error('navigateTo: 请传入url');
return false;
}
my.navigateTo({ url });
};
/**
* 调起分享面板
*/
export const showSharePanel = async () => {
my.showSharePanel();
}
\ No newline at end of file
# 垂直弹幕参数
| 参数 | 说明 | 类型 | 默认值 |
| :------------------------- | :------------- | :------ | :----- |
| dataList | 弹幕列表 | String[] | - |
| barrageNum | 弹幕循环数量 | Number | 30 |
| quantity | 弹幕同时显示数量 | Number | 2 |
## 垂直弹幕使用步骤
1. 引入VerticalBarrage
2. 直接进入文件的<View className={styles.text}>{item}</View>处,定制自己需要的样式
# 横向弹幕参数
| 参数 | 说明 | 类型 | 默认值 |
| :------------------------- | :------------- | :------ | :----- |
| dataList | 弹幕列表 | String[] | - |
| trackCount | 弹幕轨道数量 | Number | 1 |
## 横向弹幕使用步骤
1. 引入Barrage
2. 直接进入文件的<View className={styles.text}>{item}</View>处,定制自己需要的样式
\ No newline at end of file
import React, { useState, useEffect, useCallback, useRef } from 'react'
import { View, Text } from '@tarojs/components'
import Taro from '@tarojs/taro'
import styles from './Barrage.module.less'
const Barrage = props => {
const {
dataList = [
'1撒撒打算打算打算的',
'2撒撒打算打算打算的',
'3撒撒打算打算打算的',
'4撒撒打算打算打算的',
'5撒撒打算打算打算的',
'3撒撒打算打算打算的',
'3撒撒打算打算打算的',
'3撒撒打算打算打算的',
'3撒撒打算打算打算的',
'3撒撒打算打算打算的',
'3撒撒打算打算打算的',
'3撒撒打算打算打算的'
],
interval = 5000,
width = 500,
trackCount = 1
} = props
const [ barrageList, setBarrageList ] = useState([])
const [ animationData, setAnimationData ] = useState(null)
const timer = useRef(null)
const nowIndex = useRef(0)
const createData = () => {
const Tracks = Array.from({ length: trackCount }, (v, k) => (k + 1) * 40 - 40)
// TODO 多轨道弹幕
setBarrageList(
dataList.map((item, index) => ({
top: Tracks[index < Tracks.length ? index : index % trackCount],
context: item
}))
)
}
const callback = useCallback(() => {
timer.current = setInterval(() => {
setAnimationData(
Taro.createAnimation()
.translateX(nowIndex.current === 0 ? 0 : Taro.pxTransform(-width * nowIndex.current))
.step({
duration: nowIndex.current === 0 ? 0 : 1000
})
.export()
)
if (nowIndex.current < barrageList.length - 1) {
nowIndex.current = nowIndex.current + 1
} else {
nowIndex.current = 0
}
}, interval + 1000)
}, [ barrageList, width ])
useEffect(() => {
callback()
return () => clearInterval(timer.current)
}, [ callback ])
useEffect(() => {
createData()
return () => {}
}, [])
return (
<View id='barrage_wrap' style={`width:${width / 100}rem`} className={styles['barrage-wrap']}>
<View id='barrage_container' animation={animationData} className={styles['barrage-fly']}>
{barrageList.map((item, index) => {
return (
<View id={`barrage-${index}`} key={index} style={`width:${width / 100}rem`} className={styles['barrage-textFly']}>
{/* 弹幕内容 */}
<Text className={styles['barrage-text']}>{item.context}</Text>
</View>
)
})}
</View>
</View>
)
}
export default Barrage
.barrage-wrap {
height: 64px;
border: 1px solid red;
overflow: hidden;
}
.barrage-fly {
position: relative;
height: 100%;
z-index: 3;
display: flex;
}
.barrage-textFly {
flex-shrink: 0;
height: 64px;
overflow: hidden;
line-height: 64px;
color: #f9c797;
font-size: 32px;
padding: 0 10px;
box-sizing: border-box;
text-align: center;
}
.barrage-text {
white-space: nowrap;
}
@keyframes first {
from {
left: 100%;
}
to {
left: -100%;
}
}
import React, { useState, useMemo, useEffect, useRef, useCallback } from 'react'
import { View } from '@tarojs/components'
import Taro, { useReady } from '@tarojs/taro'
import styles from './VerticalBarrage.module.less'
const animation = Taro.createAnimation({
transformOrigin: 'top right',
duration: 850,
timeFunction: 'ease-in-out',
delay: 150
})
const VerticalBarrage = (props) => {
const { dataList, barrageNum = 30, quantity = 2 } = props
const [ height, setHeight ] = useState(0)
const [ animationData, setAnimationData ] = useState(null)
const nowIndex = useRef(0)
const timer = useRef(null)
useReady(() => {
const query = Taro.createSelectorQuery()
query.select('#barrage_0').boundingClientRect().exec((res) => {
console.log(res)
const h = res[0].height
setHeight(h)
})
})
const barrageList = useMemo(() => {
if (!dataList.length) return []
if (barrageNum && dataList.length < barrageNum) {
let reslut = [ ...dataList ]
while (reslut.length < barrageNum) {
reslut = [ ...reslut, ...dataList ]
}
return [ ...reslut.slice(0, barrageNum), ...reslut.slice(0, quantity) ]
} else {
return [ ...dataList.slice(0, barrageNum), ...dataList.slice(0, quantity) ]
}
}, [ dataList ])
const callback = useCallback(() => {
timer.current = setInterval(() => {
const data = animation.translateY(-nowIndex.current * height).step()
setAnimationData(data.export())
if (nowIndex.current < barrageList.length - quantity) {
nowIndex.current = nowIndex.current + 1
} else {
nowIndex.current = 0
}
}, 1000)
}, [ barrageList, height ])
useEffect(() => {
callback()
return () => clearInterval(timer.current)
}, [ callback ])
// 控制淡入淡出
const barrageStyle = (index) => {
if (quantity === 1) {
return `${(nowIndex.current === index && nowIndex.current !== 0) ? styles.in : ''} ${(nowIndex.current - 1 === index) ? styles.out : ''}`
} else {
return `${(nowIndex.current + quantity - 1 === index && nowIndex.current !== 0) ? styles.in : ''} ${nowIndex.current - 1 === index ? styles.out : ''}`
}
}
return (
<View className={styles['barrage-container']} style={{ height: height * quantity }}>
<View
className={styles['barrage-box']}
animation={animationData}
style={nowIndex.current === 0 && { transform: 'translateY(0)' }}
>
{
barrageList.map((item, i) => (
<View key={`barrage-${i}`} id={`barrage_${i}`} className={barrageStyle(i)}>
<View className={styles.text}>{item}</View>
</View>
))
}
</View>
</View>
)
}
export default VerticalBarrage
.barrage-container {
border: 1px solid red;
overflow: hidden;
}
.barrage-box {
z-index: 1;
.text {
padding: 10px 0;
}
}
.in {
animation: fadeIn 1.5s;
animation-fill-mode: forwards;
}
.out {
animation: fadeOut 1s;
animation-fill-mode: forwards;
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
\ No newline at end of file
import VerticalBarrage from './VerticalBarrage/VerticalBarrage'
import Barrage from './HorizontalBarrage/Barrage'
export {
VerticalBarrage,
Barrage
}
import React, { useState, useEffect } from "react";
import { View, Image, Text } from "@tarojs/components";
import LoadingBar,{LOAD_TYPE} from "../LoadingBar/LoadingBar";
import styles from './BrowserLoading.module.less'
import {BROWSE_COUNTDOWN_OBJ} from '@/config/browse.config'
const BrowserLoading = (props) => {
const {loadFlag,count} = props;
return (
<View className={styles["browse_loading"]}>
{loadFlag == BROWSE_COUNTDOWN_OBJ.NUMBER && (
<>
<View className={styles["count_img"]}>
<View className={styles["count_time"]}>{count}s</View>
</View>
</>
)}
{loadFlag == BROWSE_COUNTDOWN_OBJ.PICTURE && <LoadingBar type={LOAD_TYPE.TASK} />}
</View>
);
};
export default BrowserLoading;
.browse_loading {
width: 200px;
height: 200px;
position: fixed;
right: 20px;
top: 400px;
border: 1px solid cyan;
box-sizing: border-box;
}
.count_img {
width: 100%;
height: 100%;
border: 1px solid rgb(47, 131, 99);
box-sizing: border-box;
background-image: url("//yun.dui88.com/7f705da9-5f16-4cf1-a239-3903c5c23995.png");
.image-property(contain, center center);
}
.count_time {
width: 100px;
height: 60px;
line-height: 60px;
margin: 40px auto 0;
border: 1px solid cyan;
box-sizing: border-box;
font-size: 40px;
font-weight: 600;
color: #000;
text-align: center;
}
##### 是否展示浏览页中的倒计时
+ 入参
| 配置项 | 类型 | 描述 | 默认值 | 备注 |
| :-------- | :-----: | :--------: | :-----: | :---:|
| loadFlag | String | 倒计时类型"NUMBER","PICTURE" | 默认空 '' | NUMBER:数字倒计时 PICTURE:loading倒计时(默认15s,可在css中修改) |
| count | Number | 倒计时时间 | 15s | 默认倒计时15s(可改配置) |
+ 使用
```jsx
import BrowserLoading from '../BrowserLoading/BrowserLoading'
export const Index = () => {
return(
<BrowserLoading loadFlag={task.loadFlag} count={count} />
)
}
```
-------
###### loadingBar组件
+ 入参
| 配置项 | 类型 | 描述 | 默认值 | 备注 |
| :-------- | :-----: | :--------: | :-----: | :---:|
| bg | String | 背景loading | 'https://yun.duiba.com.cn/spark/assets/5a725b9e4ebf436a0a92c6317a5a4e47d87c8c1f.png' | / |
| bgTop | String | 上层loading | 'https://yun.duiba.com.cn/spark/assets/ccc405e28a5140d3aff9dff27b97fa5fb6bd7ff5.png' | / |
| type | String | "TASK","NORMAL" | 'NORMAL' | "TASK":自动loading倒计时 "NORMAL":静态的loading进度显示 |
| total | Number | loading总值 | / | type为"NORMAL"传入 |
| current | Number | 当前值 | 0 | type为"NORMAL"传入 |
+ 使用
```jsx
import LoadingBar,{LOAD_TYPE} from "../LoadingBar/LoadingBar";
export const Index = () => {
return(
<LoadingBar type={LOAD_TYPE.NORMAL} total={100} current={10} />
<LoadingBar type={LOAD_TYPE.TASK} />
)
}
```
\ No newline at end of file
import React from 'react'
import { View, Text } from '@tarojs/components'
import tbcc from 'tbcc-sdk-ts'
import './Countdown.less'
const { getServerTime } = tbcc.tb
export default class Countdown extends React.Component {
static defaultProps = {
targetTime: '', // 以时间来进行倒计时
startTime: '', // 开始时间
count: '', // 以秒数来进行倒计时
// targetTime 模式下生效
showText: false, // 显示时分秒
showDay: false, // 显示天数
symbol: ':', // 间隔符号
isClose: false, // 手动关闭倒计时
// 共用事件
onTick: () => { }, // 倒计时过程事件
onEnd: () => { } // 倒计时结束事件
};
constructor() {
super(...arguments)
this.state = {
day: '0',
hour: '00',
minute: '00',
second: '00',
countText: '0'
}
this.targetTimestamp = null
this.startTimestamp = null
this.timer = null
}
componentDidMount() {
const { targetTime, count } = this.props
if (targetTime) {
this.formatTargetTime(targetTime)
} else if (count) {
this.setCountdown(count)
}
}
UNSAFE_componentWillReceiveProps(nextProps) {
const { targetTime, count } = nextProps
if (targetTime && targetTime !== this.props.targetTime) {
this.formatTargetTime(targetTime)
} else if (count && count !== this.props.count) {
clearInterval(this.timer)
this.setCountdown(count)
}
}
componentWillUnmount() {
this.targetTimestamp = null
clearInterval(this.timer)
}
componentDidHide() {
this.targetTimestamp = null
clearInterval(this.timer)
}
componentDidShow() {
const { targetTime, count } = this.props
if (targetTime) {
this.formatTargetTime(targetTime)
} else if (count) {
this.setCountdown(count)
}
}
setCountdown(count) {
if (count <= 0) {
return
}
let second = count
this.setState({
countText: second
})
this.timer = setInterval(() => {
second--
this.setState({
countText: second
})
this.props.onTick && this.props.onTick(second)
if (second <= 0) {
clearInterval(this.timer)
this.onTimeEnd()
}
}, 1000)
}
// 处理日期格式
formatTargetTime(targetTime) {
if (!targetTime) {
return
}
// 避免iOS端上的日期格式有问题
const time = targetTime.replace(/-/g, '/')
this.targetTimestamp = new Date(time).getTime()
this.getRemainingSecond()
}
// 计算剩余时间秒数
async getRemainingSecond() {
if (!this.targetTimestamp) return
// 当前时间
const currentTimestamp = await getServerTime()
// 剩余时间
const remainingSecond = Math.floor((this.targetTimestamp - currentTimestamp) / 1000)
// 天 时 分 秒
let day = ''
let hour = ''
let minute = ''
let second = ''
if (remainingSecond > 0 && !this.props.isClose) {
day = this.formatNum(parseInt(remainingSecond / 86400))
hour = this.props.showDay
? this.formatNum(parseInt((remainingSecond % 86400) / 3600))
: this.formatNum(parseInt(remainingSecond / 3600))
minute = this.formatNum(parseInt((remainingSecond % 3600) / 60))
second = this.formatNum(parseInt((remainingSecond % 3600) % 60))
this.timeTick(remainingSecond)
} else {
day = '0'
hour = minute = second = '00'
this.onTimeEnd()
}
this.setTimeState(day, hour, minute, second)
}
// 设置时间
setTimeState(day, hour, minute, second) {
// 是否显示天时分秒文字
if (this.props.showText) {
hour += '时'
minute += '分'
second += '秒'
}
this.setState({
day,
hour,
minute,
second
})
}
// 格式数字
formatNum(num) {
return num > 9 ? `${num}` : `0${num}`
}
// 倒计时过程事件
timeTick(remainingSecond) {
this.props.onTick && this.props.onTick(remainingSecond)
setTimeout(() => {
this.getRemainingSecond()
}, 1000)
}
// 倒计时结束触发事件
onTimeEnd() {
this.targetTimestamp = null
this.props.onEnd && this.props.onEnd()
}
render() {
const { symbol, showDay, showText, className, targetTime } = this.props
const { day, hour, minute, second, countText } = this.state
// 末尾有空格
let countdownClass = 'countdown '
if (className) countdownClass += className
if (targetTime) {
return (
<View className={countdownClass}>
{showDay && day !== '00' && (
<Text>
<Text className='day'>{day}</Text>
<Text className='day-text'></Text>
</Text>
)}
<Text>
<Text className='hour'>{hour}</Text>
{!showText && <Text className='symbol'>{symbol}</Text>}
</Text>
<Text className='minute'>{minute}</Text>
{!showText && <Text className='symbol'>{symbol}</Text>}
<Text className='second'>{second}</Text>
</View>
)
}
return (
<View className={countdownClass}>
<Text className='second'>{countText}</Text>
</View>
)
}
}
.countdown {
display: inline-block;
box-sizing: border-box;
}
\ No newline at end of file
## 参数
| 参数 | 说明 | 类型 | 默认值 | 注意 |
| :------------------------- | :------------- | :------ | :----- | :------ |
| targetTime | 目标时间 | String | - | 优先级比count高 |
| count | 倒计秒数 | Number | - | - |
| 以下为使用targetTime 生效 |
| symbol | 间隔符号 | String | - | - |
| showDay | 是否显示天 | Boolean | false | 配合showText同时搭配 |
| showText | 是否显示时分秒 | Boolean | false | 打开自动无视symbol |
| isClose | 是否关闭倒计时 | Boolean | false | - |
## 事件
| 事件名称 | 说明 | 返回参数 |
| :------- | :--------------- | :--------- |
| onTick | 倒计时过程事件 | 剩余的秒数 |
| onEnd | 倒计时结束时触发 | - |
## 实例
```jsx
<CounDown targetTime="2021-10-01 09:00:00">
```
import Countdown from './CountDown'
export default Countdown
import React, { useState, useEffect, useRef } from 'react'
import { View, Image, ScrollView } from '@tarojs/components'
import classnames from 'classnames'
import tbccTs from 'tbcc-sdk-ts'
const { openDetail } = tbccTs.tb
import styles from './GoodsList.module.less'
function GoodsList(props) {
const {
goodsList = [],
task = {
itemId: '617724147979,617724563528,617300295119',
taskType: 'browseGoods',
image: {
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'
},
onOpenDetail,
onCompleteTask
} = props
const goToGoodsDetail = async(item) => {
const { taskType } = task
const { itemId } = item
if (taskType === 'browseGoods') {
onOpenDetail && onOpenDetail(itemId)
}
await openDetail(String(itemId))
}
// 收藏商品
const goToCollectGoods = async(item) => {
const { itemId, collected } = item
onCompleteTask && onCompleteTask(itemId,collected)
}
// 收藏按钮
const getCollectStyle = (collected) => {
return classnames(styles['item__price-collect'],{
[`${styles['item__price-no-collect']}`]: collected
})
}
return (
<ScrollView scrollY className={styles['container__scroll']}>
<View className={styles['container__content-list']}>
{
goodsList.map((item, i) => {
return (
<View className={styles['content-item']} key={'goods_'+i}>
<View className={styles['item__img']} onClick={() => goToGoodsDetail(item)}>
<Image mode='scaleToFill' src={item.image} />
</View>
<View className={styles['item__name']} style={{ color: task.color }}>{item.name}</View>
<View className={styles['item__price']}>
<View className={styles['item__price-num']}><text>¥</text>{item.price}</View>
{
task.taskType === 'collectGoods' &&
<View
onClick={() => goToCollectGoods(item)}
className={() => getCollectStyle(item.collected)}
>
<Image mode='scaleToFill' src={item.collected ? task.image.collect : task.image.no_collect} />
</View>
}
</View>
</View>
)
})
}
</View>
</ScrollView>
)
}
export default GoodsList
\ No newline at end of file
.goods-modal-container { .container__scroll {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
min-height: 100vh;
/* background: url('//yun.dui88.com/taobaomini/clientCTestgoods_bg@2x.png') no-repeat; */
background-size: 750px 1624px;
overflow: hidden;
z-index: 20;
}
.goods-modal-container__title {
width: 750px;
height: 120px;
margin: 96px 0 11px;
}
.goods-modal-container__banner {
width: 750px;
height: 400px;
margin-bottom: 30px;
background: #e1e1e1;
}
.goods-modal-container__content {
position: absolute;
top: 190px;
left: 0;
bottom: 0;
padding-top: 41px;
padding: 41px 25px 0;
width: 750px;
/* height: inherit; */
}
.goods-modal-container__scroll {
height: 100%; height: 100%;
} }
.goods-modal-container__content-list { .container__content-list {
width: 100%; width: 100%;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
} }
.goods-modal-container__content-item { .content-item {
width: 340px; width: 340px;
height: 510px; height: 510px;
background: #fff; background: #fff;
margin-bottom: 20px; margin-bottom: 20px;
} }
.goods-modal-container__content-item:nth-child(even) { .content-item:nth-child(even) {
margin-left: 20px; margin-left: 20px;
} }
.goods-modal-container__content-item__img { .item__img {
width: 340px; width: 340px;
height: 340px; height: 340px;
background: #e1e1e1; background: #e1e1e1;
} }
.goods-modal-container__content-item__img image { .item__img image {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.goods-modal-container__content-item__name { .item__name {
margin: 25px 0 31px 21px; margin: 25px 0 31px 21px;
width: 310px; width: 310px;
height: 60px; height: 60px;
...@@ -71,35 +37,35 @@ ...@@ -71,35 +37,35 @@
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.goods-modal-container__content-item__price { .item__price {
padding: 0 31px 0 24px; padding: 0 31px 0 24px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
.goods-modal-container__content-item__price-num { .item__price-num {
font-size: 32px; 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 { .item__price-num text {
font-size: 24px; font-size: 24px;
margin-right: 2px; margin-right: 2px;
} }
.goods-modal-container__content-item__price-collect { .item__price-collect {
width: 118px; width: 118px;
height: 31px; height: 31px;
background-size: 100% 100%; background-size: 100% 100%;
} }
.goods-modal-container__content-item__price-collect image { .item__price-collect image {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.goods-modal-container__content-item__price-no-collect { .item__price-no-collect {
width: 30px; width: 30px;
height: 30px; height: 30px;
} }
.goods-modal-container__content-item__price-no-collect image { .item__price-no-collect image {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
...@@ -111,3 +77,4 @@ ...@@ -111,3 +77,4 @@
right: 19px; right: 19px;
background: #eee; background: #eee;
} }
\ No newline at end of file
import React,{useState, useEffect} from 'react'
import {View,Image,Text} from '@tarojs/components'
import styles from './LoadingBar.module.less'
export const LOAD_TYPE = {
'TASK':"TASK",
'NORMAL':"NORMAL"
}
const LoadingBar = (props) => {
const {
bg='https://yun.duiba.com.cn/spark/assets/5a725b9e4ebf436a0a92c6317a5a4e47d87c8c1f.png',
bgTop='https://yun.duiba.com.cn/spark/assets/ccc405e28a5140d3aff9dff27b97fa5fb6bd7ff5.png',
type='',
total=0,
current=0
} = props;
const [percent,setPercent] = useState(0);
useEffect(()=>{
showCurrentLoading();
},[])
/**
* @description 显示当前进度(进度条上没有其他内容)
*/
const showCurrentLoading = () => {
if(type == LOAD_TYPE.NORMAL){
let cur = current >= total ? toal : current;
setPercent((cur / total * 100).toFixed(1))
}
}
return(
<View className={styles['load_container']} >
<View className={styles['load_content']} >
<View className={styles['load_bg']} style={{backgroundImage:`url(${bg})`}} ></View>
<View
className={`${styles['load_top']} ${type == LOAD_TYPE.TASK && 'loadAni'}`}
style={{
backgroundImage:`url(${bgTop})`,
clipPath:`${type==LOAD_TYPE.NORMAL && `inset(0% 0% 0% ${percent}%)`}`
}}
></View>
</View>
</View>
)
}
export default LoadingBar
\ No newline at end of file
.load_container{
.load_content{
width: 200px;
height: 80px;
margin: 0 auto;
overflow: hidden;
}
.load_bg,
.load_top{
width: 200px;
height: 60px;
position: absolute;
background-size: cover;
background-repeat: no-repeat;
}
.loadAni{
animation: move 15s linear forwards;
}
}
@keyframes move {
0%{
clip-path: inset(0% 0% 0% 0%);
}
99%{
opacity: 1;
}
100%{
clip-path: inset(0% 100% 0% 0%);
opacity: 0;
}
}
\ No newline at end of file
import React, { useState } from 'react'
import { View, Image } from '@tarojs/components'
import classnames from 'classnames'
import styles from './Progress.module.less'
import { PROGRESS_LIST } from '@/mock'
const PRO_ICON = {
icon: '//yun.duiba.com.cn/taobaomini/provideBeauty/total/progress_icon.png'
}
function Progress(props) {
const { data = {}, width = 692, type = 1, hasIcon = false, hasTxt = true } = props
const { allHotValue = 0, levleList = PROGRESS_LIST } = data
const maxLevel = levleList[levleList.length -1]
const getProItm = classnames(styles['progress-box-com'],{
[`${styles['progress-box-com-02']}`]: allHotValue === maxLevel
})
const tranHotValue = (value) => {
const _num = parseInt(value / 10000)
if(_num) return _num + '万'
return value
}
const getTextStyle = classnames(styles['progress-box__txt'],{
[`${styles['progress-box__txt-bottom']}`]: type === 2
})
const getDescStyle = (itm) => {
return classnames(styles['progress-box__txt-item'],{
[`${styles['progress-txt-active']}`]: itm <= allHotValue
})
}
return (
<View className={styles['progress-box']} style={{ width: width / 100 + 'rem'}}>
<View className={getProItm} style={{width: allHotValue >= maxLevel ? '100%' : (allHotValue / maxLevel) * 100 +'%'}}></View>
{hasIcon && <View className={styles['progress-box__icon']}>
{ levleList.map((itm,i) => {
return(
<Image className={styles['progress-box__icon-itm']}
style={{left: (itm / maxLevel) * width / 100 + 'rem'}} src={PRO_ICON['icon']} />
)
})}
</View>}
{hasTxt && <View className={getTextStyle}>
{ levleList.map((itm,i) => {
return(
<View className={getDescStyle(itm)}
style={{left: (itm / maxLevel) * width / 100 + 'rem'}}>{tranHotValue(itm)}</View>
)
})}
</View>}
</View>
)
}
export default Progress
\ No newline at end of file
.progress-box {
width: 692px;
height: 20px;
background: #371B15;
border-radius: 4px;
position: relative;
}
.progress-box-com {
height: 20px;
background: linear-gradient(270deg, #FFDE8B 0%, #FFCE38 100%);
border-radius: 4px;
position: absolute;
top: 0;
left: 0;
transition: all 0.5s cubic-bezier(0, 0.64, 0.36, 1);
}
.progress-box__icon, .progress-box__txt {
.wh(100%,100%);
position: absolute;
top: 0;
left: 0;
}
.progress-box__icon-itm {
.wh(32px,32px);
position: absolute;
top: -4px;
transform: translate(-50%,0);
}
.progress-box__icon-itm:last-child {
transform: none;
}
.progress-box__txt {
top: -8px;
}
.progress-box__txt-bottom {
top: 46px;
}
.progress-box__txt-item {
.wh(78px,36px);
font-size: 30px;
background-color: #3C1D17;
border-radius: 4px;
.flex-row-center();
font-size: 26px;
font-family: SourceHanSansCN;
font-weight: 500;
color: #FFF1CC;
position: absolute;
transform: translate(-50%,0);
top: 0;
white-space: nowrap;
}
.progress-box__txt-item:last-child {
transform: translate(-100%,0);
}
.progress-txt-active {
background-color: #FFD64D;
color: #302A28;
}
\ No newline at end of file
# 排行榜组件
---
## 何时使用
- 当某个页面需要展示累计邀请、分等级奖励进度条、累计签到等。
## 目录结构
`RankTitle` 排行榜头部
`RankList` 排行榜列表
## RankTitle API
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --------- | ------------ | ----------- | ------------------------------------------------------- | ---- |
| titleList | 表头 | ArrayString | - | |
| myRank | 个人排名信息 | Object | { rank: '11', userNick: '用户名', rankValue: '534443' } | |
## RankList API
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| -------- | ----------- | ----------- | -------- | ---- |
| rankList | 排名信息 | ArrayObject | 见示例一 | |
| rankIcon | 前三名 Icon | Object | 见示例二 | |
## 示例一
```
[{
rank: 1,
userNick: '用户昵称',
hotValue: 98882892
}]
```
## 示例二
```
{
1: '//yun.duiba.com.cn/taobaomini/pike_call/icon_06.png',
2: '//yun.duiba.com.cn/taobaomini/pike_call/icon_07.png',
3: '//yun.duiba.com.cn/taobaomini/pike_call/icon_08.png'
}
```
import React ,{ memo } from 'react'
import { View, Image, ScrollView } from '@tarojs/components'
import { RANK_LIST } from '@/mock'
import styles from './RankList.module.less'
const RANK_ICON = {
1: '//yun.duiba.com.cn/taobaomini/pike_call/icon_06.png',
2: '//yun.duiba.com.cn/taobaomini/pike_call/icon_07.png',
3: '//yun.duiba.com.cn/taobaomini/pike_call/icon_08.png'
}
const RankList = memo((props) => {
const { rankList = RANK_LIST, rankIcon = RANK_ICON } = props
return (
<ScrollView scrollY className={styles['scroll-container']}>
{rankList.map((itm, i) => {
return (
<View
className={`${styles['scroll-item']} ${
styles['scroll-item' + (i + 1)]
}`}
>
<View className={styles['scroll-item__rank']}>
{i <= 2 && (
<Image className={styles['rank-icon']} src={rankIcon[i + 1]} />
)}
<View className={styles['rank-num']}>{itm.rank}</View>
</View>
<View className={styles['scroll-item__user']}>{itm.userNick}</View>
<View className={styles['scroll-item__score']}>
{itm.rankValue}
</View>
</View>
)
})}
</ScrollView>
)
})
export default RankList
.scroll-container {
.wh(100%,100%);
max-height: 700px;
}
.text-overflow-el() {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.scroll-item {
.flex-row-space();
.wh(100%,70px);
background-color: #F7F0E6;
border: 1px solid #ECDBC0;
box-sizing: border-box;
}
.scroll-item__rank {
width: 33%;
.flex-row-center();
.text-overflow-el();
position: relative;
}
.rank-icon {
position: absolute;
left: 50%;
top: 50%;
.wh(30px,30px);
transform: translate(-50px,-50%);
}
.rank-num {
// transform: translate(-40px,0);
font-size: 30px;
font-family: YouSheBiaoTiHei;
font-weight: 400;
color: #2D2B28;
font-weight: 600;
}
.scroll-item__user {
width: 34%;
.flex-row-center();
.text-overflow-el();
font-size: 26px;
font-family: SourceHanSansCN;
font-weight: 400;
color: #666666;
}
.scroll-item__score {
width: 33%;
.flex-row-center();
.text-overflow-el();
font-size: 28px;
font-family: WorkSans;
font-weight: 600;
color: #2D2B28;
}
\ No newline at end of file
import React, { memo } from 'react'
import { View } from '@tarojs/components'
import styles from './RankTitle.module.less'
const RankTitle = memo((props) => {
const {
titleList = ['排名', '用户名', '人气值'],
myRank = { rank: '11', userNick: '用户名', rankValue: '534443' }
} = props
return (
<View className={styles['rank-box']}>
<View className={styles['rank-box__title']}>
{titleList.map(val => {
return <View className={styles['box-itm']}>{val}</View>
})}
</View>
<View className={styles['rank-box__info']}>
<View className={styles['box-itm']}>{myRank.rank}</View>
<View className={styles['box-itm']}>{myRank.userNick}</View>
<View className={styles['box-itm']}>{myRank.rankValue}</View>
</View>
</View>
)
})
export default RankTitle
.rank-box {
.wh(100%,168px);
}
.rank-box__title {
.wh(100%,102px);
.flex-row-space();
.image('//yun.duiba.com.cn/taobaomini/pike_call/rank_title_bg.png');
}
.rank-box__info {
.wh(100%,66px);
.flex-row-space();
background: linear-gradient(-90deg, #FFDE8B 0%, #FFEAB5 100%);
}
.box-itm {
height: 66px;
width: 33%;
.flex-row-center();
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.box-itm:first-child {
font-size: 30px;
font-family: YouSheBiaoTiHei;
font-weight: 600;
color: #2D2B28;
}
.box-itm:nth-child(2) {
width: 34%;
font-size: 26px;
font-family: SourceHanSansCN;
font-weight: 400;
color: #666666;
}
.box-itm:last-child {
font-size: 28px;
font-family: WorkSans;
font-weight: 600;
color: #2D2B28;
}
\ No newline at end of file
import { RankList, RankTitle } from '..'
const Demo = () => {
return (
<>
<RankTitle />
<RankList />
</>
)
}
export default Demo
export { default as RankList } from './RankList/RankList'
export { default as RankTitle } from './RankTitle/RankTitle'
import React, { useState, useEffect } from "react";
import { View, Image, Text } from "@tarojs/components";
import styles from "./RotateWheel.module.less";
import { useThrottle } from "@/hooks/useThrottle";
import API from "@/api";
import { prizeList } from "@/mock";
import Taro, { showToast, redirectTo, navigateTo } from "@tarojs/taro";
const oneTurn = 360;
const RotateWheel = props => {
const {
containerInfo={width:600,height:600},
bg = "https://yun.duiba.com.cn/spark/assets/8b6e920ffd09fab8f9ac2de09f9154879f4d0607.png",
ratio = 0.65,
radius = 300,
circles = 4,
divideNum = 8,
duration = 5000,
timeFunction = "ease-out",
nodeInfo = { width: 100, height: 100 },
imgInfo = { width: 80, height: 80 },
showWay = "negative",
isShowPrizeName = true,
prizeNameWidthRatio = 1.2,
callback = () => {}
} = props;
/* 奖品列表 */
const [prizelist, setPrizelist] = useState(prizeList);
/* 平分角度 */
const [angle, setAngle] = useState(oneTurn / divideNum);
/* 展示奖品列表 */
const [show, setShow] = useState(false);
/* 转动一圈时的角度值 */
const [rotateInOneTurn, setRotateInOneTurn] = useState(0);
/* 动画参数 */
const [ani, setAni] = useState({
startFlag: false,
option: { duration: duration, timeFunction: timeFunction, rotate: 0 }
});
useEffect(() => {
computePosition();
}, [prizeList]);
/**
* @description 获取奖品列表
*/
// const getPrizeListInfo = async () => {
// const {success,data,code,message} = await API.getRotatePrizeListInfo().catch((res)=>{
// showToast({title:res?.message ? res?.message : '网络异常,请稍后再试'})
// })
// if(success && data){
// const {list} = data;
// // setPrizelist(list)
// computePosition(list)
// }
// }
/**
* @description 计算奖品位置,旋转角度
*/
const computePosition = () => {
if (!prizeList.length) return;
let centerX, centerY, color, rotate;
/* 获取每块奖品的中心位置 */
prizelist.forEach((ele, i) => {
if (i % 2 == 0) {
color = "#527aff";
} else {
color = "#f13082";
}
// 当前奖品左上角位置
if (showWay === "negative") {
centerX =
radius -
ratio * radius * Math.sin(((angle / 2 + angle * i) / 180) * Math.PI);
centerY =
radius -
ratio * radius * Math.cos(((angle / 2 + angle * i) / 180) * Math.PI);
rotate = -(angle / 2 + angle * i);
} else {
centerX =
radius -
ratio * radius * Math.sin(((angle / 2 + angle * -i) / 180) * Math.PI);
centerY =
radius -
ratio * radius * Math.cos(((angle / 2 + angle * -i) / 180) * Math.PI);
// 旋转角度
rotate = -(angle / 2 + angle * -i);
}
ele.rotate = rotate;
ele.color = color;
// 奖品位置
let disleft = centerX - nodeInfo.width / 2;
let distop = centerY - nodeInfo.height / 2;
ele.centerX = disleft;
ele.centerY = distop;
ele.num = i;
// 测试:设置奖品Id
ele.prizeId = 100 + i;
});
console.log("prizelist", prizelist);
setPrizelist(prizelist);
setShow(true);
};
/**
* @description 抽奖
*/
const drawPrize = useThrottle(async () => {
// const {success,data,message,code} = await API.drawRotatePrize().catch((res)=>{
// showToast({title:res?.message ? res?.message : '网络异常,请稍后再试'})
// });
// if(success && data){
// const {prizeId,id} = data;
// }
// test
let prizeId = Math.floor(Math.random() * prizelist.length) + 100;
startRotation(prizeId);
setTimeout(() => {
// 弹出弹窗
callback && callback(); //prizeInfo
}, duration + 1000);
}, duration);
/**
* @description 根据奖品id开始旋转
* @param {*} prizeId 抽中的奖品id
*/
const startRotation = prizeId => {
console.log("start");
let rotateAngle;
let num;
let oneturnAngle;
prizelist.forEach(ele => {
if (ele.prizeId === prizeId) {
num = ele.num;
}
});
// 旋转角度 ( 旋转圈数 + 对应位置(0,7) ) * 平分角度 + 平分角度 / 2
if (showWay === "negative") {
rotateAngle = oneTurn * circles + num * angle + angle / 2;
oneturnAngle = num * angle + angle / 2;
} else {
rotateAngle =
oneTurn * circles + (prizelist.length - num) * angle + angle / 2;
oneturnAngle = (prizelist.length - num) * angle + angle / 2;
}
console.log("rotateAngle", rotateAngle, num, prizeId);
/* 执行动画 */
setAni({
startFlag: true,
option: {
...ani.option,
rotate: ani.option.rotate - rotateInOneTurn + rotateAngle
}
});
setRotateInOneTurn(oneturnAngle);
};
return (
<View
className={styles["rotate"]}
style={{
width: `${containerInfo.width / 100}rem`,
height: `${containerInfo.height / 100}rem`,
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<View
className={`${styles["rotate_container"]}`}
style={{
backgroundImage: `url(${bg})`,
transform: ani.startFlag ? `rotate(${ani.option.rotate}deg)` : "",
transition: ani.startFlag
? `all ${ani.option.timeFunction} ${ani.option.duration}ms`
: ""
}}
>
{show &&
prizelist.length &&
prizelist.map((ele, i) => {
return (
<View
className={styles["item"]}
style={{
// backgroundColor:`${ele?.color}`,
width: `${nodeInfo.width / 100}rem`,
height: `${nodeInfo.height / 100}rem`,
transform: `rotate(${ele?.rotate}deg)`,
top: `${ele.centerY / 100}rem`,
left: `${ele.centerX / 100}rem`,
position: "absolute"
}}
key={"item" + i}
>
{isShowPrizeName && (
<View
className={styles["prize_name"]}
style={{
width: `${prizeNameWidthRatio * 100}%`,
height: `${30 / 100}rem`
}}
>
{ele.name}
</View>
)}
<View
className={styles["prize_img"]}
style={{
backgroundImage: `url(${ele.image})`,
width: `${imgInfo.width / 100}rem`,
height: `${imgInfo.height / 100}rem`
}}
></View>
</View>
);
})}
</View>
<View className={styles["circle"]} onTap={drawPrize}>
draw
</View>
</View>
);
};
export default RotateWheel;
.rotate{
margin: 0 auto;
}
.rotate_container{
width: 100%;
height: 100%;
// border: 1px solid cyan;
// box-sizing: border-box;
position: relative;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
.image-property(cover, center center);
.item{
position: absolute;
display: flex;
justify-content: space-evenly;
align-items: center;
transform-origin: center;
flex-direction: column;
// border: 1px solid cyan;
// box-sizing: border-box;
.prize_name{
// margin: 0 auto;
// border: 1px solid cyan;
// box-sizing: border-box;
// display: flex;
// justify-content: center;
// align-items: center;
text-align: center;
white-space: nowrap;
font-size: 18px;
text-overflow: ellipsis;
overflow: hidden;
}
.prize_img{
.image-property(cover, center center);
margin: 0 auto;
}
}
}
.circle{
// border-radius: 50%;
width: 100px;
height: 100px;
background-color: rgb(78, 159, 165);
display: flex;
justify-content: center;
align-items: center;
position: absolute;
clip-path: polygon(50% 0%,0% 100%,100% 100%);
}
<mxfile host="65bd71144e">
<diagram id="aWOBYljMTH3HiLopJFIH" name="第 1 页">
<mxGraphModel dx="899" dy="594" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="3" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;shadow=1;fillColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="200" y="160" width="300" height="300" as="geometry"/>
</mxCell>
<mxCell id="6" value="" style="endArrow=none;dashed=1;html=1;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="200" y="310" as="sourcePoint"/>
<mxPoint x="500" y="310" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="8" value="" style="endArrow=none;dashed=1;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;fillColor=#FF9999;" edge="1" parent="1" source="3" target="3">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="430" y="330" as="sourcePoint"/>
<mxPoint x="480" y="280" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="9" value="" style="rounded=0;whiteSpace=wrap;html=1;shadow=0;fillColor=#FFCCCC;rotation=45;" vertex="1" parent="1">
<mxGeometry x="379" y="210" width="70" height="70" as="geometry"/>
</mxCell>
<mxCell id="10" value="" style="rounded=0;whiteSpace=wrap;html=1;shadow=0;fillColor=#FFCCCC;rotation=45;" vertex="1" parent="1">
<mxGeometry x="379" y="340" width="70" height="70" as="geometry"/>
</mxCell>
<mxCell id="11" value="" style="rounded=0;whiteSpace=wrap;html=1;shadow=0;fillColor=#FFCCCC;rotation=45;" vertex="1" parent="1">
<mxGeometry x="250" y="210" width="70" height="70" as="geometry"/>
</mxCell>
<mxCell id="12" value="" style="rounded=0;whiteSpace=wrap;html=1;shadow=0;fillColor=#FFCCCC;rotation=45;" vertex="1" parent="1">
<mxGeometry x="250" y="340" width="70" height="70" as="geometry"/>
</mxCell>
<mxCell id="13" value="1.找到每个奖品的中心位置&lt;br&gt;2.每个奖品旋转相应角度&lt;br&gt;3.给相应的奖品区域设置样式" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;shadow=0;" vertex="1" parent="1">
<mxGeometry x="490" y="180" width="250" height="50" as="geometry"/>
</mxCell>
<mxCell id="16" value="旋转逻辑" style="rounded=0;whiteSpace=wrap;html=1;shadow=0;strokeColor=#000000;fillColor=#FFCCCC;" vertex="1" parent="1">
<mxGeometry x="80" y="510" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="17" value="给每个奖品设置一个索引值,抽中的奖品id与奖品列表对应,则根据索引值旋转对应的角度。" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;shadow=0;" vertex="1" parent="1">
<mxGeometry x="80" y="590" width="200" height="110" as="geometry"/>
</mxCell>
<mxCell id="18" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;shadow=0;strokeColor=#000000;fillColor=#FFCCCC;" vertex="1" parent="1">
<mxGeometry x="310" y="270" width="80" height="80" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
\ No newline at end of file
**记事本打开((cmd + k) + v)**
##### 大转盘配置项入参
| 配置项 | 类型 | 描述 | 默认值 | 备注 |
| :-------- | :-----: | :--------: | :-----: | :---:|
| bg | String | 转盘背景 | / | / |
| radius | Number | 转盘半径大小 | 300 | px |
| ratio | Number | 距离中心点的比例系数 | 0.65 | 小于1 |
| divideNum | Number | 转盘划分为几等分 | 8 | 最少2等分|
| circles | Number | 转盘基本转动圈数 | 4 | / |
| duration | Number | 转盘持续时间 | 4000 | 毫秒 |
| timeFunction | String | 缓动动画方式 | 'ease-out' | 'ease-in-out', cubic-bezier(xx,xx,xx,xx) |
| nodeInfo | Object | 单个奖品区域大小 | {width:100,height:100} | 宽高100px |
| imgInfo | Object | 奖品图片大小 | {width:80,height:80} | 宽高80px |
| showWay | String | 奖品排列方式 | 'negative' |negative逆时针排列 positive顺时针排列|
| isShowPrizeName | Boolean | 是否展示奖品名称 | true | true展示 false不展示 |
| prizeNameWidthRatio | Number | 奖品名称容器宽度与nodeInfo宽度的比值 | 1.2 | 超过容器宽度则...省略 |
##### 出参
| 配置项 | 类型 | 描述 | 默认值 | 备注 |
| :-------- | :-----: | :--------: | :-----: | :---:|
| callback | Function | 回调函数 | / |抽中奖品后的回调方法,后续的弹窗处理|
+ 使用
```jsx
import RotateWheel from '@/components/_tb_comps/RotateWheel/RotateWheel'
const rotateConfig = { //不传走默认配置
bg: 'https://yun.duiba.com.cn/spark/assets/8b6e920ffd09fab8f9ac2de09f9154879f4d0607.png',
ratio: 0.65,
radius: 300,
circles: 4,
divideNum: 8,
duration: 5000,
timeFunction:'ease-out',
nodeInfo:{
width:100,
height:100
},
imgInfo:{
width:80,
height:80
},
showWay:'negative',//positive/negative
isShowPrizeName:true
}
export const Index = () => {
return(
<RotateWheel {...rotateConfig} callback={()=>{}} />
)
}
```
\ No newline at end of file
import React, { useState } from 'react'
import { View, Image, ScrollView } from '@tarojs/components'
import Taro, { useShareAppMessage, useDidShow } from '@tarojs/taro'
import classnames from 'classnames'
import styles from './ScrollXView.module.less'
function ScrollXView(props) {
const {
prizeList = [],
containerWidth = 300,
containerHeight = 300,
imgWidth=240,
imgHeight=240,
isShowPrizeName=true,
prizeInCenterNum=2,
marginRight=26,
backgroundColor='#E7E7EF',
backgroundImage=''
} = props
const showPrize = classnames(styles['show-pize-box'],{
[`${styles['show-pize-box__two']}`]: prizeList.length <= prizeInCenterNum,
[`${styles['show-pize-box__more']}`]: prizeList.length > prizeInCenterNum,
})
return (
<>
<ScrollView scrollX className={styles['scroll-x-box']}>
{<View className={showPrize}>
{
prizeList.length > 0 && prizeList.map((itm,i) => {
return(
<View
key={'prize'+i}
className={styles['show-pirze__item']}
style={{
width:`${containerWidth/100}rem`,
height:`${containerHeight/100}rem`,
backgroundImage:`${backgroundImage}`,
backgroundColor:backgroundColor,
marginRight:`${((prizeList.length > prizeInCenterNum) &&(prizeList.length - 1) !== i) ? `${marginRight/100}rem` : ''}`
}}
>
<View
className={styles['pirze__item-img']}
style={{
width:`${imgWidth/100}rem`,
height:`${imgHeight/100}rem`
}}
>
<Image
src={itm.image}
className={styles['pirze__item-img-icon']}
style={{
width:`${imgWidth/100}rem`,
height:`${imgHeight/100}rem`
}}
/>
</View>
{
isShowPrizeName &&
<View
className={styles['pirze__item-name']}
style={{
height:`${60/100}rem`,
fontSize:`${24/100}rem`
}}
>{itm.name}</View>
}
</View>
)
})
}
</View>}
</ScrollView>
</>
)
}
export default ScrollXView
\ No newline at end of file
##### ScrollXView滑动配置项
+ 入参
| 配置项 | 类型 | 描述 | 默认值 | 备注 |
| :-------- | :-----: | :--------: | :-----: | :---:|
| prizeList | Array | 滑动数据 | [...] | / |
| containerWidth | Number | 单个奖品容器的宽度 | 300 | / |
| containerHeight | Number | 单个奖品容器的高度 | 300 | / |
| imgWidth | Number | 奖品图片宽度 | 200 | / |
| imgHeight | Number | 奖品图片高度 | 200 | / |
| isShowPrizeName | Boolean | 是否展示奖品名称 | true | true:展示 false:不展示 |
| prizeInCenterNum | Number | 奖品少于n个时居中展示 | n=2 | 多于n个,可滑动查看 |
| marginRight | Number | 奖品容器之间间距 | 26 | px |
| backgroundColor | String | 奖品容器背景颜色 | #E7E7EF | / |
| backgroundImage | String | 奖品容器背景图 | '' | 默认为空 |
+ prizeList数组格式
```json
[
{
"image":"",
"name":"xxx",
// ...
},
// ...
]
```
+ 使用
```jsx
import ScrollXView from '@/components/_tb_comps/ScrollXView/ScrollXView'
// 组件不一定在该目录下,仅做举例引入
const SCROLLXVIEW_CONFIG = { //不传走默认配置
prizeList: [],
containerWidth : 300,
containerHeight : 300,
imgWidth:200,
imgHeight:200,
isShowPrizeName:true,
prizeInCenterNum:2,
marginRight:26,
backgroundColor:'#E7E7EF',
backgroundImage:''
}
export const Index = () => {
return(
<View className="scroll">
<ScrollXView {...SCROLLXVIEW_CONFIG} prizeList={prizeList} />
</View>
)
}
```
\ No newline at end of file
.scroll-x-box {
.wh(100%,100%);
display: flex;
justify-content: flex-start;
align-items: center;
}
.show-pize-box {
display: flex;
justify-content: flex-start;
align-items: center;
}
.show-pize-box__two {
width: 100%;
display: flex;
justify-content: space-around;
align-items: center;
}
.show-pirze__item {
// background: #E7E7EF;
border-radius: 25px;
// margin-left: 26px;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
.image-property(cover, center center);
}
// .show-pirze__item:last-child {
// margin-right: 26px;
// }
// .show-pirze__item:first-child {
// margin-left: 10px;
// }
.show-pize-box__more{
// padding: 10px;
}
.pirze__item-img {
// width: 224px;
// height: 224px;
background: #FFFFFF;
border-radius: 25px;
margin: 16px auto 20px;
}
.pirze__item-img-icon {
// width: 224px;
// height: 224px;
border-radius: 25px;
.image-property(cover, center center);
}
.pirze__item-name {
width: 90%;
font-family: FZLTHProGlobal;
font-weight: 400;
color: #1C1C1C;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
// 单行
// white-space: nowrap;
// 多行
// display: -webkit-box;
// -webkit-box-orient:vertical;
// -webkit-line-clamp: 2;
}
\ No newline at end of file
import React, { useState, useEffect,useRef, useMemo, useCallback } from "react";
import { View, Image, Text, Swiper, SwiperItem } from "@tarojs/components";
import './SwiperView.less'
import Taro,{showToast,redirectTo,navigateTo} from '@tarojs/taro'
export const EASING_WAYS = {
'DEFAULT':'default',
'LINEAR':'linear',
'EASEIN':'easeInCubic',
'EASEOUT':'easeOutCubic',
'EASEINOUT':'easeInOutCubic'
}
const SwiperView = (props) => {
/**
* @description swiperList
* {image,content,hasBtn}
*/
const {
swiperList=[],
easingFunction=`${EASING_WAYS.LINEAR}`,
circular=false,
autoplay=false,
vertical=false,
duration=1000,
interval=1500,
disableTouch=false,
swiperGroup={width:300,height:300,overflow:'visible'},
swiperContent={width:250,height:300,backgroundColor:'#fff',backgroundImage:'',borderRadius:25},
imgContent={width:200,height:200},
textContent={width:200,height:60},
showLeftRightBtn=true,
} = props;
const [currentIndex, setCurrentIndex] = useState(1);
const indexref = useRef(0)
// const index = useMemo(()=>{
// return currentIndex;
// },currentIndex)
/**
* @description onchange事件
* @param {*} e
*/
const swiperChange = e => {
console.log("e", e);
console.log('swiperChange-----currentindex',currentIndex,indexref.current,e.detail.current);
setCurrentIndex(e.detail.current);
};
const onTransition = (e) => {
// console.warn(e)
// setCurrentIndex(indexref.current);
}
/**
* @description 入场/出场动画
* @param {*} index 数组索引值
* @returns 返回类名
*/
const useCustomAni =((i) => {
return `${((currentIndex < i && ((i !== swiperList.length - 1)||currentIndex!==0)) || ((currentIndex === swiperList.length - 1) && i == 0 ))
? 'item_right'
:
(((currentIndex > i && (i !== 0 || currentIndex !== swiperList.length - 1)) || (currentIndex !== 0 && i !== swiperList.length - 1)) ? 'item_left' : 'item_middle')
}`
})
/**
* @description 获取当前卡片详情之类
*/
const getCurrentDetail = (item) => {
showToast({
title:'111'
})
}
const motionType = {
LEFT:"LEFT",
RIGHT:"RIGHT"
}
/**
* @description
*/
const turnRound = ((type) => {
const motion = {
[motionType.LEFT]:()=>{
setTimeout(() => {
// setCurrentIndex((indexref.current==0) ? (swiperList.length - 1) : (indexref.current - 1))
// indexref.current = (indexref.current==0) ? (swiperList.length - 1) : (indexref.current - 1)
// console.log('left----',currentIndex,indexref.current);
if(currentIndex >= 1){
setCurrentIndex(currentIndex - 1)
}
}, 0);
},
[motionType.RIGHT]:()=>{
setTimeout(() => {
// setCurrentIndex((indexref.current == (swiperList.length - 1)) ? 0 : (indexref.current + 1))
// indexref.current = (indexref.current == (swiperList.length - 1)) ? 0 : (indexref.current + 1)
// console.log('right----',currentIndex,indexref.current);
if(currentIndex < swiperList.length - 1){
setCurrentIndex(currentIndex + 1)
}
}, 0);
}
}
motion[type]();
})
useEffect(()=>{
console.log('cur----',currentIndex);
},[currentIndex])
return (
<View className="contain">
<View className='left_btn' onTap={()=>turnRound('LEFT')}>&lt;&lt;</View>
<View
className="scroll_container"
style={{
width:`${700/100}rem`,
overflow:'hidden'
}}
>
<Swiper
className="swiper_group"
current={currentIndex}
easingFunction={easingFunction}
onChange={swiperChange}
circular={circular}
vertical={vertical}
autoplay={autoplay}
duration={duration}
interval={interval}
disableTouch={disableTouch}
style={{
width:`${swiperGroup.width/100}rem`,
height:`${swiperGroup.height/100}rem`,
overflow:`${swiperGroup.overflow}`
}}
>
{swiperList.length &&
swiperList.map((item, i) => (
<SwiperItem>
<View
className={`scroll_item ${i === currentIndex ? "current_item" : ""} ${useCustomAni(i)}`}
style={{
width:`${swiperContent.width/100}rem`,
height:`${swiperContent.height/100}rem`,
backgroundColor:`${ swiperContent.backgroundColor}`,
backgroundImage:`${ swiperContent.backgroundImage}`,
borderRadius:`${swiperContent.borderRadius/100}rem`
}}
onTap={()=>getCurrentDetail(item)}
>
{/* {i === currentIndex && (
<Image
className="checkPic"
src="https://yun.dui88.com/tebuXinYuan/checkGoods.png"
></Image>
)} */}
<View
className="item_img_content"
>
{/* 单个swiper主要内容 */}
<Image
className="img"
style={{
width:`${imgContent.width/100}rem`,
height:`${imgContent.height/100}rem`
}}
src={item.image}></Image>
{
item?.content &&
<View
className='text_content'
style={{
width:`${textContent.width/100}rem`,
height:`${textContent.height/100}rem`,
}}
>
<View className='text' >{item.content}</View>
</View>
}
</View>
</View>
</SwiperItem>
))}
</Swiper>
</View>
<View className='right_btn' onTap={()=>turnRound('RIGHT')}>&gt;&gt;</View>
</View>
);
};
export default SwiperView;
.contain{
width: 100%;
position: relative;
}
.left_btn{
width: 80px;
height: 40px;
display: flex;
position: absolute;
justify-content: center;
align-items: center;
z-index: 1;
top: 50%;
transform: translateY(-50%);
border: 1px solid cyan;
box-sizing: border-box;
}
.right_btn{
width: 80px;
height: 40px;
display: flex;
position: absolute;
justify-content: center;
align-items: center;
z-index: 1;
top: 50%;
transform: translateY(-50%);
border: 1px solid cyan;
box-sizing: border-box;
}
.left_btn{
left: 10px;
}
.right_btn{
right: 10px;
}
.scroll_container{
// width: 450px;
// overflow: hidden;
width: 80%;
margin: 0 auto;
display: flex;
justify-content: space-around;
align-items: center;
position: relative;
.swiper_group {
margin: 0 auto;
position: relative;
swiper-item {
width: 300px;
height: 300px;
display: flex;
justify-content: center;
align-items: center;
.image-property(cover, center center);
// border: 1px solid cyan;
// box-sizing: border-box;
}
.scroll_item {
// width: 300px;
margin: 0 auto;
position: relative;
border-radius: 20px;
.image-property(cover, top center);
.checkPic {
width: 60px;
height: 60px;
border-radius: 50%;
position: absolute;
right: 40px;
top: 40px;
}
.text_content{
margin: 0 auto;
box-sizing: border-box;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
text-overflow: ellipsis;
overflow: hidden;
.text{
color: #232323;
text-align: center;
line-height: 30px;
font-size: 24px;
}
}
&.current_item {
.item_img_content {
.img{}
.text_content{
.text{}
}
}
}
}
// &.item_left{
// transform: translateX(-300px);
// transition: all 1s ease;
// }
// &.move_right{
// transform: translateX(300px);
// transition: all 1s ease;
// }
// &.item_middle{
// transform: translateX(0px);
// transition: all 1s ease;
// }
}
}
@keyframes movein {
from {
transform: perspective(1000px) translate3d(0,0,0) rotate3d(0, 1, 0, 25deg) scale(0.85);
transform-style: preserve-3d;
transform-origin: 45% center;
}
50%{
transform: perspective(1000px) translate3d(0,20px,0) rotate3d(0, 1, 0, 25deg) scale(0.85);
transform-style: preserve-3d;
transform-origin: 45% center;
}
to {
transform: perspective(1100px) translate3d(0,0,0) rotate3d(0, 1, 0, 0deg) scale(1);
transform-origin: 45% center;
transform-style: preserve-3d;
}
}
@keyframes moveout{
from {
transform: perspective(1000px) rotate3d(0, 1, 0, 0deg) scale(1);
transform-style: preserve-3d;
transform-origin: 45% center;
}
50%{
transform: perspective(1000px) rotate3d(0, 1, 0, 25deg) scale(1);
transform-style: preserve-3d;
transform-origin: 45% center;
}
to {
transform: perspective(1200px) rotate3d(0, 1, 0, 25deg) scale(0.85);
transform-origin: 45% center;
transform-style: preserve-3d;
}
}
@keyframes scales {
from{
transform: scale(1,1);
transform-origin: center;
}
50%{
transform: scale(1.1,1.1);
transform-origin: center;
}
to{
transform: scale(1,1);
transform-origin: center;
}
}
\ No newline at end of file
##### swiperView 滑动
| 配置项 | 类型 | 描述 | 默认值 | 备注 |
| :-------- | :-----: | :--------: | :-----: | :---:|
| swiperList | Array | 滑动数组 | [...] | / |
| disableTouch | Boolean | 是否可手动滑动 | false | true 可滑动 false 不可滑动 |
| autoplay | Boolean | 是否自动切换 | false |/|
| circular | Boolean | 是否采用衔接滑动| false |/|
| vertical | Boolean | 是否垂直切换| false |/|
| duration | Number | 滑动动画时长| 1000 |/|
| interval | Number | 自动切换时间间隔| 1500 |/|
| easingFunction|String | 缓动动画类型| linear |'default','linear' 'easeInCubic' 'easeOutCubic' 'easeInOutCubic'|
| swiperGroup | Object | 整个swiper的宽高 |{width:300,height:300,overflow:''}|/|
| swiperContent | Object | 单个swiperItem的宽高 |{width:250,height:300,backgroundColor:'#fff',borderRadius:25,backgroundImage:''}|/|
| imgContent | Object | 图片宽高 |{width:200,height:200}|/|
| textContent | Object | 文本宽高 |{width:300,height:60}|/|
| showLeftRightBtn | Boolean | 是否显示左右点击按钮|/|
#### swiperList数据
```json
[
{
"image":"",//图片链接,图片宽高需要在less中修改
"content":"",//图片描述
// 或者手动修改组件中的字段名称 ...
},
// ...
]
```
+ 使用
```jsx
import SwiperView from '@/components/_tb_comps/SwiperView/SwiperView'
const swiper_config = {//不传走默认配置
easingFunction:`linear`,
circular:true,
autoplay:false,
vertical:false,
duration:1000,
interval:1500,
disableTouch:false,
swiperGroup:{width:300,height:300,overflow:''},
swiperContent:{width:250,height:300,backgroundColor:'#fff',backgroundImage:'',borderRadius:25},
imgContent:{width:200,height:200},
textContent:{width:240,height:60}
}
export const Index = () => {
return(
<SwiperView {...swiperConfig} swiperList={swiperList} />
)
}
```
\ No newline at end of file
import React, { useState } from 'react'
import { View, Image, ScrollView } from '@tarojs/components'
import classnames from 'classnames'
import styles from './TitleImg.module.less'
function TitleImg(props) {
const { config } = props
return (
<View className={styles['title-box']}>
<Image src={config.image} mode="widthFix" style={{width: config.width / 100 + 'rem', height: config.height / 100 + 'rem' }} />
</View>
)
}
export default TitleImg
\ No newline at end of file
.title-box {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
\ No newline at end of file
/**
* 全局配置
*/
import React from 'react'
export const ProgressGlobalConfig = {
type: 1,
maxLevel: 15,
width: 500
}
export const ProgressContext = React.createContext()
import { View } from '@tarojs/components'
import React, { memo, useContext, useMemo } from 'react'
import classnames from 'classnames'
import tbcc from 'tbcc-sdk-ts'
import { ProgressContext } from '../ProgressContext'
// css
import styles from './index.module.less'
const { throttleHandle } = tbcc.utils
const ProgressNode = memo(props => {
/**
* isReach 是否达到当前进度
* levelValue 节点值
* levelPos 节点偏移
* onReceviceClick 达到当前进度才可点击触发
*/
const { isReach, levelValue, levelPos, onReceviceClick = () => {} } = props
const { maxLevel, nodeClassName } = useMemo(
() => useContext(ProgressContext),
ProgressContext
)
const getNodeStyle = classnames(styles['defualt-progress-node'], {
[nodeClassName]: nodeClassName
})
const onNodeClick = throttleHandle(() => {
isReach && onReceviceClick()
}, 2000)
return (
<View
onTap={onNodeClick}
className={getNodeStyle}
style={{
left: levelPos || (levelValue / maxLevel) * 100 + '%'
}}
>
{/* 自定义 */}
</View>
)
})
export default ProgressNode
.defualt-progress-node {
display: flex;
flex-direction: column;
position: absolute;
z-index: 10;
font-size: 20px;
transform: translate(-100%, -100%);
white-space: nowrap;
}
// 描述
.defualt-progress-node-desc {
position: absolute;
right: -110px;
top: -54px;
z-index: 11;
}
// Icon
.defualt-progress-node-icon {
.wh(32px, 32px);
}
// Icon贴图
.defualt-progress-node-icon-iconImg {
.wh(155px, 151px);
position: absolute;
top: -54px;
}
// Icon文本
.defualt-progress-node-icon-txt {
font-size: 22px;
}
// 按钮
.defualt-progress-node-btn-txt {
position: absolute;
top: 80px;
left: 6px;
}
# 进度条组件
---
## 何时使用
- 当某个页面需要展示累计邀请、分等级奖励进度条、累计签到等。
## 目录结构
`ProgressContext` 进度条全局默认配置
`ProgressNode` 进度条节点 自定义
## API
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| levelList | 节点数据 | ArrayObject | 见下面示例一 | |
| currentValue | 当前进度值 | Numbver | 0 | |
| className | 进度条节点组样式 | Object | - | |
| maxLevel | 最大值 | Number | 15 | |
| width | 进度条长度 | Number | 500 | |
| onReceviceClick | 点击进度按钮回调 | Function | ()=>{} | |
| nodeClassName | 节点样式 | Object | - | |
## 示例一
```
[
{
levelValue: 0, //节点值
...
},
{
levelValue: 5,
...
]
```
import React, { useState } from 'react'
import { View } from '@tarojs/components'
import Progress from '../index'
import styles from './index.module.less'
const initLevelList = [
{
id: 0,
levelValue: 0,
isReceived: true,
iconInfo: {
iconUrl: '//yun.dui88.com/tebuXinYuan/main-circle-tip.png',
iconTxt: ''
},
descInfo: {
descTxt: '等级1'
}
},
{
id: 1,
levelValue: 5,
isReceived: false,
iconInfo: {
iconUrl: '//yun.dui88.com/tebuXinYuan/main-circle-tip.png',
iconTxt: ''
},
descInfo: {
descTxt: '等级2'
}
},
{
id: 2,
levelValue: 10,
isReceived: false,
iconInfo: {
iconUrl: '//yun.dui88.com/tebuXinYuan/main-circle-tip.png',
iconTxt: ''
},
descInfo: {
descTxt: '等级3'
}
},
{
id: 3,
levelValue: 15,
isReceived: false,
iconInfo: {
iconUrl: '//yun.dui88.com/tebuXinYuan/main-circle-tip.png',
iconTxt: ''
},
descInfo: {
descTxt: '等级4'
}
}
]
const Demo = props => {
const [levelList, setLevelList] = useState(initLevelList)
const [currentValue, setCurrentValue] = useState(10)
const onReceviceClick = id => {
console.log('点击:', id)
}
return (
<View className={styles['progress']}>
<Progress
levelList={levelList}
currentValue={currentValue}
btnClassName={styles['btnClassName']}
activeBtnClassName={styles['activeBtn']}
disableBtnClassName={styles['disableBtn']}
onReceviceClick={onReceviceClick}
/>
</View>
)
}
export default Demo
.progress {
margin-top: 100px;
display: flex;
justify-content: center;
}
.disableBtn {
.wh(108px, 42px);
.image('//yun.dui88.com/tebuXinYuan/main-received-btn.png');
}
.activeBtn {
.wh(108px, 42px);
.image('//yun.dui88.com/tebuXinYuan/main-waitReceive-btn.png');
}
.btnClassName {
.wh(108px, 42px);
.image('//yun.dui88.com/tebuXinYuan/main-waitUnlock-btn.png');
}
import { View } from '@tarojs/components'
import React,{ memo, useMemo, useCallback } from 'react'
import classnames from 'classnames'
import ProgressNode from './ProgressNode'
import styles from './index.module.less'
import {
ProgressContext,
ProgressGlobalConfig
} from './ProgressContext'
const Progress = memo(props => {
const {
onReceviceClick,
levelList = [],
currentValue = 0,
className,
maxLevel = ProgressGlobalConfig.maxLevel,
width = ProgressGlobalConfig.width
} = props
const LevelList = useMemo(() => {
return levelList
}, levelList)
// 合并全局配置
const mergeConfigToDefault = useCallback(config => {
return { ...ProgressGlobalConfig, ...config }
}, [])
/**
* 过滤props
* @param {*} props 过滤对象
* @param {*} filterPropArr 需要过滤掉的属性名称
* @returns
*/
const filterProps = (props, filterPropArr) => {
if (typeof props !== 'object' || !Array.isArray(filterPropArr)) {
throw new Error('参数格式不正确')
}
let obj = {}
Object.keys(props)
.filter(key => !filterPropArr.includes(key))
.map(key => {
obj[key] = props[key]
})
return obj
}
const getProgressStyle = classnames(styles['defualt-progress'], {
[className]: className
})
const getProItm = classnames(styles['progress-box-com'], {
[`${styles['progress-box-com-02']}`]: currentValue === maxLevel
})
return (
// 全局配置注入
<ProgressContext.Provider
value={mergeConfigToDefault(
filterProps(props, ['levelList', 'currentValue'])
)}
>
{/* 进度条-all */}
<View
className={styles['progress-box']}
style={{ width: width / 100 + 'rem' }}
>
{/* 进度条-current */}
<View
className={getProItm}
style={{
width:
currentValue >= maxLevel
? '100%'
: (currentValue / maxLevel) * 100 + '%'
}}
/>
{/* 进度条节点 */}
<View className={getProgressStyle}>
{LevelList.map((item, index) => {
return (
<ProgressNode
key={index}
{...item}
onReceviceClick={onReceviceClick}
isReach={item.levelValue <= currentValue}
/>
)
})}
</View>
</View>
</ProgressContext.Provider>
)
})
export default Progress
.progress-box {
width: 692px;
height: 20px;
background: #371b15;
// border: 1px solid #371B15;
// box-shadow: 0px 0px 3px 0px #4D3328;
border-radius: 4px;
position: relative;
}
.defualt-progress {
}
.progress-box-com {
height: 20px;
background: linear-gradient(270deg, #ffde8b 0%, #ffce38 100%);
border-radius: 4px;
position: absolute;
top: 0;
left: 0;
transition: all 0.5s cubic-bezier(0, 0.64, 0.36, 1);
}
import React, { useState } from 'react'
import { View, Image, ScrollView } from '@tarojs/components'
import classnames from 'classnames'
import styles from './Test.module.less'
function Test(props) {
const { } = props
return (
<View className={styles['test-box']}>
</View>
)
}
export default Test
\ No newline at end of file
import React from 'react' import React from 'react'
import { View, Image } from '@tarojs/components' import { View, Image } from '@tarojs/components'
import './DeliveryModal.less' import styles from './DeliveryModal.module.less'
export default function DeliveryModal(props) { export default function DeliveryModal(props) {
const { prizeItem, task, onClose = () => {} } = props const { prizeItem, task, onClose = () => {} } = props
return ( return (
<View className='logistics-modal-container'> <View className={styles['container']}>
<View <View
className='logistics-modal-container__content' className={styles['content']}
style={prizeItem.shipStatus === 3 style={prizeItem.shipStatus === 3
? `background: url(${task.shipped}) no-repeat;background-size: 100% 100%;` ? `background: url(${task.shipped}) no-repeat;background-size: 100% 100%;`
: `background: url(${task.notShipped}) 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={styles['container__close']} onTap={onClose} />
<View className='logistics-modal-container__content-item'> <View className={styles['content-item']}>
<View className='logistics-modal-container__content-left'> <View className={styles['content-left']}>
<Image mode='scaleToFill' src={prizeItem.image} /> <Image mode='scaleToFill' src={prizeItem.image} />
</View> </View>
<View className='logistics-modal-container__content-right'> <View className={styles['content-right']}>
<View className='logistics-modal-container__content-name'>{prizeItem.name}</View> <View className={styles['content-name']}>{prizeItem.name}</View>
<View className='logistics-modal-container__content-copy'> <View className={styles['content-copy']}>
<View className='logistics-modal-container__content-copy__code'>奖品编码:{prizeItem._id}</View> <View className={styles['content-copy__code']}>奖品编码:{prizeItem._id}</View>
<View className='logistics-modal-container__content-copy__img' onTap='copyPrizeId' /> <View className={styles['content-copy__img']} onTap='copyPrizeId' />
</View> </View>
<View className='logistics-modal-container__content-desc'>{prizeItem.shipStatus === 3 ? '发货状态:已发货' : '发货状态:未发货'}</View> <View className={styles['content-desc']}>{prizeItem.shipStatus === 3 ? '发货状态:已发货' : '发货状态:未发货'}</View>
{prizeItem.shipStatus === 2 && <View className='logistics-modal-container__content-desc'>温馨提示:奖品将在活动结束7天内陆续发出</View>} {prizeItem.shipStatus === 2 && <View className={styles['content-desc']}>温馨提示:奖品将在活动结束7天内陆续发出</View>}
{ {
prizeItem.shipStatus !== 2 && prizeItem.shipStatus !== 2 &&
<View className='logistics-modal-container__content-copy'> <View className={styles['content-copy']}>
<View className='logistics-modal-container__content-copy__code'>{'物流单号:' + prizeItem.shipNum}</View> <View className={styles['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 className={styles['content-copy__img']} style={`background: url(${task.copyIcon}) no-repeat;background-size: 100% 100%;`} onTap={onClose} />
</View> </View>
} }
</View> </View>
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
} }
} }
.logistics-modal-container { .container {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
} }
} }
.logistics-modal-container__content { .content {
width: 646px; width: 646px;
height: 244px; height: 244px;
padding: 0 46px; padding: 0 46px;
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
animation: containerAnimation 0.3s forwards; animation: containerAnimation 0.3s forwards;
} }
.logistics-modal-container__close { .container__close {
position: absolute; position: absolute;
right: -10px; right: -10px;
top: -70px; top: -70px;
...@@ -49,23 +49,23 @@ ...@@ -49,23 +49,23 @@
background-size: 100% 100%; background-size: 100% 100%;
} }
.logistics-modal-container__content-item { .content-item {
border-radius: 10px; border-radius: 10px;
display: flex; display: flex;
} }
.logistics-modal-container__content-left { .content-left {
width: 144px; width: 144px;
height: 144px; height: 144px;
background: rgba(244, 244, 244, 1); background: rgba(244, 244, 244, 1);
} }
.logistics-modal-container__content-left image { .content-left image {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.logistics-modal-container__content-right { .content-right {
height: 144px; height: 144px;
margin-left: 24px; margin-left: 24px;
display: flex; display: flex;
...@@ -78,30 +78,30 @@ ...@@ -78,30 +78,30 @@
white-space: nowrap; white-space: nowrap;
} }
.logistics-modal-container__content-name { .content-name {
font-size: 28px; font-size: 28px;
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
} }
.logistics-modal-container__content-desc { .content-desc {
font-size: 20px; font-size: 20px;
} }
.logistics-modal-container__content-ids { .content-ids {
margin-bottom: 20px; margin-bottom: 20px;
} }
.logistics-modal-container__content-copy { .content-copy {
display: flex; display: flex;
align-items: center; align-items: center;
height: 30px; height: 30px;
} }
.logistics-modal-container__content-copy__code { .content-copy__code {
font-size: 20px; font-size: 20px;
} }
.logistics-modal-container__content-copy__img { .content-copy__img {
width: 20px; width: 20px;
height: 20px; height: 20px;
background-size: 100% 100%; background-size: 100% 100%;
......
import { View, Text, Image } from '@tarojs/components'
import React, { useState } from 'react'
import { useThrottle } from '@/hooks/useThrottle'
import Modal from '@/components/_base/Modal/Modal'
import styles from './DoHelpModal.module.less'
import API from '@/api'
import { useLoginInfoModel, useActivityInfoModel } from '@/store'
import { checkIsMember } from 'tbcc-sdk-ts/lib/utils'
import { commonToast } from 'tbcc-sdk-ts/lib/core/tb'
import { HELP_MODAL_TYPE, HELP_CONFIG } from '@/config/help.config'
export default function DoHelpModal(props) {
const { activityInfo } = useActivityInfoModel()
const { type = HELP_MODAL_TYPE.HELP, onClose = () => {}, newVip = true, helpInfo = {} } = props
const [ memberModalVisible, setMemberModalVisible ] = useState(false)
const { loginInfo: { inviteId } } = useLoginInfoModel()
const onAuthSuccess = async () => {
const isVip = await checkIsMember()
const helpRes = await API.completeTask({ key: HELP_CONFIG.KEY, inviteId, isVip })
if (helpRes?.success) {
commonToast('助力成功')
} else {
commonToast(helpRes?.message)
}
onClose()
setMemberModalVisible(false)
}
const onAuthFail = () => {
setMemberModalVisible(false)
}
const closeMemberModal = () => {
setMemberModalVisible(false)
}
const onHandleHelp = useThrottle(async () => {
const helpFn = {
[HELP_MODAL_TYPE.HELP]: async () => {
// 条件不是需要新会员的情况下,直接助力
if (!newVip) {
const helpRes = await API.completeTask({ key: HELP_CONFIG.KEY, inviteId, isVip })
if (helpRes?.success) {
commonToast('助力成功')
} else {
commonToast(helpRes?.message)
}
onClose()
}
// 助力条件为新会员
const isVip = await checkIsMember()
// 已经是会员
if (isVip) {
commonToast('新会员才能助力哦~')
} else {
// 还不是会员,弹出会员弹窗
setMemberModalVisible(true)
}
},
[HELP_MODAL_TYPE.FAIL]: async () => onClose(type),
[HELP_MODAL_TYPE.REWARDS]: async () => {
const { success, data } = await API.receiveTask({ key: HELP_CONFIG.KEY })
if(success && data) {
onClose(type)
}
}
}
helpFn[type]()
})
const onCloseModal = () => {
if(type === HELP_MODAL_TYPE.REWARDS) {
onHandleHelp()
return;
}
onClose && onClose()
}
return (
<View>
<Modal hideCloseButton={false} onClose={onCloseModal} top={'50%'} closePos={{ right: 0, top: 0 }}>
<View className={styles.container} style={
{ width: HELP_CONFIG[type].width / 100 + 'rem',
height: HELP_CONFIG[type].height / 100 + 'rem',
backgroundImage: `url(${HELP_CONFIG[type].bg})`
}}>
<Text className={styles['help-title']}>{
type !== HELP_MODAL_TYPE.REWARDS ? HELP_CONFIG[type].title :
`恭喜你成功邀请${helpInfo.inviteCount}名好友\n获得XXX奖励` }
</Text>
<Image src={HELP_CONFIG[type].btnTxt}
style={{ marginBottom: HELP_CONFIG[type].marginBottom / 100 + 'rem' }}
className={styles['help-btn']} onClick={() => onHandleHelp()} />
</View>
</Modal>
{
<member-modal
expend={memberModalVisible}
onClose={() => closeMemberModal()}
onAuthFail={onAuthFail}
onAuthSuccess={onAuthSuccess}
/>
}
</View>
)
}
\ No newline at end of file
.container {
.wh(570px,398px);
position: relative;
.image-property();
display: flex;
justify-content: flex-end;
flex-direction: column;
align-items: center;
}
.pos-row-center() {
position: absolute;
left: 50%;
transform: translate(-50%,0);
}
.help-title {
font-size: 38px;
font-family: SourceHanSansCN;
font-weight: 500;
color: #573F54;
line-height: 54px;
margin-bottom: 63px;
text-align: center;
}
.help-btn {
.wh(328px,76px);
margin-bottom: 121px;
}
import React, { useState } from 'react'
import { View, Text, Image } from '@tarojs/components'
import Modal from '@/components/_base/Modal/Modal'
import { useThrottle } from '@/hooks/useThrottle'
import useReceive from '@/hooks/useReceive'
import './JackpotModal.less'
const prizeItem = {
image:'',
name:'',
type:'', // 奖品类型
id:'', // 奖品id
_id:'',
ename:'', // 会员权益
benefitType:'' // 会员权益
}
const JackpotModal = (props) => {
const { receivePrize } = useReceive({ successMsg: '奖品发放成功,请前往我的奖品处进行使用', errorMsg: '奖品发放失败,请前往我的奖品处进行处理' })
const { onClose, top = '50%', bg = '', width = 600, height = 700, receive = true, prizeItem={}, showMember=()=>{} } = props
const [ memberModalVisible, setMemberModalVisible ] = useState(false)
const handleClick = useThrottle(async() => {
// 如果跳转我的奖品页面
if(!receive) {
onClose && onClose()
return;
}
const callObj = {
vipCallback:()=>{
setMemberModalVisible(true)
},
updateCallback:()=>{},
closeCallback:()=>{onClose()}
}
receivePrize(prizeItem,callObj);
})
// 入会弹窗
const onAuthSuccess = async() => {
setMemberModalVisible(false)
handleClick()
}
const onAuthFail = () => {
setMemberModalVisible(false)
}
const closeMemberModal = () => {
setMemberModalVisible(false)
}
return (
<>
<Modal onClose={onClose} top={top}>
<View className='jackpot_content' style={{ width: `${width / 100}rem`, height: `${height / 100}rem`, backgroundImage: `url(${bg})` }}>
<View className='title_box'>
<Text className='title'>恭喜您</Text>
</View>
<Text className='notify'>抽到了以下奖品</Text>
<View className='gift_box'>
<View className='gift'>
<Image src={prizeItem?.image} />
</View>
<Text className='gift_name'>{prizeItem?.name}</Text>
</View>
<View className='btn' onTap={handleClick}>领取奖励</View>
<Text className='hint'>奖品可在<Text className='hint_hot'>“我的奖品”</Text>中查看</Text>
</View>
</Modal>
{
<member-modal
expend={memberModalVisible}
onClose={() => closeMemberModal()}
onAuthFail={onAuthFail}
onAuthSuccess={onAuthSuccess}
/>
}
</>
)
}
export default JackpotModal
.jackpot_content {
background-color: #FFF;
.image-property();
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: inset 0.469px 0.883px 1px 0px rgba(249, 223, 203, 0.6);
.title_box {
display: flex;
justify-content: center;
align-items: center;
width: 322px;
height: 70px;
.title {
font-size: 36.364px;
color: #000
}
}
.notify {
margin-top: 45px;
}
.gift_box {
margin-top: 25px;
text-align: center;
.gift {
border-radius: 12px;
background-color: rgb(255, 244, 235);
width: 224px;
height: 225px;
overflow: hidden;
image {
width: 100%;
height: 100%;
}
}
.gift_name {
margin-top: 10px;
}
}
.btn {
margin-top: 40px;
width: 361px;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
}
}
\ No newline at end of file
## 参数
| 参数 | 说明 | 类型 | 默认值 |
| :--------- | :----------------------------- | :------- | :----- |
| width | 宽度 | Number | - |
| height | 高度 | Number | - |
| top | 定位 | String | - |
| bg | 背景 | String | false |
| prizesData | 奖品数据 | Object | false |
| receive | 领取按钮是回到页面还是直接领取 | Boolean | false |
| onClose | 关闭按钮的回调 | Function | false |
### 注
```javascript
prizesData = {
_id: "奖品的id",
type: "奖品的类型",
image: "奖品图片url",
name: "奖品名字"
}
```
## useReceive 用法
```javascript
const [ getReceive ] = useReceive({ receiveEnamePrize, receiveObjectPrize })
// 引入useReceive Hooks,调用时传入receiveEnamePrize(优惠券和积分接口), receiveObjectPrize(实物接口),返回一个处理函数。
const result = await getReceive({ prizeId, type })
// 在用户领取时调用它,传入奖品id以及type(符合PRIZE_TYPE枚举格式),返回一个promise对象,处理完毕后成功或者用户身份验证失败等会返回对象(message, stest),如果是上阶段传入的领取接口失败则不返回任何信息。
```
\ No newline at end of file
import JackpotModal from './JackpotModal'
export default JackpotModal
import { View, ScrollView, Image } from '@tarojs/components'
import React, { useState, useRef } from 'react'
import { noopFn } from '@/utils/util'
import Popup from '@/components/_base/Popup/Popup'
import styles from './TasksModal.module.less'
import { useTasks } from '@/hooks/useTasks'
import { TASK_CONFIG, TASK_STATUS } from '@/config/task.config'
const { icons, commonTxt, doTxt, rewardMsg } = TASK_CONFIG
export default function TasksModal(props) {
const { onClose = noopFn } = props
const [ memberShopVisible, setMemberShopVisible ] = useState(false)
const { taskList, handleItem, fetchTaskList } = useTasks(props)
const checkItem = useRef(null)
const handleTask = (itm) => {
checkItem.current = itm
handleItem(itm,() => {
setMemberShopVisible(true)
})
}
const onCloseMemberModal = () => {
setMemberShopVisible(false)
}
const onAuthSuccess = () => {
setMemberShopVisible(false)
checkItem.current && handleItem(checkItem.current)
}
const onAuthFail = () => {
setMemberShopVisible(false)
}
const onCloseModal = () => {
onClose && onClose()
}
return (
<>
<Popup onClose={onCloseModal} height='700rpx'>
<View className={styles['modal-container']}>
<View className={styles['modal-content']}>
<View className={styles['modal-content__title']}>任务模块</View>
<View className={styles['modal-content__list']}>
<ScrollView scroll-y='{{true}}' className={styles['modal-content__scroll']}>
{
taskList.map((item, i) => {
return (
<View className={styles['item']} key={'task_I' + i}>
<View className={styles['item__left']}>
<Image src={icons[item.key]} mode='widthFix' />
</View>
<View className={styles['item__left-label']}>
<View className={styles['item__left-title']}>{item.name}</View>
<View className={styles['item__left-reward']}>{rewardMsg + item.reward}</View>
</View>
<View className={`${styles['list-item__right']} ${styles['item__status-' + item.status]}`} onClick={() => handleTask(item)}>
{
item.status === TASK_STATUS.WAIT_DO ? (doTxt[item.key] || '去完成') : commonTxt[item.status]
}
</View>
</View>
)
})
}
</ScrollView>
</View>
</View>
</View>
</Popup>
{<member-modal expend={memberShopVisible} onClose={onCloseMemberModal} onAuthFail={onAuthFail} onAuthSuccess={onAuthSuccess} />}
</>
)
}
.task-modal-container { .modal-container {
width: 100%; width: 100%;
height: 700rpx; height: 700rpx;
position: absolute; position: absolute;
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
left: 0; left: 0;
background-color: #ffffff; background-color: #ffffff;
} }
.task-modal__close { .modal__close {
position: absolute; position: absolute;
right: 20rpx; right: 20rpx;
top: -80rpx; top: -80rpx;
...@@ -15,20 +15,20 @@ ...@@ -15,20 +15,20 @@
background: url('//yun.dui88.com/taobaomini/clientCTest/modal_close_btn.png') center center no-repeat; background: url('//yun.dui88.com/taobaomini/clientCTest/modal_close_btn.png') center center no-repeat;
background-size: 48rpx 48rpx; background-size: 48rpx 48rpx;
} }
.task-modal-content__title { .modal-content__title {
text-align: center; text-align: center;
margin: 30rpx 0; margin: 30rpx 0;
} }
.task-modal-content__list { .modal-content__list {
width: 100%; width: 100%;
height: 600rpx; height: 600rpx;
padding: 0 16rpx; padding: 0 16rpx;
} }
.task-modal-content__scroll { .modal-content__scroll {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.task-modal-content-item { .item {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
padding: 16rpx; padding: 16rpx;
border: 1rpx solid #eee; border: 1rpx solid #eee;
} }
.task-modal-content-item__left { .item__left {
width: 120rpx; width: 120rpx;
height: 120rpx; height: 120rpx;
display: flex; display: flex;
...@@ -44,22 +44,22 @@ ...@@ -44,22 +44,22 @@
align-items: center; align-items: center;
background-color: #eee; background-color: #eee;
} }
.task-modal-content-item__left image { .item__left image {
width: 80rpx; width: 80rpx;
} }
.task-modal-content-item__left-label { .item__left-label {
flex: 1; flex: 1;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
margin: 0 16rpx; margin: 0 16rpx;
} }
.task-modal-content-item__left-title { .item__left-title {
} }
.task-modal-content-item__left-reward { .item__left-reward {
margin-top: 10rpx; margin-top: 10rpx;
} }
.task-modal-content__list-item__right { .list-item__right {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
...@@ -68,13 +68,12 @@ ...@@ -68,13 +68,12 @@
background-color: #eee; background-color: #eee;
border-radius: 30rpx; border-radius: 30rpx;
} }
.item__status-1 {
.task-item__status-1 {
} }
.task-item__status-2 { .item__status-2 {
} }
.task-item__status-3 { .item__status-3 {
} }
\ No newline at end of file
import { View, Image, Text } from '@tarojs/components'
import React from 'react'
import Modal from '@/components/_base/Modal/Modal'
import { useThrottle } from '@/hooks/useThrottle'
import classnames from 'classnames'
import styles from './ThanksModal.module.less'
const thanksConfig = {
1: {
title: '很遗憾,未抽中奖品\n再接再厉',
btnTxt: '再抽一次',
btnImg: '//yun.duiba.com.cn/taobaomini/pike_call/thanks_btn.png',
icon: '//yun.duiba.com.cn/taobaomini/provideBeauty/close_btn.png',
desc: '奖品可在我的奖品中查看'
},
2: {
title: '很遗憾,未抽中奖品\n再接再厉',
btnTxt: '再抽一次',
icon: '//yun.duiba.com.cn/taobaomini/provideBeauty/close_btn.png',
desc: '奖品可在我的奖品中查看'
}
}
export default function ThanksModal(props) {
const { onHandle = () => {}, onClose, type = 1, hasClose = false, hasDesc = false } = props
const getContainerStyle = classnames(styles['thanks-container'],{
[`${styles['thanks-container-02']}`]: type === 2
})
// 标题样式
const getTitleStyle = classnames(styles['thanks-title'],{
[`${styles['thanks-title-02']}`]: type === 2
})
// 按钮样式
const getBtnStyle = classnames(styles['thanks-btn'],{
[`${styles['thanks-btn-02']}`]: type === 2
})
const onCloseModal = () => {
onClose && onClose()
}
return (
<Modal hideCloseButton={false} onClose={onCloseModal} top={'50%'}>
<View className={getContainerStyle}>
<Text className={getTitleStyle}>{thanksConfig[type].title}</Text>
<Image src={thanksConfig[type].btnImg} className={getBtnStyle} onClick={()=> onHandle(type)} />
{hasDesc && <View className={styles['thanks-desc']}>{thanksConfig[type].desc}</View>}
</View>
</Modal>
)
}
\ No newline at end of file
.thanks-container {
.wh(636px,826px);
position: relative;
.flex-col-bottom();
align-items: center;
.image('//yun.duiba.com.cn/taobaomini/pike_call/thanks_bg.png');
}
.thanks-container-02 {
.wh(570px,398px);
.image('//yun.duiba.com.cn/taobaomini/provideBeauty/prize/prize_bg_01.png');
}
.pos-row-center() {
position: absolute;
left: 50%;
transform: translate(-50%,0);
}
.thanks-title {
font-size: 40px;
font-family: SourceHanSansSC;
font-weight: bold;
color: #473F39;
line-height: 58px;
text-align: center;
margin-bottom: 46px;
}
.thanks-title-02 {
font-size: 40px;
font-family: SourceHanSansSC;
font-weight: bold;
color: #473F39;
line-height: 58px;
}
.thanks-btn {
.wh(437px,101px);
margin-bottom: 133px;
}
.thanks-btn-02 {
width: 172px;
height: 36px;
background: #F6F6F6;
border-radius: 18px;
font-size: 25px;
font-family: FZLTHProGlobal;
font-weight: 400;
color: #F4B45E;
position: absolute;
left: 180px;
bottom: 94px;
.flex-row-center();
margin-right: 30px;
}
.thanks-close {
.wh(38px,38px);
.pos-row-center();
bottom: -60px;
}
.thanks-desc{
position: absolute;
left: 160px;
bottom: 55px;
font-size: 20px;
font-family: FZLTHProGlobal;
font-weight: 400;
color: #1C1C1C;
text-align: center;
}
\ No newline at end of file
export default {
env: 'test', // 云函数环境 test 测试环境 online 线上环境
requestType: 'yapi', // cloud: 云函数, yapi: yapi 接口, ams: ams接口,tornadoAPI: 风驰台接口
cloudName: 'CommonToC', // 主云函数项目名 CommonToC
sellerId: '2207644377875',
tornadoAPI: 'https://tornado.duibadev.com.cn/tbServer/api?db=db3000000038851072&proxyIp=172.16.130.158', // 新增:当requestType: 'tornadoAPI'时,找服务端提供地址
defaultActivityId: '60642aafc752d118247c2a2d' // 默认活动id
}
export const BROWSE_CONFIG = {
taskType: 'browseGoods',
type: '01',
bg: '//yun.dui88.com/taobaomini/clientCTest/goods_bg@2x.png',
head: {
image: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_title@2x.png',
width: 750,// 头部banner/title 宽度
height: 120,// 头部banner/title 高度
},
image: {
img: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_img@2x.png'
},
color: '#181818',
countDown: '',
isMock: false, // 是否使用mock数据
}
/*
浏览页倒计时标识
DEFAULT: 默认为空,不展示
PICTURE: loading进度条显示倒计时
NUMBER: 数字倒计时(15s)
*/
export const BROWSE_COUNTDOWN_OBJ = {
PICTURE:'PICTURE',
NUMBER:"NUMBER",
DEFAULT:""
}
\ No newline at end of file
export const CART_CONFIG = {
taskType: 'cartGoods',
type: 1, // 1 单个商品可重复完成 2 单个商品只可完成一次
bg: '//yun.dui88.com/taobaomini/clientCTest/goods_bg@2x.png',
head: {
image: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_title@2x.png',
width: 750,// 头部banner/title 宽度
height: 120,// 头部banner/title 高度
},
image: {
collect: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_collection@2x.png',
no_collect: '//yun.duiba.com.cn/taobaomini/clientCTest/collection_no_collect@2x.png',
},
color: '#181818'
}
// 任务完成类型
export const CART_TYPE = {
ONE: 1, // 单个商品可重复完成
MORE: 2 // 单个商品只可完成一次
}
\ No newline at end of file
export default {
taskType: 'collectGoods',
type: '01',
bg: '//yun.dui88.com/taobaomini/clientCTest/goods_bg@2x.png',
head: {
image: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_title@2x.png',
width: 750,// 头部banner/title 宽度
height: 120,// 头部banner/title 高度
},
image: {
collect: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_collection@2x.png',
no_collect: '//yun.duiba.com.cn/taobaomini/clientCTest/collection_no_collect@2x.png'
},
color: '#181818'
}
\ No newline at end of file
export default {
env: 'test', // 云函数环境 test 测试环境 online 线上环境
requestType: 'cloud', // cloud: 云函数, yapi: yapi 接口, ams: ams接口,tornadoAPI: 风驰台接口
cloudName: 'testNewS', // 主云函数项目名 CommonToC
sellerId: '2207644377875',
tornadoAPI: 'https://tornado.duibadev.com.cn/tbServer/api?db=db3000000038851072&proxyIp=172.16.130.158', // 新增:当requestType: 'tornadoAPI'时,找服务端提供地址
defaultActivityId: '610cff3f086e659a96b7336c' // 默认活动id
}
\ No newline at end of file
// 助力弹窗类型
export const HELP_MODAL_TYPE = {
HELP: 'HELP', // 邀请助力
FAIL: 'FAIL', // 助力失败
REWARDS: 'REWARDS', // 助力信息展示
}
// 助力弹窗是否已弹
export const HELP_MODAL_SHOW = {
SHOW: 1, // 已弹
NOT_SHOW: 2, // 未弹
}
// 助力配置
export const HELP_CONFIG = {
KEY: 'invites',
HELP: {
width: 639,
height: 730,
bg: '//yun.dui88.com/taobaomini/pike_call/m_help_bg.png',
btnTxt: '//yun.dui88.com/taobaomini/pike_call/m_help_btn_01.png',
title: '您的好友正在参与XXXX\n快来一起参与吧~',
marginBottom: 80
},
FAIL: {
width: 639,
height: 701,
bg: '//yun.dui88.com/taobaomini/pike_call/m_help_bg_02.png',
btnTxt: '//yun.dui88.com/taobaomini/pike_call/m_help_btn_02.png',
title: '您已是会员',
marginBottom: 80
},
REWARDS: {
width: 639,
height: 800,
bg: '//yun.dui88.com/taobaomini/pike_call/m_help_bg_03.png',
btnTxt: '//yun.dui88.com/taobaomini/pike_call/m_help_btn_02.png',
title: '',
marginBottom: 120
}
}
\ No newline at end of file
export default {
}
\ No newline at end of file
// 领取奖品状态
export const DRAW_STATUS = {
// 待领取
WAITAWARD: 1,
// 处理中
PROCESSING: 2,
// 领取成功
SUCCESS: 3,
// 领取失败
FAIL: 4,
// 已过期
EXPIRED: 5,
// 重新领取
RETRY: 6
}
// 奖品类型
export const PRIZE_TYPE = {
ENAME: 1,
CREDITS: 2,
OBJECT: 3,
THANKS: 5
}
// 权益类型
export const BENEFIT_TYPE = {
ENAME: 1,// 普通权益
MEMBER: 2 // 会员权益
}
// 我的奖品配置
export const PRIZE_CONFIG = {
bg: '//yun.duiba.com.cn/aurora/assets/e13fab3165d3a6bb4cbe8827e8f0fd41084b0091.png', // 背景图
head: {
image: '//yun.dui88.com/duiba-components-c-myprizeprize_title.png',// 标题
width: 208,// 头部banner/title 宽度
height: 36,// 头部banner/title 高度
},
copyIcon: '//yun.dui88.com/duiba-components-c-myprizemyprize_copy_code.png', // 复制
receiveBtn: '//yun.duiba.com.cn/bestoreBox/prize_received.png', // 已领取 权益类奖品未配置 uesUrl使用
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', // 去使用(除实物奖品外)
},
orderStatus: {
1: '//yun.dui88.com/duiba-components-c-myprizeget_now.png', // 去支付(除实物奖品外)
3: '//yun.duiba.com.cn/bestoreBox/prize_received.png', // 已领取
},
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' // 领取失败
},
logisticsImage: {
shipped: '//yun.dui88.com/duiba-components-c-myprizeprize_shipped.png', // 已发货物理弹窗
notShipped: '//yun.dui88.com/duiba-components-c-myprizenotshipped.png' // 待发货物流弹窗
},
btnStyle: {
width: '150rpx'
},
contentTop: '208rpx',
blankTxt: '暂无奖品',
hasLogistic: false, // 是否展示物流信息
isShowPirzeId: false // 是否显示奖品编号
}
\ No newline at end of file
export default {
taskType: 'orderGoods',
type: '01',
bg: '//yun.dui88.com/taobaomini/clientCTest/goods_bg@2x.png',
head: {
image: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_title@2x.png',
width: 750,// 头部banner/title 宽度
height: 120,// 头部banner/title 高度
},
image: {
img: '//yun.duiba.com.cn/taobaomini/clientCTest/goods_img@2x.png'
},
color: '#181818'
}
\ No newline at end of file
// 任务状态
export const TASK_STATUS = {
WAIT_DO: 1, // 未完成
WAIT_RECEIVE: 2, // 待领取
DONE: 3 // 已完成
}
// 任务频率类型常量
export const TASK_RATE_TYPE = {
FOREVER: 1,
EVERYDAY: 2,
NOLIMIT: 3
}
// 浏览商品类型
export const BROSE_GOOD_TYPE = {
PAGE: 1, // 浏览集合页15s
CLICK: 2, // 点击就算完成任务
DETAIL: 3 // 浏览指定商品详情页15s
}
// 购买商品类型
export const ORDER_GOOD_TYPE = {
PAGE: 1, // 集合页下单
SHOP: 2, // 跳转店铺
}
// 任务展示形式
export const TASK_SHOW_TYPE = {
MODAL: 1, // 弹窗
LIST: 2 // 列表
}
export const TASK_CONFIG = {
icons: { // 任务图标
follow: '//yun.duiba.com.cn/baicaoweiFarmer/follow.png',
member: '//yun.duiba.com.cn/baicaoweiFarmer/memberStar1.png',
invites: '//yun.duiba.com.cn/baicaoweiFarmer/invites.png',
jumpLink: '//yun.duiba.com.cn/baicaoweiFarmer/browseGoodsLink.png',
browseGoods: '//yun.duiba.com.cn/baicaoweiFarmer/browseGoods.png',
orderGoods: '//yun.duiba.com.cn/baicaoweiFarmer/orderGoods.png',
collectGoods: '//yun.duiba.com.cn/baicaoweiFarmer/orderGoods.png',
sign: '//yun.duiba.com.cn/baicaoweiFarmer/orderGoods.png',
exchangeCredits: '//yun.duiba.com.cn/baicaoweiFarmer/orderGoods.png',
share: '//yun.duiba.com.cn/baicaoweiFarmer/orderGoods.png'
},
commonTxt: {
1: '去完成',
2: '待领取',
3: '已完成'
},
doTxt: {
follow: '立即关注',
invites: '去邀请',
member: '1秒入会',
sign: '立即签到',
jumpLink: '去完成',
browseGoods: '去完成',
orderGoods: '去完成',
collectGoods: '去完成'
},
successMsg: '领取成功,奖励值+',
rewardMsg: '抽盒次数+',
rewardsKey: 'totalHotValue',
browseGoodType: 'browseGoods',// 临时使用 browse | browseGoods
isMock: false, // 是否使用mock数据
jumpType: 1, // 1 跳转浏览15s 才算完成 2 跳转即算完成
browseTime: 15, // 浏览时间
browseType: 1, // 1 浏览15s 才算完成 2 点击就算完成任务 3 浏览指定商品详情页 15s
showTaskType: 1,// 1 弹窗 2 列表
orderType: 1, // 1 集合页下单 2 跳转店铺
}
\ No newline at end of file
...@@ -4,48 +4,6 @@ export const SHARE_CONFIG = { ...@@ -4,48 +4,6 @@ export const SHARE_CONFIG = {
desc: '分享测试描述', desc: '分享测试描述',
imageUrl: '' // 分享图片URL imageUrl: '' // 分享图片URL
} }
// 领取奖品状态
export const DRAW_STATUS = {
// 待领取
WAITAWARD: 1,
// 处理中
PROCESSING: 2,
// 领取成功
SUCCESS: 3,
// 领取失败
FAIL: 4,
// 已过期
EXPIRED: 5,
// 重新领取
RETRY: 6
}
// 奖品类型
export const PRIZE_TYPE = {
ENAME: 1,
CREDITS: 2,
OBJECT: 3,
THANKS: 5
}
// 我的奖品状态
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 = { export const ACTIVITY_STATUS = {
NO_EXIST: 0, // 不存在 NO_EXIST: 0, // 不存在
...@@ -53,49 +11,30 @@ export const ACTIVITY_STATUS = { ...@@ -53,49 +11,30 @@ export const ACTIVITY_STATUS = {
NO_START: 2, // 未开始 NO_START: 2, // 未开始
END: 3 // 已经结束 END: 3 // 已经结束
} }
// 店铺ID
export const SHOP_ID = '60014226'
// 任务频率类型常量 /* 背景音乐 应用:牧场消消乐有效,其他应用需要重新上传文件 */
export const TASK_RATE_TYPE = { export const BGMUSIC_URL = {
FOREVER: 1, MUSIC:'cloud://FBE3C154661ACEEBA164E8B70B4D71E2/yoga bgm.mp3'
EVERYDAY: 2,
NOLIMIT: 3
} }
/* 点击音效 */
// 任务状态 export const CLOUD_OBJ = {
export const TASK_STATUS = { '1':'cloud://0BD38A1B739CCF6A552396F3FE2D5A09/开始-音效.mp3',
WAIT_DO: 1, // 未完成 '2':'cloud://93FBC274B6B43CE37B0A70DF8C1AFA52/答题-音效.mp3',
WAIT_RECEIVE: 2, // 待领取 '3':'cloud://B8F0BA22BBA74F8FEA261E1CC9D55355/欢呼声.mp3'
DONE: 3 // 已完成
} }
export const TASK_CONFIG = {
taskIcon: { // 任务图标
follow: '//yun.duiba.com.cn/baicaoweiFarmer/follow.png', /*
member: '//yun.duiba.com.cn/baicaoweiFarmer/memberStar1.png', 浏览页倒计时标识
invites: '//yun.duiba.com.cn/baicaoweiFarmer/invites.png', DEFAULT: 默认为空,不展示
jumpLink: '//yun.duiba.com.cn/baicaoweiFarmer/browseGoodsLink.png', PICTURE: loading进度条显示倒计时
browseGoods: '//yun.duiba.com.cn/baicaoweiFarmer/browseGoods.png', NUMBER: 数字倒计时(15s)
orderGoods: '//yun.duiba.com.cn/baicaoweiFarmer/orderGoods.png', */
collectGoods: '//yun.duiba.com.cn/baicaoweiFarmer/orderGoods.png', export const BROWSE_COUNTDOWN_OBJ = {
sign: '//yun.duiba.com.cn/baicaoweiFarmer/orderGoods.png', PICTURE:'PICTURE',
exchangeCredits: '//yun.duiba.com.cn/baicaoweiFarmer/orderGoods.png' NUMBER:"NUMBER",
}, DEFAULT:""
commonTaskTxt: {
1: '去完成',
2: '待领取',
3: '已完成'
},
doTaskTxt: {
follow: '立即关注',
invites: '去邀请',
member: '1秒入会',
sign: '立即签到',
jumpLink: '去完成',
browseGoods: '去完成',
orderGoods: '去完成',
collectGoods: '去完成'
},
browseType: 2, // 1 跳转浏览15s 才算完成 2 跳转即算完成
browseTime: 15, // 浏览时间
jumpType: 1 // 1 浏览15s 才算完成 2 点击就算完成任务
} }
\ No newline at end of file
/**
* @description 音乐
*/
import React, { useState, useEffect } from "react";
import { playBgMusic1 } from "@/utils/audio";
const app = getApp();
/**
* @description 一个页面不能同时create两个音频文件
* @param {*} url cloud文件链接
* @param {*} isBgMusic 是否自动调用(背景音乐自动调用),非背景音乐(点击音效)手动调用
* @returns
*/
export const useAudio = (url, isBgMusic=false) => {
const [musicUrl, setMusicUrl] = useState();
// 若需要控制所有的音效状态,app上自定义一个变量
const [openStatus,setOpenStatus] = useState(true);
useEffect(() => {
getMusicInfo();
}, []);
useEffect(()=>{
if(isBgMusic){
console.log('enter use audio function with bg music');
playAudio();
}
},[musicUrl,openStatus])
/**
* @description 获取音频url链接
*/
const getMusicInfo = async () => {
const resultSrc = await app.cloud.file.getTempFileURL({ fileId: url });
console.log('resultSrc--------hook',resultSrc);
if (resultSrc[0]){
setMusicUrl(resultSrc[0].url);
}
};
/**
* @description 播放音效
* @param {boolean} selfStatus 是否单独控制音效状态
* @param {function} callbackFn 播放之后的回调
*/
const playAudio = (callbackFn=()=>{}) => {
console.log('musicUrl',musicUrl,'selfStatus',openStatus)
musicUrl && playBgMusic1(openStatus, musicUrl, callbackFn,isBgMusic);
};
return {
playAudio,
openStatus,
setOpenStatus,
musicUrl
};
};
##### useAudio
```jsx
const {playAudio, openStatus, setOpenStatus, musicUrl} = useAudio(url,isBgMusic)
```
##### 入参
| 入参 | 类型 | 描述 | 默认值 | 备注 |
| :--- | :---: | :---: |:---: | :---: |
| url | String | cloud开头的链接 | 'cloud:xxxx.mp3' | 当前应用云存储上的链s接才有效 |
| isBgMusic | Boolean | 是否自动加载背景音乐/音效 | false | / |
##### 出参
| 出参 | 类型 | 描述 | 默认值 | 备注 |
| :--- | :---: | :---: |:---: | :---: |
| playAudio | Function | 播放音乐/音效 | 有回调函数callback | callback默认为空函数 |
| openStatus | Boolean | 默认开启播放 | true | / |
| setOpenStatus | Function | 改变开始/暂停状态 | / | true/false都会调用playMusic1方法|
| musicUrl | String | cloud文件得到的音频url链接 | 'https:xxxx.mp3' | 音频文件url链接|
+ 使用
```jsx
import {useAudio} from '@/hooks/useAudio'
import {CLOUD_OBJ} from '@/const'
export const Index = () => {
const {playAudio,musicUrl,setOpenStatus,openStatus} = useAudio(CLOUD_OBJ['1'])
const gotoMainPage = () => {
setOpenStatus(!openStatus)
playAudio(()=>{
// ...场景:点击有音效,跳转的页面有背景音乐
navigateTo({url:`/pages/other/other`})
});
// TODO...
}
return(
<>
<View onTap={gotoMainPage}>点击音效button</View>
</>
)
}
import {useAudio} from '@/hooks/useAudio'
import {BGMUSIC_URL} from '@/const'
export const Other = () => {
// 自动播放背景音乐
const {playAudio,setOpenStatus,openStatus} = useAudio(CLOUD_OBJ['1'],true)
return(
<View>
other page
</View>
)
}
```
+ 所有音效状态统一, 则需要在useAudio中设置一个全局变量(app.openStatus)
+ 音效缓存,音效关闭,下次进入音效还是关闭
```js
let status = my.getStorageSync({ key: 'musicStatus' });
console.log('status',status);
if(status.data == undefined || status.data === null || status.data === ''){
app.openStatus = true;
my.setStorageSync({ key: 'musicStatus',data:true })
} else if(status?.data == true){
app.openStatus = true;
} else if(status?.data == false){
app.openStatus = false;
}
const {openStatus,setOpenStatus} = useAudio(CLOUD_OBJ['2'],true)
/**
* @description 切换音效状态
*/
const handleMusic = () => {
setOpenStatus(status => !status)
/* 切换音效状态 */
app.openStatus = !app.openStatus;
let musicStatus = my.getStorageSync({ key: 'musicStatus' }).data;
console.log('musicStatus-----初始值---->>>>>>>>>>',musicStatus);
my.setStorageSync({ key: 'musicStatus',data:!musicStatus });
let musics = my.getStorageSync({ key: 'musicStatus' }).data;
console.log('musicStatus----改变值----->>>>>>>>>>',musics);
}
```
+ 背景音效后台关闭
```jsx
const {openStatus,setOpenStatus} = useAudio(CLOUD_OBJ['2'],true)
useDidHide(()=>{
// 停止背景音乐
openStatus && setOpenStatus(false)
})
useDidShow(()=>{
// 播放背景音乐(缓存处理)
let musicStatus = my.getStorageSync({ key: 'musicStatus' }).data;
if(musicStatus){
setOpenStatus(true)
}
// 非缓存 默认开启
setOpenStatus(true)
})
```
\ No newline at end of file
import { getAuthUserInfo } from 'tbcc-sdk-ts/lib/core/tbFn' import { getAuthUserInfo } from 'tbcc-sdk-ts/lib/core/tbFn'
import { useEffect } from 'react' import { useEffect } from 'react'
import { commonToast } from 'tbcc-sdk-ts/lib/core/tb'
// 授权 // 授权
export function useAuth(callback = noopFn) { export function useAuth(callback = noopFn) {
const authUserInfo = async() => { const authUserInfo = async() => {
const info = await getAuthUserInfo().catch(err => { const info = await getAuthUserInfo().catch(err => {
console.log('未授权成功', err) console.log('未授权成功', err)
commonToast('点击右上角,打开授权开关,重进活动即可参与')
}) })
if (info) { if (info) {
callback(info) callback(info)
......
import { useEffect, useState } from 'react' import { useEffect, useState, useReducer } from 'react'
import { useRouter } from '@tarojs/taro' import { useRouter } from '@tarojs/taro'
import { useLoginInfoModel } from '../store/loginInfo' import { useLoginInfoModel } from '../store/loginInfo'
import API from '../api' import API from '../api'
import config from '../config' import config from '@/config/config'
import { useAuth } from './useAuth' import { useAuth } from './useAuth'
import { noopFn } from '@/utils/util' import { noopFn } from '@/utils/util'
import { checkShopFavoredStatus } from 'tbcc-sdk-ts/lib/core/tb' import { checkShopFavoredStatus } from 'tbcc-sdk-ts/lib/core/tb'
import { checkIsMember } from 'tbcc-sdk-ts/lib/utils'
const { login } = API const { login } = API
...@@ -24,17 +25,19 @@ export function useLogin(callback = noopFn) { ...@@ -24,17 +25,19 @@ export function useLogin(callback = noopFn) {
const doLogin = async (authInfo) => { const doLogin = async (authInfo) => {
const isFollow = await checkShopFavoredStatus(config.sellerId) const isFollow = await checkShopFavoredStatus(config.sellerId)
const isVip = await checkIsMember()
const { nickName: userNick, avatar } = authInfo const { nickName: userNick, avatar } = authInfo
const { inviteId } = params const { inviteId } = params
const res = await login({ userNick, avatar, isFollow, inviteId }) const res = await login({ userNick, avatar, isFollow, inviteId, isVip })
if (res?.success) { if (res?.success) {
const { newUser,firstLoginToday, openId } = res.data
const _loginInfo = { const _loginInfo = {
...res.data, newUser,firstLoginToday, openId,
userNick, userNick,
avatar, avatar,
inviteId inviteId
} }
setLoginInfo(_loginInfo) setLoginInfo({ ..._loginInfo, isLogin: true })
setLoginStatus(true) setLoginStatus(true)
console.log('isLogin :>> ', true) console.log('isLogin :>> ', true)
callback(_loginInfo) callback(_loginInfo)
......
import { useCallback } from 'react'
import { checkIsMember } from 'tbcc-sdk-ts/lib/utils'
import { commonToast } from 'tbcc-sdk-ts/lib/core/tb'
import API from '@/api'
import {receiveEnamePrize, receiveMemberEname, receiveObjectPrize } from 'tbcc-sdk-ts/lib/request/capiFn'
import { PRIZE_TYPE, BENEFIT_TYPE } from '@/config/myPrize.config'
import config from '@/config/config'
const useReceive = (props) => {
const { successMsg = '领取成功', errorMsg = '' } = props
const receivePrize = async(item, callbackObj={vipCallback:()=>{},updateCallback:()=>{},closeCallback:()=>{}}) => {
const app = getApp();
const { activityId } = app
const { type, id, ename = '', benefitType } = item
if(type === PRIZE_TYPE.CREDITS || (type === PRIZE_TYPE.ENAME && benefitType === BENEFIT_TYPE.MEMBER)) {
const isVip = await checkIsMember()
if(!isVip) {
commonToast('需加入会员才能领取成功哦', 1000, ()=>{callbackObj.vipCallback && callbackObj.vipCallback()})
return;
}
}
const receiveFn = {
[PRIZE_TYPE.ENAME]: async () => {
if(benefitType === BENEFIT_TYPE.MEMBER) {
await receiveMemberEname(API.receiveMemberEname,{params: { sellerId: config.sellerId, ename, id }, successMsg, errorMsg })
}else {
await receiveEnamePrize(API.receiveEnamePrize, {params: { activityId, id }, successMsg, errorMsg })
}
},
[PRIZE_TYPE.OBJECT]: async () => {
await receiveObjectPrize(API.receiveObjectPrize, {params: { activityId, id }, successMsg, errorMsg })
},
[PRIZE_TYPE.CREDITS]: async () => {
await receiveEnamePrize(API.receiveEnamePrize, {params: { activityId, id }, successMsg, errorMsg })
}
}
await receiveFn[type]()
// 更新奖品列表
callbackObj.updateCallback && callbackObj.updateCallback();
callbackObj.closeCallback && callbackObj.closeCallback();
}
return {
receivePrize
}
}
export default useReceive
import API from '@/api' import API from '@/api'
import { useState, useEffect } from 'react' import { useThrottle } from '@/hooks/useThrottle'
import { useRequest } from './useRequest' import { useState, useEffect, useRef } from 'react'
import { useDidShow } from '@tarojs/taro'
import { useLoginInfoModel } from '@/store'
import { checkIsMember } from 'tbcc-sdk-ts/lib/utils'
import { commonToast, navigateTo, favorShop, checkShopFavoredStatus, navigateToOutside, showSharePanel, navigateToTaobaoPage } from 'tbcc-sdk-ts/lib/core/tb'
import { TASK_DATA } from '@/mock'
import { SHOP_ID } from '@/const'
import config from '@/config/config'
import { TASK_CONFIG, ORDER_GOOD_TYPE, BROSE_GOOD_TYPE, TASK_STATUS, TASK_SHOW_TYPE } from '@/config/task.config'
export function useTasks() { const { jumpType, browseTime, showTaskType, browseType, orderType, successMsg, isMock = false, browseGoodType } = TASK_CONFIG
const { data, run: fetchTaskList } = useRequest(API.getTaskList, {}, { manual: false }) export function useTasks(props) {
const { onUpdate } = props
const app = getApp()
const { loginInfo } = useLoginInfoModel()
const [ taskList, setTaskList ] = useState(isMock ? TASK_DATA : [])
const [ currentTaskType, setCurrentTaskType ] = useState('')
const preBrowseTime = useRef(null)
// 页面显示
useDidShow(() => {
if (app.isFlashTask) {
app.isFlashTask = false
if (preBrowseTime.current && ((Date.now() - preBrowseTime.current) / 1000) >= browseTime && currentTaskType !== browseGoodType) {
doCompleteTaskHandle(currentTaskType)
preBrowseTime.current = null
} else if(preBrowseTime.current && ((Date.now() - preBrowseTime.current) / 1000) < browseTime) {
commonToast(`未达到${browseTime}秒~`)
preBrowseTime.current = null
}else {
fetchTaskList()
}
}
})
// 数据初始化
useEffect(() => {
if(showTaskType === TASK_SHOW_TYPE.LIST && loginInfo.isLogin) {
fetchTaskList()
}else if(showTaskType === TASK_SHOW_TYPE.MODAL){
fetchTaskList()
}
},[loginInfo])
// 任务数据获取
const fetchTaskList = async () => {
const isVip = await checkIsMember()
const { success, data } = await API.getTaskList({ isVip })
if(success && data) {
const { list = [] } = data
const _list = list.map(itm => {
return {
...itm,
rateType: itm?.rateType || itm?.taskRateType,
reward: itm?.reward || itm?.rewards,
key: itm?.key || itm?.taskType,
name: itm?.name || itm?.title,
}
})
setTaskList(_list)
}
}
// 点击任务项
const handleItem = useThrottle(async(item, callbackFn) => {
const { status, key } = item
const tapFn = {
[TASK_STATUS.WAIT_RECEIVE]: async() => {
const { success, data } = await API.receiveTask({ key, rewardsKey: TASK_CONFIG.rewardsKey })
if (success && data) {
const { rewards } = data
commonToast(`${successMsg}${rewards}`)
fetchTaskList()
onUpdate && onUpdate()
}
},
[TASK_STATUS.WAIT_DO]: () => completeTask(item, callbackFn)
}
tapFn[status] && tapFn[status]()
})
// 完成任务处理
const completeTask = async(item, callbackFn = () => {}) => {
const { key, url, itemIds, keepTime } = item
setCurrentTaskType(key)
const completeFn = {
member: async () => {
const isVip = await checkIsMember()
if(!isVip) {
callbackFn()
return;
}
doCompleteTaskHandle(key,isVip)
},
invites: () => showSharePanel(),
follow: async () => {
// 已关注店铺直接完成任务
const isFollow = await checkShopFavoredStatus(config.sellerId)
if (isFollow) {
doCompleteTaskHandle(key)
return;
}
// 未关注店铺直接关注并完成任务
const checkFollow = await favorShop(config.sellerId)
if(checkFollow) {
doCompleteTaskHandle(key)
return;
}
},
// 跳转任务 https://www.feizhu.com
jumpLink: async() => {
navigateToOutside(url)
if (jumpType === 2) {
doCompleteTaskHandle(key)
} else {
app.isFlashTask = true
preBrowseTime.current = Date.now()
}
// await API.addStat({ type: 'BROWSE_LIVE'})
},
browseGoods: async() => {
app.isFlashTask = true
if(browseType === BROSE_GOOD_TYPE.PAGE) preBrowseTime.current = Date.now()
navigateTo(`/pages/packageGood/browseGoods/browseGoods?itemIds=${itemIds}&keepTime=${keepTime}`)
},
orderGoods: async() => {
app.isFlashTask = true
if(orderType === ORDER_GOOD_TYPE.PAGE) {
navigateTo(`/pages/packageGood/orderGoods/orderGoods?itemIds=${itemIds}`)
}else {
await navigateToTaobaoPage(SHOP_ID)
}
},
collectGoods: async() => {
app.isFlashTask = true
navigateTo(`/pages/packageGood/collectGoods/collectGoods?itemIds=${itemIds}`)
},
sign: () => doCompleteTaskHandle(key),
share: () => {
showSharePanel();
setTimeout(() => {
doCompleteTaskHandle(key)
}, 2000);
}
}
completeFn[key] && completeFn[key]()
}
// 完成任务接口调用
const doCompleteTaskHandle = async(key,isVip) => {
const { success } = await API.completeTask({ key, isVip })
if (success) {
await fetchTaskList()
}
}
return { return {
taskList: data?.list || [], taskList,
handleItem,
fetchTaskList fetchTaskList
} }
} }
const testLottie = require('./lottieData') const testLottie = require('./lottieData.json')
export default { export default {
1: testLottie 1: testLottie
......
// 任务数据模拟
export const TASK_DATA = [
{
taskType: 'follow',
rewards: 100,
times: 1,
taskRateType: 1,
todayCompleteTimes: 0,
status: 1,
title: '关注店铺',
},
{
taskType: 'orderGoods',
rewards: 100,
times: 1,
taskRateType: 1,
todayCompleteTimes: 0,
status: 1,
title: '下单任意商品'
},
{
taskType: 'invites',
rewards: 100,
times: 1,
taskRateType: 3,
todayCompleteTimes: 0,
status: 1,
title: '邀请好友'
},
{
taskType: 'sign',
rewards: 100,
times: 1,
taskRateType: 1,
todayCompleteTimes: 0,
status: 1,
title: '每日签到'
},
{
taskType: 'browseGoods',
rewards: 50,
times: 1,
taskRateType: 1,
todayCompleteTimes: 0,
status: 1,
title: '每日浏览宝贝'
},
{
taskType: 'cart',
rewards: 50,
times: 1,
taskRateType: 1,
todayCompleteTimes: 0,
status: 1,
title: '加购商品'
},
{
taskType: 'member',
rewards: 200,
times: 1,
taskRateType: 1,
todayCompleteTimes: 0,
status: 1,
title: '加入会员 '
},
{
taskType: 'share',
rewards: 80,
times: 1,
taskRateType: 1,
todayCompleteTimes: 0,
status: 1,
title: '分享活动'
},
{
taskType: 'jumpLink',
rewards: 50,
times: 1,
taskRateType: 2,
todayCompleteTimes: 0,
status: 1,
title: '每日观看直播'
}
]
// 排行榜
export const RANK_LIST = [
{
rank: 1,
userNick: '用户昵称用户昵称用户昵称',
hotValue: 98882892
},
{
rank: 2,
userNick: '用户昵称',
hotValue: 98882892
},
{
rank: 3,
userNick: '用户昵称',
hotValue: 98882892
},
{
rank: 4,
userNick: '用户昵称',
hotValue: 98882892
},
{
rank: 5,
userNick: '用户昵称',
hotValue: 98882892
},
{
rank: 6,
userNick: '用户昵称',
hotValue: 98882892
},
{
rank: 7,
userNick: '用户昵称',
hotValue: 98882892
},
{
rank: 8,
userNick: '用户昵称',
hotValue: 98882892
},
{
rank: 9,
userNick: '用户昵称',
hotValue: 98882892
},
{
rank: 10,
userNick: '用户昵称',
hotValue: 98882892
},
{
rank: 11,
userNick: '用户昵称',
hotValue: 98882892
},
{
rank: 12,
userNick: '用户昵称',
hotValue: 98882892
}
]
// 档次
export const PROGRESS_LIST = [10000,30000,50000,100000]
// 商品列表
export const GOOD_LIST = [
{
"itemId":41762748280,
"image":"https://img.alicdn.com/bao/uploaded/i1/2032870312/O1CN01oVcb3x1EAwGvKOhgC_!!2032870312-0-picasso.jpg",
"price":"89.90",
"name":"避孕套超薄中号冈本官方正品旗舰店安全套套子避育男用持润久0.01",
"url":"https://item.taobao.com/item.htm?id=41762748280&amp;spm=2015.32819095.0.0",
"desc":"&lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01DypJaF1EAwCLTysGt_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01GMKADp1EAwCHgCziU_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01mhBuAj1EAwCPBvsEt_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01m96nH81EAwCJP9jFC_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01u9zYTu1EAwCrf2mpO_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01cKa55c1EAwCHEA0aZ_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01Fs58Mj1EAwCKIhSkW_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN011AVYEO1EAwCO1aTxl_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01FDIokI1EAwC34q9Rv_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt;&lt;br&gt; &lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01JNEZb41EAwF9ZpHvM_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01I9m2SM1EAwCUjIVLB_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt;"
},
{
"itemId":637727538858,
"image":"https://img.alicdn.com/bao/uploaded/i1/2032870312/O1CN01Sr35MC1EAwGIQZZub_!!0-item_pic.jpg",
"price":"94.90",
"name":"冈本旗舰店0.03白金超薄避孕套系列6片装003套套男用安全成人用品",
"url":"https://item.taobao.com/item.htm?id=637727538858&amp;spm=2015.32819095.0.0",
"desc":"&lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01CD1sXk1EAwAVDtqrK_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01ZXWOHW1EAwC2Mgi4B_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01pDWAa81EAwANGIMgD_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01FDIokI1EAwC34q9Rv_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01qEQRnn1EAwC4wD3yV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt;&lt;br&gt; &lt;/p&gt; &lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01wykYTn1EAwCSuA1fV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt;"
},
{
"itemId":630862853821,
"image":"https://img.alicdn.com/bao/uploaded/i4/2032870312/O1CN017ywnYM1EAwGgKVjwJ_!!0-item_pic.jpg",
"price":"179.90",
"name":"冈本官方旗舰店超薄避孕套超值量贩装男用持久情趣套套正品安全套",
"url":"https://item.taobao.com/item.htm?id=630862853821&amp;spm=2015.32819095.0.0",
"desc":"&lt;p style=\"text-align: center;\"&gt; &lt;/p&gt; &lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01h16kYf1EAwELr3jxo_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01FDIokI1EAwC34q9Rv_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01qEQRnn1EAwC4wD3yV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01I9m2SM1EAwCUjIVLB_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt;"
},
{
"itemId":604642839549,
"image":"https://img.alicdn.com/bao/uploaded/i1/2032870312/O1CN01kgEmZd1EAwGG7LTjo_!!2-item_pic.png",
"price":"219.00",
"name":"【0.03新品贴合润滑】冈本官方旗舰店正品003贴身滑超薄避孕套6片",
"url":"https://item.taobao.com/item.htm?id=604642839549&amp;spm=2015.32819095.0.0",
"desc":"&lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01RyCFOb1EAwAWgpY9u_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01gqhdui1EAwAZewDwe_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01TTrnlO1EAwAVkygwZ_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01usea1E1EAwC06qxzR_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01vi2LyK1EAwATKCPTw_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01voDvgs1EAwAVkz1ip_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01FDIokI1EAwC34q9Rv_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01qEQRnn1EAwC4wD3yV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01wykYTn1EAwCSuA1fV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt;"
},
{
"itemId":647187674381,
"image":"https://img.alicdn.com/bao/uploaded/i3/2032870312/O1CN01Er4eOL1EAwH6MkLJF_!!0-item_pic.jpg",
"price":"179.90",
"name":"冈本官方旗舰店避孕套超薄零感套套男用情趣持久套正品安全套byt",
"url":"https://item.taobao.com/item.htm?id=647187674381&amp;spm=2015.32819095.0.0",
"desc":"&lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01kDrwVC1EAwG5w1vPD_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01Xjmj2l1EAwFyEYG9t_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01fiQrlp1EAwFzKpbZ1_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01kSg2Lm1EAwFsgUDV7_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01OfvbZl1EAwG1m9Wwa_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01sGj94k1EAwG564H4r_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01ugsEU91EAwFyEYnST_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01ovTYBJ1EAwFwic4TB_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01z5KJkE1EAwFyEYzvj_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt;"
},
{
"itemId":603002986449,
"image":"https://img.alicdn.com/bao/uploaded/i1/2032870312/O1CN01JeXve11EAwGfjzHqZ_!!0-item_pic.jpg",
"price":"98.80",
"name":"冈本003避孕套超薄安全套官方旗舰店正品男用超润滑套套byt",
"url":"https://item.taobao.com/item.htm?id=603002986449&amp;spm=2015.32819095.0.0",
"desc":"&lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01gldzV81EAwANJwbfI_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01CJP32M1EAwBxRaECR_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01xZIKbT1EAwANJvbL2_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt;&lt;br&gt; &lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01wl11iM1EAwExCe0Er_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01qEQRnn1EAwC4wD3yV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt;&lt;br&gt; &lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01eyMPas1EAwF8K9jFd_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt;"
},
{
"itemId":637285661487,
"image":"https://img.alicdn.com/bao/uploaded/i4/2032870312/O1CN01IZuIx51EAwH71unXs_!!0-item_pic.jpg",
"price":"199.00",
"name":"【入会享5折】冈本旗舰店避孕套超薄润滑男避育套套003安全套6片",
"url":"https://item.taobao.com/item.htm?id=637285661487&amp;spm=2015.32819095.0.0",
"desc":"&lt;p style=\"text-align: center;\"&gt; &lt;/p&gt; &lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01GMKADp1EAwCHgCziU_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01mhBuAj1EAwCPBvsEt_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01cKa55c1EAwCHEA0aZ_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01FDIokI1EAwC34q9Rv_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01qEQRnn1EAwC4wD3yV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01I9m2SM1EAwCUjIVLB_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt;"
},
{
"itemId":643979759067,
"image":"https://img.alicdn.com/bao/uploaded/i1/2032870312/O1CN01fFwlFa1EAwGl7ya2k_!!0-item_pic.jpg",
"price":"199.00",
"name":"冈本官方正品旗舰店避孕套超薄颗粒情趣超润滑男用持久安全套",
"url":"https://item.taobao.com/item.htm?id=643979759067&amp;spm=2015.32819095.0.0",
"desc":"&lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01pd28f41EAwFVVhXAi_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01kafRe91EAwFS4kGcR_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01y7BBmU1EAwFS4iK2b_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN017OO1Z01EAwFTDmp3a_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01n1vpre1EAwFU0mSLb_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01c2QGud1EAwFHyDLYN_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt;"
},
{
"itemId":625197198308,
"image":"https://img.alicdn.com/bao/uploaded/i1/2032870312/O1CN01kNRnan1EAwGdiesng_!!0-item_pic.jpg",
"price":"668.00",
"name":"【003囤货装】冈本官方旗舰店正品0.03超薄避孕套003套套男用安全",
"url":"https://item.taobao.com/item.htm?id=625197198308&amp;spm=2015.32819095.0.0",
"desc":"&lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01TGRvJM1EAwBJUhzHz_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN016psrAS1EAwBStb2i1_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01vgi4u31EAwBLXAcpw_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01Jsr9wx1EAwBM8KyFC_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01udczt51EAwBGbVMK2_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01M2FO1g1EAwBJ1gVsf_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01P6B05q1EAwBIbadco_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01yQT8MW1EAwBJ1hiGj_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01FDIokI1EAwC34q9Rv_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01qEQRnn1EAwC4wD3yV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01wykYTn1EAwCSuA1fV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt;"
},
{
"itemId":44953265864,
"image":"https://img.alicdn.com/bao/uploaded/i1/2032870312/O1CN012wDChu1EAwGqxJOyv_!!0-item_pic.jpg",
"price":"149.00",
"name":"避孕套冈本003超薄正品旗舰店安全套套中号男女延0.01时持情趣久",
"url":"https://item.taobao.com/item.htm?id=44953265864&amp;spm=2015.32819095.0.0",
"desc":"&lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01z0BYYj1EAwDCcpEnX_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt;&lt;br&gt; &lt;/p&gt; &lt;p style=\"text-align: center;\"&gt; &lt;/p&gt;"
},
{
"itemId":528265530793,
"image":"https://img.alicdn.com/bao/uploaded/i1/2032870312/O1CN01O0O4Lp1EAwGg4y0za_!!0-item_pic.jpg",
"price":"89.90",
"name":"【玻尿酸润滑液】冈本润滑油剂夫妻用品水溶性人体男女用情趣私处",
"url":"https://item.taobao.com/item.htm?id=528265530793&amp;spm=2015.32819095.0.0",
"desc":"&lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN0194cqIZ1EAw8a9Q5Ur_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt; &lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01bn3VA01EAw8Ws4h1K_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN010gyaUx1EAw8W7BV59_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN017RwKuy1EAw8W7AYqU_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01hU5Dio1EAw7WavDYK_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01zSilG81EAw7UZdHEo_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN015Dwgbo1EAwABWdxhH_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01QS6bvG1EAwAGE8EcD_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01lBw0VC1EAw7UwVDsM_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt;"
},
{
"itemId":573293943564,
"image":"https://img.alicdn.com/bao/uploaded/i1/2032870312/O1CN01oVgaq31EAwGOZekQN_!!2-item_pic.png",
"price":"79.90",
"name":"冈本官方旗舰店避孕套无感透薄套套10片装男用持久超润滑安全套",
"url":"https://item.taobao.com/item.htm?id=573293943564&amp;spm=2015.32819095.0.0",
"desc":"&lt;p&gt;&lt;img class=\"desc_anchor\" id=\"desc-module-1\" src=\"https://assets.alicdn.com/kissy/1.0.0/build/imglazyload/spaceball.gif\"&gt;&lt;/p&gt; &lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01Gbu0K61EAw9HWEQ12_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01NZpRwk1EAw9RG1C6q_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01wJn0UL1EAw9QTRXOz_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN014ZFTtX1EAw9N9EZdY_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01pEEPIK1EAw9P8bbos_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01tMUjq71EAw9PeR25p_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01FDIokI1EAwC34q9Rv_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01qEQRnn1EAwC4wD3yV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01wykYTn1EAwCSuA1fV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt;"
},
{
"itemId":38049467649,
"image":"https://img.alicdn.com/bao/uploaded/i1/2032870312/O1CN01etTsvP1EAwGfksH6W_!!0-item_pic.jpg",
"price":"139.00",
"name":"【冰感透薄】冈本安全套旗舰店超薄避孕套男用套套情趣男正品byt",
"url":"https://item.taobao.com/item.htm?id=38049467649&amp;spm=2015.32819095.0.0",
"desc":"&lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01yG6tDq1EAwAWWu650_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01btyEB91EAwAecwKf7_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01hRXbQQ1EAwAZRqKdV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01bfUT9F1EAwAeWAPGV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01FDIokI1EAwC34q9Rv_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01qEQRnn1EAwC4wD3yV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01wykYTn1EAwCSuA1fV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt;"
},
{
"itemId":45536880524,
"image":"https://img.alicdn.com/bao/uploaded/i1/2032870312/O1CN01ugAFmM1EAwGjzOh0C_!!0-item_pic.jpg",
"price":"288.00",
"name":"【超薄新体验】冈本官方旗舰店超薄避孕套男用套套正品男用安全套",
"url":"https://item.taobao.com/item.htm?id=45536880524&amp;spm=2015.32819095.0.0",
"desc":"&lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01yG6tDq1EAwAWWu650_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01btyEB91EAwAecwKf7_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01hRXbQQ1EAwAZRqKdV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01bfUT9F1EAwAeWAPGV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01FDIokI1EAwC34q9Rv_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01qEQRnn1EAwC4wD3yV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN012SG1LD1EAwDv7abeX_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01wykYTn1EAwCSuA1fV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt;"
},
{
"itemId":564942778404,
"image":"https://img.alicdn.com/bao/uploaded/i4/2032870312/O1CN01UzTahU1EAwH4frEh0_!!0-item_pic.jpg",
"price":"109.90",
"name":"冈本官方旗舰店0.02避孕套003超薄0.01套套男用持久情趣安全套",
"url":"https://item.taobao.com/item.htm?id=564942778404&amp;spm=2015.32819095.0.0",
"desc":"&lt;p&gt; &lt;/p&gt; &lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01ZDSPn51EAwAXqrEgQ_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01btyEB91EAwAecwKf7_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01hRXbQQ1EAwAZRqKdV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01bfUT9F1EAwAeWAPGV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01FDIokI1EAwC34q9Rv_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01qEQRnn1EAwC4wD3yV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01wykYTn1EAwCSuA1fV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt;"
},
{
"itemId":631248349054,
"image":"https://img.alicdn.com/bao/uploaded/i1/2032870312/O1CN01KPS5bN1EAwGjkwF6x_!!0-item_pic.jpg",
"price":"139.00",
"name":"冈本官方旗舰店超薄避孕套超值装安全套男用超润滑持久套套byt",
"url":"https://item.taobao.com/item.htm?id=631248349054&amp;spm=2015.32819095.0.0",
"desc":"&lt;p style=\"text-align: center;\"&gt; &lt;/p&gt; &lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01GMKADp1EAwCHgCziU_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01mhBuAj1EAwCPBvsEt_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01Fs58Mj1EAwCKIhSkW_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN011AVYEO1EAwCO1aTxl_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01FDIokI1EAwC34q9Rv_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01qEQRnn1EAwC4wD3yV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p style=\"text-align: center;\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01I9m2SM1EAwCUjIVLB_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;a href=\"https://market.m.taobao.com/app/miniapp-biz/qrcode/index.html?_ariver_appid=3000000002153653&amp;page=pages%2Flanding%2Flanding%3FpageId%3D1464604\" target=\"_blank\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01UgfLNC1EAwCD3uttE_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/a&gt;&lt;/p&gt;"
},
{
"itemId":630732514531,
"image":"https://img.alicdn.com/bao/uploaded/i1/2032870312/O1CN01lmfW7b1EAwGEUyCe2_!!2-item_pic.png",
"price":"49.00",
"name":"冈本SKIN超薄紧型避孕套男用10片旗舰店正品安全套成人用品byt",
"url":"https://item.taobao.com/item.htm?id=630732514531&amp;spm=2015.32819095.0.0",
"desc":"&lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01pfYSAQ1EAw7oOafO2_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01WqGLoC1EAwFgiMbJI_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01J5VNzk1EAw7mKzGVh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01z0oouU1EAw7ecoIER_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN019LZzLp1EAw7ouD7uS_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01jSTEph1EAw7mKyOUN_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01FDIokI1EAwC34q9Rv_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01qEQRnn1EAwC4wD3yV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01wykYTn1EAwCSuA1fV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt;"
},
{
"itemId":572833841756,
"image":"https://img.alicdn.com/bao/uploaded/i1/2032870312/O1CN01JELKbs1EAwGgA9WfX_!!0-item_pic.jpg",
"price":"149.00",
"name":"冈本旗舰店0.03白金系列超薄避孕套情趣男用润滑裸入10片装安全套",
"url":"https://item.taobao.com/item.htm?id=572833841756&amp;spm=2015.32819095.0.0",
"desc":"&lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01gSu0FR1EAwASu6bP2_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nLPaor1EAwAZcWeSY_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01vjg8yZ1EAwAXUZygX_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01tzkDTC1EAwBxwcEUO_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01jusdJ81EAwAYsLdUH_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01uT2aib1EAwARxJfmP_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01nSh0Iw1EAwAXDlulb_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01FDIokI1EAwC34q9Rv_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01qEQRnn1EAwC4wD3yV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01wykYTn1EAwCSuA1fV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt;&lt;img class=\"desc_anchor\" id=\"desc-module-4\" src=\"https://assets.alicdn.com/kissy/1.0.0/build/imglazyload/spaceball.gif\"&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt;"
},
{
"itemId":573611107609,
"image":"https://img.alicdn.com/bao/uploaded/i1/2032870312/O1CN01JFWU2L1EAwGF4ruj4_!!2-item_pic.png",
"price":"74.90",
"name":"冈本官方旗舰店避孕套超润滑透薄系列套套10片装男用持久正品byt",
"url":"https://item.taobao.com/item.htm?id=573611107609&amp;spm=2015.32819095.0.0",
"desc":"&lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01emfrEX1EAw9RGRuVM_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01OOy6tH1EAw9QTs3J2_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01XikXmp1EAw9PenOCT_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01HNSsuT1EAw9N9bjO5_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01djdOlE1EAw9RGR2UT_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN019TJ0Kz1EAw9NS3qAH_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01FDIokI1EAwC34q9Rv_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01qEQRnn1EAwC4wD3yV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01wykYTn1EAwCSuA1fV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i1/2032870312/O1CN01nZQXOR1EAwGfmbYYh_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt;"
},
{
"itemId":628533160056,
"image":"https://img.alicdn.com/bao/uploaded/i3/2032870312/O1CN01k8mmS61EAwH4fgMLp_!!0-item_pic.jpg",
"price":"119.90",
"name":"【肤感3合1】冈本官方旗舰店正品001超薄避孕安全套套男用003零感",
"url":"https://item.taobao.com/item.htm?id=628533160056&amp;spm=2015.32819095.0.0",
"desc":"&lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01VDrkib1EAwCNFpGpU_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN010gKrZl1EAwBxD25D2_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i3/2032870312/O1CN01vkIjpb1EAwBIqGsJL_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt; &lt;p&gt; &lt;/p&gt; &lt;p&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i2/2032870312/O1CN01wykYTn1EAwCSuA1fV_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;img src=\"https://img.alicdn.com/imgextra/i4/2032870312/O1CN01qG819P1EAwGllcAoE_!!2032870312.jpg\" align=\"absmiddle\"&gt;&lt;/p&gt;"
}
]
/* 测试数据mock */
export const prizeList = [
{
image:
"https://yun.duiba.com.cn/spark/assets/240121a7e7e10a66ad0cfa99a8b3ba9631fc7855.png",
name:'商品描述商品描述商品描述商品描述商品描述1',
content:'商品描述商品描述商品描述商品描述商品描述1',
hasBtn:true
},
{
image:
"https://yun.duiba.com.cn/spark/assets/f9160b053f525455503ea58ae6b43bacea400362.png",
name:'商品描述商品描述商品描述商品描述商品描述2',
content:'商品描述商品描述商品描述商品描述商品描述2'
},
{
image:
"https://yun.duiba.com.cn/spark/assets/28b868b5da81ebd28a4eb14291a8ac287937221c.png",
name:'商品描述商品描述商品描述商品描述商品描述3',
content:'商品描述商品描述商品描述商品描述商品描述3'
},
{
image:
"https://yun.duiba.com.cn/spark/assets/3c305fb907bbebb85ad392cb959fbe62200c407f.png",
name:'商品描述商品描述商品描述商品描述商品描述4',
content:'商品描述商品描述商品描述商品描述商品描述4'
},
{
image:
"https://yun.duiba.com.cn/spark/assets/28b868b5da81ebd28a4eb14291a8ac287937221c.png",
name:'商品描述商品描述商品描述商品描述商品描述5',
content:'商品描述商品描述商品描述商品描述商品描述5'
},
{
image:
"https://yun.duiba.com.cn/spark/assets/3c305fb907bbebb85ad392cb959fbe62200c407f.png",
name:'商品描述商品描述商品描述商品描述商品描述6',
content:'商品描述商品描述商品描述商品描述商品描述6'
},
{
image:
"https://yun.duiba.com.cn/spark/assets/554a90eee5a31ae7628950907974bdee2da8c01e.png",
name:'商品描述商品描述商品描述商品描述商品描述7',
content:'商品描述商品描述商品描述商品描述商品描述7'
},
{
image:
"https://yun.duiba.com.cn/spark/assets/554a90eee5a31ae7628950907974bdee2da8c01e.png",
name:'商品描述商品描述商品描述商品描述商品描述8',
content:'商品描述商品描述商品描述商品描述商品描述8'
}
];
export const BARRAGE_LIST = [
'111',
'222',
'333',
'444',
'555',
'666',
'777',
'888',
'999'
]
\ No newline at end of file
import React, { useEffect, useState } from 'react'
import { View } from '@tarojs/components'
import GoodsPage from '@/components/GoodsPage/GoodsPage'
import { useDidShow, useRouter, useDidHide } from '@tarojs/taro'
import API from '@/api'
import { TASK_CONFIG } from '@/const'
import { useRequest } from '@/hooks/useRequest'
import { commonToast } from 'tbcc-sdk-ts/lib/core/tb'
const browseConfig = {
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'
}
function BrowseGoodsPage() {
const [ percent, setPercent ] = useState(0)
const [ isJumpLink, setIsJumpLink ] = useState(false)
const { jumpType, browseTime } = TASK_CONFIG
let browserTimer = null
let usePercent = 0
const { params: { itemIds, keepTime } } = useRouter()
const { data: { list = [] }, success: listSuccess } = useRequest(API.getItemListByItemIds, { itemIds }, { isShowLoading: true })
useEffect(() => {
listSuccess && list.length && browseGoodsTimes()
return () => clearInterval(browserTimer)
}, [ list ])
useDidShow(() => {
// 重新回到页面开启倒计时
if (!browserTimer && jumpType === 1 && isJumpLink) {
usePercent = percent
browseGoodsTimes()
}
})
useDidHide(() => {
// 页面关闭清除定时器
clearInterval(browserTimer)
})
const onCompleteTask = async(itemId) => {
const { success } = await API.doCompleteTask({ taskType: browseConfig.taskType, itemId })
if (success) {
commonToast('浏览成功')
setIsJumpLink(false)
}
}
const onOpenDetail = (itemId) => {
if (jumpType === 1) {
setPercent(usePercent)
setIsJumpLink(true)
clearInterval(browserTimer)
return
}
onCompleteTask(itemId)
}
const browseGoodsTimes = () => {
browserTimer = setInterval(() => {
usePercent += 1
const keepTime_ = +keepTime || browseTime
if (usePercent >= keepTime_) {
onCompleteTask()
clearInterval(browserTimer)
}
}, 1000)
}
return (
<View>
<GoodsPage goodsList={list} task={browseConfig} onOpenDetail={() => onOpenDetail} />
</View>
)
}
export default BrowseGoodsPage
import React, { useState, useEffect } from 'react'
import { View } from '@tarojs/components'
import GoodsPage from '@/components/GoodsPage/GoodsPage'
import { useRouter } from '@tarojs/taro'
import tbccTs from 'tbcc-sdk-ts'
import API from '@/api'
import { useThrottle } from '@/hooks/useThrottle'
const { commonToast } = tbccTs.tb
const browseConfig = {
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'
}
function CollectGoodsPage() {
const router = useRouter()
const [goodsList, setGoodsList] = useState([])
const { itemIds } = router?.params
useEffect(() => {
getGoodsList()
}, [])
const getGoodsList = async () => {
const { data } = await API.getCollectGoodsList({ itemIds })
setGoodsList(data?.list || [])
}
const onCompleteTask = useThrottle(async(taskType, itemId) => {
const { success } = await API.doCompleteTask({ taskType, itemId })
if (success) {
getGoodsList()
commonToast('收藏成功')
}
})
return (
<View>
<GoodsPage goodsList={goodsList} task={browseConfig} onCompleteTask={() => onCompleteTask} />
</View>
)
}
export default CollectGoodsPage
.canvas {
width:100%;
height:100%;
}
.page-game {
width:100%;
height:100vh;
flex: 1;
display: flex;
flex-direction: column;
background: #f5cccd;
}
import { Main } from './output';
import { getUserAddress, favorShop, getAuthUserInfo, checkShopFavoredStatus, openDetail, reportAnalytics, commonToast, playBgMusic, getSystemInfo } from './utils';
//这种方式/表示client下面的路径
// import { getSystemInfo } from '/utils/utils';
//检查版本号
const checkSystem = async () => {
const { version } = await getSystemInfo();
console.log('version', version);
return versionCompare(version, '9.10.0');
}
//对比版本号
const versionCompare = (cur, compare) => {
const _cur = cur.split('.');
const _compare = compare.split('.');
if (+_cur[0] < +_compare[0]) {
return false;
} else if (+_cur[0] === +_compare[0] && +_cur[1] < +_compare[1]) {
return false;
} else if (+_cur[0] === +_compare[0] && +_cur[1] === +_compare[1] && +_cur[2] < +_compare[2]) {
return false;
}
return true;
}
/**
* 直接取,app
*/
const app = getApp()
/**
* 分享信息
*/
const SHARE_CONFIG = {//待写
title: 'babycare欢乐城!',
desc: '赢TF彩妆礼盒',
imageUrl: "https://yun.duiba.com.cn/db_games/activity/pacman/share.jpg",
path: 'pages/index/index'
}
Page({
//暂时先不用吧,有问题
data: {},
onLoad(query) {
// 页面加载
console.info(`Page onLoad with query: ${JSON.stringify(query)}`);
//带参数的取链接参数
if (query.inviteId) this.inviteId = query.inviteId;
//版本提示
checkSystem().then(r => {
if (!r) {
my.showToast({
content: "请升级最新版本的淘宝体验",
duration: 5000
});
}
});
},
onReady() {
},
onCanvasReady() {
console.log("进canvas了")
//版本检查
checkSystem().then(r => {
if (r) {//检查通过开始授权
this.getAuth();
}
});
},
//创建引入的Main
createMain() {
var self = this;
my.createCanvas({
id: 'canvas',
success: (ccc) => {
const dpr = my.getSystemInfoSync().pixelRatio
const windowWidth = my.getSystemInfoSync().windowWidth;
const windowHeight = my.getSystemInfoSync().windowHeight;
ccc.width = windowWidth * dpr + dpr;//重新修改会有误差
ccc.height = windowHeight * dpr + dpr;
// console.log(Main)
console.log(ccc.width, ccc.height)
if (!this.main) {
try {
this.main = new Main(ccc)
//添加事件,为了和main里的通信
this.main.addGlobalEvent("onMessage", self.onMessage, self)
} catch (err) {
console.error(err)
}
}
}
})
},
//授权
async getAuth() {
const userInfo = await getAuthUserInfo().catch(() => {
commonToast('未授权成功');
this.postMessage('mine.authorize', { success: false })
})
//未授权成功不进入活动
if (!userInfo) return
const { nickName, avatar } = userInfo
app.nickName = nickName;
app.avatar = avatar;
//检查是否关注的
const isFollow = await checkShopFavoredStatus(app.sellerId).catch(() => { })
app.isFollow = this.isFollow = isFollow;//app上也挂一个
//创建Main,授权登录完后才创建,也可定制在游戏内授权
this.createMain();
//分享图片,TODO,注释打开替换
const result1 = await app.cloud.file.getTempFileURL({ fileId: 'cloud://2D60DC04FC1888AF6F865B88F0B2EBCC//share.png' });
if (result1[0]) SHARE_CONFIG.imageUrl = result1[0].url
//音乐路径
const resultSrc = await app.cloud.file.getTempFileURL({ fileId: 'cloud://293F2E7D0C6799617660563A322DEE55//bgm_babycare.mp3' });
if (resultSrc[0]) this.musicUrl = resultSrc[0].url;
},
/**
* 获取及选择用户收货地址
* @param {*} prizeId
*/
async getUserAddress(prizeId) {
const userAddress = await getUserAddress().catch(res => {
commonToast(res.errorMessage)
this.postMessage('mine.getUserAddress', { success: false })
})
if (userAddress) {//格式需要修改具体
const { name, phone, address } = userAddress.duibaAddress
const { provinceName, cityName, countyName } = userAddress;
my.confirm({
title: '提示',
content: '确认使用该收货地址:' + name + phone + address,
confirmButtonText: '确定',
cancelButtonText: '取消',
success: (result) => {
const { confirm } = result
if (confirm) {
//发接口提交地址
const { cloud, activityId, cloudName } = app
cloud.function.invoke(cloudName, {//定制
activityId,
id: prizeId,
_id: prizeId,
// address,
name,
phone,
addressdetail: address,
provice: provinceName,
city: cityName,
area: countyName,
// address: JSON.stringify(userAddress.duibaAddress)
}, "receiveObjectPrize")
.then(res => {
if (res && res.success) {
//确认发送成功信息
this.postMessage('mine.getUserAddress', { success: true })
} else {
//新增失败0514
this.postMessage('mine.getUserAddress', { success: false })
}
})
.catch(() => {
//新增失败0514
this.postMessage('mine.getUserAddress', { success: false })
})
} else {
//新增失败0514
this.postMessage('mine.getUserAddress', { success: false })
}
},
})
}
},
/**
* 关注店铺
*/
async doFavorShop() {
const { sellerId } = getApp()
const success = await favorShop(sellerId).catch(err => {
// commonToast(err.errorMessage || '活动太火爆,请稍后重试')
//这个包括用户取消的,所以下面不用判断success了;
this.postMessage('mine.favorShop', { success: false })
// console.log("啊啊啊请问请问")
})
// console.log("asdasd",success)
if (success) {
this.postMessage('mine.favorShop', { success: true })
}
// else {
// // console.log("飒飒飒飒大苏打")
// this.postMessage('mine.favorShop', { success: false })
// }
},
//和main通信的方法,接收main的信息
onMessage(e) {
//接口名字
let netName = e.data.netName;
//接口参数
let parameter = e.data.parameter;
//暂时发现3个需要用户操作的接口
//定制的授权
if (netName == "mine.authorize") {
this.getAuth()
}
//获取地址
else if (netName == "mine.getUserAddress") {
this.getUserAddress(parameter.prizeId)
}
//关注店铺的操作
else if (netName == "mine.favorShop") {
this.doFavorShop()
}
//领养名字,有弹框的
else if (netName == "mine.getAdoptName") {
this.setData({ "adoptModalVisible": true, type: parameter.type })
}
//打开详情页,还有成功失败
else if (netName == "mine.openDetail") {
openDetail(parameter.itemId).then(
() => {
this.postMessage(netName, { success: true })
},
() => {
this.postMessage(netName, { success: false })
}
)
}
//前端同步接口
else if (netName.indexOf("mine") == 0) {
var data = { success: true }
switch (netName) {
case "mine.getAppData"://获取信息
data = {
success: true,
data: {
activityId: app.activityId,//|| "5e7c40e98564b57d7fb95812",
nickName: app.nickName,
avatar: app.avatar,
openId: app.openId,
inviteId: this.inviteId,//判断是否要发助力用
isFollow: app.isFollow
}
}
break;
case "mine.navigateToOutside"://跳转小程序外的页面
my.call("navigateToOutside", {
url: parameter.url
}, (res) => {
// my.alert({ content: "success - " + JSON.stringify(res)})
})
console.log(parameter.url)
break;
case "mine.navigateTo"://跳转小程序内的页面
my.navigateTo({
url: parameter.url
})
break;
case "mine.navigateBack"://返回页面
my.navigateBack({
delta: parameter.delta || 1
})
break;
case "mine.showSharePanel"://分享
//多一个参数
this.openId = parameter.openId;
my.showSharePanel()
break;
case "mine.reportAnalytics": ////淘宝自定义埋点
reportAnalytics(parameter.logkey);
break;
case "mine.openMusic": //音频
playBgMusic(parameter.isOn, this.musicUrl);
break;
case "mine.openRankPanel": //打开排行榜
this.setData({ "expRankModalVisible": true });
break;
}
this.postMessage(netName, data)
} else {
//获取后端接口
const { function: fc } = app.cloud;
//处理下带上activityId
if (!parameter) parameter = {};
parameter.activityId = app.activityId;
//netName得分割下
fc.invoke(netName.split(".")[0], parameter, netName.split(".")[1])
.then(res => {
// console.log("返回数据" + JSON.stringify(res));
//发送消息
this.postMessage(netName, res || { success: false })
}, (err) => {
//网络异常,返回失败
this.postMessage(netName, { success: false })
})
}
},
postMessage(netName, data) {
// this.main.dispatchGlobalEvent(netName, data)
this.main && this.main.dispatchGlobalEvent({ type: netName }, data)//以防复用事件event串了,就用这种方式
},
onShow() {
// 页面显示
this.main && this.main.run();
},
onHide() {
// 页面隐藏
this.main && this.main.pause();
},
onUnload() {
// 页面被关闭
this.main && this.main.destroy();
},
onTitleClick() {
// 标题被点击
},
onPullDownRefresh() {
// 页面被下拉
},
onReachBottom() {
// 页面被拉到底部
},
/**
* 分享参数处理
*/
onShareAppMessage() {
// 返回自定义分享信息
// const { activityId, openId } = app
// 发送分享记录,记录数据用
// API.getShareId({ activityId });
let { path, ...rest } = SHARE_CONFIG
if (this.openId) {
path = path + '?inviteId=' + this.openId
this.openId = null//用完清掉
}
console.log(path)
return {
...rest,
path
}
},
//鼠标事件
onMouseEvent(e) {
if (this.main) this.main.stage.onMouseEvent(e)
},
});
{
"disableScroll": "true",
"usingComponents": {
"task-modal": "/components/taskModal/taskModal"
}
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
/**
* 通用toast
* @param {string} content toast内容
* @param {number} duration 显示时长
*/
export const commonToast = (content, duration = 3000) => {
my.showToast({
content: content || '换个姿势再试一次~',
duration
});
};
/**
* 获取授权用户信息
* @returns {object} 用户登录信息
*/
export const getAuthUserInfo = () => {
return new Promise((resolve, reject) => {
my.authorize({
scopes: 'scope.userInfo',
success: res => {
my.getAuthUserInfo({
success: userInfo => { resolve(userInfo); }
});
},
fail: err => {
reject(err);
}
});
});
};
/**
* 获取系统信息
* @returns {object} 小程序系统信息
*/
export const getSystemInfo = () => {
return new Promise((resolve, reject) => {
my.getSystemInfo({
success: res => { resolve(res); },
fail: err => { reject(err); }
});
});
};
/**
* 执行关注店铺
* @param {number} sellerId 店铺归属的卖家Id
* @returns {boolean} 关注状态
*/
export const favorShop = sellerId => {
return new Promise((resolve, reject) => {
my.tb.favorShop({
id: sellerId,
success: res => {
console.log(res, '关注店铺');
resolve(res);
},
fail: err => {
console.log(err, '关注店铺失败');
// error code 11 为用户取消操作不认作失败场景
// if (err.error !== 11) {
reject(err);
// }
}
});
})
};
/**
* 判断是否关注店铺
* @param {number} sellerId 店铺归属的卖家Id
* @returns {boolean} 关注状态
*/
export const checkShopFavoredStatus = sellerId => {
return new Promise((resolve, reject) => {
my.tb.checkShopFavoredStatus({
id: sellerId,
success: res => {
resolve(res.isFavor);
},
fail: err => {
reject(err);
}
});
});
};
/**
* 跳转到外部链接
* @param {string} url 跳转链接
*/
export const navigateToOutside = url => my.call("navigateToOutside", { url });
/**
* 跳转到内部链接(新开窗口)
* @param {string} url 跳转链接
*/
export const navigateTo = url => my.navigateTo({ url });
/**
* 跳转到内部链接(不新开窗口)
* @param {string} url 跳转链接
*/
export const redirectTo = url => my.redirectTo({ url });
/**
* 获取服务器时间
* @returns {number} 服务器时间戳
*/
export const getServerTime = () => {
return new Promise((resolve, reject) => {
my.getServerTime({
success: (res) => {
resolve(res.time);
},
fail: err => {
reject(err);
}
});
});
};
/**
* 收藏商品
* @param {number} 商品id
* @returns {object} 收藏结果
*/
export const collectGoods = id => {
return new Promise((resolve, reject) => {
my.tb.collectGoods({
id,
success: res => {
resolve(res);
},
fail: err => {
if (err.errorMessage === '该商品已收藏') {
// 已收藏没有给到errorCode,只能通过message判断
// 返回收藏成功,并且给到标记代表是已收藏的
resolve({ success: true, hasCollected: true, errorMessage: err.errorMessage });
} else if (err.error !== 11) {
// error code 11 为用户取消操作不认作失败场景
reject(err);
}
}
});
});
};
/**
* 查询商品收藏状态
* @param {number} 商品id
* @returns {boolean} 商品收场状态
*/
export const checkGoodsCollectedStatus = id => {
return new Promise((resolve) => {
my.tb.checkGoodsCollectedStatus({
id,
success: res => {
resolve(res.isCollect);
}
});
});
};
/**
* 时间格式化
* @param date 接收可以被new Date()方法转换的内容
* @param format 字符串,需要的格式例如:'yyyy-MM-dd hh:mm:ss'
* @returns {String}
*/
export const dateFormatter = (date, format = 'yyyy-MM-dd') => {
if (!date) return '-';
date = new Date(typeof date === 'string' && isNaN(date) ? date.replace(/-/g, '/') : date);
let o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds(),
'q+': Math.floor((date.getMonth() + 3) / 3),
'S': date.getMilliseconds()
};
if (/(y+)/.test(format)) {
format = format.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
}
for (let k in o) {
if (new RegExp('(' + k + ')').test(format)) {
format = format.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
}
}
return format;
};
/**
* 创建音频类
*/
class AudioModel {
constructor(opts = {}) {
const { src } = opts;
if (!src) {
console.error('请输入音频链接');
return false;
}
try {
const innerAudioContext = my.createInnerAudioContext();
Object.keys(opts).forEach(item => {
innerAudioContext[item] = opts[item];
});
return innerAudioContext;
} catch(e) {
console.error('当前版本不支持音频');
}
}
};
/**
* 创建音频实例
* @param {object} opts 配置对象 例 { src: '音频链接', autoplay: false }
*/
export const createAudio = opts => {
const audioInstance = new AudioModel(opts)
return audioInstance;
};
//暂时只有一个音乐,临时处理
let bgMusic
export const playBgMusic = (isOn,src) => {//待测试
if (isOn) {
if (!bgMusic) {
bgMusic = createAudio({
src: src||"https://yun.duiba.com.cn/db_games/activity/babycare_xiaoxiao/bgm_babycare.mp3",
autoplay: true,
loop: true//暂时无效?
});
//监听结束,因为loop无效
bgMusic.onEnded(() => {
// bgMusic.seek(0);
bgMusic.play();
})
console.log(src)
} else {
bgMusic.play();
}
} else {
if (bgMusic) {
// bgMusic.pause();
bgMusic.stop();
// bgMusic.seek(0);
}
}
}
/**
* 版本比较
* @param {string} cur 当前版本
* @param {string} compare 比较版本
*/
export const versionCompare = (cur, compare) => {
const _cur = cur.split('.');
const _compare = compare.split('.');
if (+_cur[0] < +_compare[0]) {
return false;
} else if ( +_cur[0] === +_compare[0] && +_cur[1] < +_compare[1] ) {
return false;
} else if (+_cur[0] === +_compare[0] && +_cur[1] === +_compare[1] && +_cur[2] < +_compare[2]) {
return false;
}
return true;
}
//暂时只有一个音乐,临时处理
// let bgMusic;
// let gameMusic;
let bgs = {
}
export const playBgMusic1 = (isOn, src) => {//待测试
for (let k in bgs) {
if (bgs[k].isOn) {
bgs[k].stop();
}
// bgs[k].isOn = false; /// TODO 自己记一个开关,因为淘宝的玩意stop之后虽然听不到,但还是在播放,会触发loop播放和onEnded事件
}
const key = src;
if (isOn) {
if (!bgs[key]) {
bgs[key] = createAudio({
src: src,
autoplay: true,
loop: false /// TODO 暂时无效?2020.08.12 目前有效,但stop后还会跑数据,导致到音频结束到时间又自动播放
});
bgs[key].onPlay(() => {
bgs[key].isOn = true;
});
bgs[key].onStop(() => {
bgs[key].isOn = false;
});
/// TODO监听结束,因为loop无效 2020.08.12 loop有效,这个操作是为了解决stop之后还会跑音频数据的问题
bgs[key].onEnded(() => {
if (bgs[key].isOn) { // 判断自己记录的开关
bgs[key].play();
// bgs[key].isOn = true;
}
});
bgs[key].play(); // 安卓有时候autoplay好像莫得用
// console.log(src)
// bgs[key].isOn = true; /// TODO 自己记一个开关,因为淘宝的玩意stop之后虽然听不到,但还是在播放,会触发loop播放和onEnded事件
} else {
bgs[key].play();
// bgs[key].isOn = true; /// TODO 自己记一个开关,因为淘宝的玩意stop之后虽然听不到,但还是在播放,会触发loop播放和onEnded事件
}
} else {
if (bgs[key]) {
// bgs[key].pause(); /// pause 垃圾,不能用
bgs[key].stop();
// bgs[key].isOn = false; /// TODO 自己记一个开关,因为淘宝的玩意stop之后虽然听不到,但还是在播放,会触发loop播放和onEnded事件
}
}
}
// TODO: 确认天猫是否也是9.3.0以下有问题
export const checkSystem = async ()=> {
const { version, app } = await getSystemInfo();
console.log('version', version);
return versionCompare(version, '9.3.0');
}
/**
* 获取淘宝用户收货地址
*/
export const getUserAddress = async (opts = {}) => {
let defaults = {
addAddress: 'show',
searchAddress: 'hide',
locateAddress: 'hide',
joinTag: '' // 分割收货地址符号(淘宝商家有特定发货需求)
};
const isValidVersion = await checkSystem();
if(!isValidVersion) {
commonToast('当前版本不支持选择收货地址,请升级到最新版本');
return;
}
let _opts = Object.assign({}, defaults, opts);
return new Promise((resolve, reject) => {
my.authorize({
scopes: 'scope.addressList',
success: () => {
const { addAddress, searchAddress, locateAddress, joinTag } = _opts;
if (!my.tb.chooseAddress) {
reject({ support: false, errorMessage: '当前版本不支持选择收货地址,请升级到最新版本' });
return false;
}
my.tb.chooseAddress({
addAddress,
searchAddress,
locateAddress
}, res => {
const { error, provinceName, countyName, cityName, streetName, detailInfo, name, telNumber } = res;
// 如果有error为用户取消操作不做操作
if (error) {
resolve(false);
} else {
resolve({
...res,
duibaAddress: {
name,
phone: telNumber,
address: [provinceName, cityName, countyName, streetName, detailInfo].join(joinTag)
}
});
}
}, res => {
reject(res);
})
},
});
});
};
/**
* 打开详情页
* @param {string} itemId
*/
export const openDetail = (itemId) => {
return new Promise((resolve, reject) => {
my.tb.openDetail({
itemId,
success: (res) => {
resolve(res)
},
fail: (res) => {
reject(res)
},
});
})
}
/**
* 自定义埋点的方法
* @param {string}} logkey
*/
export const reportAnalytics = (logkey) => {
my.reportAnalytics('click', {
// 标志某一类事件,方便数据分析进行分组
'logkey': logkey
})
}
\ No newline at end of file
...@@ -5,6 +5,6 @@ export default { ...@@ -5,6 +5,6 @@ export default {
enableShareAppMessage: true, enableShareAppMessage: true,
usingComponents: { usingComponents: {
'member-modal': '../../components/_miniprogram/memberModal/memberModal', 'member-modal': '../../components/_miniprogram/memberModal/memberModal',
'lottie-modal': '../../components/_miniprogram/lottieBlank/lottieBlank' 'canvas-comp': '../../../components/_miniprogram/comBlank/comBlank'
} }
} }
import React, { useState } from 'react' import React, { useRef, useState } from 'react'
import { View } from '@tarojs/components' import { View, Image } from '@tarojs/components'
import Taro, { useShareAppMessage, useDidShow } from '@tarojs/taro' import Taro, { useShareAppMessage, useDidShow, getApp } from '@tarojs/taro'
import classnames from 'classnames' import classnames from 'classnames'
import { SHARE_CONFIG } from '@/const.js' import { SHARE_CONFIG } from '@/const.js'
import { HELP_MODAL_TYPE } from '@/config/help.config'
import { useLogin, useLoginFromShare } from '@/hooks/useLogin' import { useLogin, useLoginFromShare } from '@/hooks/useLogin'
import { useActivityInfoModel, useLoginInfoModel } from '@/store' import { useActivityInfoModel, useLoginInfoModel } from '@/store'
import API from '@/api' import API from '@/api'
import RuleModal from '@/components/RuleModal/RuleModal' import ContainerFit from '@/components/_base/ContainerFit/ContainerFit'
import DoHelpModal from '@/components/DoHelpModal/DoHelpModal' import RuleModal from '@/components/_tb_modal/RuleModal/RuleModal'
import TasksModal from '@/components/TasksModal/TasksModal' import DoHelpModal from '@/components/_tb_modal/DoHelpModal/DoHelpModal'
import CountDown from '@/components/CountDown/CountDown' import TasksModal from '@/components/_tb_modal/TasksModal/TasksModal'
import JackpotModal from '@/components/_tb_modal/JackpotModal'
import styles from './index.module.less' import styles from './index.module.less'
import tbcc from 'tbcc-sdk-ts' import tbcc from 'tbcc-sdk-ts'
const { navigateTo } = tbcc.tb import { useEffect } from 'react'
const { getImgShareUrl } = tbcc.utils const { navigateTo, openDetail, showSharePanel } = tbcc.tb
const { getImgShareUrl, checkIsMember, validateActivityStatus } = tbcc.utils
import {useAudio} from '@/hooks/useAudio'
import {CLOUD_OBJ, BGMUSIC_URL} from '@/const'
import { commonToast } from 'tbcc-sdk-ts/lib/core/tb'
const INDEX_CONFIG = { const INDEX_CONFIG = {
bg: '', // 背景 cloud://C4015B7CEC23CF013A78247E2852524F//火箭待机小球抖动.json bg: '', // 背景 cloud://C4015B7CEC23CF013A78247E2852524F//火箭待机小球抖动.json
...@@ -22,8 +31,15 @@ const INDEX_CONFIG = { ...@@ -22,8 +31,15 @@ const INDEX_CONFIG = {
shareButton: '', // 分享按钮 shareButton: '', // 分享按钮
taskButton: '' // 任务按钮 taskButton: '' // 任务按钮
} }
const prizeItem = {
id: "6115fc6a3075d9bde4b16a53",
image: "https://img.alicdn.com/imgextra/i4/353042333/O1CN012k7gbk1T6YigovFNu_!!353042333-0-isvtu.jpg",
type: 3,
name: '测试i奖品'
}
function Index() { function Index() {
const app = getApp()
const { activityInfo, setActivityInfoAndStatus } = useActivityInfoModel() const { activityInfo, setActivityInfoAndStatus } = useActivityInfoModel()
const { loginInfo } = useLoginInfoModel() const { loginInfo } = useLoginInfoModel()
...@@ -31,33 +47,77 @@ function Index() { ...@@ -31,33 +47,77 @@ function Index() {
const [ doHelpModalVisible, setDoHelpModalVisible ] = useState(false) const [ doHelpModalVisible, setDoHelpModalVisible ] = useState(false)
const [ ruleModalVisible, setRuleModalVisible ] = useState(false) const [ ruleModalVisible, setRuleModalVisible ] = useState(false)
const [ tasksModalVisible, setTasksModalVisible ] = useState(false) const [ tasksModalVisible, setTasksModalVisible ] = useState(false)
// 中奖弹窗
const [ prizeModalVisible, setPrizeModalVisible] = useState(false)
// 分享图
const SHARE_IMG = useRef('')
// 助力弹窗类型
const [helpType,setHelpType] = useState(HELP_MODAL_TYPE.HELP)
// 助力弹窗标识
const showHelp = useRef(false)
const fetchActivityInfo = async() => { const fetchActivityInfo = async() => {
const { success, data } = await API.getActivityBaseInfoById() const { success, data } = await API.getActivityBaseInfoById()
success && setActivityInfoAndStatus(data) if(success) {
const { startTime, endTime, rule } = data
setActivityInfoAndStatus({
startTime, endTime, rule
})
} }
// 授权登录完成 }
useLogin(async () => { // 获取用户信息
const fetchUserInfo = async () => {
const { success, data } = await API.getUserInfo()
success && setUserInfo(data)
}
useEffect(() =>{
fetchActivityInfo() fetchActivityInfo()
// getImgShareUrl('cloud://CEFE74AE67921906B5AF842150646D35/share.png').then(url => {
// SHARE_IMG.current = url
// })
},[])
// 授权登录完成
useLogin(async (info) => {
handleVisibleModal(info)
}) })
// 查看是否有助力信息
const getShareInfo = async () => {
const { success, data } = await API.getShareInfo()
if(success && data && data?.inviteCount) {
setHelpInfo(data)
setDoHelpModalVisible(true)
setHelpType(HELP_MODAL_TYPE.REWARDS)
}
}
// 分享链接进入活动 // 分享链接进入活动
useLoginFromShare((inviteId) => { useLoginFromShare((inviteId) => {
console.log('inviteId :>> ', inviteId) console.log('inviteId :>> ', inviteId)
setDoHelpModalVisible(true) app.inviteId = inviteId
}) })
useShareAppMessage(() => { useShareAppMessage(() => {
return { return {
...SHARE_CONFIG, ...SHARE_CONFIG,
imageUrl: SHARE_IMG.current,
path: `pages/index/index?inviteId=${loginInfo.openId}` path: `pages/index/index?inviteId=${loginInfo.openId}`
} }
}) })
// 助力弹窗 -> 其他弹窗
const handleVisibleModal = async (info) => {
const { inviteId } = info
const isVip = await checkIsMember()
// 是否为助力弹窗
if(inviteId && !showHelp.current) {
setDoHelpModalVisible(true)
setHelpType(isVip? HELP_MODAL_TYPE.FAIL: HELP_MODAL_TYPE.HELP)
showHelp.current = true
return;
}
// 其他弹窗
}
const onClose = () => { const onClose = () => {
setMemberVisible(false) setMemberVisible(false)
} }
const onAuthSuccess = () => { const onAuthSuccess = () => {
setMemberVisible(false) setMemberVisible(false)
} }
...@@ -65,36 +125,64 @@ function Index() { ...@@ -65,36 +125,64 @@ function Index() {
setMemberVisible(false) setMemberVisible(false)
} }
const onShare = () => my.showSharePanel() const onHandleClose = () => {
const onGoMyPrizePage = () => navigateTo('/pages/myPrize/myPrize') setDoHelpModalVisible(false)
if(loginInfo.newUser) {
handleVisibleModal(loginInfo)
}
if(helpType === HELP_MODAL_TYPE.REWARDS) {
fetchUserInfo()
}
}
const homeStyle = classnames(styles.container,{ const onHandleIndex = (type,updateActInfo) => {
[`${styles['content_fixed']}`]: ruleModalVisible const Fn = {
}) 'updateUserInfo': () => {
fetchUserInfo()
updateActInfo && fetchActivityInfo()
},
'rule': () => setRuleModalVisible(true),
'prize': () => {
navigateTo('/pages/packagePrize/myPrize/myPrize')
},
'game': () => navigateTo('/pages/packageGame/game/game'),
'comps': () => {
navigateTo('/pages/packageComs/comsTest/comsTest')
}
}
Fn[type]()
}
return ( return (
<View className={homeStyle} style={{ backgroundImage: `url(${INDEX_CONFIG.bg})`}}> <>
{/* <View className={styles['page-container__1206']}> <ContainerFit hasFitPsd={true} bg={INDEX_CONFIG.bg}>
<View className={styles['page-container__1624']}> <View className={styles.rule} style={{ backgroundImage: `url(${INDEX_CONFIG.ruleButton})` }} onClick={() => setRuleModalVisible(true)}>活动规则</View>
<View className={styles['page-container__content']}>适配方案</View> <View className={styles.my_prize} style={{ backgroundImage: `url(${INDEX_CONFIG.myPrizeButton})` }} onClick={() => onHandleIndex('prize')}>我的奖品</View>
</View> <View className={styles.tasks} onClick={() => setTasksModalVisible(true)}>任务</View>
</View> */} <View className={styles.games} onClick={() =>onHandleIndex('game') }>游戏</View>
{/* <View className={styles['blank_content']}><text>{`弹窗后内容区禁止滑动方案{position: fixed}`}</text></View> */}
{/* <View className={styles.countTime}>
<CountDown endTime={(Date.now() + 3 * 60 * 60 * 1000)} fontSize={'20rpx'} color={'#1B5F7F'} onUpdate={() => updateFlash()} />
</View> */}
{ true && <lottie-modal />}
<View className={styles.rule} style={{ backgroundImage: `url(${INDEX_CONFIG.ruleButton})` }} onTap={() => setRuleModalVisible(true)}>活动规则</View>
<View className={styles.my_prize} style={{ backgroundImage: `url(${INDEX_CONFIG.myPrizeButton})` }} onTap={onGoMyPrizePage}>我的奖品</View>
<View className={styles.share} style={{ backgroundImage: `url(${INDEX_CONFIG.shareButton})` }} onTap={onShare}>分享</View>
<View className={styles.tasks} style={{ backgroundImage: `url(${INDEX_CONFIG.taskButton})` }} onTap={() => setTasksModalVisible(true)}>任务</View>
<View className={styles.games} style={{ backgroundImage: `url(${INDEX_CONFIG.taskButton})` }} onTap={() => navigateTo('/pages/games/games')}>游戏</View>
<View className={styles.bemember} onTap={() => setMemberVisible(true)}>入会</View> <View className={styles.bemember} onTap={() => setMemberVisible(true)}>入会</View>
<View className={styles.comps} onClick={() => onHandleIndex('comps')}>组件预览</View>
<View className={styles['prize_modal_btn']} onTap={()=>setPrizeModalVisible(true)}>中奖弹窗</View>
</ContainerFit>
{/* 中奖弹窗 */}
{
prizeModalVisible &&
<JackpotModal
closePostion='bottom'
bg=''
width='600'
height='700'
top='45%'
prizeItem={prizeItem}
onClose={() => setPrizeModalVisible(false)}
/>
}
{ {
tasksModalVisible && tasksModalVisible &&
<TasksModal <TasksModal
onUpdate={() => {}} onUpdate={() => { console.warn('任务数据更新了')}}
onClose={() => setTasksModalVisible(false)} onClose={() => setTasksModalVisible(false)}
/> />
} }
...@@ -117,12 +205,13 @@ function Index() { ...@@ -117,12 +205,13 @@ function Index() {
width='500' width='500'
height='500' height='500'
top='45%' top='45%'
newVip newVip={false}
onClose={() => setDoHelpModalVisible(false)} onClose={() => setDoHelpModalVisible(false)}
/> />
} }
{memberVisible && <member-modal onClose={onClose} onAuthFail={onAuthFail} onAuthSuccess={onAuthSuccess} />} {<member-modal expend={memberVisible} sellerId={98563612} onClose={onClose} onAuthFail={onAuthFail} onAuthSuccess={onAuthSuccess} />}
</View> {false && <canvas-comp />}
</>
) )
} }
......
...@@ -8,39 +8,7 @@ ...@@ -8,39 +8,7 @@
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
} background-image: linear-gradient(45deg,#fbc2eb 10%, #a6c1ee);
// 适配方案开始
.page-container__1206 {
width: 750px;
height: 1206px;
position: absolute;
left: 0;
top: 50%;
margin-top: calc(-1206px / 2);
}
.page-container__1624 {
width: 750px;
min-height: 1624px;
position: absolute;
left: 0;
top: 50%;
margin-top: calc(-1624px / 2);
}
.page-container__content {
width: 200px;
height: 60px;
position: absolute;
left: 0;
top: 208px;
color: #fff;
background-color: #000;
text-align: center;
line-height: 60px;
}
// 适配方案结束
.content_fixed {
position: fixed;
} }
.index_button() { .index_button() {
.wh(150px, 46px); .wh(150px, 46px);
...@@ -55,35 +23,40 @@ ...@@ -55,35 +23,40 @@
.rule { .rule {
.index_button(); .index_button();
left: 20px; left: 20px;
top: 100px; top: 228px;
} }
.my_prize { .my_prize {
.index_button(); .index_button();
right: 20px; right: 20px;
top: 100px; top: 228px;
} }
.share { .share {
.index_button(); .index_button();
right: 20px; right: 20px;
top: 180px; top: 280px;
} }
.bemember { .bemember {
.index_button(); .index_button();
right: 20px; right: 20px;
top: 260px; top: 340px;
} }
.tasks { .tasks {
.index_button(); .index_button();
right: 20px; right: 20px;
top: 340px; top: 400px;
} }
.games { .games {
.index_button(); .index_button();
right: 20px; right: 20px;
top: 420px; top: 460px;
} }
.blank_content { .comps {
width: 100%; .index_button();
height: 2500px; right: 20px;
background: #eee; top: 520px;
}
.prize_modal_btn {
.index_button();
right: 20px;
top: 620px;
} }
\ No newline at end of file
import React, { useState, useEffect } from 'react'
import { Image, ScrollView, View } from '@tarojs/components'
import API from '@/api'
import { DRAW_STATUS, PRIZE_TYPE } from '@/const'
import DeliveryModal from '@/components/DeliveryModal/DeliveryModal'
import tbccTs from 'tbcc-sdk-ts'
import { useThrottle } from '@/hooks/useThrottle'
import { checkIsMember } from 'tbcc-sdk-ts/lib/utils'
import { dateFormatter } from '@/utils/date'
import './myPrize.less'
const { commonToast, getUserAddress, navigateToOutside, setClipboard } = tbccTs.tb
const { getMyPrizeList, receiveEnamePrize, receiveObjectPrize } = API
// 我的奖品配置项
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使用
objectStatus: {
1: '//yun.dui88.com/duiba-components-c-myprizeto_address.png', // 填写地址(实物奖品)
3: '//yun.dui88.com/duiba-components-c-myprizelogistics.png', // 查看物流
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' // 领取失败
},
enameStatus: {
1: '//yun.dui88.com/duiba-components-c-myprizeget_now.png', // 立即领取(除实物奖品外)
3: '//yun.duiba.com.cn/bestoreBox/prize_received.png', // 去使用(除实物奖品外)
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' // 领取失败
},
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 // 是否显示奖品编号
}
function Empty(blankTxt) {
return <View className='my-prize-item__empty'>{blankTxt}</View>
}
function MyPrizeList() {
const [ myPrizeList, setMyPrizeList ] = useState([])
const [ expiredTime, setExpiredTime ] = useState('')
const [ currentPrize, setCurrentPrize ] = useState({})
const [ memberVisible, setMemberVisible ] = useState(false)
const [ deliveryModalVisible, setDeliveryModalVisible ] = useState(false)
useEffect(() => {
fetchMyPriceList()
}, [])
async function fetchMyPriceList() {
const res = await getMyPrizeList()
if (res.success && res?.data?.list?.length) {
setMyPrizeList(res?.data?.list)
}
if (res?.data?.expiredTime) {
setExpiredTime(dateFormatter(res?.data?.expiredTime, 'yyyy/MM/dd hh:mm:ss'))
}
}
// 复制编码
const handleCopyId = async(text) => {
await setClipboard(text)
commonToast('复制成功')
}
// 优惠券
async function handleGetEquity(id) {
const { success, data } = await receiveEnamePrize({ id })
if (success && data) {
commonToast('领取成功')
}
fetchMyPriceList()
}
// 领取实物
async function handleReceiveObjectPrize(params) {
const { success, data } = await receiveObjectPrize(params).catch(res => {
commonToast(res && res.message)
}) || {}
if (success && data) {
commonToast('领取成功')
}
fetchMyPriceList()
}
// 积分
async function handleGetCredits(id) {
// 判断是否为会员
const isVip = await checkIsMember()
if (!isVip) {
commonToast('需加入会员才能领取成功哦', 2000, () => { setMemberVisible(true) })
return
}
const { success, data } = await receiveEnamePrize({ id })
if (success && data) {
commonToast('领取成功')
}
fetchMyPriceList()
}
// 领取实物前询问
async function handleChooseAddress(id) {
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 = {
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) {
handleReceiveObjectPrize(params)
}
}
})
}
const handleClick = useThrottle(async(item) => {
const { type, drawStatus, useUrl = '', id, _id } = item
if (drawStatus === DRAW_STATUS.SUCCESS) {
if (type === PRIZE_TYPE.OBJECT && prizeConfig.hasLogistic) {
setCurrentPrize(item)
setDeliveryModalVisible(true)
return
}
if (type !== PRIZE_TYPE.OBJECT && useUrl) {
navigateToOutside(useUrl)
return
}
}
if (!(drawStatus === DRAW_STATUS.RETRY || drawStatus === DRAW_STATUS.WAITAWARD)) {
return
}
// 领取权益
const prizeId = id || _id
if (type === PRIZE_TYPE.ENAME) return handleGetEquity(prizeId)
// 领取实物
if (type === PRIZE_TYPE.OBJECT) return handleChooseAddress(prizeId)
// 领取积分
if (type === PRIZE_TYPE.CREDITS) return handleGetCredits(prizeId)
}, 2000)
const onClose = () => {
setMemberVisible(false)
}
const onAuthSuccess = () => {
setMemberVisible(false)
}
const onAuthFail = () => {
setMemberVisible(false)
}
return (
<View>
<View className='my-prize-container' style={{ background: `url(${prizeConfig.bg}) no-repeat center top/cover` }}>
<View className='my-prize-label' style={{ background: `url(${prizeConfig.title}) no-repeat`, backgroundSize: '100% 100%' }} />
<View className='my-prize-content' style={{ top: prizeConfig.contentTop }}>
<ScrollView scrollY className='my-prize-content__scroll'>
{myPrizeList.length > 0 &&
<View className='my-prize-list'>
{myPrizeList.map((item, i) => {
return (
<View className='my-prize-item' key={'prize_'+i}>
<Image className='my-prize-item__avatar' src={item.image} />
<View className='my-prize-item__content'>
<View className='my-prize-item__name'>{item.name}</View>
{
prizeConfig.isShowPirzeId &&
<View className='my-prize-item__subtitle'>
<View className='my-prize-item__code'>奖品编码:{item.id || item._id}</View>
{/* <!-- 复制按钮 --> */}
<Image className='my-prize-item__copy' onTap={() => handleCopyId(item.id || item._id)} src={prizeConfig.copyIcon} />
</View>
}
{item.drawStatus === DRAW_STATUS.FAIL && <View className='my-prize-item__tip'>{item.remark}</View>}
{(item.drawStatus === DRAW_STATUS.RETRY || item.drawStatus === DRAW_STATUS.WAITAWARD) && <View className='my-prize-item__tip'>请于{expiredTime}前领取</View>}
{item.drawStatus === DRAW_STATUS.EXPIRED && <View className='my-prize-item__tip'>奖品已过期失效</View>}
</View>
<View className='prize-item__status' style={prizeConfig.btnStyle}>
{item.type === PRIZE_TYPE.OBJECT
? <Image onTap={() => handleClick(item)} src={prizeConfig.objectStatus[item.drawStatus]} mode='widthFix' />
: <Image onTap={() => handleClick(item)} src={(item.drawStatus === 3 && !item.useUrl) ? prizeConfig.receiveBtn : prizeConfig.enameStatus[item.drawStatus]} mode='widthFix' />}
</View>
</View>
)
})}
</View>}
{
myPrizeList.length === 0 && Empty(prizeConfig.blankTxt)
}
</ScrollView>
</View>
</View>
{deliveryModalVisible && <DeliveryModal prizeItem={currentPrize} task={prizeConfig.logisticsImage} onClose={() => setDeliveryModalVisible(false)} />}
{memberVisible && <member-modal onClose={onClose} onAuthFail={onAuthFail} onAuthSuccess={onAuthSuccess} />}
</View>
)
}
export default MyPrizeList
import React, { useEffect, useState, useRef, createRef } from 'react'
import { View } from '@tarojs/components'
import { useDidShow, useRouter, useDidHide, getApp } from '@tarojs/taro'
import API from '@/api'
import { useLogin, useLoginFromShare } from '@/hooks/useLogin'
import { useActivityInfoModel, useLoginInfoModel } from '@/store'
import { prizeList, BARRAGE_LIST } from '@/mock'
import { dateFormatter } from 'tbcc-sdk-ts/lib/utils/index'
import styles from './comsTest.module.less'
import {useAudio} from '@/hooks/useAudio'
import { CLOUD_OBJ, BGMUSIC_URL } from '@/const'
import ContainerFit from '@/components/_base/ContainerFit/ContainerFit'
import ContainerScroll from '@/components/_base/ContainerScroll/ContainerScroll'
import ScrollXView from '@/components/_tb_comps/ScrollXView/ScrollXView'
import SwiperView from '@/components/_tb_comps/SwiperView/SwiperView'
import RotateWheel from '@/components/_tb_comps/RotateWheel/RotateWheel'
import RankTitle from '@/components/_tb_comps/Rank/RankTitle/RankTitle'
import RankList from '@/components/_tb_comps/Rank/RankList/RankList'
import Demo from '@/components/_tb_comps/_Progress/demo'
import Countdown from '@/components/_tb_comps/CountDown/CountDown'
import JackpotModal from '@/components/_tb_modal/JackpotModal'
import { VerticalBarrage } from '@/components/_tb_comps/Barrage'
const rotateConfig = {
bg: 'https://yun.duiba.com.cn/spark/assets/8b6e920ffd09fab8f9ac2de09f9154879f4d0607.png',
ratio: 0.65,
radius: 300,
circles: 4,
divideNum: 8,
duration: 5000,
timeFunction:'ease-in-out',
nodeInfo:{
width:100,
height:100
},
imgInfo:{width:80,height:80},
showWay:'negative',//positive/negative
isShowPrizeName:true,
prizeNameWidthRatio:1.2
}
const SCROLLXVIEW_CONFIG = {
prizeList: [],
containerWidth : 200,
containerHeight : 200,
imgWidth:120,
imgHeight:120,
isShowPrizeName:true,
prizeInCenterNum:3,
marginRight:26,
backgroundColor:'#E7E7EF',
backgroundImage:''
}
const swiper_config = {
easingFunction:`linear`,
circular:true,
autoplay:true,
vertical:false,
duration:1000,
interval:1500,
disableTouch:false,
swiperGroup:{width:300,height:400},
swiperContent:{width:300,height:400},
imgContent:{width:200,height:200},
textContent:{width:300,height:60}
}
const prizeItem = {
id: "6115fc6a3075d9bde4b16a53",
image: "https://img.alicdn.com/imgextra/i4/353042333/O1CN012k7gbk1T6YigovFNu_!!353042333-0-isvtu.jpg",
type: 2,
name: '测试i奖品'
}
export default function ComsTest(props) {
/* 背景音乐 */
const {playAudio,musicUrl,setOpenStatus,openStatus} = useAudio(BGMUSIC_URL.MUSIC,true)
const [endTime, setEndTime] = useState('')
const [prizeModalVisible,setPrizeModalVisible] = useState(false)
const containerRef = createRef()
useEffect(() => {
const _endTime = dateFormatter(Date.now() + 10 * 24 * 60 * 60 * 1000,'yyyy-MM-dd hh:mm:ss')
setEndTime(_endTime)
})
const playCurrentAudio = () => {
setOpenStatus(!openStatus)
}
return(
<>
<ContainerScroll ref={containerRef}>
<View className={styles['scroll-view']}>
<View className={styles['comp-title']}>滑动组件</View>
<ScrollXView prizeList={prizeList} />
</View>
<View className={styles['comp-view']}>
<View className={styles['comp-title']}>swiper左右滑动</View>
<SwiperView swiperList={prizeList} />
</View>
<View className={styles['comp-view']}>
<View className={styles['comp-title']}>大转盘</View>
<RotateWheel />
</View>
<View className={styles['comp-view']}>
<View className={styles['comp-title']}>进度条</View>
<Demo />
</View>
<View className={styles['comp-view']}>
<View className={styles['comp-title']}>排行榜</View>
<RankTitle />
<RankList />
</View>
<View className={styles['comp-view']}>
<View className={styles['comp-title']}>倒计时</View>
{endTime && <Countdown targetTime={endTime} showDay={true} />}
</View>
<View className={styles['comp-view']}>
<View className={styles['comp-title']}>音频hooks</View>
<View className={styles['view']} onTap={playCurrentAudio}>play audio</View>
</View>
<View className={styles['comp-view']} style={{ marginBottom: '0.5rem'}}>
<View className={styles['comp-title']}>竖向弹幕</View>
<VerticalBarrage dataList={BARRAGE_LIST} />
</View>
<View onClick={() => containerRef.current?.scrollFixTop(600)} className={styles['btn-one']}>滚动指定位置</View>
<View onClick={() => setPrizeModalVisible(true)} className={styles['btn-two']}>中奖弹窗</View>
</ContainerScroll>
{ prizeModalVisible && <JackpotModal prizeItem={prizeItem} onClose={() => setPrizeModalVisible(false)} />}
</>
)
}
\ No newline at end of file
.scroll-view {
margin-top: 40px;
}
.comp-view {
margin-top: 50px;
width: 100%;
height: auto;
}
.comp-title {
margin: 80px 0 20px;
}
.pre-btn() {
width: 160px;
height: 40px;
border: 1px solid #000;
display: flex;
justify-content: center;
align-items: center;
font-size: 22px;
position: absolute;
right: 0;
}
.btn-one {
.pre-btn();
top: 200px;
}
.btn-two {
.pre-btn();
top: 260px;
}
\ No newline at end of file
export default {
navigationBarTitleText: '游戏页面',
enableSkia: 'true',
allowsBounceVertical: 'NO',
enableShareAppMessage: true,
usingComponents: {
'member-modal': '../../../components/_miniprogram/memberModal/memberModal',
'canvas-comp': '../../../components/_miniprogram/comBlank/comBlank'
}
}
\ No newline at end of file
import React, { useEffect, useState } from 'react'
import { View } from '@tarojs/components'
import { useDidShow, useRouter, useDidHide, getApp } from '@tarojs/taro'
import API from '@/api'
import { useLogin, useLoginFromShare } from '@/hooks/useLogin'
import { useActivityInfoModel, useLoginInfoModel } from '@/store'
import {
commonToast,
getSystemInfo,
navigateToOutside,
navigateBack,
showSharePanel,
navigateTo,
openDetail
} from 'tbcc-sdk-ts/lib/core/tb'
import { versionCompare, getImgShareUrl } from 'tbcc-sdk-ts/lib/utils'
import styles from './game.module.less'
function GamePage(props) {
const app = getApp()
const { activityInfo, setActivityInfoAndStatus } = useActivityInfoModel()
const { loginInfo } = useLoginInfoModel()
const [canvasData,setCanvasData] = useState({})
const [ taskModalVisible, setTaskModalVisible] = useState(false)
const [ canvasVisible, setCanvasVisible] = useState(false)
const [ memberVisible,setMemberVisible] = useState(false)
const [updateFlag, setUpdateFlag] = useState(1)
useEffect(() =>{
// getImgShareUrl('cloud://2D60DC04FC1888AF6F865B88F0B2EBCC//share.png').then(res => {
// console.log(res)
// })
},[])
useDidShow(() =>{})
useDidHide(() =>{})
//检查版本号
const checkSystem = async () => {
const { version } = await getSystemInfo();
console.log('version', version);
return versionCompare(version, '9.10.0');
}
// 授权后登录
useLogin(res => {
// 登录完成渲染cavans
setCanvasVisible(true)
})
//和main通信的方法,接收main的信息
const onMessage = (e)=> {
// 接口名字 参数
const { netName, parameter } = e.mpEvent.data
console.warn('netName:'+netName,parameter)
//定制的授权
if (netName == "mine.authorize") {
this.getAuth()
}
//获取地址
else if (netName == "mine.getUserAddress") {
this.getUserAddress(parameter.prizeId)
}
//关注店铺的操作
else if (netName == "mine.favorShop") {
this.doFavorShop()
}
//领养名字,有弹框的
else if (netName == "mine.getAdoptName") {
this.setData({ "adoptModalVisible": true, type: parameter.type })
}
//打开详情页,还有成功失败
else if (netName == "mine.openDetail") {
openDetail(parameter.itemId)
}
//前端同步接口
else if (netName.indexOf("mine") == 0) {
let data = { success: true, type: netName }
switch (netName) {
case "mine.getAppData"://获取信息
const { userNick, avatar, openId, newUser, remainTimes, firstLoginToday } = loginInfo
data = {
success: true,
type: netName,
data: {
activityId: app.activityId,//|| "5e7c40e98564b57d7fb95812",
nickName: userNick,
avatar,
openId,
inviteId: app.inviteId,//判断是否要发助力用
isFollow: app.isFollow,
needDataInCanvas: {},
activityInfo: activityInfo,
userInfo: {},
newUser: newUser,
remainTimes: remainTimes,
firstLoginToday: firstLoginToday
}
}
console.warn(data)
break;
case "mine.navigateToOutside"://跳转小程序外的页面
navigateToOutside(parameter.url)
console.log(parameter.url)
break;
case "mine.navigateTo"://跳转小程序内的页面
navigateTo(parameter.url)
console.warn('navigateTo:'+parameter.url)
break;
case "mine.navigateBack"://返回页面
navigateBack()
break;
case "mine.showSharePanel"://分享
//多一个参数
showSharePanel()
break;
case "mine.reportAnalytics": ////淘宝自定义埋点
reportAnalytics(parameter.logkey);
break;
case "mine.openMusic": //音频
playBgMusic(parameter.isOn, this.musicUrl);
break;
case "mine.openTask": //打开任务
setTaskModalVisible(true)
break;
case "mine.openMember": //打开入会
setMemberVisible(true)
break;
}
// commonToast(JSON.stringify(data))
setUpdateFlag(Math.floor(Math.random() * 1000))
setCanvasData(data)
} else {
//获取后端接口
const { function: fc } = app.cloud;
//处理下带上activityId
if (!parameter) parameter = {};
parameter.activityId = app.activityId;
//netName得分割下
fc.invoke(netName.split(".")[0], parameter, netName.split(".")[1])
.then(res => {
// console.log("返回数据" + JSON.stringify(res));
//发送消息
this.postMessage(netName, res || { success: false })
}, (err) => {
//网络异常,返回失败
this.postMessage(netName, { success: false })
})
}
}
// 入会插件
const onClose = () => {
setMemberVisible(false)
}
const onAuthSuccess = () => {
setMemberVisible(false)
}
const onAuthFail = () => {
setMemberVisible(false)
}
const onTestHandle = (e) => {
setMemberVisible(true)
}
return(
<View>
{ canvasVisible && <canvas-comp data={canvasData} onMessage={onMessage} updateFlag={updateFlag} />}
<View className={styles['test-box']} onClick={(e) => onTestHandle(e)}>入会按钮</View>
{<member-modal expend={memberVisible} onClose={onClose} onAuthFail={onAuthFail} onAuthSuccess={onAuthSuccess} />}
</View>
)
}
export default GamePage
\ No newline at end of file
.page-game {
width:100%;
height:100vh;
flex: 1;
display: flex;
flex-direction: column;
// background: #f5cccd;
}
.test-box {
position: absolute;
top: 200px;
width: 200px;
height: 50px;
background-color: aqua;
}
\ No newline at end of file
import React, { useEffect, useRef, useState } from 'react'
import { View } from '@tarojs/components'
import ContainerFit from '@/components/_base/ContainerFit/ContainerFit'
import GoodsTitle from '@/components/_tb_comps/TitleImg/TitleImg'
import GoodsList from '@/components/_tb_comps/GoodsList/GoodsList'
import BrowserLoading from '@/components/_tb_comps/BrowserLoading/BrowserLoading'
import { useDidShow, useRouter, useDidHide } from '@tarojs/taro'
import API from '@/api'
import { commonToast } from 'tbcc-sdk-ts/lib/core/tb'
import styles from './browseGoods.module.less'
import { TASK_CONFIG, BROSE_GOOD_TYPE } from '@/config/task.config'
import { BROWSE_CONFIG } from '@/config/browse.config'
import { GOOD_LIST } from '@/mock'
function BrowseGoodsPage() {
const { params: { itemIds, keepTime } } = useRouter()
const { browseType, browseTime = 15 } = TASK_CONFIG
const [ startTimer, setStartTimer ] = useState(false)
const preBrowseTime = useRef(null)
const currentItemId = useRef('')
const usePercent = useRef(0)
const [accumulateTime, setAccumulateTime] = useState(+keepTime || 15)
const browserTimer = useRef(null)
const [goodsList, setGoodsList] = useState(BROWSE_CONFIG.isMock?GOOD_LIST:[])
useEffect(() => {
getGoodsList()
}, [])
// 浏览集合页15s
useEffect(() => {
if(browseType === BROSE_GOOD_TYPE.PAGE) {
(!browserTimer.current && goodsList.length) && browseGoodsTimes()
return () => browserTimer.current && clearInterval(browserTimer.current)
}
},[goodsList])
// 获取商品列表
const getGoodsList = async () => {
const { success, data } = await API.getCollectGoods({ key: BROWSE_CONFIG.taskType, itemIds })
if(success && !BROWSE_CONFIG.isMock ) {
setGoodsList(data?.list || [])
}
}
// 页面显示
useDidShow(() => {
// 重新回到页面开启倒计时
if(browseType === BROSE_GOOD_TYPE.PAGE && startTimer && usePercent.current < +browseTime) {
browseGoodsTimes()
}
console.warn('回到该页面了')
if (browseType === BROSE_GOOD_TYPE.DETAIL && preBrowseTime.current && ((Date.now() - preBrowseTime.current) / 1000) >= browseTime) {
onCompleteTask()
currentItemId.current = ''
preBrowseTime.current = null
} else if(preBrowseTime.current) {
commonToast(`未达到${browseTime}秒~`)
currentItemId.current = ''
preBrowseTime.current = null
}
})
useDidHide(() => {
// 页面关闭清除定时器
if(browserTimer.curren) {
clearInterval(browserTimer.current)
setStartTimer(true)
}
})
// 浏览记录
const onCompleteTask = async(itemId) => {
const { success } = await API.completeTask({ key: BROWSE_CONFIG.taskType, itemId: currentItemId.current })
if (success) {
getGoodsList()
commonToast('浏览成功')
}
}
// 点击商品 跳转时处理
const onOpenDetail = (itemId, collected) => {
const handleFn = {
[BROSE_GOOD_TYPE.PAGE]: () => {
setStartTimer(true)
clearInterval(browserTimer.current)
return;
},
[BROSE_GOOD_TYPE.CLICK]: () => {
currentItemId.current = itemId
onCompleteTask()
},
[BROSE_GOOD_TYPE.DETAIL]: () => {
if(collected) {
commonToast('该商品已经浏览过了')
return;
}
preBrowseTime.current = Date.now()
currentItemId.current = itemId
}
}
handleFn[browseType]()
}
// 浏览页面定时器
const browseGoodsTimes = () => {
browserTimer.current = setInterval(() => {
const keepTime_ = browseTime
console.log('enter the browse time ');
usePercent.current += 1
setAccumulateTime(+keepTime_ - usePercent.current)
console.warn(usePercent.current,keepTime_)
if (usePercent.current >= keepTime_) {
onCompleteTask()
clearInterval(browserTimer.current)
usePercent.current = 0
setAccumulateTime(0)
}
}, 1000)
}
return (
<ContainerFit bg={BROWSE_CONFIG.bg}>
<View className={styles['page-content__title']}>
<GoodsTitle config={BROWSE_CONFIG.head}/>
</View>
<View className={styles['page-content__list']}>
<GoodsList goodsList={goodsList} task={BROWSE_CONFIG} onOpenDetail={(itemId,collected) => onOpenDetail(itemId,collected)} />
</View>
{
BROWSE_CONFIG.countDown &&
<BrowserLoading loadFlag={BROWSE_CONFIG.countDown} count={accumulateTime} />
}
</ContainerFit>
)
}
export default BrowseGoodsPage
.page-content__title {
.wh(750px,208px);
}
.page-content__list {
position: absolute;
top: 208px;
left: 0;
bottom: 0;
padding: 41px 25px 0;
width: 750px;
}
\ No newline at end of file
export default {
navigationBarTitleText: '加购商品',
enableSkia: 'true',
allowsBounceVertical: 'NO',
enableShareAppMessage: true
}
import React, { useState, useEffect, useRef } from 'react'
import { View } from '@tarojs/components'
import ContainerFit from '@/components/_base/ContainerFit/ContainerFit'
import TitleImg from '@/components/_tb_comps/TitleImg/TitleImg'
import GoodsList from '@/components/_tb_comps/GoodsList/GoodsList'
import { useRouter } from '@tarojs/taro'
import tbccTs from 'tbcc-sdk-ts'
import API from '@/api'
import { useThrottle } from '@/hooks/useThrottle'
import styles from './cartGoods.module.less'
import { CART_CONFIG, CART_TYPE } from '@/config/cart.config'
const { commonToast, showSkuModal } = tbccTs.tb
function CartGoodsPage() {
const router = useRouter()
const [goodsList, setGoodsList] = useState([])
const { itemIds } = router?.params
// 收藏状态
const collectflag = useRef(false)
useEffect(() => {
getGoodsList()
}, [])
const getGoodsList = async () => {
const { data } = await API.getCollectGoods({ key: CART_CONFIG.taskType, itemIds })
setGoodsList(data?.list || [])
}
// 加入购物车 // 对应itemId为 574141925233 skuId为4018047819826 数量为4
const onCompleteTask = useThrottle(async (itemId, collected) => {
console.log(itemId)
if (collected && CART_CONFIG.type === CART_TYPE.ONE) {
commonToast('您已加购过该商品了')
return;
}
if(collectflag.current && CART_CONFIG.type === CART_TYPE.ONE) {
commonToast('该任务今天已完成,明天再来吧~')
}
const { status, quantity } = await showSkuModal(String(itemId))
if (status == "addCartSuccess") {
const { success } = await API.completeTask({ key: CART_CONFIG.taskType, itemId, count: quantity })
if (success) {
collectflag.current = true
getGoodsList()
commonToast('加购成功')
}
}
}, 2000);
return (
<ContainerFit bg={CART_CONFIG.bg}>
<View className={styles['page-content__title']}>
<TitleImg config={CART_CONFIG.head}/>
</View>
<View className={styles['page-content__list']}>
<GoodsList goodsList={goodsList} task={CART_CONFIG} onOpenDetail={(itemId,collected) => onCompleteTask(itemId,collected)} />
</View>
</ContainerFit>
)
}
export default CartGoodsPage
.page-content__title {
.wh(750px,208px);
}
.page-content__list {
position: absolute;
top: 208px;
left: 0;
bottom: 0;
padding: 41px 25px 0;
width: 750px;
}
\ No newline at end of file
import React, { useState, useEffect, useRef } from 'react'
import { View } from '@tarojs/components'
import ContainerFit from '@/components/_base/ContainerFit/ContainerFit'
import TitleImg from '@/components/_tb_comps/TitleImg/TitleImg'
import GoodsList from '@/components/_tb_comps/GoodsList/GoodsList'
import { useRouter } from '@tarojs/taro'
import tbccTs from 'tbcc-sdk-ts'
import API from '@/api'
import { useThrottle } from '@/hooks/useThrottle'
const { commonToast } = tbccTs.tb
import styles from './collectGoods.module.less'
import COLLECT_CONFIG from '@/config/collect.config'
function CollectGoodsPage() {
const router = useRouter()
const [goodsList, setGoodsList] = useState([])
const { itemIds } = router?.params
// 收藏状态
const collectFlag = useRef(false)
useEffect(() => {
getGoodsList()
}, [])
const getGoodsList = async () => {
const { data } = await API.getCollectGoods({ key: COLLECT_CONFIG.taskType, itemIds })
setGoodsList(data?.list || [])
}
const onCompleteTask = useThrottle(async(itemId,collected) => {
const collectFn = async () => {
const { success } = await API.completeTask({ key: taskType, itemId })
if (success) {
collectFlag.current = true
getGoodsList()
commonToast('收藏成功')
}
}
// 今日是否已收藏过
if(collectFlag.current) {
commonToast('任务已完成,请明日再来')
return;
}
if (collected) {
commonToast('您已收藏过该商品了')
return;
}
// 判断是否活动外已收藏商品
const isCollected = await checkGoodsCollectedStatus(+itemId)
if (isCollected) {
collectFn()
return;
}
const result = await collectGoods(+itemId)
if (result) {
collectFn()
return;
}
})
return (
<ContainerFit bg={COLLECT_CONFIG.bg}>
<View className={styles['page-content__title']}>
<TitleImg config={COLLECT_CONFIG.head}/>
</View>
<View className={styles['page-content__list']}>
<GoodsList goodsList={goodsList} task={COLLECT_CONFIG} onOpenDetail={(itemId,collected) => onCompleteTask(itemId,collected)} />
</View>
</ContainerFit>
)
}
export default CollectGoodsPage
.page-content__title {
.wh(750px,208px);
}
.page-content__list {
position: absolute;
top: 208px;
left: 0;
bottom: 0;
padding: 41px 25px 0;
width: 750px;
}
\ No newline at end of file
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { View } from '@tarojs/components' import { View } from '@tarojs/components'
import GoodsPage from '@/components/GoodsPage/GoodsPage' import ContainerFit from '@/components/_base/ContainerFit/ContainerFit'
import TitleImg from '@/components/_tb_comps/TitleImg/TitleImg'
import GoodsList from '@/components/_tb_comps/GoodsList/GoodsList'
import { useRequest } from '@/hooks/useRequest' import { useRequest } from '@/hooks/useRequest'
import { useRouter } from '@tarojs/taro' import { useRouter } from '@tarojs/taro'
import API from '@/api' import API from '@/api'
import styles from './orderGoods.module.less'
const browseConfig = { import ORDER_CONFIG from '@/config/order.config'
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'
}
function OrderGoodsPage() { function OrderGoodsPage() {
const router = useRouter() const router = useRouter()
const { itemIds } = router?.params const { itemIds } = router?.params
const { data } = useRequest(API.getItemListByItemIds, { itemIds }) const { data } = useRequest(API.getCollectGoods, { key: ORDER_CONFIG.taskType, itemIds })
return ( return (
<View> <ContainerFit bg={ORDER_CONFIG.bg}>
<GoodsPage goodsList={data?.list || []} task={browseConfig} /> <View className={styles['page-content__title']}>
<TitleImg config={ORDER_CONFIG.head}/>
</View> </View>
<View className={styles['page-content__list']}>
<GoodsList goodsList={data?.list || []} task={ORDER_CONFIG} onOpenDetail={(itemId,isBrowsed) => onOpenDetail(itemId,isBrowsed)} />
</View>
</ContainerFit>
) )
} }
......
.page-content__title {
.wh(750px,208px);
}
.page-content__list {
position: absolute;
top: 208px;
left: 0;
bottom: 0;
padding: 41px 25px 0;
width: 750px;
}
\ No newline at end of file
...@@ -4,6 +4,6 @@ export default { ...@@ -4,6 +4,6 @@ export default {
allowsBounceVertical: 'NO', allowsBounceVertical: 'NO',
enableShareAppMessage: true, enableShareAppMessage: true,
usingComponents: { usingComponents: {
'member-modal': '../../components/_miniprogram/memberModal/memberModal' 'member-modal': '../../../components/_miniprogram/memberModal/memberModal'
} }
} }
import React, { useState, useEffect } from 'react'
import { Image, ScrollView, View } from '@tarojs/components'
import API from '@/api'
import DeliveryModal from '@/components/_tb_modal/DeliveryModal/DeliveryModal'
import ContainerFit from '@/components/_base/ContainerFit/ContainerFit'
import TitleImg from '@/components/_tb_comps/TitleImg/TitleImg'
import tbccTs from 'tbcc-sdk-ts'
import { useThrottle } from '@/hooks/useThrottle'
import { dateFormatter } from '@/utils/date'
import styles from './myPrize.module.less'
import { useDidHide, getApp } from '@tarojs/taro'
import useReceive from '@/hooks/useReceive'
import { PRIZE_CONFIG, DRAW_STATUS, PRIZE_TYPE } from '@/config/myPrize.config'
const { objectStatus: _objectStatus, enameStatus: _enameStatus, orderStatus, commonStatus } = PRIZE_CONFIG
const objectStatus = { ..._objectStatus, ...commonStatus }
const enameStatus = { ..._enameStatus, ...commonStatus }
const { commonToast, navigateToOutside, setClipboard } = tbccTs.tb
function Empty(blankTxt) {
return <View className={styles['my-prize-item__empty']}>{blankTxt}</View>
}
function MyPrizeList() {
const app = getApp()
const { receivePrize } = useReceive({ successMsg: '领取成功', errorMsg: '' })
const [ myPrizeList, setMyPrizeList ] = useState([])
const [ expiredTime, setExpiredTime ] = useState('')
const [ currentPrize, setCurrentPrize ] = useState({})
const [ memberVisible, setMemberVisible ] = useState(false)
const [ deliveryModalVisible, setDeliveryModalVisible ] = useState(false)
useEffect(() => {
fetchMyPriceList()
}, [])
async function fetchMyPriceList() {
const res = await API.getMyAwardsList()
if (res.success && res?.data?.list?.length) {
setMyPrizeList(res?.data?.list)
}
if (res?.data?.expiredTime) {
setExpiredTime(dateFormatter(res?.data?.expiredTime, 'yyyy/MM/dd hh:mm:ss'))
}
}
// 复制编码
const handleCopyId = async(text) => {
await setClipboard(text)
commonToast('复制成功')
}
// 领取奖品
const handleClick = useThrottle(async (item) => {
const { activityId } = app
const { type, drawStatus, useUrl = '', itemId = '' } = item
// 领取前验证
if (drawStatus === DRAW_STATUS.SUCCESS) {
if (type === PRIZE_TYPE.OBJECT && PRIZE_CONFIG.hasLogistic) {
setCurrentPrize(item)
setDeliveryModalVisible(true)
return
}
if (type !== PRIZE_TYPE.OBJECT && useUrl) {
navigateToOutside(useUrl)
return
}
}
if (!(drawStatus === DRAW_STATUS.RETRY || drawStatus === DRAW_STATUS.WAITAWARD)) {
return
}
// 执行奖品领取
receivePrize(item,{
vipCallback:()=>{
console.log('enter the vip callback function');
setMemberVisible(true)
},
updateCallback:()=>{
console.log('enter the update list callback function');
fetchMyPriceList()
}
});
},1500)
// 入会插件
const onClose = () => {
setMemberVisible(false)
}
const onAuthSuccess = () => {
setMemberVisible(false)
}
const onAuthFail = () => {
setMemberVisible(false)
}
return (
<>
<ContainerFit bg={PRIZE_CONFIG.bg}>
<View className={styles['my-prize-label']}>
<TitleImg config={PRIZE_CONFIG.head}></TitleImg>
</View>
<View className={styles['my-prize-content']} style={{ top: PRIZE_CONFIG.contentTop }}>
<ScrollView scrollY className={styles['my-prize-content__scroll']}>
{myPrizeList.length > 0 &&
<View className={styles['my-prize-list']}>
{myPrizeList.map((item, i) => {
return (
<View className={styles['my-prize-item']} key={'prize_'+i}>
<Image className={styles['my-prize-item__avatar']} src={item.image} />
<View className={styles['my-prize-item__content']}>
<View className={styles['my-prize-item__name']}>{item.name}</View>
{
PRIZE_CONFIG.isShowPirzeId &&
<View className={styles['my-prize-item__subtitle']}>
<View className={styles['my-prize-item__code']}>奖品编码:{item.id || item._id}</View>
{/* <!-- 复制按钮 --> */}
<Image className={styles['my-prize-item__copy']} onTap={() => handleCopyId(item.id || item._id)} src={PRIZE_CONFIG.copyIcon} />
</View>
}
{item.drawStatus === DRAW_STATUS.FAIL && <View className={styles['my-prize-item__tip']}>{item.remark}</View>}
{(item.drawStatus === DRAW_STATUS.RETRY || item.drawStatus === DRAW_STATUS.WAITAWARD) && <View className={styles['my-prize-item__tip']}>请于{expiredTime}前领取</View>}
{item.drawStatus === DRAW_STATUS.EXPIRED && <View className={styles['my-prize-item__tip']}>奖品已过期失效</View>}
</View>
<View className={styles['prize-item__status']} style={PRIZE_CONFIG.btnStyle}>
{item.type === PRIZE_TYPE.OBJECT
? <Image onTap={() => handleClick(item)} src={objectStatus[item.drawStatus]} mode='widthFix' />
: <Image onTap={() => handleClick(item)} src={(item.drawStatus === 3 && !item.useUrl) ? PRIZE_CONFIG.receiveBtn : enameStatus[item.drawStatus]} mode='widthFix' />}
</View>
</View>
)
})}
</View>}
{
myPrizeList.length === 0 && Empty(PRIZE_CONFIG.blankTxt)
}
</ScrollView>
</View>
</ContainerFit>
{deliveryModalVisible && <DeliveryModal prizeItem={currentPrize} task={PRIZE_CONFIG.logisticsImage} onClose={() => setDeliveryModalVisible(false)} />}
{<member-modal expend={memberVisible} onClose={onClose} onAuthFail={onAuthFail} onAuthSuccess={onAuthSuccess} />}
</>
)
}
export default MyPrizeList
\ No newline at end of file
.my-prize-container { .my-prize-label {
width: 100vw; width: 100%;
height: 100vh; height: 208px;
opacity: 1;
position: absolute;
top: 0;
left: 0;
overflow: hidden;
/* background: url('') no-repeat center top/cover; */
}
.my-prize-container .my-prize-label {
width: 208px;
height: 36px;
margin: 140px auto 40px;
background: url("//yun.duiba.com.cn/duiba-components-c-myprizeprize_title.png")
no-repeat;
background-size: 100% 100%;
} }
.my-prize-content { .my-prize-content {
position: absolute; position: absolute;
left: 0; left: 0;
...@@ -30,19 +14,16 @@ ...@@ -30,19 +14,16 @@
height: 100%; height: 100%;
padding-top: 30px; padding-top: 30px;
} }
.my-prize-list {
.my-prize-container .my-prize-list {
width: 100%; width: 100%;
height: auto; height: auto;
} }
.my-prize-item__empty {
.my-prize-container .my-prize-item__empty {
text-align: center; text-align: center;
color: #fff; color: #fff;
margin-top: 30px; margin-top: 30px;
} }
.my-prize-item {
.my-prize-list .my-prize-item {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
...@@ -57,13 +38,11 @@ ...@@ -57,13 +38,11 @@
border-radius: 10px; border-radius: 10px;
box-sizing: border-box; box-sizing: border-box;
} }
.my-prize-item__avatar {
.my-prize-list .my-prize-item .my-prize-item__avatar {
width: 120px; width: 120px;
height: 120px; height: 120px;
} }
.my-prize-item__content {
.my-prize-list .my-prize-item .my-prize-item__content {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
padding: 0 20px; padding: 0 20px;
...@@ -73,28 +52,23 @@ ...@@ -73,28 +52,23 @@
align-items: flex-start; align-items: flex-start;
justify-content: center; justify-content: center;
} }
.my-prize-item__subtitle {
.my-prize-list .my-prize-item .my-prize-item__subtitle {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.my-prize-item__copy {
.my-prize-list .my-prize-item .my-prize-item__copy {
width: 20px; width: 20px;
height: 20px; height: 20px;
margin-left: 18px; margin-left: 18px;
} }
.prize-item__status {
.my-prize-list .my-prize-item .prize-item__status {
width: 150px; width: 150px;
} }
.prize-item__status > image { .prize-item__status > image {
width: 100%; width: 100%;
} }
.my-prize-item__name {
.my-prize-list .my-prize-item .my-prize-item__name {
width: 100%; width: 100%;
font-size: 28px; font-size: 28px;
color: #2d2d2d; color: #2d2d2d;
...@@ -103,16 +77,14 @@ ...@@ -103,16 +77,14 @@
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.my-prize-item__tip {
.my-prize-list .my-prize-item .my-prize-item__tip {
margin-top: 8px; margin-top: 8px;
font-size: 20px; font-size: 20px;
line-height: 20px; line-height: 20px;
letter-spacing: 0; letter-spacing: 0;
color: #8a8a8a; color: #8a8a8a;
} }
.my-prize-item__code {
.my-prize-list .my-prize-item .my-prize-item__code {
width: 290px; width: 290px;
font-family: PingFangSC-Regular; font-family: PingFangSC-Regular;
font-size: 20px; font-size: 20px;
......
import { useState } from 'react' import { useState, useReducer } from 'react'
import { createModel } from 'hox' import { createModel } from 'hox'
function useLoginInfo() { function useLoginInfo() {
const [ loginInfo, setLoginInfo ] = useState({ const [ loginInfo, setLoginInfo ] = useState({
isLogin: false,
newUser: '', newUser: '',
openId: '', openId: '',
firstLoginToday: '', firstLoginToday: '',
...@@ -10,7 +11,6 @@ function useLoginInfo() { ...@@ -10,7 +11,6 @@ function useLoginInfo() {
avatar: '', avatar: '',
inviteId: '' inviteId: ''
}) })
return { return {
loginInfo, loginInfo,
setLoginInfo setLoginInfo
......
import { useState } from 'react'
import { createModel } from 'hox'
function useUserInfo() {
const [ activityInfo, setUserInfo ] = useState({
success: false,
gameTimes: 0
})
return {
activityInfo,
setUserInfo
}
}
export const useUserInfoModel = createModel(useUserInfo)
...@@ -38,15 +38,18 @@ ...@@ -38,15 +38,18 @@
align-items: stretch; align-items: stretch;
} }
.flex-row-left() { .flex-row-left() {
.flex-row-normal();
justify-content: flex-start; justify-content: flex-start;
} }
.flex-row-center() { .flex-row-center() {
.flex-row-normal();
justify-content: center; justify-content: center;
} }
.flex-row-right() { .flex-row-right() {
justify-content: flex-end; justify-content: flex-end;
} }
.flex-row-space() { .flex-row-space() {
.flex-row-normal();
justify-content: space-between; justify-content: space-between;
} }
.flex-row-wrap() { .flex-row-wrap() {
...@@ -95,6 +98,7 @@ ...@@ -95,6 +98,7 @@
align-items: flex-start; align-items: flex-start;
} }
.flex-col-center() { .flex-col-center() {
.flex-col-normal();
align-items: center; align-items: center;
} }
.flex-col-right() { .flex-col-right() {
......
...@@ -9,3 +9,4 @@ ...@@ -9,3 +9,4 @@
@import "./text.less"; @import "./text.less";
@import "./utils.less"; @import "./utils.less";
@import "./image.less"; @import "./image.less";
@import "./page.less";
.page-container() {
position: relative;
top: 0;
right: 0;
left: 0;
width: 100vw;
height: 100vh;
min-height: 100vh;
overflow: hidden;
}
.page-container__1624() {
position: absolute;
width: 100vw;
height: 1624px;
left: 0;
right: 0;
top: 50%;
transform: translate(0,-50%);
}
.page-content() {
position: absolute;
width: 100vw;
height: 100vh;
left: 0;
right: 0;
top: 50%;
transform: translate(0,-50%);
}
\ No newline at end of file
/**
* 创建音频类
*/
class AudioModel {
constructor(opts = {}) {
const { src } = opts;
if (!src) {
console.error("请输入音频链接");
return false;
}
try {
const innerAudioContext = my.createInnerAudioContext();
Object.keys(opts).forEach(item => {
innerAudioContext[item] = opts[item];
});
return innerAudioContext;
} catch (e) {
console.error("当前版本不支持音频");
}
}
}
/**
* 创建音频实例
* @param {object} opts 配置对象 例 { src: '音频链接', autoplay: false }
*/
export const createAudio = opts => {
const audioInstance = new AudioModel(opts);
return audioInstance;
};
/**
* 获取系统信息
* @returns {object} 小程序系统信息
*/
export const getSystemInfo = () => {
return new Promise((resolve, reject) => {
my.getSystemInfo({
success: res => {
resolve(res);
},
fail: err => {
reject(err);
}
});
});
};
let bgs = {
}
export const playBgMusic1 = async (isOn, src, callbackFn=()=>{}, loop=false) => {//待测试
for (let k in bgs) {
if (bgs[k].isOn) {
bgs[k].stop();
}
// bgs[k].isOn = false; /// TODO 自己记一个开关,因为淘宝的玩意stop之后虽然听不到,但还是在播放,会触发loop播放和onEnded事件
}
const key = src;
console.log('---------->>>>>>>>>>>>>ison',isOn);
if (isOn) {
if (!bgs[key]) {
console.log('enter inner before the createAudio function',src,loop);
bgs[key] = createAudio({
src: src,
autoplay: true,
loop: loop /// TODO 暂时无效?2020.08.12 目前有效,但stop后还会跑数据,导致到音频结束到时间又自动播放
});
console.log('----------------src',src,bgs[key],'bgs[key]')
bgs[key].onPlay(() => {
bgs[key].isOn = true;
});
bgs[key].onStop(() => {
bgs[key].isOn = false;
});
const systemInfo = await getSystemInfo();
console.log(systemInfo,'systemInfo-------systemInfo')
/// TODO监听结束,因为loop无效 2020.08.12 loop有效,这个操作是为了解决stop之后还会跑音频数据的问题
bgs[key].onEnded(() => {
if (bgs[key].isOn) {
// 判断自己记录的开关
if(systemInfo?.platform.includes('Android')){
// 安卓
bgs[key].play();
} else if(systemInfo?.platform.includes('iOS') || systemInfo?.platform.includes('iPhone OS ')){
// ios
bgs[key].isOn = false;
}
delete bgs[key];
callbackFn && callbackFn();
}
});
bgs[key].play(); // 安卓有时候autoplay好像莫得用
// console.log(src)
// bgs[key].isOn = true; /// TODO 自己记一个开关,因为淘宝的玩意stop之后虽然听不到,但还是在播放,会触发loop播放和onEnded事件
} else {
console.log('---------->>>>>>>>>>>>>not enter ');
bgs[key].play();
// bgs[key].isOn = true; /// TODO 自己记一个开关,因为淘宝的玩意stop之后虽然听不到,但还是在播放,会触发loop播放和onEnded事件
callbackFn && callbackFn();
}
} else {
if (bgs[key]) {
// bgs[key].pause(); /// pause 垃圾,不能用
console.log('test stop function is work or not')
bgs[key].stop();
// bgs[key].isOn = false; /// TODO 自己记一个开关,因为淘宝的玩意stop之后虽然听不到,但还是在播放,会触发loop播放和onEnded事件
callbackFn && callbackFn();
} else {
callbackFn && callbackFn();
}
}
}
\ No newline at end of file
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
*/ */
export const dateFormatter = (date, format = 'yyyy/MM/dd') => { export const dateFormatter = (date, format = 'yyyy/MM/dd') => {
if (!date) return '-' if (!date) return '-'
date = new Date( date = new Date(
// eslint-disable-next-line no-restricted-globals // eslint-disable-next-line no-restricted-globals
typeof date === 'string' && isNaN(date) ? date.replace(/-/g, '/') : date typeof date === 'string' && isNaN(date) ? date.replace(/-/g, '/') : date
......
import tbccTs from 'tbcc-sdk-ts'
import API from '@/api'
const { getUserAddress, commonToast } = tbccTs.tb
export const noopFn = () => { } export const noopFn = () => { }
// 领取实物
const getUerAddressParam = async () => {
const userAddress = await getUserAddress().catch(err => {
commonToast(err.errorMessage);
});
if(!userAddress) return false
const { name, telNumber, provinceName, cityName, cityCode, countyName, detailInfo, streetName } = userAddress || {};
const params = {
name,
phone: telNumber,
addressDetail: detailInfo,
cityCode,
city: cityName,
province: provinceName,
area: countyName,
streetName,
}
return {
params,
duibaAddress: userAddress.duibaAddress.address
}
}
const confirmAddress = async (address) => {
return new Promise(async (resolve,reject) => {
my.confirm({
title: '提示',
content: '确认使用该收货地址:' + address,
success: (res) => {
if(res.confirm) {
resolve(true)
}else {
resolve(false)
}
},
});
})
}
export const receiveObjectPrize = async (data) => {
const { params, successMsg, errorMsg } = data
return new Promise(async (resolve,reject) => {
const userInfo = await getUerAddressParam()
if(!userInfo) {
resolve(false)
return;
}
const { params: _params, duibaAddress } = userInfo
const { name, phone } = _params
const address = name + phone + duibaAddress
const canPass = await confirmAddress(address)
if(!canPass) {
resolve(false)
return;
}
const { success, data, message } = await API.receiveObjectPrize({ ...params, ..._params })
if (success) {
commonToast(successMsg)
resolve(true)
}else {
commonToast(errorMsg || message)
resolve(false)
}
})
}
// 领取权益
export const receiveEnamePrize = async (data) => {
const { params, successMsg, errorMsg } = data
return new Promise(async (resolve,reject) => {
const { success, data, message } = await API.receiveEnamePrize(params).catch(res => {
commonToast(successMsg || res.message)
resolve(false)
});
if (success) {
commonToast(successMsg)
resolve(true)
}else {
commonToast(errorMsg || message)
resolve(false)
}
})
}
// 领取会员优惠券
const applyActivity = (sellerId, activityId) => {
const memberBenefitPlugin = requirePlugin("memberBenefit")
if(!memberBenefitPlugin) return false
return new Promise(async (resolve,reject) => {
memberBenefitPlugin.applyActivity({
data: {
sellerId,
activityId,
},
success: (result) => {
resolve(result)
},
fail: (error) => {
console.warn('领取商家权益失败'+JSON.stringify(error.data))
resolve(error.data)
}
});
})
}
export const receiveMemberEname = async(data) => {
const { params, successMsg, errorMsg } = data
const { sellerId, ename, ...ext } = params
if(!ename) return false
return new Promise(async (resolve,reject) => {
const result = await applyActivity(sellerId,ename)
if(!result) resolve(false)
if(result.businessSuccess) {
const { success, data, message } = await API.updateEnamePrizeReceived({ ...ext })
if(success) {
commonToast(successMsg || '奖品发放成功,请前往我的奖品处进行使用')
resolve(true)
}else {
commonToast(errorMsg || message)
resolve(false)
}
}else{
commonToast(result.errorMsg)
resolve(false)
}
})
}
//打点
export const addStat = async(activityId, type)=> {
return new Promise(async (resolve,reject) => {
const result = await API.addStat({ activityId, type }).catch(res => {
commonToast(res && res.message);
});
if (result && result.success) {
resolve(true)
}
resolve(false)
})
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
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