gui-page.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. <template>
  2. <view :class="[
  3. 'gui-flex', 'gui-columns', 'gui-sbody',
  4. fullPage ? 'gui-flex1':'' ,
  5. refresh || loadmore ? 'gui-flex1' : ''
  6. ]">
  7. <!-- 自定义头部 -->
  8. <view
  9. class="gui-header gui-transition-all"
  10. v-if="customHeader"
  11. id="guiPageHeader"
  12. ref="guiPageHeader"
  13. :style="'height:'
  14. +(headerSets.height+statusBarHeight)+'px; z-index:'
  15. +headerSets.zIndex+';'+headerStyle">
  16. <!-- 状态栏 -->
  17. <view
  18. class="gui-page-status-bar"
  19. :style="'height:'+statusBarHeight+'px;'+statusBarStyle"></view>
  20. <!-- 头部插槽 -->
  21. <view
  22. class="gui-flex gui-columns gui-justify-content-center"
  23. @tap.stop.prevnet="headerTap"
  24. :style="{height:headerSets.height+'px'}">
  25. <slot name="gHeader"></slot>
  26. </view>
  27. </view>
  28. <!-- 自定义头部占位 -->
  29. <view
  30. v-if="customHeader && isHeaderSized"
  31. :style="'height:'+(headerSets.height+statusBarHeight)+'px; '+ headerSizedStyle + ';'"></view>
  32. <!-- 页面主体 -->
  33. <view
  34. class="gui-flex gui-columns"
  35. v-if="!refresh && !loadmore"
  36. id="guiPageBody"
  37. ref="guiPageBody"
  38. :class="[fullPage?'gui-flex1':'']">
  39. <slot name="gBody"></slot>
  40. </view>
  41. <!-- 刷新加载主体 -->
  42. <view class="gui-flex gui-columns gui-flex1"
  43. v-if="refresh || loadmore"
  44. id="guiPageBody"
  45. ref="guiPageBody"
  46. :style="{
  47. marginTop:fixedTopMargin+'px',
  48. height:refreshBodyHeight+'px'
  49. }">
  50. <scroll-view
  51. class="gui-relative"
  52. :scroll-y="true"
  53. :show-scrollbar="false"
  54. :style="{
  55. height:refreshBodyHeight+'px',
  56. opacity:refreshBodyHeight < 1 ? 0 : 1
  57. }"
  58. @touchstart="touchstart"
  59. @touchmove="touchmove"
  60. @touchend="touchend"
  61. @scroll="scroll"
  62. :scroll-top="scrollTop"
  63. @scrolltolower="loadmorefun">
  64. <view>
  65. <gui-refresh
  66. ref="guiPageRefresh"
  67. @reload="reload"
  68. :refreshText="refreshText"
  69. :refreshBgColor="refreshBgColor"
  70. :refreshColor="refreshColor"
  71. :refreshFontSize="refreshFontSize"></gui-refresh>
  72. </view>
  73. <slot name="gBody"></slot>
  74. <view
  75. v-if="loadmore"
  76. class="gui-page-loadmore">
  77. <gui-loadmore
  78. ref="guipageloadmore"
  79. :loadMoreText="loadMoreText"
  80. :loadMoreColor="loadMoreColor"
  81. :loadMoreFontSize="loadMoreFontSize"></gui-loadmore>
  82. </view>
  83. </scroll-view>
  84. </view>
  85. <!-- 页面底部 -->
  86. <!-- 底部占位 -->
  87. <view v-if="customFooter"
  88. :style="{height:footerHeight}"></view>
  89. <view class="gui-page-footer gui-border-box"
  90. :class="[isSwitchPage?'gui-switch-page-footer':'']"
  91. v-if="customFooter"
  92. id="guiPageFooter"
  93. ref="guiPageFooter"
  94. :style="{
  95. height:footerHeight,
  96. 'background-image':footerSets.bg,
  97. 'z-index':footerSets.zIndex
  98. }">
  99. <view>
  100. <slot name="gFooter"></slot>
  101. </view>
  102. <view
  103. :style="'height:'+iphoneXButtomHeight+'; '+ iphoneXButtomStyle"></view>
  104. </view>
  105. <!-- 右下角悬浮挂件 -->
  106. <view
  107. class="gui-page-pendant"
  108. :style="{
  109. right:pendantSets.right, bottom:pendantSets.bottom,
  110. width:pendantSets.width, zIndex:pendantSets.zIndex}">
  111. <slot name="gPendant"></slot>
  112. </view>
  113. <!-- 吸顶元素 -->
  114. <view
  115. class="gui-page-fixed-top"
  116. ref="guiPageFixedTop"
  117. id="guiPageFixedTop"
  118. :style="{
  119. top:fixedTop+'px',
  120. zIndex:fixedTopZIndex
  121. }">
  122. <slot name="gFixedTop"></slot>
  123. </view>
  124. <!-- 全屏 loading -->
  125. <gui-page-loading ref="guipageloading"></gui-page-loading>
  126. </view>
  127. </template>
  128. <script>
  129. // #ifdef APP-NVUE
  130. const dom = weex.requireModule('dom');
  131. // #endif
  132. export default{
  133. name : 'gui-page',
  134. props : {
  135. fullPage : {type:Boolean, default:false},
  136. customHeader : {type:Boolean, default:false},
  137. headerSets : {type:Object , default:function(){return {height:44, zIndex:100}}},
  138. headerStyle : {type:String , default:'background-color:#FFFFFF;'},
  139. isHeaderSized : {type:Boolean, default:true},
  140. statusBarStyle : {type:String , default:'background-color:#FFFFFF;'},
  141. customFooter : {type:Boolean, default:false},
  142. footerSets : {type:Object , default:function(){return {height:100, zIndex:100, bg:'linear-gradient(to bottom, #FFFFFF,#FFFFFF)'}}},
  143. pendantSets : {type:Object , default:function(){return {width:'100rpx', right:'25rpx', bottom:'100rpx', zIndex:100};}},
  144. isLoading : {type:Boolean, default:false},
  145. isSwitchPage : {type:Boolean, default:false},
  146. iphoneXButtomStyle : {type:String, default:''},
  147. headerSizedStyle : {type:String, default:''},
  148. fixedTopZIndex : {type:Number, default:2},
  149. /* 刷新 */
  150. refresh : {type:Boolean, default:false},
  151. refreshText : {type:Array, default:function () {
  152. return ['继续下拉刷新','松开手指开始刷新','数据刷新中','数据已刷新'];
  153. }},
  154. refreshBgColor : {type:Array, default:function () {
  155. return ['#FFFFFF','#FFFFFF','#FFFFFF','#63D2BC'];
  156. }},
  157. refreshColor : {type:Array, default:function () {
  158. return ['rgba(69, 90, 100, 0.6)','rgba(69, 90, 100, 0.6)','#63D2BC','#FFFFFF'];
  159. }},
  160. refreshFontSize : {type:String, default:'26rpx'},
  161. /* 加载更多 */
  162. loadmore : {type:Boolean, default:false},
  163. loadMoreText : {type:Array, default:function () {
  164. return ['','更多数据加载中', '已加载全部数据'];
  165. }},
  166. loadMoreColor : {type:Array, default:function () {
  167. return ['rgba(69, 90, 100, 0.6)', 'rgba(69, 90, 100, 0.6)', 'rgba(69, 90, 100, 0.8)'];
  168. }},
  169. loadMoreFontSize : {type:String, default:'26rpx'},
  170. apiLoadingStatus : {type:Boolean, default:false}
  171. },
  172. data() {
  173. return {
  174. footerHeight : '100rpx',
  175. iphoneXButtomHeight : '0rpx',
  176. statusBarHeight : 0,
  177. // #ifdef APP-NVUE
  178. animateCount : 0,
  179. // #endif
  180. headerTapNumber : 0,
  181. fixedTop : 0,
  182. refreshBodyHeight : 0,
  183. loadMoreTimer : null,
  184. fixedTopMargin : 0,
  185. scrollTop : 0,
  186. srcollTimer : null
  187. }
  188. },
  189. mounted:function(){
  190. if(this.isLoading){
  191. this.pageLoadingOpen();
  192. }
  193. // 刷新相关
  194. setTimeout(()=>{
  195. if(this.refresh || this.loadmore){
  196. this.getDomSize('guiPageBody', (res)=>{
  197. this.refreshBodyHeight = res.height;
  198. this.getDomSize('guiPageFixedTop', (res)=>{
  199. if(res.height){
  200. this.refreshBodyHeight -= res.height;
  201. this.fixedTopMargin = res.height;
  202. }
  203. })
  204. });
  205. }
  206. },200);
  207. },
  208. watch:{
  209. isLoading : function (val) {
  210. if(val){
  211. this.pageLoadingOpen();
  212. }else{
  213. this.pageLoadingClose();
  214. }
  215. }
  216. },
  217. created:function(){
  218. this.footerHeight = this.footerSets.height + 'rpx';
  219. // #ifdef H5
  220. if(this.customHeader){
  221. this.fixedTop = this.headerSets.height;
  222. }else{
  223. this.fixedTop = 44;
  224. }
  225. // #endif
  226. try {
  227. var system = uni.getSystemInfoSync();
  228. if(system.model){
  229. system.model = system.model.replace(' ', '');
  230. system.model = system.model.toLowerCase();
  231. this.statusBarHeight = system.statusBarHeight;
  232. var res1 = system.model.indexOf('iphonex');
  233. if(res1 > 5){res1 = -1;}
  234. var res2 = system.model.indexOf('iphone1');
  235. if(res2 > 5){res2 = -1;}
  236. if(res1 != -1 || res2 != -1){
  237. this.iphoneXButtomHeight = '50rpx';
  238. this.footerHeight = (this.footerSets.height + 50 ) + 'rpx';
  239. }
  240. }
  241. // #ifdef MP-ALIPAY
  242. this.statusBarHeight = 0;
  243. // #endif
  244. // #ifdef APP-PLUS
  245. this.iphoneXButtomHeight = '0rpx';
  246. this.footerHeight = this.footerSets.height + 'rpx';
  247. if(plus.navigator.isFullscreen()){
  248. this.statusBarHeight = 0;
  249. }
  250. // #endif
  251. if(this.isSwitchPage){
  252. this.iphoneXButtomHeight = '0rpx';
  253. this.footerHeight = this.footerSets.height + 'rpx';
  254. }
  255. // #ifndef H5
  256. if(this.customHeader){
  257. this.fixedTop = this.headerSets.height + this.statusBarHeight;
  258. }else{
  259. this.fixedTop = 0;
  260. }
  261. // #endif
  262. } catch (e){return null;}
  263. },
  264. methods:{
  265. pageLoadingOpen : function(){
  266. this.getRefs('guipageloading',0,(ref)=>{
  267. this.$refs.guipageloading.open();
  268. });
  269. },
  270. pageLoadingClose : function(){
  271. this.getRefs('guipageloading',0,(ref)=>{
  272. ref.close();
  273. });
  274. },
  275. // 下拉刷新相关
  276. touchstart : function (e){
  277. if(!this.refresh){return false;}
  278. if(this.apiLoadingStatus){return false;}
  279. this.$refs.guiPageRefresh.touchstart(e);
  280. },
  281. touchmove : function(e){
  282. if(!this.refresh){return false;}
  283. if(this.apiLoadingStatus){return false;}
  284. this.$refs.guiPageRefresh.touchmove(e);
  285. },
  286. touchend : function (e) {
  287. if(!this.refresh){return false;}
  288. if(this.apiLoadingStatus){return false;}
  289. this.$refs.guiPageRefresh.touchend(e);
  290. },
  291. scroll:function(e){
  292. if(this.srcollTimer != null){
  293. clearTimeout(this.srcollTimer);
  294. }
  295. this.srcollTimer = setTimeout(()=>{
  296. this.$refs.guiPageRefresh.scroll(e);
  297. this.$emit('scroll', e);
  298. this.scrollTop = e.detail.scrollTop;
  299. }, 100);
  300. },
  301. setScrollTop : function (scrollTop){
  302. this.scrollTop = scrollTop;
  303. },
  304. endReload : function(){
  305. this.$refs.guiPageRefresh.endReload();
  306. },
  307. reload : function(){
  308. if(this.apiLoadingStatus){return false;}
  309. this.$emit('reload');
  310. if(this.loadmore){this.$refs.guipageloadmore.stoploadmore();}
  311. },
  312. // 获取元素尺寸
  313. getDomSize : function(domIDOrRef, fun, count){
  314. if(!count){count = 1;}
  315. if(count >= 50){
  316. fun({width:0, height:0});
  317. return false;
  318. }
  319. // #ifndef APP-NVUE
  320. uni.createSelectorQuery().in(this).select('#'+domIDOrRef).boundingClientRect().exec((res)=>{
  321. if(res[0] == null){
  322. count += 1;
  323. setTimeout(()=>{this.getDomSize(domIDOrRef, fun, count);}, 50);
  324. }else{
  325. fun(res[0]);
  326. return ;
  327. }
  328. });
  329. // #endif
  330. // #ifdef APP-NVUE
  331. var el = this.$refs[domIDOrRef];
  332. dom.getComponentRect(el, (res) => {
  333. if(res.result == false){
  334. count += 1;
  335. setTimeout(()=>{this.getDomSize(domIDOrRef, fun, count);}, 50);
  336. }else{
  337. fun(res.size);
  338. return ;
  339. }
  340. });
  341. // #endif
  342. },
  343. stopfun : function(e){e.stopPropagation(); return null;},
  344. headerTap : function(){
  345. this.headerTapNumber ++;
  346. if(this.headerTapNumber >= 2){
  347. this.$emit('gotoTop');
  348. this.headerTapNumber = 0;
  349. }else{
  350. setTimeout(()=>{this.headerTapNumber = 0;}, 1000);
  351. }
  352. },
  353. getRefs : function(ref, count, fun){
  354. if(count >= 50){
  355. fun(this.$refs[ref]);
  356. return false;
  357. }
  358. var refReturn = this.$refs[ref];
  359. if(refReturn){
  360. // #ifdef APP-NVUE
  361. fun(refReturn);
  362. return;
  363. // #endif
  364. // #ifndef APP-NVUE
  365. if(refReturn._data){
  366. fun(refReturn);
  367. return;
  368. }
  369. // #endif
  370. }else{
  371. count++;
  372. setTimeout(()=>{
  373. this.getRefs(ref, count, fun);
  374. }, 100);
  375. }
  376. },
  377. loadmorefun : function () {
  378. if(!this.loadmore){return false;}
  379. if(this.apiLoadingStatus){return false;}
  380. // 获取加载组件状态看一下是否还能继续加载
  381. // 保证触底只执行一次加载
  382. if(this.loadMoreTimer != null){clearTimeout(this.loadMoreTimer);}
  383. this.loadMoreTimer = setTimeout(() => {
  384. var status = this.$refs.guipageloadmore.loadMoreStatus;
  385. if(status != 0){return null;}
  386. this.$refs.guipageloadmore.loading();
  387. this.$emit('loadmorefun');
  388. }, 80);
  389. },
  390. stoploadmore : function(){
  391. this.$refs.guipageloadmore.stoploadmore();
  392. },
  393. nomore : function () {
  394. this.$refs.guipageloadmore.nomore();
  395. }
  396. }
  397. }
  398. </script>
  399. <style scoped>
  400. .gui-sbody{width:750rpx;}
  401. .gui-page-loading{width:750rpx; position:fixed; left:0; top:0; bottom:0; flex:1; z-index:99999;}
  402. .gui-page-loading-points{width:20rpx; height:20rpx; border-radius:50rpx; margin:10rpx;}
  403. /* #ifndef APP-NVUE */
  404. .gui-sbody{min-height:calc(100vh - var(--window-top) - var(--window-bottom));}
  405. @keyframes pageLoading1{0% {opacity:0.5; transform:scale(1);} 40% {opacity:1; transform:scale(1.5);} 60%{opacity:0.5; transform:scale(1);}}
  406. @keyframes pageLoading2{20% {opacity:0.5; transform:scale(1);} 60% {opacity:1; transform:scale(1.5);} 80% {opacity:0.5; transform:scale(1);}}
  407. @keyframes pageLoading3{40% {opacity:0.5; transform:scale(1);} 80% {opacity:1; transform:scale(1.5);} 100% {opacity:0.5; transform:scale(1);}}
  408. .animate1{animation:pageLoading1 1.2s infinite linear;}
  409. .animate2{animation:pageLoading2 1.2s infinite linear;}
  410. .animate3{animation:pageLoading3 1.2s infinite linear;}
  411. /* #endif */
  412. .gui-header{width:750rpx; position:fixed; left:0; top:0;}
  413. .gui-page-footer{width:750rpx; position:fixed; left:0; bottom:0;}
  414. /* #ifdef H5 */
  415. .gui-switch-page-footer{bottom:50px;}
  416. /* #endif */
  417. .gui-page-status-bar{width:750rpx;}
  418. .gui-page-pendant{position:fixed;}
  419. .gui-page-fixed-top{position:fixed; top:44px; left:0px; width:750rpx; z-index:99998; overflow:hidden;}
  420. .gui-page-loadmore{padding-bottom:30rpx;}
  421. </style>