1
0

list.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. <template>
  2. <view class="app-page" @touchstart="fnOnTouchstart" @touchend="fnOnTouchend" @touchcancel="fnOnTouchend">
  3. <view class="love-card" :class="{ ani: isDoAni }">
  4. <view class="head">
  5. <image class="avatar" :src="loveConfig.boy.avatar" mode="scaleToFill"></image>
  6. <view class="love-days">
  7. <view class="tip-text">相恋</view>
  8. <view class="number">
  9. <text class="boy">-</text>
  10. <text class="days">{{ calcLoveDays }}</text>
  11. <text class="girl">-</text>
  12. </view>
  13. <view class="tip-text">天</view>
  14. </view>
  15. <image class="avatar" :src="loveConfig.girl.avatar" mode="scaleToFill"></image>
  16. </view>
  17. <view class="foot">
  18. <view class="text" v-if="false">
  19. 我们已经相恋
  20. <text class="number">- {{ calcLoveDays }} -</text>
  21. 天啦
  22. </view>
  23. 看看我们的恋爱清单都完成了哪些吧
  24. </view>
  25. </view>
  26. <view v-if="list.length == 0" class="list empty">
  27. <view class="card">
  28. <image class="empty-image" :src="loveConfig.loveImageUrl" mode="scaleToFill"></image>
  29. <view class="empty-text">暂时还没有恋爱清单,快去制定你们的恋爱清单吧~</view>
  30. </view>
  31. </view>
  32. <view v-else class="list">
  33. <block v-for="(item, index) in list" :key="index">
  34. <view class="card" :class="{ ani: isDoAni }" :style="{ '--delay': calcCardDelay(index) }">
  35. <view class="head">
  36. <view class="status">
  37. <view v-if="!item.finish" class="text">进行中</view>
  38. <view v-else class="text finish">已完成</view>
  39. </view>
  40. <view class="title">
  41. <view class="title-name">{{ item.title }}</view>
  42. <view class="title-desc">{{ item.desc }}</view>
  43. </view>
  44. <view class="actions" @click="fnOnItemOpen(item)">
  45. <text class="icon">{{ item.open ? '-' : '+' }}</text>
  46. </view>
  47. </view>
  48. <view v-if="item.open" class="body">
  49. <view class="desc">
  50. <view class="desc-label">开始时间:</view>
  51. <view class="desc-value">{{ item.detail.start || '暂无计划' }}</view>
  52. </view>
  53. <view class="desc" v-if="item.detail.desc">
  54. <view class="desc-label">事件描述:</view>
  55. <view class="desc-value">{{ item.detail.desc }}</view>
  56. </view>
  57. <view class="desc">
  58. <view class="desc-label">完成时间:</view>
  59. <view class="desc-value">{{ item.detail.end || '未开始或正在进行中...' }}</view>
  60. </view>
  61. <view class="desc">
  62. <view class="desc-label">完成打卡:</view>
  63. <view class="desc-value">{{ item.detail.moment || '未开始或正在进行中...' }}</view>
  64. </view>
  65. <view class="desc" v-if="item.detail.other">
  66. <view class="desc-label">爱心备注:</view>
  67. <view class="desc-value">{{ item.detail.other }}</view>
  68. </view>
  69. </view>
  70. </view>
  71. </block>
  72. </view>
  73. <scroll-btn :scrollTop.sync="scrollTop" @on-status="fnOnScrollStatus"></scroll-btn>
  74. </view>
  75. </template>
  76. <script>
  77. import LoveConfig from '@/config/love.config.js';
  78. import ScrollBtn from '@/components/scroll-btn/scroll-btn.vue';
  79. export default {
  80. components: { ScrollBtn },
  81. data() {
  82. return {
  83. isDoAni: true,
  84. scrollTop: 0,
  85. loveConfig: LoveConfig,
  86. list: []
  87. };
  88. },
  89. computed: {
  90. calcLoveDays() {
  91. const formatStartDate = this.loveConfig.loveStartDate.replace(/-/g, '/');
  92. const start = new Date(formatStartDate),
  93. now = new Date();
  94. const T = now.getTime() - start.getTime();
  95. const i = 24 * 60 * 60 * 1000;
  96. const d = T / i;
  97. const D = Math.floor(d);
  98. return D;
  99. },
  100. calcCardDelay() {
  101. return index => {
  102. return Math.random() * index + 1 + 's';
  103. };
  104. }
  105. },
  106. created() {
  107. this.fnGetList();
  108. },
  109. onPageScroll(e) {
  110. this.scrollTop = e.scrollTop;
  111. },
  112. methods: {
  113. fnGetList() {
  114. if (LoveConfig.loveList.useApi && LoveConfig.loveList.api) {
  115. uni.request({
  116. url: LoveConfig.loveList.api,
  117. header: {
  118. ContentType: 'application/json'
  119. },
  120. method: 'GET',
  121. dataType: 'json',
  122. success: res => {
  123. if (res.statusCode == 200 && res.data.status == 200) {
  124. this.list = res.data.data.map(item => {
  125. item['open'] = false;
  126. return item;
  127. });
  128. } else {
  129. uni.$tm.toast('数据请求失败,请检查接口!');
  130. }
  131. },
  132. fail: err => {
  133. uni.$tm.toast('数据请求失败,请检查接口!');
  134. }
  135. });
  136. } else {
  137. this.list = LoveConfig.loveList.data.map(item => {
  138. item['open'] = false;
  139. return item;
  140. });
  141. }
  142. },
  143. fnOnItemOpen(item) {
  144. item.open = !item.open;
  145. this.$forceUpdate();
  146. },
  147. fnOnScrollStatus(isEnd) {
  148. this.isDoAni = isEnd;
  149. },
  150. fnOnTouchstart() {
  151. this.isDoAni = false;
  152. },
  153. fnOnTouchend() {
  154. this.isDoAni = true;
  155. }
  156. }
  157. };
  158. </script>
  159. <style scoped lang="scss">
  160. .app-page {
  161. width: 100vw;
  162. min-height: 100vh;
  163. box-sizing: border-box;
  164. padding: 36rpx;
  165. /* #ifdef H5 */
  166. padding-top: 60rpx;
  167. /* #endif */
  168. /* #ifndef H5 */
  169. padding-top: 180rpx;
  170. /* #endif */
  171. background: linear-gradient(
  172. 135deg,
  173. rgba(247, 149, 51, 0.1),
  174. rgba(243, 112, 85, 0.1) 15%,
  175. rgba(239, 78, 123, 0.1) 30%,
  176. rgba(161, 102, 171, 0.1) 44%,
  177. rgba(80, 115, 184, 0.1) 58%,
  178. rgba(16, 152, 173, 0.1) 72%,
  179. rgba(7, 179, 155, 0.1) 86%,
  180. rgba(109, 186, 130, 0.1)
  181. );
  182. }
  183. .love-card {
  184. width: 100%;
  185. display: flex;
  186. flex-direction: column;
  187. align-items: center;
  188. box-sizing: border-box;
  189. padding: 0 24rpx;
  190. padding-top: 66rpx;
  191. padding-bottom: 52rpx;
  192. border-radius: 50rpx;
  193. border: 4rpx solid rgba(96, 77, 68, 0.9);
  194. border-color: #faf8eb;
  195. background-color: rgba(255, 199, 184, 0.9);
  196. margin-bottom: 52rpx;
  197. box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.1);
  198. &.ani {
  199. animation: loveCardAni 3s ease-in-out infinite;
  200. }
  201. .head {
  202. display: flex;
  203. .avatar {
  204. width: 150rpx;
  205. height: 150rpx;
  206. box-sizing: border-box;
  207. border-radius: 50%;
  208. border: 6rpx solid rgba(255, 255, 255, 0.7);
  209. &.boy {
  210. border-color: #56bbf9;
  211. }
  212. &.girl {
  213. border-color: #f88ca2;
  214. }
  215. }
  216. .love-days {
  217. margin: 0 12rpx;
  218. display: flex;
  219. flex-direction: column;
  220. align-items: center;
  221. justify-content: center;
  222. font-size: 26rpx;
  223. .tip-text {
  224. color: #333;
  225. }
  226. .number {
  227. font-size: 46rpx;
  228. padding: 12rpx 0;
  229. > .boy {
  230. color: #56bbf9;
  231. margin-right: 12rpx;
  232. }
  233. > .girl {
  234. color: #f88ca2;
  235. margin-left: 12rpx;
  236. }
  237. }
  238. .days {
  239. animation: daysAni 6s ease-in-out infinite;
  240. font-weight: bold;
  241. }
  242. }
  243. }
  244. .foot {
  245. display: none;
  246. margin-top: 36rpx;
  247. font-size: 24rpx;
  248. }
  249. }
  250. @keyframes daysAni {
  251. 0% {
  252. color: #f88ca2;
  253. }
  254. 50% {
  255. color: #56bbf9;
  256. }
  257. 100% {
  258. color: #f88ca2;
  259. }
  260. }
  261. @keyframes loveCardAni {
  262. 0% {
  263. transform: scale(1);
  264. }
  265. 50% {
  266. transform: scale(1.03);
  267. }
  268. 100% {
  269. transform: scale(1);
  270. }
  271. }
  272. .list {
  273. display: flex;
  274. flex-direction: column;
  275. align-items: center;
  276. justify-content: center;
  277. box-sizing: border-box;
  278. }
  279. .empty {
  280. height: calc(100vh - 180rpx - 280rpx - 36rpx);
  281. .card {
  282. height: 100%;
  283. padding: 100rpx;
  284. margin-bottom: 0;
  285. text-align: center;
  286. justify-content: center;
  287. color: rgba(96, 77, 68, 0.9);
  288. }
  289. &-image {
  290. width: 300rpx;
  291. height: 300rpx;
  292. }
  293. &-text {
  294. margin-top: 36rpx;
  295. line-height: 50rpx;
  296. }
  297. }
  298. .card {
  299. width: 100%;
  300. display: flex;
  301. flex-direction: column;
  302. align-items: center;
  303. box-sizing: border-box;
  304. padding: 24rpx;
  305. border-radius: 50rpx;
  306. border: 4rpx solid rgba(96, 77, 68, 0.9);
  307. border-color: #fff;
  308. // background-color: #faf8eb;
  309. background-color: #ffffff;
  310. margin-bottom: 36rpx;
  311. box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.05);
  312. animation-delay: var(--delay);
  313. &.ani {
  314. animation: cardAni 3s ease-in-out infinite;
  315. }
  316. .head {
  317. width: 100%;
  318. display: flex;
  319. align-items: center;
  320. box-sizing: border-box;
  321. .status {
  322. width: 100rpx;
  323. display: flex;
  324. .text {
  325. width: 100rpx;
  326. height: 100rpx;
  327. border-radius: 50%;
  328. background-color: #ffc6ba;
  329. font-size: 24rpx;
  330. line-height: 100rpx;
  331. text-align: center;
  332. color: #55423b;
  333. &.finish {
  334. background-color: #bfe9ef;
  335. }
  336. }
  337. }
  338. .title {
  339. width: 0;
  340. flex-grow: 1;
  341. box-sizing: border-box;
  342. padding-left: 30rpx;
  343. padding-right: 24rpx;
  344. &-name {
  345. font-weight: bold;
  346. font-size: 30rpx;
  347. color: #333;
  348. }
  349. &-desc {
  350. margin-top: 8rpx;
  351. font-size: 26rpx;
  352. color: #555;
  353. text-overflow: ellipsis;
  354. overflow: hidden;
  355. white-space: nowrap;
  356. }
  357. }
  358. .actions {
  359. width: 50rpx;
  360. .icon {
  361. display: inline-block;
  362. width: 45rpx;
  363. height: 45rpx;
  364. background-color: rgba(96, 77, 68, 0.2);
  365. border-radius: 50%;
  366. text-align: center;
  367. line-height: 45rpx;
  368. font-size: 32rpx;
  369. font-weight: bold;
  370. }
  371. }
  372. }
  373. .body {
  374. margin-top: 24rpx;
  375. width: 100%;
  376. background-color: #ffffff;
  377. // background-color: #faf8eb;
  378. background-color: rgba(96, 77, 68, 0.05);
  379. border-radius: 24rpx;
  380. box-sizing: border-box;
  381. padding: 24rpx;
  382. padding-bottom: 12rpx;
  383. font-size: 26rpx;
  384. }
  385. }
  386. @keyframes cardAni {
  387. 0% {
  388. transform: translateY(0rpx);
  389. }
  390. 50% {
  391. transform: translateY(-10rpx);
  392. }
  393. 100% {
  394. transform: translateY(0rpx);
  395. }
  396. }
  397. .desc {
  398. display: flex;
  399. margin-bottom: 12rpx;
  400. &-label {
  401. color: #333;
  402. width: 140rpx;
  403. // font-weight: bold;
  404. }
  405. &-value {
  406. width: 0;
  407. flex-grow: 1;
  408. line-height: 1.5;
  409. color: #333;
  410. }
  411. }
  412. </style>