list-news.uvue 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. <template>
  2. <view :class="['uni-container', { 'flex-row': isWideScreen }, isDarkMode ? 'theme-dark' : 'theme-light']" style="flex: 1;">
  3. <!-- 列表区域 :宽屏时在左侧显示,窄屏时只显示列表 -->
  4. <list-view class="list-container" :class="{'list-narrow': isWideScreen}" refresher-enabled=true
  5. @refresherrefresh="onRefresherrefresh" :refresher-triggered="refresherTriggered" enable-back-to-top="true">
  6. <list-item class="banner" @click="bannerClick(banner)" type=1>
  7. <image class="banner-img" :src="banner.cover"></image>
  8. <text class="banner-title">{{ banner.title }}</text>
  9. </list-item>
  10. <sticky-section>
  11. <sticky-header>
  12. <view class="th-item">
  13. <text v-for="(name,index) in th_item" :key="index" @click="clickTH(index)" class="th-item-text">
  14. {{name}}
  15. </text>
  16. </view>
  17. </sticky-header>
  18. <list-item v-for="(value, index) in listData" :key="index" type=2>
  19. <view class="uni-list-cell" hover-class="uni-list-cell-hover" @click="goDetail(value, index)">
  20. <view class="uni-media-list">
  21. <image v-if="isWideScreen" class="uni-media-list-logo" :src="value.cover"></image>
  22. <share-element v-else :share-key="'image_'+index">
  23. <image class="uni-media-list-logo" :src="value.cover"></image>
  24. </share-element>
  25. <view class="uni-media-list-body">
  26. <text class="uni-media-list-text-top">{{ value.title }}</text>
  27. <view class="uni-media-list-text-bottom">
  28. <text class="uni-media-list-text">{{ value.author_name }}</text>
  29. <text class="uni-media-list-text">{{ value.published_at }}</text>
  30. </view>
  31. </view>
  32. </view>
  33. </view>
  34. </list-item>
  35. </sticky-section>
  36. </list-view>
  37. <!-- 详情区域:宽屏时,右侧显示-->
  38. <view v-if="isWideScreen" class="detail-container">
  39. <detail
  40. v-if="post_id != '' && title != '' && cover != ''"
  41. :key="post_id"
  42. :post_id="post_id"
  43. :cover="cover"
  44. :title="title"
  45. :isWideScreen="isWideScreen"
  46. :firstDetailContent="currentDetailContent"
  47. />
  48. </view>
  49. </view>
  50. </template>
  51. <script>
  52. import { state } from '@/store/index.uts'
  53. type Banner = {
  54. cover : string | null,
  55. title : string | null,
  56. post_id : string | null
  57. }
  58. type Item = {
  59. author_name : string,
  60. cover : string,
  61. id : number,
  62. post_id : string,
  63. published_at : string,
  64. title : string
  65. }
  66. // 导入详情组件,宽屏时,右侧直接展示详情内容
  67. import detail from './detail/detail.uvue'
  68. export default {
  69. components: {
  70. detail
  71. },
  72. data() {
  73. return {
  74. th_item: ["排序", "筛选"],
  75. refresherTriggered: false,
  76. banner: {} as Banner,
  77. listData: [] as Item[],
  78. last_id: '',
  79. isWideScreen: false,// 是否为宽屏模式
  80. currentIndex: 0, // 当前选中的列表项索引
  81. post_id: '',
  82. cover: '',
  83. title: '',
  84. firstDetailContent: '', // 并行预加载的第一个详情内容
  85. };
  86. },
  87. onLoad() {
  88. // 判断是否为宽屏
  89. this.checkScreenWidth()
  90. this.getBanner();
  91. this.getList();
  92. },
  93. onUnload() {
  94. },
  95. onResize() {
  96. // 监听屏幕尺寸变化,动态切换宽屏/窄屏
  97. this.checkScreenWidth()
  98. },
  99. methods: {
  100. checkScreenWidth() {
  101. const deviceType = uni.getDeviceInfo().deviceType
  102. const { windowWidth } = uni.getWindowInfo()
  103. // 只要是pad/pc,或者宽度大于700,都算宽屏
  104. this.isWideScreen = (
  105. deviceType === 'pad' ||
  106. deviceType === 'pc' ||
  107. windowWidth > 700
  108. )
  109. },
  110. getBanner() {
  111. let data = {
  112. column: 'id,post_id,title,author_name,cover,published_at' //需要的字段名
  113. };
  114. uni.request<Banner>({
  115. url: 'https://unidemo.dcloud.net.cn/api/banner/36kr',
  116. data: data,
  117. success: data => {
  118. this.refresherTriggered = false
  119. if (data.statusCode == 200) {
  120. const result = data.data
  121. if (result != null) {
  122. this.banner = result;
  123. }
  124. }
  125. },
  126. fail: (e) => {
  127. console.log('fail', e);
  128. }
  129. });
  130. },
  131. getList() {
  132. let url = "https://unidemo.dcloud.net.cn/api/news?column=id,post_id,title,author_name,cover,published_at";
  133. /* if (this.last_id != "") {
  134. const minId = parseInt((this.last_id))
  135. const time = new Date().getTime() + '';
  136. const pageSize = 10;
  137. url = url + "&minId=" + minId + "&time=" + time + "&pageSize=" + pageSize;
  138. } */
  139. uni.request<Item[]>({
  140. url: url,
  141. method: "GET",
  142. success: (res) => {
  143. if (res.statusCode == 200) {
  144. const result = res.data
  145. //因本接口没有更多分页数据,所以重新赋值。正常有分页的列表应该如下面push方式增加数组项
  146. // this.listData.push(...result)
  147. // this.last_id = this.listData[0].id + "";
  148. if (result != null) {
  149. this.listData = result
  150. // 宽屏模式下,自动选中并展示第一个新闻详情
  151. if (this.isWideScreen && this.listData.length > 0) {
  152. this.goDetail(this.listData[0], 0)
  153. // 并行预加载第一个详情内容,提升右侧展示速度
  154. const first = this.listData[0]
  155. uni.request({
  156. url: 'https://unidemo.dcloud.net.cn/api/news/36kr/' + first.post_id,
  157. success: (res) => {
  158. if (res.statusCode == 200) {
  159. const result = res.data as UTSJSONObject;
  160. this.firstDetailContent = result["content"] as string
  161. }
  162. }
  163. })
  164. }
  165. }
  166. this.refresherTriggered = false;
  167. }
  168. },
  169. fail: (res) => {
  170. console.log('fail', res);
  171. this.refresherTriggered = false
  172. }
  173. });
  174. },
  175. goDetail(e : Item, key : number) {
  176. this.currentIndex = key
  177. const detail = e;
  178. this.post_id = detail.post_id;
  179. this.cover = detail.cover;
  180. this.title = detail.title;
  181. // 窄屏时跳转到详情页,宽屏时右侧直接展示
  182. if (!this.isWideScreen) {
  183. // #ifdef APP-ANDROID
  184. this.cover = btoa(this.cover);
  185. // #endif
  186. uni.navigateTo({
  187. url: '/pages/template/list-news/detail/detail?post_id=' + this.post_id + "&cover=" + this.cover + "&title=" + this.title + "&shareKey=image_" + key
  188. });
  189. }
  190. },
  191. bannerClick(e : Banner) {
  192. const detail = e;
  193. const post_id = detail.post_id;
  194. let cover = detail.cover ?? "";
  195. // #ifdef APP-ANDROID
  196. cover = btoa(cover);
  197. // #endif
  198. const title = detail.title;
  199. uni.navigateTo({
  200. url: '/pages/template/list-news/detail/detail?post_id=' + post_id + "&cover=" + cover + "&title=" + title
  201. });
  202. },
  203. clickTH(index : number) {
  204. uni.showModal({
  205. content: "点击表头项:" + index,
  206. showCancel: false
  207. });
  208. },
  209. onRefresherrefresh() {
  210. this.refresherTriggered = true
  211. this.getBanner();
  212. this.getList();
  213. }
  214. },
  215. computed: {
  216. currentDetailContent() {
  217. // 只在宽屏且是第一个文章时用预加载内容
  218. if (
  219. this.isWideScreen &&
  220. this.listData.length > 0 &&
  221. this.post_id === this.listData[0].post_id &&
  222. this.firstDetailContent !== ''
  223. ) {
  224. return this.firstDetailContent
  225. }
  226. return ''
  227. },
  228. isDarkMode():boolean {
  229. return state.isDarkMode
  230. }
  231. }
  232. };
  233. </script>
  234. <style>
  235. .flex-row {
  236. flex-direction: row;
  237. }
  238. .list-container {
  239. width: 100%;
  240. background-color: var(--list-background-color,#ffffff);
  241. /* #ifdef APP */
  242. flex: 1;
  243. /* #endif */
  244. }
  245. .list-narrow {
  246. /* #ifdef WEB */
  247. width: 360px;
  248. /* #endif */
  249. /* #ifndef WEB */
  250. width: 100px;
  251. /* #endif */
  252. }
  253. .th-item{
  254. width: 100%;
  255. height:44px;
  256. background-color: var(--list-background-color,#ffffff);
  257. flex-direction: row;
  258. justify-content:center;
  259. align-items:center;
  260. }
  261. .th-item-text{
  262. margin-right: 20px;
  263. color: var(--text-color,#333333);
  264. }
  265. .detail-container {
  266. flex: 1;
  267. min-width: 0;
  268. /* 防止溢出 */
  269. padding: 20px;
  270. background-color: var(--background-color,#f8f8f8);
  271. /* #ifdef WEB */
  272. overflow: auto;
  273. /* #endif */
  274. }
  275. .banner {
  276. height: 180px;
  277. overflow: hidden;
  278. position: relative;
  279. background-color: var(--background-color,#f8f8f8);
  280. }
  281. .banner-img {
  282. width: 100%;
  283. }
  284. .banner-title {
  285. max-height: 42px;
  286. overflow: hidden;
  287. position: absolute;
  288. left: 15px;
  289. bottom: 15px;
  290. width: 90%;
  291. font-size: 16px;
  292. font-weight: 400;
  293. line-height: 21px;
  294. color: white;
  295. }
  296. .uni-media-list {
  297. padding: 11px 15px;
  298. box-sizing: border-box;
  299. display: flex;
  300. width: 100%;
  301. flex-direction: row;
  302. }
  303. .uni-media-list-logo {
  304. width: 90px;
  305. height: 70px;
  306. }
  307. .uni-media-list-body {
  308. flex: 1;
  309. padding-left: 7px;
  310. justify-content: space-around;
  311. }
  312. .uni-media-list-text-top {
  313. /* height: 37px; */
  314. font-size: 14px;
  315. lines: 2;
  316. overflow: hidden;
  317. color: var(--text-color,#333333);
  318. }
  319. .uni-media-list-text-bottom {
  320. display: flex;
  321. flex-direction: row;
  322. justify-content: space-between;
  323. }
  324. .uni-media-list-text {
  325. color: #9D9D9F;
  326. font-size: 13px;
  327. }
  328. </style>