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.

260 lines
5.2 KiB

1 week ago
<template>
<view class="pt">
<view class="pt-head">
<view>
<text class="pt-kicker">SUPPLY MAP</text>
<text class="pt-title">星球补给地图</text>
</view>
<text class="pt-sub">完成校园行动点亮补给点</text>
</view>
<view class="pt-list">
<view class="pt-item" v-for="(t, i) in taskCards" :key="i" :class="t.cardClass">
<view class="pt-route-dot"></view>
<view class="pt-item-icon">{{t.icon}}</view>
<view class="pt-item-mid">
<view class="pt-item-name">{{t.name}}</view>
<view class="pt-item-desc">{{t.description}}</view>
</view>
<view class="pt-item-btn"
:class="{auto: t.actionType==='auto', done: t.actionType==='claim' && !t.canClaim}"
@tap="onClick(t)">
<text>+{{t.rewardTickets}}</text>
4 hours ago
<text>{{btnText(t)}} <text v-if="t.actionType==='auto'" class="btn-arrow"></text></text>
1 week ago
</view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
tasks: {
type: Array,
default: () => []
}
},
computed: {
taskCards() {
return (this.tasks || []).map((item, index) => {
return Object.assign({}, item, {
icon: this.taskEmoji(item.code),
cardClass: this.taskClass(item.code) + (index % 2 === 1 ? ' offset' : '')
})
})
}
},
methods: {
taskEmoji(code) {
const map = {
waimai: '食',
group: '团',
invite: '邀',
sign: '签'
}
return map[code] || '券'
},
taskClass(code) {
const map = {
waimai: 'type-food',
group: 'type-group',
invite: 'type-invite',
sign: 'type-sign'
}
return map[code] || 'type-other'
},
btnText(t) {
if (t.actionType === 'auto') return '去完成'
if (t.code === 'sign') return t.canClaim ? '签到' : '已领取'
return t.canClaim ? '领取' : '已领取'
},
onClick(t) {
if (t.actionType === 'auto') {
this.$emit('go', t)
return
}
if (!t.canClaim) return
this.$emit('claim', t)
}
}
}
</script>
<style lang="scss" scoped>
.pt {
position: relative;
4 hours ago
margin-top: 20rpx;
1 week ago
}
.pt-head {
4 hours ago
display: none;
1 week ago
}
.pt-kicker {
display: block;
color: #35D6A6;
font-size: 18rpx;
font-weight: 900;
letter-spacing: 3rpx;
margin-bottom: 4rpx;
}
.pt-title {
display: block;
color: #12342F;
font-size: 36rpx;
font-weight: 900;
}
.pt-sub {
color: #7E9691;
font-size: 22rpx;
font-weight: 600;
}
.pt-list {
position: relative;
display: flex;
4 hours ago
flex-direction: column;
gap: 16rpx;
1 week ago
}
.pt-list:before {
4 hours ago
display: none;
1 week ago
}
.pt-item {
position: relative;
4 hours ago
width: 100%;
min-height: 148rpx;
1 week ago
box-sizing: border-box;
4 hours ago
padding: 22rpx;
border-radius: 30rpx;
1 week ago
background: linear-gradient(145deg, rgba(255,255,255,0.82), rgba(244,255,249,0.64));
border: 2rpx solid rgba(255,255,255,0.9);
box-shadow: 0 18rpx 42rpx rgba(53,214,166,0.1);
overflow: hidden;
4 hours ago
display: flex;
align-items: center;
gap: 18rpx;
1 week ago
}
.pt-item.offset {
4 hours ago
margin-top: 0;
transform: none;
1 week ago
}
.pt-item:not(.offset) {
4 hours ago
transform: none;
1 week ago
}
.pt-item:before {
content: '';
position: absolute;
right: -36rpx;
top: -40rpx;
width: 150rpx;
height: 150rpx;
border-radius: 50%;
background: rgba(53,214,166,0.12);
}
.pt-item.type-food:before { background: rgba(255,184,77,0.18); }
.pt-item.type-group:before { background: rgba(79,183,255,0.18); }
.pt-item.type-invite:before { background: rgba(143,124,255,0.16); }
.pt-item.type-sign:before { background: rgba(53,214,166,0.16); }
.pt-route-dot {
position: absolute;
4 hours ago
left: 18rpx;
top: 18rpx;
1 week ago
width: 20rpx;
height: 20rpx;
border-radius: 50%;
background: #35D6A6;
border: 5rpx solid rgba(255,255,255,0.9);
box-shadow: 0 6rpx 16rpx rgba(53,214,166,0.22);
}
.pt-item-icon {
position: relative;
4 hours ago
width: 76rpx;
height: 76rpx;
border-radius: 26rpx;
1 week ago
background: linear-gradient(145deg, #E5FFF1, #EAF8FF);
display: flex;
align-items: center;
justify-content: center;
color: #12342F;
font-size: 30rpx;
font-weight: 900;
box-shadow: inset 0 0 0 1rpx rgba(255,255,255,0.86);
4 hours ago
flex-shrink: 0;
1 week ago
}
.pt-item-mid {
position: relative;
4 hours ago
margin-top: 0;
flex: 1;
min-width: 0;
1 week ago
}
.pt-item-name {
color: #12342F;
font-size: 28rpx;
font-weight: 900;
}
.pt-item-desc {
color: #7E9691;
font-size: 22rpx;
margin-top: 8rpx;
line-height: 32rpx;
4 hours ago
min-height: 0;
1 week ago
}
.pt-item-btn {
position: relative;
4 hours ago
margin-top: 0;
width: 142rpx;
height: 82rpx;
padding: 0;
border-radius: 28rpx;
1 week ago
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
display: flex;
align-items: center;
4 hours ago
justify-content: center;
flex-direction: column;
font-size: 22rpx;
1 week ago
font-weight: 800;
box-shadow: 0 12rpx 26rpx rgba(53,214,166,0.22);
4 hours ago
flex-shrink: 0;
border: 2rpx solid rgba(255,255,255,0.82);
transition: transform .16s ease, opacity .16s ease;
1 week ago
}
.pt-item-btn.auto {
4 hours ago
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
box-shadow: 0 12rpx 28rpx rgba(53,214,166,0.24);
1 week ago
}
.pt-item-btn.done {
background: rgba(18,52,47,0.06);
color: #9AA9A5;
box-shadow: none;
}
4 hours ago
.pt-item-btn:active {
transform: scale(.96);
opacity: .88;
}
.btn-arrow {
margin-left: 4rpx;
font-size: 26rpx;
font-weight: 900;
}
1 week ago
</style>