234 lines
5.8 KiB
Vue
234 lines
5.8 KiB
Vue
<template>
|
|
<view class="page">
|
|
<scroll-view class="content" scroll-y>
|
|
<view v-if="loading" class="loading-wrap"><view class="spinner"></view></view>
|
|
<view v-else-if="filteredActivities.length > 0" class="activity-grid">
|
|
<view class="activity-item" v-for="a in filteredActivities" :key="a.id" @tap="onActivityTap(a)">
|
|
<view class="thumb-box">
|
|
<image class="thumb" :src="a.image" mode="aspectFill" />
|
|
<view class="tag-hot">HOT</view>
|
|
</view>
|
|
<view class="info">
|
|
<view class="name">{{ a.title }}</view>
|
|
<view class="bottom-row">
|
|
<text class="price-text">{{ a.category_name }} · {{ a.subtitle }}</text>
|
|
<view class="btn-go">GO</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view v-else class="empty">
|
|
<image class="empty-img" src="/static/empty.png" mode="widthFix" />
|
|
<text class="empty-text">暂无{{ title }}活动</text>
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed } from 'vue'
|
|
import { onLoad } from '@dcloudio/uni-app'
|
|
import { request, authRequest } from '@/utils/request.js'
|
|
|
|
const title = ref('')
|
|
const categoryTarget = ref('')
|
|
const activities = ref([])
|
|
const loading = ref(false)
|
|
|
|
const filteredActivities = computed(() => {
|
|
if (!categoryTarget.value) return activities.value
|
|
const target = categoryTarget.value.trim()
|
|
return activities.value.filter(a => {
|
|
const cat = (a.category_name || '').trim()
|
|
return cat === target || cat.includes(target)
|
|
})
|
|
})
|
|
|
|
function apiGet(url) {
|
|
const token = uni.getStorageSync('token')
|
|
const fn = token ? authRequest : request
|
|
return fn({ url })
|
|
}
|
|
|
|
function cleanUrl(u) {
|
|
const s = String(u || '').trim()
|
|
const m = s.match(/https?:\/\/[^\s'"`]+/)
|
|
if (m && m[0]) return m[0]
|
|
return s.replace(/[`'\"]/g, '').trim()
|
|
}
|
|
|
|
function buildSubtitle(i) {
|
|
const base = i.subTitle ?? i.sub_title ?? i.subtitle ?? i.desc ?? i.description ?? ''
|
|
if (base) return base
|
|
const price = (i.price_draw !== undefined && i.price_draw !== null) ? `¥${(Number(i.price_draw || 0) / 100).toFixed(2)}` : ''
|
|
return price
|
|
}
|
|
|
|
async function loadData() {
|
|
loading.value = true
|
|
try {
|
|
const res = await apiGet('/api/app/activities')
|
|
let list = []
|
|
if (Array.isArray(res)) list = res
|
|
else if (res && (Array.isArray(res.list) || Array.isArray(res.data))) list = res.list || res.data
|
|
|
|
activities.value = list.map((i, idx) => ({
|
|
id: i.id ?? String(idx),
|
|
image: cleanUrl(i.image ?? i.banner ?? i.coverUrl ?? i.cover_url ?? i.img ?? i.pic ?? ''),
|
|
title: (i.title ?? i.name ?? '').replace(/无限赏|一番赏|对对碰|爬塔/g, '').trim(),
|
|
subtitle: buildSubtitle(i),
|
|
category_name: i.category_name ?? i.categoryName ?? '',
|
|
link: cleanUrl(i.linkUrl ?? i.link_url ?? i.link ?? i.url ?? '')
|
|
})).filter(i => i.image || i.title)
|
|
|
|
} catch (e) {
|
|
activities.value = []
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
function onActivityTap(a) {
|
|
const name = (a.category_name || '').trim()
|
|
const id = a.id
|
|
let path = ''
|
|
|
|
// Navigate to DETAIL, not list
|
|
if (name.includes('一番赏')) path = '/pages/activity/yifanshang/index'
|
|
else if (name.includes('无限赏')) path = '/pages/activity/wuxianshang/index'
|
|
else if (name.includes('对对碰')) path = '/pages/activity/duiduipeng/index'
|
|
else if (name.includes('爬塔')) path = '/pages/activity/pata/index'
|
|
|
|
if (path && id) {
|
|
uni.navigateTo({ url: `${path}?id=${id}` })
|
|
return
|
|
}
|
|
if (a.link && /^\/.+/.test(a.link)) {
|
|
uni.navigateTo({ url: a.link })
|
|
}
|
|
}
|
|
|
|
onLoad((opts) => {
|
|
if (opts && opts.category) {
|
|
categoryTarget.value = decodeURIComponent(opts.category)
|
|
title.value = categoryTarget.value
|
|
uni.setNavigationBarTitle({ title: categoryTarget.value })
|
|
}
|
|
loadData()
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.page {
|
|
min-height: 100vh;
|
|
background: #F8F8F8;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.content {
|
|
flex: 1;
|
|
padding: 24rpx;
|
|
}
|
|
.activity-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(2, 1fr);
|
|
gap: 20rpx;
|
|
}
|
|
.activity-item {
|
|
background: #fff;
|
|
border-radius: 20rpx;
|
|
overflow: hidden;
|
|
box-shadow: 0 8rpx 20rpx rgba(0,0,0,0.06);
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.thumb-box {
|
|
position: relative;
|
|
width: 100%;
|
|
padding-top: 100%; /* 1:1 Aspect Ratio */
|
|
height: 0;
|
|
background: #f0f0f0;
|
|
}
|
|
.thumb {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
.tag-hot {
|
|
position: absolute;
|
|
top: 12rpx; left: 12rpx;
|
|
background: #333;
|
|
color: #FFD700;
|
|
font-size: 20rpx;
|
|
padding: 4rpx 12rpx;
|
|
border-radius: 8rpx;
|
|
font-weight: 800;
|
|
}
|
|
.info {
|
|
padding: 20rpx 16rpx;
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex: 1;
|
|
justify-content: space-between;
|
|
}
|
|
.name {
|
|
font-size: 28rpx;
|
|
font-weight: 700;
|
|
color: #1A1A1A;
|
|
margin-bottom: 20rpx;
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
height: 76rpx;
|
|
line-height: 1.35;
|
|
}
|
|
.bottom-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
.price-text {
|
|
font-size: 24rpx;
|
|
color: #FF4D4F;
|
|
font-weight: 700;
|
|
}
|
|
.btn-go {
|
|
background: #1A1A1A;
|
|
color: #FFD700;
|
|
font-size: 24rpx;
|
|
font-weight: 900;
|
|
padding: 8rpx 24rpx;
|
|
border-radius: 999rpx;
|
|
}
|
|
|
|
.loading-wrap {
|
|
display: flex; justify-content: center; padding: 100rpx;
|
|
}
|
|
.spinner {
|
|
width: 48rpx; height: 48rpx;
|
|
border: 4rpx solid #ddd; border-top-color: #FF9F43;
|
|
border-radius: 50%;
|
|
animation: spin 0.8s linear infinite;
|
|
}
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
|
|
.empty {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding-top: 200rpx;
|
|
}
|
|
.empty-img {
|
|
width: 240rpx;
|
|
margin-bottom: 24rpx;
|
|
opacity: 0.4;
|
|
}
|
|
.empty-text {
|
|
color: #999;
|
|
font-size: 28rpx;
|
|
}
|
|
</style>
|