203 lines
5.3 KiB
Vue
203 lines
5.3 KiB
Vue
<script setup>
|
|
import { RouterView } from 'vue-router'
|
|
import { ref, computed, watchEffect } from 'vue'
|
|
import SvgSprite from '@/components/icons/SvgSprite.vue'
|
|
import { useUserStore } from '@/stores/user'
|
|
import tokenManager from '@gold/utils/token-manager'
|
|
import zhCN from 'ant-design-vue/es/locale/zh_CN'
|
|
|
|
// ========================================
|
|
// Ant Design Vue 主题配置
|
|
// ========================================
|
|
const isDark = ref(false)
|
|
|
|
// 初始化主题
|
|
const initTheme = () => {
|
|
const stored = localStorage.getItem('theme')
|
|
if (stored) {
|
|
isDark.value = stored === 'dark'
|
|
} else {
|
|
isDark.value = window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
}
|
|
document.documentElement.setAttribute('data-theme', isDark.value ? 'dark' : 'light')
|
|
}
|
|
|
|
// 主题切换
|
|
const toggleTheme = () => {
|
|
isDark.value = !isDark.value
|
|
document.documentElement.setAttribute('data-theme', isDark.value ? 'dark' : 'light')
|
|
localStorage.setItem('theme', isDark.value ? 'dark' : 'light')
|
|
}
|
|
|
|
// Ant Design 主题 Token
|
|
const themeToken = computed(() => {
|
|
const lightToken = {
|
|
// 品牌色
|
|
colorPrimary: '#3B82F6',
|
|
colorSuccess: '#22C55E',
|
|
colorWarning: '#F59E0B',
|
|
colorError: '#EF4444',
|
|
colorInfo: '#3B82F6',
|
|
// 背景色
|
|
colorBgContainer: '#FFFFFF',
|
|
colorBgElevated: '#FFFFFF',
|
|
colorBgLayout: '#F9FAFB',
|
|
// 边框
|
|
colorBorder: '#E5E7EB',
|
|
colorBorderSecondary: '#F3F4F6',
|
|
// 文字
|
|
colorText: '#111827',
|
|
colorTextSecondary: '#4B5563',
|
|
colorTextTertiary: '#6B7280',
|
|
colorTextQuaternary: '#9CA3AF',
|
|
// 填充
|
|
colorFill: '#F3F4F6',
|
|
colorFillSecondary: '#F9FAFB',
|
|
// 字体
|
|
fontFamily: '-apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif',
|
|
fontSize: 14,
|
|
// 圆角
|
|
borderRadius: 6,
|
|
borderRadiusSM: 4,
|
|
borderRadiusLG: 8,
|
|
// 控件尺寸
|
|
controlHeight: 32,
|
|
controlHeightSM: 28,
|
|
controlHeightLG: 40,
|
|
// 动画
|
|
motionDurationFast: '0.15s',
|
|
motionDurationMid: '0.2s',
|
|
motionDurationSlow: '0.3s',
|
|
// 阴影
|
|
boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1)',
|
|
boxShadowSecondary: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)',
|
|
}
|
|
|
|
const darkToken = {
|
|
colorPrimary: '#60A5FA',
|
|
colorBgContainer: '#1E293B',
|
|
colorBgElevated: '#334155',
|
|
colorBgLayout: '#0F172A',
|
|
colorBorder: '#334155',
|
|
colorBorderSecondary: '#1E293B',
|
|
colorText: '#F1F5F9',
|
|
colorTextSecondary: '#94A3B8',
|
|
colorTextTertiary: '#64748B',
|
|
colorTextQuaternary: '#475569',
|
|
colorFill: '#334155',
|
|
colorFillSecondary: '#1E293B',
|
|
boxShadow: '0 1px 3px 0 rgba(0, 0, 0, 0.4), 0 1px 2px -1px rgba(0, 0, 0, 0.4)',
|
|
boxShadowSecondary: '0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -2px rgba(0, 0, 0, 0.4)',
|
|
}
|
|
|
|
return {
|
|
token: isDark.value ? { ...lightToken, ...darkToken } : lightToken,
|
|
components: {
|
|
Button: {
|
|
primaryShadow: 'none',
|
|
defaultShadow: 'none',
|
|
fontWeight: 500,
|
|
},
|
|
Input: {
|
|
paddingBlock: 6,
|
|
paddingInline: 12,
|
|
activeShadow: '0 0 0 3px rgba(59, 130, 246, 0.15)',
|
|
},
|
|
Select: {
|
|
optionSelectedBg: isDark.value ? 'rgba(96, 165, 250, 0.15)' : '#EFF6FF',
|
|
optionActiveBg: isDark.value ? '#334155' : '#F3F4F6',
|
|
},
|
|
Table: {
|
|
headerBg: isDark.value ? '#1E293B' : '#F9FAFB',
|
|
rowHoverBg: isDark.value ? '#334155' : '#F9FAFB',
|
|
borderColor: isDark.value ? '#334155' : '#E5E7EB',
|
|
headerColor: '#4B5563',
|
|
},
|
|
Card: {
|
|
paddingLG: 20,
|
|
borderRadiusLG: 12,
|
|
},
|
|
Modal: {
|
|
borderRadiusLG: 12,
|
|
},
|
|
Menu: {
|
|
itemHoverBg: isDark.value ? '#334155' : '#F3F4F6',
|
|
itemSelectedBg: isDark.value ? 'rgba(96, 165, 250, 0.15)' : '#EFF6FF',
|
|
itemSelectedColor: isDark.value ? '#60A5FA' : '#3B82F6',
|
|
},
|
|
},
|
|
}
|
|
})
|
|
|
|
// 初始化
|
|
initTheme()
|
|
|
|
// 监听系统主题变化
|
|
watchEffect(() => {
|
|
const media = window.matchMedia('(prefers-color-scheme: dark)')
|
|
const handler = (e) => {
|
|
if (!localStorage.getItem('theme')) {
|
|
isDark.value = e.matches
|
|
document.documentElement.setAttribute('data-theme', e.matches ? 'dark' : 'light')
|
|
}
|
|
}
|
|
media.addEventListener('change', handler)
|
|
})
|
|
|
|
// 暴露给模板使用
|
|
defineExpose({ toggleTheme, isDark })
|
|
</script>
|
|
|
|
<template>
|
|
<a-config-provider :theme="themeToken" :locale="zhCN">
|
|
<SvgSprite />
|
|
<keep-alive>
|
|
<RouterView />
|
|
</keep-alive>
|
|
</a-config-provider>
|
|
</template>
|
|
|
|
<style>
|
|
/* 全局样式保持不变 */
|
|
.ant-btn{
|
|
align-items: center;
|
|
text-align: center;
|
|
display: flex;
|
|
}
|
|
|
|
|
|
.ant-select-selection-item,
|
|
.ant-select-selection-placeholder {
|
|
line-height: 30px !important;
|
|
color: var(--color-text) !important;
|
|
}
|
|
.ant-modal-confirm-btns {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
|
|
.ant-btn {
|
|
margin-left: 0 !important;
|
|
}
|
|
}
|
|
.ant-select-focused .ant-select-selector {
|
|
border-color: var(--color-primary) !important;
|
|
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.1) !important;
|
|
}
|
|
|
|
.ant-select:hover .ant-select-selector {
|
|
border-color: var(--color-primary) !important;
|
|
}
|
|
|
|
.ant-select-arrow {
|
|
color: var(--color-text-secondary) !important;
|
|
}
|
|
.ant-modal .ant-modal-footer {
|
|
display: flex;
|
|
justify-content: end;
|
|
}
|
|
.ant-tooltip{
|
|
z-index: 100
|
|
}
|
|
</style>
|