list.vue 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. <template>
  2. <view class="content">
  3. <view class="navbar">
  4. <view v-for="(item, index) in navList" :key="index" class="nav-item" :class="{current: tabCurrentIndex === index}"
  5. @click="tabClick(index)">
  6. <view class="hour">
  7. <view>{{item.starttime_hour}}</view>
  8. <view class="text">{{state[item.state].text}}</view>
  9. </view>
  10. </view>
  11. </view>
  12. <swiper :current="tabCurrentIndex" class="swiper-box" duration="300" @change="changeTab">
  13. <swiper-item v-for="(tabItem, tabIndex) in navList" :key="tabIndex">
  14. <scroll-view class="list-scroll-content" scroll-y @scrolltolower="loadData">
  15. <!-- 空白页 -->
  16. <empty v-if="tabItem.loaded === true && tabItem.list.length === 0"></empty>
  17. <!-- 产品列表 -->
  18. <view v-for="(item, index) in tabItem.list" :key="index" class="order-item">
  19. <view class="info" @click="navToDetailPage(item.product.product_id, tabItem.flash_id)">
  20. <view class="image">
  21. <image mode="aspectFill" :src="item.product.image"></image>
  22. </view>
  23. <view class="detail">
  24. <view class="title">{{item.product.title}}</view>
  25. <view class="introduction">{{item.introduction}}</view>
  26. <view class="price">
  27. <view class="sales">¥{{item.product.sales_price}} </view>
  28. <view class="market"> ¥{{item.product.market_price}}</view>
  29. </view>
  30. <ProgressBar class="ProgressBar" :Sold="item.sold" :widthUpx="250" :Width="percentage(item.number,item.sold)"
  31. Type="candy" :Vice="true"></ProgressBar>
  32. <view class="loot">{{tabItem.state > 0 ? "马上抢" : "未开始"}}</view>
  33. </view>
  34. </view>
  35. </view>
  36. <uni-load-more :status="tabItem.loadingType"></uni-load-more>
  37. </scroll-view>
  38. </swiper-item>
  39. </swiper>
  40. </view>
  41. </template>
  42. <script>
  43. import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue';
  44. import empty from "@/components/empty";
  45. import ProgressBar from '@/components/Progress-Bar/Progress-Bar';
  46. export default {
  47. components: {
  48. uniLoadMore,
  49. empty,
  50. ProgressBar
  51. },
  52. computed: {},
  53. data() {
  54. return {
  55. pageSize: 15,
  56. tabCurrentIndex: 0,
  57. navList: [],
  58. state: [{
  59. text: '未开始'
  60. }, {
  61. text: '已开抢'
  62. }, {
  63. text: '抢购进行中'
  64. }]
  65. }
  66. },
  67. onPullDownRefresh() {
  68. this.loadNavBar();
  69. },
  70. onLoad(options) {
  71. this.loadNavBar();
  72. },
  73. methods: {
  74. // 加载标签
  75. async loadNavBar() {
  76. let navbar = await this.$api.request('/flash/navbar', 'GET');
  77. uni.stopPullDownRefresh();
  78. if (navbar) {
  79. this.navList = navbar;
  80. this.navList.forEach((item, index) => {
  81. item = Object.assign(item, {
  82. list: [],
  83. page: 1,
  84. loadingType: 'more'
  85. }); //新增一个数组用来储存商品
  86. if (item.current) {
  87. this.tabCurrentIndex = index;
  88. }
  89. // 建议把后端处理的starttime_hour和text字段在这里用js处理
  90. });
  91. this.loadData('tabChange', this.navList[this.tabCurrentIndex].flash_id);
  92. this.setNavigationBarTitle(this.navList[this.tabCurrentIndex].title);
  93. //console.log(this.navList);
  94. }
  95. },
  96. // 加载数据
  97. async loadData(source = false, flash_id = 0) {
  98. //这里是将订单挂载到tab列表下
  99. let index = this.tabCurrentIndex;
  100. let navItem = this.navList[index];
  101. if (source === 'tabChange' && navItem.loaded === true) {
  102. //tab切换只有第一次需要加载数据
  103. return;
  104. }
  105. if (navItem.loadingType === 'loading') {
  106. //防止重复加载
  107. return;
  108. }
  109. if (navItem.loadingType == 'noMore') {
  110. //没有更多数据
  111. return;
  112. }
  113. navItem.loadingType = 'loading';
  114. let result = await this.$api.request('/flash/product', 'GET', {
  115. flash_id: flash_id,
  116. page: navItem.page,
  117. pagesize: this.pageSize
  118. });
  119. if (result) {
  120. if (result.length >= this.pageSize) {
  121. //判断是否还有数据, 有改为 more, 没有改为noMore
  122. navItem.loadingType = 'more';
  123. } else {
  124. navItem.loadingType = 'noMore';
  125. }
  126. // 页数加一
  127. navItem.page++;
  128. result.forEach((item, index) => {
  129. navItem.list.push(item);
  130. })
  131. //loaded新字段用于表示数据加载完毕,如果为空可以显示空白页
  132. this.$set(navItem, 'loaded', true);
  133. }
  134. },
  135. //顶部tab点击
  136. tabClick(index) {
  137. this.tabCurrentIndex = index;
  138. },
  139. //swiper 切换
  140. changeTab(e) {
  141. this.tabCurrentIndex = e.target.current;
  142. this.loadData('tabChange', this.navList[this.tabCurrentIndex].flash_id);
  143. this.setNavigationBarTitle(this.navList[this.tabCurrentIndex].title);
  144. },
  145. // 计算百分比
  146. percentage(number, sold) {
  147. if (sold == 0) {
  148. return 0;
  149. }
  150. return parseInt(sold / number * 100);
  151. },
  152. // 商品详情页
  153. navToDetailPage(product_id, flash_id = 0) {
  154. uni.navigateTo({
  155. url: `/pages/product/product?id=${product_id}&flash=${flash_id}`
  156. });
  157. },
  158. // 设置导航栏名称
  159. setNavigationBarTitle(title) {
  160. uni.setNavigationBarTitle({
  161. title: '限时秒杀|' + title
  162. });
  163. }
  164. }
  165. }
  166. </script>
  167. <style lang="scss">
  168. page,
  169. .content {
  170. //background: $page-color-base;
  171. background: #f5f5f5;
  172. height: 100%;
  173. }
  174. .swiper-box {
  175. height: calc(100% - 50px);
  176. }
  177. .list-scroll-content {
  178. height: 100%;
  179. }
  180. .navbar {
  181. display: flex;
  182. height: 50px;
  183. padding: 0 5px;
  184. background: #000;
  185. box-shadow: 0 1px 5px rgba(0, 0, 0, .06);
  186. position: relative;
  187. z-index: 10;
  188. overflow: auto;
  189. .nav-item {
  190. flex: 1;
  191. display: flex;
  192. justify-content: center;
  193. align-items: center;
  194. height: 100%;
  195. font-size: 15px;
  196. //color: $font-color-dark;
  197. color: #fff;
  198. font-weight: 900;
  199. position: relative;
  200. text-align: center;
  201. min-width: 150upx;
  202. .hour {
  203. flex-flow: column;
  204. display: flex;
  205. .text {
  206. font-size: 10px;
  207. font-weight: 400;
  208. }
  209. }
  210. &.current {
  211. //color: $base-color;
  212. background-color: $base-color;
  213. &:after {
  214. content: '';
  215. position: absolute;
  216. //left: 50%;
  217. bottom: -10upx;
  218. //transform: translateX(-50%);
  219. transform: rotate(45deg);
  220. //width: 44px;
  221. width: 20upx;
  222. //height: 0;
  223. height: 20upx;
  224. background-color: $base-color;
  225. //border-bottom: 2px solid $base-color;
  226. }
  227. }
  228. }
  229. }
  230. .order-item {
  231. display: flex;
  232. flex-direction: column;
  233. padding-left: 30upx;
  234. background: #fff;
  235. margin-top: 16upx;
  236. .info {
  237. display: flex;
  238. flex-direction: row;
  239. .image {
  240. image {
  241. width: 250upx;
  242. height: 250upx;
  243. border-radius: 10upx;
  244. }
  245. }
  246. .detail {
  247. width: 440upx;
  248. height: 250upx;
  249. padding-left: 30upx;
  250. padding-top: 6upx;
  251. position: relative;
  252. .title {
  253. color: #303133;
  254. overflow: hidden;
  255. text-overflow: ellipsis;
  256. white-space: nowrap;
  257. }
  258. .introduction {
  259. color: #999999;
  260. font-size: 26upx;
  261. -webkit-line-clamp: 2;
  262. overflow: hidden;
  263. display: -webkit-box;
  264. -webkit-box-orient: vertical;
  265. }
  266. .price {
  267. position: absolute;
  268. bottom: 56upx;
  269. display: flex;
  270. flex-direction: row;
  271. .sales {
  272. font-size: 40upx;
  273. color: $base-color;
  274. font-weight: 500;
  275. }
  276. .market {
  277. vertical-align: bottom;
  278. font-size: 25upx;
  279. text-decoration: line-through;
  280. }
  281. }
  282. .ProgressBar {
  283. position: absolute;
  284. bottom: 0;
  285. }
  286. .loot {
  287. position: absolute;
  288. right: 0;
  289. bottom: 14rpx;
  290. background: $base-color;
  291. color: #fff;
  292. padding: 4upx 14upx;
  293. border-radius: 4upx;
  294. box-shadow: 2upx 2upx 8upx -2px #000;
  295. font-size: 32rpx;
  296. }
  297. }
  298. }
  299. }
  300. /* load-more */
  301. .uni-load-more {
  302. display: flex;
  303. flex-direction: row;
  304. height: 80upx;
  305. align-items: center;
  306. justify-content: center
  307. }
  308. .uni-load-more__text {
  309. font-size: 28upx;
  310. color: #999
  311. }
  312. .uni-load-more__img {
  313. height: 24px;
  314. width: 24px;
  315. margin-right: 10px
  316. }
  317. .uni-load-more__img>view {
  318. position: absolute
  319. }
  320. .uni-load-more__img>view view {
  321. width: 6px;
  322. height: 2px;
  323. border-top-left-radius: 1px;
  324. border-bottom-left-radius: 1px;
  325. background: #999;
  326. position: absolute;
  327. opacity: .2;
  328. transform-origin: 50%;
  329. animation: load 1.56s ease infinite
  330. }
  331. .uni-load-more__img>view view:nth-child(1) {
  332. transform: rotate(90deg);
  333. top: 2px;
  334. left: 9px
  335. }
  336. .uni-load-more__img>view view:nth-child(2) {
  337. transform: rotate(180deg);
  338. top: 11px;
  339. right: 0
  340. }
  341. .uni-load-more__img>view view:nth-child(3) {
  342. transform: rotate(270deg);
  343. bottom: 2px;
  344. left: 9px
  345. }
  346. .uni-load-more__img>view view:nth-child(4) {
  347. top: 11px;
  348. left: 0
  349. }
  350. .load1,
  351. .load2,
  352. .load3 {
  353. height: 24px;
  354. width: 24px
  355. }
  356. .load2 {
  357. transform: rotate(30deg)
  358. }
  359. .load3 {
  360. transform: rotate(60deg)
  361. }
  362. .load1 view:nth-child(1) {
  363. animation-delay: 0s
  364. }
  365. .load2 view:nth-child(1) {
  366. animation-delay: .13s
  367. }
  368. .load3 view:nth-child(1) {
  369. animation-delay: .26s
  370. }
  371. .load1 view:nth-child(2) {
  372. animation-delay: .39s
  373. }
  374. .load2 view:nth-child(2) {
  375. animation-delay: .52s
  376. }
  377. .load3 view:nth-child(2) {
  378. animation-delay: .65s
  379. }
  380. .load1 view:nth-child(3) {
  381. animation-delay: .78s
  382. }
  383. .load2 view:nth-child(3) {
  384. animation-delay: .91s
  385. }
  386. .load3 view:nth-child(3) {
  387. animation-delay: 1.04s
  388. }
  389. .load1 view:nth-child(4) {
  390. animation-delay: 1.17s
  391. }
  392. .load2 view:nth-child(4) {
  393. animation-delay: 1.3s
  394. }
  395. .load3 view:nth-child(4) {
  396. animation-delay: 1.43s
  397. }
  398. @-webkit-keyframes load {
  399. 0% {
  400. opacity: 1
  401. }
  402. 100% {
  403. opacity: .2
  404. }
  405. }
  406. </style>