feat: 视频问题

This commit is contained in:
2025-11-28 20:26:47 +08:00
parent 46b98e78e5
commit d9f3103304
26 changed files with 1582 additions and 1021 deletions

View File

@@ -1,22 +1,7 @@
<template>
<div class="login-container">
<!-- 科技感背景光点 -->
<div class="bg-stars">
<div
v-for="(star, index) in stars"
:key="index"
class="star"
:class="{ active: star.active }"
:style="{
left: star.left + '%',
top: star.top + '%',
width: star.size + 'px',
height: star.size + 'px'
}"
@mouseenter="activateStar(index)"
@mouseleave="deactivateStar(index)"
></div>
</div>
<!-- 静态背景 -->
<div class="bg-static"></div>
<div class="login-content">
<!-- 左侧品牌区 -->
@@ -26,12 +11,12 @@
<svg width="80" height="80" viewBox="0 0 80 80">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6B8AFF;stop-opacity:1" />
<stop offset="100%" style="stop-color:#A855F7;stop-opacity:1" />
<stop offset="0%" style="stop-color:#3B82F6;stop-opacity:1" />
<stop offset="100%" style="stop-color:#60A5FA;stop-opacity:1" />
</linearGradient>
</defs>
<circle cx="40" cy="40" r="35" fill="none" stroke="url(#grad1)" stroke-width="2"/>
<path d="M 40 20 L 40 40 L 55 40" stroke="url(#grad1)" stroke-width="3" fill="none" stroke-linecap="round"/>
<circle cx="40" cy="40" r="35" fill="none" stroke="url(#grad1)" stroke-width="2" opacity="0.6"/>
<path d="M 40 20 L 40 40 L 55 40" stroke="url(#grad1)" stroke-width="3" fill="none" stroke-linecap="round" opacity="0.8"/>
</svg>
</div>
<h1 class="brand-title">金牌内容大师</h1>
@@ -61,120 +46,73 @@
<p class="login-subtitle">进入您的创作空间</p>
</div>
<a-tabs v-model:activeKey="activeTab" centered class="login-tabs">
<a-tab-pane key="sms" tab="短信登录">
<a-form
:model="smsForm"
:rules="smsRules"
ref="smsFormRef"
layout="vertical"
<a-form
:model="smsForm"
:rules="smsRules"
ref="smsFormRef"
layout="vertical"
>
<a-form-item name="mobile" label="手机号">
<a-input
v-model:value="smsForm.mobile"
size="large"
placeholder="请输入手机号"
:maxlength="11"
class="tech-input"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
>
<a-form-item name="mobile" label="手机号">
<a-input
v-model:value="smsForm.mobile"
size="large"
placeholder="请输入手机号"
:maxlength="11"
class="tech-input"
>
<template #prefix>
<PhoneOutlined class="input-icon" />
</template>
</a-input>
</a-form-item>
<template #prefix>
<PhoneOutlined class="input-icon" />
</template>
</a-input>
</a-form-item>
<a-form-item name="code" label="验证码">
<div class="code-input">
<a-input
v-model:value="smsForm.code"
size="large"
placeholder="请输入验证码"
:maxlength="6"
class="tech-input"
>
<template #prefix>
<SafetyOutlined class="input-icon" />
</template>
</a-input>
<a-button
type="primary"
:disabled="codeCountdown > 0"
:loading="sendingCode"
@click="sendSmsCode"
class="send-code-btn"
>
{{ codeCountdown > 0 ? `${codeCountdown}s后重发` : '发送验证码' }}
</a-button>
</div>
</a-form-item>
<a-form-item name="code" label="验证码">
<div class="code-input">
<a-input
v-model:value="smsForm.code"
size="large"
placeholder="请输入验证码"
:maxlength="4"
class="tech-input"
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
>
<template #prefix>
<SafetyOutlined class="input-icon" />
</template>
</a-input>
<a-button
type="primary"
:disabled="codeCountdown > 0"
:loading="sendingCode"
@click="sendSmsCode"
class="send-code-btn"
>
{{ codeCountdown > 0 ? `${codeCountdown}s后重发` : '发送验证码' }}
</a-button>
</div>
</a-form-item>
<a-form-item>
<a-button
type="primary"
html-type="submit"
size="large"
block
:loading="loggingIn"
@click="handleSmsLogin"
class="login-btn"
>
立即登录
</a-button>
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane key="password" tab="密码登录">
<a-form
:model="passwordForm"
:rules="passwordRules"
ref="passwordFormRef"
layout="vertical"
<a-form-item>
<a-button
type="primary"
html-type="submit"
size="large"
block
:loading="loggingIn"
@click="handleSmsLogin"
class="login-btn"
>
<a-form-item name="mobile" label="手机号">
<a-input
v-model:value="passwordForm.mobile"
size="large"
placeholder="请输入手机号"
:maxlength="11"
class="tech-input"
>
<template #prefix>
<PhoneOutlined class="input-icon" />
</template>
</a-input>
</a-form-item>
<a-form-item name="password" label="密码">
<a-input-password
v-model:value="passwordForm.password"
size="large"
placeholder="请输入密码"
@pressEnter="handlePasswordLogin"
class="tech-input"
>
<template #prefix>
<LockOutlined class="input-icon" />
</template>
</a-input-password>
</a-form-item>
<a-form-item>
<a-button
type="primary"
html-type="submit"
size="large"
block
:loading="loggingIn"
@click="handlePasswordLogin"
class="login-btn"
>
立即登录
</a-button>
</a-form-item>
</a-form>
</a-tab-pane>
</a-tabs>
立即登录
</a-button>
</a-form-item>
</a-form>
</div>
</div>
</div>
@@ -182,20 +120,19 @@
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ref, reactive } from 'vue'
import { useRouter } from 'vue-router'
import { message } from 'ant-design-vue'
import {
PhoneOutlined,
SafetyOutlined,
LockOutlined
SafetyOutlined
} from '@ant-design/icons-vue'
import authService from '@/services/AuthService'
import authApi, { SMS_SCENE } from '@/api/auth'
import tokenManager from '@gold/utils/token-manager'
import { useUserStore } from '@/stores/user'
const router = useRouter()
// 标签页
const activeTab = ref('sms')
const userStore = useUserStore()
window.a = router
// 短信登录表单
const smsForm = reactive({
@@ -204,13 +141,6 @@ const smsForm = reactive({
})
const smsFormRef = ref()
// 密码登录表单
const passwordForm = reactive({
mobile: '',
password: ''
})
const passwordFormRef = ref()
// 状态
const sendingCode = ref(false)
const loggingIn = ref(false)
@@ -219,9 +149,6 @@ const codeCountdown = ref(0)
// 验证码倒计时
let countdownTimer = null
// 背景光点
const stars = ref([])
// 表单验证规则
const smsRules = {
mobile: [
@@ -230,51 +157,17 @@ const smsRules = {
],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ len: 6, message: '验证码为6位数字', trigger: 'blur' }
{ pattern: /^\d{4}$/, message: '验证码为4位数字', trigger: 'blur' }
]
}
const passwordRules = {
mobile: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
{ min: 4, max: 16, message: '密码长度为4-16位', trigger: 'blur' }
]
}
// 初始化背景光点
onMounted(() => {
const starCount = 50
for (let i = 0; i < starCount; i++) {
stars.value.push({
left: Math.random() * 100,
top: Math.random() * 100,
size: Math.random() * 3 + 1,
active: false
})
}
})
// 激活光点
function activateStar(index) {
stars.value[index].active = true
}
// 停用光点
function deactivateStar(index) {
stars.value[index].active = false
}
// 发送验证码
async function sendSmsCode() {
try {
await smsFormRef.value.validateFields(['mobile'])
sendingCode.value = true
await authService.sendSmsCode(smsForm.mobile, 1)
await authApi.sendSmsCode(smsForm.mobile, SMS_SCENE.MEMBER_LOGIN)
message.success('验证码已发送')
@@ -305,41 +198,30 @@ async function handleSmsLogin() {
await smsFormRef.value.validateFields()
loggingIn.value = true
const result = await authService.loginBySms(smsForm.mobile, smsForm.code)
const info = await authApi.loginBySms(smsForm.mobile, smsForm.code)
// 更新用户登录状态
const userInfo = info?.userInfo || {}
await userStore.loginWithPhone({
profile: {
userId: String(userInfo.id || userInfo.userId || smsForm.mobile),
nickname: userInfo.nickname || userInfo.username || '用户',
avatar: userInfo.avatar || '',
balance: userInfo.balance ?? 0,
vipLevel: userInfo.vipLevel ?? 0,
credits: userInfo.credits ?? 0,
}
})
// 获取完整的用户信息
await userStore.fetchUserInfo()
message.success('登录成功')
// 获取redirect参数跳转到之前的页面或默认首页
const redirect = router.currentRoute.value.query.redirect
setTimeout(() => {
router.push(redirect || '/content-style/benchmark')
}, 500)
} catch (error) {
if (error?.response?.data?.message) {
message.error(error.response.data.message)
} else {
message.error('登录失败,请检查输入信息')
}
} finally {
loggingIn.value = false
}
}
// 密码登录
async function handlePasswordLogin() {
try {
await passwordFormRef.value.validateFields()
loggingIn.value = true
await authService.loginByPassword(passwordForm.mobile, passwordForm.password)
message.success('登录成功')
// 获取redirect参数跳转到之前的页面或默认首页
const redirect = router.currentRoute.value.query.redirect
setTimeout(() => {
router.push(redirect || '/content-style/benchmark')
}, 500)
router.push({
name: '对标分析'
})
} catch (error) {
if (error?.response?.data?.message) {
message.error(error.response.data.message)
@@ -360,29 +242,6 @@ async function handlePasswordLogin() {
overflow: hidden;
}
/* 背景光点 */
.bg-stars {
position: absolute;
width: 100%;
height: 100%;
pointer-events: none;
}
.star {
position: absolute;
background: linear-gradient(135deg, #6B8AFF 0%, #A855F7 100%);
border-radius: 50%;
opacity: 0.3;
transition: all 0.3s ease;
cursor: pointer;
}
.star.active {
opacity: 0.8;
transform: scale(1.5);
box-shadow: 0 0 20px rgba(107, 138, 255, 0.6);
}
/* 主内容区 */
.login-content {
display: flex;
@@ -397,70 +256,21 @@ async function handlePasswordLogin() {
align-items: center;
justify-content: center;
padding: 60px;
position: relative;
overflow: hidden;
}
.brand-panel::before {
content: '';
position: absolute;
width: 500px;
height: 500px;
background: radial-gradient(circle, rgba(107, 138, 255, 0.1) 0%, transparent 70%);
border-radius: 50%;
top: -100px;
right: -100px;
animation: pulse 4s ease-in-out infinite;
}
.brand-panel::after {
content: '';
position: absolute;
width: 400px;
height: 400px;
background: radial-gradient(circle, rgba(168, 85, 247, 0.1) 0%, transparent 70%);
border-radius: 50%;
bottom: -50px;
left: -50px;
animation: pulse 4s ease-in-out infinite 2s;
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
opacity: 0.5;
}
50% {
transform: scale(1.1);
opacity: 0.8;
}
}
.brand-inner {
position: relative;
z-index: 1;
text-align: center;
max-width: 400px;
}
.brand-icon {
margin-bottom: 30px;
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
}
.brand-title {
font-size: 48px;
font-weight: 700;
background: linear-gradient(135deg, #6B8AFF 0%, #A855F7 100%);
background: linear-gradient(135deg, #3B82F6 0%, #60A5FA 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
@@ -469,9 +279,9 @@ async function handlePasswordLogin() {
.brand-subtitle {
font-size: 18px;
color: #6B8AFF;
color: #3B82F6;
margin-bottom: 50px;
opacity: 0.8;
opacity: 0.9;
}
.brand-features {
@@ -489,7 +299,7 @@ async function handlePasswordLogin() {
}
.feature-icon {
color: #A855F7;
color: #3B82F6;
font-size: 20px;
}
@@ -499,113 +309,92 @@ async function handlePasswordLogin() {
display: flex;
align-items: center;
justify-content: center;
padding: 60px;
padding: 60px 80px;
background: #0a0a0a;
}
.login-card {
width: 100%;
max-width: 420px;
background: rgba(26, 26, 46, 0.6);
backdrop-filter: blur(20px);
border-radius: 24px;
padding: 50px 40px;
border: 1px solid rgba(107, 138, 255, 0.2);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
background: rgba(18, 18, 38, 0.88);
backdrop-filter: blur(25px);
border-radius: 20px;
padding: 40px 32px;
border: 1px solid rgba(59, 130, 246, 0.25);
box-shadow:
0 12px 40px rgba(0, 0, 0, 0.5),
inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
.card-header {
text-align: center;
margin-bottom: 40px;
margin-bottom: 32px;
}
.login-title {
font-size: 32px;
font-weight: 700;
color: #A855F7;
color: #3B82F6;
margin-bottom: 10px;
}
.login-subtitle {
font-size: 14px;
color: #6B8AFF;
color: #60A5FA;
margin: 0;
opacity: 0.8;
opacity: 0.9;
}
/* 标签页 */
.login-tabs {
margin-bottom: 20px;
/* 输入框样式覆盖 */
:deep(.ant-input-affix-wrapper) {
border-radius: 12px !important;
background: rgba(15, 15, 30, 0.5) !important;
border: 1.5px solid rgba(255, 255, 255, 0.1) !important;
box-shadow: none !important;
}
.login-tabs :deep(.ant-tabs-nav) {
margin-bottom: 30px;
:deep(.ant-input-affix-wrapper:hover) {
border-color: rgba(59, 130, 246, 0.4) !important;
}
.login-tabs :deep(.ant-tabs-tab) {
font-size: 15px;
font-weight: 500;
padding: 10px 24px;
color: #666;
}
.login-tabs :deep(.ant-tabs-tab-active) {
color: #A855F7;
}
.login-tabs :deep(.ant-tabs-ink-bar) {
background: linear-gradient(135deg, #6B8AFF 0%, #A855F7 100%);
height: 3px;
}
/* 输入框 */
.tech-input {
border-radius: 12px;
border: 1.5px solid rgba(255, 255, 255, 0.1);
background: rgba(15, 15, 30, 0.5);
transition: all 0.3s;
}
.tech-input:hover {
border-color: rgba(107, 138, 255, 0.5);
}
.tech-input:focus,
.tech-input-focused {
border-color: transparent;
background: rgba(15, 15, 30, 0.8);
box-shadow: 0 0 0 2px rgba(107, 138, 255, 0.2);
:deep(.ant-input-affix-wrapper-focused) {
border-color: rgba(59, 130, 246, 0.25) !important;
background: rgba(15, 15, 30, 0.95) !important;
box-shadow: 0 0 0 1px rgba(59, 130, 246, 0.25) !important;
}
:deep(.ant-input) {
color: #fff;
font-size: 15px;
border: none !important;
background: transparent !important;
}
:deep(.ant-input::placeholder) {
color: rgba(160, 160, 160, 0.65);
font-size: 14px;
}
:deep(.ant-input-prefix) {
color: rgba(107, 138, 255, 0.8);
color: rgba(59, 130, 246, 0.7);
}
:deep(.ant-form-item-label > label) {
font-weight: 500;
color: #a0a0a0;
color: #e0e0e0;
letter-spacing: 0.2px;
}
:deep(.ant-input-affix-wrapper) {
border-radius: 12px;
border: 1.5px solid rgba(255, 255, 255, 0.1);
background: rgba(15, 15, 30, 0.5);
transition: all 0.3s;
/* 键盘焦点优化 */
:deep(.ant-input-affix-wrapper:focus-within) {
outline: 2px solid rgba(59, 130, 246, 0.6);
outline-offset: 2px;
}
:deep(.ant-input-affix-wrapper:hover) {
border-color: rgba(107, 138, 255, 0.5);
}
:deep(.ant-input-affix-wrapper-focused) {
border-color: transparent;
background: rgba(15, 15, 30, 0.8);
box-shadow: 0 0 0 2px rgba(107, 138, 255, 0.2);
/* 表单验证错误状态 */
:deep(.ant-form-item-has-error .ant-input-affix-wrapper) {
border-color: rgba(255, 77, 79, 0.5) !important;
}
/* 验证码输入 */
@@ -618,15 +407,29 @@ async function handlePasswordLogin() {
border-radius: 12px;
white-space: nowrap;
flex-shrink: 0;
background: linear-gradient(135deg, rgba(107, 138, 255, 0.2) 0%, rgba(168, 85, 247, 0.2) 100%);
border: 1px solid rgba(107, 138, 255, 0.3);
color: #6B8AFF;
background: linear-gradient(135deg, rgba(59, 130, 246, 0.15) 0%, rgba(96, 165, 250, 0.15) 100%);
border: 1px solid rgba(59, 130, 246, 0.25);
color: #3B82F6;
font-weight: 500;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.send-code-btn:hover {
background: linear-gradient(135deg, rgba(107, 138, 255, 0.3) 0%, rgba(168, 85, 247, 0.3) 100%);
border-color: rgba(107, 138, 255, 0.5);
.send-code-btn:hover:not(:disabled) {
background: linear-gradient(135deg, rgba(59, 130, 246, 0.3) 0%, rgba(96, 165, 250, 0.3) 100%);
border-color: rgba(59, 130, 246, 0.6);
color: #60A5FA;
box-shadow: 0 4px 16px rgba(59, 130, 246, 0.25);
}
.send-code-btn:active:not(:disabled) {
box-shadow: 0 2px 8px rgba(59, 130, 246, 0.15);
}
.send-code-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* 登录按钮 */
@@ -635,9 +438,9 @@ async function handlePasswordLogin() {
font-size: 16px;
font-weight: 600;
border-radius: 12px;
background: linear-gradient(135deg, #6B8AFF 0%, #A855F7 100%);
background: linear-gradient(135deg, #3B82F6 0%, #60A5FA 100%);
border: none;
box-shadow: 0 4px 20px rgba(107, 138, 255, 0.3);
box-shadow: 0 4px 20px rgba(59, 130, 246, 0.25);
position: relative;
overflow: hidden;
}
@@ -658,12 +461,7 @@ async function handlePasswordLogin() {
}
.login-btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 30px rgba(107, 138, 255, 0.5);
}
.login-btn:active {
transform: translateY(0);
box-shadow: 0 6px 30px rgba(59, 130, 246, 0.35);
}
/* 图标 */
@@ -671,27 +469,189 @@ async function handlePasswordLogin() {
font-size: 18px;
}
/* 响应式 */
@media (max-width: 992px) {
.login-content {
flex-direction: column;
/* 背景 */
.bg-static {
position: absolute;
inset: 0;
background:
radial-gradient(circle at 30% 30%, rgba(59, 130, 246, 0.03) 0%, transparent 50%),
radial-gradient(circle at 70% 70%, rgba(96, 165, 250, 0.03) 0%, transparent 50%);
pointer-events: none;
}
/* 响应式优化 - 大屏保持左右分栏 */
@media (max-width: 1200px) {
.brand-title {
font-size: 40px;
}
.brand-subtitle {
font-size: 16px;
margin-bottom: 40px;
}
}
/* 平板横屏优化 */
@media (max-width: 992px) and (min-width: 769px) {
.brand-panel {
padding: 40px 20px;
min-height: 200px;
min-height: 140px;
padding: 25px 15px;
}
.login-panel {
padding: 40px 20px;
.brand-icon svg {
width: 50px;
height: 50px;
}
.brand-title {
font-size: 36px;
font-size: 28px;
margin-bottom: 8px;
}
.brand-subtitle {
font-size: 14px;
margin-bottom: 20px;
}
.feature-item {
font-size: 13px;
gap: 8px;
}
.login-card {
padding: 30px 20px;
padding: 28px 24px;
max-width: 360px;
margin: 0 auto;
}
}
/* 平板竖屏和手机横屏 */
@media (max-width: 768px) {
.login-content {
flex-direction: column;
height: 100vh;
}
.brand-panel {
padding: 20px 15px;
min-height: 100px;
flex: none;
}
.brand-inner {
max-width: 100%;
}
.brand-icon svg {
width: 45px;
height: 45px;
}
.brand-title {
font-size: 24px;
margin-bottom: 5px;
}
.brand-subtitle {
font-size: 12px;
margin-bottom: 15px;
}
.feature-item {
font-size: 12px;
gap: 6px;
}
.feature-item:nth-child(n+2) {
display: none;
}
.login-panel {
flex: 1;
display: flex;
align-items: flex-start;
justify-content: center;
padding: 20px 15px 40px;
overflow-y: auto;
}
.login-card {
width: 100%;
max-width: 380px;
padding: 32px 28px;
margin: 0 auto;
background: rgba(18, 18, 38, 0.92);
backdrop-filter: blur(32px);
border-radius: 18px;
border: 1px solid rgba(59, 130, 246, 0.3);
box-shadow:
0 10px 36px rgba(0, 0, 0, 0.55),
inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.login-title {
font-size: 24px;
}
.login-subtitle {
font-size: 13px;
}
}
/* 小屏手机优化 */
@media (max-width: 480px) {
.brand-panel {
min-height: 80px;
padding: 15px 10px;
}
.brand-icon svg {
width: 40px;
height: 40px;
}
.brand-title {
font-size: 20px;
}
.brand-subtitle {
display: none;
}
.feature-item:nth-child(1) {
display: none;
}
.login-panel {
padding: 15px 10px 30px;
}
.login-card {
padding: 28px 20px;
border-radius: 16px;
max-width: 92%;
}
.card-header {
margin-bottom: 20px;
}
.login-title {
font-size: 22px;
}
.code-input {
flex-direction: column;
gap: 8px;
}
.send-code-btn {
width: 100%;
}
.login-btn {
height: 48px;
font-size: 15px;
}
}
</style>