mirror of
https://github.com/vbenjs/vben-admin-thin-next.git
synced 2025-01-23 01:30:23 +08:00
feat: dark mode
This commit is contained in:
parent
f05cc6d34e
commit
5b8eb4a49a
@ -11,6 +11,7 @@
|
||||
- 移除 useFullScreen 函数
|
||||
- tinymce 由 Cdn 改为 npm(打包体积偏大)
|
||||
- Dashboard 重构
|
||||
- 移除 ApexCharts 及示例
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
|
@ -2,11 +2,7 @@ import { generate } from '@ant-design/colors';
|
||||
|
||||
export const primaryColor = '#0960bd';
|
||||
|
||||
export const borderColorBase = '#d9d9d9';
|
||||
|
||||
export const themeMode = 'light';
|
||||
|
||||
export type ThemeMode = 'dark' | 'light';
|
||||
export const darkMode = 'light';
|
||||
|
||||
type Fn = (...arg: any) => any;
|
||||
|
||||
@ -17,18 +13,17 @@ export interface GenerateColorsParams {
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export function generateAntColors(color: string, mode: ThemeMode) {
|
||||
export function generateAntColors(color: string) {
|
||||
return generate(color, {
|
||||
theme: mode == 'dark' ? 'dark' : 'default',
|
||||
theme: 'default',
|
||||
});
|
||||
}
|
||||
|
||||
export function getThemeColors(color?: string, theme?: ThemeMode) {
|
||||
export function getThemeColors(color?: string) {
|
||||
const tc = color || primaryColor;
|
||||
const tm = theme || themeMode;
|
||||
const colors = generateAntColors(tc, tm);
|
||||
const colors = generateAntColors(tc);
|
||||
const primary = colors[5];
|
||||
const modeColors = generateAntColors(primary, tm === 'dark' ? 'light' : 'dark');
|
||||
const modeColors = generateAntColors(primary);
|
||||
|
||||
return [...colors, ...modeColors];
|
||||
}
|
||||
@ -71,36 +66,3 @@ export function generateColors({
|
||||
.filter((item) => item !== '#000000');
|
||||
return [...lightens, ...darkens, ...alphaColors, ...tinycolorDarkens, ...tinycolorLightens];
|
||||
}
|
||||
|
||||
/**
|
||||
* less global variable
|
||||
*/
|
||||
export function generateModifyVars() {
|
||||
const palettes = generateAntColors(primaryColor, themeMode);
|
||||
const primary = palettes[5];
|
||||
|
||||
const primaryColorObj: Record<string, string> = {};
|
||||
|
||||
for (let index = 0; index < 10; index++) {
|
||||
primaryColorObj[`primary-${index + 1}`] = palettes[index];
|
||||
}
|
||||
|
||||
return {
|
||||
'primary-color': primary,
|
||||
...primaryColorObj,
|
||||
'info-color': primary,
|
||||
'processing-color': primary,
|
||||
'success-color': '#55D187', // Success color
|
||||
'error-color': '#ED6F6F', // False color
|
||||
'warning-color': '#EFBD47', // Warning color
|
||||
'disabled-color': 'rgba(0, 0, 0, 0.25)', // Failure color
|
||||
'heading-color': 'rgba(0, 0, 0, 0.85)', // Title color
|
||||
'text-color': 'rgba(0, 0, 0, 0.85)', // Main text color
|
||||
'text-color-secondary': 'rgba(0, 0, 0, 0.45)', // Subtext color
|
||||
'font-size-base': '14px', // Main font size
|
||||
'box-shadow-base': '0 2px 8px rgba(0, 0, 0, 0.15)', // Floating shadow
|
||||
'border-color-base': borderColorBase, // Border color,
|
||||
'border-radius-base': '2px', // Component/float fillet
|
||||
'link-color': primary, // Link color
|
||||
};
|
||||
}
|
||||
|
35
build/generate/generateModifyVars.ts
Normal file
35
build/generate/generateModifyVars.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { generateAntColors, primaryColor } from '../config/themeConfig';
|
||||
import { getThemeVariables } from 'ant-design-vue/dist/theme';
|
||||
import { resolve } from 'path';
|
||||
|
||||
/**
|
||||
* less global variable
|
||||
*/
|
||||
export function generateModifyVars(dark = false) {
|
||||
const palettes = generateAntColors(primaryColor);
|
||||
const primary = palettes[5];
|
||||
|
||||
const primaryColorObj: Record<string, string> = {};
|
||||
|
||||
for (let index = 0; index < 10; index++) {
|
||||
primaryColorObj[`primary-${index + 1}`] = palettes[index];
|
||||
}
|
||||
|
||||
const modifyVars = getThemeVariables({ dark });
|
||||
return {
|
||||
...modifyVars,
|
||||
// Used for global import to avoid the need to import each style file separately
|
||||
// reference: Avoid repeated references
|
||||
hack: `${modifyVars.hack} @import (reference) "${resolve('src/design/config.less')}";`,
|
||||
'primary-color': primary,
|
||||
...primaryColorObj,
|
||||
'info-color': primary,
|
||||
'processing-color': primary,
|
||||
'success-color': '#55D187', // Success color
|
||||
'error-color': '#ED6F6F', // False color
|
||||
'warning-color': '#EFBD47', // Warning color
|
||||
'font-size-base': '14px', // Main font size
|
||||
'border-radius-base': '2px', // Component/float fillet
|
||||
'link-color': primary, // Link color
|
||||
};
|
||||
}
|
@ -62,7 +62,7 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
|
||||
vitePlugins.push(configVisualizerConfig());
|
||||
|
||||
//vite-plugin-theme
|
||||
vitePlugins.push(configThemePlugin());
|
||||
vitePlugins.push(configThemePlugin(isBuild));
|
||||
|
||||
// The following plugins only work in the production environment
|
||||
if (isBuild) {
|
||||
|
@ -2,18 +2,50 @@
|
||||
* Vite plugin for website theme color switching
|
||||
* https://github.com/anncwb/vite-plugin-theme
|
||||
*/
|
||||
import { viteThemePlugin, mixLighten, mixDarken, tinycolor } from 'vite-plugin-theme';
|
||||
import type { Plugin } from 'vite';
|
||||
import {
|
||||
viteThemePlugin,
|
||||
antdDarkThemePlugin,
|
||||
mixLighten,
|
||||
mixDarken,
|
||||
tinycolor,
|
||||
} from 'vite-plugin-theme';
|
||||
import { getThemeColors, generateColors } from '../../config/themeConfig';
|
||||
import { generateModifyVars } from '../../generate/generateModifyVars';
|
||||
|
||||
export function configThemePlugin() {
|
||||
export function configThemePlugin(isBuild: boolean): Plugin[] {
|
||||
const colors = generateColors({
|
||||
mixDarken,
|
||||
mixLighten,
|
||||
tinycolor,
|
||||
});
|
||||
|
||||
const plugin = viteThemePlugin({
|
||||
colorVariables: [...getThemeColors(), ...colors],
|
||||
});
|
||||
return plugin;
|
||||
const plugin = [
|
||||
viteThemePlugin({
|
||||
resolveSelector: (s) => `[data-theme] ${s}`,
|
||||
colorVariables: [...getThemeColors(), ...colors],
|
||||
}),
|
||||
antdDarkThemePlugin({
|
||||
filter: (id) => {
|
||||
if (isBuild) {
|
||||
return !id.endsWith('antd.less');
|
||||
}
|
||||
return true;
|
||||
},
|
||||
// extractCss: false,
|
||||
darkModifyVars: {
|
||||
...generateModifyVars(true),
|
||||
'text-color': '#c9d1d9',
|
||||
'text-color-base': '#c9d1d9',
|
||||
'component-background': '#151515',
|
||||
// black: '#0e1117',
|
||||
// #8b949e
|
||||
'text-color-secondary': '#8b949e',
|
||||
'border-color-base': '#30363d',
|
||||
'item-active-bg': '#111b26',
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
return (plugin as unknown) as Plugin[];
|
||||
}
|
||||
|
18
index.html
18
index.html
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" id="htmlRoot">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
@ -13,8 +13,24 @@
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
(() => {
|
||||
var htmlRoot = document.getElementById('htmlRoot');
|
||||
const theme = window.localStorage.getItem('__APP__DARK__MODE__');
|
||||
if (!htmlRoot || !theme) return;
|
||||
htmlRoot.setAttribute('data-theme', theme);
|
||||
})();
|
||||
</script>
|
||||
<div id="app">
|
||||
<style>
|
||||
html[data-theme='dark'] .app-loading {
|
||||
background: #2c344a;
|
||||
}
|
||||
|
||||
html[data-theme='dark'] .app-loading .app-loading-title {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
|
||||
.app-loading {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
@ -35,7 +35,6 @@
|
||||
"@vueuse/core": "^4.7.0",
|
||||
"@zxcvbn-ts/core": "^0.3.0",
|
||||
"ant-design-vue": "^2.1.2",
|
||||
"apexcharts": "^3.26.0",
|
||||
"axios": "^0.21.1",
|
||||
"cropperjs": "^1.5.11",
|
||||
"crypto-js": "^4.0.0",
|
||||
@ -51,7 +50,7 @@
|
||||
"vditor": "^3.8.4",
|
||||
"vue": "3.0.11",
|
||||
"vue-i18n": "^9.0.0",
|
||||
"vue-router": "^4.0.5",
|
||||
"vue-router": "^4.0.6",
|
||||
"vue-types": "^3.0.2",
|
||||
"vuex": "^4.0.0",
|
||||
"vuex-module-decorators": "^1.0.1",
|
||||
@ -110,12 +109,12 @@
|
||||
"vite-plugin-compression": "^0.2.4",
|
||||
"vite-plugin-html": "^2.0.6",
|
||||
"vite-plugin-imagemin": "^0.3.0",
|
||||
"vite-plugin-mock": "^2.4.2",
|
||||
"vite-plugin-mock": "^2.5.0",
|
||||
"vite-plugin-purge-icons": "^0.7.0",
|
||||
"vite-plugin-pwa": "^0.6.5",
|
||||
"vite-plugin-style-import": "^0.9.2",
|
||||
"vite-plugin-svg-icons": "^0.4.1",
|
||||
"vite-plugin-theme": "^0.5.0",
|
||||
"vite-plugin-theme": "^0.6.0",
|
||||
"vite-plugin-windicss": "0.12.5",
|
||||
"vue-eslint-parser": "^7.6.0"
|
||||
},
|
||||
|
16
src/assets/icons/moon.svg
Normal file
16
src/assets/icons/moon.svg
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 499.712 499.712" style="enable-background: new 0 0 499.712 499.712;" xml:space="preserve">
|
||||
<path style="fill: #FFD93B;" d="M146.88,375.528c126.272,0,228.624-102.368,228.624-228.64c0-55.952-20.16-107.136-53.52-146.88
|
||||
C425.056,33.096,499.696,129.64,499.696,243.704c0,141.392-114.608,256-256,256c-114.064,0-210.608-74.64-243.696-177.712
|
||||
C39.744,355.368,90.944,375.528,146.88,375.528z"/>
|
||||
<path style="fill: #F4C534;" d="M401.92,42.776c34.24,43.504,54.816,98.272,54.816,157.952c0,141.392-114.608,256-256,256
|
||||
c-59.68,0-114.448-20.576-157.952-54.816c46.848,59.472,119.344,97.792,200.928,97.792c141.392,0,256-114.608,256-256
|
||||
C499.712,162.12,461.392,89.64,401.92,42.776z"/>
|
||||
<g>
|
||||
<polygon style="fill: #FFD83B;" points="128.128,99.944 154.496,153.4 213.472,161.96 170.8,203.56 180.864,262.296
|
||||
128.128,234.568 75.376,262.296 85.44,203.56 42.768,161.96 101.744,153.4"/>
|
||||
<polygon style="fill: #FFD83B;" points="276.864,82.84 290.528,110.552 321.104,114.984 298.976,136.552 304.208,166.984
|
||||
276.864,152.616 249.52,166.984 254.752,136.552 232.624,114.984 263.2,110.552"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
42
src/assets/icons/sun.svg
Normal file
42
src/assets/icons/sun.svg
Normal file
@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 60 60" style="enable-background: new 0 0 60 60;" xml:space="preserve">
|
||||
<g>
|
||||
<path style="fill: #F0C419;" d="M30,0c-0.552,0-1,0.448-1,1v6c0,0.552,0.448,1,1,1s1-0.448,1-1V1C31,0.448,30.552,0,30,0z"/>
|
||||
<path style="fill: #F0C419;" d="M30,52c-0.552,0-1,0.448-1,1v6c0,0.552,0.448,1,1,1s1-0.448,1-1v-6C31,52.448,30.552,52,30,52z"/>
|
||||
<path style="fill: #F0C419;" d="M59,29h-6c-0.552,0-1,0.448-1,1s0.448,1,1,1h6c0.552,0,1-0.448,1-1S59.552,29,59,29z"/>
|
||||
<path style="fill: #F0C419;" d="M8,30c0-0.552-0.448-1-1-1H1c-0.552,0-1,0.448-1,1s0.448,1,1,1h6C7.552,31,8,30.552,8,30z"/>
|
||||
<path style="fill: #F0C419;" d="M46.264,14.736c0.256,0,0.512-0.098,0.707-0.293l5.736-5.736c0.391-0.391,0.391-1.023,0-1.414
|
||||
s-1.023-0.391-1.414,0l-5.736,5.736c-0.391,0.391-0.391,1.023,0,1.414C45.752,14.639,46.008,14.736,46.264,14.736z"/>
|
||||
<path style="fill: #F0C419;" d="M13.029,45.557l-5.736,5.736c-0.391,0.391-0.391,1.023,0,1.414C7.488,52.902,7.744,53,8,53
|
||||
s0.512-0.098,0.707-0.293l5.736-5.736c0.391-0.391,0.391-1.023,0-1.414S13.42,45.166,13.029,45.557z"/>
|
||||
<path style="fill: #F0C419;" d="M46.971,45.557c-0.391-0.391-1.023-0.391-1.414,0s-0.391,1.023,0,1.414l5.736,5.736
|
||||
C51.488,52.902,51.744,53,52,53s0.512-0.098,0.707-0.293c0.391-0.391,0.391-1.023,0-1.414L46.971,45.557z"/>
|
||||
<path style="fill: #F0C419;" d="M8.707,7.293c-0.391-0.391-1.023-0.391-1.414,0s-0.391,1.023,0,1.414l5.736,5.736
|
||||
c0.195,0.195,0.451,0.293,0.707,0.293s0.512-0.098,0.707-0.293c0.391-0.391,0.391-1.023,0-1.414L8.707,7.293z"/>
|
||||
<path style="fill: #F0C419;" d="M50.251,21.404c0.162,0.381,0.532,0.61,0.921,0.61c0.13,0,0.263-0.026,0.39-0.08l2.762-1.172
|
||||
c0.508-0.216,0.746-0.803,0.53-1.311s-0.804-0.746-1.311-0.53l-2.762,1.172C50.272,20.309,50.035,20.896,50.251,21.404z"/>
|
||||
<path style="fill: #F0C419;" d="M9.749,38.596c-0.216-0.508-0.803-0.746-1.311-0.53l-2.762,1.172
|
||||
c-0.508,0.216-0.746,0.803-0.53,1.311c0.162,0.381,0.532,0.61,0.921,0.61c0.13,0,0.263-0.026,0.39-0.08l2.762-1.172
|
||||
C9.728,39.691,9.965,39.104,9.749,38.596z"/>
|
||||
<path style="fill: #F0C419;" d="M54.481,38.813L51.7,37.688c-0.511-0.207-1.095,0.041-1.302,0.553
|
||||
c-0.207,0.512,0.041,1.095,0.553,1.302l2.782,1.124c0.123,0.049,0.25,0.073,0.374,0.073c0.396,0,0.771-0.236,0.928-0.626
|
||||
C55.241,39.603,54.994,39.02,54.481,38.813z"/>
|
||||
<path style="fill: #F0C419;" d="M5.519,21.188L8.3,22.312c0.123,0.049,0.25,0.073,0.374,0.073c0.396,0,0.771-0.236,0.928-0.626
|
||||
c0.207-0.512-0.041-1.095-0.553-1.302l-2.782-1.124c-0.513-0.207-1.095,0.04-1.302,0.553C4.759,20.397,5.006,20.98,5.519,21.188z"
|
||||
/>
|
||||
<path style="fill: #F0C419;" d="M39.907,50.781c-0.216-0.508-0.803-0.745-1.311-0.53c-0.508,0.216-0.746,0.803-0.53,1.311
|
||||
l1.172,2.762c0.162,0.381,0.532,0.61,0.921,0.61c0.13,0,0.263-0.026,0.39-0.08c0.508-0.216,0.746-0.803,0.53-1.311L39.907,50.781z"
|
||||
/>
|
||||
<path style="fill: #F0C419;" d="M21.014,9.829c0.13,0,0.263-0.026,0.39-0.08c0.508-0.216,0.746-0.803,0.53-1.311l-1.172-2.762
|
||||
c-0.215-0.509-0.802-0.747-1.311-0.53c-0.508,0.216-0.746,0.803-0.53,1.311l1.172,2.762C20.254,9.6,20.625,9.829,21.014,9.829z"/>
|
||||
<path style="fill: #F0C419;" d="M21.759,50.398c-0.511-0.205-1.095,0.04-1.302,0.553l-1.124,2.782
|
||||
c-0.207,0.512,0.041,1.095,0.553,1.302c0.123,0.049,0.25,0.073,0.374,0.073c0.396,0,0.771-0.236,0.928-0.626l1.124-2.782
|
||||
C22.519,51.188,22.271,50.605,21.759,50.398z"/>
|
||||
<path style="fill: #F0C419;" d="M38.615,9.675c0.396,0,0.771-0.236,0.928-0.626l1.124-2.782c0.207-0.512-0.041-1.095-0.553-1.302
|
||||
c-0.511-0.207-1.095,0.041-1.302,0.553L37.688,8.3c-0.207,0.512,0.041,1.095,0.553,1.302C38.364,9.651,38.491,9.675,38.615,9.675z"
|
||||
/>
|
||||
</g>
|
||||
<circle style="fill: #F0C419;" cx="30" cy="30" r="20"/>
|
||||
<circle style="fill: #EDE21B;" cx="30" cy="30" r="15"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
19
src/assets/svg/login-bg-dark.svg
Normal file
19
src/assets/svg/login-bg-dark.svg
Normal file
@ -0,0 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="6395" height="1080" viewBox="0 0 6395 1080">
|
||||
<defs>
|
||||
<clipPath id="clip-path">
|
||||
<rect id="Rectangle_73" data-name="Rectangle 73" width="6395" height="1079" transform="translate(-5391)" fill="#fff"/>
|
||||
</clipPath>
|
||||
<linearGradient id="linear-gradient" x1="0.631" y1="0.5" x2="0.958" y2="0.488" gradientUnits="objectBoundingBox">
|
||||
<stop offset="0" stop-color="#2e364a"/>
|
||||
<stop offset="1" stop-color="#2c344a"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="Web_1920_1" data-name="Web 1920 – 1" clip-path="url(#clip-Web_1920_1)">
|
||||
<g id="Mask_Group_1" data-name="Mask Group 1" transform="translate(5391)" clip-path="url(#clip-path)">
|
||||
<g id="Group_118" data-name="Group 118" transform="translate(-419.333 -1.126)">
|
||||
<path id="Path_142" data-name="Path 142" d="M6271.734-6.176s-222.478,187.809-55.349,583.254c44.957,106.375,81.514,205.964,84.521,277,8.164,192.764-156.046,268.564-156.046,268.564l-653.53-26.8L5475.065-21.625Z" transform="translate(-4876.383)" fill="#2d3750"/>
|
||||
<path id="Union_6" data-name="Union 6" d="M-2631.1,1081.8v-1.6H-8230.9V.022h5599.8V0h759.7s-187.845,197.448-91.626,488.844c49.167,148.9,96.309,256.289,104.683,362.118,7.979,100.852-57.98,201.711-168.644,254.286-65.858,31.29-144.552,42.382-223.028,42.383C-2441.2,1147.632-2631.1,1081.8-2631.1,1081.8Z" transform="translate(3259.524 0.803)" fill="url(#linear-gradient)"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
@ -4,8 +4,8 @@
|
||||
<rect id="Rectangle_73" data-name="Rectangle 73" width="6395" height="1079" transform="translate(-5391)" fill="#fff"/>
|
||||
</clipPath>
|
||||
<linearGradient id="linear-gradient" x1="0.747" y1="0.222" x2="0.973" y2="0.807" gradientUnits="objectBoundingBox">
|
||||
<stop offset="0" stop-color="#2b51b4"/>
|
||||
<stop offset="1" stop-color="#1c3faa"/>
|
||||
<stop offset="0" stop-color="#2c41b4"/>
|
||||
<stop offset="1" stop-color="#1b4fab"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="Mask_Group_1" data-name="Mask Group 1" transform="translate(5391)" clip-path="url(#clip-path)">
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@ -2,6 +2,7 @@ import AppLogo from './src/AppLogo.vue';
|
||||
import AppProvider from './src/AppProvider.vue';
|
||||
import AppSearch from './src/search/AppSearch.vue';
|
||||
import AppLocalePicker from './src/AppLocalePicker.vue';
|
||||
import AppDarkModeToggle from './src/AppDarkModeToggle.vue';
|
||||
|
||||
export { useAppProviderContext } from './src/useAppContext';
|
||||
export { AppLogo, AppProvider, AppSearch, AppLocalePicker };
|
||||
export { AppLogo, AppProvider, AppSearch, AppLocalePicker, AppDarkModeToggle };
|
||||
|
111
src/components/Application/src/AppDarkModeToggle.vue
Normal file
111
src/components/Application/src/AppDarkModeToggle.vue
Normal file
@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="getShowDarkModeToggle"
|
||||
:class="[
|
||||
prefixCls,
|
||||
`${prefixCls}--${size}`,
|
||||
{
|
||||
[`${prefixCls}--dark`]: isDark,
|
||||
},
|
||||
]"
|
||||
@click="toggleDarkMode"
|
||||
>
|
||||
<div :class="`${prefixCls}-inner`"> </div>
|
||||
<SvgIcon size="14" name="sun" />
|
||||
<SvgIcon size="14" name="moon" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from 'vue';
|
||||
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
|
||||
import { SvgIcon } from '/@/components/Icon';
|
||||
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
||||
import { updateHeaderBgColor, updateSidebarBgColor } from '/@/logics/theme/updateBackground';
|
||||
import { updateDarkTheme } from '/@/logics/theme/dark';
|
||||
|
||||
import { ThemeEnum } from '/@/enums/appEnum';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DarkModeToggle',
|
||||
components: { SvgIcon },
|
||||
props: {
|
||||
size: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
validate: (val) => ['default', 'large'].includes(val),
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { prefixCls } = useDesign('dark-mode-toggle');
|
||||
const { getDarkMode, setDarkMode, getShowDarkModeToggle } = useRootSetting();
|
||||
const isDark = computed(() => getDarkMode.value === ThemeEnum.DARK);
|
||||
function toggleDarkMode() {
|
||||
const darkMode = getDarkMode.value === ThemeEnum.DARK ? ThemeEnum.LIGHT : ThemeEnum.DARK;
|
||||
setDarkMode(darkMode);
|
||||
updateDarkTheme(darkMode);
|
||||
updateHeaderBgColor();
|
||||
updateSidebarBgColor();
|
||||
}
|
||||
|
||||
return {
|
||||
isDark,
|
||||
prefixCls,
|
||||
toggleDarkMode,
|
||||
getShowDarkModeToggle,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
@prefix-cls: ~'@{namespace}-dark-mode-toggle';
|
||||
|
||||
html[data-theme='dark'] {
|
||||
.@{prefix-cls} {
|
||||
border: 1px solid rgb(196, 188, 188);
|
||||
}
|
||||
}
|
||||
|
||||
.@{prefix-cls} {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 50px;
|
||||
height: 26px;
|
||||
padding: 0 6px;
|
||||
margin-left: auto;
|
||||
cursor: pointer;
|
||||
background-color: #151515;
|
||||
border-radius: 30px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
&-inner {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
transition: transform 0.5s, background-color 0.5s;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
&--dark {
|
||||
.@{prefix-cls}-inner {
|
||||
transform: translateX(calc(100% + 2px));
|
||||
}
|
||||
}
|
||||
|
||||
&--large {
|
||||
width: 72px;
|
||||
height: 34px;
|
||||
padding: 0 10px;
|
||||
|
||||
.@{prefix-cls}-inner {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -42,9 +42,9 @@
|
||||
padding: 0 16px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
background: rgb(255 255 255);
|
||||
background: @component-background;
|
||||
border-top: 1px solid @border-color-base;
|
||||
border-radius: 0 0 16px 16px;
|
||||
box-shadow: 0 -1px 0 0 #e0e3e8, 0 -3px 6px 0 rgba(69, 98, 155, 0.12);
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
|
@ -190,12 +190,10 @@
|
||||
&-content {
|
||||
position: relative;
|
||||
width: 632px;
|
||||
// padding: 14px;
|
||||
margin: 0 auto auto auto;
|
||||
background: #f5f6f7;
|
||||
background: @component-background;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
||||
// box-shadow: inset 1px 1px 0 0 hsla(0, 0%, 100%, 0.5), 0 3px 8px 0 #555a64;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@ -253,8 +251,7 @@
|
||||
font-size: 14px;
|
||||
color: @text-color-base;
|
||||
cursor: pointer;
|
||||
// background: @primary-color;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 3px 0 #d4d9e1;
|
||||
align-items: center;
|
||||
|
@ -101,7 +101,7 @@
|
||||
@prefix-cls: ~'@{namespace}-collapse-container';
|
||||
|
||||
.@{prefix-cls} {
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
border-radius: 2px;
|
||||
transition: all 0.3s ease-in-out;
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
&:not(.ant-menu-item-disabled):hover {
|
||||
color: @text-color-base;
|
||||
background: #eee;
|
||||
background: @item-hover-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -36,7 +36,7 @@
|
||||
width: 156px;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
background-color: #fff;
|
||||
background-color: @component-background;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.1),
|
||||
|
@ -221,7 +221,7 @@
|
||||
.ant-drawer-body {
|
||||
height: calc(100% - @header-height);
|
||||
padding: 0;
|
||||
background-color: #fff;
|
||||
background-color: @component-background;
|
||||
|
||||
.scrollbar__wrap {
|
||||
padding: 16px !important;
|
||||
|
@ -74,7 +74,7 @@
|
||||
width: 100%;
|
||||
padding: 0 12px 0 20px;
|
||||
text-align: right;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
border-top: 1px solid @border-color-base;
|
||||
|
||||
> * {
|
||||
|
@ -258,7 +258,7 @@
|
||||
const { label, helpMessage, helpComponentProps, subLabel } = props.schema;
|
||||
const renderLabel = subLabel ? (
|
||||
<span>
|
||||
{label} <span style="color:#00000073">{subLabel}</span>
|
||||
{label} <span class="text-secondary">{subLabel}</span>
|
||||
</span>
|
||||
) : (
|
||||
label
|
||||
|
@ -1,16 +1,15 @@
|
||||
<template>
|
||||
<section class="full-loading" :class="{ absolute }" v-show="loading" :style="getStyle">
|
||||
<section class="full-loading" :class="{ absolute }" v-show="loading">
|
||||
<Spin v-bind="$attrs" :tip="tip" :size="size" :spinning="loading" />
|
||||
</section>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { computed, CSSProperties, PropType } from 'vue';
|
||||
import { PropType } from 'vue';
|
||||
|
||||
import { defineComponent } from 'vue';
|
||||
import { Spin } from 'ant-design-vue';
|
||||
|
||||
import { SizeEnum } from '/@/enums/sizeEnum';
|
||||
import { ThemeEnum } from '/@/enums/appEnum';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Loading',
|
||||
@ -38,25 +37,6 @@
|
||||
background: {
|
||||
type: String as PropType<string>,
|
||||
},
|
||||
theme: {
|
||||
type: String as PropType<'dark' | 'light'>,
|
||||
default: 'light',
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const getStyle = computed(
|
||||
(): CSSProperties => {
|
||||
const { background, theme } = props;
|
||||
const bgColor = background
|
||||
? background
|
||||
: theme === ThemeEnum.DARK
|
||||
? 'rgba(0, 0, 0, 0.2)'
|
||||
: 'rgba(240, 242, 245, 0.4)';
|
||||
return { background: bgColor };
|
||||
}
|
||||
);
|
||||
|
||||
return { getStyle };
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@ -71,6 +51,7 @@
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: rgba(240, 242, 245, 0.4);
|
||||
|
||||
&.absolute {
|
||||
position: absolute;
|
||||
@ -79,4 +60,10 @@
|
||||
z-index: 300;
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='dark'] {
|
||||
.full-loading {
|
||||
background: @modal-mask-bg;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -10,7 +10,7 @@
|
||||
onUnmounted,
|
||||
nextTick,
|
||||
computed,
|
||||
watchEffect,
|
||||
watch,
|
||||
} from 'vue';
|
||||
import Vditor from 'vditor';
|
||||
import 'vditor/dist/index.css';
|
||||
@ -18,6 +18,7 @@
|
||||
import { propTypes } from '/@/utils/propTypes';
|
||||
import { useLocale } from '/@/locales/useLocale';
|
||||
import { useModalContext } from '../../Modal';
|
||||
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
||||
|
||||
type Lang = 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' | undefined;
|
||||
export default defineComponent({
|
||||
@ -35,8 +36,24 @@
|
||||
const modalFn = useModalContext();
|
||||
|
||||
const { getLocale } = useLocale();
|
||||
const { getDarkMode } = useRootSetting();
|
||||
|
||||
watchEffect(() => {});
|
||||
watch(
|
||||
[() => getDarkMode.value, () => initedRef.value],
|
||||
([val]) => {
|
||||
const vditor = unref(vditorRef);
|
||||
|
||||
if (!vditor) {
|
||||
return;
|
||||
}
|
||||
const theme = val === 'dark' ? 'dark' : undefined;
|
||||
vditor.setTheme(theme as 'dark');
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
flush: 'post',
|
||||
}
|
||||
);
|
||||
|
||||
const getCurrentLang = computed((): 'zh_CN' | 'en_US' | 'ja_JP' | 'ko_KR' => {
|
||||
let lang: Lang;
|
||||
@ -60,6 +77,7 @@
|
||||
if (!wrapEl) return;
|
||||
const bindValue = { ...attrs, ...props };
|
||||
vditorRef.value = new Vditor(wrapEl, {
|
||||
theme: 'classic',
|
||||
lang: unref(getCurrentLang),
|
||||
mode: 'sv',
|
||||
preview: {
|
||||
|
@ -38,8 +38,8 @@
|
||||
align-items: center;
|
||||
padding: 0 24px;
|
||||
line-height: 44px;
|
||||
background: #fff;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
background: @component-background;
|
||||
border-top: 1px solid @border-color-base;
|
||||
box-shadow: 0 -6px 16px -8px rgba(0, 0, 0, 0.08), 0 -9px 28px 0 rgba(0, 0, 0, 0.05),
|
||||
0 -12px 48px 16px rgba(0, 0, 0, 0.03);
|
||||
transition: width 0.2s;
|
||||
|
@ -17,11 +17,7 @@
|
||||
<slot :name="item" v-bind="data"></slot>
|
||||
</template>
|
||||
</PageHeader>
|
||||
<div
|
||||
class="overflow-hidden"
|
||||
:class="[`${prefixCls}-content`, contentClass]"
|
||||
:style="getContentStyle"
|
||||
>
|
||||
<div class="overflow-hidden" :class="getContentClass" :style="getContentStyle">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<PageFooter v-if="getShowFooter" ref="footerRef">
|
||||
@ -87,14 +83,12 @@
|
||||
|
||||
const getContentStyle = computed(
|
||||
(): CSSProperties => {
|
||||
const { contentBackground, contentFullHeight, contentStyle, fixedHeight } = props;
|
||||
const bg = contentBackground ? { backgroundColor: '#fff' } : {};
|
||||
const { contentFullHeight, contentStyle, fixedHeight } = props;
|
||||
if (!contentFullHeight) {
|
||||
return { ...bg, ...contentStyle };
|
||||
return { ...contentStyle };
|
||||
}
|
||||
const height = `${unref(pageHeight)}px`;
|
||||
return {
|
||||
...bg,
|
||||
...contentStyle,
|
||||
minHeight: height,
|
||||
...(fixedHeight ? { height } : {}),
|
||||
@ -103,6 +97,17 @@
|
||||
}
|
||||
);
|
||||
|
||||
const getContentClass = computed(() => {
|
||||
const { contentBackground, contentClass } = props;
|
||||
return [
|
||||
`${prefixCls}-content`,
|
||||
contentClass,
|
||||
{
|
||||
[`${prefixCls}-content-bg`]: contentBackground,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
watch(
|
||||
() => [contentHeight?.value, getShowFooter.value],
|
||||
() => {
|
||||
@ -170,6 +175,7 @@
|
||||
getShowFooter,
|
||||
pageHeight,
|
||||
omit,
|
||||
getContentClass,
|
||||
};
|
||||
},
|
||||
});
|
||||
@ -190,6 +196,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
&-content-bg {
|
||||
background: @component-background;
|
||||
}
|
||||
|
||||
&--dense {
|
||||
.@{prefix-cls}-content {
|
||||
margin: 0;
|
||||
|
@ -11,9 +11,9 @@
|
||||
|
||||
&-dark&-vertical .@{simple-prefix-cls}__children,
|
||||
&-dark&-popup .@{simple-prefix-cls}__children {
|
||||
background-color: @sider-dark-lighten-1-bg-color;
|
||||
background-color: @sider-dark-lighten-bg-color;
|
||||
> .@{prefix-cls}-submenu-title {
|
||||
background-color: @sider-dark-lighten-1-bg-color;
|
||||
background-color: @sider-dark-lighten-bg-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -298,6 +298,26 @@
|
||||
|
||||
@prefix-cls: ~'@{namespace}-basic-table';
|
||||
|
||||
html[data-theme='light'] {
|
||||
.@{prefix-cls} {
|
||||
&-row__striped {
|
||||
td {
|
||||
background: #fafafa;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='dark'] {
|
||||
.@{prefix-cls} {
|
||||
&-row__striped {
|
||||
td {
|
||||
background: rgb(255 255 255 / 4%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{prefix-cls} {
|
||||
&-form-container {
|
||||
padding: 16px;
|
||||
@ -305,17 +325,11 @@
|
||||
.ant-form {
|
||||
padding: 12px 10px 6px 10px;
|
||||
margin-bottom: 16px;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&-row__striped {
|
||||
td {
|
||||
background: #fafafa;
|
||||
}
|
||||
}
|
||||
|
||||
&--inset {
|
||||
.ant-table-wrapper {
|
||||
padding: 0;
|
||||
@ -328,7 +342,7 @@
|
||||
|
||||
.ant-table-wrapper {
|
||||
padding: 6px;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
border-radius: 2px;
|
||||
|
||||
.ant-table-title {
|
||||
@ -340,7 +354,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
.ant-table {
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
|
@ -324,7 +324,7 @@
|
||||
const showTitle = title || toolbar || search || slots.headerTitle;
|
||||
const scrollStyle: CSSProperties = { height: 'calc(100% - 38px)' };
|
||||
return (
|
||||
<div class={[prefixCls, 'h-full bg-white', attrs.class]}>
|
||||
<div class={[prefixCls, 'h-full', attrs.class]}>
|
||||
{showTitle && (
|
||||
<TreeHeader
|
||||
checkable={checkable}
|
||||
@ -361,6 +361,8 @@
|
||||
@prefix-cls: ~'@{namespace}-basic-tree';
|
||||
|
||||
.@{prefix-cls} {
|
||||
background: @component-background;
|
||||
|
||||
.ant-tree-node-content-wrapper {
|
||||
position: relative;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
.file-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
// border: 1px solid @border-color-light;
|
||||
|
||||
.center {
|
||||
text-align: center;
|
||||
@ -21,12 +20,12 @@
|
||||
}
|
||||
|
||||
thead {
|
||||
background-color: @background-color-dark;
|
||||
background-color: @background-color-light;
|
||||
}
|
||||
|
||||
table,
|
||||
td,
|
||||
th {
|
||||
border: 1px solid @border-color-light;
|
||||
border: 1px solid @border-color-base;
|
||||
}
|
||||
}
|
||||
|
@ -61,9 +61,9 @@
|
||||
&.ant-btn-link.is-disabled {
|
||||
color: rgba(0, 0, 0, 0.25) !important;
|
||||
text-shadow: none;
|
||||
cursor: not-allowed;
|
||||
background-color: transparent;
|
||||
border-color: transparent;
|
||||
cursor: not-allowed !important;
|
||||
background-color: transparent !important;
|
||||
border-color: transparent !important;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
@ -187,7 +187,7 @@
|
||||
|
||||
&-ghost {
|
||||
color: @button-ghost-color;
|
||||
background-color: @white;
|
||||
background-color: transparent;
|
||||
border-color: @button-ghost-color;
|
||||
border-width: 1px;
|
||||
|
||||
@ -205,4 +205,14 @@
|
||||
border-color: fade(@button-ghost-color, 40%);
|
||||
}
|
||||
}
|
||||
|
||||
&-ghost.ant-btn-link:not([disabled='disabled']) {
|
||||
color: @button-ghost-color;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: @button-ghost-hover-color;
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,33 @@
|
||||
html[data-theme='dark'] {
|
||||
.ant-pagination {
|
||||
&.mini {
|
||||
.ant-pagination-prev,
|
||||
.ant-pagination-next,
|
||||
.ant-pagination-item {
|
||||
background: rgb(255 255 255 / 4%) !important;
|
||||
|
||||
a {
|
||||
color: #8b949e !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-select-arrow {
|
||||
color: @text-color-secondary !important;
|
||||
}
|
||||
|
||||
.ant-pagination-item-active {
|
||||
background: @primary-color !important;
|
||||
border: none;
|
||||
border-radius: none !important;
|
||||
|
||||
a {
|
||||
color: @white !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-pagination {
|
||||
&.mini {
|
||||
.ant-pagination-prev,
|
||||
|
@ -1,4 +1,4 @@
|
||||
:root {
|
||||
html {
|
||||
// header
|
||||
--header-bg-color: #394664;
|
||||
--header-bg-hover-color: #273352;
|
||||
@ -7,16 +7,13 @@
|
||||
// sider
|
||||
--sider-dark-bg-color: #273352;
|
||||
--sider-dark-darken-bg-color: #273352;
|
||||
--sider-dark-lighten-1-bg-color: #273352;
|
||||
--sider-dark-lighten-2-bg-color: #273352;
|
||||
--sider-dark-lighten-bg-color: #273352;
|
||||
}
|
||||
|
||||
@white: #fff;
|
||||
|
||||
@content-bg: #f4f7f9;
|
||||
// @content-bg: #f0f2f5;
|
||||
|
||||
@basic-mask-color: fade(@white, 30%);
|
||||
// :export {
|
||||
// name: "less";
|
||||
// mainColor: @mainColor;
|
||||
@ -35,10 +32,7 @@
|
||||
@border-color-shallow-dark: #cececd;
|
||||
|
||||
// Light-dark
|
||||
@border-color-light: #ebeef5;
|
||||
|
||||
// Light-light
|
||||
@border-color-shallow-light: #f2f6fc;
|
||||
@border-color-light: @border-color-base;
|
||||
|
||||
// =================================
|
||||
// ==============message==============
|
||||
@ -53,17 +47,6 @@
|
||||
// danger-bg-color
|
||||
@danger-background-color: #fef0f0;
|
||||
|
||||
// =================================
|
||||
// ==============bg color============
|
||||
// =================================
|
||||
|
||||
// dark
|
||||
@background-color-dark: #f4f7f9;
|
||||
// light
|
||||
@background-color-light: #f5f7fa;
|
||||
// layout content background
|
||||
@layout-content-bg-color: #f1f1f6;
|
||||
|
||||
// =================================
|
||||
// ==============Header=============
|
||||
// =================================
|
||||
@ -83,14 +66,11 @@
|
||||
// let -menu
|
||||
@sider-dark-bg-color: var(--sider-dark-bg-color);
|
||||
@sider-dark-darken-bg-color: var(--sider-dark-darken-bg-color);
|
||||
@sider-dark-lighten-1-bg-color: var(--sider-dark-lighten-1-bg-color);
|
||||
@sider-dark-lighten-2-bg-color: var(--sider-dark-lighten-2-bg-color);
|
||||
@sider-dark-lighten-bg-color: var(--sider-dark-lighten-bg-color);
|
||||
|
||||
// trigger
|
||||
@trigger-dark-hover-bg-color: rgba(255, 255, 255, 0.2);
|
||||
@trigger-dark-bg-color: rgba(255, 255, 255, 0.1);
|
||||
@trigger-light-bg-color: @white;
|
||||
@trigger-light-hover-bg-color: rgba(255, 255, 255, 0.7);
|
||||
|
||||
// =================================
|
||||
// ==============tree============
|
||||
@ -119,9 +99,6 @@
|
||||
// Auxiliary information color-dark
|
||||
@text-color-help-dark: #909399;
|
||||
|
||||
// Auxiliary information color-light color
|
||||
@text-color-help-light: #c0c4cc;
|
||||
|
||||
// =================================
|
||||
// ==============breadcrumb=========
|
||||
// =================================
|
||||
|
@ -2,6 +2,7 @@
|
||||
@import 'var/index.less';
|
||||
@import 'public.less';
|
||||
@import 'ant/index.less';
|
||||
@import './theme.less';
|
||||
|
||||
input:-webkit-autofill {
|
||||
-webkit-box-shadow: 0 0 0 1000px white inset !important;
|
||||
|
31
src/design/theme.less
Normal file
31
src/design/theme.less
Normal file
@ -0,0 +1,31 @@
|
||||
.bg-white {
|
||||
background: @component-background !important;
|
||||
}
|
||||
|
||||
html[data-theme='light'] {
|
||||
.text-secondary {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='dark'] {
|
||||
.text-secondary {
|
||||
color: #8b949e;
|
||||
}
|
||||
|
||||
.ant-card-grid-hoverable:hover {
|
||||
box-shadow: 0 3px 6px -4px rgb(0 0 0 / 48%), 0 6px 16px 0 rgb(0 0 0 / 32%),
|
||||
0 9px 28px 8px rgb(0 0 0 / 20%);
|
||||
}
|
||||
|
||||
.ant-alert-message,
|
||||
.ant-alert-with-description .ant-alert-message,
|
||||
.ant-alert-description {
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
|
||||
.ant-checkbox-checked .ant-checkbox-inner::after {
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
}
|
||||
}
|
@ -5,14 +5,12 @@ const loadingDirective: Directive = {
|
||||
mounted(el, binding) {
|
||||
const tip = el.getAttribute('loading-tip');
|
||||
const background = el.getAttribute('loading-background');
|
||||
const theme = el.getAttribute('loading-theme');
|
||||
const size = el.getAttribute('loading-size');
|
||||
const fullscreen = !!binding.modifiers.fullscreen;
|
||||
const instance = createLoading(
|
||||
{
|
||||
tip,
|
||||
background,
|
||||
theme,
|
||||
size: size || 'large',
|
||||
loading: !!binding.value,
|
||||
absolute: !fullscreen,
|
||||
|
@ -8,17 +8,9 @@ export enum ContentEnum {
|
||||
FIXED = 'fixed',
|
||||
}
|
||||
|
||||
// app current theme
|
||||
export enum ThemeModeEnum {
|
||||
LIGHT = 'light-mode',
|
||||
DARK = 'dark-mode',
|
||||
SEMI_DARK = 'semi-dark-mode',
|
||||
}
|
||||
|
||||
// menu theme enum
|
||||
export enum ThemeEnum {
|
||||
DARK = 'dark',
|
||||
|
||||
LIGHT = 'light',
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,8 @@ export const PROJ_CFG_KEY = 'PROJ__CFG__KEY__';
|
||||
// lock info
|
||||
export const LOCK_INFO_KEY = 'LOCK__INFO__KEY__';
|
||||
|
||||
export const APP_DARK_MODE_KEY_ = '__APP__DARK__MODE__';
|
||||
|
||||
// base global local key
|
||||
export const APP_LOCAL_CACHE_KEY = 'COMMON__LOCAL__KEY__';
|
||||
|
||||
|
@ -4,6 +4,7 @@ import { computed, unref } from 'vue';
|
||||
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
import { ContentEnum } from '/@/enums/appEnum';
|
||||
import { ThemeEnum } from '../../enums/appEnum';
|
||||
|
||||
type RootSetting = Omit<
|
||||
ProjectConfig,
|
||||
@ -48,6 +49,10 @@ const getGrayMode = computed(() => unref(getRootSetting).grayMode);
|
||||
|
||||
const getLockTime = computed(() => unref(getRootSetting).lockTime);
|
||||
|
||||
const getShowDarkModeToggle = computed(() => unref(getRootSetting).showDarkModeToggle);
|
||||
|
||||
const getDarkMode = computed(() => appStore.getDarkMode);
|
||||
|
||||
const getLayoutContentMode = computed(() =>
|
||||
unref(getRootSetting).contentMode === ContentEnum.FULL ? ContentEnum.FULL : ContentEnum.FIXED
|
||||
);
|
||||
@ -56,6 +61,10 @@ function setRootSetting(setting: Partial<RootSetting>) {
|
||||
appStore.commitProjectConfigState(setting);
|
||||
}
|
||||
|
||||
function setDarkMode(mode: ThemeEnum) {
|
||||
appStore.commitDarkMode(mode);
|
||||
}
|
||||
|
||||
export function useRootSetting() {
|
||||
return {
|
||||
setRootSetting,
|
||||
@ -80,5 +89,8 @@ export function useRootSetting() {
|
||||
getContentMode,
|
||||
getLockTime,
|
||||
getThemeColor,
|
||||
getDarkMode,
|
||||
setDarkMode,
|
||||
getShowDarkModeToggle,
|
||||
};
|
||||
}
|
||||
|
@ -1,57 +0,0 @@
|
||||
import { useTimeoutFn } from '/@/hooks/core/useTimeout';
|
||||
import { unref, Ref, nextTick } from 'vue';
|
||||
import { tryOnUnmounted } from '@vueuse/core';
|
||||
|
||||
interface CallBackFn {
|
||||
(instance: Nullable<ApexCharts>): void;
|
||||
}
|
||||
|
||||
export function useApexCharts(elRef: Ref<HTMLDivElement>) {
|
||||
let chartInstance: Nullable<ApexCharts> = null;
|
||||
|
||||
function setOptions(options: any, callback?: CallBackFn) {
|
||||
nextTick(() => {
|
||||
useTimeoutFn(async () => {
|
||||
const el = unref(elRef);
|
||||
|
||||
if (!el || !unref(el)) return;
|
||||
const ApexCharts = await (await import('apexcharts')).default;
|
||||
chartInstance = new ApexCharts(el, options);
|
||||
|
||||
chartInstance && chartInstance.render();
|
||||
|
||||
// The callback method is added to setOptions to return the chartInstance to facilitate the re-operation of the chart, such as calling the updateOptions method to update the chart
|
||||
callback && callback(chartInstance);
|
||||
}, 30);
|
||||
});
|
||||
}
|
||||
|
||||
// Call the updateOptions method of ApexCharts to update the chart
|
||||
function updateOptions(
|
||||
chartInstance: Nullable<ApexCharts>,
|
||||
options: any,
|
||||
redraw = false,
|
||||
animate = true,
|
||||
updateSyncedCharts = true,
|
||||
callback: CallBackFn
|
||||
) {
|
||||
nextTick(() => {
|
||||
useTimeoutFn(() => {
|
||||
chartInstance && chartInstance.updateOptions(options, redraw, animate, updateSyncedCharts);
|
||||
|
||||
callback && callback(chartInstance);
|
||||
}, 30);
|
||||
});
|
||||
}
|
||||
|
||||
tryOnUnmounted(() => {
|
||||
if (!chartInstance) return;
|
||||
chartInstance?.destroy?.();
|
||||
chartInstance = null;
|
||||
});
|
||||
|
||||
return {
|
||||
setOptions,
|
||||
updateOptions,
|
||||
};
|
||||
}
|
@ -1,31 +1,46 @@
|
||||
import { useTimeoutFn } from '/@/hooks/core/useTimeout';
|
||||
import { tryOnUnmounted } from '@vueuse/core';
|
||||
import { unref, Ref, nextTick } from 'vue';
|
||||
import { unref, Ref, nextTick, watch, computed, ref } from 'vue';
|
||||
import type { EChartsOption } from 'echarts';
|
||||
import { useDebounce } from '/@/hooks/core/useDebounce';
|
||||
import { useEventListener } from '/@/hooks/event/useEventListener';
|
||||
import { useBreakpoint } from '/@/hooks/event/useBreakpoint';
|
||||
|
||||
import echarts from '/@/plugins/echarts';
|
||||
import { useRootSetting } from '../setting/useRootSetting';
|
||||
|
||||
export function useECharts(
|
||||
elRef: Ref<HTMLDivElement>,
|
||||
theme: 'light' | 'dark' | 'default' = 'light'
|
||||
) {
|
||||
const { getDarkMode } = useRootSetting();
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
let resizeFn: Fn = resize;
|
||||
const cacheOptions = ref<EChartsOption>({});
|
||||
let removeResizeFn: Fn = () => {};
|
||||
|
||||
const [debounceResize] = useDebounce(resize, 200);
|
||||
resizeFn = debounceResize;
|
||||
|
||||
function initCharts() {
|
||||
const getOptions = computed(
|
||||
(): EChartsOption => {
|
||||
if (getDarkMode.value !== 'dark') {
|
||||
return cacheOptions.value;
|
||||
}
|
||||
return {
|
||||
backgroundColor: '#151515',
|
||||
...cacheOptions.value,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
function initCharts(t = theme) {
|
||||
const el = unref(elRef);
|
||||
if (!el || !unref(el)) {
|
||||
return;
|
||||
}
|
||||
|
||||
chartInstance = echarts.init(el, theme);
|
||||
chartInstance = echarts.init(el, t);
|
||||
const { removeEvent } = useEventListener({
|
||||
el: window,
|
||||
name: 'resize',
|
||||
@ -41,22 +56,23 @@ export function useECharts(
|
||||
}
|
||||
|
||||
function setOptions(options: EChartsOption, clear = true) {
|
||||
cacheOptions.value = options;
|
||||
if (unref(elRef)?.offsetHeight === 0) {
|
||||
useTimeoutFn(() => {
|
||||
setOptions(options);
|
||||
setOptions(unref(getOptions));
|
||||
}, 30);
|
||||
return;
|
||||
}
|
||||
nextTick(() => {
|
||||
useTimeoutFn(() => {
|
||||
if (!chartInstance) {
|
||||
initCharts();
|
||||
initCharts(getDarkMode.value);
|
||||
|
||||
if (!chartInstance) return;
|
||||
}
|
||||
clear && chartInstance?.clear();
|
||||
|
||||
chartInstance?.setOption(options);
|
||||
chartInstance?.setOption(unref(getOptions));
|
||||
}, 30);
|
||||
});
|
||||
}
|
||||
@ -65,6 +81,17 @@ export function useECharts(
|
||||
chartInstance?.resize();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => getDarkMode.value,
|
||||
(theme) => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
initCharts(theme);
|
||||
setOptions(cacheOptions.value);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
tryOnUnmounted(() => {
|
||||
if (!chartInstance) return;
|
||||
removeResizeFn();
|
||||
|
@ -112,7 +112,6 @@
|
||||
@prefix-cls: ~'@{namespace}-layout-multiple-header';
|
||||
|
||||
.@{prefix-cls} {
|
||||
// margin-left: 1px;
|
||||
transition: width 0.2s;
|
||||
flex: 0 0 auto;
|
||||
|
||||
|
@ -91,7 +91,6 @@
|
||||
position: relative;
|
||||
height: 240px;
|
||||
padding: 130px 30px 60px 30px;
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
|
@ -131,10 +131,6 @@
|
||||
cursor: pointer;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
background: @header-light-bg-hover-color;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
|
@ -131,7 +131,7 @@
|
||||
}
|
||||
|
||||
&--light {
|
||||
background: @white;
|
||||
background: @white !important;
|
||||
border-bottom: 1px solid @header-light-bottom-border-color;
|
||||
border-left: 1px solid @header-light-bottom-border-color;
|
||||
|
||||
@ -165,8 +165,9 @@
|
||||
}
|
||||
|
||||
&--dark {
|
||||
background: @header-dark-bg-color;
|
||||
|
||||
background: @header-dark-bg-color !important;
|
||||
border-bottom: 1px solid @border-color-base;
|
||||
border-left: 1px solid @border-color-base;
|
||||
.@{header-prefix-cls}-logo {
|
||||
&:hover {
|
||||
background: @header-dark-bg-hover-color;
|
||||
|
@ -3,13 +3,15 @@ import { BasicDrawer } from '/@/components/Drawer/index';
|
||||
import { Divider } from 'ant-design-vue';
|
||||
import {
|
||||
TypePicker,
|
||||
ThemePicker,
|
||||
ThemeColorPicker,
|
||||
SettingFooter,
|
||||
SwitchItem,
|
||||
SelectItem,
|
||||
InputNumberItem,
|
||||
} from './components';
|
||||
|
||||
import { AppDarkModeToggle } from '/@/components/Application';
|
||||
|
||||
import { MenuTypeEnum, TriggerEnum } from '/@/enums/menuEnum';
|
||||
|
||||
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
||||
@ -52,6 +54,7 @@ export default defineComponent({
|
||||
getColorWeak,
|
||||
getGrayMode,
|
||||
getLockTime,
|
||||
getShowDarkModeToggle,
|
||||
getThemeColor,
|
||||
} = useRootSetting();
|
||||
|
||||
@ -116,7 +119,7 @@ export default defineComponent({
|
||||
|
||||
function renderHeaderTheme() {
|
||||
return (
|
||||
<ThemePicker
|
||||
<ThemeColorPicker
|
||||
colorList={HEADER_PRESET_BG_COLOR_LIST}
|
||||
def={unref(getHeaderBgColor)}
|
||||
event={HandlerEnum.HEADER_THEME}
|
||||
@ -126,7 +129,7 @@ export default defineComponent({
|
||||
|
||||
function renderSiderTheme() {
|
||||
return (
|
||||
<ThemePicker
|
||||
<ThemeColorPicker
|
||||
colorList={SIDE_BAR_BG_COLOR_LIST}
|
||||
def={unref(getMenuBgColor)}
|
||||
event={HandlerEnum.MENU_THEME}
|
||||
@ -136,7 +139,7 @@ export default defineComponent({
|
||||
|
||||
function renderMainTheme() {
|
||||
return (
|
||||
<ThemePicker
|
||||
<ThemeColorPicker
|
||||
colorList={APP_PRESET_COLOR_LIST}
|
||||
def={unref(getThemeColor)}
|
||||
event={HandlerEnum.CHANGE_THEME_COLOR}
|
||||
@ -404,6 +407,8 @@ export default defineComponent({
|
||||
width={330}
|
||||
wrapClassName="setting-drawer"
|
||||
>
|
||||
{unref(getShowDarkModeToggle) && <Divider>{() => t('layout.setting.darkMode')}</Divider>}
|
||||
{unref(getShowDarkModeToggle) && <AppDarkModeToggle class="mx-auto" size="large" />}
|
||||
<Divider>{() => t('layout.setting.navMode')}</Divider>
|
||||
{renderSidebar()}
|
||||
<Divider>{() => t('layout.setting.sysTheme')}</Divider>
|
||||
|
@ -26,7 +26,7 @@
|
||||
import { HandlerEnum } from '../enum';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ThemePicker',
|
||||
name: 'ThemeColorPicker',
|
||||
components: { CheckOutlined },
|
||||
props: {
|
||||
colorList: {
|
@ -74,7 +74,8 @@
|
||||
content: '';
|
||||
}
|
||||
|
||||
&--sidebar {
|
||||
&--sidebar,
|
||||
&--light {
|
||||
&::before {
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -124,6 +125,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
&--dark {
|
||||
background-color: #273352;
|
||||
}
|
||||
|
||||
&--mix-sidebar {
|
||||
&::before {
|
||||
top: 0;
|
||||
@ -152,17 +157,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// &::after {
|
||||
// position: absolute;
|
||||
// top: 50%;
|
||||
// left: 50%;
|
||||
// width: 0;
|
||||
// height: 0;
|
||||
// content: '';
|
||||
// opacity: 0;
|
||||
// transition: all 0.3s;
|
||||
// }
|
||||
|
||||
&:hover,
|
||||
&--active {
|
||||
padding: 12px;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent';
|
||||
|
||||
export const TypePicker = createAsyncComponent(() => import('./TypePicker.vue'));
|
||||
export const ThemePicker = createAsyncComponent(() => import('./ThemePicker.vue'));
|
||||
export const ThemeColorPicker = createAsyncComponent(() => import('./ThemeColorPicker.vue'));
|
||||
export const SettingFooter = createAsyncComponent(() => import('./SettingFooter.vue'));
|
||||
export const SwitchItem = createAsyncComponent(() => import('./SwitchItem.vue'));
|
||||
export const SelectItem = createAsyncComponent(() => import('./SelectItem.vue'));
|
||||
|
@ -14,6 +14,7 @@ const { t } = useI18n();
|
||||
export enum HandlerEnum {
|
||||
CHANGE_LAYOUT,
|
||||
CHANGE_THEME_COLOR,
|
||||
CHANGE_THEME,
|
||||
// menu
|
||||
MENU_HAS_DRAG,
|
||||
MENU_ACCORDION,
|
||||
|
@ -6,15 +6,20 @@ import { updateGrayMode } from '/@/logics/theme/updateGrayMode';
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
import { ProjectConfig } from '/#/config';
|
||||
import { changeTheme } from '/@/logics/theme';
|
||||
import { updateDarkTheme } from '/@/logics/theme/dark';
|
||||
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
|
||||
|
||||
export function baseHandler(event: HandlerEnum, value: any) {
|
||||
const config = handler(event, value);
|
||||
appStore.commitProjectConfigState(config);
|
||||
if (event === HandlerEnum.CHANGE_THEME) {
|
||||
updateHeaderBgColor();
|
||||
updateSidebarBgColor();
|
||||
}
|
||||
}
|
||||
|
||||
export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConfig> {
|
||||
const { getThemeColor } = useRootSetting();
|
||||
const { getThemeColor, getDarkMode } = useRootSetting();
|
||||
switch (event) {
|
||||
case HandlerEnum.CHANGE_LAYOUT:
|
||||
const { mode, type, split } = value;
|
||||
@ -36,8 +41,17 @@ export function handler(event: HandlerEnum, value: any): DeepPartial<ProjectConf
|
||||
return {};
|
||||
}
|
||||
changeTheme(value);
|
||||
|
||||
return { themeColor: value };
|
||||
|
||||
case HandlerEnum.CHANGE_THEME:
|
||||
if (getDarkMode.value === value) {
|
||||
return {};
|
||||
}
|
||||
updateDarkTheme(value);
|
||||
|
||||
return { darkMode: value };
|
||||
|
||||
case HandlerEnum.MENU_HAS_DRAG:
|
||||
return { menuSetting: { canDrag: value } };
|
||||
|
||||
|
@ -403,7 +403,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
@border-color: @sider-dark-lighten-1-bg-color;
|
||||
@border-color: @sider-dark-lighten-bg-color;
|
||||
|
||||
&.dark {
|
||||
&.open {
|
||||
|
@ -1,10 +1,19 @@
|
||||
@prefix-cls: ~'@{namespace}-multiple-tabs';
|
||||
|
||||
html[data-theme='dark'] {
|
||||
.@{prefix-cls} {
|
||||
.ant-tabs-tab {
|
||||
border-bottom: 1px solid @border-color-base;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.@{prefix-cls} {
|
||||
z-index: 10;
|
||||
height: @multiple-height + 2;
|
||||
line-height: @multiple-height + 2;
|
||||
background: @white;
|
||||
background: @component-background;
|
||||
border-bottom: 1px solid @border-color-base;
|
||||
box-shadow: 0 1px 2px 0 rgba(29, 35, 41, 0.05);
|
||||
|
||||
.ant-tabs-small {
|
||||
@ -15,7 +24,7 @@
|
||||
.ant-tabs-card-bar {
|
||||
height: @multiple-height;
|
||||
margin: 0;
|
||||
background: @white;
|
||||
background: @component-background;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
|
||||
@ -28,35 +37,14 @@
|
||||
height: calc(@multiple-height - 2px);
|
||||
padding-right: 12px;
|
||||
line-height: calc(@multiple-height - 2px);
|
||||
color: @text-color-call-out;
|
||||
background: @white;
|
||||
border-bottom: 1px solid @header-light-bottom-border-color;
|
||||
color: @text-color-base;
|
||||
background: @component-background;
|
||||
transition: none;
|
||||
|
||||
// &:not(.ant-tabs-tab-active)::before {
|
||||
// position: absolute;
|
||||
// top: -1px;
|
||||
// left: 50%;
|
||||
// width: 100%;
|
||||
// height: 2px;
|
||||
// background-color: @primary-color;
|
||||
// content: '';
|
||||
// opacity: 0;
|
||||
// transform: translate(-50%, 0) scaleX(0);
|
||||
// transform-origin: center;
|
||||
// transition: none;
|
||||
// }
|
||||
|
||||
&:hover {
|
||||
.ant-tabs-close-x {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
// &:not(.ant-tabs-tab-active)::before {
|
||||
// opacity: 1;
|
||||
// transform: translate(-50%, 0) scaleX(1);
|
||||
// transition: all 0.3s ease-in-out;
|
||||
// }
|
||||
}
|
||||
|
||||
.ant-tabs-close-x {
|
||||
@ -85,26 +73,20 @@
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-tab:not(.ant-tabs-tab-active) {
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-tabs-tab-active {
|
||||
position: relative;
|
||||
padding-left: 26px;
|
||||
padding-left: 18px;
|
||||
color: @white;
|
||||
background: fade(@primary-color, 100%);
|
||||
background: @primary-color;
|
||||
border: 0;
|
||||
transition: none;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: calc(50% - 3px);
|
||||
left: 8px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
content: '';
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.ant-tabs-close-x {
|
||||
opacity: 1;
|
||||
}
|
||||
@ -158,10 +140,10 @@
|
||||
width: 36px;
|
||||
height: @multiple-height;
|
||||
line-height: @multiple-height;
|
||||
color: #666;
|
||||
color: @text-color-secondary;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
border-left: 1px solid #eee;
|
||||
border-left: 1px solid @border-color-base;
|
||||
|
||||
&:hover {
|
||||
color: @text-color-base;
|
||||
|
@ -14,4 +14,7 @@ export default {
|
||||
|
||||
redo: 'Refresh',
|
||||
back: 'Back',
|
||||
|
||||
light: 'Light',
|
||||
dark: 'Dark',
|
||||
};
|
||||
|
@ -30,6 +30,7 @@ export default {
|
||||
|
||||
drawerTitle: 'Configuration',
|
||||
|
||||
darkMode: 'Dark mode',
|
||||
navMode: 'Navigation mode',
|
||||
interfaceFunction: 'Interface function',
|
||||
interfaceDisplay: 'Interface display',
|
||||
|
@ -6,5 +6,4 @@ export default {
|
||||
map: 'Map',
|
||||
line: 'Line',
|
||||
pie: 'Pie',
|
||||
apexChart: 'ApexChart',
|
||||
};
|
||||
|
@ -14,4 +14,7 @@ export default {
|
||||
|
||||
redo: '刷新',
|
||||
back: '返回',
|
||||
|
||||
light: '亮色主题',
|
||||
dark: '黑暗主题',
|
||||
};
|
||||
|
@ -29,6 +29,7 @@ export default {
|
||||
|
||||
drawerTitle: '项目配置',
|
||||
|
||||
darkMode: '主题',
|
||||
navMode: '导航栏模式',
|
||||
interfaceFunction: '界面功能',
|
||||
interfaceDisplay: '界面显示',
|
||||
|
@ -6,5 +6,4 @@ export default {
|
||||
map: '地图',
|
||||
line: '折线图',
|
||||
pie: '饼图',
|
||||
apexChart: 'ApexChart',
|
||||
};
|
||||
|
@ -9,6 +9,7 @@ import projectSetting from '/@/settings/projectSetting';
|
||||
import { updateHeaderBgColor, updateSidebarBgColor } from '/@/logics/theme/updateBackground';
|
||||
import { updateColorWeak } from '/@/logics/theme/updateColorWeak';
|
||||
import { updateGrayMode } from '/@/logics/theme/updateGrayMode';
|
||||
import { updateDarkTheme } from '/@/logics/theme/dark';
|
||||
import { changeTheme } from '/@/logics/theme';
|
||||
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
@ -19,30 +20,43 @@ import { getCommonStoragePrefix, getStorageShortName } from '/@/utils/env';
|
||||
import { primaryColor } from '../../build/config/themeConfig';
|
||||
import { Persistent } from '/@/utils/cache/persistent';
|
||||
import { deepMerge } from '/@/utils';
|
||||
import { ThemeEnum } from '../enums/appEnum';
|
||||
|
||||
// Initial project configuration
|
||||
export function initAppConfigStore() {
|
||||
let projCfg: ProjectConfig = Persistent.getLocal(PROJ_CFG_KEY) as ProjectConfig;
|
||||
projCfg = deepMerge(projectSetting, projCfg || {});
|
||||
const darkMode = appStore.getDarkMode;
|
||||
const {
|
||||
colorWeak,
|
||||
grayMode,
|
||||
themeColor,
|
||||
|
||||
headerSetting: { bgColor: headerBgColor } = {},
|
||||
menuSetting: { bgColor } = {},
|
||||
} = projCfg;
|
||||
try {
|
||||
const {
|
||||
colorWeak,
|
||||
grayMode,
|
||||
themeColor,
|
||||
headerSetting: { bgColor: headerBgColor } = {},
|
||||
menuSetting: { bgColor } = {},
|
||||
} = projCfg;
|
||||
if (themeColor && themeColor !== primaryColor) {
|
||||
changeTheme(themeColor);
|
||||
}
|
||||
headerBgColor && updateHeaderBgColor(headerBgColor);
|
||||
bgColor && updateSidebarBgColor(bgColor);
|
||||
|
||||
grayMode && updateGrayMode(grayMode);
|
||||
colorWeak && updateColorWeak(colorWeak);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
appStore.commitProjectConfigState(projCfg);
|
||||
|
||||
// init dark mode
|
||||
updateDarkTheme(darkMode);
|
||||
if (darkMode === ThemeEnum.DARK) {
|
||||
updateHeaderBgColor();
|
||||
updateSidebarBgColor();
|
||||
} else {
|
||||
headerBgColor && updateHeaderBgColor(headerBgColor);
|
||||
bgColor && updateSidebarBgColor(bgColor);
|
||||
}
|
||||
// init store
|
||||
localeStore.initLocale();
|
||||
|
||||
setTimeout(() => {
|
||||
|
13
src/logics/theme/dark.ts
Normal file
13
src/logics/theme/dark.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { darkCssIsReady, loadDarkThemeCss } from 'vite-plugin-theme/es/client';
|
||||
|
||||
export async function updateDarkTheme(mode: string | null = 'light') {
|
||||
const htmlRoot = document.getElementById('htmlRoot');
|
||||
if (mode === 'dark') {
|
||||
if (import.meta.env.PROD && !darkCssIsReady) {
|
||||
await loadDarkThemeCss();
|
||||
}
|
||||
htmlRoot?.setAttribute('data-theme', 'dark');
|
||||
} else {
|
||||
htmlRoot?.setAttribute('data-theme', 'light');
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import { getThemeColors, ThemeMode, generateColors } from '../../../build/config/themeConfig';
|
||||
import { getThemeColors, generateColors } from '../../../build/config/themeConfig';
|
||||
|
||||
import { replaceStyleVariables } from 'vite-plugin-theme/es/client';
|
||||
import { mixLighten, mixDarken, tinycolor } from 'vite-plugin-theme/es/colorUtils';
|
||||
|
||||
export async function changeTheme(color: string, theme?: ThemeMode) {
|
||||
export async function changeTheme(color: string) {
|
||||
const colors = generateColors({
|
||||
mixDarken,
|
||||
mixLighten,
|
||||
@ -12,6 +12,6 @@ export async function changeTheme(color: string, theme?: ThemeMode) {
|
||||
});
|
||||
|
||||
return await replaceStyleVariables({
|
||||
colorVariables: [...getThemeColors(color, theme), ...colors],
|
||||
colorVariables: [...getThemeColors(color), ...colors],
|
||||
});
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { isHexColor, colorIsDark, lighten, darken } from '/@/utils/color';
|
||||
import { colorIsDark, lighten, darken } from '/@/utils/color';
|
||||
import { appStore } from '/@/store/modules/app';
|
||||
import { ThemeEnum } from '/@/enums/appEnum';
|
||||
import { setCssVar } from './util';
|
||||
@ -9,29 +9,35 @@ const HEADER_MENU_ACTIVE_BG_COLOR_VAR = '--header-active-menu-bg-color';
|
||||
|
||||
const SIDER_DARK_BG_COLOR = '--sider-dark-bg-color';
|
||||
const SIDER_DARK_DARKEN_BG_COLOR = '--sider-dark-darken-bg-color';
|
||||
const SIDER_LIGHTEN_1_BG_COLOR = '--sider-dark-lighten-1-bg-color';
|
||||
const SIDER_LIGHTEN_2_BG_COLOR = '--sider-dark-lighten-2-bg-color';
|
||||
const SIDER_LIGHTEN_BG_COLOR = '--sider-dark-lighten-bg-color';
|
||||
|
||||
/**
|
||||
* Change the background color of the top header
|
||||
* @param color
|
||||
*/
|
||||
export function updateHeaderBgColor(color: string) {
|
||||
if (!isHexColor(color)) return;
|
||||
export function updateHeaderBgColor(color?: string) {
|
||||
const darkMode = appStore.getDarkMode === ThemeEnum.DARK;
|
||||
if (!color) {
|
||||
if (darkMode) {
|
||||
color = '#151515';
|
||||
} else {
|
||||
color = appStore.getProjectConfig.headerSetting.bgColor;
|
||||
}
|
||||
}
|
||||
// bg color
|
||||
setCssVar(HEADER_BG_COLOR_VAR, color);
|
||||
|
||||
// hover color
|
||||
const hoverColor = lighten(color, 6);
|
||||
const hoverColor = lighten(color!, 6);
|
||||
setCssVar(HEADER_BG_HOVER_COLOR_VAR, hoverColor);
|
||||
setCssVar(HEADER_MENU_ACTIVE_BG_COLOR_VAR, hoverColor);
|
||||
|
||||
// Determine the depth of the color value and automatically switch the theme
|
||||
const isDark = colorIsDark(color);
|
||||
const isDark = colorIsDark(color!);
|
||||
|
||||
appStore.commitProjectConfigState({
|
||||
headerSetting: {
|
||||
theme: isDark ? ThemeEnum.DARK : ThemeEnum.LIGHT,
|
||||
theme: isDark || darkMode ? ThemeEnum.DARK : ThemeEnum.LIGHT,
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -40,21 +46,27 @@ export function updateHeaderBgColor(color: string) {
|
||||
* Change the background color of the left menu
|
||||
* @param color bg color
|
||||
*/
|
||||
export function updateSidebarBgColor(color: string) {
|
||||
if (!isHexColor(color)) return;
|
||||
|
||||
export function updateSidebarBgColor(color?: string) {
|
||||
// if (!isHexColor(color)) return;
|
||||
const darkMode = appStore.getDarkMode === ThemeEnum.DARK;
|
||||
if (!color) {
|
||||
if (darkMode) {
|
||||
color = '#212121';
|
||||
} else {
|
||||
color = appStore.getProjectConfig.menuSetting.bgColor;
|
||||
}
|
||||
}
|
||||
setCssVar(SIDER_DARK_BG_COLOR, color);
|
||||
setCssVar(SIDER_DARK_DARKEN_BG_COLOR, darken(color, 6));
|
||||
setCssVar(SIDER_LIGHTEN_1_BG_COLOR, lighten(color, 5));
|
||||
setCssVar(SIDER_LIGHTEN_2_BG_COLOR, lighten(color, 8));
|
||||
setCssVar(SIDER_DARK_DARKEN_BG_COLOR, darken(color!, 6));
|
||||
setCssVar(SIDER_LIGHTEN_BG_COLOR, lighten(color!, 5));
|
||||
|
||||
// only #ffffff is light
|
||||
// Only when the background color is #fff, the theme of the menu will be changed to light
|
||||
const isLight = ['#fff', '#ffffff'].includes(color.toLowerCase());
|
||||
const isLight = ['#fff', '#ffffff'].includes(color!.toLowerCase());
|
||||
|
||||
appStore.commitProjectConfigState({
|
||||
menuSetting: {
|
||||
theme: isLight ? ThemeEnum.LIGHT : ThemeEnum.DARK,
|
||||
theme: isLight && !darkMode ? ThemeEnum.LIGHT : ThemeEnum.DARK,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -21,11 +21,6 @@ const menu: MenuModule = {
|
||||
path: 'googleMap',
|
||||
name: t('routes.demo.charts.googleMap'),
|
||||
},
|
||||
{
|
||||
path: 'apexChart',
|
||||
name: t('routes.demo.charts.apexChart'),
|
||||
},
|
||||
|
||||
{
|
||||
path: 'echarts',
|
||||
name: 'Echarts',
|
||||
|
@ -38,14 +38,6 @@ const charts: AppRouteModule = {
|
||||
component: () => import('/@/views/demo/charts/map/Google.vue'),
|
||||
},
|
||||
|
||||
{
|
||||
path: 'apexChart',
|
||||
name: 'ApexChart',
|
||||
meta: {
|
||||
title: t('routes.demo.charts.apexChart'),
|
||||
},
|
||||
component: () => import('/@/views/demo/charts/apex/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'echarts',
|
||||
name: 'Echarts',
|
||||
|
@ -1,7 +1,10 @@
|
||||
import { ThemeEnum } from '../enums/appEnum';
|
||||
export default {
|
||||
prefixCls: 'vben',
|
||||
};
|
||||
|
||||
export const darkMode = ThemeEnum.LIGHT;
|
||||
|
||||
// app theme preset color
|
||||
export const APP_PRESET_COLOR_LIST: string[] = [
|
||||
'#0960bd',
|
||||
@ -18,6 +21,7 @@ export const APP_PRESET_COLOR_LIST: string[] = [
|
||||
// header preset color
|
||||
export const HEADER_PRESET_BG_COLOR_LIST: string[] = [
|
||||
'#ffffff',
|
||||
'#151515',
|
||||
'#009688',
|
||||
'#5172DC',
|
||||
'#018ffb',
|
||||
@ -32,6 +36,7 @@ export const HEADER_PRESET_BG_COLOR_LIST: string[] = [
|
||||
// sider preset color
|
||||
export const SIDE_BAR_BG_COLOR_LIST: string[] = [
|
||||
'#001529',
|
||||
'#212121',
|
||||
'#273352',
|
||||
'#ffffff',
|
||||
'#191b24',
|
||||
|
@ -9,13 +9,16 @@ import {
|
||||
SettingButtonPositionEnum,
|
||||
} from '/@/enums/appEnum';
|
||||
import { SIDE_BAR_BG_COLOR_LIST, HEADER_PRESET_BG_COLOR_LIST } from './designSetting';
|
||||
import { primaryColor, themeMode } from '../../build/config/themeConfig';
|
||||
import { primaryColor } from '../../build/config/themeConfig';
|
||||
|
||||
// ! You need to clear the browser cache after the change
|
||||
const setting: ProjectConfig = {
|
||||
// Whether to show the configuration button
|
||||
showSettingButton: true,
|
||||
|
||||
// Whether to show the theme switch button
|
||||
showDarkModeToggle: true,
|
||||
|
||||
// `Settings` button position
|
||||
settingButtonPosition: SettingButtonPositionEnum.AUTO,
|
||||
|
||||
@ -28,9 +31,6 @@ const setting: ProjectConfig = {
|
||||
// color
|
||||
themeColor: primaryColor,
|
||||
|
||||
// TODO dark theme
|
||||
themeMode: themeMode,
|
||||
|
||||
// Website gray mode, open for possible mourning dates
|
||||
grayMode: false,
|
||||
|
||||
|
@ -4,13 +4,16 @@ import type { BeforeMiniState } from '../types';
|
||||
import { VuexModule, getModule, Module, Mutation, Action } from 'vuex-module-decorators';
|
||||
import store from '/@/store';
|
||||
|
||||
import { PROJ_CFG_KEY } from '/@/enums/cacheEnum';
|
||||
import { PROJ_CFG_KEY, APP_DARK_MODE_KEY_ } from '/@/enums/cacheEnum';
|
||||
|
||||
import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
|
||||
import { Persistent } from '/@/utils/cache/persistent';
|
||||
import { deepMerge } from '/@/utils';
|
||||
|
||||
import { resetRouter } from '/@/router';
|
||||
import { ThemeEnum } from '../../enums/appEnum';
|
||||
|
||||
import { darkMode } from '/@/settings/designSetting';
|
||||
|
||||
export interface LockInfo {
|
||||
pwd: string | undefined;
|
||||
@ -22,6 +25,8 @@ const NAME = 'app';
|
||||
hotModuleUnregisterModule(NAME);
|
||||
@Module({ dynamic: true, namespaced: true, store, name: NAME })
|
||||
export default class App extends VuexModule {
|
||||
private darkMode;
|
||||
|
||||
// Page loading status
|
||||
private pageLoadingState = false;
|
||||
|
||||
@ -38,6 +43,10 @@ export default class App extends VuexModule {
|
||||
return this.pageLoadingState;
|
||||
}
|
||||
|
||||
get getDarkMode() {
|
||||
return this.darkMode || localStorage.getItem(APP_DARK_MODE_KEY_) || darkMode;
|
||||
}
|
||||
|
||||
get getBeforeMiniState() {
|
||||
return this.beforeMiniState;
|
||||
}
|
||||
@ -55,6 +64,12 @@ export default class App extends VuexModule {
|
||||
this.pageLoadingState = loading;
|
||||
}
|
||||
|
||||
@Mutation
|
||||
commitDarkMode(mode: ThemeEnum): void {
|
||||
this.darkMode = mode;
|
||||
localStorage.setItem(APP_DARK_MODE_KEY_, mode);
|
||||
}
|
||||
|
||||
@Mutation
|
||||
commitBeforeMiniState(state: BeforeMiniState): void {
|
||||
this.beforeMiniState = state;
|
||||
|
@ -32,7 +32,6 @@
|
||||
return;
|
||||
}
|
||||
setOptions({
|
||||
backgroundColor: '#fff',
|
||||
legend: {
|
||||
bottom: 0,
|
||||
data: ['访问', '购买'],
|
||||
|
@ -32,7 +32,6 @@
|
||||
return;
|
||||
}
|
||||
setOptions({
|
||||
backgroundColor: '#fff',
|
||||
legend: {
|
||||
bottom: 0,
|
||||
data: ['Visits', 'Sales'],
|
||||
|
@ -32,7 +32,6 @@
|
||||
return;
|
||||
}
|
||||
setOptions({
|
||||
backgroundColor: '#fff',
|
||||
legend: {
|
||||
bottom: 0,
|
||||
data: ['Visits', 'Sales'],
|
||||
|
@ -1,58 +0,0 @@
|
||||
<template>
|
||||
<div ref="chartRef" :style="{ width: '100%' }"></div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, Ref, onMounted } from 'vue';
|
||||
|
||||
import { useApexCharts } from '/@/hooks/web/useApexCharts';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
const { setOptions } = useApexCharts(chartRef as Ref<HTMLDivElement>);
|
||||
|
||||
onMounted(() => {
|
||||
setOptions({
|
||||
series: [
|
||||
{
|
||||
name: 'series1',
|
||||
data: [31, 40, 28, 51, 42, 109, 100],
|
||||
},
|
||||
{
|
||||
name: 'series2',
|
||||
data: [11, 32, 45, 32, 34, 52, 41],
|
||||
},
|
||||
],
|
||||
chart: {
|
||||
height: 350,
|
||||
type: 'area',
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
stroke: {
|
||||
curve: 'smooth',
|
||||
},
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
categories: [
|
||||
'2018-09-19T00:00:00.000Z',
|
||||
'2018-09-19T01:30:00.000Z',
|
||||
'2018-09-19T02:30:00.000Z',
|
||||
'2018-09-19T03:30:00.000Z',
|
||||
'2018-09-19T04:30:00.000Z',
|
||||
'2018-09-19T05:30:00.000Z',
|
||||
'2018-09-19T06:30:00.000Z',
|
||||
],
|
||||
},
|
||||
tooltip: {
|
||||
x: {
|
||||
format: 'dd/MM/yy HH:mm',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
return { chartRef };
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,52 +0,0 @@
|
||||
<template>
|
||||
<div ref="chartRef" :style="{ width: '100%' }"></div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, Ref, onMounted } from 'vue';
|
||||
|
||||
import { useApexCharts } from '/@/hooks/web/useApexCharts';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
const { setOptions } = useApexCharts(chartRef as Ref<HTMLDivElement>);
|
||||
|
||||
onMounted(() => {
|
||||
setOptions({
|
||||
series: [
|
||||
{
|
||||
data: [400, 430, 448, 470, 540, 580, 690, 1100, 1200, 1380],
|
||||
},
|
||||
],
|
||||
chart: {
|
||||
type: 'bar',
|
||||
height: 350,
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: true,
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
xaxis: {
|
||||
categories: [
|
||||
'South Korea',
|
||||
'Canada',
|
||||
'United Kingdom',
|
||||
'Netherlands',
|
||||
'Italy',
|
||||
'France',
|
||||
'Japan',
|
||||
'United States',
|
||||
'China',
|
||||
'Germany',
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
return { chartRef };
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,53 +0,0 @@
|
||||
<template>
|
||||
<div ref="chartRef" :style="{ width: '100%' }"></div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, Ref, onMounted } from 'vue';
|
||||
|
||||
import { useApexCharts } from '/@/hooks/web/useApexCharts';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
const { setOptions } = useApexCharts(chartRef as Ref<HTMLDivElement>);
|
||||
|
||||
onMounted(() => {
|
||||
setOptions({
|
||||
series: [
|
||||
{
|
||||
name: 'Desktops',
|
||||
data: [10, 41, 35, 51, 49, 62, 69, 91, 148],
|
||||
},
|
||||
],
|
||||
chart: {
|
||||
height: 350,
|
||||
type: 'line',
|
||||
zoom: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
stroke: {
|
||||
curve: 'straight',
|
||||
},
|
||||
title: {
|
||||
text: 'Product Trends by Month',
|
||||
align: 'left',
|
||||
},
|
||||
grid: {
|
||||
row: {
|
||||
colors: ['#f3f3f3', 'transparent'], // takes an array which will be repeated on columns
|
||||
opacity: 0.5,
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep'],
|
||||
},
|
||||
});
|
||||
});
|
||||
return { chartRef };
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,138 +0,0 @@
|
||||
<template>
|
||||
<div ref="chartRef" :style="{ width: '100%' }"></div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, Ref, onMounted } from 'vue';
|
||||
|
||||
import { useApexCharts } from '/@/hooks/web/useApexCharts';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
const { setOptions } = useApexCharts(chartRef as Ref<HTMLDivElement>);
|
||||
|
||||
onMounted(() => {
|
||||
setOptions({
|
||||
series: [
|
||||
{
|
||||
name: 'Income',
|
||||
type: 'column',
|
||||
data: [1.4, 2, 2.5, 1.5, 2.5, 2.8, 3.8, 4.6],
|
||||
},
|
||||
{
|
||||
name: 'Cashflow',
|
||||
type: 'column',
|
||||
data: [1.1, 3, 3.1, 4, 4.1, 4.9, 6.5, 8.5],
|
||||
},
|
||||
{
|
||||
name: 'Revenue',
|
||||
type: 'line',
|
||||
data: [20, 29, 37, 36, 44, 45, 50, 58],
|
||||
},
|
||||
],
|
||||
chart: {
|
||||
height: 350,
|
||||
type: 'line',
|
||||
stacked: false,
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
stroke: {
|
||||
width: [1, 1, 4],
|
||||
},
|
||||
title: {
|
||||
text: 'XYZ - Stock Analysis (2009 - 2016)',
|
||||
align: 'left',
|
||||
offsetX: 110,
|
||||
},
|
||||
xaxis: {
|
||||
categories: [2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016],
|
||||
},
|
||||
yaxis: [
|
||||
{
|
||||
axisTicks: {
|
||||
show: true,
|
||||
},
|
||||
axisBorder: {
|
||||
show: true,
|
||||
color: '#008FFB',
|
||||
},
|
||||
labels: {
|
||||
style: {
|
||||
colors: '#008FFB',
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: 'Income (thousand crores)',
|
||||
style: {
|
||||
color: '#008FFB',
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
seriesName: 'Income',
|
||||
opposite: true,
|
||||
axisTicks: {
|
||||
show: true,
|
||||
},
|
||||
axisBorder: {
|
||||
show: true,
|
||||
color: '#00E396',
|
||||
},
|
||||
labels: {
|
||||
style: {
|
||||
colors: '#00E396',
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: 'Operating Cashflow (thousand crores)',
|
||||
style: {
|
||||
color: '#00E396',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
seriesName: 'Revenue',
|
||||
opposite: true,
|
||||
axisTicks: {
|
||||
show: true,
|
||||
},
|
||||
axisBorder: {
|
||||
show: true,
|
||||
color: '#FEB019',
|
||||
},
|
||||
labels: {
|
||||
style: {
|
||||
colors: '#FEB019',
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: 'Revenue (thousand crores)',
|
||||
style: {
|
||||
color: '#FEB019',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
tooltip: {
|
||||
fixed: {
|
||||
enabled: true,
|
||||
position: 'topLeft', // topRight, topLeft, bottomRight, bottomLeft
|
||||
offsetY: 30,
|
||||
offsetX: 60,
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
horizontalAlign: 'left',
|
||||
offsetX: 40,
|
||||
},
|
||||
});
|
||||
});
|
||||
return { chartRef };
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,61 +0,0 @@
|
||||
<template>
|
||||
<div ref="chartRef" :style="{ width: '100%' }"></div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, Ref, ref, onMounted } from 'vue';
|
||||
|
||||
import { useApexCharts } from '/@/hooks/web/useApexCharts';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
const { setOptions } = useApexCharts(chartRef as Ref<HTMLDivElement>);
|
||||
onMounted(() => {
|
||||
setOptions({
|
||||
series: [
|
||||
{ name: 'Visits', data: [90, 50, 86, 40, 100, 20] },
|
||||
{ name: 'Sales', data: [70, 75, 70, 76, 20, 85] },
|
||||
],
|
||||
chart: {
|
||||
height: 350,
|
||||
type: 'radar',
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
show: false,
|
||||
},
|
||||
|
||||
title: {
|
||||
show: false,
|
||||
},
|
||||
markers: {
|
||||
// size: 0,
|
||||
},
|
||||
xaxis: {
|
||||
categories: ['2016', '2017', '2018', '2019', '2020', '2021'],
|
||||
},
|
||||
stroke: {
|
||||
width: 0,
|
||||
},
|
||||
colors: ['#9f8ed7', '#1edec5'],
|
||||
|
||||
fill: {
|
||||
type: 'gradient',
|
||||
gradient: {
|
||||
shade: 'dark',
|
||||
gradientToColors: ['#8e9ad6', '#1fcadb'],
|
||||
shadeIntensity: 1,
|
||||
type: 'horizontal',
|
||||
opacityFrom: 1,
|
||||
opacityTo: 1,
|
||||
stops: [0, 100, 100, 100],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
return { chartRef };
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,46 +0,0 @@
|
||||
<template>
|
||||
<div class="apex-demo p-4">
|
||||
<div class="demo-box">
|
||||
<Line />
|
||||
</div>
|
||||
<div class="demo-box">
|
||||
<Bar />
|
||||
</div>
|
||||
<div class="demo-box">
|
||||
<Area />
|
||||
</div>
|
||||
<div class="demo-box">
|
||||
<Mixed />
|
||||
</div>
|
||||
<div class="demo-box">
|
||||
<SaleRadar />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import Line from './Line.vue';
|
||||
import Bar from './Bar.vue';
|
||||
import Area from './Area.vue';
|
||||
import Mixed from './Mixed.vue';
|
||||
import SaleRadar from './SaleRadar.vue';
|
||||
export default defineComponent({
|
||||
components: { Line, Bar, Area, Mixed, SaleRadar },
|
||||
setup() {},
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.apex-demo {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
|
||||
.demo-box {
|
||||
width: 49%;
|
||||
margin-bottom: 20px;
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -29,7 +29,7 @@
|
||||
height: 2000px;
|
||||
margin: 20px auto;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
@ -33,7 +33,7 @@
|
||||
height: 2000px;
|
||||
margin: 20px auto;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
@ -54,6 +54,6 @@
|
||||
.scroll-wrap {
|
||||
width: 50%;
|
||||
height: 300px;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
}
|
||||
</style>
|
||||
|
@ -50,7 +50,7 @@
|
||||
&-wrap {
|
||||
display: flex;
|
||||
margin: 0 30%;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@
|
||||
height: 40px;
|
||||
padding: 0 20px;
|
||||
line-height: 40px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
border-bottom: 1px solid @border-color-base;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -26,6 +26,6 @@
|
||||
.scroll-wrap {
|
||||
width: 50%;
|
||||
height: 300px;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
}
|
||||
</style>
|
||||
|
@ -26,7 +26,7 @@
|
||||
<style lang="less" scoped>
|
||||
.demo-wrap {
|
||||
width: 50%;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
border-radius: 10px;
|
||||
}
|
||||
</style>
|
||||
|
@ -3,17 +3,18 @@
|
||||
Current Param : {{ params }}
|
||||
<br />
|
||||
Keep Alive
|
||||
<input />
|
||||
<Input />
|
||||
</PageWrapper>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, unref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { PageWrapper } from '/@/components/Page';
|
||||
import { Input } from 'ant-design-vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TestTab',
|
||||
components: { PageWrapper },
|
||||
components: { PageWrapper, Input },
|
||||
setup() {
|
||||
const { currentRoute } = useRouter();
|
||||
return {
|
||||
|
@ -2,10 +2,11 @@
|
||||
<div class="p-5">
|
||||
多层级缓存-页面1-1-1
|
||||
<br />
|
||||
<input />
|
||||
<Input />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
export default defineComponent({ name: 'Menu111Demo' });
|
||||
import { Input } from 'ant-design-vue';
|
||||
export default defineComponent({ name: 'Menu111Demo', components: { Input } });
|
||||
</script>
|
||||
|
@ -2,10 +2,11 @@
|
||||
<div class="p-5">
|
||||
多层级缓存-页面1-2
|
||||
<br />
|
||||
<input />
|
||||
<Input />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
export default defineComponent({ name: 'Menu12Demo' });
|
||||
import { Input } from 'ant-design-vue';
|
||||
export default defineComponent({ name: 'Menu12Demo', components: { Input } });
|
||||
</script>
|
||||
|
@ -2,12 +2,14 @@
|
||||
<div class="p-5">
|
||||
多层级缓存-页面2
|
||||
<br />
|
||||
<input />
|
||||
<Input />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { Input } from 'ant-design-vue';
|
||||
export default defineComponent({
|
||||
name: 'Menu2Demo',
|
||||
components: { Input },
|
||||
});
|
||||
</script>
|
||||
|
@ -64,7 +64,6 @@
|
||||
margin-bottom: 5px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
|
||||
.icon {
|
||||
margin-top: -5px;
|
||||
@ -75,19 +74,18 @@
|
||||
&-num {
|
||||
margin-left: 24px;
|
||||
line-height: 36px;
|
||||
color: #7d7a7a;
|
||||
color: @text-color-secondary;
|
||||
|
||||
span {
|
||||
margin-left: 5px;
|
||||
font-size: 18px;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
&-download {
|
||||
float: right;
|
||||
font-size: 20px !important;
|
||||
color: #1890ff;
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +102,7 @@
|
||||
&-top {
|
||||
padding: 10px;
|
||||
margin: 16px 16px 12px 16px;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
border-radius: 3px;
|
||||
|
||||
&__avatar {
|
||||
@ -144,7 +144,7 @@
|
||||
&-bottom {
|
||||
padding: 10px;
|
||||
margin: 0 16px 16px 16px;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
@ -48,14 +48,14 @@
|
||||
<style lang="less">
|
||||
.account-setting {
|
||||
margin: 12px;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
|
||||
.base-title {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.ant-tabs-tab-active {
|
||||
background-color: #e6f7ff;
|
||||
background-color: @item-active-bg;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -93,6 +93,6 @@
|
||||
<style lang="less" scoped>
|
||||
.desc-wrap {
|
||||
padding: 16px;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
}
|
||||
</style>
|
||||
|
@ -62,6 +62,6 @@
|
||||
<style lang="less" scoped>
|
||||
.form-wrap {
|
||||
padding: 24px;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
}
|
||||
</style>
|
||||
|
@ -78,18 +78,18 @@
|
||||
margin: 0 0 12px;
|
||||
font-size: 16px;
|
||||
line-height: 32px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
color: @text-color;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin: 0 0 4px;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
color: @text-color;
|
||||
}
|
||||
|
||||
p {
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
color: @text-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,6 @@
|
||||
.desc-wrap {
|
||||
padding: 24px 40px;
|
||||
margin-top: 24px;
|
||||
background: #fafafa;
|
||||
background: @background-color-light;
|
||||
}
|
||||
</style>
|
||||
|
@ -85,7 +85,7 @@
|
||||
<style lang="less" scoped>
|
||||
.step-form-content {
|
||||
padding: 24px;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
}
|
||||
|
||||
.step-form-form {
|
||||
|
@ -86,25 +86,25 @@
|
||||
&__top {
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
|
||||
&-col {
|
||||
&:not(:last-child) {
|
||||
border-right: 1px dashed rgba(206, 206, 206, 0.4);
|
||||
border-right: 1px dashed @border-color-base;
|
||||
}
|
||||
|
||||
div {
|
||||
margin-bottom: 12px;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
color: @text-color;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
color: @text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -112,7 +112,7 @@
|
||||
&__content {
|
||||
padding: 24px;
|
||||
margin-top: 12px;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
|
||||
.list {
|
||||
position: relative;
|
||||
@ -127,7 +127,7 @@
|
||||
top: 20px;
|
||||
right: 15px;
|
||||
font-weight: normal;
|
||||
color: #1890ff;
|
||||
color: @primary-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@
|
||||
margin-bottom: 5px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
color: @text-color;
|
||||
|
||||
.icon {
|
||||
margin-top: -5px;
|
||||
@ -97,7 +97,7 @@
|
||||
padding-top: 10px;
|
||||
padding-left: 30px;
|
||||
font-size: 14px;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
color: @text-color-secondary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +91,7 @@
|
||||
|
||||
&__container {
|
||||
padding: 12px;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
}
|
||||
|
||||
&__title {
|
||||
@ -100,7 +100,7 @@
|
||||
}
|
||||
|
||||
&__content {
|
||||
color: rgba(0, 0, 0, 0.65);
|
||||
color: @text-color-secondary;
|
||||
}
|
||||
|
||||
&__action {
|
||||
@ -109,7 +109,7 @@
|
||||
&-item {
|
||||
display: inline-block;
|
||||
padding: 0 16px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
color: @text-color-secondary;
|
||||
|
||||
&:nth-child(1) {
|
||||
padding-left: 0;
|
||||
@ -117,7 +117,7 @@
|
||||
|
||||
&:nth-child(1),
|
||||
&:nth-child(2) {
|
||||
border-right: 1px solid rgba(206, 206, 206, 0.4);
|
||||
border-right: 1px solid @border-color-base;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,17 +34,16 @@
|
||||
<style lang="less" scoped>
|
||||
.result-error {
|
||||
padding: 48px 32px;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
|
||||
&__content {
|
||||
padding: 24px 40px;
|
||||
background: #fafafa;
|
||||
background: @background-color-light;
|
||||
|
||||
&-title {
|
||||
margin-bottom: 16px;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
|
||||
&-icon {
|
||||
|
@ -48,11 +48,11 @@
|
||||
<style lang="less" scoped>
|
||||
.result-success {
|
||||
padding: 48px 32px;
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
|
||||
&__content {
|
||||
padding: 24px 40px;
|
||||
background: #fafafa;
|
||||
background: @background-color-light;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -83,6 +83,6 @@
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.demo {
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
}
|
||||
</style>
|
||||
|
@ -38,6 +38,6 @@
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.demo {
|
||||
background: #fff;
|
||||
background: @component-background;
|
||||
}
|
||||
</style>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user