gui-page.vue 13 KB

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