fix: 个人中心优化

This commit is contained in:
Gsh
2025-10-29 00:17:36 +08:00
parent c6425ca206
commit dd3f6325bb
10 changed files with 366 additions and 152 deletions

View File

@@ -1,4 +1,5 @@
<script lang="ts" setup>
import { FullScreen } from '@element-plus/icons-vue';
import { ref, watch } from 'vue';
interface NavItem {
@@ -17,7 +18,7 @@ interface Props {
const props = withDefaults(defineProps<Props>(), {
title: '弹窗标题',
width: '1000px',
width: '75%',
defaultActive: '',
});
@@ -25,9 +26,13 @@ const emit = defineEmits(['update:modelValue', 'confirm', 'close', 'nav-change']
const visible = ref(false);
const activeNav = ref(props.defaultActive || (props.navItems.length > 0 ? props.navItems[0].name : ''));
const isFullscreen = ref(false);
watch(() => props.modelValue, (val) => {
visible.value = val;
if (!val) {
isFullscreen.value = false; // 关闭时重置全屏状态
}
});
watch(() => props.defaultActive, (val) => {
@@ -51,17 +56,46 @@ function handleConfirm() {
emit('confirm', activeNav.value);
handleClose();
}
function toggleFullscreen() {
isFullscreen.value = !isFullscreen.value;
}
</script>
<template>
<el-dialog
v-model="visible"
:title="title"
:width="width"
:width="isFullscreen ? '100%' : width"
:before-close="handleClose"
:fullscreen="isFullscreen"
:top="isFullscreen ? '0' : '5vh'"
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="nav-side">
@@ -104,24 +138,52 @@ function handleConfirm() {
</template>
<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 {
display: flex;
height: 500px;
height: 70vh;
min-height: 500px;
}
:deep(.el-dialog.is-fullscreen) .dialog-container {
height: calc(100vh - 120px);
}
.nav-side {
width: 200px;
border-right: 1px solid #e6e6e6;
flex-shrink: 0;
}
.nav-menu {
border-right: none;
height: 100%;
}
.content-main {
flex: 1;
padding: 0 20px;
overflow: auto;
min-width: 0;
}
.empty-content {

View File

@@ -262,7 +262,7 @@ onMounted(async () => {
<!-- 自适应缩放 iframe -->
<iframe
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"
sandbox="allow-scripts allow-same-origin allow-popups"
@load="document.querySelector('.iframe-loading')?.remove()"
@@ -321,6 +321,13 @@ onMounted(async () => {
min-height: 200px;
}
/* iframe 响应式高度 */
.iframe-responsive {
height: 60vh;
min-height: 400px;
max-height: 800px;
}
/* 未领取状态样式 */
.unclaimed-state {
text-align: center;

View File

@@ -1,8 +1,8 @@
<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 { getWeeklyTaskStatus, flipCard, useInviteCode, generateMyInviteCode } from '@/api/cardFlip';
import type { CardFlipStatusOutput, CardFlipRecord, FlipCardOutput } from '@/api/cardFlip/types';
import { onMounted, ref } from 'vue';
import { flipCard, generateMyInviteCode, getWeeklyTaskStatus, useInviteCode } from '@/api/cardFlip';
const taskData = ref<CardFlipStatusOutput | null>(null);
const loading = ref(false);
@@ -26,9 +26,11 @@ async function fetchTaskStatus() {
try {
const res = await getWeeklyTaskStatus();
taskData.value = res.data;
} catch (error: any) {
}
catch (error: any) {
ElMessage.error(error?.message || '获取任务状态失败');
} finally {
}
finally {
loading.value = false;
}
}
@@ -50,7 +52,7 @@ async function handleFlipCard(record: CardFlipRecord) {
confirmButtonText: '去使用',
cancelButtonText: '取消',
type: 'warning',
}
},
)
.then(() => {
inviteCodeDialog.value = true;
@@ -70,6 +72,12 @@ async function handleFlipCard(record: CardFlipRecord) {
// 等待翻牌动画完成
await new Promise(resolve => setTimeout(resolve, 800));
// 刷新任务状态(先刷新数据,让卡片显示翻转后的结果)
await fetchTaskStatus();
// 稍微延迟后显示结果提示
await new Promise(resolve => setTimeout(resolve, 300));
// 显示结果
if (res.data.isWin) {
// 播放中奖动画
@@ -78,31 +86,31 @@ async function handleFlipCard(record: CardFlipRecord) {
ElMessage.success({
message: res.data.rewardDesc || '🎉 恭喜中奖!',
duration: 3000,
customClass: 'win-message'
customClass: 'win-message',
});
// 第9次中奖显示翻倍提示
if (res.data.showDoubleRewardTip) {
setTimeout(() => {
showDoubleRewardDialog.value = true;
}, 1500);
}, 500);
}
} else {
}
else {
ElMessage.info({
message: '😢 很遗憾,未中奖',
duration: 2000
duration: 2000,
});
}
// 刷新任务状态
await fetchTaskStatus();
} catch (error: any) {
}
catch (error: any) {
ElMessage.error(error?.message || '翻牌失败');
} finally {
}
finally {
flipping.value = false;
setTimeout(() => {
flippingCards.value.delete(record.flipNumber);
}, 800);
}, 300);
}
}
}
@@ -112,17 +120,23 @@ function showWinAnimation() {
// 创建烟花效果
const container = document.querySelector('.card-flip-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');
confetti.className = 'confetti';
confetti.style.left = Math.random() * 100 + '%';
confetti.style.animationDelay = Math.random() * 0.5 + 's';
confetti.style.background = ['#f56c6c', '#409eff', '#67c23a', '#e6a23c', '#9c27b0'][Math.floor(Math.random() * 5)];
confetti.style.left = `${Math.random() * 100}%`;
confetti.style.animationDelay = `${Math.random() * 0.5}s`;
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);
setTimeout(() => {
confetti.remove();
}, 2000);
}, 3000);
}
}
}
@@ -141,9 +155,11 @@ async function handleUseInviteCode() {
inviteCodeDialog.value = false;
inputInviteCode.value = '';
await fetchTaskStatus();
} catch (error: any) {
}
catch (error: any) {
ElMessage.error(error?.message || '使用邀请码失败');
} finally {
}
finally {
loading.value = false;
}
}
@@ -155,9 +171,11 @@ async function handleGenerateInviteCode() {
await generateMyInviteCode();
ElMessage.success('✨ 邀请码生成成功!');
await fetchTaskStatus();
} catch (error: any) {
}
catch (error: any) {
ElMessage.error(error?.message || '生成邀请码失败');
} finally {
}
finally {
loading.value = false;
}
}
@@ -189,7 +207,8 @@ function getCardClass(record: CardFlipRecord): string[] {
classes.push('card-flipped');
if (record.isWin) {
classes.push('card-win');
} else {
}
else {
classes.push('card-lose');
}
}
@@ -201,7 +220,8 @@ function getCardClass(record: CardFlipRecord): string[] {
// 可点击的卡片(任何未翻过的牌都可以点击)
if (!record.isFlipped && (taskData.value?.canFlip || false)) {
classes.push('card-clickable');
} else if (!record.isFlipped) {
}
else if (!record.isFlipped) {
classes.push('card-locked');
}
@@ -216,100 +236,13 @@ function toggleInviteSection() {
<template>
<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">
<el-button
type="primary"
:icon="showInviteSection ? 'ArrowUp' : 'Gift'"
@click="toggleInviteSection"
class="invite-toggle-btn"
@click="toggleInviteSection"
>
{{ showInviteSection ? '收起' : '邀请码' }}
</el-button>
@@ -317,8 +250,8 @@ function toggleInviteSection() {
<el-button
type="warning"
icon="Unlock"
@click="inviteCodeDialog = true"
class="use-code-btn"
@click="inviteCodeDialog = true"
>
使用邀请码
</el-button>
@@ -359,6 +292,125 @@ function toggleInviteSection() {
</div>
</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
v-model="inviteCodeDialog"
@@ -369,7 +421,9 @@ function toggleInviteSection() {
class="invite-dialog"
>
<div class="invite-dialog-content">
<p class="dialog-tip">请输入好友的邀请码解锁最后2次翻牌机会</p>
<p class="dialog-tip">
请输入好友的邀请码解锁最后2次翻牌机会
</p>
<el-input
v-model="inputInviteCode"
placeholder="请输入8位邀请码"
@@ -384,8 +438,10 @@ function toggleInviteSection() {
</el-input>
</div>
<template #footer>
<el-button @click="inviteCodeDialog = false">取消</el-button>
<el-button type="primary" @click="handleUseInviteCode" :loading="loading">
<el-button @click="inviteCodeDialog = false">
取消
</el-button>
<el-button type="primary" :loading="loading" @click="handleUseInviteCode">
确认使用
</el-button>
</template>
@@ -401,16 +457,22 @@ function toggleInviteSection() {
>
<div class="double-reward-content">
<div class="double-icon-container">
<div class="double-icon">🎁</div>
<div class="double-sparkle"></div>
<div class="double-icon">
🎁
</div>
<div class="double-sparkle">
</div>
</div>
<h3 class="double-title">恭喜获得额外翻倍包!</h3>
<h3 class="double-title">
恭喜获得额外翻倍包!
</h3>
<p class="double-text">
您已获得第9次奖励<br>
<span class="highlight">第10次翻牌将获得翻倍奖励</span><br>
赶快翻开最后一张牌吧
</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>
</div>
@@ -422,8 +484,9 @@ function toggleInviteSection() {
.card-flip-container {
position: relative;
padding: 16px;
min-height: 500px;
max-height: 70vh;
min-height: 400px;
max-height: none;
height: 100%;
overflow-y: auto;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 16px;
@@ -464,10 +527,10 @@ function toggleInviteSection() {
background: rgba(255, 255, 255, 0.95);
border-radius: 12px;
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 {
transform: translateY(-2px);
transform: translateY(-2px) scale(1.02);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
}
}
@@ -567,11 +630,18 @@ function toggleInviteSection() {
&:not(.card-flipping):not(.card-flipped) {
.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 {
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%;
width: 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;
pointer-events: none;
}
}
@@ -682,8 +753,6 @@ function toggleInviteSection() {
display: flex;
align-items: center;
justify-content: center;
position: relative;
top: -170px;
}
.card-result {
@@ -698,17 +767,18 @@ function toggleInviteSection() {
&.win {
position: relative;
overflow: hidden;
.win-glow {
position: absolute;
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;
}
.result-icon {
font-size: 32px;
animation: bounce 0.6s ease;
animation: bounce 0.6s ease, rotate 2s ease-in-out infinite;
}
.result-text {
@@ -716,6 +786,7 @@ function toggleInviteSection() {
font-weight: bold;
color: #f56c6c;
margin: 4px 0;
text-shadow: 0 2px 4px rgba(245, 108, 108, 0.3);
}
.result-amount {
@@ -725,6 +796,7 @@ function toggleInviteSection() {
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin: 2px 0;
animation: scaleUp 0.5s ease;
}
.result-unit {
@@ -777,16 +849,34 @@ function toggleInviteSection() {
.el-button {
flex: 1;
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 {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
&:hover {
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
}
}
.use-code-btn {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
border: none;
&:hover {
background: linear-gradient(135deg, #f5576c 0%, #f093fb 100%);
}
}
}
@@ -796,6 +886,7 @@ function toggleInviteSection() {
border-radius: 12px;
padding: 16px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
margin: 10px auto;
}
.invite-header {
@@ -1028,4 +1119,24 @@ function toggleInviteSection() {
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>

View File

@@ -189,6 +189,8 @@ function getProgressColor(task: DailyTaskItem): string {
.daily-task-container {
padding: 20px;
min-height: 400px;
height: 100%;
overflow-y: auto;
}
.task-header {

View File

@@ -273,6 +273,8 @@ function onProductPackage() {
.premium-service {
padding: 20px;
position: relative;
height: 100%;
overflow-y: auto;
}
.header {

View File

@@ -104,6 +104,11 @@ function contactCustomerService() {
innerVisibleContact.value = !innerVisibleContact.value;
}
// 暴露方法给父组件使用
defineExpose({
contactCustomerService,
});
// 过滤和排序后的数据
const filteredData = computed(() => {
let data = [...logData.value];
@@ -253,7 +258,6 @@ onMounted(() => {
</div>
<el-table
v-loading="loading"
:data="filteredData"
style="width: 100%"
@@ -265,7 +269,7 @@ onMounted(() => {
<el-table-column
prop="content"
label="套餐类型"
width="150"
min-width="150"
sortable="custom"
show-overflow-tooltip
/>
@@ -273,7 +277,7 @@ onMounted(() => {
show-overflow-tooltip
prop="rechargeAmount"
label="金额(元)"
width="110"
min-width="110"
sortable="custom"
>
<template #default="{ row }">
@@ -283,14 +287,14 @@ onMounted(() => {
<el-table-column
prop="creationTime"
label="充值时间"
width="160"
min-width="160"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="expireDateTime"
label="到期时间"
width="160"
min-width="160"
sortable="custom"
show-overflow-tooltip
/>
@@ -302,7 +306,7 @@ onMounted(() => {
<span v-else>{{ row.contactInfo || '-' }}</span>
</template>
</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 }">
<el-tooltip v-if="row.remark && row.remark.length > 10" :content="row.remark" placement="top">
<span class="ellipsis-text">{{ row.remark }}</span>
@@ -379,6 +383,8 @@ onMounted(() => {
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
height: 100%;
overflow-y: auto;
}
.recharge-log-container:hover {

View File

@@ -74,6 +74,8 @@ const gridTemplateColumns = computed(() => {
<style scoped>
.model-container {
margin: 10px 0;
height: 100%;
overflow-y: auto;
}
.model-header {

View File

@@ -494,7 +494,7 @@ onBeforeUnmount(() => {
</div>
</template>
<div class="chart-container">
<div ref="lineChart" class="chart" style="height: 400px;" />
<div ref="lineChart" class="chart" style="height: 350px;" />
</div>
</el-card>
@@ -505,7 +505,7 @@ onBeforeUnmount(() => {
</div>
</template>
<div class="chart-container">
<div ref="pieChart" class="chart" style="height: 450px;" />
<div ref="pieChart" class="chart" style="height: 400px;" />
</div>
</el-card>
@@ -516,7 +516,7 @@ onBeforeUnmount(() => {
</div>
</template>
<div class="chart-container">
<div ref="barChart" class="chart" style="height: 500px;" />
<div ref="barChart" class="chart" style="height: 450px;" />
</div>
</el-card>
</div>
@@ -524,13 +524,14 @@ onBeforeUnmount(() => {
<style scoped>
.usage-statistics {
padding: 30px;
padding: 20px;
position: relative;
background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%);
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
min-height: 100vh;
height: 100%;
overflow-y: auto;
}
.usage-statistics:hover {

View File

@@ -293,6 +293,8 @@ function bindWechat() {
<style scoped>
.user-profile {
padding: 20px;
height: 100%;
overflow-y: auto;
}
.header {

View File

@@ -2,6 +2,7 @@
<script setup lang="ts">
import { computed } from 'vue';
import { useRouter } from 'vue-router';
import { ChatLineRound } from '@element-plus/icons-vue';
import Popover from '@/components/Popover/index.vue';
import SvgIcon from '@/components/SvgIcon/index.vue';
import { useUserStore } from '@/stores';
@@ -58,6 +59,8 @@ const popoverList = ref([
]);
const dialogVisible = ref(false);
const rechargeLogRef = ref();
const activeNav = ref('user');
const navItems = [
{ name: 'user', label: '用户信息', icon: 'User' },
@@ -82,6 +85,12 @@ function handleConfirm(activeNav: string) {
// 导航切换
function handleNavChange(nav: string) {
activeNav.value = nav;
}
// 联系售后
function handleContactSupport() {
rechargeLogRef.value?.contactCustomerService();
}
// 点击
@@ -317,6 +326,16 @@ function onProductPackage() {
@confirm="handleConfirm"
@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>
<user-management />
@@ -354,7 +373,7 @@ function onProductPackage() {
<card-flip-activity />
</template>
<template #rechargeLog>
<recharge-log />
<recharge-log ref="rechargeLogRef" />
</template>
</nav-dialog>
</div>