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

401 lines
10 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
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-detail">
  3. <!-- 轮播图 -->
  4. <view class="banner-container">
  5. <swiper class="banner-swiper" height="450rpx" :indicator-dots="true" :autoplay="true" :interval="3000" :duration="500">
  6. <swiper-item v-for="(image, index) in activityData.image && activityData.image.split(',')" :key="index">
  7. <image class="banner-image" :src="image" mode="aspectFill"></image>
  8. </swiper-item>
  9. </swiper>
  10. </view>
  11. <!-- 活动信息 -->
  12. <view class="activity-info">
  13. <!-- 活动标题和标签 -->
  14. <view class="title-section">
  15. <view class="activity-badge">
  16. <text class="badge-text">{{ activityData.score }}积分</text>
  17. </view>
  18. <text class="activity-title">{{ activityData.title }}</text>
  19. </view>
  20. <!-- 活动详细信息 -->
  21. <view class="info-section">
  22. <view class="info-item">
  23. <uv-icon name="calendar" size="16" color="#666"></uv-icon>
  24. <text class="info-label">活动时间</text>
  25. <text class="info-value">{{ activityData.activityTime }}</text>
  26. </view>
  27. <view class="info-item">
  28. <uv-icon name="clock" size="16" color="#666"></uv-icon>
  29. <text class="info-label">报名时间</text>
  30. <text class="info-value">{{ activityData.startTime }}</text>
  31. </view>
  32. <view class="info-item">
  33. <uv-icon name="account-fill" size="16" color="#666"></uv-icon>
  34. <text class="info-label">联系人</text>
  35. <text class="info-value">{{ activityData.contact }}</text>
  36. </view>
  37. <view class="info-item">
  38. <uv-icon name="phone" size="16" color="#666"></uv-icon>
  39. <text class="info-label">取消规则</text>
  40. <text class="info-value">{{ activityData.rule }}</text>
  41. </view>
  42. <view class="info-item">
  43. <uv-icon name="map-fill" size="16" color="#666"></uv-icon>
  44. <text class="info-label">活动地点</text>
  45. <text class="info-value">{{ activityData.address }}</text>
  46. </view>
  47. </view>
  48. <!-- 活动详情 -->
  49. <view class="detail-section">
  50. <view class="section-title">
  51. <text class="title-text">活动详情</text>
  52. </view>
  53. <view class="detail-content">
  54. <rich-text :nodes="activityData.details"></rich-text>
  55. </view>
  56. </view>
  57. <!-- 活动图集 -->
  58. <view class="gallery-section">
  59. <view class="section-title">
  60. <text class="title-text">活动图集</text>
  61. </view>
  62. <view class="gallery-grid">
  63. <image
  64. v-for="(image, index) in activityData.atlas && activityData.atlas.split(',')"
  65. :key="index"
  66. class="gallery-image"
  67. :src="image"
  68. mode="aspectFill"
  69. @click="previewImage(image, activityData.atlas.split(','))"
  70. ></image>
  71. </view>
  72. </view>
  73. </view>
  74. <!-- 固定底部操作栏 -->
  75. <view class="bottom-action">
  76. <view class="action-left">
  77. <button class="action-item" open-type="share" >
  78. <uv-icon name="share" size="24" color="#000"></uv-icon>
  79. <text class="action-text">分享</text>
  80. </button>
  81. <view class="action-item" @click="collectActivity">
  82. <uv-icon name="heart-fill" size="24" :color="activityData.isCollection === 1 ? '#ff4757' : '#999'"></uv-icon>
  83. <text class="action-text">收藏</text>
  84. </view>
  85. <view class="action-item">
  86. <text class="participants-count">
  87. <text :style="{'color': activityData.numActivity >= activityData.numLimit ? '#999' : '#1488DB'}">{{ activityData.numActivity }}</text>
  88. /{{ activityData.numLimit }}</text>
  89. <text class="action-text">已报名</text>
  90. </view>
  91. </view>
  92. <view class="action-right">
  93. <!-- 未签到状态 -->
  94. <uv-button
  95. v-if="activityData.status === '0'"
  96. type="primary"
  97. size="normal"
  98. text="扫码签到"
  99. shape="circle"
  100. @click="scanQRCode"
  101. ></uv-button>
  102. <!-- 已签到状态 -->
  103. <uv-button
  104. v-else-if="activityData.status === '1'"
  105. type="success"
  106. size="normal"
  107. text="已签到"
  108. shape="circle"
  109. :disabled="true"
  110. ></uv-button>
  111. <!-- 系统取消状态 -->
  112. <uv-button
  113. v-else-if="activityData.status === '2'"
  114. type="error"
  115. size="normal"
  116. text="系统取消"
  117. shape="circle"
  118. :disabled="true"
  119. ></uv-button>
  120. </view>
  121. </view>
  122. </view>
  123. </template>
  124. <script>
  125. export default {
  126. data() {
  127. return {
  128. // status: 'unsigned', // unsigned: 未签到, signed: 已签到, cancelled: 系统取消
  129. activityData: {
  130. },
  131. activityId: null,
  132. }
  133. },
  134. onLoad(options) {
  135. if (options.id) {
  136. this.activityId = options.id
  137. this.loadActivityDetail(options.id)
  138. } else {
  139. uni.showToast({
  140. title: '没有给活动id',
  141. icon: 'none'
  142. })
  143. }
  144. },
  145. methods: {
  146. // 自定义分享内容
  147. mixinCustomShare() {
  148. return {
  149. desc: '',
  150. title: `邀请您参加${this.activityData.title || ''}`,
  151. imageUrl: this.activityData.image.split(',')[0],
  152. path: '/subPages/index/activityDetail?id=' + this.activityId
  153. }
  154. },
  155. async loadActivityDetail(id) {
  156. // 根据ID加载活动详情
  157. const res = await this.$api.activity.queryActivityById({
  158. activityId: id,
  159. token: uni.getStorageSync('token')
  160. })
  161. this.activityData = res.result
  162. },
  163. previewImage(current, urls) {
  164. uni.previewImage({
  165. current: current,
  166. urls: urls
  167. })
  168. },
  169. async collectActivity() {
  170. const res = await this.$api.activity.collectionActivity({
  171. activityId: this.activityId,
  172. })
  173. await this.loadActivityDetail(this.activityId)
  174. uni.showToast({
  175. title: `${res.message}`,
  176. icon: 'none'
  177. })
  178. },
  179. async scanQRCode() {
  180. // 扫码签到功能
  181. uni.scanCode({
  182. success: async (res) => {
  183. const { activityId } = JSON.parse(res.result)
  184. const scanRes = await this.$api.activity.signActivity({ activityId })
  185. if (scanRes.code === 200) {
  186. this.status = 'signed'
  187. uni.navigateTo({
  188. url: `/subPages/my/signupSuccess?score=${this.activityData.score}`
  189. })
  190. } else {
  191. uni.showToast({
  192. title: scanRes.message,
  193. icon: 'none'
  194. })
  195. }
  196. },
  197. fail: (err) => {
  198. console.log('扫码失败:', err)
  199. uni.showToast({
  200. title: '扫码失败',
  201. icon: 'none'
  202. })
  203. }
  204. })
  205. }
  206. }
  207. }
  208. </script>
  209. <style lang="scss" scoped>
  210. .activity-detail {
  211. min-height: 100vh;
  212. background: #f8f8f8;
  213. padding-bottom: 120rpx;
  214. .banner-container {
  215. width: 100%;
  216. height: 450rpx;
  217. .banner-swiper {
  218. width: 100%;
  219. height: 100%;
  220. .banner-image {
  221. width: 100%;
  222. height: 100%;
  223. }
  224. }
  225. }
  226. .activity-info {
  227. background: #ffffff;
  228. margin: 20rpx;
  229. border-radius: 16rpx;
  230. padding: 30rpx;
  231. .title-section {
  232. display: flex;
  233. align-items: center;
  234. margin-bottom: 30rpx;
  235. .activity-badge {
  236. background: #218CDD;
  237. border-radius: 8rpx;
  238. padding: 4rpx 10rpx;
  239. margin-right: 16rpx;
  240. .badge-text {
  241. color: #ffffff;
  242. font-size: 24rpx;
  243. font-weight: 500;
  244. }
  245. }
  246. .activity-title {
  247. font-size: 36rpx;
  248. font-weight: bold;
  249. color: #333333;
  250. flex: 1;
  251. }
  252. }
  253. .info-section {
  254. background: #F3F7F8;
  255. margin-bottom: 40rpx;
  256. border: 2rpx dashed #F3F7F8;
  257. .info-item {
  258. display: flex;
  259. align-items: center;
  260. margin-bottom: 20rpx;
  261. &:last-child {
  262. margin-bottom: 0;
  263. }
  264. .info-label {
  265. font-size: 28rpx;
  266. color: #999999;
  267. margin-left: 12rpx;
  268. margin-right: 8rpx;
  269. }
  270. .info-value {
  271. font-size: 28rpx;
  272. color: #999999;
  273. flex: 1;
  274. }
  275. }
  276. }
  277. .detail-section {
  278. margin-bottom: 40rpx;
  279. .section-title {
  280. margin-bottom: 20rpx;
  281. .title-text {
  282. font-size: 32rpx;
  283. font-weight: bold;
  284. color: #333333;
  285. }
  286. }
  287. .detail-content {
  288. .detail-text {
  289. display: block;
  290. font-size: 28rpx;
  291. color: #666666;
  292. line-height: 1.6;
  293. margin-bottom: 16rpx;
  294. &:last-child {
  295. margin-bottom: 0;
  296. }
  297. }
  298. }
  299. }
  300. .gallery-section {
  301. .section-title {
  302. margin-bottom: 20rpx;
  303. .title-text {
  304. font-size: 32rpx;
  305. font-weight: bold;
  306. color: #333333;
  307. }
  308. }
  309. .gallery-grid {
  310. display: grid;
  311. grid-template-columns: repeat(2, 1fr);
  312. gap: 16rpx;
  313. .gallery-image {
  314. width: 100%;
  315. height: 200rpx;
  316. border-radius: 12rpx;
  317. }
  318. }
  319. }
  320. }
  321. .bottom-action {
  322. position: fixed;
  323. bottom: 0;
  324. left: 0;
  325. right: 0;
  326. background: #ffffff;
  327. padding: 20rpx 30rpx;
  328. border-top: 1rpx solid #eeeeee;
  329. display: flex;
  330. align-items: center;
  331. justify-content: space-between;
  332. z-index: 100;
  333. .action-left {
  334. display: flex;
  335. align-items: center;
  336. gap: 100rpx;
  337. .action-item {
  338. display: flex;
  339. flex-direction: column;
  340. align-items: center;
  341. gap: 8rpx;
  342. .action-text {
  343. font-size: 22rpx;
  344. color: #000;
  345. }
  346. .participants-count {
  347. font-size: 24rpx;
  348. color: #333333;
  349. }
  350. }
  351. }
  352. .action-right {
  353. flex-shrink: 0;
  354. }
  355. }
  356. }
  357. </style>