listview.html 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. <!doctype html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>列表到详情最佳实践</title>
  6. <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
  7. <link href="../../../css/mui.min.css" rel="stylesheet" />
  8. <style type="text/css">
  9. .mui-media {
  10. font-size: 14px;
  11. }
  12. .mui-table-view .mui-media-object {
  13. max-width: initial;
  14. width: 90px;
  15. height: 70px;
  16. }
  17. .meta-info {
  18. position: absolute;
  19. left: 115px;
  20. right: 15px;
  21. bottom: 8px;
  22. color: #8f8f94;
  23. }
  24. .meta-info .author,
  25. .meta-info .time {
  26. display: inline-block;
  27. }
  28. .meta-info .time {
  29. float: right;
  30. }
  31. .mui-table-view:before,
  32. .mui-table-view:after {
  33. height: 0;
  34. }
  35. .mui-content>.mui-table-view:first-child {
  36. margin-top: 1px;
  37. }
  38. .banner {
  39. height: 180px;
  40. overflow: hidden;
  41. position: relative;
  42. background-position: center;
  43. background-color: #ccc;
  44. }
  45. .banner img {
  46. width: 100%;
  47. height: auto;
  48. }
  49. .banner .title {
  50. position: absolute;
  51. left: 15px;
  52. bottom: 15px;
  53. width: 90%;
  54. font-size: 16px;
  55. font-weight: 400;
  56. line-height: 21px;
  57. color: white;
  58. z-index: 11;
  59. }
  60. </style>
  61. </head>
  62. <body>
  63. <div class="mui-content" id="news">
  64. <!--顶部banner图 开始-->
  65. <div class="banner">
  66. <a href="javascript:;" :data-guid="banner.guid" @tap="open_detail(banner)">
  67. <img :src="banner.cover" />
  68. <h2 class="title mui-ellipsis-2">{{banner.title}}</h2>
  69. <div style="display: none;">
  70. <div class="author">{{banner.author}}</div>
  71. <div class="time">{{banner.time}}</div>
  72. </div>
  73. </a>
  74. </div>
  75. <!--顶部banner图 结束-->
  76. <!--列表信息流 开始-->
  77. <div id="list" class="mui-scroll-wrapper">
  78. <div class="mui-scroll">
  79. <ul class="mui-table-view">
  80. <li class="mui-table-view-cell mui-media" v-for="item in items">
  81. <a href="javascript:;" :data-guid="item.guid" @tap="open_detail(item)">
  82. <img class="mui-media-object mui-pull-left" :src="item.cover">
  83. <div class="mui-media-body">
  84. <div class="mui-ellipsis-2">{{item.title}}</div>
  85. </div>
  86. <div class="meta-info">
  87. <div class="author">{{item.author}}</div>
  88. <div class="time">{{item.time}}</div>
  89. </div>
  90. </a>
  91. </li>
  92. </ul>
  93. </div>
  94. </div>
  95. <!--列表信息流 结束-->
  96. </div>
  97. <script src="../../../js/mui.min.js"></script>
  98. <script src="vue.min.js" type="text/javascript" charset="utf-8"></script>
  99. <script type="text/javascript">
  100. var lastId = '',
  101. minId = ''; //最新新闻的id
  102. var webview_detail = null; //详情页webview
  103. var titleNView = { //详情页原生导航配置
  104. backgroundColor: '#f7f7f7', //导航栏背景色
  105. titleText: '', //导航栏标题
  106. titleColor: '#000000', //文字颜色
  107. type: 'transparent', //透明渐变样式
  108. autoBackButton: true, //自动绘制返回箭头
  109. splitLine: { //底部分割线
  110. color: '#cccccc'
  111. }
  112. }
  113. //mui初始化,配置下拉刷新
  114. mui.init({
  115. pullRefresh: {
  116. container: '#list',
  117. down: {
  118. style: 'circle',
  119. offset: '0px',
  120. auto: true,
  121. callback: pulldownRefresh
  122. },
  123. up: {
  124. contentrefresh: '正在加载...',
  125. callback: pullupRefresh
  126. }
  127. }
  128. });
  129. /**
  130. * 下拉刷新获取最新列表
  131. */
  132. function pulldownRefresh() {
  133. if(window.plus && plus.networkinfo.getCurrentType() === plus.networkinfo.CONNECTION_NONE) {
  134. plus.nativeUI.toast('似乎已断开与互联网的连接', {
  135. verticalAlign: 'top'
  136. });
  137. return;
  138. }
  139. var data = {
  140. column: "id,post_id,title,author_name,cover,published_at" //需要的字段名
  141. }
  142. if(lastId) { //说明已有数据,目前处于下拉刷新,增加时间戳,触发服务端立即刷新,返回最新数据
  143. data.lastId = lastId;
  144. data.time = new Date().getTime() + "";
  145. }
  146. //请求顶部banner信息
  147. mui.getJSON("http://spider.dcloud.net.cn/api/banner/36kr", data, function(rsp) {
  148. news.banner = {
  149. guid: rsp.post_id,
  150. title: rsp.title,
  151. cover: rsp.cover,
  152. author: rsp.author_name,
  153. time: dateUtils.format(rsp.published_at)
  154. };
  155. });
  156. //请求最新列表信息流
  157. mui.getJSON("http://spider.dcloud.net.cn/api/news", data, function(rsp) {
  158. mui('#list').pullRefresh().endPulldownToRefresh();
  159. if(rsp && rsp.length > 0) {
  160. lastId = rsp[0].id; //保存最新消息的id,方便下拉刷新时使用
  161. if(!minId) {//首次拉取列表时保存最后一条消息的id,方便上拉加载时使用
  162. minId = rsp[rsp.length - 1].id;
  163. }
  164. news.items = convert(rsp).concat(news.items);
  165. }
  166. });
  167. }
  168. /**
  169. * 上拉加载拉取历史列表
  170. */
  171. function pullupRefresh() {
  172. var data = {
  173. column: "id,post_id,title,author_name,cover,published_at" //需要的字段名
  174. };
  175. if(minId) { //说明已有数据,目前处于上拉加载,传递当前minId 返回历史数据
  176. data.minId = minId;
  177. data.time = new Date().getTime() + "";
  178. data.pageSize = 10;
  179. }
  180. //请求历史列表信息流
  181. mui.getJSON("http://spider.dcloud.net.cn/api/news", data, function(rsp) {
  182. mui('#list').pullRefresh().endPullupToRefresh();
  183. if(rsp && rsp.length > 0) {
  184. minId = rsp[rsp.length - 1].id; //保存最后一条消息的id,上拉加载时使用
  185. news.items = news.items.concat(convert(rsp));
  186. }
  187. });
  188. }
  189. mui.plusReady(function() {
  190. //预加载详情页
  191. webview_detail = mui.preload({
  192. url: 'detail.html',
  193. id: 'news_detail',
  194. styles: {
  195. "render": "always",
  196. "popGesture": "hide",
  197. "bounce": "vertical",
  198. "bounceBackground": "#efeff4",
  199. "titleNView": titleNView
  200. }
  201. });
  202. });
  203. var news = new Vue({
  204. el: '#news',
  205. data: {
  206. banner: {}, //顶部banner数据
  207. items: [] //列表信息流数据
  208. }
  209. });
  210. /**
  211. * 打开新闻详情
  212. *
  213. * @param {Object} item 当前点击的新闻对象
  214. */
  215. function open_detail(item) {
  216. //触发子窗口变更新闻详情
  217. mui.fire(webview_detail, 'get_detail', {
  218. guid: item.guid,
  219. title: item.title,
  220. author: item.author,
  221. time: item.time,
  222. cover: item.cover
  223. });
  224. //更改详情页原生导航条信息
  225. titleNView.titleText = item.title;
  226. webview_detail.setStyle({
  227. "titleNView": titleNView
  228. });
  229. setTimeout(function() {
  230. webview_detail.show("slide-in-right", 300);
  231. }, 150);
  232. }
  233. /**
  234. * 1、将服务端返回数据,转换成前端需要的格式
  235. * 2、若服务端返回格式和前端所需格式相同,则不需要改功能
  236. *
  237. * @param {Array} items
  238. */
  239. function convert(items) {
  240. var newItems = [];
  241. items.forEach(function(item) {
  242. newItems.push({
  243. guid: item.post_id,
  244. title: item.title,
  245. author: item.author_name,
  246. cover: item.cover,
  247. time: dateUtils.format(item.published_at)
  248. });
  249. });
  250. return newItems;
  251. }
  252. /**
  253. * 格式化时间的辅助类,将一个时间转换成x小时前、y天前等
  254. */
  255. var dateUtils = {
  256. UNITS: {
  257. '年': 31557600000,
  258. '月': 2629800000,
  259. '天': 86400000,
  260. '小时': 3600000,
  261. '分钟': 60000,
  262. '秒': 1000
  263. },
  264. humanize: function(milliseconds) {
  265. var humanize = '';
  266. mui.each(this.UNITS, function(unit, value) {
  267. if(milliseconds >= value) {
  268. humanize = Math.floor(milliseconds / value) + unit + '前';
  269. return false;
  270. }
  271. return true;
  272. });
  273. return humanize || '刚刚';
  274. },
  275. format: function(dateStr) {
  276. var date = this.parse(dateStr)
  277. var diff = Date.now() - date.getTime();
  278. if(diff < this.UNITS['天']) {
  279. return this.humanize(diff);
  280. }
  281. var _format = function(number) {
  282. return(number < 10 ? ('0' + number) : number);
  283. };
  284. return date.getFullYear() + '/' + _format(date.getMonth() + 1) + '/' + _format(date.getDay()) + '-' + _format(date.getHours()) + ':' + _format(date.getMinutes());
  285. },
  286. parse: function(str) { //将"yyyy-mm-dd HH:MM:ss"格式的字符串,转化为一个Date对象
  287. var a = str.split(/[^0-9]/);
  288. return new Date(a[0], a[1] - 1, a[2], a[3], a[4], a[5]);
  289. }
  290. };
  291. </script>
  292. </body>
  293. </html>