Signed-off-by: TIANHE <TIANHE@GMAIL.COM>
This commit is contained in:
TIANHE
2025-12-29 03:06:49 +08:00
commit f43312a858
292 changed files with 103739 additions and 0 deletions
@@ -0,0 +1,423 @@
@import '~ant-design-vue/es/style/themes/default.less';
/* ========== 完全隐藏 ant-layout-footer ========== */
:global {
/* 全局隐藏所有 ant-layout-footer */
.ant-layout-footer,
footer.ant-layout-footer,
.ant-pro-layout .ant-layout-footer,
.ant-layout .ant-layout-footer {
display: none !important;
height: 0 !important;
padding: 0 !important;
margin: 0 !important;
border: none !important;
min-height: 0 !important;
visibility: hidden !important;
overflow: hidden !important;
}
}
/* ========== ant-layout-header 主题适配 ========== */
/* 全局样式:根据 body 或 layout 的主题类自动适配 */
:global {
/* 暗黑主题下的 ant-layout-header */
body.dark .ant-layout-header,
body.realdark .ant-layout-header,
.ant-layout.dark .ant-layout-header,
.ant-layout.realdark .ant-layout-header,
.ant-pro-layout.dark .ant-layout-header,
.ant-pro-layout.realdark .ant-layout-header {
background: #001529 !important;
border-bottom: 1px solid #1f1f1f !important;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08) !important;
color: rgba(255, 255, 255, 0.85) !important;
/* Header 内的所有文字和图标 */
* {
color: rgba(255, 255, 255, 0.85) !important;
}
/* Header 内的图标按钮 */
.anticon {
color: rgba(255, 255, 255, 0.85) !important;
&:hover {
color: #1890ff !important;
background: rgba(255, 255, 255, 0.08) !important;
border-radius: 4px;
}
}
/* Header 内的链接 */
a {
color: rgba(255, 255, 255, 0.85) !important;
&:hover {
color: #1890ff !important;
}
}
/* Header 内的工具提示容器 */
.ant-tooltip-open {
.anticon {
color: rgba(255, 255, 255, 0.85) !important;
}
}
}
/* 浅色主题下的 ant-layout-header(确保覆盖默认样式) */
body.light .ant-layout-header,
.ant-layout.light .ant-layout-header,
.ant-pro-layout.light .ant-layout-header {
background: #fff !important;
border-bottom: 1px solid #e8e8e8 !important;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08) !important;
color: rgba(0, 0, 0, 0.85) !important;
}
/* ========== ant-layout-content 主题适配(中间页面内容区域) ========== */
/* 暗黑主题下的内容区域 */
body.dark .ant-layout-content,
body.realdark .ant-layout-content,
.ant-layout.dark .ant-layout-content,
.ant-layout.realdark .ant-layout-content,
.ant-pro-layout.dark .ant-layout-content,
.ant-pro-layout.realdark .ant-layout-content {
background: #141414 !important;
color: rgba(255, 255, 255, 0.85) !important;
}
/* 浅色主题下的内容区域 */
body.light .ant-layout-content,
.ant-layout.light .ant-layout-content,
.ant-pro-layout.light .ant-layout-content {
background: #f0f2f5 !important;
color: rgba(0, 0, 0, 0.85) !important;
}
/* ========== GlobalHeader 核心样式(基于 .ant-pro-global-header ========== */
/* 暗黑主题下的 .ant-pro-global-header - 这是控制顶部标题栏的关键样式 */
body.dark .ant-pro-global-header,
body.realdark .ant-pro-global-header,
.ant-layout.dark .ant-pro-global-header,
.ant-layout.realdark .ant-pro-global-header,
.ant-pro-layout.dark .ant-pro-global-header,
.ant-pro-layout.realdark .ant-pro-global-header,
body.dark .ant-layout-header .ant-pro-global-header,
body.realdark .ant-layout-header .ant-pro-global-header,
.ant-pro-layout.dark .ant-layout-header .ant-pro-global-header,
.ant-pro-layout.realdark .ant-layout-header .ant-pro-global-header {
/* 核心:背景色和边框 */
background: #001529 !important;
border-bottom: 1px solid #1f1f1f !important;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08) !important;
color: rgba(255, 255, 255, 0.85) !important;
/* 强制所有子元素文字颜色 */
* {
color: rgba(255, 255, 255, 0.85) !important;
}
/* Logo 区域 */
.ant-pro-global-header-logo {
background: transparent !important;
color: rgba(255, 255, 255, 0.85) !important;
h1 {
color: rgba(255, 255, 255, 0.85) !important;
}
img {
opacity: 0.9;
}
* {
color: rgba(255, 255, 255, 0.85) !important;
}
}
}
/* Header 内容区域(中间部分,包含刷新按钮等) */
.ant-pro-global-header-content {
background: transparent !important;
color: rgba(255, 255, 255, 0.85) !important;
* {
color: rgba(255, 255, 255, 0.85) !important;
}
/* 刷新按钮等图标 */
.anticon {
color: rgba(255, 255, 255, 0.85) !important;
&:hover {
color: #1890ff !important;
background: rgba(255, 255, 255, 0.08) !important;
border-radius: 4px;
}
}
/* 工具提示内的图标 */
.ant-tooltip-open .anticon {
color: rgba(255, 255, 255, 0.85) !important;
}
}
/* Header 右侧内容区域(包含头像、语言选择、设置按钮) */
.ant-pro-global-header-index-right {
background: transparent !important;
color: rgba(255, 255, 255, 0.85) !important;
* {
color: rgba(255, 255, 255, 0.85) !important;
}
/* 操作按钮(头像、语言、设置) */
.ant-pro-global-header-index-action {
color: rgba(255, 255, 255, 0.85) !important;
&:hover {
color: #1890ff !important;
background: rgba(255, 255, 255, 0.08) !important;
}
.anticon {
color: rgba(255, 255, 255, 0.85) !important;
}
}
/* 头像区域 */
.ant-pro-account-avatar {
color: rgba(255, 255, 255, 0.85) !important;
span {
color: rgba(255, 255, 255, 0.85) !important;
}
.antd-pro-global-header-index-avatar {
background: rgba(255, 255, 255, 0.25) !important;
}
}
/* 下拉菜单触发器 */
.ant-pro-drop-down,
.ant-dropdown-trigger {
color: rgba(255, 255, 255, 0.85) !important;
&:hover {
color: #1890ff !important;
background: rgba(255, 255, 255, 0.08) !important;
}
.anticon {
color: rgba(255, 255, 255, 0.85) !important;
}
}
}
/* 链接样式 */
a {
color: rgba(255, 255, 255, 0.85) !important;
&:hover {
color: #1890ff !important;
}
}
/* 所有图标 */
.anticon {
color: rgba(255, 255, 255, 0.85) !important;
&:hover {
color: #1890ff !important;
}
}
/* 浅色主题下的 .ant-pro-global-header */
body.light .ant-pro-global-header,
.ant-layout.light .ant-pro-global-header,
.ant-pro-layout.light .ant-pro-global-header,
body.light .ant-layout-header .ant-pro-global-header,
.ant-pro-layout.light .ant-layout-header .ant-pro-global-header {
background: #fff !important;
border-bottom: 1px solid #e8e8e8 !important;
color: rgba(0, 0, 0, 0.85) !important;
}
}
/* ========== 全局布局主题适配 - 强化覆盖 ========== */
:global(.ant-pro-layout) {
/* 暗黑主题下的完整布局 */
&.dark,
&.realdark {
/* 0. 基础 Layout 背景 - 确保整体背景变暗 */
:global(.ant-layout) {
background-color: #001529 !important;
}
/* 1. 顶部 Header */
:global(.ant-layout-header) {
background: #001529 !important;
border-bottom: 1px solid #1f1f1f !important;
color: rgba(255, 255, 255, 0.85) !important;
:global(.ant-pro-global-header) {
background: #001529 !important;
border-bottom: none !important;
color: rgba(255, 255, 255, 0.85) !important;
* {
color: rgba(255, 255, 255, 0.85) !important;
}
}
}
/* 2. 中间内容区域 */
:global(.ant-layout-content) {
background: #141414 !important;
color: rgba(255, 255, 255, 0.85) !important;
}
:global(.ant-pro-basicLayout-content) {
background: #141414 !important;
color: rgba(255, 255, 255, 0.85) !important;
}
}
/* 浅色主题下的完整布局 */
&.light {
/* 0. 基础 Layout 背景 */
:global(.ant-layout) {
background-color: #f0f2f5 !important;
}
/* 1. 顶部 Header */
:global(.ant-layout-header) {
background: #fff !important;
border-bottom: 1px solid #e8e8e8 !important;
color: rgba(0, 0, 0, 0.85) !important;
}
/* 2. 中间内容区域 */
:global(.ant-layout-content) {
background: #f0f2f5 !important;
color: rgba(0, 0, 0, 0.85) !important;
}
:global(.ant-pro-basicLayout-content) {
background: #f0f2f5 !important;
color: rgba(0, 0, 0, 0.85) !important;
}
}
}
.ant-pro-global-header-index-right {
margin-right: 8px;
&.ant-pro-global-header-index-dark {
.ant-pro-global-header-index-action {
color: hsl(0deg 0% 100% / 85%);
&:hover {
background: #1890ff;
}
}
/* 暗黑主题下的头像下拉菜单 */
.ant-pro-account-avatar {
color: rgba(255, 255, 255, 0.85);
.antd-pro-global-header-index-avatar {
background: rgba(255, 255, 255, 0.25) !important;
}
span {
color: rgba(255, 255, 255, 0.85);
}
}
/* 语言选择器和设置按钮 */
.ant-pro-drop-down {
color: rgba(255, 255, 255, 0.85);
&:hover {
color: #1890ff;
background: rgba(255, 255, 255, 0.08);
}
}
}
/* 浅色主题下的头像样式 */
.ant-pro-account-avatar {
.antd-pro-global-header-index-avatar {
margin: ~'calc((@{layout-header-height} - 24px) / 2)' 0;
margin-right: 8px;
color: @primary-color;
vertical-align: top;
background: rgb(255 255 255 / 85%);
}
}
.menu {
.anticon {
margin-right: 8px;
}
.ant-dropdown-menu-item {
min-width: 100px;
}
}
}
/* 暗黑主题下的下拉菜单样式 */
:global {
.ant-pro-layout.dark,
.ant-pro-layout.realdark {
.ant-dropdown-menu {
background: #1f1f1f !important;
border: 1px solid #303030 !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;
.ant-dropdown-menu-item {
color: rgba(255, 255, 255, 0.85) !important;
&:hover {
background: #262626 !important;
color: #1890ff !important;
}
}
.ant-dropdown-menu-item-divider {
background: #303030 !important;
}
}
}
}
/* 手机端适配 */
@media (max-width: 768px) {
:global {
/* 使用更具体的选择器强制覆盖 */
.ant-layout-header .ant-pro-global-header .ant-pro-global-header-logo,
.ant-pro-layout .ant-layout-header .ant-pro-global-header .ant-pro-global-header-logo,
body .ant-layout-header .ant-pro-global-header .ant-pro-global-header-logo,
.ant-pro-global-header-logo {
padding: 0 !important;
padding-left: 0 !important;
padding-right: 0 !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,16 @@
<template>
<div>
<router-view />
</div>
</template>
<script>
export default {
name: 'BlankLayout'
}
</script>
<style scoped>
</style>
+12
View File
@@ -0,0 +1,12 @@
<template>
<page-header-wrapper>
<router-view />
</page-header-wrapper>
</template>
<script>
export default {
name: 'PageView'
}
</script>
+32
View File
@@ -0,0 +1,32 @@
<script>
export default {
name: 'RouteView',
props: {
keepAlive: {
type: Boolean,
default: true
}
},
data () {
return {}
},
render () {
const { $route: { meta }, $store: { getters } } = this
const inKeep = (
<keep-alive>
<router-view />
</keep-alive>
)
const notKeep = (
<router-view />
)
// 这里增加了 multiTab 的判断,当开启了 multiTab 时
// 应当全部组件皆缓存,否则会导致切换页面后页面还原成原始状态
// 若确实不需要,可改为 return meta.keepAlive ? inKeep : notKeep
if (!getters.multiTab && !meta.keepAlive) {
return notKeep
}
return this.keepAlive || getters.multiTab || meta.keepAlive ? inKeep : notKeep
}
}
</script>
+264
View File
@@ -0,0 +1,264 @@
<template>
<div id="userLayout" :class="['user-layout-wrapper', isMobile && 'mobile']">
<div class="container">
<div class="fx-layer" aria-hidden="true">
<div class="fx-gradient"></div>
<div class="fx-grid"></div>
</div>
<div class="user-layout-lang">
<select-lang class="select-lang-trigger" />
</div>
<div class="user-layout-content">
<div class="top">
<div class="header">
<a href="/">
<img src="~@/assets/logo.png" class="logo" alt="logo">
<!-- <span class="title">QuantDinger</span> -->
</a>
</div>
<!-- <div class="desc">
{{ $t('layouts.userLayout.title') }}
</div> -->
</div>
<div class="main-content">
<router-view />
</div>
<div class="footer">
<div class="copyright">
Copyright &copy; 2025-2026 Quantdinger.com
<div style="width: 70%; text-align: center; margin-left: 15%; margin-top: 10px;">
<a @click="toggleRisk" style="color: #1890ff; cursor: pointer;">
{{ showRisk ? $t('user.login.privacy.collapse') : $t('user.login.privacy.view') }}
</a>
<div v-if="showRisk" style="margin-top: 10px; font-size: 12px; color: rgba(0,0,0,0.65); line-height: 1.6; text-align: left;">
<div style="font-weight: 600; margin-bottom: 6px;">{{ $t('user.login.privacy.title') }}</div>
{{ $t('user.login.privacy.content') }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { deviceMixin } from '@/store/device-mixin'
import SelectLang from '@/components/SelectLang'
export default {
name: 'UserLayout',
components: {
SelectLang
},
mixins: [deviceMixin],
data () {
return {
showRisk: false
}
},
methods: {
toggleRisk () {
this.showRisk = !this.showRisk
}
},
mounted () {
document.body.classList.add('userLayout')
},
beforeDestroy () {
document.body.classList.remove('userLayout')
}
}
</script>
<style lang="less" scoped>
#userLayout.user-layout-wrapper {
height: 100%;
&.mobile {
.container {
.main {
max-width: 368px;
width: 98%;
}
}
}
.container {
width: 100%;
min-height: 100%;
background: #f0f2f5 url(~@/assets/background.svg) no-repeat 50%;
background-size: 100%;
//padding: 50px 0 84px;
position: relative;
.fx-layer {
position: absolute;
inset: 0;
overflow: hidden;
z-index: 0;
pointer-events: none;
.fx-gradient {
position: absolute;
inset: -20% -20% -20% -20%;
background: radial-gradient(1200px 600px at 10% 10%, rgba(78, 161, 255, 0.18), transparent 60%),
radial-gradient(900px 500px at 90% 20%, rgba(127, 92, 255, 0.18), transparent 60%),
radial-gradient(800px 500px at 30% 90%, rgba(0, 210, 170, 0.14), transparent 60%);
filter: blur(20px);
animation: fxFloat 18s ease-in-out infinite alternate;
transform: translateZ(0);
}
.fx-grid {
position: absolute;
inset: 0;
background-image: linear-gradient(rgba(255,255,255,0.06) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.06) 1px, transparent 1px);
background-size: 44px 44px, 44px 44px;
background-position: 0 0, 0 0;
mix-blend-mode: overlay;
animation: gridDrift 40s linear infinite;
}
}
.user-layout-lang {
width: 100%;
height: 40px;
line-height: 44px;
text-align: right;
.select-lang-trigger {
cursor: pointer;
padding: 12px;
margin-right: 24px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 18px;
vertical-align: middle;
}
}
.user-layout-content {
padding: 32px 0 24px;
display: flex;
flex-direction: column;
min-height: calc(100vh - 40px);
position: relative;
z-index: 1;
.top {
text-align: center;
.header {
height: 56px;
line-height: 56px;
.badge {
position: absolute;
display: inline-block;
line-height: 1;
vertical-align: middle;
margin-left: -12px;
margin-top: -10px;
opacity: 0.8;
}
.logo {
width: 342px; // approx 3.8:1 when height ~90px, keep responsive
max-width: 42vw;
height: auto;
vertical-align: middle;
margin-right: 0;
border-style: none;
}
.title {
font-size: 33px;
color: rgba(0, 0, 0, .85);
font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
font-weight: 600;
position: relative;
top: 2px;
}
}
.desc {
font-size: 14px;
color: rgba(0, 0, 0, 0.45);
margin-top: 12px;
margin-bottom: 40px;
}
}
.main {
min-width: 320px;
width: 480px;
margin: 0 auto;
}
.main-content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.footer {
width: 100%;
padding: 0 16px;
margin-top: auto;
margin-bottom: 16px;
text-align: center;
.links {
margin-bottom: 8px;
font-size: 14px;
a {
color: rgba(0, 0, 0, 0.45);
transition: all 0.3s;
&:not(:last-child) {
margin-right: 40px;
}
}
}
.copyright {
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
}
}
}
a {
text-decoration: none;
}
}
}
@media (max-width: 576px) {
#userLayout.user-layout-wrapper .container .user-layout-content .top .header .logo {
width: 208px;
max-width: 70vw;
margin-top: 8px;
}
#userLayout.user-layout-wrapper .container .user-layout-content .main {
width: 92vw;
}
}
@keyframes fxFloat {
0% { transform: translate3d(-2%, -1%, 0) scale(1); }
50% { transform: translate3d(1%, 2%, 0) scale(1.02); }
100% { transform: translate3d(3%, -2%, 0) scale(1.04); }
}
@keyframes gridDrift {
0% { background-position: 0 0, 0 0; transform: rotate(0deg); }
50% { background-position: 22px 22px, 22px 22px; }
100% { background-position: 44px 44px, 44px 44px; transform: rotate(0.01turn); }
}
</style>
+7
View File
@@ -0,0 +1,7 @@
import UserLayout from './UserLayout'
import BlankLayout from './BlankLayout'
import BasicLayout from './BasicLayout'
import RouteView from './RouteView'
import PageView from './PageView'
export { UserLayout, BasicLayout, BlankLayout, RouteView, PageView }