修复权限验证问题:普通用户无法访问后台管理页面

This commit is contained in:
AIGC Developer
2025-10-23 09:59:54 +08:00
parent a294f61f3c
commit 08b737b1ef
59 changed files with 3586 additions and 607 deletions

View File

@@ -15,7 +15,7 @@
<el-icon><Compass /></el-icon>
<span>会员订阅</span>
</div>
<div class="nav-item" :class="{ active: currentSection === 'works' }" @click="setSection('works')" @mousedown="console.log('mousedown 我的作品')">
<div class="nav-item" @click="goToMyWorks">
<el-icon><Document /></el-icon>
<span>我的作品</span>
</div>
@@ -29,15 +29,15 @@
<!-- 工具菜单 -->
<nav class="tools-menu">
<div class="nav-item" @click="goToTextToVideo">
<el-icon><Document /></el-icon>
<el-icon><VideoPlay /></el-icon>
<span>文生视频</span>
</div>
<div class="nav-item" @click="goToImageToVideo">
<el-icon><Picture /></el-icon>
<span>图生视频</span>
</div>
<div class="nav-item storyboard-item">
<el-icon><VideoPlay /></el-icon>
<div class="nav-item storyboard-item" @click="goToStoryboardVideo">
<el-icon><Film /></el-icon>
<span>分镜视频</span>
<el-tag size="small" type="primary" class="sora-tag">Sora2.0</el-tag>
</div>
@@ -50,146 +50,199 @@
<MyWorks />
</template>
<template v-else>
<!-- 顶部 两层合并为一个盒子 -->
<section class="top-merged-card">
<!-- 上层用户信息 + 右侧按钮 -->
<div class="row-top">
<div class="user-left">
<div class="avatar-wrap">
<div class="avatar-circle">
<div class="pause-line"></div>
<div class="pause-line second"></div>
<!-- 顶部 两层合并为一个盒子 -->
<section class="top-merged-card">
<!-- 上层用户信息 + 右侧按钮 -->
<div class="row-top">
<div class="user-left">
<div class="avatar-wrap">
<div class="avatar-circle">
<div class="pause-line"></div>
<div class="pause-line second"></div>
</div>
</div>
<div class="user-meta">
<div class="username">mingzi_FBx7foZYDS7inLQb</div>
<div class="user-id">ID 2994509784706419</div>
</div>
</div>
<div class="user-meta">
<div class="username">mingzi_FBx7foZYDS7inLQb</div>
<div class="user-id">ID 2994509784706419</div>
<div class="user-right">
<div class="points-pill">
<div class="star-icon">
<el-icon><Star /></el-icon>
</div>
<span>50</span>
</div>
<button class="mini-btn" @click="goToOrderDetails">积分详情</button>
<button class="mini-btn" @click="goToWorks">我的订单</button>
</div>
</div>
<div class="user-right">
<div class="points-pill">
<el-icon><Plus /></el-icon>
<span>50</span>
<!-- 下层三项总结 -->
<div class="row-bottom">
<div class="summary-item">
<div class="summary-label">当前生效权益</div>
<div class="summary-value">免费版</div>
</div>
<button class="mini-btn">积分详情</button>
<button class="mini-btn" @click="goToWorks">我的订单</button>
</div>
</div>
<!-- 下层三项总结 -->
<div class="row-bottom">
<div class="summary-item">
<div class="summary-label">当前生效权益</div>
<div class="summary-value">免费版</div>
</div>
<div class="divider-v"></div>
<div class="summary-item">
<div class="summary-label">到期时间</div>
<div class="summary-value">永久</div>
</div>
<div class="divider-v"></div>
<div class="summary-item">
<div class="summary-label">剩余积分</div>
<div class="summary-value highlight">
<el-icon class="plus-icon"><Plus /></el-icon>
<span class="points-number">50</span>
<div class="divider-v"></div>
<div class="summary-item">
<div class="summary-label">到期时间</div>
<div class="summary-value">永久</div>
</div>
<div class="divider-v"></div>
<div class="summary-item">
<div class="summary-label">剩余积分</div>
<div class="summary-value highlight">
<div class="star-icon">
<el-icon><Star /></el-icon>
</div>
<span class="points-number">50</span>
</div>
</div>
</div>
</div>
</section>
</section>
<!-- 套餐选择 -->
<section class="subscription-packages">
<h3 class="section-title">套餐</h3>
<div class="packages-grid">
<!-- 免费版 -->
<div class="package-card free-card" :class="{ selected: selectedPlan === 'free' }" @click="selectPlan('free')">
<div class="package-header">
<h4 class="package-title">免费版</h4>
</div>
<div class="package-price">¥0/</div>
<button class="package-button current">当前套餐</button>
<div class="package-features">
<div class="feature-item">
<el-icon class="check-icon"><Check /></el-icon>
<span>新用户首次登陆免费获得50积分</span>
</div>
</div>
</div>
<!-- 套餐选择 -->
<section class="subscription-packages">
<h3 class="section-title">套餐</h3>
<!-- 标准版 -->
<div class="package-card standard-card" :class="{ selected: selectedPlan === 'standard' }" @click="selectPlan('standard')">
<div class="package-header">
<h4 class="package-title">标准版</h4>
<div class="discount-tag">首购低至8.5</div>
<div class="packages-grid">
<!-- 免费版 -->
<div class="package-card free-card" :class="{ selected: selectedPlan === 'free' }" @click="selectPlan('free')">
<div class="package-header">
<h4 class="package-title">免费版</h4>
</div>
<div class="package-price">$0/</div>
<button class="package-button current">当前套餐</button>
<div class="package-features">
<div class="feature-item">
<el-icon class="check-icon"><Check /></el-icon>
<span>新用户首次登陆免费获得50积分</span>
</div>
</div>
</div>
<div class="package-price">$59/</div>
<div class="points-box">每月200积分</div>
<button class="package-button subscribe">立即订阅</button>
<div class="package-features">
<div class="feature-item">
<el-icon class="check-icon"><Check /></el-icon>
<span>快速通道生成</span>
<!-- 标准版 -->
<div class="package-card standard-card" :class="{ selected: selectedPlan === 'standard' }" @click="selectPlan('standard')">
<div class="package-header">
<h4 class="package-title">标准版</h4>
<div class="discount-tag">首购低至8.5</div>
</div>
<div class="feature-item">
<el-icon class="check-icon"><Check /></el-icon>
<span>支持商用</span>
<div class="package-price">$59/</div>
<div class="points-box">每月200积分</div>
<button class="package-button subscribe">立即订阅</button>
<div class="package-features">
<div class="feature-item">
<el-icon class="check-icon"><Check /></el-icon>
<span>快速通道生成</span>
</div>
<div class="feature-item">
<el-icon class="check-icon"><Check /></el-icon>
<span>支持商用</span>
</div>
<div class="feature-item">
<el-icon class="check-icon"><Check /></el-icon>
<span>下载去水印</span>
</div>
</div>
<div class="feature-item">
<el-icon class="check-icon"><Check /></el-icon>
<span>下载去水印</span>
</div>
<!-- 专业版 -->
<div class="package-card premium-card" :class="{ selected: selectedPlan === 'premium' }" @click="selectPlan('premium')">
<div class="package-header">
<h4 class="package-title">专业版</h4>
<div class="value-tag">超值之选</div>
</div>
<div class="package-price">$259/</div>
<div class="points-box">每月1000积分</div>
<button class="package-button premium">立即订阅</button>
<div class="package-features">
<div class="feature-item">
<el-icon class="check-icon"><Check /></el-icon>
<span>极速通道生成</span>
</div>
<div class="feature-item">
<el-icon class="check-icon"><Check /></el-icon>
<span>支持商用</span>
</div>
<div class="feature-item">
<el-icon class="check-icon"><Check /></el-icon>
<span>下载去水印</span>
</div>
<div class="feature-item">
<el-icon class="check-icon"><Check /></el-icon>
<span>新功能优先体验</span>
</div>
</div>
</div>
</div>
<!-- 专业版 -->
<div class="package-card premium-card" :class="{ selected: selectedPlan === 'premium' }" @click="selectPlan('premium')">
<div class="package-header">
<h4 class="package-title">专业版</h4>
<div class="value-tag">超值之选</div>
</div>
<div class="package-price">$259/</div>
<div class="points-box">每月1000积分</div>
<button class="package-button premium">立即订阅</button>
<div class="package-features">
<div class="feature-item">
<el-icon class="check-icon"><Check /></el-icon>
<span>极速通道生成</span>
</div>
<div class="feature-item">
<el-icon class="check-icon"><Check /></el-icon>
<span>支持商用</span>
</div>
<div class="feature-item">
<el-icon class="check-icon"><Check /></el-icon>
<span>下载去水印</span>
</div>
<div class="feature-item">
<el-icon class="check-icon"><Check /></el-icon>
<span>新功能优先体验</span>
</div>
</div>
</div>
</div>
</section>
</section>
</template>
</main>
</div>
<!-- 订单详情模态框 -->
<el-dialog
v-model="orderDialogVisible"
title="订单详情"
width="80%"
class="order-dialog"
:modal="true"
:close-on-click-modal="true"
:close-on-press-escape="true"
@close="handleOrderDialogClose"
>
<div class="order-content">
<div class="order-summary">
<h3>账户订单总览</h3>
<div class="summary-stats">
<div class="stat-item">
<span class="stat-label">总订单数</span>
<span class="stat-value">{{ orders.length }}</span>
</div>
<div class="stat-item">
<span class="stat-label">总金额</span>
<span class="stat-value">¥{{ totalAmount }}</span>
</div>
</div>
</div>
<div class="orders-list">
<div class="order-item" v-for="order in orders" :key="order.id">
<div class="order-header">
<span class="order-id">订单号{{ order.id }}</span>
<span class="order-status" :class="order.status">{{ order.statusText }}</span>
</div>
<div class="order-details">
<div class="order-info">
<p><strong>创建时间</strong>{{ order.createdAt }}</p>
<p><strong>订单类型</strong>{{ order.type }}</p>
<p><strong>金额</strong>¥{{ order.amount }}</p>
</div>
<div class="order-actions">
<el-button type="primary" size="small" @click="viewOrderDetail(order)">查看详情</el-button>
</div>
</div>
</div>
</div>
</div>
</el-dialog>
</template>
<script setup>
import { ref } from 'vue'
import { ref, computed } from 'vue'
import MyWorks from '@/views/MyWorks.vue'
import { useRouter } from 'vue-router'
import {
User,
Compass,
Document,
Picture,
VideoPlay,
Plus,
User as Plus,
Bell,
Check
Check,
Compass,
VideoPlay,
Picture,
Film,
Star
} from '@element-plus/icons-vue'
const router = useRouter()
@@ -200,6 +253,10 @@ const goToProfile = () => {
router.push('/profile')
}
const goToMyWorks = () => {
router.push('/works')
}
const goToTextToVideo = () => {
router.push('/text-to-video')
}
@@ -208,11 +265,64 @@ const goToImageToVideo = () => {
router.push('/image-to-video')
}
// 当前主区块subscription | works
const currentSection = ref('subscription')
const setSection = (section) => {
console.log('切换区块到:', section)
currentSection.value = section
const goToStoryboardVideo = () => {
router.push('/storyboard-video')
}
// 订单模态框相关
const orderDialogVisible = ref(false)
const orders = ref([
{
id: 'ORD-2024-001',
status: 'completed',
statusText: '已完成',
createdAt: '2024-01-15 10:30:00',
type: '标准版订阅',
amount: 59.00
},
{
id: 'ORD-2024-002',
status: 'pending',
statusText: '待支付',
createdAt: '2024-01-20 14:20:00',
type: '专业版订阅',
amount: 259.00
},
{
id: 'ORD-2024-003',
status: 'completed',
statusText: '已完成',
createdAt: '2024-01-25 09:15:00',
type: '积分充值',
amount: 100.00
}
])
// 计算总金额
const totalAmount = computed(() => {
return orders.value.reduce((sum, order) => sum + order.amount, 0).toFixed(2)
})
// 显示订单详情模态框
const goToOrderDetails = () => {
console.log('点击积分详情,显示订单详情模态框')
orderDialogVisible.value = true
}
// 关闭订单模态框
const handleOrderDialogClose = () => {
orderDialogVisible.value = false
}
// 查看订单详情
const viewOrderDetail = (order) => {
console.log('查看订单详情:', order)
// 这里可以添加查看订单详情的逻辑
}
// 跳转到我的作品页面
const goToWorks = () => {
router.push('/works')
}
// 选中套餐(紫色边框)
@@ -342,7 +452,10 @@ const selectPlan = (plan) => {
/* 套餐选择 */
.subscription-packages {
padding: 0 40px 30px; /* 与顶部盒子保持一致的左右留白 */
padding: 0 30px 30px; /* 与顶部盒子保持一致的左右留白 */
margin: 0 auto; /* 居中显示,与上方盒子对齐 */
max-width: calc(100% - 80px); /* 限制最大宽度,与上方盒子一致 */
margin-left: 15px; /* 增加左边距,与上方盒子更精确对齐 */
}
.subscription-packages .section-title {
@@ -370,7 +483,7 @@ const selectPlan = (plan) => {
grid-template-columns: repeat(3, 1fr);
gap: 24px;
max-width: 1440px;
margin: 0 40px 0 0; /* 右侧留白与左侧 padding(40px) 保持一致 */
margin: 0 30px 0 0; /* 右侧留白与左侧 padding(30px) 保持一致 */
width: 100%;
align-items: stretch; /* 卡片等高 */
}
@@ -503,9 +616,9 @@ const selectPlan = (plan) => {
/* 顶部合并的两层盒子 */
.top-merged-card {
max-width: none; /* 取消限制,铺满内容区域 */
max-width: calc(100% - 80px); /* 限制最大宽度减去套餐区域向右移动的80px */
width: 100%;
margin: 32px 40px 10px 40px; /* 左右各 40px 留白,与套餐区对齐 */
margin: 20px auto; /* 上下20px左右自动居中 */
background: #1a1a1a; /* 与卡片、页面风格保持一致 */
border: 1px solid #333;
border-radius: 10px;
@@ -515,8 +628,8 @@ const selectPlan = (plan) => {
display: flex;
align-items: center;
justify-content: space-between;
gap: 26px;
padding: 22px 26px; /* 比例放大 */
gap: 20px; /* 与个人主页保持一致 */
padding: 30px; /* 与个人主页保持一致 */
border-bottom: 1px solid #1f2937;
}
@@ -524,7 +637,7 @@ const selectPlan = (plan) => {
display: grid;
grid-template-columns: 1fr auto 1fr auto 1fr;
gap: 30px;
padding: 22px 26px 24px; /* 比例放大 */
padding: 30px; /* 与row-top保持一致 */
align-items: center;
}
@@ -546,6 +659,17 @@ const selectPlan = (plan) => {
.user-id { font-size:15px; color:#9ca3af; }
.user-right { display:flex; align-items:center; gap:12px; }
.points-pill { display:flex; align-items:center; gap:6px; padding:9px 14px; border-radius:999px; background:#0b1220; border:1px solid #1f3758; color:#60a5fa; font-weight:600; font-size:16px; }
.star-icon {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
background: #3b82f6;
border-radius: 50%;
color: white;
font-size: 12px;
}
.mini-btn { background:#0f172a; color:#e5e7eb; border:1px solid #334155; padding:9px 14px; border-radius:6px; font-size:14px; cursor:pointer; transition:.2s ease; }
.mini-btn:hover { background:#111827; border-color:#3b82f6; }
@@ -554,4 +678,108 @@ const selectPlan = (plan) => {
.top-merged-card .row-top { flex-direction: column; align-items: flex-start; gap: 10px; }
.top-merged-card .row-bottom { grid-template-columns: 1fr; gap: 12px; }
}
/* 订单详情模态框样式 */
.order-dialog {
background: #1a1a1a;
color: white;
}
.order-content {
background: #1a1a1a;
color: white;
}
.order-summary {
margin-bottom: 30px;
padding: 20px;
background: #2a2a2a;
border-radius: 8px;
}
.order-summary h3 {
color: white;
margin: 0 0 15px 0;
font-size: 18px;
}
.summary-stats {
display: flex;
gap: 30px;
}
.stat-item {
display: flex;
flex-direction: column;
gap: 5px;
}
.stat-label {
color: #9ca3af;
font-size: 14px;
}
.stat-value {
color: #60a5fa;
font-size: 16px;
font-weight: 600;
}
.orders-list {
display: flex;
flex-direction: column;
gap: 15px;
}
.order-item {
background: #2a2a2a;
border-radius: 8px;
padding: 20px;
border: 1px solid #333;
}
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.order-id {
color: white;
font-weight: 600;
}
.order-status {
padding: 4px 12px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.order-status.completed {
background: #10b981;
color: white;
}
.order-status.pending {
background: #f59e0b;
color: white;
}
.order-details {
display: flex;
justify-content: space-between;
align-items: center;
}
.order-info p {
margin: 5px 0;
color: #d1d5db;
font-size: 14px;
}
.order-info strong {
color: white;
}
</style>