@@ -211,7 +217,7 @@ const clear = () => {
{{ t('setting.theme') }}
-
+
{{ t('setting.layout') }}
diff --git a/src/components/ThemeSwitch/src/ThemeSwitch.vue b/src/components/ThemeSwitch/src/ThemeSwitch.vue
index 71d8052..acf6c31 100644
--- a/src/components/ThemeSwitch/src/ThemeSwitch.vue
+++ b/src/components/ThemeSwitch/src/ThemeSwitch.vue
@@ -7,6 +7,8 @@ import { useDesign } from '@/hooks/web/useDesign'
const { getPrefixCls } = useDesign()
+const emit = defineEmits(['change'])
+
const prefixCls = getPrefixCls('theme-switch')
const Sun = useIcon({ icon: 'emojione-monotone:sun', color: '#fde047' })
@@ -23,6 +25,7 @@ const blackColor = 'var(--el-color-black)'
const themeChange = (val: boolean) => {
appStore.setIsDark(val)
+ emit('change', val)
}
diff --git a/src/hooks/web/usePageLoading.ts b/src/hooks/web/usePageLoading.ts
index bb89457..2f0e393 100644
--- a/src/hooks/web/usePageLoading.ts
+++ b/src/hooks/web/usePageLoading.ts
@@ -1,13 +1,13 @@
import { useAppStoreWithOut } from '@/store/modules/app'
-const appStore = useAppStoreWithOut()
-
export const usePageLoading = () => {
const loadStart = () => {
+ const appStore = useAppStoreWithOut()
appStore.setPageLoading(true)
}
const loadDone = () => {
+ const appStore = useAppStoreWithOut()
appStore.setPageLoading(false)
}
diff --git a/src/hooks/web/useTitle.ts b/src/hooks/web/useTitle.ts
index d0eb188..546267d 100644
--- a/src/hooks/web/useTitle.ts
+++ b/src/hooks/web/useTitle.ts
@@ -3,10 +3,10 @@ import { isString } from '@/utils/is'
import { useAppStoreWithOut } from '@/store/modules/app'
import { useI18n } from '@/hooks/web/useI18n'
-const appStore = useAppStoreWithOut()
-
export const useTitle = (newTitle?: string) => {
const { t } = useI18n()
+ const appStore = useAppStoreWithOut()
+
const title = ref(
newTitle ? `${appStore.getTitle} - ${t(newTitle as string)}` : appStore.getTitle
)
diff --git a/src/layout/components/ToolHeader.vue b/src/layout/components/ToolHeader.vue
index 10d00e6..18bf00a 100644
--- a/src/layout/components/ToolHeader.vue
+++ b/src/layout/components/ToolHeader.vue
@@ -41,8 +41,7 @@ export default defineComponent({
id={`${variables.namespace}-tool-header`}
class={[
prefixCls,
- 'h-[var(--top-tool-height)] relative px-[var(--top-tool-p-x)] flex items-center justify-between',
- 'dark:bg-[var(--el-bg-color)]'
+ 'h-[var(--top-tool-height)] relative px-[var(--top-tool-p-x)] flex items-center justify-between'
]}
>
{layout.value !== 'top' ? (
diff --git a/src/plugins/elementPlus/index.ts b/src/plugins/elementPlus/index.ts
index a5362a1..29a7840 100644
--- a/src/plugins/elementPlus/index.ts
+++ b/src/plugins/elementPlus/index.ts
@@ -12,7 +12,13 @@ export const setupElementPlus = (app: App) => {
app.use(plugin)
})
+ // 为了开发环境启动更快,一次性引入所有样式
+ if (import.meta.env.VITE_USE_ALL_ELEMENT_PLUS_STYLE === 'true') {
+ import('element-plus/dist/index.css')
+ return
+ }
+
components.forEach((component) => {
- app.component(component.name, component)
+ app.component(component.name!, component)
})
}
diff --git a/src/plugins/svgIcon/index.ts b/src/plugins/svgIcon/index.ts
index b5b7f70..1b11e2d 100644
--- a/src/plugins/svgIcon/index.ts
+++ b/src/plugins/svgIcon/index.ts
@@ -1,3 +1 @@
import 'virtual:svg-icons-register'
-
-import '@purge-icons/generated'
diff --git a/src/store/modules/app.ts b/src/store/modules/app.ts
index c5459ea..4126dcf 100644
--- a/src/store/modules/app.ts
+++ b/src/store/modules/app.ts
@@ -1,6 +1,7 @@
import { defineStore } from 'pinia'
import { store } from '../index'
import { setCssVar, humpToUnderline } from '@/utils'
+import { mix } from '@/utils/color'
import { ElMessage, ComponentSize } from 'element-plus'
interface AppState {
@@ -239,6 +240,7 @@ export const useAppStore = defineStore('app', {
document.documentElement.classList.add('light')
document.documentElement.classList.remove('dark')
}
+ this.setPrimaryLight()
},
setCurrentSize(currentSize: ComponentSize) {
this.currentSize = currentSize
@@ -253,9 +255,21 @@ export const useAppStore = defineStore('app', {
for (const key in this.theme) {
setCssVar(`--${humpToUnderline(key)}`, this.theme[key])
}
+ this.setPrimaryLight()
},
setFooter(footer: boolean) {
this.footer = footer
+ },
+ setPrimaryLight() {
+ if (this.theme.elColorPrimary) {
+ const elColorPrimary = this.theme.elColorPrimary
+ const color = this.isDark ? '#000000' : '#ffffff'
+ const lightList = [3, 5, 7, 8, 9]
+ lightList.forEach((v) => {
+ setCssVar(`--el-color-primary-light-${v}`, mix(color, elColorPrimary, v / 10))
+ })
+ setCssVar(`--el-color-primary-dark-2`, mix(color, elColorPrimary, 0.2))
+ }
}
},
persist: true
diff --git a/src/utils/color.ts b/src/utils/color.ts
index 6888583..387ffc6 100644
--- a/src/utils/color.ts
+++ b/src/utils/color.ts
@@ -151,3 +151,22 @@ const subtractLight = (color: string, amount: number) => {
const c = cc < 0 ? 0 : cc
return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}`
}
+
+/**
+ * Mixes two colors.
+ *
+ * @param {string} color1 - The first color, should be a 6-digit hexadecimal color code starting with `#`.
+ * @param {string} color2 - The second color, should be a 6-digit hexadecimal color code starting with `#`.
+ * @param {number} [weight=0.5] - The weight of color1 in the mix, should be a number between 0 and 1, where 0 represents 100% of color2, and 1 represents 100% of color1.
+ * @returns {string} The mixed color, a 6-digit hexadecimal color code starting with `#`.
+ */
+export const mix = (color1: string, color2: string, weight: number = 0.5): string => {
+ let color = '#'
+ for (let i = 0; i <= 2; i++) {
+ const c1 = parseInt(color1.substring(1 + i * 2, 3 + i * 2), 16)
+ const c2 = parseInt(color2.substring(1 + i * 2, 3 + i * 2), 16)
+ const c = Math.round(c1 * weight + c2 * (1 - weight))
+ color += c.toString(16).padStart(2, '0')
+ }
+ return color
+}
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 77aca12..c161637 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -47,6 +47,10 @@ export const setCssVar = (prop: string, val: any, dom = document.documentElement
dom.style.setProperty(prop, val)
}
+export const getCssVar = (prop: string, dom = document.documentElement) => {
+ return getComputedStyle(dom).getPropertyValue(prop)
+}
+
/**
* 查找数组对象的某个下标
* @param {Array} ary 查找的数组
diff --git a/uno.config.ts b/uno.config.ts
index 0645fe6..5119e5b 100644
--- a/uno.config.ts
+++ b/uno.config.ts
@@ -1,9 +1,33 @@
-import { defineConfig, toEscapedSelector as e, presetUno } from 'unocss'
+import { defineConfig, toEscapedSelector as e, presetUno, presetIcons } from 'unocss'
import transformerVariantGroup from '@unocss/transformer-variant-group'
+const createPresetIcons = () => {
+ // @ts-ignore
+ if (import.meta.env.VITE_USE_ONLINE_ICON === 'true') {
+ return [
+ presetIcons({
+ prefix: ''
+ })
+ ]
+ } else {
+ return []
+ }
+}
+
export default defineConfig({
// ...UnoCSS options
rules: [
+ [
+ /^overflow-ellipsis$/,
+ ([], { rawSelector }) => {
+ const selector = e(rawSelector)
+ return `
+${selector} {
+ text-overflow: ellipsis;
+}
+`
+ }
+ ],
[
/^custom-hover$/,
([], { rawSelector }) => {
@@ -100,6 +124,11 @@ ${selector}:after {
}
]
],
- presets: [presetUno({ dark: 'class', attributify: false })],
- transformers: [transformerVariantGroup()]
+ presets: [presetUno({ dark: 'class', attributify: false }), ...createPresetIcons()],
+ transformers: [transformerVariantGroup()],
+ content: {
+ pipeline: {
+ include: [/\.(vue|svelte|[jt]sx|mdx?|astro|elm|php|phtml|html|ts)($|\?)/]
+ }
+ }
})
diff --git a/vite.config.ts b/vite.config.ts
index 621448a..490930a 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -12,6 +12,7 @@ import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite"
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import { createStyleImportPlugin, ElementPlusResolve } from 'vite-plugin-style-import'
import UnoCSS from 'unocss/vite'
+import { visualizer } from 'rollup-plugin-visualizer'
// https://vitejs.dev/config/
const root = process.cwd()
@@ -39,19 +40,23 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
}),
VueJsx(),
progress(),
- createStyleImportPlugin({
- resolves: [ElementPlusResolve()],
- libs: [{
- libraryName: 'element-plus',
- esModule: true,
- resolveStyle: (name) => {
- if (name === 'click-outside') {
- return ''
- }
- return `element-plus/es/components/${name.replace(/^el-/, '')}/style/css`
- }
- }]
- }),
+ env.VITE_USE_ALL_ELEMENT_PLUS_STYLE === 'false'
+ ? createStyleImportPlugin({
+ resolves: [ElementPlusResolve()],
+ libs: [
+ {
+ libraryName: 'element-plus',
+ esModule: true,
+ resolveStyle: (name) => {
+ if (name === 'click-outside') {
+ return ''
+ }
+ return `element-plus/es/components/${name.replace(/^el-/, '')}/style/css`
+ }
+ }
+ ]
+ })
+ : undefined,
EslintPlugin({
cache: false,
include: ['src/**/*.vue', 'src/**/*.ts', 'src/**/*.tsx'] // 检查的文件
@@ -67,17 +72,19 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
svgoOptions: true
}),
PurgeIcons(),
- viteMockServe({
- ignore: /^\_/,
- mockPath: 'mock',
- localEnabled: !isBuild,
- prodEnabled: isBuild,
- injectCode: `
+ env.VITE_USE_MOCK === 'true'
+ ? viteMockServe({
+ ignore: /^\_/,
+ mockPath: 'mock',
+ localEnabled: !isBuild,
+ prodEnabled: isBuild,
+ injectCode: `
import { setupProdMockServer } from '../mock/_createProductionServer'
setupProdMockServer()
`
- }),
+ })
+ : undefined,
ViteEjsPlugin({
title: env.VITE_APP_TITLE
}),
@@ -106,17 +113,27 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
}
]
},
+ esbuild: {
+ pure: env.VITE_DROP_CONSOLE === 'true' ? ['console.log'] : undefined,
+ drop: env.VITE_DROP_DEBUGGER === 'true' ? ['debugger'] : undefined
+ },
build: {
- minify: 'terser',
+ target: 'es2015',
outDir: env.VITE_OUT_DIR || 'dist',
- sourcemap: env.VITE_SOURCEMAP === 'true' ? 'inline' : false,
+ sourcemap: env.VITE_SOURCEMAP === 'true',
// brotliSize: false,
- terserOptions: {
- compress: {
- drop_debugger: env.VITE_DROP_DEBUGGER === 'true',
- drop_console: env.VITE_DROP_CONSOLE === 'true'
+ rollupOptions: {
+ plugins: env.VITE_USE_BUNDLE_ANALYZER === 'true' ? [visualizer()] : undefined,
+ // 拆包
+ output: {
+ manualChunks: {
+ 'vue-chunks': ['vue', 'vue-router', 'pinia', 'vue-i18n'],
+ 'element-plus': ['element-plus'],
+ 'wang-editor': ['@wangeditor/editor', '@wangeditor/editor-for-vue']
+ }
}
- }
+ },
+ cssCodeSplit: !(env.VITE_USE_CSS_SPLIT === 'false')
},
server: {
port: 4000,