wangfukang 3 weeks ago
parent
commit
eb0bea054c
  1. 2
      package1/buyFood/buyFood.vue
  2. 284
      package1/components/delivery-time-op/delivery-time-op.vue
  3. 185
      package1/components/planet/buff-shop.vue
  4. 291
      package1/components/planet/hunt-modal.vue
  5. 179
      package1/components/planet/planet-box.vue
  6. 690
      package1/components/planet/planet-header.vue
  7. 237
      package1/components/planet/planet-me.vue
  8. 70
      package1/components/planet/planet-news.vue
  9. 218
      package1/components/planet/planet-rank.vue
  10. 250
      package1/components/planet/planet-tasks.vue
  11. 251
      package1/components/planet/rank-item.vue
  12. 346
      package1/planet/drawResult.vue
  13. 717
      package1/planet/index.vue
  14. 385
      package1/planet/ticketLog.vue
  15. 2
      package1/runErrand/runErrand.vue

2
package1/buyFood/buyFood.vue

@ -522,7 +522,7 @@
</template>
<script>
import deliveryTimeOp from '@/components/delivery-time-op/delivery-time-op.vue'
import deliveryTimeOp from '@/package1/components/delivery-time-op/delivery-time-op.vue'
import addressList from '@/package1/address/addressList.vue'
export default {
data() {

284
package1/components/delivery-time-op/delivery-time-op.vue

@ -0,0 +1,284 @@
<!-- 弹窗组件小龙开发所有 -->
<template>
<view>
<!-- 模态框 -->
<view @tap="Modal" :class="{ mask: model }"></view>
<!-- 弹窗主体 -->
<view
:style="{ height: barHidth + 'rpx' }"
class="active"
:class="{ add: model }"
>
<view class="title-model">{{ title }} <text @tap="close">x</text></view>
<view class="cont" :style="{ height: barHidth - 80 + 'rpx' }">
<!-- -->
<scroll-view class="day" :scroll-y="true">
<view
:class="index === isIndex ? 'active_copy' : ''"
v-for="(item, index) in content"
:key="item.id"
@tap="dataCallback(index, item)"
>{{ item.timezh }}</view
>
</scroll-view>
<!-- -->
<scroll-view class="content-model" :scroll-y="true" :scroll-top="scrollTop">
<view
class="appoint"
:class="index === Indexes ? 'longActive' : ''"
@tap="timeCallback(index, item)"
v-for="(item, index) in Days"
:key="index"
>{{ item.timestr
}}<text :class="index === Indexes ? 'cuIcon-check' : ''"></text
></view>
</scroll-view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
//
title: {
type: String,
default: "请传入title",
},
//
content: {
type: Array,
//
default: () => [
{
timezh: "今天 (周三)",
timeformatter: "8-10",
id: 108,
timelist: [
{
timestr: "立即送达",
},
{
timestr: "15:35",
},
{
timestr: "16:05",
},
],
},
],
},
//
barHidth: {
type: Number,
default: 400,
},
//
dodge: {
type: Boolean,
default: false,
},
},
data() {
return {
scrollTop: 0,
isIndex: 0,
Indexes: 0,
Days: [],
model: false,
};
},
//
watch: {
content: {
immediate: true,
handler(newValue, oldValue) {
this.Days = this.content[0].timelist;
},
},
},
methods: {
//
close() {
this.model = false;
},
//
open() {
this.model = true;
},
//
Modal() {
if (this.dodge) {
this.close();
}
},
//
gotop() {
this.scrollTop = 1;
this.$nextTick(function () {
this.scrollTop = 0;
});
},
//
dataCallback(inedx, item) {
this.isIndex = inedx;
this.Days = this.content[inedx].timelist;
this.Indexes = null;
this.gotop();
this.$emit("dataCallback", item);
},
//
timeCallback(inedx, item) {
this.Indexes = inedx;
this.modalName = null;
this.$emit("timeCallback", item);
},
},
};
</script>
<style scoped>
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #000;
animation: getInto 0.5s 1;
opacity: 0.5;
z-index: 999;
}
@keyframes getInto {
0% {
opacity: 0;
}
100% {
opacity: 0.5;
}
}
.active {
position: fixed;
bottom: 0;
left: 0;
z-index: 1000;
width: 100%;
height: 400rpx;
border-top-left-radius: 16rpx;
border-top-right-radius: 16rpx;
overflow: hidden;
transform: translateY(100%);
transition: 0.4s;
}
.add {
transform: translateY(0);
}
.title-model {
position: relative;
text-align: center;
background-color: #fff;
padding: 20rpx 0;
border-bottom: 2rpx solid #eee;
}
.title-model > text {
position: absolute;
right: 14rpx;
width: 46rpx;
height: 46rpx;
line-height: 38rpx;
background-color: #ccc;
color: #fff;
border-radius: 50%;
}
.cont {
display: flex;
background-color: #fff;
overflow-y: scroll;
}
.day {
flex: 2;
background-color: #fff;
border-right: 2rpx solid #f8f8f8;
text-align: center;
border-bottom: 20px solid #fff;
}
.day view {
padding: 30rpx 12rpx;
font-size: 28rpx;
box-sizing: border-box;
}
.content-model {
flex: 4;
font-size: 28rpx;
border-bottom: 40rpx solid #fff;
background-color: #fff;
}
.appoint {
text-align: left;
padding: 30rpx;
border-bottom: 2rpx solid #f8f8f8;
}
.appoint text {
margin-left: 30rpx;
}
.active_copy {
position: relative;
background-color: #d7f7e3;
color: #27c866;
box-sizing: border-box;
}
.active_copy::after {
content: "";
width: 5rpx;
height: 94rpx;
background: #27c866;
position: absolute;
top: 0;
right: 0;
}
.longActive {
position: relative;
color: #27c866;
}
.cuIcon-check {
position: absolute;
width: 30rpx;
left: 210rpx;
height: 16rpx;
border-bottom: 4rpx solid #27c866;
border-left: 4rpx solid #27c866;
transform: rotate(-45deg);
}
scroll-view ::-webkit-scrollbar {
display: none;
width: 0;
height: 0;
color: transparent;
}
</style>

185
package1/components/planet/buff-shop.vue

@ -0,0 +1,185 @@
<template>
<view class="bs">
<view class="bs-head">
<view>
<text class="bs-kicker">ENERGY SHELF</text>
<text class="bs-title">BUFF 能量站</text>
</view>
<text class="bs-sub">当前 {{myTicket}} </text>
</view>
<scroll-view scroll-x class="bs-scroll" show-scrollbar="false">
<view class="bs-row">
<view class="bs-card" v-for="(b, i) in list" :key="i" :class="{active: b.active}" @tap="onBuy(b)">
<view class="bs-card-emoji">{{buffEmoji(b.type)}}</view>
<view class="bs-card-name">{{b.name}}</view>
<view class="bs-card-desc">{{b.description}}</view>
<view class="bs-card-foot">
<text class="bs-card-cost" v-if="!b.active">{{b.costTickets}} </text>
<text class="bs-card-cost active" v-else>生效中</text>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
props: {
list: {
type: Array,
default: () => []
},
myTicket: {
type: Number,
default: 0
}
},
methods: {
buffEmoji(type) {
const map = {
double: '2X',
shield: '盾',
lucky: 'LU',
hunt: '追',
stealth: '隐'
}
return map[type] || 'UP'
},
onBuy(b) {
this.$emit('buy', b)
}
}
}
</script>
<style lang="scss" scoped>
.bs {
margin-top: 34rpx;
}
.bs-head {
display: flex;
align-items: flex-end;
justify-content: space-between;
margin-bottom: 18rpx;
}
.bs-kicker {
display: block;
color: #59CBB5;
font-size: 18rpx;
font-weight: 900;
letter-spacing: 3rpx;
margin-bottom: 4rpx;
}
.bs-title {
display: block;
color: #12342F;
font-size: 36rpx;
font-weight: 900;
}
.bs-sub {
color: #7E9691;
font-size: 22rpx;
font-weight: 700;
}
.bs-scroll {
width: 100%;
white-space: nowrap;
}
.bs-row {
display: inline-flex;
padding-bottom: 12rpx;
}
.bs-card {
display: inline-block;
width: 218rpx;
min-height: 240rpx;
margin-right: 20rpx;
padding: 24rpx;
border-radius: 36rpx;
background: linear-gradient(155deg, rgba(255,255,255,0.9), rgba(241,255,249,0.72));
border: 2rpx solid rgba(255,255,255,0.9);
white-space: normal;
vertical-align: top;
box-shadow: 0 18rpx 42rpx rgba(53,214,166,0.09);
position: relative;
overflow: hidden;
}
.bs-card:before {
content: '';
position: absolute;
right: -36rpx;
top: -38rpx;
width: 130rpx;
height: 130rpx;
border-radius: 50%;
background: rgba(210,247,255,0.72);
}
.bs-card.active {
border-color: rgba(53,214,166,0.45);
box-shadow: 0 20rpx 46rpx rgba(53,214,166,0.16);
}
.bs-card-emoji {
position: relative;
width: 70rpx;
height: 70rpx;
line-height: 70rpx;
text-align: center;
border-radius: 24rpx;
background: linear-gradient(145deg, rgba(255,255,255,0.95), rgba(213,248,255,0.9));
color: #22B889;
border: 2rpx solid rgba(255,255,255,0.95);
box-shadow: 0 12rpx 26rpx rgba(79,183,255,0.12);
font-size: 24rpx;
font-weight: 900;
font-family: DIN, Arial, sans-serif;
}
.bs-card-name {
position: relative;
margin-top: 14rpx;
color: #12342F;
font-size: 28rpx;
font-weight: 900;
}
.bs-card-desc {
position: relative;
margin-top: 8rpx;
color: #7E9691;
font-size: 20rpx;
min-height: 56rpx;
line-height: 28rpx;
}
.bs-card-foot {
position: relative;
margin-top: 14rpx;
}
.bs-card-cost {
display: inline-block;
padding: 8rpx 20rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #DFFBF1, #E8F6FF);
color: #22B889;
border: 1rpx solid rgba(255,255,255,0.95);
font-size: 22rpx;
font-weight: 900;
}
.bs-card-cost.active {
background: rgba(53,214,166,0.12);
color: #22B889;
}
</style>

291
package1/components/planet/hunt-modal.vue

@ -0,0 +1,291 @@
<template>
<view v-if="show" class="hm-mask">
<view class="hm">
<!-- 追击动画区 -->
<view class="hm-stage" v-if="phase !== 'result'">
<view class="hm-radar">
<view class="hm-radar-sweep"></view>
<view class="hm-radar-ring r1"></view>
<view class="hm-radar-ring r2"></view>
<view class="hm-rocket" :class="phase">GO</view>
<image class="hm-target-avatar" :src="target.avatar || defaultAvatar" mode="aspectFill"></image>
</view>
<view class="hm-phase-text">{{phaseText}}</view>
<view class="hm-dots">
<text class="hm-dot"></text>
<text class="hm-dot"></text>
<text class="hm-dot"></text>
</view>
</view>
<!-- 结果区 -->
<view class="hm-result" v-else>
<view class="hm-result-emoji">{{resultBadge}}</view>
<view class="hm-result-title" :class="result.result">{{resultTitle}}</view>
<view class="hm-result-msg">{{result.message}}</view>
<view class="hm-result-gain" v-if="result.result === 'success'">
<text class="hm-gain-num">+{{result.totalGain}}</text>
<text class="hm-gain-unit">星球券</text>
</view>
<view class="hm-result-remain">今日剩余追捕 {{result.remainHunt}} </view>
<view class="hm-result-btn" @tap="onClose">{{result.result === 'success' ? '收入囊中' : '知道了'}}</view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
show: { type: Boolean, default: false },
phase: { type: String, default: 'searching' },
result: { type: Object, default: null },
target: { type: Object, default: () => ({}) }
},
data() {
return {
defaultAvatar: 'https://jewel-shop.oss-cn-beijing.aliyuncs.com/41cfb56caff4419b94b69d0f2303b602.png'
}
},
computed: {
phaseText() {
const map = {
searching: '雷达搜索目标飞船...',
locking: '已锁定目标坐标...',
chasing: '星际追击中...'
}
return map[this.phase] || '追击中...'
},
resultBadge() {
if (!this.result) return 'GO'
if (this.result.result === 'success') return 'GET'
if (this.result.result === 'shield') return 'SAFE'
return 'MISS'
},
resultTitle() {
if (!this.result) return ''
if (this.result.result === 'success') return '追捕成功'
if (this.result.result === 'shield') return '目标已防护'
return '追捕扑空'
}
},
methods: {
onClose() {
this.$emit('close')
}
}
}
</script>
<style lang="scss" scoped>
.hm-mask {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(18,52,47,0.28);
backdrop-filter: blur(8px);
z-index: 80;
display: flex;
align-items: center;
justify-content: center;
}
.hm {
width: 580rpx;
padding: 56rpx 40rpx 40rpx;
border-radius: 46rpx;
background: linear-gradient(155deg, rgba(255,255,255,0.94), rgba(244,255,249,0.78));
border: 2rpx solid rgba(255,255,255,0.92);
box-shadow: 0 34rpx 80rpx rgba(53,214,166,0.22);
}
.hm-stage {
display: flex;
flex-direction: column;
align-items: center;
}
.hm-radar {
position: relative;
width: 340rpx;
height: 340rpx;
border-radius: 50%;
background: radial-gradient(circle, rgba(53,214,166,0.14), rgba(79,183,255,0.06) 52%, transparent 70%);
display: flex;
align-items: center;
justify-content: center;
}
.hm-radar-ring {
position: absolute;
border-radius: 50%;
border: 2rpx solid rgba(53,214,166,0.26);
}
.hm-radar-ring.r1 { width: 240rpx; height: 240rpx; }
.hm-radar-ring.r2 { width: 340rpx; height: 340rpx; }
.hm-radar-sweep {
position: absolute;
width: 154rpx;
height: 5rpx;
top: 50%;
left: 50%;
transform-origin: left center;
background: linear-gradient(90deg, rgba(53,214,166,0.68), rgba(53,214,166,0));
border-radius: 999rpx;
animation: sweep 1.6s linear infinite;
}
@keyframes sweep {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hm-target-avatar {
width: 96rpx;
height: 96rpx;
border-radius: 50%;
border: 5rpx solid rgba(255,255,255,0.94);
opacity: 0.95;
box-shadow: 0 14rpx 30rpx rgba(255,122,89,0.18);
}
.hm-rocket {
position: absolute;
width: 58rpx;
height: 58rpx;
line-height: 58rpx;
text-align: center;
border-radius: 50%;
background: linear-gradient(135deg, #FF7A59, #FFB84D);
color: #FFFFFF;
font-size: 20rpx;
font-weight: 900;
left: 20rpx;
top: 130rpx;
box-shadow: 0 12rpx 26rpx rgba(255,122,89,0.24);
}
.hm-rocket.searching { animation: orbit 2.5s linear infinite; }
.hm-rocket.locking { animation: orbit 1.4s linear infinite; }
.hm-rocket.chasing { animation: orbit 0.7s linear infinite; }
@keyframes orbit {
0% { transform: rotate(0deg) translateX(150rpx) rotate(0deg); }
100% { transform: rotate(360deg) translateX(150rpx) rotate(-360deg); }
}
.hm-phase-text {
margin-top: 30rpx;
color: #12342F;
font-size: 28rpx;
font-weight: 800;
letter-spacing: 1rpx;
}
.hm-dots {
margin-top: 16rpx;
display: flex;
}
.hm-dot {
width: 12rpx;
height: 12rpx;
border-radius: 50%;
background: #35D6A6;
margin: 0 6rpx;
animation: blink 1.2s ease infinite;
}
.hm-dot:nth-child(2) { animation-delay: 0.2s; }
.hm-dot:nth-child(3) { animation-delay: 0.4s; }
@keyframes blink {
0%, 100% { opacity: 0.3; }
50% { opacity: 1; }
}
.hm-result {
display: flex;
flex-direction: column;
align-items: center;
}
.hm-result-emoji {
width: 160rpx;
height: 160rpx;
line-height: 160rpx;
text-align: center;
border-radius: 50%;
background: linear-gradient(145deg, #E5FFF1, #EAF8FF);
color: #22B889;
font-size: 30rpx;
font-weight: 900;
letter-spacing: 2rpx;
animation: pop 0.5s ease;
box-shadow: inset 0 0 0 2rpx rgba(255,255,255,0.92), 0 18rpx 42rpx rgba(53,214,166,0.14);
}
@keyframes pop {
0% { transform: scale(0.3); opacity: 0; }
70% { transform: scale(1.2); }
100% { transform: scale(1); opacity: 1; }
}
.hm-result-title {
margin-top: 16rpx;
font-size: 40rpx;
font-weight: 800;
color: #12342F;
}
.hm-result-title.success { color: #22B889; }
.hm-result-title.shield { color: #4FB7FF; }
.hm-result-title.fail { color: #7E9691; }
.hm-result-msg {
margin-top: 14rpx;
color: #42635E;
font-size: 26rpx;
text-align: center;
padding: 0 20rpx;
}
.hm-result-gain {
margin-top: 24rpx;
display: flex;
align-items: baseline;
}
.hm-gain-num {
color: #22B889;
font-size: 64rpx;
font-weight: 800;
}
.hm-gain-unit {
color: #42635E;
font-size: 26rpx;
margin-left: 8rpx;
}
.hm-result-remain {
margin-top: 16rpx;
color: #7E9691;
font-size: 22rpx;
}
.hm-result-btn {
margin-top: 36rpx;
width: 100%;
height: 84rpx;
line-height: 84rpx;
text-align: center;
border-radius: 42rpx;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
font-size: 30rpx;
font-weight: 800;
box-shadow: 0 18rpx 36rpx rgba(53,214,166,0.22);
}
</style>

179
package1/components/planet/planet-box.vue

@ -0,0 +1,179 @@
<template>
<view class="pb" @tap="onOpen">
<view class="pb-tag">{{available ? 'TODAY DROP' : 'TOMORROW DROP'}}</view>
<view class="pb-visual">
<view class="pb-glow" v-if="available"></view>
<view class="pb-box" :class="{shaking: opening, dim: !available}">
<view class="pb-box-lid"></view>
<view class="pb-box-face">?</view>
</view>
</view>
<view class="pb-mid">
<view class="pb-title">幸运盲盒舱</view>
<view class="pb-desc">{{available ? '今日补给已抵达,拆开看看有什么' : '今日已拆盒,明天再来补给'}}</view>
</view>
<view class="pb-btn" :class="{disabled: !available}">
{{opening ? '拆盒中' : (available ? '拆盲盒' : '已拆')}}
</view>
</view>
</template>
<script>
export default {
props: {
available: {
type: Boolean,
default: false
},
opening: {
type: Boolean,
default: false
}
},
methods: {
onOpen() {
if (!this.available || this.opening) return
this.$emit('open')
}
}
}
</script>
<style lang="scss" scoped>
.pb {
margin-top: 28rpx;
padding: 34rpx 28rpx;
border-radius: 46rpx 30rpx 46rpx 30rpx;
background:
radial-gradient(circle at 16% 18%, rgba(255,255,255,0.86), transparent 22%),
linear-gradient(135deg, rgba(255,255,255,0.86), rgba(255,246,219,0.78) 52%, rgba(232,223,255,0.72));
border: 2rpx solid rgba(255,255,255,0.9);
display: flex;
align-items: center;
position: relative;
overflow: hidden;
box-shadow: 0 22rpx 54rpx rgba(255,184,77,0.15);
}
.pb-tag {
position: absolute;
right: 24rpx;
top: 18rpx;
color: rgba(255,122,89,0.52);
font-size: 18rpx;
font-weight: 900;
letter-spacing: 2rpx;
}
.pb-visual {
position: relative;
width: 126rpx;
height: 126rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.pb-glow {
position: absolute;
width: 150rpx;
height: 150rpx;
border-radius: 50%;
background: radial-gradient(circle, rgba(255,184,77,0.38), transparent 66%);
animation: spark 2s ease-in-out infinite;
}
.pb-box {
position: relative;
width: 90rpx;
height: 92rpx;
border-radius: 22rpx;
background: linear-gradient(145deg, #FF8B6A, #FFCF7A);
box-shadow: inset -10rpx -12rpx 22rpx rgba(146,75,26,0.18), 0 16rpx 34rpx rgba(255,122,89,0.26);
}
.pb-box.shaking {
animation: shake 0.3s ease-in-out infinite;
}
.pb-box.dim {
filter: grayscale(0.8);
opacity: 0.58;
}
.pb-box-lid {
position: absolute;
left: -8rpx;
top: -18rpx;
width: 106rpx;
height: 34rpx;
border-radius: 18rpx;
background: linear-gradient(135deg, #FFFFFF, #FFD38C);
transform: rotate(-5deg);
}
.pb-box-face {
position: absolute;
left: 50%;
top: 54%;
transform: translate(-50%, -50%);
width: 42rpx;
height: 42rpx;
line-height: 42rpx;
text-align: center;
border-radius: 50%;
background: rgba(255,255,255,0.86);
color: #FF7A59;
font-size: 28rpx;
font-weight: 900;
}
@keyframes shake {
0%, 100% { transform: rotate(-6deg) scale(1); }
50% { transform: rotate(7deg) scale(1.04); }
}
@keyframes spark {
0%, 100% { opacity: 0.42; transform: scale(0.86); }
50% { opacity: 0.95; transform: scale(1.1); }
}
.pb-mid {
flex: 1;
margin-left: 18rpx;
min-width: 0;
}
.pb-title {
color: #12342F;
font-size: 32rpx;
font-weight: 900;
}
.pb-desc {
color: #42635E;
font-size: 23rpx;
margin-top: 8rpx;
line-height: 34rpx;
}
.pb-btn {
padding: 0 24rpx;
height: 62rpx;
line-height: 62rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #FFB84D, #FF7A59);
color: #FFFFFF;
font-size: 25rpx;
font-weight: 800;
box-shadow: 0 14rpx 28rpx rgba(255,122,89,0.24);
flex-shrink: 0;
}
.pb-btn.disabled {
background: rgba(18,52,47,0.08);
color: #7E9691;
box-shadow: none;
}
</style>

690
package1/components/planet/planet-header.vue

@ -0,0 +1,690 @@
<template>
<view class="ph">
<view class="ph-story">
<text class="ph-kicker">BANJINLI ORBIT</text>
<text class="ph-title">今天登陆白嫖星球</text>
<text class="ph-sub">攒券追捕开盲盒全校一起瓜分补给池</text>
</view>
<view class="ph-meteor ph-meteor-a"></view>
<view class="ph-meteor ph-meteor-b"></view>
<view class="ph-station">
<view class="ph-station-core"></view>
<view class="ph-station-wing ph-station-wing-left"></view>
<view class="ph-station-wing ph-station-wing-right"></view>
</view>
<view class="ph-asteroid ph-asteroid-a"></view>
<view class="ph-asteroid ph-asteroid-b"></view>
<view class="ph-floating-box">
<view class="ph-floating-box-lid"></view>
<text>?</text>
</view>
<view class="ph-orbit ph-orbit-a"></view>
<view class="ph-orbit ph-orbit-b"></view>
<view class="ph-orbit ph-orbit-c"></view>
<view class="ph-planet-wrap">
<view class="ph-planet-glow"></view>
<view class="ph-planet">
<view class="ph-planet-map ph-planet-map-a"></view>
<view class="ph-planet-map ph-planet-map-b"></view>
<view class="ph-planet-map ph-planet-map-c"></view>
<view class="ph-shine ph-shine-a"></view>
<view class="ph-shine ph-shine-b"></view>
<view class="ph-atmosphere"></view>
</view>
<view class="ph-planet-core">
<view class="ph-core-label">今日补给池</view>
<view class="ph-pool">
<text class="ph-pool-symbol"></text>
<text class="ph-pool-num">{{poolText}}</text>
</view>
<view class="ph-pool-label">星球券越多瓜分权重越高</view>
</view>
</view>
<view class="ph-squirrel">
<image class="ph-squirrel-img" src="/static/images/img/loading.gif" mode="aspectFit"></image>
<view class="ph-squirrel-tag">IP</view>
</view>
<view class="ph-satellite ph-satellite-left">
<text class="ph-satellite-num">{{data.joinCount || 0}}</text>
<text class="ph-satellite-label">全校登陆</text>
</view>
<view class="ph-satellite ph-satellite-right">
<text class="ph-satellite-num">{{rankText}}</text>
<text class="ph-satellite-label">财富坐标</text>
</view>
<view class="ph-ticket-pill">我的星球券 {{data.myTicketCount || 0}} </view>
<view class="ph-count">
<view class="ph-period"> {{data.periodNo || '--'}} </view>
<view class="ph-count-unit">
<text>{{cd.h}}</text>
<text>H</text>
</view>
<view class="ph-count-unit">
<text>{{cd.m}}</text>
<text>M</text>
</view>
<view class="ph-count-unit">
<text>{{cd.s}}</text>
<text>S</text>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
data: {
type: Object,
default: () => ({})
}
},
data() {
return {
remain: 0,
timer: null
}
},
computed: {
poolText() {
const v = Number(this.data.poolAmount || 0)
return v.toFixed(2)
},
rankText() {
const r = this.data.myRankNo || 0
return r > 0 ? ('No.' + r) : '未上榜'
},
cd() {
let s = Math.floor(this.remain / 1000)
const h = Math.floor(s / 3600)
s -= h * 3600
const m = Math.floor(s / 60)
s -= m * 60
return {
h: this.pad(h),
m: this.pad(m),
s: this.pad(s)
}
}
},
watch: {
'data.countdownMillis'(val) {
this.remain = val || 0
this.startTimer()
}
},
mounted() {
this.remain = this.data.countdownMillis || 0
this.startTimer()
},
beforeDestroy() {
if (this.timer) clearInterval(this.timer)
},
methods: {
pad(n) {
return n < 10 ? ('0' + n) : ('' + n)
},
startTimer() {
if (this.timer) clearInterval(this.timer)
this.timer = setInterval(() => {
if (this.remain <= 1000) {
this.remain = 0
clearInterval(this.timer)
return
}
this.remain -= 1000
}, 1000)
}
}
}
</script>
<style lang="scss" scoped>
.ph {
position: relative;
margin-top: 6rpx;
height: 820rpx;
overflow: visible;
}
.ph-story {
position: absolute;
left: 8rpx;
top: 12rpx;
z-index: 6;
display: flex;
flex-direction: column;
}
.ph-kicker {
color: #35D6A6;
font-size: 18rpx;
font-weight: 900;
letter-spacing: 4rpx;
}
.ph-title {
margin-top: 10rpx;
color: #12342F;
font-size: 44rpx;
font-weight: 900;
letter-spacing: -1rpx;
}
.ph-sub {
margin-top: 10rpx;
color: #42635E;
font-size: 24rpx;
font-weight: 600;
}
.ph-meteor {
position: absolute;
width: 180rpx;
height: 3rpx;
border-radius: 999rpx;
background: linear-gradient(90deg, rgba(255,255,255,0), rgba(255,255,255,0.95), rgba(79,183,255,0.4));
transform: rotate(-28deg);
animation: meteorFly 4.8s linear infinite;
z-index: 2;
}
.ph-meteor-a {
top: 132rpx;
right: -120rpx;
}
.ph-meteor-b {
top: 370rpx;
left: 40rpx;
width: 120rpx;
animation-delay: -2.2s;
opacity: 0.72;
}
@keyframes meteorFly {
0% { transform: translate3d(180rpx, -70rpx, 0) rotate(-28deg); opacity: 0; }
12% { opacity: 1; }
70% { opacity: 1; }
100% { transform: translate3d(-760rpx, 260rpx, 0) rotate(-28deg); opacity: 0; }
}
.ph-station {
position: absolute;
right: 26rpx;
top: 150rpx;
width: 170rpx;
height: 78rpx;
z-index: 4;
animation: stationFloat 5.6s ease-in-out infinite;
}
@keyframes stationFloat {
0%, 100% { transform: translateY(0) rotate(4deg); }
50% { transform: translateY(-16rpx) rotate(-2deg); }
}
.ph-station-core {
position: absolute;
left: 58rpx;
top: 20rpx;
width: 56rpx;
height: 38rpx;
border-radius: 18rpx;
background: linear-gradient(145deg, rgba(255,255,255,0.96), rgba(224,247,255,0.88));
border: 2rpx solid rgba(79,183,255,0.28);
box-shadow: 0 14rpx 28rpx rgba(79,183,255,0.18);
}
.ph-station-wing {
position: absolute;
top: 30rpx;
width: 58rpx;
height: 18rpx;
border-radius: 999rpx;
background: linear-gradient(90deg, rgba(143,124,255,0.76), rgba(79,183,255,0.72));
}
.ph-station-wing-left { left: 0; }
.ph-station-wing-right { right: 0; }
.ph-asteroid {
position: absolute;
border-radius: 46% 54% 44% 56%;
background: linear-gradient(145deg, #FFE6A6, #FFB7D1);
box-shadow: inset -10rpx -10rpx 20rpx rgba(150,93,48,0.12), 0 12rpx 30rpx rgba(255,184,77,0.18);
z-index: 3;
animation: asteroidFloat 6.2s ease-in-out infinite;
}
.ph-asteroid-a {
left: 18rpx;
top: 260rpx;
width: 62rpx;
height: 48rpx;
}
.ph-asteroid-b {
right: 86rpx;
top: 514rpx;
width: 42rpx;
height: 34rpx;
animation-delay: -2s;
}
@keyframes asteroidFloat {
0%, 100% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(18rpx) rotate(12deg); }
}
.ph-floating-box {
position: absolute;
left: 34rpx;
top: 525rpx;
width: 88rpx;
height: 86rpx;
border-radius: 22rpx;
background: linear-gradient(145deg, #FF8B6A, #FFCF7A);
box-shadow: inset -8rpx -10rpx 18rpx rgba(146,75,26,0.18), 0 18rpx 36rpx rgba(255,122,89,0.22);
display: flex;
align-items: center;
justify-content: center;
color: #FFFFFF;
font-size: 32rpx;
font-weight: 900;
z-index: 5;
animation: treasureFloat 4.4s ease-in-out infinite;
}
.ph-floating-box-lid {
position: absolute;
left: -7rpx;
top: -16rpx;
width: 102rpx;
height: 32rpx;
border-radius: 18rpx;
background: linear-gradient(135deg, #FFFFFF, #FFD38C);
transform: rotate(-5deg);
}
@keyframes treasureFloat {
0%, 100% { transform: translateY(0) rotate(-7deg); }
50% { transform: translateY(-18rpx) rotate(5deg); }
}
.ph-orbit {
position: absolute;
left: 50%;
border: 2rpx solid rgba(255,255,255,0.72);
border-radius: 50%;
transform: translateX(-50%) rotate(-16deg);
z-index: 2;
}
.ph-orbit-a {
top: 196rpx;
width: 720rpx;
height: 330rpx;
opacity: 0.76;
}
.ph-orbit-b {
top: 250rpx;
width: 610rpx;
height: 260rpx;
opacity: 0.46;
}
.ph-orbit-c {
top: 312rpx;
width: 760rpx;
height: 220rpx;
opacity: 0.36;
transform: translateX(-50%) rotate(12deg);
}
.ph-planet-wrap {
position: absolute;
top: 142rpx;
left: 50%;
transform: translateX(-50%);
width: 610rpx;
height: 610rpx;
z-index: 3;
animation: planetBreath 4.4s ease-in-out infinite;
}
.ph-planet-glow {
position: absolute;
left: 50%;
top: 50%;
width: 680rpx;
height: 680rpx;
border-radius: 50%;
transform: translate(-50%, -50%);
background: radial-gradient(circle, rgba(53,214,166,0.26), rgba(79,183,255,0.12) 42%, rgba(255,255,255,0) 68%);
filter: blur(8rpx);
}
.ph-planet {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 570rpx;
height: 570rpx;
border-radius: 50%;
background:
radial-gradient(circle at 28% 22%, rgba(255,255,255,0.96) 0%, rgba(255,255,255,0.72) 15%, transparent 28%),
radial-gradient(circle at 72% 74%, rgba(143,124,255,0.34), transparent 38%),
linear-gradient(145deg, rgba(201,255,225,0.82) 0%, rgba(166,242,255,0.78) 46%, rgba(232,223,255,0.82) 100%);
box-shadow:
inset -54rpx -66rpx 90rpx rgba(18,52,47,0.12),
inset 36rpx 42rpx 78rpx rgba(255,255,255,0.68),
0 42rpx 96rpx rgba(53,214,166,0.28);
border: 2rpx solid rgba(255,255,255,0.72);
overflow: hidden;
}
@keyframes planetBreath {
0%, 100% { transform: translateX(-50%) translateY(0) scale(1); }
50% { transform: translateX(-50%) translateY(-14rpx) scale(1.02); }
}
.ph-planet-map {
position: absolute;
background: rgba(53,214,166,0.16);
filter: blur(1rpx);
}
.ph-planet-map-a {
left: 96rpx;
top: 172rpx;
width: 172rpx;
height: 82rpx;
border-radius: 56% 44% 60% 40%;
transform: rotate(-16deg);
}
.ph-planet-map-b {
right: 96rpx;
top: 298rpx;
width: 188rpx;
height: 92rpx;
border-radius: 44% 56% 42% 58%;
background: rgba(79,183,255,0.14);
transform: rotate(20deg);
}
.ph-planet-map-c {
left: 214rpx;
bottom: 92rpx;
width: 138rpx;
height: 62rpx;
border-radius: 50%;
background: rgba(143,124,255,0.12);
}
.ph-shine {
position: absolute;
border-radius: 50%;
background: rgba(255,255,255,0.72);
filter: blur(2rpx);
}
.ph-shine-a {
left: 94rpx;
top: 80rpx;
width: 140rpx;
height: 74rpx;
transform: rotate(-22deg);
}
.ph-shine-b {
right: 96rpx;
top: 98rpx;
width: 66rpx;
height: 34rpx;
opacity: 0.58;
}
.ph-atmosphere {
position: absolute;
left: 28rpx;
right: 28rpx;
top: 38rpx;
height: 150rpx;
border-top: 3rpx solid rgba(255,255,255,0.5);
border-radius: 50%;
transform: rotate(-12deg);
}
.ph-planet-core {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 390rpx;
min-height: 220rpx;
border-radius: 56rpx;
background: rgba(255,255,255,0.34);
border: 1rpx solid rgba(255,255,255,0.72);
backdrop-filter: blur(14px);
box-shadow: inset 0 0 0 1rpx rgba(255,255,255,0.36), 0 18rpx 44rpx rgba(53,214,166,0.12);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 5;
}
.ph-core-label {
color: #42635E;
font-size: 24rpx;
font-weight: 800;
letter-spacing: 2rpx;
}
.ph-period {
position: relative;
text-align: center;
color: #7E9691;
font-size: 22rpx;
font-weight: 700;
margin-right: 12rpx;
}
.ph-pool {
position: relative;
text-align: center;
margin-top: 8rpx;
color: #16A779;
}
.ph-pool-symbol {
font-size: 38rpx;
font-weight: 900;
}
.ph-pool-num {
font-size: 92rpx;
font-weight: 900;
font-family: DIN, sans-serif;
letter-spacing: -2rpx;
}
.ph-pool-label {
position: relative;
text-align: center;
color: #42635E;
font-size: 24rpx;
margin-top: 2rpx;
}
.ph-ticket-pill {
position: absolute;
left: 30rpx;
bottom: 86rpx;
z-index: 6;
padding: 12rpx 22rpx;
border-radius: 999rpx;
background: rgba(18,52,47,0.86);
border: 1rpx solid rgba(255,255,255,0.9);
color: #FFFFFF;
font-size: 23rpx;
font-weight: 900;
box-shadow: 0 16rpx 34rpx rgba(18,52,47,0.18);
}
.ph-count {
position: absolute;
left: 50%;
bottom: 18rpx;
transform: translateX(-50%);
display: flex;
align-items: center;
justify-content: center;
padding: 12rpx 16rpx;
border-radius: 999rpx;
background: rgba(255,255,255,0.72);
border: 1rpx solid rgba(255,255,255,0.92);
box-shadow: 0 18rpx 42rpx rgba(79,183,255,0.14);
backdrop-filter: blur(8px);
z-index: 6;
}
.ph-count-label {
color: #42635E;
font-size: 24rpx;
font-weight: 700;
margin-right: 12rpx;
}
.ph-count-unit {
width: 64rpx;
height: 64rpx;
margin-left: 8rpx;
border-radius: 22rpx;
background: linear-gradient(145deg, #F8FFFB, #E1F8FF);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: inset 0 0 0 1rpx rgba(255,255,255,0.72);
}
.ph-count-unit text:first-child {
color: #12342F;
font-size: 28rpx;
font-weight: 900;
line-height: 30rpx;
font-family: DIN, Arial, sans-serif;
}
.ph-count-unit text:last-child {
color: #7E9691;
font-size: 16rpx;
font-weight: 700;
line-height: 20rpx;
}
.ph-satellite {
position: absolute;
z-index: 6;
min-width: 132rpx;
padding: 14rpx 18rpx;
border-radius: 28rpx;
background: rgba(255,255,255,0.74);
border: 1rpx solid rgba(255,255,255,0.92);
box-shadow: 0 18rpx 38rpx rgba(53,214,166,0.12);
display: flex;
flex-direction: column;
align-items: center;
animation: orbitFloat 4s ease-in-out infinite;
}
.ph-satellite-left {
left: 0;
top: 350rpx;
transform: rotate(-6deg);
}
.ph-satellite-right {
right: 0;
top: 428rpx;
transform: rotate(7deg);
animation-delay: -1.5s;
}
@keyframes orbitFloat {
0%, 100% { margin-top: 0; }
50% { margin-top: -12rpx; }
}
.ph-satellite-num {
color: #12342F;
font-size: 30rpx;
font-weight: 900;
max-width: 150rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.ph-satellite-label {
margin-top: 2rpx;
color: #7E9691;
font-size: 20rpx;
font-weight: 600;
}
.ph-squirrel {
position: absolute;
right: 18rpx;
top: 585rpx;
width: 142rpx;
height: 164rpx;
z-index: 7;
animation: squirrelFloat 4.6s ease-in-out infinite;
}
@keyframes squirrelFloat {
0%, 100% { transform: translateY(0) rotate(6deg); }
50% { transform: translateY(-18rpx) rotate(-4deg); }
}
.ph-squirrel-img {
position: absolute;
left: 0;
top: 0;
width: 142rpx;
height: 142rpx;
border-radius: 50%;
background: rgba(255,255,255,0.5);
border: 3rpx solid rgba(255,255,255,0.9);
box-shadow: 0 16rpx 34rpx rgba(79,183,255,0.18);
}
.ph-squirrel-tag {
position: absolute;
left: 38rpx;
bottom: 0;
width: 68rpx;
height: 38rpx;
line-height: 38rpx;
text-align: center;
border-radius: 999rpx;
background: rgba(255,255,255,0.82);
color: #35D6A6;
font-size: 19rpx;
font-weight: 900;
border: 1rpx solid rgba(255,255,255,0.92);
box-shadow: 0 10rpx 22rpx rgba(53,214,166,0.12);
}
</style>

237
package1/components/planet/planet-me.vue

@ -0,0 +1,237 @@
<template>
<view class="pm">
<view class="pm-stamp">PLANET PASS</view>
<view class="pm-main">
<view class="pm-left">
<image class="pm-avatar" :src="data.avatar || defaultAvatar" mode="aspectFill"></image>
<view class="pm-info">
<view class="pm-name">{{data.nickname || '星球居民'}}</view>
<view class="pm-sub">
<text>{{data.level || 'C'}} 级探索者</text>
<text>连签 {{data.consecutiveSignDays || 0}} </text>
</view>
</view>
</view>
<view class="pm-ticket">
<text class="pm-ticket-num">{{data.myTicketCount || 0}}</text>
<text class="pm-ticket-label">星球券</text>
</view>
</view>
<view class="pm-sign" :class="{signed: data.signedToday}" @tap="onSign">
{{data.signedToday ? '今日已盖章' : '今日签到盖章'}}
</view>
<view class="pm-buffs" v-if="data.myBuffs && data.myBuffs.length">
<text class="pm-buffs-label">正在生效</text>
<view class="pm-buff" v-for="(b, i) in data.myBuffs" :key="i">
<text class="pm-buff-emoji">{{buffEmoji(b.type)}}</text>
</view>
</view>
<view class="pm-buffs pm-buffs-empty" v-else>
<text class="pm-buffs-label">暂无增益去能量站补给</text>
</view>
</view>
</template>
<script>
export default {
props: {
data: {
type: Object,
default: () => ({})
}
},
data() {
return {
defaultAvatar: 'https://jewel-shop.oss-cn-beijing.aliyuncs.com/41cfb56caff4419b94b69d0f2303b602.png'
}
},
methods: {
buffEmoji(type) {
const map = {
double: '2X',
shield: '盾',
lucky: 'LU',
hunt: '追',
stealth: '隐'
}
return map[type] || 'UP'
},
onSign() {
if (this.data.signedToday) return
this.$emit('sign')
}
}
}
</script>
<style lang="scss" scoped>
.pm {
position: relative;
margin-top: -12rpx;
padding: 30rpx 28rpx 28rpx;
border-radius: 38rpx;
background: linear-gradient(145deg, rgba(255,255,255,0.86), rgba(245,255,250,0.64));
border: 2rpx solid rgba(255,255,255,0.9);
box-shadow: 0 22rpx 60rpx rgba(53,214,166,0.14);
backdrop-filter: blur(10px);
overflow: hidden;
transform: rotate(-1.2deg);
}
.pm:before {
content: '';
position: absolute;
right: -50rpx;
top: -80rpx;
width: 220rpx;
height: 220rpx;
border-radius: 50%;
background: radial-gradient(circle, rgba(79,183,255,0.28), transparent 66%);
}
.pm-stamp {
position: absolute;
right: 26rpx;
top: 22rpx;
color: rgba(53,214,166,0.22);
font-size: 24rpx;
font-weight: 900;
letter-spacing: 3rpx;
}
.pm-main {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
}
.pm-left {
display: flex;
align-items: center;
min-width: 0;
flex: 1;
}
.pm-avatar {
width: 92rpx;
height: 92rpx;
border-radius: 50%;
border: 5rpx solid rgba(255,255,255,0.9);
box-shadow: 0 14rpx 28rpx rgba(53,214,166,0.18);
flex-shrink: 0;
}
.pm-info {
flex: 1;
margin-left: 18rpx;
min-width: 0;
}
.pm-name {
color: #12342F;
font-size: 32rpx;
font-weight: 800;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.pm-sub {
margin-top: 10rpx;
display: flex;
align-items: center;
flex-wrap: wrap;
}
.pm-sub text {
color: #42635E;
font-size: 22rpx;
margin-right: 14rpx;
}
.pm-ticket {
position: relative;
min-width: 132rpx;
height: 96rpx;
padding: 0 16rpx;
border-radius: 28rpx;
background: linear-gradient(145deg, rgba(255,255,255,0.96), rgba(221,248,255,0.88));
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: 2rpx solid rgba(255,255,255,0.96);
box-shadow: 0 16rpx 34rpx rgba(79,183,255,0.12);
}
.pm-ticket-num {
color: #22B889;
font-size: 36rpx;
font-weight: 900;
font-family: DIN, Arial, sans-serif;
line-height: 38rpx;
}
.pm-ticket-label {
margin-top: 4rpx;
color: #7E9691;
font-size: 20rpx;
font-weight: 700;
}
.pm-sign {
position: relative;
margin-top: 24rpx;
height: 66rpx;
line-height: 66rpx;
text-align: center;
border-radius: 999rpx;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
font-size: 26rpx;
font-weight: 800;
box-shadow: 0 16rpx 34rpx rgba(53,214,166,0.24);
}
.pm-sign.signed {
background: rgba(18,52,47,0.08);
color: #7E9691;
box-shadow: none;
}
.pm-buffs {
position: relative;
margin-top: 24rpx;
display: flex;
align-items: center;
flex-wrap: wrap;
}
.pm-buffs-label {
color: #7E9691;
font-size: 22rpx;
margin-right: 14rpx;
font-weight: 600;
}
.pm-buff {
width: 52rpx;
height: 52rpx;
border-radius: 18rpx;
background: rgba(255,255,255,0.72);
border: 1rpx solid rgba(53,214,166,0.26);
display: flex;
align-items: center;
justify-content: center;
margin-right: 12rpx;
}
.pm-buff-emoji {
font-size: 19rpx;
font-weight: 900;
color: #22B889;
font-family: DIN, Arial, sans-serif;
}
</style>

70
package1/components/planet/planet-news.vue

@ -0,0 +1,70 @@
<template>
<view class="pn" v-if="list && list.length">
<view class="pn-icon">RADIO</view>
<swiper class="pn-swiper" vertical autoplay circular :interval="2800" :duration="500" :display-multiple-items="1">
<swiper-item v-for="(item, idx) in list" :key="idx" class="pn-item">
<text class="pn-text">{{item.content}}</text>
</swiper-item>
</swiper>
</view>
</template>
<script>
export default {
props: {
list: {
type: Array,
default: () => []
}
}
}
</script>
<style lang="scss" scoped>
.pn {
display: flex;
align-items: center;
margin-top: -8rpx;
padding: 0 18rpx;
height: 72rpx;
border-radius: 999rpx;
background: rgba(255,255,255,0.72);
border: 1rpx solid rgba(255,255,255,0.92);
box-shadow: 0 16rpx 34rpx rgba(79,183,255,0.12);
backdrop-filter: blur(8px);
transform: rotate(1.2deg);
}
.pn-icon {
height: 42rpx;
line-height: 42rpx;
padding: 0 16rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
font-size: 18rpx;
font-weight: 900;
letter-spacing: 1rpx;
margin-right: 12rpx;
}
.pn-swiper {
flex: 1;
height: 72rpx;
}
.pn-item {
display: flex;
align-items: center;
height: 72rpx;
}
.pn-text {
color: #42635E;
font-size: 24rpx;
font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>

218
package1/components/planet/planet-rank.vue

@ -0,0 +1,218 @@
<template>
<view class="pr">
<view class="pr-game">
<view class="pr-radar">
<view class="pr-radar-line"></view>
<view class="pr-radar-dot"></view>
</view>
<view class="pr-game-copy">
<text class="pr-kicker">SCHOOL RICH LIST</text>
<text class="pr-title">全校首富雷达</text>
<text class="pr-sub">锁定券王发起追捕抢回今日补给权</text>
</view>
<view class="pr-remain">
<text>{{remainHunt}}</text>
<text>次追捕</text>
</view>
</view>
<scroll-view v-if="list && list.length" scroll-x class="pr-scroll" show-scrollbar="false">
<view class="pr-list">
<rank-item
v-for="(item, i) in list"
:key="i"
:item="item"
:index="i"
@hunt="onHunt">
</rank-item>
</view>
</scroll-view>
<view v-else class="pr-empty">
<text>暂无上榜居民攒券登顶成为今日主角</text>
</view>
</view>
</template>
<script>
import rankItem from './rank-item.vue'
export default {
components: {
rankItem
},
props: {
list: {
type: Array,
default: () => []
},
remainHunt: {
type: Number,
default: 0
}
},
methods: {
onHunt(item) {
this.$emit('hunt', item)
}
}
}
</script>
<style lang="scss" scoped>
.pr {
margin-top: 38rpx;
}
.pr-game {
position: relative;
display: flex;
align-items: center;
min-height: 190rpx;
box-sizing: border-box;
padding: 28rpx 24rpx;
margin-bottom: 24rpx;
border-radius: 46rpx;
background:
radial-gradient(circle at 18% 50%, rgba(255,122,89,0.14), transparent 34%),
linear-gradient(135deg, rgba(255,255,255,0.86), rgba(255,246,219,0.66));
border: 2rpx solid rgba(255,255,255,0.92);
box-shadow: 0 24rpx 58rpx rgba(255,122,89,0.12);
overflow: hidden;
}
.pr-radar {
position: relative;
width: 126rpx;
height: 126rpx;
border-radius: 50%;
border: 2rpx solid rgba(255,122,89,0.22);
background: radial-gradient(circle, rgba(255,122,89,0.12), rgba(255,255,255,0.44));
flex-shrink: 0;
overflow: hidden;
}
.pr-radar:before,
.pr-radar:after {
content: '';
position: absolute;
border-radius: 50%;
border: 1rpx solid rgba(255,122,89,0.2);
}
.pr-radar:before {
left: 24rpx;
top: 24rpx;
width: 76rpx;
height: 76rpx;
}
.pr-radar:after {
left: 44rpx;
top: 44rpx;
width: 36rpx;
height: 36rpx;
}
.pr-radar-line {
position: absolute;
left: 50%;
top: 50%;
width: 58rpx;
height: 4rpx;
border-radius: 999rpx;
transform-origin: left center;
background: linear-gradient(90deg, rgba(255,122,89,0.72), rgba(255,122,89,0));
animation: radarSpin 2.4s linear infinite;
}
@keyframes radarSpin {
0% { transform: rotate(0deg) translateX(0); }
100% { transform: rotate(360deg) translateX(0); }
}
.pr-radar-dot {
position: absolute;
right: 30rpx;
top: 36rpx;
width: 14rpx;
height: 14rpx;
border-radius: 50%;
background: #FF7A59;
box-shadow: 0 0 18rpx rgba(255,122,89,0.55);
}
.pr-game-copy {
flex: 1;
margin-left: 22rpx;
min-width: 0;
}
.pr-kicker {
display: block;
color: #FF7A59;
font-size: 18rpx;
font-weight: 900;
letter-spacing: 3rpx;
margin-bottom: 4rpx;
}
.pr-title {
display: block;
color: #12342F;
font-size: 38rpx;
font-weight: 900;
letter-spacing: -1rpx;
}
.pr-sub {
display: block;
color: #7E9691;
font-size: 22rpx;
margin-top: 6rpx;
font-weight: 600;
}
.pr-remain {
color: #FFFFFF;
width: 112rpx;
height: 112rpx;
background: linear-gradient(135deg, #FF7A59, #FFB84D);
border-radius: 34rpx;
box-shadow: 0 12rpx 26rpx rgba(255,122,89,0.18);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
flex-shrink: 0;
transform: rotate(4deg);
}
.pr-remain text:first-child {
font-size: 40rpx;
font-weight: 900;
line-height: 42rpx;
font-family: DIN, Arial, sans-serif;
}
.pr-remain text:last-child {
font-size: 19rpx;
font-weight: 800;
margin-top: 4rpx;
}
.pr-scroll {
width: 100%;
white-space: nowrap;
}
.pr-list {
display: inline-flex;
padding: 4rpx 0 12rpx;
}
.pr-empty {
padding: 60rpx 0;
text-align: center;
color: #7E9691;
font-size: 24rpx;
}
</style>

250
package1/components/planet/planet-tasks.vue

@ -0,0 +1,250 @@
<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>
<text>{{btnText(t)}}</text>
</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;
margin-top: 34rpx;
}
.pt-head {
display: flex;
align-items: flex-end;
justify-content: space-between;
margin-bottom: 22rpx;
}
.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;
flex-wrap: wrap;
justify-content: space-between;
}
.pt-list:before {
content: '';
position: absolute;
left: 42rpx;
right: 42rpx;
top: 102rpx;
height: 180rpx;
border-top: 3rpx dashed rgba(53,214,166,0.28);
border-radius: 50%;
transform: rotate(-7deg);
background: transparent;
}
.pt-item {
position: relative;
width: 318rpx;
min-height: 224rpx;
box-sizing: border-box;
padding: 26rpx 22rpx 24rpx;
margin-bottom: 24rpx;
border-radius: 36rpx;
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;
}
.pt-item.offset {
margin-top: 54rpx;
transform: rotate(1.4deg);
}
.pt-item:not(.offset) {
transform: rotate(-1deg);
}
.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;
left: 28rpx;
top: -8rpx;
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;
width: 72rpx;
height: 72rpx;
border-radius: 24rpx;
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);
}
.pt-item-mid {
position: relative;
margin-top: 18rpx;
}
.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;
min-height: 64rpx;
}
.pt-item-btn {
position: relative;
margin-top: 18rpx;
height: 58rpx;
padding: 0 18rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 23rpx;
font-weight: 800;
box-shadow: 0 12rpx 26rpx rgba(53,214,166,0.22);
}
.pt-item-btn.auto {
background: rgba(18,52,47,0.08);
color: #22B889;
box-shadow: none;
}
.pt-item-btn.done {
background: rgba(18,52,47,0.06);
color: #9AA9A5;
box-shadow: none;
}
</style>

251
package1/components/planet/rank-item.vue

@ -0,0 +1,251 @@
<template>
<view class="ri" :class="cardClass">
<view class="ri-scan"></view>
<view class="ri-topline">
<view class="ri-rank">
<text>#{{item.rankNo}}</text>
</view>
<text class="ri-self" v-if="item.self">ME</text>
</view>
<view class="ri-avatar-wrap">
<image class="ri-avatar" :src="item.avatar || defaultAvatar" mode="aspectFill"></image>
<view class="ri-ring"></view>
</view>
<view class="ri-mid">
<view class="ri-name">{{item.nickname || '神秘同学'}}</view>
<view class="ri-bounty">{{item.ticketCount}} 张星球券</view>
<view class="ri-tags">
<text class="ri-danger">{{item.dangerLevelName || '校园玩家'}}</text>
<text class="ri-keep" v-if="item.rankKeepDays > 0">霸榜 {{item.rankKeepDays}} </text>
</view>
<view class="ri-action"
:class="{disabled: item.self || item.shielded}"
@tap.stop="onHunt">
<text v-if="item.shielded">防护中</text>
<text v-else-if="item.self">榜上有我</text>
<text v-else>发起追捕</text>
</view>
<view class="ri-extra" v-if="item.bountyTickets > 0 && !item.self">额外悬赏+{{item.bountyTickets}}</view>
</view>
</view>
</template>
<script>
export default {
props: {
item: {
type: Object,
default: () => ({})
},
index: {
type: Number,
default: 0
}
},
data() {
return {
defaultAvatar: 'https://jewel-shop.oss-cn-beijing.aliyuncs.com/41cfb56caff4419b94b69d0f2303b602.png'
}
},
computed: {
cardClass() {
return 'lv-' + (this.item.dangerLevel || 'C').toLowerCase() + (this.index < 3 ? ' top' : '')
}
},
methods: {
onHunt() {
this.$emit('hunt', this.item)
}
}
}
</script>
<style lang="scss" scoped>
.ri {
position: relative;
display: inline-flex;
flex-direction: column;
align-items: center;
width: 252rpx;
min-height: 386rpx;
box-sizing: border-box;
padding: 22rpx 20rpx 20rpx;
border-radius: 42rpx;
margin-right: 22rpx;
background: linear-gradient(155deg, rgba(255,255,255,0.88), rgba(245,255,250,0.68));
border: 2rpx solid rgba(255,255,255,0.92);
overflow: hidden;
vertical-align: top;
white-space: normal;
box-shadow: 0 22rpx 52rpx rgba(53,214,166,0.12);
}
.ri.top {
width: 286rpx;
min-height: 416rpx;
background: linear-gradient(155deg, rgba(255,255,255,0.94), rgba(255,246,219,0.74));
}
.ri.lv-s, .ri.lv-sss {
border-color: rgba(255,122,89,0.48);
box-shadow: 0 24rpx 58rpx rgba(255,122,89,0.14);
}
.ri.lv-a {
border-color: rgba(255,184,77,0.52);
}
.ri-scan {
position: absolute;
top: 0;
left: -60%;
width: 60%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.62), transparent);
animation: scan 4.2s linear infinite;
}
@keyframes scan {
0% { left: -60%; }
100% { left: 120%; }
}
.ri-topline {
position: relative;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.ri-rank {
height: 42rpx;
line-height: 42rpx;
padding: 0 16rpx;
border-radius: 999rpx;
background: rgba(255,255,255,0.82);
color: #22B889;
border: 1rpx solid rgba(255,255,255,0.96);
box-shadow: 0 8rpx 18rpx rgba(53,214,166,0.1);
font-size: 22rpx;
font-weight: 900;
}
.ri-self {
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
font-size: 18rpx;
font-weight: 900;
padding: 4rpx 12rpx;
border-radius: 999rpx;
}
.ri-avatar-wrap {
position: relative;
margin-top: 26rpx;
width: 132rpx;
height: 132rpx;
display: flex;
align-items: center;
justify-content: center;
}
.ri-ring {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
border-radius: 50%;
border: 3rpx dashed rgba(53,214,166,0.42);
animation: ringRotate 10s linear infinite;
}
@keyframes ringRotate {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.ri-avatar {
width: 106rpx;
height: 106rpx;
border-radius: 34rpx;
border: 5rpx solid rgba(255,255,255,0.92);
box-shadow: 0 16rpx 34rpx rgba(18,52,47,0.14);
}
.ri-mid {
position: relative;
width: 100%;
margin-top: 18rpx;
text-align: center;
overflow: hidden;
}
.ri-name {
color: #12342F;
font-size: 28rpx;
font-weight: 900;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.ri-bounty {
margin-top: 8rpx;
color: #22B889;
font-size: 24rpx;
font-weight: 900;
font-family: DIN, Arial, sans-serif;
}
.ri-tags {
margin-top: 14rpx;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
}
.ri-danger {
color: #FF7A59;
font-size: 20rpx;
font-weight: 800;
border: 1rpx solid rgba(255,122,89,0.32);
padding: 4rpx 12rpx;
border-radius: 999rpx;
background: rgba(255,122,89,0.08);
}
.ri-keep {
color: #7E9691;
font-size: 20rpx;
margin-top: 8rpx;
}
.ri-action {
margin: 18rpx auto 0;
height: 58rpx;
line-height: 58rpx;
border-radius: 999rpx;
background: linear-gradient(135deg, #FF7A59, #FFB84D);
color: #fff;
font-size: 23rpx;
font-weight: 900;
box-shadow: 0 14rpx 28rpx rgba(255,122,89,0.22);
}
.ri-action.disabled {
background: rgba(18,52,47,0.08);
color: #9AA9A5;
box-shadow: none;
}
.ri-extra {
margin-top: 8rpx;
color: #FFB84D;
font-size: 20rpx;
font-weight: 800;
text-align: center;
}
</style>

346
package1/planet/drawResult.vue

@ -0,0 +1,346 @@
<template>
<view class="dr" :style="{'--sb': statusBarHeight + 'px'}">
<view class="dr-bg"></view>
<view class="nav" :style="{paddingTop: statusBarHeight + 'px'}">
<view class="nav-back" @tap="goBack"><text class="nav-back-icon"></text></view>
<view class="nav-title">开奖结果</view>
</view>
<scroll-view scroll-y class="dr-scroll" :style="{paddingTop: (statusBarHeight + 44) + 'px'}">
<!-- 切换 -->
<view class="dr-tabs">
<view class="dr-tab" :class="{on: tab===0}" @tap="tab=0">最近开奖</view>
<view class="dr-tab" :class="{on: tab===1}" @tap="tab=1">我的中奖</view>
</view>
<block v-if="tab===0">
<view v-if="record" class="dr-card">
<view class="dr-period"> {{record.periodNo}} </view>
<view class="dr-amount"><text></text>{{Number(record.poolAmount||0).toFixed(2)}}</view>
<view class="dr-meta">
<text>参与 {{record.joinCount}} </text>
<text>·</text>
<text>中奖 {{record.winnerCount}} </text>
</view>
<view class="dr-time">开奖时间 {{record.drawTime}}</view>
</view>
<view v-else class="dr-empty">本商圈暂未开奖敬请期待 🌌</view>
<view class="dr-winners" v-if="winners && winners.length">
<view class="dr-sec-title">中奖名单</view>
<view class="dr-winner" v-for="(w, i) in winners" :key="i">
<text class="dr-w-level" :class="'lv'+w.rewardLevel">{{w.levelName}}</text>
<text class="dr-w-name">{{w.userName || '神秘居民'}}</text>
<text class="dr-w-amount">{{Number(w.amount||0).toFixed(2)}}</text>
</view>
</view>
</block>
<block v-else>
<view v-if="myWinning && myWinning.length" class="dr-my">
<view class="dr-my-item" v-for="(w, i) in myWinning" :key="i">
<view class="dr-my-left">
<text class="dr-w-level" :class="'lv'+w.rewardLevel">{{w.levelName}}</text>
<text class="dr-my-period"> {{w.periodNo}} </text>
</view>
<text class="dr-my-amount">{{Number(w.amount||0).toFixed(2)}}</text>
<view class="dr-my-btn" :class="{done: w.isReceived===1}" @tap="receive(w)">
{{w.isReceived===1 ? '已领取' : '领取'}}
</view>
</view>
</view>
<view v-else class="dr-empty">还没有中奖记录继续攒券瓜分奖池</view>
</block>
<view style="height:60rpx;"></view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
statusBarHeight: 20,
tab: 0,
userId: '',
regionId: '',
record: null,
winners: [],
myWinning: []
}
},
onLoad() {
const sys = uni.getSystemInfoSync()
this.statusBarHeight = sys.statusBarHeight || 20
this.userId = uni.getStorageSync('id') || ''
try {
const area = uni.getStorageSync('area')
if (area) this.regionId = JSON.parse(area).id || ''
} catch (e) {}
if (!this.regionId) {
this.tui.toast('未获取到校区信息,开奖仅限本区域内进行')
return
}
this.loadResult()
this.loadMyWinning()
},
methods: {
loadResult() {
this.tui.request('/app/planet/draw/result', 'POST', {
userId: this.userId,
regionId: this.regionId
}).then((res) => {
if (res.code == 200 && res.result) {
this.record = res.result.record
this.winners = res.result.winners || []
}
})
},
loadMyWinning() {
this.tui.request('/app/planet/draw/myWinning', 'POST', {
userId: this.userId,
regionId: this.regionId
}, false, false, true).then((res) => {
if (res.code == 200) this.myWinning = res.result || []
})
},
receive(w) {
if (w.isReceived === 1) return
this.tui.request('/app/planet/draw/receive', 'POST', {
userId: this.userId,
regionId: this.regionId,
winnerId: w.id
}).then((res) => {
this.tui.toast(res.message, 1500, res.code == 200)
if (res.code == 200) this.loadMyWinning()
})
},
goBack() {
uni.navigateBack({ delta: 1 })
}
}
}
</script>
<style lang="scss" scoped>
.dr {
min-height: 100vh;
background: linear-gradient(155deg, #F3FFF4 0%, #EAF8FF 46%, #F7EEFF 100%);
color: #12342F;
}
.dr-bg {
position: fixed;
top: 0; left: 0; right: 0; height: 500rpx;
background:
radial-gradient(circle at 78% 12%, rgba(79,183,255,0.28), transparent 42%),
radial-gradient(circle at 12% 30%, rgba(53,214,166,0.22), transparent 40%);
pointer-events: none;
}
.nav {
position: fixed;
top: 0; left: 0; right: 0;
height: 44px;
z-index: 20;
display: flex;
align-items: flex-end;
justify-content: center;
background: linear-gradient(180deg, rgba(243,255,244,0.9), rgba(243,255,244,0));
}
.nav-back {
position: absolute;
left: 20rpx;
bottom: 0;
height: 44px;
width: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.nav-back-icon {
color: #12342F;
font-size: 48rpx;
}
.nav-title {
color: #12342F;
font-size: 34rpx;
font-weight: 800;
padding-bottom: 6rpx;
}
.dr-scroll {
height: 100vh;
box-sizing: border-box;
padding: 0 32rpx;
}
.dr-tabs {
display: flex;
margin-top: 20rpx;
background: rgba(255,255,255,0.68);
border-radius: 36rpx;
padding: 6rpx;
border: 1rpx solid rgba(255,255,255,0.92);
box-shadow: 0 16rpx 34rpx rgba(53,214,166,0.1);
}
.dr-tab {
flex: 1;
height: 64rpx;
line-height: 64rpx;
text-align: center;
border-radius: 32rpx;
color: #42635E;
font-size: 26rpx;
font-weight: 700;
}
.dr-tab.on {
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
font-weight: 900;
}
.dr-card {
margin-top: 28rpx;
padding: 40rpx;
border-radius: 42rpx;
background: linear-gradient(150deg, rgba(255,255,255,0.9), rgba(255,248,222,0.72));
border: 2rpx solid rgba(255,255,255,0.92);
text-align: center;
box-shadow: 0 24rpx 60rpx rgba(255,184,77,0.14);
}
.dr-period {
color: #7E9691;
font-size: 24rpx;
font-weight: 700;
}
.dr-amount {
margin-top: 12rpx;
color: #22B889;
font-size: 72rpx;
font-weight: 800;
}
.dr-amount text {
font-size: 36rpx;
}
.dr-meta {
margin-top: 14rpx;
color: #42635E;
font-size: 24rpx;
}
.dr-meta text {
margin: 0 8rpx;
}
.dr-time {
margin-top: 10rpx;
color: #7E9691;
font-size: 22rpx;
}
.dr-empty {
margin-top: 120rpx;
text-align: center;
color: #7E9691;
font-size: 26rpx;
}
.dr-sec-title {
margin: 32rpx 0 18rpx;
color: #12342F;
font-size: 30rpx;
font-weight: 900;
}
.dr-winner {
display: flex;
align-items: center;
padding: 22rpx 24rpx;
margin-bottom: 14rpx;
border-radius: 28rpx;
background: rgba(255,255,255,0.72);
border: 1rpx solid rgba(255,255,255,0.9);
box-shadow: 0 12rpx 28rpx rgba(53,214,166,0.08);
}
.dr-w-level {
font-size: 22rpx;
padding: 2rpx 14rpx;
border-radius: 8rpx;
background: rgba(53,214,166,0.14);
color: #22B889;
}
.dr-w-level.lv1 { background: rgba(255,122,89,0.14); color: #FF7A59; }
.dr-w-level.lv2 { background: rgba(255,184,77,0.18); color: #C98716; }
.dr-w-name {
flex: 1;
margin-left: 18rpx;
color: #12342F;
font-size: 26rpx;
font-weight: 700;
}
.dr-w-amount {
color: #22B889;
font-size: 28rpx;
font-weight: 700;
}
.dr-my-item {
display: flex;
align-items: center;
padding: 26rpx 24rpx;
margin-top: 18rpx;
border-radius: 30rpx;
background: rgba(255,255,255,0.74);
border: 1rpx solid rgba(255,255,255,0.92);
box-shadow: 0 12rpx 30rpx rgba(53,214,166,0.08);
}
.dr-my-left {
flex: 1;
display: flex;
flex-direction: column;
}
.dr-my-period {
margin-top: 10rpx;
color: #7E9691;
font-size: 22rpx;
}
.dr-my-amount {
color: #22B889;
font-size: 32rpx;
font-weight: 800;
margin-right: 20rpx;
}
.dr-my-btn {
padding: 0 26rpx;
height: 60rpx;
line-height: 60rpx;
border-radius: 30rpx;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
font-size: 24rpx;
font-weight: 700;
}
.dr-my-btn.done {
background: rgba(18,52,47,0.08);
color: #9AA9A5;
}
</style>

717
package1/planet/index.vue

@ -0,0 +1,717 @@
<template>
<view class="planet" :style="{'--statusbar': statusBarHeight + 'px'}">
<view class="planet-bg">
<view class="bg-aura bg-aura-a"></view>
<view class="bg-aura bg-aura-b"></view>
<view class="bg-aura bg-aura-c"></view>
<view class="bg-space-dust bg-space-dust-a"></view>
<view class="bg-space-dust bg-space-dust-b"></view>
<view class="bg-shooting bg-shooting-a"></view>
<view class="bg-shooting bg-shooting-b"></view>
<view class="bg-orbit bg-orbit-a"></view>
<view class="bg-orbit bg-orbit-b"></view>
<view class="bg-star" v-for="(s, i) in stars" :key="i" :style="s"></view>
<view class="bg-planet bg-planet-a"></view>
<view class="bg-planet bg-planet-b"></view>
</view>
<view class="nav" :style="{paddingTop: statusBarHeight + 'px'}">
<view class="nav-back" @tap="goBack">
<text class="nav-back-icon"></text>
</view>
<view class="nav-title">白嫖星球</view>
<view class="nav-right">
<text class="nav-link" @tap="goTicketLog">明细</text>
<text class="nav-link nav-link-main" @tap="goDrawResult">开奖</text>
</view>
</view>
<scroll-view scroll-y class="planet-scroll" :style="{paddingTop: (statusBarHeight + 44) + 'px'}">
<!-- 骨架屏 -->
<view v-if="loading" class="skeleton">
<view class="sk-block sk-header"></view>
<view class="sk-block sk-me"></view>
<view class="sk-block sk-row"></view>
<view class="sk-block sk-list"></view>
</view>
<block v-else>
<planet-header :data="home"></planet-header>
<planet-news :list="home.newsList"></planet-news>
<planet-me
:data="home"
@sign="onSign"
@openbox="openBox">
</planet-me>
<planet-box
:available="home.boxAvailable"
:opening="boxOpening"
@open="openBox">
</planet-box>
<planet-tasks
:tasks="home.tasks"
@claim="onClaimTask">
</planet-tasks>
<planet-rank
:list="home.rankList"
:remain-hunt="home.remainHunt"
@hunt="onHunt">
</planet-rank>
<buff-shop
:list="home.buffShop"
:my-ticket="home.myTicketCount"
@buy="onBuyBuff">
</buff-shop>
<view class="bottom-space"></view>
</block>
</scroll-view>
<!-- 追捕浮层 -->
<hunt-modal
:show="huntModal.show"
:phase="huntModal.phase"
:result="huntModal.result"
:target="huntModal.target"
@close="closeHunt">
</hunt-modal>
<view v-if="boxResult.show" class="box-result-mask" @tap="boxResult.show=false">
<view class="box-result" @tap.stop>
<view class="box-result-burst"></view>
<view class="box-result-icon">BLIND BOX</view>
<view class="box-result-title">{{boxResult.data.rewardName}}</view>
<view class="box-result-desc">{{boxResult.data.message}}</view>
<view class="box-result-btn" @tap="boxResult.show=false">收下了</view>
</view>
</view>
</view>
</template>
<script>
import planetHeader from '@/package1/components/planet/planet-header.vue'
import planetMe from '@/package1/components/planet/planet-me.vue'
import planetTasks from '@/package1/components/planet/planet-tasks.vue'
import planetBox from '@/package1/components/planet/planet-box.vue'
import planetRank from '@/package1/components/planet/planet-rank.vue'
import huntModal from '@/package1/components/planet/hunt-modal.vue'
import buffShop from '@/package1/components/planet/buff-shop.vue'
import planetNews from '@/package1/components/planet/planet-news.vue'
export default {
components: {
planetHeader,
planetMe,
planetTasks,
planetBox,
planetRank,
huntModal,
buffShop,
planetNews
},
data() {
return {
statusBarHeight: 20,
loading: true,
userId: '',
regionId: '',
nickname: '',
avatar: '',
college: '',
home: {
poolAmount: 0,
joinCount: 0,
myTicketCount: 0,
myRankNo: 0,
remainHunt: 3,
tasks: [],
rankList: [],
buffShop: [],
newsList: [],
myBuffs: []
},
boxOpening: false,
boxResult: {
show: false,
data: {}
},
huntModal: {
show: false,
phase: 'searching',
result: null,
target: {}
}
}
},
computed: {
stars() {
const arr = []
for (let n = 1; n <= 46; n++) {
const top = (n * 29) % 100
const left = (n * 47) % 100
const delay = (n % 12) / 2
const size = (n % 4) + 1
arr.push(`top:${top}%;left:${left}%;width:${size}px;height:${size}px;animation-delay:${delay}s;`)
}
return arr
}
},
onLoad() {
const sys = uni.getSystemInfoSync()
this.statusBarHeight = sys.statusBarHeight || 20
this.userId = uni.getStorageSync('id') || ''
this.nickname = uni.getStorageSync('nickName') || uni.getStorageSync('nickname') || ''
this.avatar = uni.getStorageSync('avatarUrl') || uni.getStorageSync('avatar') || ''
try {
const area = uni.getStorageSync('area')
if (area) {
this.regionId = JSON.parse(area).id || ''
}
} catch (e) {}
this.loadHome()
},
methods: {
loadHome(silent) {
if (!this.userId) {
this.tui.toast('请先登录')
return
}
if (!this.regionId) {
this.loading = false
this.tui.toast('未获取到校区信息,白嫖星球仅限本区域内进行')
return
}
if (!silent) this.loading = true
this.tui.request('/app/planet/home', 'POST', {
userId: this.userId,
regionId: this.regionId,
nickname: this.nickname,
avatar: this.avatar,
college: this.college
}, false, false, true).then((res) => {
this.loading = false
if (res.code == 200 && res.result) {
this.home = res.result
} else if (res.message) {
this.tui.toast(res.message)
}
}).catch(() => {
this.loading = false
})
},
onSign() {
this.tui.request('/app/planet/sign', 'POST', {
userId: this.userId,
regionId: this.regionId
}).then((res) => {
this.tui.toast(res.message, 1500, res.code == 200)
if (res.code == 200) this.loadHome(true)
})
},
openBox() {
if (!this.home.boxAvailable || this.boxOpening) return
this.boxOpening = true
this.tui.request('/app/planet/box/open', 'POST', {
userId: this.userId,
regionId: this.regionId
}).then((res) => {
setTimeout(() => {
this.boxOpening = false
if (res.code == 200 && res.result) {
this.boxResult.data = res.result
this.boxResult.show = true
this.loadHome(true)
} else {
this.tui.toast(res.message)
}
}, 800)
}).catch(() => {
this.boxOpening = false
})
},
onClaimTask(task) {
if (task.code === 'sign') {
this.onSign()
return
}
this.tui.request('/app/planet/task/claim', 'POST', {
userId: this.userId,
regionId: this.regionId,
taskCode: task.code
}).then((res) => {
this.tui.toast(res.message, 1500, res.code == 200)
if (res.code == 200) this.loadHome(true)
})
},
onHunt(item) {
if (item.self) {
this.tui.toast('不能追捕自己')
return
}
if (item.shielded) {
this.tui.toast('目标已开启防护罩')
return
}
if (this.home.remainHunt <= 0) {
this.tui.toast('今日追捕次数已用完')
return
}
this.huntModal.show = true
this.huntModal.phase = 'searching'
this.huntModal.result = null
this.huntModal.target = item
//
setTimeout(() => { this.huntModal.phase = 'locking' }, 900)
setTimeout(() => { this.huntModal.phase = 'chasing' }, 1800)
this.tui.request('/app/planet/hunt', 'POST', {
userId: this.userId,
regionId: this.regionId,
toUserId: item.userId
}, false, false, true).then((res) => {
setTimeout(() => {
if (res.code == 200 && res.result) {
this.huntModal.result = res.result
this.huntModal.phase = 'result'
this.loadHome(true)
} else {
this.huntModal.show = false
this.tui.toast(res.message)
}
}, 2600)
}).catch(() => {
this.huntModal.show = false
})
},
closeHunt() {
this.huntModal.show = false
this.huntModal.result = null
},
onBuyBuff(buff) {
if (buff.active) {
this.tui.toast('该增益正在生效中')
return
}
if (this.home.myTicketCount < buff.costTickets) {
this.tui.toast('星球券不足')
return
}
this.tui.modal('确认购买', `消耗 ${buff.costTickets} 张星球券购买「${buff.name}」?`, true, (ok) => {
if (!ok) return
this.tui.request('/app/planet/buff/buy', 'POST', {
userId: this.userId,
regionId: this.regionId,
buffId: buff.id
}).then((res) => {
this.tui.toast(res.code == 200 ? '购买成功' : res.message, 1500, res.code == 200)
if (res.code == 200) this.loadHome(true)
})
})
},
goDrawResult() {
uni.navigateTo({
url: '/package1/planet/drawResult'
})
},
goTicketLog() {
uni.navigateTo({
url: '/package1/planet/ticketLog'
})
},
goBack() {
uni.navigateBack({
delta: 1,
fail() {
uni.switchTab({ url: '/pages/index/index' })
}
})
}
}
}
</script>
<style lang="scss" scoped>
.planet {
min-height: 100vh;
background:
radial-gradient(circle at 50% 260rpx, rgba(255,255,255,0.72), rgba(255,255,255,0) 360rpx),
linear-gradient(155deg, #F3FFF4 0%, #EAF8FF 42%, #F7EEFF 76%, #FFF8DE 100%);
position: relative;
overflow: hidden;
color: #12342F;
}
.planet-bg {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 0;
overflow: hidden;
pointer-events: none;
}
.bg-aura {
position: absolute;
border-radius: 50%;
filter: blur(12rpx);
opacity: 0.72;
animation: auraFloat 8s ease-in-out infinite;
}
.bg-aura-a {
width: 520rpx;
height: 520rpx;
right: -160rpx;
top: 74rpx;
background: radial-gradient(circle, rgba(79,183,255,0.34), rgba(79,183,255,0));
}
.bg-aura-b {
width: 460rpx;
height: 460rpx;
left: -180rpx;
top: 360rpx;
background: radial-gradient(circle, rgba(53,214,166,0.35), rgba(53,214,166,0));
animation-delay: -2.5s;
}
.bg-aura-c {
width: 420rpx;
height: 420rpx;
right: -110rpx;
top: 860rpx;
background: radial-gradient(circle, rgba(143,124,255,0.22), rgba(143,124,255,0));
animation-delay: -4s;
}
.bg-space-dust {
position: absolute;
border-radius: 50%;
background:
radial-gradient(circle, rgba(255,255,255,0.9) 0 2rpx, transparent 3rpx),
radial-gradient(circle, rgba(53,214,166,0.5) 0 2rpx, transparent 3rpx),
radial-gradient(circle, rgba(143,124,255,0.38) 0 2rpx, transparent 3rpx);
background-size: 86rpx 92rpx, 124rpx 116rpx, 156rpx 148rpx;
opacity: 0.38;
animation: dustDrift 16s linear infinite;
}
.bg-space-dust-a {
left: -120rpx;
top: 120rpx;
width: 980rpx;
height: 860rpx;
}
.bg-space-dust-b {
left: -180rpx;
top: 900rpx;
width: 1000rpx;
height: 760rpx;
animation-delay: -6s;
opacity: 0.24;
}
@keyframes dustDrift {
0% { transform: translate3d(0, 0, 0); }
100% { transform: translate3d(-86rpx, 92rpx, 0); }
}
.bg-shooting {
position: absolute;
width: 180rpx;
height: 3rpx;
border-radius: 999rpx;
background: linear-gradient(90deg, rgba(255,255,255,0), rgba(255,255,255,0.9), rgba(79,183,255,0.25));
transform: rotate(-30deg);
animation: shooting 5.6s linear infinite;
}
.bg-shooting-a {
right: -160rpx;
top: 240rpx;
}
.bg-shooting-b {
right: -160rpx;
top: 780rpx;
width: 130rpx;
animation-delay: -2.8s;
opacity: 0.72;
}
@keyframes shooting {
0% { transform: translate3d(180rpx, -80rpx, 0) rotate(-30deg); opacity: 0; }
10% { opacity: 1; }
60% { opacity: 1; }
100% { transform: translate3d(-760rpx, 300rpx, 0) rotate(-30deg); opacity: 0; }
}
@keyframes auraFloat {
0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
50% { transform: translate3d(18rpx, -34rpx, 0) scale(1.06); }
}
.bg-orbit {
position: absolute;
border: 2rpx solid rgba(255,255,255,0.72);
border-radius: 50%;
transform: rotate(-18deg);
}
.bg-orbit-a {
width: 920rpx;
height: 300rpx;
left: -60rpx;
top: 250rpx;
opacity: 0.34;
}
.bg-orbit-b {
width: 720rpx;
height: 210rpx;
right: -190rpx;
top: 680rpx;
opacity: 0.26;
}
.bg-star {
position: absolute;
background: rgba(255,255,255,0.92);
border-radius: 50%;
box-shadow: 0 0 12rpx rgba(79,183,255,0.45);
opacity: 0.55;
animation: twinkle 3.6s ease-in-out infinite;
}
@keyframes twinkle {
0%, 100% { opacity: 0.28; transform: scale(0.8); }
50% { opacity: 0.85; transform: scale(1.28); }
}
.bg-planet {
position: absolute;
border-radius: 50%;
opacity: 0.8;
box-shadow: inset -18rpx -22rpx 42rpx rgba(18,52,47,0.08), 0 22rpx 60rpx rgba(53,214,166,0.18);
}
.bg-planet-a {
width: 120rpx;
height: 120rpx;
right: 34rpx;
top: 106rpx;
background: radial-gradient(circle at 32% 28%, #FFFFFF 0%, #BAF7CF 32%, #7DE2FF 100%);
animation: float 8.5s ease-in-out infinite;
}
.bg-planet-b {
width: 92rpx;
height: 92rpx;
left: 24rpx;
top: 760rpx;
background: radial-gradient(circle at 30% 30%, #FFFFFF 0%, #FFE7A8 36%, #FFB7D1 100%);
animation: float 10s ease-in-out infinite reverse;
}
@keyframes float {
0%, 100% { transform: translateY(0) translateX(0); }
50% { transform: translateY(-24rpx) translateX(12rpx); }
}
.nav {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 44px;
z-index: 20;
display: flex;
align-items: center;
justify-content: center;
box-sizing: content-box;
background: linear-gradient(180deg, rgba(243,255,244,0.88), rgba(243,255,244,0));
}
.nav-back {
position: absolute;
left: 20rpx;
bottom: 0;
height: 44px;
width: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.nav-back-icon {
color: #12342F;
font-size: 48rpx;
font-weight: 300;
}
.nav-title {
color: #12342F;
font-size: 34rpx;
font-weight: 800;
letter-spacing: 1rpx;
align-self: flex-end;
padding-bottom: 6rpx;
}
.nav-right {
position: absolute;
right: 24rpx;
bottom: 6rpx;
display: flex;
align-items: center;
}
.nav-link {
font-size: 26rpx;
color: #42635E;
margin-left: 16rpx;
padding: 8rpx 16rpx;
border-radius: 999rpx;
background: rgba(255,255,255,0.62);
border: 1rpx solid rgba(255,255,255,0.9);
box-shadow: 0 10rpx 24rpx rgba(53,214,166,0.1);
}
.nav-link-main {
color: #FFFFFF;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
border-color: rgba(255,255,255,0.78);
}
.planet-scroll {
position: relative;
z-index: 5;
height: 100vh;
box-sizing: border-box;
padding-left: 22rpx;
padding-right: 22rpx;
}
.bottom-space {
height: 60rpx;
}
/* 骨架屏 */
.skeleton {
padding-top: 22rpx;
}
.sk-block {
border-radius: 36rpx;
background: linear-gradient(90deg, rgba(255,255,255,0.45) 25%, rgba(255,255,255,0.9) 37%, rgba(255,255,255,0.45) 63%);
background-size: 400% 100%;
animation: shimmer 1.4s ease infinite;
margin-bottom: 24rpx;
box-shadow: 0 18rpx 42rpx rgba(53,214,166,0.1);
}
.sk-header { height: 520rpx; border-radius: 48rpx; }
.sk-me { height: 150rpx; }
.sk-row { height: 220rpx; }
.sk-list { height: 330rpx; }
@keyframes shimmer {
0% { background-position: 100% 50%; }
100% { background-position: 0 50%; }
}
/* 宝箱结果 */
.box-result-mask {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(18,52,47,0.28);
backdrop-filter: blur(8px);
z-index: 60;
display: flex;
align-items: center;
justify-content: center;
}
.box-result {
position: relative;
width: 540rpx;
padding: 62rpx 40rpx 42rpx;
border-radius: 48rpx;
background: linear-gradient(155deg, rgba(255,255,255,0.92), rgba(244,255,249,0.78));
border: 2rpx solid rgba(255,255,255,0.9);
box-shadow: 0 34rpx 80rpx rgba(53,214,166,0.24);
text-align: center;
overflow: hidden;
}
.box-result-burst {
position: absolute;
top: -60rpx;
left: 50%;
transform: translateX(-50%);
width: 400rpx;
height: 400rpx;
background: radial-gradient(circle, rgba(255,184,77,0.36), transparent 62%);
animation: pulse 1.6s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: translateX(-50%) scale(0.9); opacity: 0.6; }
50% { transform: translateX(-50%) scale(1.1); opacity: 1; }
}
.box-result-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 190rpx;
height: 190rpx;
border-radius: 46rpx;
background: linear-gradient(145deg, #FFFFFF, #DDF8FF 48%, #E8DFFF);
color: #22B889;
font-size: 24rpx;
font-weight: 900;
letter-spacing: 1rpx;
position: relative;
animation: bounce 1s ease infinite;
box-shadow: inset -16rpx -20rpx 34rpx rgba(143,124,255,0.16), 0 22rpx 48rpx rgba(79,183,255,0.18);
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-12rpx); }
}
.box-result-title {
margin-top: 20rpx;
color: #12342F;
font-size: 36rpx;
font-weight: 800;
}
.box-result-desc {
margin-top: 12rpx;
color: #42635E;
font-size: 26rpx;
}
.box-result-btn {
margin-top: 36rpx;
height: 80rpx;
line-height: 80rpx;
border-radius: 40rpx;
background: linear-gradient(135deg, #35D6A6, #4FB7FF);
color: #FFFFFF;
font-size: 30rpx;
font-weight: 700;
box-shadow: 0 18rpx 36rpx rgba(53,214,166,0.25);
}
</style>

385
package1/planet/ticketLog.vue

@ -0,0 +1,385 @@
<template>
<view class="tl" :style="{'--sb': statusBarHeight + 'px'}">
<view class="tl-bg"></view>
<view class="nav" :style="{paddingTop: statusBarHeight + 'px'}">
<view class="nav-back" @tap="goBack"><text class="nav-back-icon"></text></view>
<view class="nav-title">星球券明细</view>
</view>
<scroll-view scroll-y class="tl-scroll" :style="{paddingTop: (statusBarHeight + 44) + 'px'}"
@scrolltolower="loadMore">
<!-- 汇总卡片 -->
<view class="tl-sum">
<view class="tl-sum-glow"></view>
<view class="tl-sum-label">当前持有星球券</view>
<view class="tl-sum-value">
<text class="tl-sum-num">{{balance}}</text>
<text class="tl-sum-unit"></text>
</view>
<view class="tl-sum-tip">星球券永久有效囤越多瓜分越多</view>
</view>
<!-- 骨架屏 -->
<view v-if="loading && list.length===0" class="tl-sk">
<view class="tl-sk-item" v-for="n in 6" :key="n"></view>
</view>
<!-- 明细列表 -->
<view v-else-if="list.length" class="tl-list">
<view class="tl-item" v-for="(item, i) in list" :key="i">
<view class="tl-icon" :class="'t-'+(item.type||'other')">{{typeIcon(item.type)}}</view>
<view class="tl-mid">
<text class="tl-name">{{item.remark || typeName(item.type)}}</text>
<text class="tl-time">{{item.createTime}}</text>
</view>
<view class="tl-right">
<text class="tl-change" :class="item.changeCount >= 0 ? 'up' : 'down'">
{{item.changeCount >= 0 ? '+' : ''}}{{item.changeCount}}
</text>
<text class="tl-balance"> {{item.balance}}</text>
</view>
</view>
<view class="tl-foot">
<text v-if="loading">加载中</text>
<text v-else-if="noMore"> 没有更多了 </text>
<text v-else @tap="loadMore">点击加载更多</text>
</view>
</view>
<!-- 空态 -->
<view v-else class="tl-empty">
<text class="tl-empty-icon">NO LOG</text>
<text class="tl-empty-text">还没有星球券记录</text>
<text class="tl-empty-tip">完成任务下单邀请好友都能获得星球券</text>
</view>
<view style="height:60rpx;"></view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
statusBarHeight: 20,
userId: '',
regionId: '',
balance: 0,
list: [],
pageNumber: 1,
pageSize: 15,
total: 0,
loading: false,
noMore: false
}
},
onLoad() {
const sys = uni.getSystemInfoSync()
this.statusBarHeight = sys.statusBarHeight || 20
this.userId = uni.getStorageSync('id') || ''
try {
const area = uni.getStorageSync('area')
if (area) this.regionId = JSON.parse(area).id || ''
} catch (e) {}
this.loadPage(true)
},
methods: {
loadPage(reset) {
if (this.loading) return
if (!this.userId) {
this.tui.toast('请先登录')
return
}
if (reset) {
this.pageNumber = 1
this.noMore = false
}
this.loading = true
this.tui.request('/app/planet/ticket/log', 'POST', {
userId: this.userId,
regionId: this.regionId,
pageNumber: this.pageNumber,
pageSize: this.pageSize
}, false, false, true).then((res) => {
this.loading = false
if (res.code == 200 && res.result) {
const records = res.result.records || []
this.total = res.result.total || 0
if (reset) {
this.list = records
//
if (records.length) this.balance = records[0].balance
} else {
this.list = this.list.concat(records)
}
if (this.list.length >= this.total || records.length < this.pageSize) {
this.noMore = true
}
}
}).catch(() => {
this.loading = false
})
},
loadMore() {
if (this.loading || this.noMore) return
this.pageNumber++
this.loadPage(false)
},
typeIcon(type) {
const map = {
order: '食', group: '团', invite: '邀', sign: '签',
box: '盒', hunt: '追', buff: '能', draw: '奖'
}
return map[type] || '券'
},
typeName(type) {
const map = {
order: '外卖订单', group: '团购订单', invite: '邀请好友', sign: '每日签到',
box: '幸运宝箱', hunt: '星际追捕', buff: '购买增益', draw: '开奖瓜分'
}
return map[type] || '星球券变动'
},
goBack() {
uni.navigateBack({ delta: 1 })
}
}
}
</script>
<style lang="scss" scoped>
.tl {
min-height: 100vh;
background: linear-gradient(155deg, #F3FFF4 0%, #EAF8FF 48%, #F7EEFF 100%);
color: #12342F;
}
.tl-bg {
position: fixed;
top: 0; left: 0; right: 0; height: 520rpx;
background:
radial-gradient(circle at 78% 14%, rgba(53,214,166,0.26), transparent 44%),
radial-gradient(circle at 18% 32%, rgba(143,124,255,0.18), transparent 42%);
pointer-events: none;
}
.nav {
position: fixed;
top: 0; left: 0; right: 0;
height: 44px;
z-index: 20;
display: flex;
align-items: flex-end;
justify-content: center;
background: linear-gradient(180deg, rgba(243,255,244,0.9), rgba(243,255,244,0));
}
.nav-back {
position: absolute;
left: 20rpx;
bottom: 0;
height: 44px;
width: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
.nav-back-icon { color: #12342F; font-size: 48rpx; font-weight: 300; }
.nav-title {
color: #12342F;
font-size: 34rpx;
font-weight: 800;
letter-spacing: 1rpx;
padding-bottom: 6rpx;
}
.tl-scroll {
height: 100vh;
box-sizing: border-box;
padding: 0 28rpx;
}
/* 汇总卡 */
.tl-sum {
position: relative;
margin-top: 20rpx;
padding: 48rpx 40rpx 40rpx;
border-radius: 44rpx;
background: linear-gradient(150deg, rgba(255,255,255,0.9), rgba(226,255,241,0.7));
border: 2rpx solid rgba(255,255,255,0.92);
overflow: hidden;
box-shadow: 0 24rpx 62rpx rgba(53,214,166,0.14);
}
.tl-sum-glow {
position: absolute;
top: -120rpx; right: -80rpx;
width: 320rpx; height: 320rpx;
background: radial-gradient(circle, rgba(53,214,166,0.28), transparent 65%);
animation: tl-pulse 3s ease-in-out infinite;
}
@keyframes tl-pulse {
0%, 100% { transform: scale(0.85); opacity: 0.55; }
50% { transform: scale(1.1); opacity: 0.95; }
}
.tl-sum-label {
position: relative;
color: #42635E;
font-size: 26rpx;
font-weight: 700;
}
.tl-sum-value {
position: relative;
margin-top: 12rpx;
display: flex;
align-items: baseline;
}
.tl-sum-num {
font-size: 84rpx;
font-weight: 800;
color: #22B889;
font-family: DIN, Arial, sans-serif;
}
.tl-sum-unit {
margin-left: 12rpx;
font-size: 28rpx;
color: #42635E;
}
.tl-sum-tip {
position: relative;
margin-top: 16rpx;
font-size: 24rpx;
color: #7E9691;
}
/* 骨架 */
.tl-sk { margin-top: 28rpx; }
.tl-sk-item {
height: 120rpx;
border-radius: 22rpx;
margin-bottom: 20rpx;
background: linear-gradient(90deg, rgba(255,255,255,0.45) 25%, rgba(255,255,255,0.9) 37%, rgba(255,255,255,0.45) 63%);
background-size: 400% 100%;
animation: tl-shimmer 1.4s ease infinite;
}
@keyframes tl-shimmer {
0% { background-position: 100% 50%; }
100% { background-position: 0 50%; }
}
/* 列表 */
.tl-list { margin-top: 28rpx; }
.tl-item {
display: flex;
align-items: center;
padding: 26rpx 24rpx;
margin-bottom: 18rpx;
border-radius: 30rpx;
background: rgba(255,255,255,0.74);
border: 2rpx solid rgba(255,255,255,0.92);
box-shadow: 0 12rpx 30rpx rgba(53,214,166,0.08);
}
.tl-icon {
width: 76rpx;
height: 76rpx;
border-radius: 20rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 40rpx;
background: rgba(53,214,166,0.12);
flex-shrink: 0;
}
.tl-icon.t-buff, .tl-icon.t-hunt { background: rgba(255,122,89,0.14); }
.tl-icon.t-draw, .tl-icon.t-box { background: rgba(255,184,77,0.16); }
.tl-mid {
flex: 1;
margin-left: 22rpx;
display: flex;
flex-direction: column;
min-width: 0;
}
.tl-name {
color: #12342F;
font-size: 28rpx;
font-weight: 700;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.tl-time {
margin-top: 8rpx;
color: #7E9691;
font-size: 22rpx;
}
.tl-right {
display: flex;
flex-direction: column;
align-items: flex-end;
flex-shrink: 0;
}
.tl-change {
font-size: 34rpx;
font-weight: 700;
font-family: DIN, Arial, sans-serif;
}
.tl-change.up { color: #22B889; }
.tl-change.down { color: #FF7A59; }
.tl-balance {
margin-top: 6rpx;
font-size: 22rpx;
color: #7E9691;
}
.tl-foot {
text-align: center;
color: #7E9691;
font-size: 24rpx;
padding: 24rpx 0 8rpx;
}
/* 空态 */
.tl-empty {
margin-top: 140rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.tl-empty-icon {
width: 180rpx;
height: 180rpx;
line-height: 180rpx;
text-align: center;
border-radius: 50%;
background: rgba(255,255,255,0.72);
border: 2rpx solid rgba(255,255,255,0.92);
color: #35D6A6;
font-size: 28rpx;
font-weight: 900;
letter-spacing: 2rpx;
box-shadow: 0 20rpx 44rpx rgba(53,214,166,0.12);
}
.tl-empty-text { margin-top: 24rpx; color: #42635E; font-size: 30rpx; font-weight: 800; }
.tl-empty-tip { margin-top: 12rpx; color: #7E9691; font-size: 24rpx; }
</style>

2
package1/runErrand/runErrand.vue

@ -374,7 +374,7 @@
</template>
<script>
import deliveryTimeOp from '@/components/delivery-time-op/delivery-time-op.vue'
import deliveryTimeOp from '@/package1/components/delivery-time-op/delivery-time-op.vue'
import addressList from '@/package1/address/addressList.vue'
export default {
data() {

Loading…
Cancel
Save