fix: 个人中心优化
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { FullScreen } from '@element-plus/icons-vue';
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
|
|
||||||
interface NavItem {
|
interface NavItem {
|
||||||
@@ -17,7 +18,7 @@ interface Props {
|
|||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
title: '弹窗标题',
|
title: '弹窗标题',
|
||||||
width: '1000px',
|
width: '75%',
|
||||||
defaultActive: '',
|
defaultActive: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -25,9 +26,13 @@ const emit = defineEmits(['update:modelValue', 'confirm', 'close', 'nav-change']
|
|||||||
|
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const activeNav = ref(props.defaultActive || (props.navItems.length > 0 ? props.navItems[0].name : ''));
|
const activeNav = ref(props.defaultActive || (props.navItems.length > 0 ? props.navItems[0].name : ''));
|
||||||
|
const isFullscreen = ref(false);
|
||||||
|
|
||||||
watch(() => props.modelValue, (val) => {
|
watch(() => props.modelValue, (val) => {
|
||||||
visible.value = val;
|
visible.value = val;
|
||||||
|
if (!val) {
|
||||||
|
isFullscreen.value = false; // 关闭时重置全屏状态
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(() => props.defaultActive, (val) => {
|
watch(() => props.defaultActive, (val) => {
|
||||||
@@ -51,17 +56,46 @@ function handleConfirm() {
|
|||||||
emit('confirm', activeNav.value);
|
emit('confirm', activeNav.value);
|
||||||
handleClose();
|
handleClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleFullscreen() {
|
||||||
|
isFullscreen.value = !isFullscreen.value;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
|
|
||||||
v-model="visible"
|
v-model="visible"
|
||||||
:title="title"
|
:title="title"
|
||||||
:width="width"
|
:width="isFullscreen ? '100%' : width"
|
||||||
:before-close="handleClose"
|
:before-close="handleClose"
|
||||||
|
:fullscreen="isFullscreen"
|
||||||
|
:top="isFullscreen ? '0' : '5vh'"
|
||||||
class="nav-dialog"
|
class="nav-dialog"
|
||||||
>
|
>
|
||||||
|
<template #header="{ titleId, titleClass }">
|
||||||
|
<div class="dialog-header">
|
||||||
|
<h4 :id="titleId" :class="titleClass">
|
||||||
|
{{ title }}
|
||||||
|
</h4>
|
||||||
|
<!-- 全屏按钮暂不做 -->
|
||||||
|
<div v-if="false" class="header-actions">
|
||||||
|
<slot name="extra-actions" />
|
||||||
|
<el-button
|
||||||
|
circle
|
||||||
|
plain
|
||||||
|
size="small"
|
||||||
|
class="fullscreen-btn"
|
||||||
|
:title="isFullscreen ? '退出全屏' : '全屏'"
|
||||||
|
@click="toggleFullscreen"
|
||||||
|
>
|
||||||
|
<el-icon>
|
||||||
|
<FullScreen />
|
||||||
|
</el-icon>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<div class="dialog-container">
|
<div class="dialog-container">
|
||||||
<!-- 左侧导航 -->
|
<!-- 左侧导航 -->
|
||||||
<div class="nav-side">
|
<div class="nav-side">
|
||||||
@@ -104,24 +138,52 @@ function handleConfirm() {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.dialog-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullscreen-btn {
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullscreen-btn:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
.dialog-container {
|
.dialog-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 500px;
|
height: 70vh;
|
||||||
|
min-height: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-dialog.is-fullscreen) .dialog-container {
|
||||||
|
height: calc(100vh - 120px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-side {
|
.nav-side {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
border-right: 1px solid #e6e6e6;
|
border-right: 1px solid #e6e6e6;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-menu {
|
.nav-menu {
|
||||||
border-right: none;
|
border-right: none;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-main {
|
.content-main {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-content {
|
.empty-content {
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ onMounted(async () => {
|
|||||||
<!-- 自适应缩放 iframe -->
|
<!-- 自适应缩放 iframe -->
|
||||||
<iframe
|
<iframe
|
||||||
src="https://ccnetcore.com/article/3a1bc4d1-6a7d-751d-91cc-2817eb2ddcde"
|
src="https://ccnetcore.com/article/3a1bc4d1-6a7d-751d-91cc-2817eb2ddcde"
|
||||||
class="min-w-full h-[700px] scale-100 duration-300"
|
class="min-w-full iframe-responsive scale-100 duration-300"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
sandbox="allow-scripts allow-same-origin allow-popups"
|
sandbox="allow-scripts allow-same-origin allow-popups"
|
||||||
@load="document.querySelector('.iframe-loading')?.remove()"
|
@load="document.querySelector('.iframe-loading')?.remove()"
|
||||||
@@ -321,6 +321,13 @@ onMounted(async () => {
|
|||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* iframe 响应式高度 */
|
||||||
|
.iframe-responsive {
|
||||||
|
height: 60vh;
|
||||||
|
min-height: 400px;
|
||||||
|
max-height: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
/* 未领取状态样式 */
|
/* 未领取状态样式 */
|
||||||
.unclaimed-state {
|
.unclaimed-state {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, computed } from 'vue';
|
import type { CardFlipRecord, CardFlipStatusOutput, FlipCardOutput } from '@/api/cardFlip/types';
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import { getWeeklyTaskStatus, flipCard, useInviteCode, generateMyInviteCode } from '@/api/cardFlip';
|
import { onMounted, ref } from 'vue';
|
||||||
import type { CardFlipStatusOutput, CardFlipRecord, FlipCardOutput } from '@/api/cardFlip/types';
|
import { flipCard, generateMyInviteCode, getWeeklyTaskStatus, useInviteCode } from '@/api/cardFlip';
|
||||||
|
|
||||||
const taskData = ref<CardFlipStatusOutput | null>(null);
|
const taskData = ref<CardFlipStatusOutput | null>(null);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
@@ -26,9 +26,11 @@ async function fetchTaskStatus() {
|
|||||||
try {
|
try {
|
||||||
const res = await getWeeklyTaskStatus();
|
const res = await getWeeklyTaskStatus();
|
||||||
taskData.value = res.data;
|
taskData.value = res.data;
|
||||||
} catch (error: any) {
|
}
|
||||||
|
catch (error: any) {
|
||||||
ElMessage.error(error?.message || '获取任务状态失败');
|
ElMessage.error(error?.message || '获取任务状态失败');
|
||||||
} finally {
|
}
|
||||||
|
finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,7 +52,7 @@ async function handleFlipCard(record: CardFlipRecord) {
|
|||||||
confirmButtonText: '去使用',
|
confirmButtonText: '去使用',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
inviteCodeDialog.value = true;
|
inviteCodeDialog.value = true;
|
||||||
@@ -70,6 +72,12 @@ async function handleFlipCard(record: CardFlipRecord) {
|
|||||||
// 等待翻牌动画完成
|
// 等待翻牌动画完成
|
||||||
await new Promise(resolve => setTimeout(resolve, 800));
|
await new Promise(resolve => setTimeout(resolve, 800));
|
||||||
|
|
||||||
|
// 刷新任务状态(先刷新数据,让卡片显示翻转后的结果)
|
||||||
|
await fetchTaskStatus();
|
||||||
|
|
||||||
|
// 稍微延迟后显示结果提示
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
|
|
||||||
// 显示结果
|
// 显示结果
|
||||||
if (res.data.isWin) {
|
if (res.data.isWin) {
|
||||||
// 播放中奖动画
|
// 播放中奖动画
|
||||||
@@ -78,31 +86,31 @@ async function handleFlipCard(record: CardFlipRecord) {
|
|||||||
ElMessage.success({
|
ElMessage.success({
|
||||||
message: res.data.rewardDesc || '🎉 恭喜中奖!',
|
message: res.data.rewardDesc || '🎉 恭喜中奖!',
|
||||||
duration: 3000,
|
duration: 3000,
|
||||||
customClass: 'win-message'
|
customClass: 'win-message',
|
||||||
});
|
});
|
||||||
|
|
||||||
// 第9次中奖显示翻倍提示
|
// 第9次中奖显示翻倍提示
|
||||||
if (res.data.showDoubleRewardTip) {
|
if (res.data.showDoubleRewardTip) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
showDoubleRewardDialog.value = true;
|
showDoubleRewardDialog.value = true;
|
||||||
}, 1500);
|
}, 500);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
ElMessage.info({
|
ElMessage.info({
|
||||||
message: '😢 很遗憾,未中奖',
|
message: '😢 很遗憾,未中奖',
|
||||||
duration: 2000
|
duration: 2000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// 刷新任务状态
|
catch (error: any) {
|
||||||
await fetchTaskStatus();
|
|
||||||
} catch (error: any) {
|
|
||||||
ElMessage.error(error?.message || '翻牌失败');
|
ElMessage.error(error?.message || '翻牌失败');
|
||||||
} finally {
|
}
|
||||||
|
finally {
|
||||||
flipping.value = false;
|
flipping.value = false;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
flippingCards.value.delete(record.flipNumber);
|
flippingCards.value.delete(record.flipNumber);
|
||||||
}, 800);
|
}, 300);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,17 +120,23 @@ function showWinAnimation() {
|
|||||||
// 创建烟花效果
|
// 创建烟花效果
|
||||||
const container = document.querySelector('.card-flip-container');
|
const container = document.querySelector('.card-flip-container');
|
||||||
if (container) {
|
if (container) {
|
||||||
for (let i = 0; i < 20; i++) {
|
const colors = ['#f56c6c', '#409eff', '#67c23a', '#e6a23c', '#9c27b0', '#FFD700', '#FF6B9D'];
|
||||||
|
for (let i = 0; i < 50; i++) {
|
||||||
const confetti = document.createElement('div');
|
const confetti = document.createElement('div');
|
||||||
confetti.className = 'confetti';
|
confetti.className = 'confetti';
|
||||||
confetti.style.left = Math.random() * 100 + '%';
|
confetti.style.left = `${Math.random() * 100}%`;
|
||||||
confetti.style.animationDelay = Math.random() * 0.5 + 's';
|
confetti.style.animationDelay = `${Math.random() * 0.5}s`;
|
||||||
confetti.style.background = ['#f56c6c', '#409eff', '#67c23a', '#e6a23c', '#9c27b0'][Math.floor(Math.random() * 5)];
|
confetti.style.background = colors[Math.floor(Math.random() * colors.length)];
|
||||||
|
const size = 6 + Math.random() * 10;
|
||||||
|
confetti.style.width = `${size}px`;
|
||||||
|
confetti.style.height = `${size}px`;
|
||||||
|
confetti.style.borderRadius = Math.random() > 0.5 ? '50%' : '2px';
|
||||||
|
confetti.style.opacity = `${0.7 + Math.random() * 0.3}`;
|
||||||
container.appendChild(confetti);
|
container.appendChild(confetti);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
confetti.remove();
|
confetti.remove();
|
||||||
}, 2000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,9 +155,11 @@ async function handleUseInviteCode() {
|
|||||||
inviteCodeDialog.value = false;
|
inviteCodeDialog.value = false;
|
||||||
inputInviteCode.value = '';
|
inputInviteCode.value = '';
|
||||||
await fetchTaskStatus();
|
await fetchTaskStatus();
|
||||||
} catch (error: any) {
|
}
|
||||||
|
catch (error: any) {
|
||||||
ElMessage.error(error?.message || '使用邀请码失败');
|
ElMessage.error(error?.message || '使用邀请码失败');
|
||||||
} finally {
|
}
|
||||||
|
finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -155,9 +171,11 @@ async function handleGenerateInviteCode() {
|
|||||||
await generateMyInviteCode();
|
await generateMyInviteCode();
|
||||||
ElMessage.success('✨ 邀请码生成成功!');
|
ElMessage.success('✨ 邀请码生成成功!');
|
||||||
await fetchTaskStatus();
|
await fetchTaskStatus();
|
||||||
} catch (error: any) {
|
}
|
||||||
|
catch (error: any) {
|
||||||
ElMessage.error(error?.message || '生成邀请码失败');
|
ElMessage.error(error?.message || '生成邀请码失败');
|
||||||
} finally {
|
}
|
||||||
|
finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,7 +207,8 @@ function getCardClass(record: CardFlipRecord): string[] {
|
|||||||
classes.push('card-flipped');
|
classes.push('card-flipped');
|
||||||
if (record.isWin) {
|
if (record.isWin) {
|
||||||
classes.push('card-win');
|
classes.push('card-win');
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
classes.push('card-lose');
|
classes.push('card-lose');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,7 +220,8 @@ function getCardClass(record: CardFlipRecord): string[] {
|
|||||||
// 可点击的卡片(任何未翻过的牌都可以点击)
|
// 可点击的卡片(任何未翻过的牌都可以点击)
|
||||||
if (!record.isFlipped && (taskData.value?.canFlip || false)) {
|
if (!record.isFlipped && (taskData.value?.canFlip || false)) {
|
||||||
classes.push('card-clickable');
|
classes.push('card-clickable');
|
||||||
} else if (!record.isFlipped) {
|
}
|
||||||
|
else if (!record.isFlipped) {
|
||||||
classes.push('card-locked');
|
classes.push('card-locked');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,100 +236,13 @@ function toggleInviteSection() {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-loading="loading" class="card-flip-container">
|
<div v-loading="loading" class="card-flip-container">
|
||||||
<!-- 顶部统计栏 -->
|
<!-- 邀请码操作区 -->
|
||||||
<div class="top-stats">
|
|
||||||
<div class="stat-item">
|
|
||||||
<div class="stat-icon-box progress">
|
|
||||||
<span class="stat-icon">🎯</span>
|
|
||||||
</div>
|
|
||||||
<div class="stat-content">
|
|
||||||
<div class="stat-value">{{ taskData?.totalFlips || 0 }}/10</div>
|
|
||||||
<div class="stat-label">已翻</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stat-item">
|
|
||||||
<div class="stat-icon-box gift">
|
|
||||||
<span class="stat-icon">🎁</span>
|
|
||||||
</div>
|
|
||||||
<div class="stat-content">
|
|
||||||
<div class="stat-value">{{ taskData ? 10 - taskData.totalFlips : 10 }}</div>
|
|
||||||
<div class="stat-label">剩余</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="stat-item">
|
|
||||||
<div class="stat-icon-box invite">
|
|
||||||
<span class="stat-icon">👥</span>
|
|
||||||
</div>
|
|
||||||
<div class="stat-content">
|
|
||||||
<div class="stat-value">{{ taskData?.invitedCount || 0 }}</div>
|
|
||||||
<div class="stat-label">邀请</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 提示横幅 -->
|
|
||||||
<div v-if="taskData?.nextFlipTip" class="tip-banner">
|
|
||||||
<span class="tip-icon">💡</span>
|
|
||||||
<span class="tip-text">{{ taskData.nextFlipTip }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 翻牌区域 -->
|
|
||||||
<div class="cards-section">
|
|
||||||
<div class="cards-grid">
|
|
||||||
<div
|
|
||||||
v-for="record in taskData?.flipRecords"
|
|
||||||
:key="record.flipNumber"
|
|
||||||
:class="getCardClass(record)"
|
|
||||||
@click="handleFlipCard(record)"
|
|
||||||
>
|
|
||||||
<div class="card-inner">
|
|
||||||
<!-- 卡片正面 -->
|
|
||||||
<div class="card-front">
|
|
||||||
<div class="card-corner">
|
|
||||||
<span class="card-number">#{{ record.flipNumber }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="card-content">
|
|
||||||
<div class="card-icon">🎴</div>
|
|
||||||
<div class="card-mystery">?</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-type-badge">{{ record.flipTypeDesc }}</div>
|
|
||||||
<div class="card-shine"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 卡片背面 -->
|
|
||||||
<div class="card-back">
|
|
||||||
<div v-if="record.isWin" class="card-result win">
|
|
||||||
<div class="win-glow"></div>
|
|
||||||
<div class="result-icon">🎉</div>
|
|
||||||
<div class="result-text">中奖啦!</div>
|
|
||||||
<div class="result-amount">{{ formatTokenDisplay(record.rewardAmount || 0) }}</div>
|
|
||||||
<div class="result-unit">Tokens</div>
|
|
||||||
</div>
|
|
||||||
<div v-else class="card-result lose">
|
|
||||||
<div class="result-icon">💫</div>
|
|
||||||
<div class="result-text">未中奖</div>
|
|
||||||
<div class="result-tip">继续加油!</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 锁定遮罩 -->
|
|
||||||
<div v-if="!record.isFlipped && record.flipNumber > (taskData?.totalFlips || 0) + 1" class="card-lock">
|
|
||||||
<span class="lock-icon">🔒</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 底部操作区 -->
|
|
||||||
<div class="bottom-actions">
|
<div class="bottom-actions">
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
:icon="showInviteSection ? 'ArrowUp' : 'Gift'"
|
:icon="showInviteSection ? 'ArrowUp' : 'Gift'"
|
||||||
@click="toggleInviteSection"
|
|
||||||
class="invite-toggle-btn"
|
class="invite-toggle-btn"
|
||||||
|
@click="toggleInviteSection"
|
||||||
>
|
>
|
||||||
{{ showInviteSection ? '收起' : '邀请码' }}
|
{{ showInviteSection ? '收起' : '邀请码' }}
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -317,8 +250,8 @@ function toggleInviteSection() {
|
|||||||
<el-button
|
<el-button
|
||||||
type="warning"
|
type="warning"
|
||||||
icon="Unlock"
|
icon="Unlock"
|
||||||
@click="inviteCodeDialog = true"
|
|
||||||
class="use-code-btn"
|
class="use-code-btn"
|
||||||
|
@click="inviteCodeDialog = true"
|
||||||
>
|
>
|
||||||
使用邀请码
|
使用邀请码
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -359,6 +292,125 @@ function toggleInviteSection() {
|
|||||||
</div>
|
</div>
|
||||||
</el-collapse-transition>
|
</el-collapse-transition>
|
||||||
|
|
||||||
|
<!-- 顶部统计栏 -->
|
||||||
|
<div class="top-stats">
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-icon-box progress">
|
||||||
|
<span class="stat-icon">🎯</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-content">
|
||||||
|
<div class="stat-value">
|
||||||
|
{{ taskData?.totalFlips || 0 }}/10
|
||||||
|
</div>
|
||||||
|
<div class="stat-label">
|
||||||
|
已翻
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-icon-box gift">
|
||||||
|
<span class="stat-icon">🎁</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-content">
|
||||||
|
<div class="stat-value">
|
||||||
|
{{ taskData ? 10 - taskData.totalFlips : 10 }}
|
||||||
|
</div>
|
||||||
|
<div class="stat-label">
|
||||||
|
剩余
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stat-item">
|
||||||
|
<div class="stat-icon-box invite">
|
||||||
|
<span class="stat-icon">👥</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat-content">
|
||||||
|
<div class="stat-value">
|
||||||
|
{{ taskData?.invitedCount || 0 }}
|
||||||
|
</div>
|
||||||
|
<div class="stat-label">
|
||||||
|
邀请
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 提示横幅 -->
|
||||||
|
<div v-if="taskData?.nextFlipTip" class="tip-banner">
|
||||||
|
<span class="tip-icon">💡</span>
|
||||||
|
<span class="tip-text">{{ taskData.nextFlipTip }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 翻牌区域 -->
|
||||||
|
<div class="cards-section">
|
||||||
|
<div class="cards-grid">
|
||||||
|
<div
|
||||||
|
v-for="record in taskData?.flipRecords"
|
||||||
|
:key="record.flipNumber"
|
||||||
|
:class="getCardClass(record)"
|
||||||
|
@click="handleFlipCard(record)"
|
||||||
|
>
|
||||||
|
<div class="card-inner">
|
||||||
|
<!-- 卡片正面 -->
|
||||||
|
<div class="card-front">
|
||||||
|
<div class="card-corner">
|
||||||
|
<span class="card-number">#{{ record.flipNumber }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="card-icon">
|
||||||
|
🎴
|
||||||
|
</div>
|
||||||
|
<div class="card-mystery">
|
||||||
|
?
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-type-badge">
|
||||||
|
{{ record.flipTypeDesc }}
|
||||||
|
</div>
|
||||||
|
<div class="card-shine" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 卡片背面 -->
|
||||||
|
<div class="card-back">
|
||||||
|
<div v-if="record.isWin" class="card-result win">
|
||||||
|
<div class="win-glow" />
|
||||||
|
<div class="result-icon">
|
||||||
|
🎉
|
||||||
|
</div>
|
||||||
|
<div class="result-text">
|
||||||
|
中奖啦!
|
||||||
|
</div>
|
||||||
|
<div class="result-amount">
|
||||||
|
{{ formatTokenDisplay(record.rewardAmount || 0) }}
|
||||||
|
</div>
|
||||||
|
<div class="result-unit">
|
||||||
|
Tokens
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="card-result lose">
|
||||||
|
<div class="result-icon">
|
||||||
|
💫
|
||||||
|
</div>
|
||||||
|
<div class="result-text">
|
||||||
|
未中奖
|
||||||
|
</div>
|
||||||
|
<div class="result-tip">
|
||||||
|
继续加油!
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 锁定遮罩 -->
|
||||||
|
<div v-if="!record.isFlipped && record.flipNumber > (taskData?.totalFlips || 0) + 1" class="card-lock">
|
||||||
|
<span class="lock-icon">🔒</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 使用邀请码对话框 -->
|
<!-- 使用邀请码对话框 -->
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="inviteCodeDialog"
|
v-model="inviteCodeDialog"
|
||||||
@@ -369,7 +421,9 @@ function toggleInviteSection() {
|
|||||||
class="invite-dialog"
|
class="invite-dialog"
|
||||||
>
|
>
|
||||||
<div class="invite-dialog-content">
|
<div class="invite-dialog-content">
|
||||||
<p class="dialog-tip">请输入好友的邀请码,解锁最后2次翻牌机会</p>
|
<p class="dialog-tip">
|
||||||
|
请输入好友的邀请码,解锁最后2次翻牌机会
|
||||||
|
</p>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="inputInviteCode"
|
v-model="inputInviteCode"
|
||||||
placeholder="请输入8位邀请码"
|
placeholder="请输入8位邀请码"
|
||||||
@@ -384,8 +438,10 @@ function toggleInviteSection() {
|
|||||||
</el-input>
|
</el-input>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="inviteCodeDialog = false">取消</el-button>
|
<el-button @click="inviteCodeDialog = false">
|
||||||
<el-button type="primary" @click="handleUseInviteCode" :loading="loading">
|
取消
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" :loading="loading" @click="handleUseInviteCode">
|
||||||
确认使用
|
确认使用
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -401,16 +457,22 @@ function toggleInviteSection() {
|
|||||||
>
|
>
|
||||||
<div class="double-reward-content">
|
<div class="double-reward-content">
|
||||||
<div class="double-icon-container">
|
<div class="double-icon-container">
|
||||||
<div class="double-icon">🎁</div>
|
<div class="double-icon">
|
||||||
<div class="double-sparkle">✨</div>
|
🎁
|
||||||
|
</div>
|
||||||
|
<div class="double-sparkle">
|
||||||
|
✨
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="double-title">恭喜获得额外翻倍包!</h3>
|
<h3 class="double-title">
|
||||||
|
恭喜获得额外翻倍包!
|
||||||
|
</h3>
|
||||||
<p class="double-text">
|
<p class="double-text">
|
||||||
您已获得第9次奖励!<br>
|
您已获得第9次奖励!<br>
|
||||||
<span class="highlight">第10次翻牌将获得翻倍奖励!</span><br>
|
<span class="highlight">第10次翻牌将获得翻倍奖励!</span><br>
|
||||||
赶快翻开最后一张牌吧!
|
赶快翻开最后一张牌吧!
|
||||||
</p>
|
</p>
|
||||||
<el-button type="primary" size="large" @click="showDoubleRewardDialog = false" class="double-btn">
|
<el-button type="primary" size="large" class="double-btn" @click="showDoubleRewardDialog = false">
|
||||||
知道了,去翻牌 🚀
|
知道了,去翻牌 🚀
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -422,8 +484,9 @@ function toggleInviteSection() {
|
|||||||
.card-flip-container {
|
.card-flip-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
min-height: 500px;
|
min-height: 400px;
|
||||||
max-height: 70vh;
|
max-height: none;
|
||||||
|
height: 100%;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
@@ -464,10 +527,10 @@ function toggleInviteSection() {
|
|||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.95);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
transition: transform 0.3s ease;
|
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px) scale(1.02);
|
||||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -567,11 +630,18 @@ function toggleInviteSection() {
|
|||||||
|
|
||||||
&:not(.card-flipping):not(.card-flipped) {
|
&:not(.card-flipping):not(.card-flipped) {
|
||||||
.card-inner {
|
.card-inner {
|
||||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.3s ease, filter 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover .card-inner {
|
&:hover .card-inner {
|
||||||
transform: translateY(-4px);
|
transform: translateY(-8px) scale(1.05) rotateZ(2deg);
|
||||||
|
box-shadow: 0 12px 28px rgba(102, 126, 234, 0.5), 0 4px 12px rgba(255, 215, 0, 0.3);
|
||||||
|
filter: brightness(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active .card-inner {
|
||||||
|
transform: translateY(-4px) scale(1.02);
|
||||||
|
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -670,8 +740,9 @@ function toggleInviteSection() {
|
|||||||
left: -100%;
|
left: -100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
|
||||||
animation: shine 3s infinite;
|
animation: shine 3s infinite;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -682,8 +753,6 @@ function toggleInviteSection() {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
position: relative;
|
|
||||||
top: -170px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-result {
|
.card-result {
|
||||||
@@ -698,17 +767,18 @@ function toggleInviteSection() {
|
|||||||
|
|
||||||
&.win {
|
&.win {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
.win-glow {
|
.win-glow {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: radial-gradient(circle, rgba(255, 215, 0, 0.3) 0%, transparent 70%);
|
background: radial-gradient(circle, rgba(255, 215, 0, 0.4) 0%, rgba(255, 107, 157, 0.2) 50%, transparent 70%);
|
||||||
animation: glow 1.5s infinite;
|
animation: glow 1.5s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-icon {
|
.result-icon {
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
animation: bounce 0.6s ease;
|
animation: bounce 0.6s ease, rotate 2s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-text {
|
.result-text {
|
||||||
@@ -716,6 +786,7 @@ function toggleInviteSection() {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #f56c6c;
|
color: #f56c6c;
|
||||||
margin: 4px 0;
|
margin: 4px 0;
|
||||||
|
text-shadow: 0 2px 4px rgba(245, 108, 108, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-amount {
|
.result-amount {
|
||||||
@@ -725,6 +796,7 @@ function toggleInviteSection() {
|
|||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
-webkit-text-fill-color: transparent;
|
-webkit-text-fill-color: transparent;
|
||||||
margin: 2px 0;
|
margin: 2px 0;
|
||||||
|
animation: scaleUp 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-unit {
|
.result-unit {
|
||||||
@@ -777,16 +849,34 @@ function toggleInviteSection() {
|
|||||||
.el-button {
|
.el-button {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.invite-toggle-btn {
|
.invite-toggle-btn {
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.use-code-btn {
|
.use-code-btn {
|
||||||
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
||||||
border: none;
|
border: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: linear-gradient(135deg, #f5576c 0%, #f093fb 100%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -796,6 +886,7 @@ function toggleInviteSection() {
|
|||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
margin: 10px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.invite-header {
|
.invite-header {
|
||||||
@@ -1028,4 +1119,24 @@ function toggleInviteSection() {
|
|||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
0%, 100% {
|
||||||
|
transform: rotate(-5deg);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: rotate(5deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes scaleUp {
|
||||||
|
0% {
|
||||||
|
transform: scale(0.5);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -189,6 +189,8 @@ function getProgressColor(task: DailyTaskItem): string {
|
|||||||
.daily-task-container {
|
.daily-task-container {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
min-height: 400px;
|
min-height: 400px;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-header {
|
.task-header {
|
||||||
|
|||||||
@@ -273,6 +273,8 @@ function onProductPackage() {
|
|||||||
.premium-service {
|
.premium-service {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
|
|||||||
@@ -104,6 +104,11 @@ function contactCustomerService() {
|
|||||||
innerVisibleContact.value = !innerVisibleContact.value;
|
innerVisibleContact.value = !innerVisibleContact.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 暴露方法给父组件使用
|
||||||
|
defineExpose({
|
||||||
|
contactCustomerService,
|
||||||
|
});
|
||||||
|
|
||||||
// 过滤和排序后的数据
|
// 过滤和排序后的数据
|
||||||
const filteredData = computed(() => {
|
const filteredData = computed(() => {
|
||||||
let data = [...logData.value];
|
let data = [...logData.value];
|
||||||
@@ -253,7 +258,6 @@ onMounted(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table
|
<el-table
|
||||||
|
|
||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
:data="filteredData"
|
:data="filteredData"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
@@ -265,7 +269,7 @@ onMounted(() => {
|
|||||||
<el-table-column
|
<el-table-column
|
||||||
prop="content"
|
prop="content"
|
||||||
label="套餐类型"
|
label="套餐类型"
|
||||||
width="150"
|
min-width="150"
|
||||||
sortable="custom"
|
sortable="custom"
|
||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
/>
|
/>
|
||||||
@@ -273,7 +277,7 @@ onMounted(() => {
|
|||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
prop="rechargeAmount"
|
prop="rechargeAmount"
|
||||||
label="金额(元)"
|
label="金额(元)"
|
||||||
width="110"
|
min-width="110"
|
||||||
sortable="custom"
|
sortable="custom"
|
||||||
>
|
>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
@@ -283,14 +287,14 @@ onMounted(() => {
|
|||||||
<el-table-column
|
<el-table-column
|
||||||
prop="creationTime"
|
prop="creationTime"
|
||||||
label="充值时间"
|
label="充值时间"
|
||||||
width="160"
|
min-width="160"
|
||||||
sortable="custom"
|
sortable="custom"
|
||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
/>
|
/>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="expireDateTime"
|
prop="expireDateTime"
|
||||||
label="到期时间"
|
label="到期时间"
|
||||||
width="160"
|
min-width="160"
|
||||||
sortable="custom"
|
sortable="custom"
|
||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
/>
|
/>
|
||||||
@@ -302,7 +306,7 @@ onMounted(() => {
|
|||||||
<span v-else>{{ row.contactInfo || '-' }}</span>
|
<span v-else>{{ row.contactInfo || '-' }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column> -->
|
</el-table-column> -->
|
||||||
<el-table-column show-overflow-tooltip prop="remark" label="备注" width="160">
|
<el-table-column show-overflow-tooltip prop="remark" label="备注" min-width="160">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tooltip v-if="row.remark && row.remark.length > 10" :content="row.remark" placement="top">
|
<el-tooltip v-if="row.remark && row.remark.length > 10" :content="row.remark" placement="top">
|
||||||
<span class="ellipsis-text">{{ row.remark }}</span>
|
<span class="ellipsis-text">{{ row.remark }}</span>
|
||||||
@@ -379,6 +383,8 @@ onMounted(() => {
|
|||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.recharge-log-container:hover {
|
.recharge-log-container:hover {
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ const gridTemplateColumns = computed(() => {
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.model-container {
|
.model-container {
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.model-header {
|
.model-header {
|
||||||
|
|||||||
@@ -494,7 +494,7 @@ onBeforeUnmount(() => {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<div ref="lineChart" class="chart" style="height: 400px;" />
|
<div ref="lineChart" class="chart" style="height: 350px;" />
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
@@ -505,7 +505,7 @@ onBeforeUnmount(() => {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<div ref="pieChart" class="chart" style="height: 450px;" />
|
<div ref="pieChart" class="chart" style="height: 400px;" />
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
@@ -516,7 +516,7 @@ onBeforeUnmount(() => {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<div ref="barChart" class="chart" style="height: 500px;" />
|
<div ref="barChart" class="chart" style="height: 450px;" />
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
@@ -524,13 +524,14 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.usage-statistics {
|
.usage-statistics {
|
||||||
padding: 30px;
|
padding: 20px;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%);
|
background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
min-height: 100vh;
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.usage-statistics:hover {
|
.usage-statistics:hover {
|
||||||
|
|||||||
@@ -293,6 +293,8 @@ function bindWechat() {
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.user-profile {
|
.user-profile {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
|
import { ChatLineRound } from '@element-plus/icons-vue';
|
||||||
import Popover from '@/components/Popover/index.vue';
|
import Popover from '@/components/Popover/index.vue';
|
||||||
import SvgIcon from '@/components/SvgIcon/index.vue';
|
import SvgIcon from '@/components/SvgIcon/index.vue';
|
||||||
import { useUserStore } from '@/stores';
|
import { useUserStore } from '@/stores';
|
||||||
@@ -58,6 +59,8 @@ const popoverList = ref([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const dialogVisible = ref(false);
|
const dialogVisible = ref(false);
|
||||||
|
const rechargeLogRef = ref();
|
||||||
|
const activeNav = ref('user');
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ name: 'user', label: '用户信息', icon: 'User' },
|
{ name: 'user', label: '用户信息', icon: 'User' },
|
||||||
@@ -82,6 +85,12 @@ function handleConfirm(activeNav: string) {
|
|||||||
|
|
||||||
// 导航切换
|
// 导航切换
|
||||||
function handleNavChange(nav: string) {
|
function handleNavChange(nav: string) {
|
||||||
|
activeNav.value = nav;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 联系售后
|
||||||
|
function handleContactSupport() {
|
||||||
|
rechargeLogRef.value?.contactCustomerService();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 点击
|
// 点击
|
||||||
@@ -317,6 +326,16 @@ function onProductPackage() {
|
|||||||
@confirm="handleConfirm"
|
@confirm="handleConfirm"
|
||||||
@nav-change="handleNavChange"
|
@nav-change="handleNavChange"
|
||||||
>
|
>
|
||||||
|
<template #extra-actions>
|
||||||
|
<el-tooltip v-if="isUserVip() && activeNav === 'rechargeLog'" content="联系售后" placement="bottom">
|
||||||
|
<el-button circle plain size="small" @click="handleContactSupport">
|
||||||
|
<el-icon color="#07c160">
|
||||||
|
<ChatLineRound />
|
||||||
|
</el-icon>
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 用户管理内容 -->
|
<!-- 用户管理内容 -->
|
||||||
<template #user>
|
<template #user>
|
||||||
<user-management />
|
<user-management />
|
||||||
@@ -354,7 +373,7 @@ function onProductPackage() {
|
|||||||
<card-flip-activity />
|
<card-flip-activity />
|
||||||
</template>
|
</template>
|
||||||
<template #rechargeLog>
|
<template #rechargeLog>
|
||||||
<recharge-log />
|
<recharge-log ref="rechargeLogRef" />
|
||||||
</template>
|
</template>
|
||||||
</nav-dialog>
|
</nav-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user