8.7更新

This commit is contained in:
hrx 2026-08-07 16:38:45 +08:00
parent cd8e267c30
commit 9d430f08ab
21 changed files with 794 additions and 179 deletions

View File

@ -1,7 +1,10 @@
.news-view-page { .news-view-page {
position: relative; position: relative;
width: 100%;
max-width: 100%;
min-width: 0;
min-height: 100vh; min-height: 100vh;
overflow-x: hidden; overflow-x: clip;
color: #1f2937; color: #1f2937;
background: background:
radial-gradient(circle at 78% 18%, rgba(236, 72, 153, 0.07) 0%, rgba(236, 72, 153, 0) 32%), radial-gradient(circle at 78% 18%, rgba(236, 72, 153, 0.07) 0%, rgba(236, 72, 153, 0) 32%),
@ -12,13 +15,15 @@
.news-shell { .news-shell {
position: relative; position: relative;
z-index: 1; z-index: 1;
max-width: 1280px; box-sizing: border-box;
width: 100%;
max-width: 1400px;
margin: 0 auto; margin: 0 auto;
padding: 0 24px; padding: 0 24px;
} }
.orb { .orb {
position: fixed; position: absolute;
z-index: 0; z-index: 0;
border-radius: 50%; border-radius: 50%;
filter: blur(110px); filter: blur(110px);
@ -196,6 +201,8 @@
.news-featured, .news-featured,
.news-item { .news-item {
position: relative; position: relative;
width: 100%;
max-width: 100%;
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
background: #fff; background: #fff;
@ -253,11 +260,19 @@
.news-featured-inner { .news-featured-inner {
display: grid; display: grid;
grid-template-columns: 1fr 2fr; grid-template-columns: minmax(280px, 380px) minmax(0, 1fr);
min-height: 180px; align-items: stretch;
width: 100%;
min-height: 280px;
}
.news-featured-img {
position: relative;
overflow: hidden;
width: 100%;
min-height: 280px;
} }
.news-featured-img,
.news-item-img { .news-item-img {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
@ -280,6 +295,8 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
min-width: 0;
width: 100%;
padding: 24px 40px; padding: 24px 40px;
} }
@ -491,6 +508,15 @@
align-items: flex-start; align-items: flex-start;
} }
.news-featured-inner {
grid-template-columns: 1fr;
}
.news-featured-img {
width: 100%;
min-height: 220px;
}
.filter-tabs { .filter-tabs {
display: flex; display: flex;
border-radius: 20px; border-radius: 20px;
@ -502,15 +528,10 @@
font-size: 14px; font-size: 14px;
} }
.news-featured-inner,
.news-grid { .news-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.news-featured-img {
height: 220px;
}
.news-featured-body { .news-featured-body {
padding: 24px; padding: 24px;
} }

View File

@ -401,5 +401,59 @@ export default {
openedDecision: 'The Investment Strategy Agent page has been opened.<br>Anything else you would like to know?', openedDecision: 'The Investment Strategy Agent page has been opened.<br>Anything else you would like to know?',
openedPage: 'The related page has been opened. Anything else you would like to know?' openedPage: 'The related page has been opened. Anything else you would like to know?'
}, },
loginDialog: {
welcomeLogin: 'Welcome',
createAccount: 'Create Account',
signIn: 'Sign In',
signUp: 'Sign Up',
signInWithPassword: 'Password',
signInWithCode: 'Verification Code',
enterAccount: 'Enter account',
enterPassword: 'Enter password',
enterMobile: 'Enter mobile number',
enterVerificationCode: 'Enter verification code',
enterAccountName: 'Enter account name',
forgotPassword: 'Forgot password?',
noAccountSignUp: 'No account? Sign Up',
signInNow: 'Sign In',
getVerificationCode: 'Get Verification Code',
agreePrefix: 'I have read and agree to the ',
userAgreement: 'User Agreement',
agreeAnd: ' and ',
privacyPolicy: 'Privacy Policy',
signUpNow: 'Sign Up',
hasAccountSignIn: 'Already have an account? Sign In',
signingIn: 'Signing in...',
signingUp: 'Signing up...',
resendCode: 'Resend {seconds}s',
mobileRequired: 'Enter mobile number',
mobileInvalid: 'Please enter a valid mobile number',
accountRequired: 'Enter account',
passwordRequired: 'Enter password',
codeRequired: 'Enter verification code',
accountNameRequired: 'Enter account name',
agreeRequired: 'Please read and agree to the agreements first',
codeSent: 'Verification code sent',
codeSendFail: 'Failed to get verification code',
loginFail: 'Sign in failed',
loginSuccess: 'Signed in successfully',
registerSuccess: 'Registration successful. Please sign in.',
registerFail: 'Registration failed',
registerRetry: 'Registration failed. Please try again.',
resetPassword: 'Reset Password',
resetPasswordDesc: 'Verify identity via mobile verification code to set a new password',
mobileLabel: 'Mobile Number',
newPasswordLabel: 'New Password',
verificationCodeLabel: 'Verification Code',
enterBoundMobile: 'Enter bound mobile number',
enterNewPassword: 'Enter new password',
cancel: 'Cancel',
confirmReset: 'Confirm Reset',
newPasswordRequired: 'Enter new password',
getCodeFirst: 'Please get the verification code first',
resetSuccess: 'Password reset successfully',
resetFail: 'Password reset failed',
codeSentCheck: 'Verification code sent. Please check your messages.'
},
...autoMessages ...autoMessages
} }

View File

@ -401,5 +401,59 @@ export default {
openedDecision: '已为您打开投策智能体页面 📊<br>还有其他想了解的吗?', openedDecision: '已为您打开投策智能体页面 📊<br>还有其他想了解的吗?',
openedPage: '已为您打开相关页面。还有其他想了解的吗?' openedPage: '已为您打开相关页面。还有其他想了解的吗?'
}, },
loginDialog: {
welcomeLogin: '欢迎登录',
createAccount: '创建账号',
signIn: '登录',
signUp: '注册',
signInWithPassword: '密码登录',
signInWithCode: '验证码登录',
enterAccount: '请输入账户',
enterPassword: '请输入密码',
enterMobile: '请输入手机号',
enterVerificationCode: '请输入验证码',
enterAccountName: '请输入账户名',
forgotPassword: '忘记密码?',
noAccountSignUp: '没有账号?去注册',
signInNow: '立即登录',
getVerificationCode: '获取验证码',
agreePrefix: '我已阅读并同意',
userAgreement: '《用户协议》',
agreeAnd: '、',
privacyPolicy: '《隐私政策》',
signUpNow: '立即注册',
hasAccountSignIn: '已有账号?前往登录',
signingIn: '登录中...',
signingUp: '注册中...',
resendCode: '重新发送 {seconds}s',
mobileRequired: '请输入手机号',
mobileInvalid: '请输入正确的手机号',
accountRequired: '请输入账户',
passwordRequired: '请输入密码',
codeRequired: '请输入验证码',
accountNameRequired: '请输入账户名',
agreeRequired: '请先阅读并同意相关协议',
codeSent: '验证码已发送',
codeSendFail: '验证码获取失败',
loginFail: '登录失败',
loginSuccess: '登录成功',
registerSuccess: '注册成功,请登录',
registerFail: '注册失败',
registerRetry: '注册失败,请重试',
resetPassword: '重置密码',
resetPasswordDesc: '通过手机号验证码验证身份后设置新密码',
mobileLabel: '手机号',
newPasswordLabel: '新密码',
verificationCodeLabel: '验证码',
enterBoundMobile: '请输入绑定手机号',
enterNewPassword: '请输入新密码',
cancel: '取消',
confirmReset: '确认重置',
newPasswordRequired: '请输入新密码',
getCodeFirst: '请先获取验证码',
resetSuccess: '密码重置成功',
resetFail: '密码重置失败',
codeSentCheck: '验证码已发送,请注意查收。'
},
...autoMessages ...autoMessages
} }

View File

@ -12,6 +12,7 @@ import 'xterm/css/xterm.css'
// import 'lib-flexible/flexible' // import 'lib-flexible/flexible'
import '@/styles/index.scss' // global css import '@/styles/index.scss' // global css
import '@/assets/less/news/news.less'
import globalMixin from '@/mixin/globalMixin'; // 替换为实际的文件路径 import globalMixin from '@/mixin/globalMixin'; // 替换为实际的文件路径

View File

@ -1,6 +1,9 @@
import Vue from 'vue' import Vue from 'vue'
import Router from 'vue-router' import Router from 'vue-router'
import store from '@/store' import store from '@/store'
import { scheduleHomePageLayoutReset, resetHomePageLayout } from '@/utils/resetHomePageLayout'
const HOME_ROUTE_RE = /^\/(homePage|ncmatchHome)(\/|$)/
/* Layout */ /* Layout */
import Layout from '@/layout' import Layout from '@/layout'
import GpuProduct from '@/views/product/productHome/capitalOnline/productItem/GpuProduct/index.vue' import GpuProduct from '@/views/product/productHome/capitalOnline/productItem/GpuProduct/index.vue'
@ -2618,10 +2621,18 @@ const router = createRouter()
// 在beforeRouteLeave导航守卫中清空面包屑状态 // 在beforeRouteLeave导航守卫中清空面包屑状态
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
// 清空面包屑状态的代码 if (HOME_ROUTE_RE.test(to.path) || HOME_ROUTE_RE.test(from.path)) {
// store.commit('tagsView/resetBreadcrumbState'); resetHomePageLayout()
}
next() next()
}) })
router.afterEach((to) => {
if (!HOME_ROUTE_RE.test(to.path)) return
scheduleHomePageLayoutReset()
window.setTimeout(() => scheduleHomePageLayoutReset(), 900)
})
window.addEventListener('beforeunload', function() { window.addEventListener('beforeunload', function() {
// 清空面包屑状态的代码 // 清空面包屑状态的代码
store.commit('tagsView/resetBreadcrumbState') store.commit('tagsView/resetBreadcrumbState')

View File

@ -0,0 +1,140 @@
// 官网 shellhtml/body 不滚动 #homeOut 内部纵向滚动彻底避免 SPA 跳转后 scrollLeft 残留
html.home-page-shell-active,
body.home-page-shell-active {
width: 100% !important;
max-width: 100% !important;
height: 100% !important;
overflow: hidden !important;
margin: 0 !important;
padding: 0 !important;
}
html.home-page-shell-active #app,
body.home-page-shell-active #app {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
height: 100% !important;
overflow: hidden !important;
}
.home-page-shell {
display: flex;
flex-direction: column;
width: 100%;
max-width: 100%;
min-width: 0;
height: 100vh;
height: 100dvh;
overflow: hidden;
position: relative;
isolation: isolate;
}
.home-page-shell > #topBox,
.home-page-shell > #topBox.top-nav,
#topBox.top-nav {
flex: 0 0 auto;
min-width: 0 !important;
max-width: 100% !important;
width: 100% !important;
}
.home-page-shell #homeOut {
flex: 1 1 auto;
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
min-height: 0 !important;
overflow-x: hidden !important;
overflow-y: auto !important;
overscroll-behavior-x: none;
margin: 0 !important;
padding: 0 !important;
}
.home-page-shell .home-route-stage,
.home-page-shell .home-router-view {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
overflow-x: hidden !important;
box-sizing: border-box;
}
.home-page-shell .home-router-view > * {
width: 100%;
max-width: 100%;
min-width: 0;
box-sizing: border-box;
}
.home-page-shell .train-page,
.home-page-shell .agent-store-page,
.home-page-shell .jd-homepage,
.home-page-shell .product-service-page,
.home-page-shell .news-view-page,
.home-page-shell .about-page,
.home-page-shell .home-main-page {
overflow-x: hidden;
}
.home-page-shell .about-page,
.home-page-shell .about-container {
width: 100%;
max-width: 100%;
min-width: 0;
box-sizing: border-box;
}
.home-page-shell .grid-bg {
width: 100% !important;
max-width: 100% !important;
margin-left: 0 !important;
margin-right: 0 !important;
}
.home-page-shell .product-service-page.single-product-page {
height: auto !important;
min-height: calc(100vh - 82px);
overflow-y: visible !important;
overflow-x: hidden !important;
}
.home-page-shell .nav-container {
max-width: 100%;
}
.home-page-shell .news-shell {
width: 100%;
max-width: 1400px;
margin-left: auto;
margin-right: auto;
box-sizing: border-box;
}
.home-page-shell .news-featured-inner {
display: grid !important;
grid-template-columns: minmax(280px, 380px) minmax(0, 1fr);
align-items: stretch;
width: 100%;
min-height: 280px;
}
.home-page-shell .news-featured-body {
width: 100%;
min-width: 0;
}
.home-page-shell .news-featured,
.home-page-shell .news-item {
width: 100%;
max-width: 100%;
}
.home-page-shell .footer {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}

View File

@ -0,0 +1,182 @@
const HOME_SHELL_CLASS = 'home-page-shell-active'
const RESET_TARGETS = [
'.home-page-shell',
'#homeOut',
'.home-route-stage',
'.home-router-view'
]
let scrollLockBound = false
let homeOutObserver = null
function attachHomeOutScrollListener() {
const homeOut = getHomeOut()
if (!homeOut || homeOut.dataset.scrollLockBound === '1') return
homeOut.dataset.scrollLockBound = '1'
homeOut.addEventListener('scroll', lockHomeOutHorizontalScroll, { passive: true })
}
export function enableHomePageBodyScroll() {
document.documentElement.classList.add(HOME_SHELL_CLASS)
document.body.classList.add(HOME_SHELL_CLASS)
bindHomeOutScrollLock()
bindHomeOutResizeObserver()
attachHomeOutScrollListener()
}
export function disableHomePageBodyScroll() {
document.documentElement.classList.remove(HOME_SHELL_CLASS)
document.body.classList.remove(HOME_SHELL_CLASS)
if (homeOutObserver) {
homeOutObserver.disconnect()
homeOutObserver = null
}
}
function getHomeOut() {
return document.getElementById('homeOut')
}
function getHomeScroller() {
return getHomeOut() || document.documentElement
}
function clearInlineLayoutStyles(element) {
if (!element || !element.style) return
element.style.removeProperty('width')
element.style.removeProperty('max-width')
element.style.removeProperty('min-width')
element.style.removeProperty('transform')
element.style.removeProperty('margin-left')
element.style.removeProperty('left')
}
function resetElementScroll(element, resetVertical = true) {
if (!element) return
element.scrollLeft = 0
if (resetVertical) {
element.scrollTop = 0
}
}
function resetScrollableDescendants(root) {
if (!root || !root.querySelectorAll) return
root.querySelectorAll('*').forEach((element) => {
if (element.scrollLeft) {
element.scrollLeft = 0
}
})
}
function lockHomeOutHorizontalScroll() {
const homeOut = getHomeOut()
if (!homeOut) return
if (homeOut.scrollLeft !== 0) {
homeOut.scrollLeft = 0
}
resetElementScroll(document.documentElement, false)
resetElementScroll(document.body, false)
if (window.scrollX !== 0) {
window.scrollTo(0, 0)
}
}
export function resetHomePageLayout(resetVertical = true) {
const homeOut = getHomeOut()
RESET_TARGETS.forEach((selector) => {
document.querySelectorAll(selector).forEach((element) => {
resetElementScroll(element, resetVertical)
clearInlineLayoutStyles(element)
})
})
if (homeOut) {
resetElementScroll(homeOut, resetVertical)
clearInlineLayoutStyles(homeOut)
resetScrollableDescendants(homeOut)
}
resetElementScroll(document.documentElement, resetVertical)
resetElementScroll(document.body, resetVertical)
window.scrollTo(0, 0)
lockHomeOutHorizontalScroll()
}
export function scheduleHomePageLayoutReset(resetVertical = true) {
attachHomeOutScrollListener()
resetHomePageLayout(resetVertical)
window.requestAnimationFrame(() => resetHomePageLayout(resetVertical))
window.setTimeout(() => resetHomePageLayout(resetVertical), 0)
window.setTimeout(() => resetHomePageLayout(resetVertical), 80)
window.setTimeout(() => resetHomePageLayout(resetVertical), 200)
window.setTimeout(() => resetHomePageLayout(resetVertical), 500)
window.setTimeout(() => resetHomePageLayout(resetVertical), 900)
}
export function scrollHomeSection(id, offset = 82) {
const homeOut = getHomeOut()
const element = document.getElementById(id)
if (!element) return false
lockHomeOutHorizontalScroll()
if (homeOut) {
const top = element.getBoundingClientRect().top - homeOut.getBoundingClientRect().top + homeOut.scrollTop - offset
homeOut.scrollTo({
left: 0,
top: Math.max(top, 0),
behavior: 'smooth'
})
return true
}
const top = element.getBoundingClientRect().top + window.pageYOffset - offset
window.scrollTo({
left: 0,
top: Math.max(top, 0),
behavior: 'smooth'
})
return true
}
export function bindHomeOutScrollLock() {
if (scrollLockBound) return
scrollLockBound = true
document.addEventListener('scroll', (event) => {
if (!document.body.classList.contains(HOME_SHELL_CLASS)) return
if (event.target === getHomeOut()) {
lockHomeOutHorizontalScroll()
}
}, true)
window.addEventListener('resize', () => scheduleHomePageLayoutReset(true), { passive: true })
}
export function bindHomeOutResizeObserver() {
if (homeOutObserver || typeof ResizeObserver === 'undefined') return
homeOutObserver = new ResizeObserver(() => {
if (!document.body.classList.contains(HOME_SHELL_CLASS)) return
lockHomeOutHorizontalScroll()
})
const observe = () => {
const homeOut = getHomeOut()
if (homeOut) homeOutObserver.observe(homeOut)
}
observe()
window.setTimeout(observe, 0)
}
export const bindHomeHorizontalScrollLock = bindHomeOutScrollLock
export const bindHomeOutHorizontalScrollLock = bindHomeOutScrollLock
export function getHomeScrollContainer() {
return getHomeScroller()
}

View File

@ -13,31 +13,31 @@
<div class="brand-area"> <div class="brand-area">
<div class="brand-text"> <div class="brand-text">
<h2>{{ activeTab === 'login' ? '欢迎登录' : '创建账号' }}</h2> <h2>{{ activeTab === 'login' ? $t('loginDialog.welcomeLogin') : $t('loginDialog.createAccount') }}</h2>
</div> </div>
</div> </div>
<div class="tab-group"> <div class="tab-group">
<button type="button" :class="{ active: activeTab === 'login' }" @click="switchTab('login')">登录</button> <button type="button" :class="{ active: activeTab === 'login' }" @click="switchTab('login')">{{ $t('loginDialog.signIn') }}</button>
<button type="button" :class="{ active: activeTab === 'register' }" @click="switchTab('register')">注册</button> <button type="button" :class="{ active: activeTab === 'register' }" @click="switchTab('register')">{{ $t('loginDialog.signUp') }}</button>
</div> </div>
<div v-if="activeTab === 'login'" class="form-panel"> <div v-if="activeTab === 'login'" class="form-panel">
<div class="mode-tabs"> <div class="mode-tabs">
<button type="button" :class="{ active: loginMode === 'password' }" @click="switchLoginMode('password')">密码登录</button> <button type="button" :class="{ active: loginMode === 'password' }" @click="switchLoginMode('password')">{{ $t('loginDialog.signInWithPassword') }}</button>
<button type="button" :class="{ active: loginMode === 'mobile' }" @click="switchLoginMode('mobile')">验证码登录</button> <button type="button" :class="{ active: loginMode === 'mobile' }" @click="switchLoginMode('mobile')">{{ $t('loginDialog.signInWithCode') }}</button>
</div> </div>
<el-form ref="loginForm" :model="loginForm" :rules="activeLoginRules" label-position="top"> <el-form ref="loginForm" :model="loginForm" :rules="activeLoginRules" label-position="top">
<template v-if="loginMode === 'password'"> <template v-if="loginMode === 'password'">
<el-form-item prop="username"> <el-form-item prop="username">
<el-input v-model.trim="loginForm.username" placeholder="请输入账户" prefix-icon="el-icon-user" @keyup.enter.native="handleLogin" /> <el-input v-model.trim="loginForm.username" :placeholder="$t('loginDialog.enterAccount')" prefix-icon="el-icon-user" @keyup.enter.native="handleLogin" />
</el-form-item> </el-form-item>
<el-form-item prop="password"> <el-form-item prop="password">
<el-input <el-input
v-model="loginForm.password" v-model="loginForm.password"
:type="loginPwdVisible ? 'text' : 'password'" :type="loginPwdVisible ? 'text' : 'password'"
placeholder="请输入密码" :placeholder="$t('loginDialog.enterPassword')"
prefix-icon="el-icon-lock" prefix-icon="el-icon-lock"
@keyup.enter.native="handleLogin" @keyup.enter.native="handleLogin"
> >
@ -48,11 +48,11 @@
<template v-else> <template v-else>
<el-form-item prop="mobile"> <el-form-item prop="mobile">
<el-input v-model.trim="loginForm.mobile" placeholder="请输入手机号" prefix-icon="el-icon-mobile-phone" /> <el-input v-model.trim="loginForm.mobile" :placeholder="$t('loginDialog.enterMobile')" prefix-icon="el-icon-mobile-phone" />
</el-form-item> </el-form-item>
<el-form-item prop="vcode"> <el-form-item prop="vcode">
<div class="code-row"> <div class="code-row">
<el-input v-model.trim="loginForm.vcode" placeholder="请输入验证码" prefix-icon="el-icon-key" @keyup.enter.native="handleLogin" /> <el-input v-model.trim="loginForm.vcode" :placeholder="$t('loginDialog.enterVerificationCode')" prefix-icon="el-icon-key" @keyup.enter.native="handleLogin" />
<button type="button" class="code-btn" :disabled="loginCodeDisabled" @click="getLoginCode">{{ loginCodeText }}</button> <button type="button" class="code-btn" :disabled="loginCodeDisabled" @click="getLoginCode">{{ loginCodeText }}</button>
</div> </div>
</el-form-item> </el-form-item>
@ -60,36 +60,36 @@
</el-form> </el-form>
<div class="form-actions"> <div class="form-actions">
<button type="button" class="text-btn" @click="forgotPasswordVisible = true">忘记密码</button> <button type="button" class="text-btn" @click="forgotPasswordVisible = true">{{ $t('loginDialog.forgotPassword') }}</button>
<button type="button" class="text-btn" @click="switchTab('register')">没有账号去注册</button> <button type="button" class="text-btn" @click="switchTab('register')">{{ $t('loginDialog.noAccountSignUp') }}</button>
</div> </div>
<button type="button" class="main-btn" :disabled="loginLoading" @click="handleLogin"> <button type="button" class="main-btn" :disabled="loginLoading" @click="handleLogin">
{{ loginLoading ? '登录中...' : '立即登录' }} {{ loginLoading ? $t('loginDialog.signingIn') : $t('loginDialog.signInNow') }}
</button> </button>
</div> </div>
<div v-else class="form-panel"> <div v-else class="form-panel">
<el-form ref="registerForm" :model="registerForm" :rules="registerRules" label-position="top"> <el-form ref="registerForm" :model="registerForm" :rules="registerRules" label-position="top">
<el-form-item prop="mobile"> <el-form-item prop="mobile">
<el-input v-model.trim="registerForm.mobile" class="phone-input" placeholder="请输入手机号"> <el-input v-model.trim="registerForm.mobile" class="phone-input" :placeholder="$t('loginDialog.enterMobile')">
<span slot="prefix" class="country-prefix">+86</span> <span slot="prefix" class="country-prefix">+86</span>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item prop="vcode"> <el-form-item prop="vcode">
<div class="code-row"> <div class="code-row">
<el-input v-model.trim="registerForm.vcode" placeholder="请输入验证码" prefix-icon="el-icon-key" /> <el-input v-model.trim="registerForm.vcode" :placeholder="$t('loginDialog.enterVerificationCode')" prefix-icon="el-icon-key" />
<button type="button" class="code-btn" :disabled="regCodeDisabled" @click="getRegisterCode">{{ regCodeText }}</button> <button type="button" class="code-btn" :disabled="regCodeDisabled" @click="getRegisterCode">{{ regCodeText }}</button>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item prop="username"> <el-form-item prop="username">
<el-input v-model.trim="registerForm.username" placeholder="请输入账户名" prefix-icon="el-icon-user" /> <el-input v-model.trim="registerForm.username" :placeholder="$t('loginDialog.enterAccountName')" prefix-icon="el-icon-user" />
</el-form-item> </el-form-item>
<el-form-item prop="password"> <el-form-item prop="password">
<el-input <el-input
v-model="registerForm.password" v-model="registerForm.password"
:type="regPwdVisible ? 'text' : 'password'" :type="regPwdVisible ? 'text' : 'password'"
placeholder="请输入密码" :placeholder="$t('loginDialog.enterPassword')"
prefix-icon="el-icon-lock" prefix-icon="el-icon-lock"
@keyup.enter.native="handleRegister" @keyup.enter.native="handleRegister"
> >
@ -100,19 +100,18 @@
<div class="agreement-wrap"> <div class="agreement-wrap">
<el-checkbox v-model="registerForm.agree"> <el-checkbox v-model="registerForm.agree">
我已阅读并同意 {{ $t('loginDialog.agreePrefix') }}
<a class="agreement-link" :href="getAgreementUrl('user')" target="_blank" rel="noopener noreferrer" @click.stop>用户协议</a> <a class="agreement-link" :href="getAgreementUrl('user')" target="_blank" rel="noopener noreferrer" @click.stop>{{ $t('loginDialog.userAgreement') }}</a>
{{ $t('loginDialog.agreeAnd') }}
<a class="agreement-link" :href="getAgreementUrl('privacy')" target="_blank" rel="noopener noreferrer" @click.stop>隐私政策</a> <a class="agreement-link" :href="getAgreementUrl('privacy')" target="_blank" rel="noopener noreferrer" @click.stop>{{ $t('loginDialog.privacyPolicy') }}</a>
</el-checkbox> </el-checkbox>
</div> </div>
<button type="button" class="main-btn" :disabled="registerLoading" @click="handleRegister"> <button type="button" class="main-btn" :disabled="registerLoading" @click="handleRegister">
{{ registerLoading ? '注册中...' : '立即注册' }} {{ registerLoading ? $t('loginDialog.signingUp') : $t('loginDialog.signUpNow') }}
</button> </button>
<button type="button" class="bottom-link" @click="switchTab('login')">已有账号前往登录</button> <button type="button" class="bottom-link" @click="switchTab('login')">{{ $t('loginDialog.hasAccountSignIn') }}</button>
</div> </div>
</div> </div>
</el-dialog> </el-dialog>
@ -139,12 +138,6 @@ export default {
} }
}, },
data() { data() {
const validatePhone = (rule, value, callback) => {
if (!value) callback(new Error('请输入手机号'))
else if (!/^1[3-9]\d{9}$/.test(value)) callback(new Error('请输入正确的手机号'))
else callback()
}
return { return {
activeTab: 'login', activeTab: 'login',
loginMode: 'password', loginMode: 'password',
@ -155,8 +148,8 @@ export default {
regPwdVisible: false, regPwdVisible: false,
loginCodeDisabled: false, loginCodeDisabled: false,
regCodeDisabled: false, regCodeDisabled: false,
loginCodeText: '获取验证码', loginCodeText: '',
regCodeText: '获取验证码', regCodeText: '',
loginTimer: null, loginTimer: null,
regTimer: null, regTimer: null,
loginCount: 60, loginCount: 60,
@ -178,18 +171,6 @@ export default {
agree: false, agree: false,
wechat_openid: localStorage.getItem('wechat_openid') || '', wechat_openid: localStorage.getItem('wechat_openid') || '',
domain_name: window.location.hostname domain_name: window.location.hostname
},
loginRules: {
username: [{ required: true, message: '请输入账户', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
mobile: [{ required: true, validator: validatePhone, trigger: 'blur' }],
vcode: [{ required: true, message: '请输入验证码', trigger: 'blur' }]
},
registerRules: {
mobile: [{ required: true, validator: validatePhone, trigger: 'blur' }],
vcode: [{ required: true, message: '请输入验证码', trigger: 'blur' }],
username: [{ required: true, message: '请输入账户名', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
} }
} }
}, },
@ -213,13 +194,50 @@ export default {
username: this.loginRules.username, username: this.loginRules.username,
password: this.loginRules.password password: this.loginRules.password
} }
},
loginRules() {
return {
username: [{ required: true, message: this.$t('loginDialog.accountRequired'), trigger: 'blur' }],
password: [{ required: true, message: this.$t('loginDialog.passwordRequired'), trigger: 'blur' }],
mobile: [{ required: true, validator: this.validatePhone, trigger: 'blur' }],
vcode: [{ required: true, message: this.$t('loginDialog.codeRequired'), trigger: 'blur' }]
}
},
registerRules() {
return {
mobile: [{ required: true, validator: this.validatePhone, trigger: 'blur' }],
vcode: [{ required: true, message: this.$t('loginDialog.codeRequired'), trigger: 'blur' }],
username: [{ required: true, message: this.$t('loginDialog.accountNameRequired'), trigger: 'blur' }],
password: [{ required: true, message: this.$t('loginDialog.passwordRequired'), trigger: 'blur' }]
}
} }
}, },
watch: {
'$i18n.locale'() {
this.resetCodeButtonText()
}
},
created() {
this.resetCodeButtonText()
},
beforeDestroy() { beforeDestroy() {
clearInterval(this.loginTimer) clearInterval(this.loginTimer)
clearInterval(this.regTimer) clearInterval(this.regTimer)
}, },
methods: { methods: {
validatePhone(rule, value, callback) {
if (!value) callback(new Error(this.$t('loginDialog.mobileRequired')))
else if (!/^1[3-9]\d{9}$/.test(value)) callback(new Error(this.$t('loginDialog.mobileInvalid')))
else callback()
},
resetCodeButtonText() {
if (!this.loginCodeDisabled) {
this.loginCodeText = this.$t('loginDialog.getVerificationCode')
}
if (!this.regCodeDisabled) {
this.regCodeText = this.$t('loginDialog.getVerificationCode')
}
},
handleClose() { handleClose() {
this.loginLoading = false this.loginLoading = false
this.registerLoading = false this.registerLoading = false
@ -284,18 +302,18 @@ export default {
clearInterval(this[timerKey]) clearInterval(this[timerKey])
this[countKey] = 59 this[countKey] = 59
this[disabledKey] = true this[disabledKey] = true
this[textKey] = `重新发送 ${this[countKey]}s` this[textKey] = this.$t('loginDialog.resendCode', { seconds: this[countKey] })
this[timerKey] = setInterval(() => { this[timerKey] = setInterval(() => {
if (this[countKey] > 0) { if (this[countKey] > 0) {
this[countKey] -= 1 this[countKey] -= 1
this[textKey] = `重新发送 ${this[countKey]}s` this[textKey] = this.$t('loginDialog.resendCode', { seconds: this[countKey] })
return return
} }
clearInterval(this[timerKey]) clearInterval(this[timerKey])
this[timerKey] = null this[timerKey] = null
this[countKey] = 60 this[countKey] = 60
this[disabledKey] = false this[disabledKey] = false
this[textKey] = '获取验证码' this[textKey] = this.$t('loginDialog.getVerificationCode')
}, 1000) }, 1000)
}, },
getLoginCode() { getLoginCode() {
@ -306,12 +324,12 @@ export default {
if (res.status) { if (res.status) {
this.loginForm.codeid = res.codeid || (res.data && res.data.codeid) || '' this.loginForm.codeid = res.codeid || (res.data && res.data.codeid) || ''
this.startCountdown('login') this.startCountdown('login')
this.$message.success('验证码已发送') this.$message.success(this.$t('loginDialog.codeSent'))
} else { } else {
this.$message.error(res.msg || '验证码获取失败') this.$message.error(res.msg || this.$t('loginDialog.codeSendFail'))
} }
} catch (error) { } catch (error) {
this.$message.error('验证码获取失败') this.$message.error(this.$t('loginDialog.codeSendFail'))
} }
}) })
}, },
@ -323,12 +341,12 @@ export default {
if (res.status) { if (res.status) {
this.registerForm.codeid = res.codeid || (res.data && res.data.codeid) || res.data || '' this.registerForm.codeid = res.codeid || (res.data && res.data.codeid) || res.data || ''
this.startCountdown('register') this.startCountdown('register')
this.$message.success('验证码已发送') this.$message.success(this.$t('loginDialog.codeSent'))
} else { } else {
this.$message.error(res.msg || '验证码获取失败') this.$message.error(res.msg || this.$t('loginDialog.codeSendFail'))
} }
} catch (error) { } catch (error) {
this.$message.error('验证码获取失败') this.$message.error(this.$t('loginDialog.codeSendFail'))
} }
}) })
}, },
@ -340,12 +358,12 @@ export default {
try { try {
const check = await logintypeAPI(loginParams) const check = await logintypeAPI(loginParams)
if (!check.status) { if (!check.status) {
this.$message.error(check.msg || '登录失败') this.$message.error(check.msg || this.$t('loginDialog.loginFail'))
return return
} }
const res = await this.$store.dispatch('user/login', loginParams) const res = await this.$store.dispatch('user/login', loginParams)
if (!res.status) { if (!res.status) {
this.$message.error(res.msg || '登录失败') this.$message.error(res.msg || this.$t('loginDialog.loginFail'))
return return
} }
@ -377,12 +395,12 @@ export default {
}) })
router.addRoutes(accessRoutes) router.addRoutes(accessRoutes)
this.$message.success(res.msg || '登录成功') this.$message.success(res.msg || this.$t('loginDialog.loginSuccess'))
this.dialogVisible = false this.dialogVisible = false
this.$emit('success', res) this.$emit('success', res)
this.redirectAfterLogin(res) this.redirectAfterLogin(res)
} catch (error) { } catch (error) {
this.$message.error((error && error.msg) || '登录失败') this.$message.error((error && error.msg) || this.$t('loginDialog.loginFail'))
} finally { } finally {
this.loginLoading = false this.loginLoading = false
} }
@ -434,7 +452,7 @@ export default {
}, },
handleRegister() { handleRegister() {
if (!this.registerForm.agree) { if (!this.registerForm.agree) {
this.$message.warning('请先阅读并同意相关协议') this.$message.warning(this.$t('loginDialog.agreeRequired'))
return return
} }
this.$refs.registerForm.validate(async valid => { this.$refs.registerForm.validate(async valid => {
@ -454,17 +472,17 @@ export default {
} }
const res = await register(registerData) const res = await register(registerData)
if (res.status) { if (res.status) {
this.$message.success('注册成功,请登录') this.$message.success(this.$t('loginDialog.registerSuccess'))
this.loginForm.mobile = this.registerForm.mobile this.loginForm.mobile = this.registerForm.mobile
this.switchTab('login') this.switchTab('login')
this.loginMode = 'mobile' this.loginMode = 'mobile'
this.$refs.registerForm.resetFields() this.$refs.registerForm.resetFields()
this.registerForm.agree = false this.registerForm.agree = false
} else { } else {
this.$message.error(res.message || res.msg || '注册失败') this.$message.error(res.message || res.msg || this.$t('loginDialog.registerFail'))
} }
} catch (error) { } catch (error) {
this.$message.error('注册失败,请重试') this.$message.error(this.$t('loginDialog.registerRetry'))
} finally { } finally {
this.registerLoading = false this.registerLoading = false
} }
@ -628,7 +646,7 @@ export default {
} }
.form-panel ::v-deep .el-form-item { .form-panel ::v-deep .el-form-item {
margin-bottom: 16px; margin-bottom: 22px;
} }
.form-panel ::v-deep .el-form-item__error { .form-panel ::v-deep .el-form-item__error {

View File

@ -205,8 +205,13 @@
</template> </template>
<script> <script>
import { scheduleHomePageLayoutReset } from '@/utils/resetHomePageLayout'
export default { export default {
name: 'HomeAbout', name: 'HomeAbout',
mounted() {
scheduleHomePageLayoutReset()
},
methods: { methods: {
moveCardGlow(event) { moveCardGlow(event) {
const card = event.currentTarget const card = event.currentTarget

View File

@ -10,10 +10,8 @@
} }
.grid-bg { .grid-bg {
width: 100vw; width: 100%;
min-height: 100vh; min-height: 100vh;
margin-left: calc(50% - 50vw);
margin-right: calc(50% - 50vw);
overflow: hidden; overflow: hidden;
background: background:
linear-gradient(rgba(37, 99, 235, 0.05) 1px, transparent 1px), linear-gradient(rgba(37, 99, 235, 0.05) 1px, transparent 1px),

View File

@ -87,6 +87,7 @@
<script> <script>
import Talk from '@/views/homePage/dialog/talk/index.vue' import Talk from '@/views/homePage/dialog/talk/index.vue'
import { scheduleHomePageLayoutReset } from '@/utils/resetHomePageLayout'
export default { export default {
name: 'AgentStore', name: 'AgentStore',
@ -136,6 +137,9 @@ export default {
return result return result
} }
}, },
mounted() {
scheduleHomePageLayoutReset()
},
methods: { methods: {
setMainTab(tab) { setMainTab(tab) {
this.activeMainTab = tab this.activeMainTab = tab

View File

@ -54,7 +54,13 @@
<button v-if="!isNcmatchDomain" type="button" class="nav-item" @click.stop="goHomeAnchor('cases-section')"> <button v-if="!isNcmatchDomain" type="button" class="nav-item" @click.stop="goHomeAnchor('cases-section')">
{{ $t('topbar.cases') }} {{ $t('topbar.cases') }}
</button> </button>
<button v-if="!isNcmatchDomain" type="button" class="nav-item" @click.stop="goHomeAnchor('news')"> <button
v-if="!isNcmatchDomain"
type="button"
class="nav-item"
:class="{ active: isActiveNews }"
@click.stop="navigateTo('/homePage/news')"
>
{{ $t('topbar.news') }} {{ $t('topbar.news') }}
</button> </button>
<button <button
@ -253,6 +259,7 @@ import { gotoYuanJingAPI } from '@/api/gotoYuanJing'
import { setLocale } from '@/i18n' import { setLocale } from '@/i18n'
import MessageCenter from '@/components/MessageCenter/MessageCenter.vue' import MessageCenter from '@/components/MessageCenter/MessageCenter.vue'
import LoginDialog from '@/views/LoginDialog/LoginDialog.vue' import LoginDialog from '@/views/LoginDialog/LoginDialog.vue'
import { scheduleHomePageLayoutReset, scrollHomeSection } from '@/utils/resetHomePageLayout'
const LOCALE_KEY = 'kboss-locale' const LOCALE_KEY = 'kboss-locale'
const SUPPORTED_LOCALES = ['zh-CN', 'en-US'] const SUPPORTED_LOCALES = ['zh-CN', 'en-US']
@ -329,6 +336,9 @@ export default Vue.extend({
? this.$route.path.includes('/ncmatchHome/index') ? this.$route.path.includes('/ncmatchHome/index')
: this.$route.path.includes('/homePage/index') : this.$route.path.includes('/homePage/index')
}, },
isActiveNews() {
return this.$route.path.includes('/homePage/news')
},
langToggleText() { langToggleText() {
return this.activeLocale === 'en-US' ? 'EN/中' : '中/EN' return this.activeLocale === 'en-US' ? 'EN/中' : '中/EN'
}, },
@ -452,8 +462,18 @@ export default Vue.extend({
// ========================= // =========================
navigateTo(path) { navigateTo(path) {
this.closeProductPanelImmediate() this.closeProductPanelImmediate()
if (this.$route.path === path) return this.blurActiveElement()
this.$router.push(path).catch(() => {}) if (this.$route.path === path) {
scheduleHomePageLayoutReset()
return
}
this.$router.push(path).then(() => {
scheduleHomePageLayoutReset()
}).catch(() => {})
},
blurActiveElement() {
const active = document.activeElement
if (active && typeof active.blur === 'function') active.blur()
}, },
goHome() { goHome() {
this.closeProductPanelImmediate() this.closeProductPanelImmediate()
@ -462,21 +482,27 @@ export default Vue.extend({
}, },
goHomeAnchor(id) { goHomeAnchor(id) {
this.closeProductPanelImmediate() this.closeProductPanelImmediate()
const scrollToTarget = () => { this.blurActiveElement()
const target = document.getElementById(id) const scrollToTarget = (attempt = 0) => {
if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' }) if (scrollHomeSection(id)) return
if (attempt < 30) {
window.setTimeout(() => scrollToTarget(attempt + 1), 100)
}
} }
const startScroll = () => window.setTimeout(() => scrollToTarget(), 120)
if (this.$route.path.includes('/homePage/index')) { if (this.$route.path.includes('/homePage/index')) {
this.$nextTick(scrollToTarget) this.$nextTick(startScroll)
return return
} }
this.$router.push('/homePage/index').then(() => this.$nextTick(scrollToTarget)).catch(() => {}) this.$router.push({ path: '/homePage/index', query: { anchor: id } }).catch(() => {})
}, },
handleModelSquareClick() { handleModelSquareClick() {
this.closeProductPanelImmediate() this.closeProductPanelImmediate()
this.$router.push({ this.$router.push({
path: '/homePage/tokenMarket', path: '/homePage/tokenMarket',
query: { category: 'TOKEN市集', single: '1' } query: { category: 'TOKEN市集', single: '1' }
}).then(() => {
scheduleHomePageLayoutReset()
}).catch(() => {}) }).catch(() => {})
}, },
async goYuanjing() { async goYuanjing() {
@ -903,8 +929,19 @@ export default Vue.extend({
line-height: 82px; line-height: 82px;
font-weight: 600; font-weight: 600;
white-space: nowrap; white-space: nowrap;
outline: none;
-webkit-tap-highlight-color: transparent;
transition: color 0.2s ease; transition: color 0.2s ease;
&:focus {
outline: none;
}
&:focus-visible {
color: #1e6fff;
box-shadow: inset 0 -2px 0 #1e6fff;
}
&:hover, &:hover,
&.active { &.active {
color: #1e6fff; color: #1e6fff;

View File

@ -1,12 +1,19 @@
<template> <template>
<div id="homeOut" class="homeOut"> <div class="home-page-shell">
<TopBox id="topBox"></TopBox> <TopBox id="topBox"></TopBox>
<div class="home-router-view"> <div id="homeOut" class="homeOut">
<router-view></router-view> <div class="home-route-stage">
</div> <div class="home-router-view">
<!-- footer--> <router-view
<div class="footer"> :key="$route.fullPath"
@hook:mounted="handleChildRouteMounted"
@hook:updated="handleChildRouteMounted"
></router-view>
</div>
</div>
<!-- footer-->
<div class="footer">
<div class="footer-main"> <div class="footer-main">
<div class="footer-brand-col"> <div class="footer-brand-col">
<img <img
@ -96,12 +103,20 @@
<!-- </span>--> <!-- </span>-->
</div> </div>
</div> </div>
</div>
</div> </div>
</template> </template>
<script> <script>
import Vue from 'vue' import Vue from 'vue'
import TopBox from "@/views/homePage/components/topBox/index.vue"; import TopBox from "@/views/homePage/components/topBox/index.vue";
import {mapGetters, mapState} from "vuex"; import {mapGetters, mapState} from "vuex";
import '@/styles/home-page-shell.scss'
import {
disableHomePageBodyScroll,
enableHomePageBodyScroll,
scheduleHomePageLayoutReset,
scrollHomeSection
} from '@/utils/resetHomePageLayout'
export default Vue.extend({ export default Vue.extend({
name: "indexLast", name: "indexLast",
@ -247,21 +262,40 @@ export default Vue.extend({
}, },
}, },
mounted() {
enableHomePageBodyScroll()
scheduleHomePageLayoutReset()
},
beforeDestroy() {
disableHomePageBodyScroll()
},
watch: { watch: {
'$route.fullPath'() { '$route.fullPath'() {
scheduleHomePageLayoutReset()
this.$nextTick(() => { this.$nextTick(() => {
this.scrollHomeToTop() const anchor = this.$route.query.anchor
window.setTimeout(this.scrollHomeToTop, 0) if (anchor && this.$route.path.includes('/homePage/index')) {
window.setTimeout(() => {
scrollHomeSection(anchor)
}, 120)
return
}
scheduleHomePageLayoutReset()
}) })
} }
}, },
methods: { methods: {
handleChildRouteMounted() {
scheduleHomePageLayoutReset()
this.$nextTick(() => {
scheduleHomePageLayoutReset()
})
},
resetHomeLayout() {
scheduleHomePageLayoutReset()
},
scrollHomeToTop() { scrollHomeToTop() {
const container = this.$el scheduleHomePageLayoutReset()
if (container) container.scrollTop = 0
window.scrollTo(0, 0)
document.documentElement.scrollTop = 0
document.body.scrollTop = 0
}, },
goOut(url){ goOut(url){
window.open(url) window.open(url)
@ -271,13 +305,7 @@ export default Vue.extend({
this.$router.push(path) this.$router.push(path)
}, },
scrollToElement(id) { scrollToElement(id) {
const element = document.getElementById(id); scrollHomeSection(id)
if (element) {
element.scrollIntoView({
behavior: 'smooth', //
block: 'start', // 'start', 'center', 'end', 'nearest'
});
}
}, },
clickBaseMenu(menu) { clickBaseMenu(menu) {
console.log("emnu", menu) console.log("emnu", menu)
@ -333,43 +361,67 @@ export default Vue.extend({
<style scoped lang="scss"> <style scoped lang="scss">
.homeOut { .home-page-shell {
//padding-top: 60px; display: flex;
height: 100%; flex-direction: column;
overflow: auto !important; width: 100%;
min-width: 1500px; height: 100vh;
// background: linear-gradient(180deg, #f0f7ff 0%, #ffffff 60%, #f5f8ff 100%); height: 100dvh;
min-width: 0;
overflow: hidden;
} }
#topBox{
// height: 82px; .homeOut {
background: transparent!important; flex: 1 1 auto;
background-color: transparent!important; width: 100%;
box-shadow: none!important; min-width: 0;
border: 0!important; min-height: 0;
overflow-x: hidden;
overflow-y: auto;
}
.home-route-stage {
width: 100%;
max-width: 100%;
min-width: 0;
overflow-x: hidden;
}
#topBox {
background: transparent !important;
background-color: transparent !important;
box-shadow: none !important;
border: 0 !important;
} }
.home-router-view { .home-router-view {
width: 100%; width: 100%;
max-width: 100%;
min-width: 0;
padding-top: 0; padding-top: 0;
box-sizing: border-box; box-sizing: border-box;
overflow-x: hidden;
} }
.footer { .footer {
padding: 38px 0 22px; padding: 38px 0 22px;
width: 100%; width: 100%;
max-width: 100%;
display: flex; display: flex;
justify-content: center; justify-content: center;
flex-wrap: wrap; flex-wrap: wrap;
background: #f1f3f7; background: #f1f3f7;
box-sizing: border-box;
} }
.footer-main { .footer-main {
width: 1400px; width: 100%;
max-width: 1400px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: flex-start; align-items: flex-start;
gap: 38px; gap: 38px;
padding: 0 0 32px; padding: 0 24px 32px;
border-bottom: 1px solid rgba(122, 130, 160, 0.22); border-bottom: 1px solid rgba(122, 130, 160, 0.22);
box-sizing: border-box; box-sizing: border-box;
} }
@ -508,7 +560,8 @@ export default Vue.extend({
} }
.footer-record__text { .footer-record__text {
width: 1400px; width: 100%;
max-width: 1400px;
margin: 18px 0 0; margin: 18px 0 0;
display: flex; display: flex;
justify-content: center; justify-content: center;

View File

@ -174,6 +174,7 @@ import Vue from 'vue'
import { reqPublishProductSearchFirstPage, reqEnterpriseAuditInfoSearch, reqHomepageProductCategory, reqGetSupplyAndDemandSquareList } from '@/api/ncmatch' import { reqPublishProductSearchFirstPage, reqEnterpriseAuditInfoSearch, reqHomepageProductCategory, reqGetSupplyAndDemandSquareList } from '@/api/ncmatch'
import { mapGetters, mapState } from "vuex"; import { mapGetters, mapState } from "vuex";
import { gotoYuanJingAPI } from '@/api/gotoYuanJing' import { gotoYuanJingAPI } from '@/api/gotoYuanJing'
import { scheduleHomePageLayoutReset } from '@/utils/resetHomePageLayout'
export default Vue.extend({ export default Vue.extend({
name: "mainPage", name: "mainPage",
@ -186,6 +187,9 @@ export default Vue.extend({
created() { created() {
this.getHomepageProductCategory() this.getHomepageProductCategory()
}, },
mounted() {
scheduleHomePageLayoutReset()
},
data() { data() {
return { return {
boxLoading: false, boxLoading: false,

View File

@ -571,6 +571,8 @@ export default {
<style scoped lang="scss"> <style scoped lang="scss">
.home-main-page { .home-main-page {
min-height: 100vh; min-height: 100vh;
max-width: 100%;
min-width: 0;
background-color: #f0f7ff !important; background-color: #f0f7ff !important;
background-image: linear-gradient(180deg, #f0f7ff 0%, #ffffff 60%, #f5f8ff 100%) !important; background-image: linear-gradient(180deg, #f0f7ff 0%, #ffffff 60%, #f5f8ff 100%) !important;
color: #111827; color: #111827;

View File

@ -35,6 +35,7 @@
<script> <script>
import { reqNewsDetail } from '@/api/newsapi/newsapi' import { reqNewsDetail } from '@/api/newsapi/newsapi'
import { scheduleHomePageLayoutReset } from '@/utils/resetHomePageLayout'
export default { export default {
name: 'NewsDetail', name: 'NewsDetail',
@ -72,19 +73,12 @@ export default {
window.setTimeout(this.scrollToTop, 0) window.setTimeout(this.scrollToTop, 0)
}) })
}, },
mounted() {
scheduleHomePageLayoutReset()
},
methods: { methods: {
scrollToTop() { scrollToTop() {
window.scrollTo(0, 0) scheduleHomePageLayoutReset()
document.documentElement.scrollTop = 0
document.body.scrollTop = 0
const scrollContainers = [
document.querySelector('.homeOut'),
document.querySelector('.home-router-view'),
document.querySelector('#app')
].filter(Boolean)
scrollContainers.forEach((container) => {
container.scrollTop = 0
})
}, },
goBack() { goBack() {
this.$router.push('/homePage/news') this.$router.push('/homePage/news')

View File

@ -107,6 +107,7 @@
<script> <script>
import { reqNewsList } from '@/api/newsapi/newsapi' import { reqNewsList } from '@/api/newsapi/newsapi'
import { scheduleHomePageLayoutReset } from '@/utils/resetHomePageLayout'
export default { export default {
name: 'NewsView', name: 'NewsView',
data() { data() {
@ -157,6 +158,12 @@ export default {
created() { created() {
this.getNewsList() this.getNewsList()
}, },
mounted() {
scheduleHomePageLayoutReset()
},
activated() {
scheduleHomePageLayoutReset()
},
methods: { methods: {
goAbout() { goAbout() {
this.$router.push('/homePage/about') this.$router.push('/homePage/about')
@ -274,12 +281,12 @@ export default {
this.newsList = [] this.newsList = []
this.total = 0 this.total = 0
this.$message.error(this.$t('newsView.loadFail')) this.$message.error(this.$t('newsView.loadFail'))
} finally {
this.$nextTick(() => {
scheduleHomePageLayoutReset()
})
} }
} }
} }
} }
</script> </script>
<style scoped lang="less">
@import url('../../../assets/less/news/news.less');
</style>

View File

@ -102,6 +102,8 @@
</template> </template>
<script> <script>
import { scheduleHomePageLayoutReset } from '@/utils/resetHomePageLayout'
export default { export default {
name: 'TrainPlatform', name: 'TrainPlatform',
data() { data() {
@ -129,6 +131,9 @@ export default {
] ]
} }
}, },
mounted() {
scheduleHomePageLayoutReset()
},
methods: { methods: {
handleStart() { handleStart() {
this.$message.info('开始使用训推平台') this.$message.info('开始使用训推平台')

View File

@ -11,10 +11,8 @@
} }
.grid-bg { .grid-bg {
width: 100vw; width: 100%;
min-height: 100vh; min-height: 100vh;
margin-left: calc(50% - 50vw);
margin-right: calc(50% - 50vw);
overflow: hidden; overflow: hidden;
background: background:
linear-gradient(rgba(37, 99, 235, 0.05) 1px, transparent 1px), linear-gradient(rgba(37, 99, 235, 0.05) 1px, transparent 1px),

View File

@ -6,24 +6,24 @@
<i class="el-icon-lock"></i> <i class="el-icon-lock"></i>
</div> </div>
<div> <div>
<h3>重置密码</h3> <h3>{{ $t('loginDialog.resetPassword') }}</h3>
<p>通过手机号验证码验证身份后设置新密码</p> <p>{{ $t('loginDialog.resetPasswordDesc') }}</p>
</div> </div>
</div> </div>
<el-form ref="formRef" :model="form" :rules="rules" label-position="top" class="forgot-form" autocomplete="off"> <el-form ref="formRef" :model="form" :rules="rules" label-position="top" class="forgot-form" autocomplete="off">
<el-form-item label="手机号" prop="username"> <el-form-item :label="`* ${$t('loginDialog.mobileLabel')}`" prop="username">
<el-input v-model="form.username" clearable autocomplete="off" placeholder="请输入绑定手机号"></el-input> <el-input v-model="form.username" clearable autocomplete="off" :placeholder="$t('loginDialog.enterBoundMobile')"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="新密码" prop="password"> <el-form-item :label="`* ${$t('loginDialog.newPasswordLabel')}`" prop="password">
<el-input v-model="form.password" clearable show-password autocomplete="new-password" <el-input v-model="form.password" clearable show-password autocomplete="new-password"
placeholder="请输入新密码"></el-input> :placeholder="$t('loginDialog.enterNewPassword')"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="验证码" prop="vcode"> <el-form-item :label="`* ${$t('loginDialog.verificationCodeLabel')}`" prop="vcode">
<div class="code-row"> <div class="code-row">
<el-input v-model="form.vcode" clearable autocomplete="off" placeholder="请输入验证码"></el-input> <el-input v-model="form.vcode" clearable autocomplete="off" :placeholder="$t('loginDialog.enterVerificationCode')"></el-input>
<el-button class="code-btn" :disabled="isDisabled || isGettingCode" :loading="isGettingCode" <el-button class="code-btn" :disabled="isDisabled || isGettingCode" :loading="isGettingCode"
@click="debouncedGetCode"> @click="debouncedGetCode">
{{ sendCodeText }} {{ sendCodeText }}
@ -33,8 +33,8 @@
</el-form> </el-form>
<div slot="footer" class="forgot-footer"> <div slot="footer" class="forgot-footer">
<el-button @click="handleClose">取消</el-button> <el-button @click="handleClose">{{ $t('loginDialog.cancel') }}</el-button>
<el-button class="confirm-btn" type="primary" :loading="submitting" @click="handleSubmit">确认重置</el-button> <el-button class="confirm-btn" type="primary" :loading="submitting" @click="handleSubmit">{{ $t('loginDialog.confirmReset') }}</el-button>
</div> </div>
</el-dialog> </el-dialog>
</template> </template>
@ -53,15 +53,7 @@ export default {
data() { data() {
return { return {
form: this.createEmptyForm(), form: this.createEmptyForm(),
rules: { sendCodeText: '',
username: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
],
password: [{ required: true, message: '请输入新密码', trigger: 'blur' }],
vcode: [{ required: true, message: '请输入验证码', trigger: 'blur' }]
},
sendCodeText: '获取验证码',
isDisabled: false, isDisabled: false,
isGettingCode: false, isGettingCode: false,
submitting: false, submitting: false,
@ -70,11 +62,36 @@ export default {
debounceTimer: null debounceTimer: null
} }
}, },
computed: {
rules() {
return {
username: [
{ required: true, message: this.$t('loginDialog.mobileRequired'), trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: this.$t('loginDialog.mobileInvalid'), trigger: 'blur' }
],
password: [{ required: true, message: this.$t('loginDialog.newPasswordRequired'), trigger: 'blur' }],
vcode: [{ required: true, message: this.$t('loginDialog.codeRequired'), trigger: 'blur' }]
}
}
},
watch: {
'$i18n.locale'() {
this.resetCodeButtonText()
}
},
created() {
this.resetCodeButtonText()
},
beforeDestroy() { beforeDestroy() {
this.resetCodeState() this.resetCodeState()
clearTimeout(this.debounceTimer) clearTimeout(this.debounceTimer)
}, },
methods: { methods: {
resetCodeButtonText() {
if (!this.isDisabled) {
this.sendCodeText = this.$t('loginDialog.getVerificationCode')
}
},
handleOpen() { handleOpen() {
this.resetForm() this.resetForm()
this.$nextTick(() => { this.$nextTick(() => {
@ -110,7 +127,7 @@ export default {
async getCode() { async getCode() {
if (!this.form.username || !/^1[3-9]\d{9}$/.test(this.form.username)) { if (!this.form.username || !/^1[3-9]\d{9}$/.test(this.form.username)) {
this.isGettingCode = false this.isGettingCode = false
this.$message.error('请输入正确的手机号') this.$message.error(this.$t('loginDialog.mobileInvalid'))
return return
} }
@ -123,13 +140,13 @@ export default {
if (res.status === true) { if (res.status === true) {
this.form.codeid = res.codeid this.form.codeid = res.codeid
this.startCountdown() this.startCountdown()
this.$message.success('验证码已发送,请注意查收。') this.$message.success(this.$t('loginDialog.codeSentCheck'))
return return
} }
this.$message.error(res.msg || '验证码获取失败') this.$message.error(res.msg || this.$t('loginDialog.codeSendFail'))
} catch (error) { } catch (error) {
this.$message.error('验证码获取失败') this.$message.error(this.$t('loginDialog.codeSendFail'))
} finally { } finally {
this.isGettingCode = false this.isGettingCode = false
} }
@ -137,20 +154,20 @@ export default {
startCountdown() { startCountdown() {
this.timeCount = 59 this.timeCount = 59
this.isDisabled = true this.isDisabled = true
this.sendCodeText = `重新发送${this.timeCount}s` this.sendCodeText = this.$t('loginDialog.resendCode', { seconds: this.timeCount })
clearInterval(this.timer) clearInterval(this.timer)
this.timer = setInterval(() => { this.timer = setInterval(() => {
if (this.timeCount > 0) { if (this.timeCount > 0) {
this.timeCount-- this.timeCount--
this.sendCodeText = `重新发送${this.timeCount}s` this.sendCodeText = this.$t('loginDialog.resendCode', { seconds: this.timeCount })
return return
} }
this.resetCodeState() this.resetCodeState()
}, 1000) }, 1000)
}, },
resetCodeState() { resetCodeState() {
this.sendCodeText = '获取验证码' this.sendCodeText = this.$t('loginDialog.getVerificationCode')
clearInterval(this.timer) clearInterval(this.timer)
this.timer = null this.timer = null
this.isDisabled = false this.isDisabled = false
@ -160,7 +177,7 @@ export default {
this.$refs.formRef.validate(async valid => { this.$refs.formRef.validate(async valid => {
if (!valid) return if (!valid) return
if (!this.form.codeid) { if (!this.form.codeid) {
this.$message.error('请先获取验证码') this.$message.error(this.$t('loginDialog.getCodeFirst'))
return return
} }
@ -175,14 +192,14 @@ export default {
}) })
if (res.status === true) { if (res.status === true) {
this.$message.success('密码重置成功') this.$message.success(this.$t('loginDialog.resetSuccess'))
this.handleClose() this.handleClose()
return return
} }
this.$message.error(res.msg || '密码重置失败') this.$message.error(res.msg || this.$t('loginDialog.resetFail'))
} catch (error) { } catch (error) {
this.$message.error('密码重置失败') this.$message.error(this.$t('loginDialog.resetFail'))
} finally { } finally {
this.submitting = false this.submitting = false
} }
@ -250,7 +267,14 @@ export default {
.forgot-form { .forgot-form {
.el-form-item { .el-form-item {
margin-bottom: 22px; margin-bottom: 28px;
}
.el-form-item__error {
position: static;
margin-top: 6px;
padding-top: 0;
line-height: 1.4;
} }
.el-form-item__label { .el-form-item__label {

View File

@ -223,6 +223,7 @@
<script> <script>
import { reqNavList, reqNewHomeSync, reqNewHomeFestival } from "@/api/newHome"; import { reqNavList, reqNewHomeSync, reqNewHomeFestival } from "@/api/newHome";
import { gotoYuanJingAPI } from '@/api/gotoYuanJing' import { gotoYuanJingAPI } from '@/api/gotoYuanJing'
import { scheduleHomePageLayoutReset } from '@/utils/resetHomePageLayout'
const getImageUrlPrefix = () => { const getImageUrlPrefix = () => {
const origin = window.location.origin const origin = window.location.origin
@ -354,8 +355,12 @@ export default {
} }
}, },
async mounted() { async mounted() {
await this.loadNavData(); scheduleHomePageLayoutReset()
this.initializeDefaultData(); await this.loadNavData()
this.initializeDefaultData()
this.$nextTick(() => {
scheduleHomePageLayoutReset()
})
}, },
methods: { methods: {
// TOKEN // TOKEN
@ -912,11 +917,9 @@ export default {
min-height: calc(100vh - 100px); min-height: calc(100vh - 100px);
&.single-product-page { &.single-product-page {
height: 100vh; min-height: calc(100vh - 82px);
min-height: 100vh;
padding: 82px 0 0; padding: 82px 0 0;
background: linear-gradient(180deg, #f3f7ff 0%, #ffffff 100%); background: linear-gradient(180deg, #f3f7ff 0%, #ffffff 100%);
overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
.product-content { .product-content {