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

477 lines
12 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 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-detail">
  3. <!-- 轮播图 -->
  4. <view class="banner-container">
  5. <swiper class="banner-swiper" height="450rpx" :indicator-dots="true" :autoplay="true" :interval="3000" :duration="500">
  6. <!-- 如果没有,分割 -->
  7. <swiper-item v-for="(image, index) in imageList" :key="index">
  8. <image class="banner-image" :src="image" mode="aspectFill"></image>
  9. </swiper-item>
  10. </swiper>
  11. </view>
  12. <!-- 活动信息 -->
  13. <view class="activity-info">
  14. <!-- 活动标题和标签 -->
  15. <view class="title-section">
  16. <view class="activity-badge">
  17. <text class="badge-text">{{ activityData.score }}积分</text>
  18. </view>
  19. <text class="activity-title">{{ activityData.title }}</text>
  20. </view>
  21. <!-- 活动详细信息 -->
  22. <view class="info-section">
  23. <view class="info-item">
  24. <uv-icon name="calendar" size="16" color="#666"></uv-icon>
  25. <text class="info-label">活动时间</text>
  26. <text class="info-value">{{ activityData.activityTime }}</text>
  27. </view>
  28. <view class="info-item">
  29. <uv-icon name="clock" size="16" color="#666"></uv-icon>
  30. <text class="info-label">报名时间</text>
  31. <text class="info-value">{{ activityData.startTime }}</text>
  32. </view>
  33. <view class="info-item">
  34. <uv-icon name="account-fill" size="16" color="#666"></uv-icon>
  35. <text class="info-label">联系人</text>
  36. <text class="info-value">{{ activityData.contact }}</text>
  37. </view>
  38. <view class="info-item">
  39. <uv-icon name="phone" size="16" color="#666"></uv-icon>
  40. <text class="info-label">取消规则</text>
  41. <text class="info-value">{{ activityData.rule }}</text>
  42. </view>
  43. <view class="info-item">
  44. <uv-icon name="map-fill" size="16" color="#666"></uv-icon>
  45. <text class="info-label">活动地点</text>
  46. <text class="info-value">{{ activityData.address }}</text>
  47. </view>
  48. </view>
  49. <!-- 活动详情 -->
  50. <view class="detail-section">
  51. <view class="section-title">
  52. <text class="title-text">活动详情</text>
  53. </view>
  54. <view class="detail-content">
  55. <!-- <text class="detail-text"> -->
  56. <rich-text :nodes="activityData.details"></rich-text>
  57. <!-- </text> -->
  58. </view>
  59. </view>
  60. <!-- 活动图集 -->
  61. <view class="gallery-section">
  62. <view class="section-title">
  63. <text class="title-text">活动图集</text>
  64. </view>
  65. <view class="gallery-grid">
  66. <image
  67. v-for="(image, index) in atlas"
  68. :key="index"
  69. class="gallery-image"
  70. :src="image"
  71. mode="aspectFill"
  72. @click="previewImage(image, atlas)"
  73. ></image>
  74. <!-- <uv-album :urls="activityData.gallery"></uv-album> -->
  75. </view>
  76. </view>
  77. </view>
  78. <!-- 固定底部操作栏 -->
  79. <view class="bottom-action">
  80. <view class="action-left">
  81. <button open-type="share">
  82. <view class="action-item">
  83. <uv-icon name="share" size="24" color="#000"></uv-icon>
  84. <text class="action-text">分享</text>
  85. </view>
  86. </button>
  87. <view class="action-item" @click="collectActivity">
  88. <uv-icon name="heart-fill" class="collection-icon" size="24" :color="activityData.isCollection === 1 ? '#ff4757' : '#999'"></uv-icon>
  89. <text class="action-text">收藏</text>
  90. </view>
  91. <view class="action-item">
  92. <text class="participants-count">
  93. <text :style="{'color': activityData.numActivity >= activityData.numLimit ? '#999' : '#1488DB'}">{{ activityData.numActivity }}</text>
  94. /{{ activityData.numLimit }}</text>
  95. <text class="action-text">已报名</text>
  96. </view>
  97. </view>
  98. <view class="action-right">
  99. <uv-button
  100. v-if="activityData.status === '1'"
  101. type="primary"
  102. size="normal"
  103. text="已结束"
  104. shape="circle"
  105. @click="signUpActivity"
  106. :disabled="true"
  107. ></uv-button>
  108. <uv-button
  109. v-else-if="activityData.isApply === 1"
  110. type="primary"
  111. size="normal"
  112. text="您已报名"
  113. shape="circle"
  114. @click="signUpActivity"
  115. :disabled="true"
  116. ></uv-button>
  117. <uv-button
  118. v-else
  119. type="primary"
  120. size="normal"
  121. text="我要报名"
  122. shape="circle"
  123. @click="signUpActivity"
  124. :disabled="activityData.numActivity >= activityData.numLimit "
  125. ></uv-button>
  126. </view>
  127. </view>
  128. <SignUpForm
  129. ref="signUpFormRef"
  130. @close="onSignUpFormClose"
  131. @submit="onSignUpFormSubmit"
  132. />
  133. <GlobalPopup ref="globalPopupRef"></GlobalPopup>
  134. </view>
  135. <!-- 报名表单弹窗 -->
  136. </template>
  137. <script>
  138. import SignUpForm from '@/subPages/index/components/SignUpForm.vue'
  139. export default {
  140. components: {
  141. SignUpForm
  142. },
  143. data() {
  144. return {
  145. // isCollected: false,
  146. showSignUpForm: false,
  147. activityData: {
  148. title: '关爱自闭症儿童活动',
  149. duration: '30积分',
  150. time: '2025-06-12 14:30',
  151. registrationTime: '2025-06-01 14:30——2025-09-01 14:30',
  152. contact: '柳老师 (13256484512)',
  153. cancelRule: '报名随时可取消',
  154. location: '长沙市雨花区时代阳光大夏国际大厅2145',
  155. registeredCount: 9,
  156. maxCount: 30,
  157. details: [
  158. '身体健康,热爱志愿服务工作,富有责任感和奉献精神',
  159. '遵纪守法,思想上进,作风正派,服从安排',
  160. '年龄在60岁以下,具备广告宣传理能力'
  161. ],
  162. gallery: [
  163. '/static/bannerImage.png',
  164. '/static/bannerImage.png',
  165. '/static/bannerImage.png',
  166. '/static/bannerImage.png'
  167. ]
  168. },
  169. activityId: null
  170. }
  171. },
  172. computed:{
  173. imageList(){
  174. if (this.activityData.image) {
  175. return this.activityData.image.split(',')
  176. }
  177. return []
  178. },
  179. atlas(){
  180. if (this.activityData.atlas) {
  181. return this.activityData.atlas.split(',')
  182. }
  183. return []
  184. }
  185. },
  186. onLoad(options) {
  187. if (options.id) {
  188. this.activityId = options.id
  189. this.loadActivityDetail(options.id)
  190. }else {
  191. uni.showToast({
  192. title: '没有给活动id',
  193. icon: 'none'
  194. })
  195. }
  196. },
  197. methods: {
  198. async loadActivityDetail(id) {
  199. let params = {}
  200. if (uni.getStorageSync('token')) {
  201. params.token = uni.getStorageSync('token')
  202. }
  203. // 根据ID加载活动详情
  204. const res = await this.$api.activity.queryActivityById({
  205. activityId: id,
  206. ...params
  207. })
  208. this.activityData = res.result
  209. },
  210. previewImage(current, urls) {
  211. uni.previewImage({
  212. current: current,
  213. urls: urls
  214. })
  215. },
  216. async collectActivity() {
  217. const res = await this.$api.activity.collectionActivity({
  218. activityId: this.activityId
  219. })
  220. await this.loadActivityDetail(this.activityId)
  221. uni.showToast({
  222. title: `${res.message}`,
  223. icon: 'none'
  224. })
  225. },
  226. signUpActivity() {
  227. if (this.activityData.numActivity >= this.activityData.numLimit) {
  228. uni.showToast({
  229. title: '报名人数已满',
  230. icon: 'none'
  231. })
  232. return
  233. }
  234. this.$refs.signUpFormRef.open()
  235. },
  236. onSignUpFormClose() {
  237. this.$refs.signUpFormRef.close()
  238. },
  239. async onSignUpFormSubmit(formData) {
  240. console.log('报名表单数据:', formData)
  241. // 这里可以调用API提交报名数据
  242. const res = await this.$api.activity.applyActivity({
  243. activityId: this.activityId,
  244. ...formData
  245. })
  246. if (res.code === 200) {
  247. this.$refs.globalPopupRef.open({
  248. content: '恭喜您报名成功',
  249. subContent: '别忘了准时参与活动哦!',
  250. titleType: 'submit',
  251. popupType: 'success',
  252. closefn: () => {
  253. setTimeout(() => {
  254. uni.navigateBack()
  255. }, 300);
  256. }
  257. })
  258. // 更新状态
  259. // this.loadActivityDetail(this.activityId)
  260. }else {
  261. uni.showToast({
  262. title: `${res.message}`,
  263. icon: 'none'
  264. })
  265. }
  266. }
  267. },
  268. async onPullDownRefresh() {
  269. await this.loadActivityDetail(this.activityId)
  270. uni.stopPullDownRefresh()
  271. }
  272. }
  273. </script>
  274. <style lang="scss" scoped>
  275. .activity-detail {
  276. min-height: 100vh;
  277. background: #f8f8f8;
  278. padding-bottom: 120rpx;
  279. .banner-container {
  280. width: 100%;
  281. height: 450rpx;
  282. .banner-swiper {
  283. width: 100%;
  284. height: 100%;
  285. .banner-image {
  286. width: 100%;
  287. height: 100%;
  288. }
  289. }
  290. }
  291. .activity-info {
  292. background: #ffffff;
  293. // border: 1rpx dashed #F3F7F8;
  294. margin: 20rpx;
  295. border-radius: 16rpx;
  296. padding: 30rpx;
  297. .title-section {
  298. display: flex;
  299. align-items: center;
  300. margin-bottom: 30rpx;
  301. .activity-badge {
  302. background: #218CDD;
  303. border-radius: 8rpx;
  304. height: 40rpx;
  305. padding: 0 5rpx 10rpx;
  306. line-height: 40rpx;
  307. text-align: center;
  308. margin-right: 16rpx;
  309. .badge-text {
  310. color: #ffffff;
  311. font-size: 24rpx;
  312. font-weight: 500;
  313. }
  314. }
  315. .activity-title {
  316. font-size: 36rpx;
  317. font-weight: bold;
  318. color: #333333;
  319. flex: 1;
  320. }
  321. }
  322. .info-section {
  323. background: #F3F7F8;
  324. margin-bottom: 40rpx;
  325. // 虚线属性
  326. border: 2rpx dashed #F3F7F8;
  327. .info-item {
  328. display: flex;
  329. align-items: center;
  330. margin-bottom: 20rpx;
  331. &:last-child {
  332. margin-bottom: 0;
  333. }
  334. .info-label {
  335. font-size: 28rpx;
  336. color: #999999;
  337. margin-left: 12rpx;
  338. margin-right: 8rpx;
  339. }
  340. .info-value {
  341. font-size: 28rpx;
  342. color: #999999;
  343. flex: 1;
  344. }
  345. }
  346. }
  347. .detail-section {
  348. margin-bottom: 40rpx;
  349. .section-title {
  350. margin-bottom: 20rpx;
  351. .title-text {
  352. font-size: 32rpx;
  353. font-weight: bold;
  354. color: #333333;
  355. }
  356. }
  357. .detail-content {
  358. .detail-text {
  359. display: block;
  360. font-size: 28rpx;
  361. color: #666666;
  362. line-height: 1.6;
  363. margin-bottom: 16rpx;
  364. &:last-child {
  365. margin-bottom: 0;
  366. }
  367. }
  368. }
  369. }
  370. .gallery-section {
  371. .section-title {
  372. margin-bottom: 20rpx;
  373. .title-text {
  374. font-size: 32rpx;
  375. font-weight: bold;
  376. color: #333333;
  377. }
  378. }
  379. .gallery-grid {
  380. display: grid;
  381. grid-template-columns: repeat(2, 1fr);
  382. gap: 16rpx;
  383. .gallery-image {
  384. width: 100%;
  385. height: 200rpx;
  386. border-radius: 12rpx;
  387. }
  388. }
  389. }
  390. }
  391. .bottom-action {
  392. position: fixed;
  393. bottom: 0;
  394. left: 0;
  395. right: 0;
  396. background: #ffffff;
  397. padding: 20rpx 30rpx;
  398. border-top: 1rpx solid #eeeeee;
  399. display: flex;
  400. align-items: center;
  401. justify-content: space-between;
  402. z-index: 100;
  403. .action-left {
  404. display: flex;
  405. align-items: center;
  406. gap: 100rpx;
  407. .action-item {
  408. display: flex;
  409. flex-direction: column;
  410. align-items: center;
  411. gap: 8rpx;
  412. .action-text {
  413. font-size: 22rpx;
  414. color: #000;
  415. }
  416. .participants-count {
  417. font-size: 24rpx;
  418. color: #333333;
  419. // font-weight: bold;
  420. }
  421. }
  422. }
  423. .action-right {
  424. flex-shrink: 0;
  425. }
  426. }
  427. }
  428. </style>