木邻有你前端代码仓库
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.

402 lines
9.8 KiB

10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months 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 list"
  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="list.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. import MixinList from '@/mixins/list'
  104. export default {
  105. mixins: [MixinList],
  106. data() {
  107. return {
  108. primaryActiveTab: 'current', // current: 当前活动, past: 往期活动
  109. mixinListApi: 'activity.queryActivityList',
  110. params: {
  111. title: '', // 搜索关键字
  112. status: 0 // 活动的状态 0是当前 1是往期
  113. },
  114. secondaryActiveIndex: 0,
  115. // 模拟活动数据
  116. list: []
  117. }
  118. },
  119. computed: {
  120. // 从store获取活动分类列表
  121. categoryActivityList() {
  122. return this.$store.state.categoryActivityList || []
  123. },
  124. },
  125. methods: {
  126. mixinSetParams(){
  127. return {
  128. ...this.params
  129. }
  130. },
  131. handleSearch(value) {
  132. if (value) {
  133. this.params['title'] = value
  134. }
  135. this.initPage()
  136. this.getList(true)
  137. },
  138. // 切换一级tab
  139. switchPrimaryTab(tab) {
  140. this.primaryActiveTab = tab;
  141. this.initPage()
  142. delete this.params['categoryId']
  143. // 标签回到全部
  144. this.secondaryActiveIndex = 0
  145. this.params['status'] = tab === 'current' ? 0 : 1
  146. this.getList(true)
  147. },
  148. // 切换二级tab
  149. async switchSecondaryTab(index, tabName, categoryId = null) {
  150. this.initPage()
  151. this.secondaryActiveIndex = index
  152. delete this.params['categoryId']
  153. if (index === 0) {
  154. // 全部Tab
  155. // console.log('点击了全部Tab')
  156. } else {
  157. // 活动分类Tab
  158. this.params['categoryId'] = categoryId
  159. }
  160. this.getList(true)
  161. },
  162. // 跳转到活动详情
  163. goToActivityDetail(activity) {
  164. uni.navigateTo({
  165. url: `/subPages/index/activityDetail?id=${activity.id}`
  166. });
  167. },
  168. // 报名活动
  169. signUpActivity(item) {
  170. uni.navigateTo({
  171. url: `/subPages/index/activityDetail?id=${item.id}`
  172. });
  173. },
  174. },
  175. }
  176. </script>
  177. <style lang="scss" scoped>
  178. .activity-page {
  179. background-color: #f5f5f5;
  180. min-height: 100vh;
  181. }
  182. // 搜索栏样式 - 修改为包含一级Tab
  183. .search-section {
  184. height: 350rpx;
  185. background: linear-gradient(180deg,#1488db, #98b5f1);
  186. padding-top: 180rpx; /* 使用padding-top避免margin塌陷 */
  187. box-sizing: border-box; /* 确保padding包含在高度内 */
  188. }
  189. .search-bar {
  190. // background-color: white;
  191. // border-radius: 50rpx;
  192. padding: 5rpx 40rpx;
  193. // display: flex;
  194. // align-items: center;
  195. // gap: 20rpx;
  196. // width: 85%;
  197. // margin: 0 auto ; /* 移除margin-top,只保留左右居中和下边距 */
  198. }
  199. .search-input {
  200. flex: 1;
  201. font-size: 28rpx;
  202. color: #333;
  203. &::placeholder {
  204. color: #999;
  205. }
  206. }
  207. // 一级Tab样式 - 调整为在蓝色背景内
  208. .primary-tabs {
  209. display: flex;
  210. padding: 0 20rpx;
  211. margin-bottom: 20rpx;
  212. }
  213. .primary-tab-item {
  214. flex: 1;
  215. text-align: center;
  216. padding: 20rpx 0;
  217. font-size: 32rpx;
  218. color: #000000; /* 白色半透明 */
  219. position: relative;
  220. transition: color 0.3s ease;
  221. &.active {
  222. color: white; /* 激活状态为纯白色 */
  223. font-weight: 600;
  224. &::after {
  225. content: '';
  226. position: absolute;
  227. bottom: 0;
  228. left: 50%;
  229. transform: translateX(-50%);
  230. // transition: transform 0.3s ease;
  231. width: 100rpx;
  232. height: 6rpx;
  233. background-color: white; /* 下划线改为白色 */
  234. border-radius: 3rpx;
  235. }
  236. }
  237. }
  238. // 二级Tab样式 - 自定义实现
  239. .secondary-tabs {
  240. background-color: white;
  241. border-bottom: 1px solid #f0f0f0;
  242. position: relative;
  243. .tab-scroll {
  244. white-space: nowrap;
  245. .tab-list {
  246. display: flex;
  247. // position: relative;
  248. justify-content: space-evenly;
  249. .tab-item {
  250. flex-shrink: 0;
  251. padding: 24rpx 32rpx;
  252. display: flex;
  253. align-items: center;
  254. justify-content: center;
  255. transition: all 0.3s ease;
  256. .tab-text {
  257. font-size: 28rpx;
  258. color: #666666;
  259. font-weight: 500;
  260. transition: color 0.5s ease;
  261. }
  262. &.active {
  263. .tab-text {
  264. color: #007AFF;
  265. font-weight: 600;
  266. }
  267. }
  268. }
  269. }
  270. .tab-line {
  271. position: absolute;
  272. bottom: 10;
  273. height: 6rpx;
  274. background-color: #007AFF;
  275. border-radius: 3rpx;
  276. transition: transform 0.3s ease;
  277. }
  278. }
  279. }
  280. // 活动列表样式
  281. .activity-list {
  282. padding: 20rpx;
  283. }
  284. .activity-item {
  285. background-color: white;
  286. border-radius: 12rpx;
  287. margin-bottom: 30rpx;
  288. padding: 20rpx;
  289. display: flex;
  290. box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
  291. }
  292. .activity-image {
  293. width: 180rpx;
  294. height: 180rpx;
  295. border-radius: 8rpx;
  296. overflow: hidden;
  297. flex-shrink: 0;
  298. margin-right: 20rpx;
  299. }
  300. .image {
  301. width: 100%;
  302. height: 100%;
  303. }
  304. .activity-info {
  305. flex: 1;
  306. display: flex;
  307. flex-direction: column;
  308. justify-content: space-between;
  309. }
  310. .title-row {
  311. display: flex;
  312. align-items: center;
  313. margin-bottom: 10rpx;
  314. }
  315. .activity-tag {
  316. width: 31px;
  317. height: 20px;
  318. background: #218cdd;
  319. border-radius: 3.5px;
  320. margin-right: 7rpx;
  321. display: flex;
  322. align-items: center;
  323. justify-content: center;
  324. font-size: 18rpx;
  325. color: white;
  326. font-weight: 600;
  327. }
  328. .activity-title {
  329. font-size: 28rpx;
  330. font-weight: bold;
  331. color: #333;
  332. line-height: 1.4;
  333. }
  334. .activity-location,
  335. .activity-time,
  336. .activity-participants {
  337. display: flex;
  338. align-items: center;
  339. margin-bottom: 6rpx;
  340. .location-text,
  341. .time-text,
  342. .participants-text {
  343. font-size: 24rpx;
  344. color: #666;
  345. margin-left: 6rpx;
  346. }
  347. }
  348. .activity-action {
  349. display: flex;
  350. align-items: flex-end;
  351. padding-bottom: 10rpx;
  352. }
  353. // 空状态样式
  354. .empty-state {
  355. padding: 100rpx 40rpx;
  356. }
  357. </style>