@(項目總結)[中東許願池, 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("服務器掛了"); } );
----------