favorites.vue 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. <template>
  2. <view class="favorites-page">
  3. <!-- 列表 -->
  4. <view class="fav-list">
  5. <view class="fav-card" v-for="(item, idx) in list" :key="idx">
  6. <image @click="goPage('/pages/crowdFunding/crowdfundingDetails?id='+ item.id)" :src="item.main_image" class="fav-img"></image>
  7. <view class="fav-content">
  8. <view class="fav-header">
  9. <image :src="item.creator_avatar" class="avatar"></image>
  10. <text class="nickname" @click="goPage('/pages/crowdFunding/crowdfundingDetails?id='+ item.id)">{{ item.creator_username }}</text>
  11. <text class="tag" @click="goPage('/pages/crowdFunding/crowdfundingDetails?id='+ item.id)">发起人</text>
  12. <view style="position: relative;left: 0;top: 0;">
  13. <image src="/static/crowdFunding/more.png" class="more-img" @click="toggleDropdown(idx)"></image>
  14. <view class="dropdown-menu" v-if="currentDropdownIndex === idx">
  15. <view class="dropdown-item" @tap="handleOption('cancelCollection', idx)">取消收藏</view>
  16. </view>
  17. </view>
  18. </view>
  19. <view class="fav-title two-omit" @click="goPage('/pages/crowdFunding/crowdfundingDetails?id='+ item.id)">{{ item.title }}</view>
  20. <view class="fav-bottom">
  21. <text class="amount">已筹 ¥{{ item.current_amount }}</text>
  22. <!-- <text class="favorite-time">收藏于{{ item.favorite_time }}</text> -->
  23. </view>
  24. </view>
  25. </view>
  26. </view>
  27. <view v-if="!isLoading && list.length === 0" class="no-data">
  28. <text>暂无数据</text>
  29. </view>
  30. </view>
  31. </template>
  32. <script>
  33. export default {
  34. data() {
  35. return {
  36. list: [],
  37. page: 1,
  38. pageSize: 20,
  39. total: 0,
  40. isLoading: false,
  41. hasMore: true,
  42. currentDropdownIndex: null,
  43. show1: false,
  44. };
  45. },
  46. onLoad() {
  47. this.page = 1;
  48. this.list = [];
  49. this.hasMore = true;
  50. this.getList();
  51. },
  52. onPullDownRefresh() {
  53. this.page = 1;
  54. this.list = [];
  55. this.hasMore = true;
  56. this.getList(() => uni.stopPullDownRefresh());
  57. },
  58. onReachBottom() {
  59. if (this.hasMore && !this.isLoading) {
  60. this.getList();
  61. }
  62. },
  63. methods: {
  64. getList(cb) {
  65. if (this.isLoading) return;
  66. this.isLoading = true;
  67. uni.request({
  68. url: this.$apiHost + '/crowdfund/favorites',
  69. method: 'GET',
  70. data: {
  71. page: this.page,
  72. pageSize: this.pageSize,
  73. uuid: getApp().globalData.uuid,
  74. skey: getApp().globalData.skey,
  75. },
  76. success: (res) => {
  77. if (res.data && res.data.success === 'yes' && res.data.data) {
  78. var { list, total } = res.data.data;
  79. if (!Array.isArray(list)) {
  80. list = [];
  81. }
  82. if (this.page === 1) {
  83. this.list = list;
  84. } else {
  85. this.list = [...this.list, ...list];
  86. }
  87. this.total = total;
  88. this.hasMore = this.list && this.list.length < total;
  89. if (this.hasMore) this.page++;
  90. }
  91. },
  92. complete: () => {
  93. this.isLoading = false;
  94. cb && cb();
  95. }
  96. });
  97. },
  98. goBack() {
  99. uni.navigateBack();
  100. },
  101. toggleDropdown(idx) {
  102. this.currentDropdownIndex = this.currentDropdownIndex === idx ? null : idx;
  103. },
  104. goPage(url) {
  105. console.log(url,888);
  106. uni.navigateTo({
  107. url,
  108. });
  109. },
  110. // 处理下拉菜单选项点击
  111. handleOption(type, idx) {
  112. this.currentDropdownIndex = null;
  113. if (type === "cancelCollection") {
  114. const item = this.list[idx];
  115. uni.request({
  116. url: this.$apiHost + '/crowdfund/favorite',
  117. method: 'POST',
  118. header: {
  119. 'content-type': 'application/x-www-form-urlencoded',
  120. },
  121. data: {
  122. crowdfund_id: item.id,
  123. action: 'remove',
  124. uuid: getApp().globalData.uuid,
  125. skey: getApp().globalData.skey,
  126. },
  127. success: (res) => {
  128. if (res.data && res.data.success && res.data.success === 'yes') {
  129. this.list.splice(idx, 1);
  130. uni.showToast({
  131. title: '取消收藏成功',
  132. icon: 'none',
  133. duration: 2000
  134. });
  135. this.show1 = !this.show1;
  136. } else {
  137. uni.showToast({
  138. title: res.data.msg || '取消失败',
  139. icon: 'none',
  140. duration: 2000
  141. });
  142. }
  143. },
  144. fail: () => {
  145. uni.showToast({
  146. title: '网络错误',
  147. icon: 'none',
  148. duration: 2000
  149. });
  150. }
  151. });
  152. }
  153. },
  154. },
  155. };
  156. </script>
  157. <style lang="scss" scoped>
  158. .favorites-page {
  159. min-height: 100vh;
  160. background: #f2f6f2;
  161. .fav-list {
  162. padding: 24rpx 0;
  163. .fav-card {
  164. display: flex;
  165. background: #fff;
  166. border-radius: 18rpx;
  167. margin: 0 24rpx 24rpx 24rpx;
  168. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
  169. padding: 20rpx;
  170. .fav-img {
  171. width: 140rpx;
  172. height: 140rpx;
  173. border-radius: 12rpx;
  174. object-fit: cover;
  175. flex-shrink: 0;
  176. }
  177. .fav-content {
  178. flex: 1;
  179. margin-left: 20rpx;
  180. display: flex;
  181. flex-direction: column;
  182. justify-content: space-between;
  183. .fav-header {
  184. display: flex;
  185. align-items: center;
  186. .avatar {
  187. width: 44rpx;
  188. height: 44rpx;
  189. border-radius: 50%;
  190. margin-right: 12rpx;
  191. }
  192. .nickname {
  193. font-weight: 400;
  194. font-size: 24rpx;
  195. color: #1f1f1f;
  196. font-family: "PingFang SC-Bold";
  197. max-width: 250rpx;
  198. margin-right: 12rpx;
  199. }
  200. .tag {
  201. font-size: 20rpx;
  202. color: #acf934;
  203. background: #1f1f1f;
  204. border-radius: 8rpx;
  205. padding: 2rpx 12rpx;
  206. margin-right: auto;
  207. margin-left: 4rpx;
  208. }
  209. .more-img {
  210. width: 36rpx;
  211. height: 36rpx;
  212. margin-left: 12rpx;
  213. }
  214. }
  215. .fav-title {
  216. font-size: 26rpx;
  217. color: #222;
  218. font-weight: 500;
  219. margin: 10rpx 0 0 0;
  220. overflow: hidden;
  221. text-overflow: ellipsis;
  222. width: 100%;
  223. }
  224. .fav-bottom {
  225. margin-top: 10rpx;
  226. display: flex;
  227. justify-content: flex-end;
  228. align-items: center;
  229. .amount {
  230. font-size: 22rpx;
  231. color: #bdbdbd;
  232. }
  233. .favorite-time {
  234. font-size: 22rpx;
  235. color: #bdbdbd;
  236. margin-left: 10rpx;
  237. }
  238. }
  239. }
  240. }
  241. }
  242. .dropdown-menu {
  243. position: absolute;
  244. top: calc(100% + 10rpx);
  245. right: 20rpx;
  246. background-color: #ffffff;
  247. border-radius: 20rpx;
  248. padding: 0;
  249. width: 150rpx;
  250. box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
  251. z-index: 100;
  252. transform-origin: top right;
  253. animation: dropdownAnimation 0.2s ease-out;
  254. overflow: hidden;
  255. .dropdown-item {
  256. padding: 15rpx 0;
  257. color: #333333;
  258. font-size: 24rpx;
  259. position: relative;
  260. text-align: center;
  261. &:not(:last-child)::after {
  262. content: "";
  263. position: absolute;
  264. left: 0;
  265. right: 0;
  266. bottom: 0;
  267. height: 1rpx;
  268. background-color: #eeeeee;
  269. }
  270. &:active {
  271. background-color: #f8f8f8;
  272. }
  273. }
  274. }
  275. @keyframes dropdownAnimation {
  276. 0% {
  277. opacity: 0;
  278. transform: scale(0.95) translateY(-5rpx);
  279. }
  280. 100% {
  281. opacity: 1;
  282. transform: scale(1) translateY(0);
  283. }
  284. }
  285. .no-data {
  286. text-align: center;
  287. color: #bbb;
  288. font-size: 28rpx;
  289. padding: 80rpx 0 40rpx 0;
  290. }
  291. }
  292. </style>