diff --git a/api/appUser.js b/api/appUser.js
index 7eac8e3..682c5d9 100644
--- a/api/appUser.js
+++ b/api/appUser.js
@@ -150,6 +150,14 @@ export function redeemProductByPoints(user_id, product_id, quantity) {
return authRequest({ url: `/api/app/users/${user_id}/points/redeem-product`, method: 'POST', data: { product_id, quantity } })
}
+export function redeemItemCardByPoints(user_id, item_card_id, quantity = 1) {
+ return authRequest({ url: `/api/app/users/${user_id}/points/redeem-item-card`, method: 'POST', data: { item_card_id, quantity } })
+}
+
+export function getStoreItems(kind = 'product', page = 1, page_size = 20) {
+ return authRequest({ url: '/api/app/store/items', method: 'GET', data: { kind, page, page_size } })
+}
+
export function getTasks(page = 1, page_size = 20) {
return authRequest({ url: '/api/app/task-center/tasks', method: 'GET', data: { page, page_size } })
}
@@ -158,6 +166,10 @@ export function getTaskProgress(task_id, user_id) {
return authRequest({ url: `/api/app/task-center/tasks/${task_id}/progress/${user_id}`, method: 'GET' })
}
+export function claimTaskReward(task_id, user_id, tier_id) {
+ return authRequest({ url: `/api/app/task-center/tasks/${task_id}/claim/${user_id}`, method: 'POST', data: { tier_id } })
+}
+
export function getShipments(user_id, page = 1, page_size = 20) {
return authRequest({ url: `/api/app/users/${user_id}/shipments`, method: 'GET', data: { page, page_size } })
}
@@ -229,3 +241,15 @@ export function checkMatchingGame(game_id, total_pairs) {
data: { game_id, total_pairs }
})
}
+
+/**
+ * 支付成功后获取游戏数据
+ * @param {string} game_id - 游戏ID
+ */
+export function getMatchingGameCards(game_id) {
+ return authRequest({
+ url: '/api/app/matching/cards',
+ method: 'GET',
+ data: { game_id }
+ })
+}
diff --git a/components/YifanSelector.vue b/components/YifanSelector.vue
index f551815..3b192c1 100644
--- a/components/YifanSelector.vue
+++ b/components/YifanSelector.vue
@@ -25,7 +25,7 @@
-
+
已选 {{ selectedItems.length }} 个位置
@@ -62,10 +62,11 @@ const props = defineProps({
issueId: { type: [String, Number], required: true },
pricePerDraw: { type: Number, default: 0 },
disabled: { type: Boolean, default: false },
- disabledText: { type: String, default: '' }
+ disabledText: { type: String, default: '' },
+ hideActionBar: { type: Boolean, default: false } // 支持隐藏内置操作栏
})
-const emit = defineEmits(['payment-success'])
+const emit = defineEmits(['payment-success', 'selection-change'])
const choices = ref([])
const loading = ref(false)
@@ -162,6 +163,9 @@ function handleSelect(item) {
} else {
selectedItems.value.push(item)
}
+
+ // 通知父组件选中变化
+ emit('selection-change', [...selectedItems.value])
}
function handleBuy() {
@@ -320,6 +324,13 @@ async function onPaymentConfirm(paymentData) {
}
}
}
+
+// 暴露方法供外部调用
+defineExpose({
+ handleRandomOne,
+ handleBuy,
+ selectedItems: () => selectedItems.value
+})
diff --git a/components/activity/RecordsList.vue b/components/activity/RecordsList.vue
index 5b8a54c..89cae0f 100644
--- a/components/activity/RecordsList.vue
+++ b/components/activity/RecordsList.vue
@@ -1,5 +1,5 @@
-
+
@@ -12,9 +12,12 @@
-
- 📝
- {{ emptyText }}
+
+
+ 🎁
+
+ {{ emptyText }}
+ 快来参与活动获得奖品吧
@@ -87,21 +90,40 @@ defineProps({
color: $text-sub;
}
-.empty-state {
+/* 紧凑优雅的空状态 */
+.empty-state-compact {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
- padding: $spacing-xl;
- color: $text-sub;
+ padding: $spacing-lg $spacing-xl;
+ min-height: 200rpx;
+}
+
+.empty-icon-wrap {
+ width: 80rpx;
+ height: 80rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: linear-gradient(135deg, rgba($brand-primary, 0.1) 0%, rgba($accent-gold, 0.1) 100%);
+ border-radius: 50%;
+ margin-bottom: $spacing-md;
}
.empty-icon {
- font-size: 64rpx;
- margin-bottom: $spacing-sm;
+ font-size: 40rpx;
}
-.empty-text {
- font-size: $font-sm;
+.empty-title {
+ font-size: $font-md;
+ color: $text-sub;
+ font-weight: 600;
+ margin-bottom: 8rpx;
+}
+
+.empty-hint {
+ font-size: $font-xs;
+ color: $text-tertiary;
}
diff --git a/components/activity/RewardsPreview.vue b/components/activity/RewardsPreview.vue
index e049896..973c8ff 100644
--- a/components/activity/RewardsPreview.vue
+++ b/components/activity/RewardsPreview.vue
@@ -223,6 +223,7 @@ const rewardGroups = computed(() => {
justify-content: center;
padding: $spacing-xl;
color: $text-sub;
+ min-height: 300rpx; /* 防止切换时布局跳动 */
}
.empty-icon {
diff --git a/components/activity/RulesPopup.vue b/components/activity/RulesPopup.vue
new file mode 100644
index 0000000..d8b76e6
--- /dev/null
+++ b/components/activity/RulesPopup.vue
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+ 暂无活动规则
+
+
+
+
+
+
+
+
+
diff --git a/pages.json b/pages.json
index 81a4b4f..ce1e536 100644
--- a/pages.json
+++ b/pages.json
@@ -42,6 +42,30 @@
"navigationBarTitleText": "积分记录"
}
},
+ {
+ "path": "pages/coupons/index",
+ "style": {
+ "navigationBarTitleText": "我的优惠券"
+ }
+ },
+ {
+ "path": "pages/item-cards/index",
+ "style": {
+ "navigationBarTitleText": "我的道具卡"
+ }
+ },
+ {
+ "path": "pages/invites/index",
+ "style": {
+ "navigationBarTitleText": "邀请记录"
+ }
+ },
+ {
+ "path": "pages/tasks/index",
+ "style": {
+ "navigationBarTitleText": "任务中心"
+ }
+ },
{
"path": "pages/orders/index",
"style": {
diff --git a/pages/activity/duiduipeng/index.vue b/pages/activity/duiduipeng/index.vue
index 5f520f6..042a1d2 100644
--- a/pages/activity/duiduipeng/index.vue
+++ b/pages/activity/duiduipeng/index.vue
@@ -1,5 +1,6 @@
+
-
-
-
-
-
@@ -151,7 +184,9 @@ import ActivityTabs from '@/components/activity/ActivityTabs.vue'
import RewardsPreview from '@/components/activity/RewardsPreview.vue'
import RewardsPopup from '@/components/activity/RewardsPopup.vue'
import RecordsList from '@/components/activity/RecordsList.vue'
-import { getActivityDetail, getActivityIssues, getActivityIssueRewards, getUserCoupons, getItemCards, createWechatOrder, getMatchingCardTypes, createMatchingPreorder, checkMatchingGame, getIssueDrawLogs } from '../../../api/appUser'
+import RulesPopup from '@/components/activity/RulesPopup.vue'
+import CabinetPreviewPopup from '@/components/activity/CabinetPreviewPopup.vue'
+import { getActivityDetail, getActivityIssues, getActivityIssueRewards, getUserCoupons, getItemCards, createWechatOrder, getMatchingCardTypes, createMatchingPreorder, checkMatchingGame, getIssueDrawLogs, getMatchingGameCards } from '../../../api/appUser'
const detail = ref({})
const statusText = ref('')
@@ -173,6 +208,8 @@ const cardTypesLoading = ref(false)
const cardTypes = ref([])
const selectedCardTypeCode = ref('')
const rewardsVisible = ref(false)
+const rulesVisible = ref(false)
+const cabinetVisible = ref(false)
const resumeGame = ref(null)
const resumeIssueId = ref('')
const hasResumeGame = computed(() => {
@@ -613,15 +650,11 @@ function closeRewardsPopup() {
}
function showRules() {
- uni.showModal({
- title: '活动规则',
- content: detail.value.rules || '1. 选择卡牌类型进行对对碰\\n2. 每次抽取随机获得奖品\\n3. 奖池与概率以页面展示为准',
- showCancel: false
- })
+ rulesVisible.value = true
}
function goCabinet() {
- uni.switchTab({ url: '/pages/cabinet/index' })
+ cabinetVisible.value = true
}
function onPreviewBanner() {
@@ -823,6 +856,7 @@ function drawOne() {
function manualDraw() {
if (gameLoading.value) return
if (!canManualDraw.value) return
+ uni.vibrateShort({ type: 'light' })
drawOne()
chance.value = Math.max(0, Number(chance.value || 0) - 1)
pickedHandIndex.value = -1
@@ -854,6 +888,7 @@ async function autoDrawIfStuck() {
async function onCellTap(cell) {
if (gameLoading.value) return
if (!cell || cell.empty) return
+ uni.vibrateShort({ type: 'light' })
const hi = Number(cell.handIndex)
if (!Number.isFinite(hi) || hi < 0) return
@@ -912,6 +947,7 @@ async function finishAndReport() {
async function advanceOne() {
if (gameLoading.value) return
+ uni.vibrateShort({ type: 'light' })
const entry = gameEntry.value || null
const gameId = entry && entry.game_id ? String(entry.game_id) : ''
if (!gameId) return
@@ -969,6 +1005,7 @@ async function autoRun() {
}
async function onParticipate() {
+ uni.vibrateShort({ type: 'medium' })
const aid = activityId.value || ''
const iid = currentIssueId.value || ''
if (!aid || !iid) { uni.showToast({ title: '期数未选择', icon: 'none' }); return }
@@ -1014,6 +1051,7 @@ async function applyResumeEntry(entry) {
}
async function onResumeGame() {
+ uni.vibrateShort({ type: 'medium' })
const aid = activityId.value || ''
const latest = syncResumeGame(aid)
if (!latest || !latest.entry || !latest.entry.game_id) return
@@ -1060,7 +1098,7 @@ async function doDraw() {
return
}
- // 1. 调用 createMatchingPreorder 创建对对碰订单(同时返回游戏数据)
+ // 1. 调用 createMatchingPreorder 创建对对碰订单(不再返回游戏数据)
const preRes = await createMatchingPreorder({
issue_id: Number(iid),
position: String(selectedCardType.value.code || ''),
@@ -1069,12 +1107,12 @@ async function doDraw() {
})
if (!preRes) throw new Error('创建订单失败')
- // 2. 提取订单号和游戏数据
+ // 2. 提取订单号和游戏ID(注意:all_cards 不再在这里返回)
const orderNo = preRes.order_no || preRes.data?.order_no || preRes.result?.order_no || preRes.orderNo
if (!orderNo) throw new Error('未获取到订单号')
const gameId = preRes.game_id || preRes.data?.game_id || preRes.result?.game_id || preRes.gameId
- const allCards = normalizeAllCards(preRes.all_cards || preRes.data?.all_cards || preRes.result?.all_cards || [])
+ if (!gameId) throw new Error('未获取到游戏ID')
// 3. 用对对碰订单号调用微信支付
uni.showLoading({ title: '拉起支付...' })
@@ -1092,20 +1130,26 @@ async function doDraw() {
})
})
- // 4. 支付成功后保存游戏数据到本地缓存
- if (gameId) {
- writeMatchingGameCacheEntry(aid, iid, {
- game_id: String(gameId),
- position: String(selectedCardType.value.code || ''),
- all_cards: allCards,
- ts: Date.now()
- })
- }
+ // 4. 【关键】支付成功后,调用新接口获取游戏数据
+ uni.showLoading({ title: '加载游戏...' })
+ const cardsRes = await getMatchingGameCards(gameId)
+ if (!cardsRes) throw new Error('获取游戏数据失败')
+
+ const allCards = normalizeAllCards(cardsRes.all_cards || cardsRes.data?.all_cards || [])
+ if (!allCards.length) throw new Error('游戏数据为空')
+
+ // 5. 保存游戏数据到本地缓存
+ writeMatchingGameCacheEntry(aid, iid, {
+ game_id: String(gameId),
+ position: String(selectedCardType.value.code || ''),
+ all_cards: allCards,
+ ts: Date.now()
+ })
uni.hideLoading()
uni.showToast({ title: '支付成功', icon: 'success' })
- // 5. 自动打开游戏
+ // 6. 自动打开游戏
syncResumeGame(aid)
const latest = findLatestMatchingGameCacheEntry(aid)
if (latest && latest.entry && latest.entry.game_id) {
@@ -1335,8 +1379,10 @@ onLoad((opts) => {
display: flex;
flex-direction: column;
align-items: center;
+ transition: all 0.2s;
&:active {
- opacity: 0.6;
+ transform: scale(0.92);
+ opacity: 0.8;
}
}
.action-icon {
@@ -1872,4 +1918,219 @@ onLoad((opts) => {
animation: fadeInUp 0.5s ease-out backwards;
animation-delay: var(--delay, 0s);
}
+
+/* ============= 全屏游戏样式 ============= */
+.game-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 9999;
+}
+
+.game-mask {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.92);
+ backdrop-filter: blur(16rpx);
+ animation: fadeIn 0.3s ease-out;
+}
+
+.game-fullscreen {
+ position: relative;
+ z-index: 2;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ animation: scaleIn 0.3s ease-out;
+}
+
+.game-topbar {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 40rpx 32rpx;
+ padding-top: calc(40rpx + env(safe-area-inset-top));
+ position: relative;
+}
+
+.game-topbar-title {
+ font-size: 36rpx;
+ font-weight: 800;
+ color: #fff;
+ letter-spacing: 2rpx;
+}
+
+.game-close-btn {
+ position: absolute;
+ right: 32rpx;
+ top: calc(40rpx + env(safe-area-inset-top));
+ width: 64rpx;
+ height: 64rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(255, 255, 255, 0.1);
+ border-radius: 50%;
+
+ text {
+ font-size: 48rpx;
+ color: rgba(255, 255, 255, 0.8);
+ line-height: 1;
+ }
+
+ &:active {
+ background: rgba(255, 255, 255, 0.2);
+ }
+}
+
+.game-stats {
+ display: flex;
+ justify-content: center;
+ gap: 48rpx;
+ padding: 24rpx 32rpx;
+ margin: 0 32rpx;
+}
+
+.stat-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 8rpx;
+}
+
+.stat-label {
+ font-size: 24rpx;
+ color: rgba(255, 255, 255, 0.6);
+}
+
+.stat-value {
+ font-size: 44rpx;
+ font-weight: 900;
+ color: $accent-gold;
+ font-family: 'DIN Alternate', sans-serif;
+}
+
+.game-content {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding: 32rpx;
+}
+
+.game-loading,
+.game-error {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 20rpx;
+}
+
+.loading-icon,
+.error-icon {
+ font-size: 80rpx;
+}
+
+.loading-text,
+.error-text {
+ font-size: 28rpx;
+ color: rgba(255, 255, 255, 0.7);
+}
+
+.game-board {
+ width: 100%;
+ max-width: 700rpx;
+ padding: 32rpx;
+}
+
+.match-grid-fullscreen {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 20rpx;
+}
+
+.match-cell-large {
+ position: relative;
+ aspect-ratio: 1 / 1;
+ border-radius: 20rpx;
+ overflow: hidden;
+ background: rgba(255, 255, 255, 0.1);
+ border: 2rpx solid rgba(255, 255, 255, 0.15);
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+
+ &.empty {
+ background: rgba(255, 255, 255, 0.03);
+ border-style: dashed;
+ border-color: rgba(255, 255, 255, 0.1);
+ }
+
+ &.chosen {
+ border-color: $brand-primary;
+ box-shadow: 0 0 24rpx rgba($brand-primary, 0.5);
+ transform: scale(1.02);
+ }
+
+ &.picked {
+ border-color: $accent-gold;
+ box-shadow: 0 0 24rpx rgba($accent-gold, 0.5);
+ }
+
+ &:active {
+ transform: scale(0.95);
+ }
+}
+
+.match-cell-img-large {
+ width: 100%;
+ height: 100%;
+ background: rgba(255, 255, 255, 0.05);
+}
+
+.match-cell-type-large {
+ position: absolute;
+ left: 12rpx;
+ bottom: 12rpx;
+ max-width: 85%;
+ font-size: 24rpx;
+ font-weight: 700;
+ color: #fff;
+ background: rgba(0, 0, 0, 0.6);
+ padding: 6rpx 14rpx;
+ border-radius: 12rpx;
+ @include text-ellipsis(1);
+}
+
+.game-actions {
+ display: flex;
+ gap: 24rpx;
+ padding: 32rpx;
+ padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
+}
+
+.game-btn {
+ flex: 1;
+ height: 96rpx;
+ border-radius: 48rpx;
+ font-size: 32rpx;
+ font-weight: 800;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: none;
+ transition: all 0.3s;
+
+ &:active {
+ transform: scale(0.95);
+ }
+
+ &[disabled] {
+ opacity: 0.4;
+ }
+}
diff --git a/pages/activity/wuxianshang/index.vue b/pages/activity/wuxianshang/index.vue
index c8ac78e..944ba8d 100644
--- a/pages/activity/wuxianshang/index.vue
+++ b/pages/activity/wuxianshang/index.vue
@@ -72,6 +72,15 @@
:propCards="propCards"
@confirm="onPaymentConfirm"
/>
+
+
+
@@ -86,6 +95,8 @@ import ActivityTabs from '@/components/activity/ActivityTabs.vue'
import RewardsPreview from '@/components/activity/RewardsPreview.vue'
import RewardsPopup from '@/components/activity/RewardsPopup.vue'
import RecordsList from '@/components/activity/RecordsList.vue'
+import RulesPopup from '@/components/activity/RulesPopup.vue'
+import CabinetPreviewPopup from '@/components/activity/CabinetPreviewPopup.vue'
import FlipGrid from '@/components/FlipGrid.vue'
import PaymentPopup from '@/components/PaymentPopup.vue'
// Composables
@@ -126,6 +137,8 @@ const {
// ============ 本地状态 ============
const tabActive = ref('pool')
const rewardsVisible = ref(false)
+const rulesVisible = ref(false)
+const cabinetVisible = ref(false)
const showFlip = ref(false)
const flipRef = ref(null)
const drawLoading = ref(false)
@@ -141,15 +154,11 @@ const selectedCard = ref(null)
// ============ 业务方法 ============
function showRules() {
- uni.showModal({
- title: '活动规则',
- content: detail.value.rules || '1. 选择档位进行抽赏\n2. 每次抽赏随机获得奖品\n3. 奖池与概率以页面展示为准',
- showCancel: false
- })
+ rulesVisible.value = true
}
function goCabinet() {
- uni.switchTab({ url: '/pages/cabinet/index' })
+ cabinetVisible.value = true
}
function closeFlip() {
diff --git a/pages/activity/yifanshang/index.vue b/pages/activity/yifanshang/index.vue
index d9fa3e3..005e7f2 100644
--- a/pages/activity/yifanshang/index.vue
+++ b/pages/activity/yifanshang/index.vue
@@ -49,20 +49,41 @@
{{ orderBlockedReason }}
-
+
+
+
+
+
+
+ 已选 {{ selectedCount }} 个位置
+
+
+ 请选择位置
+
+
+
+
+
+
+
+
+
@@ -79,6 +100,18 @@
:title="`${currentIssueTitle} · 奖品与概率`"
:reward-groups="rewardGroups"
/>
+
+
+
+
+
+
@@ -93,6 +126,8 @@ import ActivityTabs from '@/components/activity/ActivityTabs.vue'
import RewardsPreview from '@/components/activity/RewardsPreview.vue'
import RewardsPopup from '@/components/activity/RewardsPopup.vue'
import RecordsList from '@/components/activity/RecordsList.vue'
+import RulesPopup from '@/components/activity/RulesPopup.vue'
+import CabinetPreviewPopup from '@/components/activity/CabinetPreviewPopup.vue'
import FlipGrid from '@/components/FlipGrid.vue'
import YifanSelector from '@/components/YifanSelector.vue'
// Composables
@@ -133,8 +168,31 @@ const {
// ============ 本地状态 ============
const tabActive = ref('pool')
const rewardsVisible = ref(false)
+const rulesVisible = ref(false)
+const cabinetVisible = ref(false)
const showFlip = ref(false)
const flipRef = ref(null)
+const yifanSelectorRef = ref(null)
+const selectedCount = ref(0) // 从外部追踪选中数量
+
+// 接收选中变化事件
+function onSelectionChange(items) {
+ selectedCount.value = Array.isArray(items) ? items.length : 0
+}
+
+// 触发随机选号
+function handleRandomDraw() {
+ if (yifanSelectorRef.value && yifanSelectorRef.value.handleRandomOne) {
+ yifanSelectorRef.value.handleRandomOne()
+ }
+}
+
+// 触发支付
+function handlePayment() {
+ if (yifanSelectorRef.value && yifanSelectorRef.value.handleBuy) {
+ yifanSelectorRef.value.handleBuy()
+ }
+}
// ============ 倒计时相关(一番赏专属) ============
const nowMs = ref(Date.now())
@@ -179,15 +237,11 @@ const orderBlockedReason = computed(() => {
// ============ 业务方法 ============
function showRules() {
- uni.showModal({
- title: '活动规则',
- content: detail.value.rules || '1. 选择号码进行抽选\n2. 每个号码对应一个奖品\n3. 已售号码不可再选\n4.未满足开赏条件,将自动为所有参与用户退款,款项将原路返回',
- showCancel: false
- })
+ rulesVisible.value = true
}
function goCabinet() {
- uni.switchTab({ url: '/pages/cabinet/index' })
+ cabinetVisible.value = true
}
function closeFlip() {
@@ -426,4 +480,77 @@ watch(currentIssueId, (newId) => {
border: none;
}
}
+
+/* ============= 底部固定操作栏 ============= */
+.float-bar {
+ position: fixed;
+ left: 32rpx;
+ right: 32rpx;
+ bottom: calc(40rpx + env(safe-area-inset-bottom));
+ z-index: 100;
+ animation: slideUp 0.4s cubic-bezier(0.23, 1, 0.32, 1) backwards;
+}
+
+.float-bar-inner {
+ background: rgba(255, 255, 255, 0.85);
+ backdrop-filter: blur(30rpx);
+ padding: 24rpx 40rpx;
+ border-radius: 999rpx;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.12);
+ border: 1rpx solid rgba(255, 255, 255, 0.6);
+}
+
+.selection-info {
+ font-size: 28rpx;
+ color: $text-main;
+ display: flex;
+ align-items: baseline;
+ font-weight: 800;
+}
+
+.highlight {
+ color: $brand-primary;
+ font-weight: 900;
+ font-size: 40rpx;
+ margin: 0 8rpx;
+ font-family: 'DIN Alternate', sans-serif;
+}
+
+.action-buttons {
+ display: flex;
+ gap: 20rpx;
+}
+
+.action-btn {
+ height: 88rpx;
+ line-height: 88rpx;
+ padding: 0 56rpx;
+ border-radius: 999rpx;
+ font-size: 30rpx;
+ font-weight: 900;
+ margin: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ border: none;
+
+ &::after {
+ border: none;
+ }
+
+ &:active {
+ transform: scale(0.92);
+ }
+
+ &.primary {
+ background: $gradient-brand !important;
+ color: #FFFFFF !important;
+ box-shadow: 0 12rpx 32rpx rgba($brand-primary, 0.35);
+ }
+}
diff --git a/pages/cabinet/index.vue b/pages/cabinet/index.vue
index 01b35d7..f0b457e 100644
--- a/pages/cabinet/index.vue
+++ b/pages/cabinet/index.vue
@@ -1,7 +1,9 @@
+
+
-
+
待处理
({{ aggregatedList.length }})
@@ -79,7 +81,7 @@
{{ getStatusText(item.status) }}
- 取消发货
+ 撤销发货
@@ -301,7 +303,8 @@ function getStatusClass(status) {
1: 'status-pending', // 待发货
2: 'status-shipped', // 已发货
3: 'status-delivered', // 已签收
- 4: 'status-cancelled' // 已取消
+ 4: 'status-abnormal', // 异常
+ 5: 'status-cancelled' // 已取消
}
return statusMap[status] || 'status-pending'
}
@@ -311,7 +314,8 @@ function getStatusText(status) {
1: '待发货',
2: '运输中',
3: '已签收',
- 4: '已取消'
+ 4: '异常',
+ 5: '已取消'
}
return statusMap[status] || '待发货'
}
@@ -590,6 +594,7 @@ async function fetchProductPrices() {
}
function toggleSelect(item) {
+ uni.vibrateShort({ type: 'light' })
item.selected = !item.selected
if (item.selected) {
// 选中时默认数量为最大值
@@ -604,6 +609,7 @@ function toggleSelect(item) {
}
function toggleSelectAll() {
+ uni.vibrateShort({ type: 'light' })
const newState = !isAllSelected.value
aggregatedList.value.forEach(item => {
item.selected = newState
@@ -628,6 +634,7 @@ function changeCount(item, delta) {
}
async function onRedeem() {
+ uni.vibrateShort({ type: 'medium' })
const user_id = uni.getStorageSync('user_id')
if (!user_id) return
@@ -675,6 +682,7 @@ async function onRedeem() {
}
async function onShip() {
+ uni.vibrateShort({ type: 'medium' })
const user_id = uni.getStorageSync('user_id')
if (!user_id) return
@@ -726,15 +734,15 @@ function onCancelShipping(shipment) {
const batchNo = shipment && shipment.batch_no
if (!user_id || !batchNo) return
uni.showModal({
- title: '取消发货',
- content: `确认取消发货单 ${batchNo} 吗?`,
- confirmText: '确认取消',
+ title: '撤销发货',
+ content: `确认不再发货,并撤销发货单 ${batchNo} 吗?`,
+ confirmText: '确认撤销',
success: async (res) => {
if (!res.confirm) return
uni.showLoading({ title: '处理中...' })
try {
await cancelShipping(user_id, batchNo)
- uni.showToast({ title: '已取消发货', icon: 'success' })
+ uni.showToast({ title: '已撤销发货', icon: 'success' })
page.value = 1
hasMore.value = true
shippedList.value = []
@@ -762,25 +770,28 @@ function onCancelShipping(shipment) {
padding-bottom: calc(180rpx + env(safe-area-inset-bottom));
display: flex;
flex-direction: column;
+ position: relative;
+ overflow: hidden;
}
/* 顶部 Tab */
.tabs {
+ @extend .glass-card;
position: fixed;
top: 0;
left: 0;
right: 0;
height: 88rpx;
- background: rgba($bg-card, 0.9);
- backdrop-filter: blur(20rpx);
display: flex;
align-items: center;
justify-content: center;
z-index: 100;
- box-shadow: $shadow-sm;
padding: 0;
margin: 0;
border-radius: 0;
+ border-top: none;
+ border-left: none;
+ border-right: none;
}
.tab-item {
@@ -1233,10 +1244,6 @@ function onCancelShipping(shipment) {
from { opacity: 0; transform: translateY(20rpx); }
to { opacity: 1; transform: translateY(0); }
}
-@keyframes slideUp {
- from { transform: translateY(100%); }
- to { transform: translateY(0); }
-}
.bottom-spacer {
height: 120rpx;
diff --git a/pages/coupons/index.vue b/pages/coupons/index.vue
new file mode 100644
index 0000000..39df375
--- /dev/null
+++ b/pages/coupons/index.vue
@@ -0,0 +1,729 @@
+
+
+
+
+
+
+
+
+
+
+ 未使用
+
+
+
+ 已使用
+
+
+
+ 已过期
+
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+ 🎟️
+ {{ getEmptyText() }}
+
+
+
+
+
+
+
+
+ ¥
+ {{ formatValue(item.remaining ?? item.amount ?? 0) }}
+
+ {{ currentTab === 1 ? '可用' : (currentTab === 2 ? '已用' : '过期') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.rules || '全场通用' }}
+
+
+
+
+
+
+ 已用 {{ formatValue(item.amount - item.remaining) }} ({{ getUsedPercent(item) }}%)
+
+
+
+
+
+
+
+ 去使用
+
+
+
+
+ {{ currentTab === 2 ? '已使用' : '已过期' }}
+
+
+
+
+
+
+
+
+ 加载更多...
+
+ - 到底啦 -
+
+
+
+
+
+
+
diff --git a/pages/index/index.vue b/pages/index/index.vue
index 4b87073..3c60c3c 100644
--- a/pages/index/index.vue
+++ b/pages/index/index.vue
@@ -1,18 +1,11 @@
-
-
-
-
-
-
+
+
@@ -333,7 +326,7 @@ export default {
onShareAppMessage() {
const inviteCode = uni.getStorageSync('invite_code') || (uni.getStorageSync('user_info') || {}).invite_code || ''
return {
- title: '柯大鸭潮玩 - 开箱惊喜等你来',
+ title: '柯大鸭 - 开箱惊喜等你来',
path: `/pages/index/index?invite_code=${inviteCode}`,
imageUrl: '/static/logo.png'
}
@@ -342,7 +335,7 @@ export default {
onShareTimeline() {
const inviteCode = uni.getStorageSync('invite_code') || (uni.getStorageSync('user_info') || {}).invite_code || ''
return {
- title: '柯大鸭潮玩 - 开箱惊喜等你来',
+ title: '柯大鸭 - 开箱惊喜等你来',
query: `invite_code=${inviteCode}`,
imageUrl: '/static/logo.png'
}
@@ -352,88 +345,31 @@ export default {
diff --git a/pages/item-cards/index.vue b/pages/item-cards/index.vue
new file mode 100644
index 0000000..c7b4a2f
--- /dev/null
+++ b/pages/item-cards/index.vue
@@ -0,0 +1,623 @@
+
+
+
+
+
+
+
+
+
+
+ 未使用
+
+
+
+ 已使用
+
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+ 🃏
+ {{ currentTab === 0 ? '暂无可用道具卡' : '暂无使用记录' }}
+
+
+
+
+
+
+
+
+ {{ getCardIcon(item.type || item.name) }}
+
+
+ ×{{ item.remaining ?? item.count ?? 1 }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name || item.title || '道具卡' }}
+ {{ item.description || item.rules || '可在抽奖时使用' }}
+
+ 使用时间:{{ formatDateTime(item.used_at) }}
+
+ 使用于:
+ {{ item.used_activity_name }}
+ - 期号 {{ item.used_issue_number }}
+
+
+ 效果:
+ {{ item.used_reward_name }}
+
+
+
+
+
+
+
+ 去使用
+
+
+
+
+ 已使用
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/login/index.vue b/pages/login/index.vue
index 3530895..2340412 100644
--- a/pages/login/index.vue
+++ b/pages/login/index.vue
@@ -11,7 +11,6 @@
- 柯大鸭潮玩
开启欧气之旅 ✨
diff --git a/pages/mine/index.vue b/pages/mine/index.vue
index 205ec80..1d828f1 100644
--- a/pages/mine/index.vue
+++ b/pages/mine/index.vue
@@ -11,35 +11,36 @@
{{ nickname || '未登录' }}
-
+
- Lv1 青铜
+ Lv1 {{ title }}
-
+ ID: {{ userId }}
+
+
立即登录
-
+
{{ pointsBalance || 0 }}
积分
-
+
{{ stats.coupon_count || 0 }}
优惠券
-
+
{{ stats.item_card_count || 0 }}
道具卡
@@ -130,25 +131,25 @@
常用功能