@(项目总结)[中东许愿池, 2016-12-20]html
[TOC]java
用 <cms:custom></cms>
标签包裹须要配置的内容react
在结构中 <%=data%>
方式展现数据android
<cms:custom name="表格" alias="alias" fields="name:实物奖品,pic:对应礼物,des:商品描述,num:星星数量" row="10" defaultRow="1" id="2809c7905e23fa827e53354b63e0d259"> <% data.forEach(function(item, index){ %> <dd> <p class="name"><%=item.name%></p> <p class="pic"> <img src="<%=item.pic%>" /> <em><%=item.des%></em> </p> <p class="num"><%=item.num%></p> </dd> <%})%> </cms>
可配置多语言key、替换逻辑中固定名称、支持标签在多语言中的运用ios
lang.template('须要翻译的文案')
代替须要翻译的文案zenbone lang key
命令 key 文件zenbone lang file
命令生成翻译文件lang.template('须要翻译的文案')
{lang.template('Up号: <%=code%>', { code: data.upliveCode})}
<ul className="rule" dangerouslySetInnerHTML={{__html: lang.template('活动规则')}}></ul>
"googleSpreadsheetId": "1EBZIWdCrGVmtxfxdvdETWnmlwpghPe1o1FbZjdnE4AU", "googleSpreadsheetIndex": 0,
<p>{lang.template('活动时间')}</p>
<span className="uplivecode"> {lang.template('Up号: <%=code%>', { code: data.upliveCode})} </span> /********lang文件中这样设置*********/ 'Up号: <%=code%>': 'Up賬號 : <%=code%>'
<ul className="rule" dangerouslySetInnerHTML={{__html: lang.template('活动规则')}}></ul> /************lang文件中这样设置***********/ "活动规则":"<li>活動規則</li>" + "<li>1.在活動期間內總U幣高過十萬,且活動開始前已有U幣不高於九萬五。</li>" + "<li>2.獎勵將在活動結束後聯絡到相關主播發放</li>" + "<li>3.活動數據會有延遲,依照官方時間為準。</li>",
@(个人第一个笔记本)[十万主播项目得来][up-lakhanchor]web
[TOC] @(万圣节活动项目得来)[2016-12-20, up-halloweenrank]ajax
管理各个不用页面的跳转json
import { Router, Route, hashHistory, useRouterHistory} from 'react-router'; import { createHashHistory } from 'history'; const appHistory = useRouterHistory(createHashHistory)({ queryKey: false });
render(( <Router history={appHistory}> <Route path="/" component={Home}/>//没有任何参数是为Home模块 <Route path="/main" component={Main}/>//参数是main时为Main模块 <Route path="*" component={Home}/>//默认设置为Home模块(*--为其余全部的参数下) </Router> ), $('#wrap')[0]);
根据需求修改从服务器请来的参数作为全局属性在业务中使用api
var getActivityInfo = function (){//请求数据 let activityId = url.query('activityId'); let promise = http.fetch({ url: api.getActivityInfo(), data:{ activityId } }) return promise; } getActivityInfo().done((res)=>{ let periodList = res.data.periodList.slice(1); //获取请求数据中须要的一段 window._global_periodList = periodList.map((item, index) =>{//将修改后的数据付给window._global_periodList,periodList为一个数组须要循环改变数组中的每一项 let _lang = lang.getLangType(); let activityTime; let dateStart,dateEnd; if (_lang == 'areg'){//更改须要的数据 dateStart = formatData(item.startTime*1000, 'dd/MM HH:mm',12); dateEnd = formatData(item.endTime*1000, 'dd/MM HH:mm',12); activityTime = dateStart +'——' +dateEnd ; } else { dateStart = formatData(item.startTime*1000, 'MM月dd日 HH:mm'); dateEnd = formatData(item.endTime*1000, 'MM月dd日 HH:mm'); activityTime = dateStart +'——' +dateEnd ; } return {//返回一个对象,这个对象就是须要的数据 title: lang.template('第' + (index + 1 )+ '天'), day: index + 1, startTime: item.startTime , endTime: item.endTime , activityTime: activityTime } }); })
console.log(global_periodList) /**********显示数据*************/ [ { title:第一天, day: 1, startTime:1408372402 , endTime:14239789 , activityTime: 10月28日 22:00——10月29日 21:59 }, { title:次日, day: 2, startTime:1408376789 , endTime:1423668999 , activityTime: 10月29日 22:00——10月30日 21:59 } ]
从服务器获取每日开始、结束时间、当前时间戳,显示距离当天活动结束时间倒计时,tab默认选中当天。数组
在配置信息中填写对应数组 index+1 为第几天
import Timer from './base/timer';
constructor(props) { super(props); this.state = { //设置默认阈值 isDisplayDaojishi: 0, currentDay: 1 } } getServerTime(serverTime){ //serverTime 从每一个活动榜单接口中返回当前时间戳 let self = this; let cutter; let activityEndTime; let activityInfo = _global_periodList; $.each(activityInfo, function(index , item){ let startTime = item.startTime; let endTime = item.endTime; if (serverTime >= startTime && serverTime < endTime){//获取如今是活动的第几天 cutter = endTime - serverTime;//计算当天距离活动还有多少时间 self.setState({ currentDay: item.day//设置当前项中天数为默认选中的第几天 }); return false; } }); activityEndTime = activityInfo[activityInfo.length-1]['endTime'];//最后一天的结束时间 if(serverTime >= activityEndTime){//若活动已经结束最后一天作为当前选择天 self.setState({ currentDay: activityInfo[activityInfo.length-1]['day'] }); } if (cutter) {//若是有当天倒计时时间开启倒计时阈值 this.setState({ isDisplayDaojishi: 1 }); self.daojishi(cutter); } loading.hide(); } daojishi(cutter){ //调用倒计时插件 var timer = new Timer({ remainTime: cutter, // remainTime: 10, selector: { day: '#day', hour: '#hour', minute: '#minute', second: '#second' }, isDoubleBit: true }); timer.on('end', function() {}) } render() { let daojishiDom; if (this.state.isDisplayDaojishi) { //倒计时阈值开启时显示倒计时 daojishiDom = ( <div className="daojishi"> <p className="daojishi-title">{lang.template("今日榜单活动结束还有")}</p> <div className="daojishi-time"><em id="day">10</em>{lang.template("天")}<em id="hour"></em>{lang.template("小时")}<em id="minute"></em>{lang.template("分")}<em id="second"></em>{lang.template("秒")}</div> </div> ); } return ( <div className="carwar"> {daojishiDom} <div className="daily-billboard"> <DailyBillboard ref="dailyBillboard" tabs={_global_periodList} getServerTime={this.getServerTime.bind(this)} currentDay={this.state.currentDay} getActivityInfo={this.state.getActivityInfo}/> </div> </div> ); }
请求回来的数据只显示前10名,前三名和4-10的UI不用。若所有数据多于10只显示9条,最后显示点击查看更多
dataArr.slice(0, 3)
获取数组中的1-3项、dataArr.slice(3)
获取数组中第四项及之后loadData() { //请求数据 let pageSize = this.props.maxSize + 1;//请求数据长度为须要的数据数 let nextPage; let promise = http.fetch({ url: api.getBillBoard(), data: { page: 1, pageCount: pageSize } }); promise.done((res) => { if (res.code == 'SC_SUCCESS') { this.setState({ dataReady: true, dataArr: res.data.hostList }); } }); } render() { //渲染 let $list; let dataArr = this.state.dataArr; let top3Arr = dataArr.slice(0, 3);//截取1-3的数据 let top4to10Arr = dataArr.slice(3);//截取3之后的数据 $list = ( <div> <div className="top3">//1-3的渲染 <Top3 list={top3Arr}/> </div> <div className="top4to10">//3之后的渲染 { top4to10Arr.length > 0 ? <Top4to10 list={top4to10Arr} maxSize={this.props.maxSize - 3}/>: null} </div> </div> ); return ( <div className="board-list-wrapper"> <div className="board-list"> {$list} </div> </div> ); }
从父组件传入数据,且传入的数据不是一次性,会改变是须要调用 react 的
componentWillReceiveProps
方法
constructor(props){ super(props); this.state = { list: props.list//将从外面传进来的数据设置为list } } componentWillReceiveProps(props) {//从父组件传入数据,且传入的数据不是一次性,会改变是须要调用componentWillReceiveProps方法 this.setState({ list: props.list }); } render(){ let dataArr = this.state.list; let maxSize = this.props.maxSize; let $list = []; if (dataArr.length > maxSize) {//得到到的数据大于需求数据长度 let arr = dataArr.slice(0, maxSize);//截取对应数据 $list = arr.map(function(item, index){ return <UserItemOther key={item.uid} rank={index + 4} data={item}/> }); $list.push (<MoreUserItem key="more"/>);//添加查看更多模块 } else {//得到到的数据不大于需求数据长度 $list = dataArr.map(function(item, index){ return <UserItemOther key={item.uid} rank={index + 4} data={item}/>; }); } return ( <div className="top4to10-list"> {$list} </div> ); }
onSelect = (index, e)=>{ //将点击时数组的index设置成selectedIndex this.setState({ selectedIndex: index }); } render(){ let self = this; let hostTabs = this.props.tabs; return ( <div className="daily-list-wrapper"> <Tabs onSelect={this.onSelect} > { hostTabs.map(function(tab, i){ let enable; /* *若是数组的索引值与selectedIndex的值相等说明如今展现的为当前选中的tab * 设置当前选中的tab中的enable为true,其余的为false */ enable = (i == self.state.selectedIndex) ? true: false; return ( <TabPanel key={i}> <DailyList scrollEnable={enable}/> </TabPanel> ) }) } </Tabs> </div> ); }
scrollList(e){ let self = this; let timer; $(window).on('scroll',function(){ if (self.props.scrollEnable) {//当scrollEnable阈值为true时才执行下拉滑动事件 timer && clearTimeout(timer); timer = setTimeout(function () { if (self.dataOver || self.isLoad) { return; } else { let body = document.body, docElement = document.documentElement; //700 在页面滑到底部前700px就能够加载,增长交互流畅性 if(body.scrollTop > docElement.offsetHeight - docElement.clientHeight - 700){ self.getData(); } } } , 500); } }) }
切换选择菜单的同时切换接口的请求地址
- 设置一个阈值,当点击时setState将阈值关闭,在setState的回调中将阈值开启,并set相应的属性值
- 在render时经过阈值和空div,清楚过去的dom,在setState时渲染新的dom
constructor(props) { super(props); this.state = {//设置tab须要的配置 tab:[ { name:lang.template('冲刺中'), url:api.getStriveInfo() }, { name:lang.template('已达成'), url:api.getFinishInfo() } ], selectId:0,//设置默认参数 url:api.getStriveInfo(), isMount:true//设置中间转态的阀门 }; } chooseTab =(index,url)=> {//点击tab是改变state对应的值 this.setState({ isMount: false//当setState时设置阀门为false }, function(){//setState后的回调 this.setState({//setState成功后再打开阀门,设置对对应的属性值 selectId:index, url:url, isMount: true }) }) } render() { let self = this; let $tab = []; this.state.tab.forEach(function(item,index){//渲染tab,并根据数组的index是否与selectId相等决定选中状态 $tab.push( <i className={self.state.selectId == index ?item.style+ " checked":item.style} key = {index} onClick={self.chooseTab.bind(this,index,item.url)} >{item.name}</i> ) }) if(this.state.isMount) {//选中状态的阈值(react对比dom树) return ( <div className="r-wrap"> <div className="banner"> <div className="bannerImg"> <p>{lang.template('活动时间')}</p> </div> <ul className="rule" dangerouslySetInnerHTML={{__html: lang.template('活动规则')}}></ul> <ol className="award" dangerouslySetInnerHTML={{__html: lang.template('活动奖励')}}></ol> </div> <div className="rank"> <h1 className="tab"> {$tab} </h1> <List goProfile={self.goProfile} url={this.state.url}/> </div> <Refresh /> </div> ); } else { //切换tab时清楚上一个dom return <div></div>; } }
constructor(props) { super(props); this.state = {//加载、没有数据等状况须要显示在页面中的阈值 list:[], page:1, url:this.props.url, dataLoading:true, empty:false, dataEnd:false }; //防止异步请求时的阈值,只在逻辑代码中使用 this.isLoading = true; this.dataOver = false; } componentDidMount(){//数据变动时调用下滑滚动事件 $(window).off('scroll'); } componentWillUnmount() {//清楚滑滚动事件 $(window).off('scroll'); } getListInfo() { let self = this; let url = this.state.url; let page = this.state.page;//page每次都会有更改 let pageSize = 25; let promise = http.fetch({ url: url, data:{ index:page, pageCount:pageSize } }); promise.done(function(res){ if (res.code == 'SC_SUCCESS') { if (res.data.length < pageSize) {//当前页数组长度小于pageSize,说明是最后一页 self.dataOver = true; self.setState({ dataLoading:false, dataEnd:true }) } if (page == 1) { if (res.data.length == 0) {//当page==1时,数组长度为0,说明没有数据 self.setState({ empty:true, dataLoading:false, dataEnd:false }) }; }; nextPage = page + 1;//请求一次事后将page增长1 self.setState({ list:self.state.list.concat(res.data),//将每次请求过来的数据追加到数组中 page:nextPage//将新的page state到page }); self.isLoading = false; } }); } scrollList(){ let self = this; let page = this.state.page; let timer; $(window).off('scroll'); $(window).on('scroll' , function () { timer && clearTimeout(timer); timer = setTimeout(function () { if (self.dataOver || self.isLoading) {//当没有数据或者正在 请求数据时,不执行下拉加载 return } else { var body = document.body, docElement = document.documentElement; // 700 在页面滑到底部前700px就能够加载,增长交互流畅性 if(body.scrollTop > docElement.offsetHeight - docElement.clientHeight - 700){ self.isLoading = true; self.getListInfo(); } }; } , 300); }); }
页码数设置为1,每页请求的长度为当前数据的长度
componentDidMount() {//数据变动时设置全局事件 let self = this; $.channel.on('refreshAll', function(){ self.refetch(); }); } componentWillUnmount() { //解绑全局事件 $.channel.off('refreshAll'); } refetch() { let self = this; let pageSize = this.state.list.length;//请求数据长度为当前数据长度 let promise = http.fetch({ url: urls, data:{ index:1,//请求第一页 pageCount:pageSize } }); promise.done(function(res){ if (res.code == 'SC_SUCCESS') { self.setState({ list:self.state.list.concat(res.data)//将刷新请求过来的数据设置为渲染数据 }); self.isLoading = false; } }); }
@(2016.12.15)
query(name, scope) { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"), scope = scope || 'search', r = location[scope].substr(1).match(reg); if (r != null) { return unescape(r[2]); } return null; }
//url:www.baidu.com?sid=1 var sid = query("sid"); console.log(sid);// 1
返回的平台类型: weixin, weibo, qq
getPlatform() { let [ua, pl] = [navigator.userAgent, 'other']; if (ua.match(/micromessenger/i)) { pl = 'wechat'; } else if (ua.match(/qq/i)) { pl = 'qq'; } else if (ua.match(/weibo/i)) { pl = 'weibo'; } return pl; }
//在微信中 platform = getPlatform(); console.log(platform) //weixi
让客户端在ua中添加对应字段和版本号(sfans)
getVersion() { var ua = navigator.userAgent; var reg = /sfans\/(.+?) /i; return reg.exec(ua)[1]; }
consoel.log(getVersion()) //sfans
getSystem() { let ua = navigator.userAgent; return /(iPhone|iPad|iPod|iOS)/i.test(ua) ? 'ios' : /[aA]ndroid/i.test(ua) ? 'android' : 'pc'; }
调取方式
//在iOS中 console.log(getSystem()) //ios
支持上下午
- time:时间戳
- pattern:时间格式
- hourType:时间类型(12,24)
formatData =(time, pattern, hourType)=> { var dateObj = new Date(time); hourType = hourType || 24; if (!pattern) { pattern = "yyyy-MM-dd HH:mm:ss"; } var val, result; var func = function(matched) { return matched.length == 2 && val < 10 ? ("0" + val) : val; }; result = pattern.replace(/y{2,4}/, dateObj.getFullYear()); // the year val = dateObj.getMonth() + 1; // the month result = result.replace(/M{1,2}/, func); val = dateObj.getDate(); // the date result = result.replace(/d{1,2}/, func); if (hourType == 12) { val = dateObj.getHours(); // the hour let isAm = 1; if (val > 12) { result = result.replace(/H{1,2}/, function(matched){ if (matched.length == 2) { if (val > 12) { val = val - 12; if (val < 10) { val = '0' + val; } isAm = 0 } else if(val < 10){ val = '0' + val; isAm = 1; } } return val; }); val = dateObj.getMinutes(); // the minute result = result.replace(/m{1,2}/, func); val = dateObj.getSeconds(); // the second result.replace(/s{1,2}/, func); } else { result = result.replace(/H{1,2}/, func); val = dateObj.getMinutes(); // the minute result = result.replace(/m{1,2}/, func); val = dateObj.getSeconds(); // the second result.replace(/s{1,2}/, func); } if( isAm ) { result +='am' } else { result +='pm'; } } else { val = dateObj.getHours(); // the hour result = result.replace(/H{1,2}/, func); val = dateObj.getMinutes(); // the minute result = result.replace(/m{1,2}/, func); val = dateObj.getSeconds(); // the second result.replace(/s{1,2}/, func); } return result; }
调取方式
console.log(formatData(时间戳,'yyyy年MM月dd日 HH:mm',12))//2016年12月12日 12:12pm
引用SwipeSlide
class Banner extends Component { static defaultProps = { selectIndex: 0 // 默认第一项,索引值0 } constructor(props) { super(props); let selectIndex = 0; if ('selectIndex' in props) { //插件中的当前图片索引 selectIndex = props.selectIndex; } else { selectIndex = props.selectIndex; } this.state = { selectIndex: selectIndex //state当前图片索引 }; } componentDidMount(){ let self = this; if (this.props.banner.length == 1) { $(".dot").hide();//一张图片是不显示圆点 } else { this.timer = setTimeout(()=>{ // 图片轮播插件调用 // $(this.refs.content)包含图片的大容器 this.swipeSlide = new SwipeSlide($(this.refs.content), { continuousScroll: true, autoSwipe : true, speed : 5000, transitionType: 'ease-in', callback: function(index){ self.setState({ selectIndex: index //回调后state索引 }); } }); }, 500); }; } render() { let self = this; let imgArr = this.props.banner; let $li = [],cls; return ( <div className="banner-slide-wrap" ref="content" style={{opacity: this.state.opacity}}> <ul className="slide-list"> { imgArr.map((item, i) => { //循环渲染单张图片 容器 let cls = 'slide-item'; cls += this.state.selectIndex == i ? ' selected' : ''; return <li className='slide-item' key = {i} onClick={self.goBanner.bind(this,item.redirect)}><img src={item.images + '?imageView2/1/w/320/h/154'} /></li> }) } </ul> <div className="slide-dots"> //圆点容器 { imgArr.map((item, i) =>{ var cls = 'dot'; cls += this.state.selectIndex == i ? ' selected' : ''; return ( <span key={i} className={cls}></span> ); }) } </div> </div> ) } }
列表中点击一个后其余全部按钮均不可重复点击
class List extends Component { constructor(props) { super(props); self.clicked = false ;//设置默承认点击状态(已点为false) } postTask = (item) => {//请求服务器接口 let promise = $.ajax({ url: urls, type: 'GET', data:{ index:page, pageSize:25 }, dataType: 'json' }); promise.done(function(res){ if (res.code == 'SC_SUCCESS') { alert("成功") } else { alert("系统错误") } self.clicked = false;//获取数据成功设置回可点状态 }); promise.fail(function(res, type){ self.clicked = false;//数据获取失败设置回可点状态 alert("获取数据失败") }); } goTask =(data)=> { let self = this; let platform = client.getPlatform(); if (self.clicked || data.status == 1) {//当已点过状态或者已作任务状态不可点击 return ; } else { self.clicked = true ;//点击进去之后设置为已点击过状态 }; } render() { let self = this; return ( <ul className="list" > <li onClick={self.goTask.bind(this,item)}></li> </ul> ) } }
list:[ { name:"aaa", status:0 }, { name:"bbb", status:0 } ]
list.forEach(function(data,i){ if ( data.name == "aaa" ) { data.status = 1; }; }); self.setState({ listInfo:list }) ``` * 修改后的数据变为
list:[ { name:"aaa", status:1 }, { name:"bbb", status:0 } ]
*** ###PB文件的使用 >- 引入PB转化插件c-pbajax >- 下载相应的PBJs编辑后的文件 >- 在HTML中引入种子文件 ` `
<script src="http://h.cdn.pengpengla.com/h5lib/3.0.0/js/protobuf.js></script>
/以上引入到HTML种子文件中/ import pbajax from 'c-pbajax';//引入统一的编译插件 import infoListpb from './pb/taskslistpb';//引入业务文件 import publicpb from './pb/publicpb';//引入说明文件 //https://star-service.pengpengla.com/t/tasks/act /*"package": "Banner.List",
"objc_class_prefix": "BannerList",
"java_package": "com.asiainno.starfan.proto"
*/ }, pbajax.do( { service: 'sfanservice', //项目PB目录 api: '/t/tasks/act',//项目文件地址 header: { usertoken:_global_token//header中传入的参数 }, inchina: 1 }, new tasksactpb.Tasks.Act.Request({ //参数 version: vc, sid:sid, taskId:item.id }),
function(res){// success var dataWrap = publicpb.Result.decode(res), data; if (dataWrap.code == 1) { data = tasksactpb.Tasks.Act.Response.decode(dataWrap.data.value);//对应文件中的package console.log(item); } }, function(){}, function(){//服务器挂了 console.log("服务器挂了"); } );
----------