木邻有你前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

437 lines
11 KiB

1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
  1. <template>
  2. <view class="activity-page">
  3. <!-- 搜索栏和一级Tab合并容器 -->
  4. <view class="search-section">
  5. <view class="search-bar">
  6. <uv-search placeholder="请输入搜索内容" v-model="params.title" @search="handleSearch" @clear="handleSearch" @clickIcon="handleSearch" :showAction="false" ></uv-search>
  7. </view>
  8. <!-- 一级Tab当前活动/往期活动 移到搜索容器内 -->
  9. <view class="primary-tabs">
  10. <view
  11. class="primary-tab-item"
  12. :class="{ active: primaryActiveTab === 'current' }"
  13. @click="switchPrimaryTab('current')"
  14. >
  15. 当前活动
  16. </view>
  17. <view
  18. class="primary-tab-item"
  19. :class="{ active: primaryActiveTab === 'past' }"
  20. @click="switchPrimaryTab('past')"
  21. >
  22. 往期活动
  23. </view>
  24. </view>
  25. </view>
  26. <!-- 二级Tab自定义Tab组件 -->
  27. <view class="secondary-tabs">
  28. <scroll-view scroll-x="true" class="tab-scroll">
  29. <view class="tab-list">
  30. <!-- 全部Tab -->
  31. <view
  32. class="tab-item"
  33. :class="{ active: secondaryActiveIndex === 0 }"
  34. @click="switchSecondaryTab(0, '全部')"
  35. >
  36. <text class="tab-text">全部</text>
  37. </view>
  38. <!-- 从store获取的活动分类Tab -->
  39. <view
  40. v-for="(category, index) in categoryActivityList"
  41. :key="category.id"
  42. class="tab-item"
  43. :class="{ active: secondaryActiveIndex === index + 1 }"
  44. @click="switchSecondaryTab(index + 1, category.title, category.id)"
  45. >
  46. <text class="tab-text">{{ category.title }}</text>
  47. </view>
  48. <!-- 动画下划线 -->
  49. <!-- <view class="tab-line" :style="lineStyle"></view> -->
  50. </view>
  51. </scroll-view>
  52. </view>
  53. <!-- 活动列表 -->
  54. <view class="activity-list">
  55. <view
  56. class="activity-item"
  57. v-for="(item, index) in activities"
  58. :key="index"
  59. @click="goToActivityDetail(item)"
  60. >
  61. <!-- 活动图片 -->
  62. <view class="activity-image">
  63. <image :src="item.image" mode="aspectFill" class="image"></image>
  64. </view>
  65. <!-- 活动信息 -->
  66. <view class="activity-info">
  67. <view class="title-row">
  68. <!-- 活动标签 -->
  69. <view class="activity-tag" :style="{ backgroundColor: item.tagColor }">
  70. {{ item.score }}
  71. </view>
  72. <view class="activity-title">{{ item.title }}</view>
  73. </view>
  74. <view class="activity-location">
  75. <uv-icon name="map-fill" size="14" color="#999"></uv-icon>
  76. <text class="location-text">{{ item.address }}</text>
  77. </view>
  78. <view class="activity-time">
  79. <uv-icon name="calendar" size="14" color="#999"></uv-icon>
  80. <text class="time-text">{{ item.createTime }}</text>
  81. </view>
  82. <view class="activity-participants">
  83. <uv-icon name="account-fill" size="14" color="#999"></uv-icon>
  84. <text class="participants-text" >报名人数{{ item.numActivity }}/{{ item.numLimit }}</text>
  85. </view>
  86. </view>
  87. <!-- 报名按钮 -->
  88. <view class="activity-action">
  89. <uv-button v-if="item.status === '1'" type="primary" size="mini" shape="circle" text="已结束" disabled @click.stop="signUpActivity(item)"></uv-button>
  90. <uv-button v-else-if="item.isApply === 1" type="primary" size="mini" shape="circle" text="已报名" disabled @click.stop="signUpActivity(item)"></uv-button>
  91. <uv-button v-else type="primary" size="mini" shape="circle" :text="item.numActivity >= item.numLimit ? '已结束' : '报名中'" :disabled="item.numActivity >= item.numLimit" @click.stop="signUpActivity(item)"></uv-button>
  92. </view>
  93. </view>
  94. </view>
  95. <!-- 空状态 -->
  96. <view class="empty-state" v-if="activities.length === 0">
  97. <uv-empty icon="/static/暂无搜索结果.png" text="暂无活动数据"></uv-empty>
  98. <!-- <image src="/static/暂无搜索结果.png" style="width: 460rpx; height: 460rpx; margin: 0 auto;"></image> -->
  99. </view>
  100. </view>
  101. </template>
  102. <script>
  103. export default {
  104. data() {
  105. return {
  106. searchKeyword: '',
  107. primaryActiveTab: 'current', // current: 当前活动, past: 往期活动
  108. params: {
  109. pageNo: 1,
  110. pageSize: 10,
  111. title: '', // 搜索关键字
  112. // categoryId: null, // 分类的id
  113. status: 0 // 活动的状态 0是当前 1是往期
  114. },
  115. secondaryActiveIndex: 0,
  116. // 模拟活动数据
  117. activities: []
  118. }
  119. },
  120. computed: {
  121. // 从store获取活动分类列表
  122. categoryActivityList() {
  123. return this.$store.state.categoryActivityList || []
  124. },
  125. },
  126. methods: {
  127. handleSearch(value) {
  128. if (value) {
  129. this.params['title'] = value
  130. }
  131. this.initData()
  132. this.getActivityList()
  133. },
  134. // 切换一级tab
  135. switchPrimaryTab(tab) {
  136. this.primaryActiveTab = tab;
  137. this.initData()
  138. delete this.params['categoryId']
  139. // 标签回到全部
  140. this.secondaryActiveIndex = 0
  141. this.params['status'] = tab === 'current' ? 0 : 1
  142. this.getActivityList()
  143. },
  144. // 切换二级tab
  145. async switchSecondaryTab(index, tabName, categoryId = null) {
  146. this.initData()
  147. this.secondaryActiveIndex = index
  148. delete this.params['categoryId']
  149. if (index === 0) {
  150. // 全部Tab
  151. console.log('点击了全部Tab')
  152. } else {
  153. // 活动分类Tab
  154. this.params['categoryId'] = categoryId
  155. }
  156. await this.getActivityList()
  157. },
  158. // 跳转到活动详情
  159. goToActivityDetail(activity) {
  160. uni.navigateTo({
  161. url: `/subPages/index/activityDetail?id=${activity.id}`
  162. });
  163. },
  164. // 报名活动
  165. signUpActivity(item) {
  166. if (item.isFullOrExpired) {
  167. uni.showToast({
  168. title: '活动已结束',
  169. icon: 'none'
  170. });
  171. return;
  172. }
  173. uni.navigateTo({
  174. url: `/subPages/index/activityDetail?id=${item.id}`
  175. });
  176. },
  177. // 获取活动列表
  178. async getActivityList(){
  179. const res = await this.$api.activity.queryActivityList(this.params)
  180. if(res.result.records.length){
  181. this.activities.push(...res.result.records)
  182. this.params.pageNo++
  183. }else {
  184. uni.showToast({
  185. title: '暂无活动数据',
  186. icon: 'none'
  187. })
  188. }
  189. },
  190. initData() {
  191. this.params['pageNo'] = 1
  192. this.activities = []
  193. }
  194. },
  195. async onShow() {
  196. // 确保store中的活动分类数据已加载
  197. if (this.categoryActivityList.length === 0) {
  198. await this.$store.dispatch('getCategoryActivityList')
  199. }
  200. this.initData()
  201. this.params['title'] = ''
  202. await this.getActivityList()
  203. },
  204. onReachBottom() {
  205. this.getActivityList()
  206. },
  207. async onPullDownRefresh() {
  208. this.initData()
  209. await this.getActivityList()
  210. uni.stopPullDownRefresh()
  211. }
  212. }
  213. </script>
  214. <style lang="scss" scoped>
  215. .activity-page {
  216. background-color: #f5f5f5;
  217. min-height: 100vh;
  218. }
  219. // 搜索栏样式 - 修改为包含一级Tab
  220. .search-section {
  221. height: 350rpx;
  222. background: linear-gradient(180deg,#1488db, #98b5f1);
  223. padding-top: 180rpx; /* 使用padding-top避免margin塌陷 */
  224. box-sizing: border-box; /* 确保padding包含在高度内 */
  225. }
  226. .search-bar {
  227. // background-color: white;
  228. // border-radius: 50rpx;
  229. padding: 5rpx 40rpx;
  230. // display: flex;
  231. // align-items: center;
  232. // gap: 20rpx;
  233. // width: 85%;
  234. // margin: 0 auto ; /* 移除margin-top,只保留左右居中和下边距 */
  235. }
  236. .search-input {
  237. flex: 1;
  238. font-size: 28rpx;
  239. color: #333;
  240. &::placeholder {
  241. color: #999;
  242. }
  243. }
  244. // 一级Tab样式 - 调整为在蓝色背景内
  245. .primary-tabs {
  246. display: flex;
  247. padding: 0 20rpx;
  248. margin-bottom: 20rpx;
  249. }
  250. .primary-tab-item {
  251. flex: 1;
  252. text-align: center;
  253. padding: 20rpx 0;
  254. font-size: 32rpx;
  255. color: #000000; /* 白色半透明 */
  256. position: relative;
  257. transition: color 0.3s ease;
  258. &.active {
  259. color: white; /* 激活状态为纯白色 */
  260. font-weight: 600;
  261. &::after {
  262. content: '';
  263. position: absolute;
  264. bottom: 0;
  265. left: 50%;
  266. transform: translateX(-50%);
  267. // transition: transform 0.3s ease;
  268. width: 100rpx;
  269. height: 6rpx;
  270. background-color: white; /* 下划线改为白色 */
  271. border-radius: 3rpx;
  272. }
  273. }
  274. }
  275. // 二级Tab样式 - 自定义实现
  276. .secondary-tabs {
  277. background-color: white;
  278. border-bottom: 1px solid #f0f0f0;
  279. position: relative;
  280. .tab-scroll {
  281. white-space: nowrap;
  282. .tab-list {
  283. display: flex;
  284. // position: relative;
  285. justify-content: space-evenly;
  286. .tab-item {
  287. flex-shrink: 0;
  288. padding: 24rpx 32rpx;
  289. display: flex;
  290. align-items: center;
  291. justify-content: center;
  292. transition: all 0.3s ease;
  293. .tab-text {
  294. font-size: 28rpx;
  295. color: #666666;
  296. font-weight: 500;
  297. transition: color 0.3s ease;
  298. }
  299. &.active {
  300. .tab-text {
  301. color: #007AFF;
  302. font-weight: 600;
  303. }
  304. }
  305. }
  306. }
  307. .tab-line {
  308. position: absolute;
  309. bottom: 10;
  310. height: 6rpx;
  311. background-color: #007AFF;
  312. border-radius: 3rpx;
  313. transition: transform 0.3s ease;
  314. }
  315. }
  316. }
  317. // 活动列表样式
  318. .activity-list {
  319. padding: 20rpx;
  320. }
  321. .activity-item {
  322. background-color: white;
  323. border-radius: 12rpx;
  324. margin-bottom: 30rpx;
  325. padding: 20rpx;
  326. display: flex;
  327. box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
  328. }
  329. .activity-image {
  330. width: 180rpx;
  331. height: 180rpx;
  332. border-radius: 8rpx;
  333. overflow: hidden;
  334. flex-shrink: 0;
  335. margin-right: 20rpx;
  336. }
  337. .image {
  338. width: 100%;
  339. height: 100%;
  340. }
  341. .activity-info {
  342. flex: 1;
  343. display: flex;
  344. flex-direction: column;
  345. justify-content: space-between;
  346. }
  347. .title-row {
  348. display: flex;
  349. align-items: center;
  350. margin-bottom: 10rpx;
  351. }
  352. .activity-tag {
  353. width: 31px;
  354. height: 20px;
  355. background: #218cdd;
  356. border-radius: 3.5px;
  357. margin-right: 7rpx;
  358. display: flex;
  359. align-items: center;
  360. justify-content: center;
  361. font-size: 18rpx;
  362. color: white;
  363. font-weight: 600;
  364. }
  365. .activity-title {
  366. font-size: 28rpx;
  367. font-weight: bold;
  368. color: #333;
  369. line-height: 1.4;
  370. }
  371. .activity-location,
  372. .activity-time,
  373. .activity-participants {
  374. display: flex;
  375. align-items: center;
  376. margin-bottom: 6rpx;
  377. .location-text,
  378. .time-text,
  379. .participants-text {
  380. font-size: 24rpx;
  381. color: #666;
  382. margin-left: 6rpx;
  383. }
  384. }
  385. .activity-action {
  386. display: flex;
  387. align-items: flex-end;
  388. padding-bottom: 10rpx;
  389. }
  390. // 空状态样式
  391. .empty-state {
  392. padding: 100rpx 40rpx;
  393. }
  394. </style>