feat: 同步master代码

This commit is contained in:
kailong321200875 2024-01-03 10:39:42 +08:00
parent 607b73a7b3
commit 6a83d08309
20 changed files with 240 additions and 63 deletions

View File

@ -1,5 +1,5 @@
# 环境 # 环境
NODE_ENV=development VITE_NODE_ENV=development
# 接口前缀 # 接口前缀
VITE_API_BASE_PATH= VITE_API_BASE_PATH=
@ -9,3 +9,12 @@ VITE_BASE_PATH=/
# 标题 # 标题
VITE_APP_TITLE=ElementAdmin VITE_APP_TITLE=ElementAdmin
# 是否全量引入element-plus样式
VITE_USE_ALL_ELEMENT_PLUS_STYLE=true
# 是否开启mock
VITE_USE_MOCK=true
# 是否使用在线图标
VITE_USE_ONLINE_ICON=true

View File

@ -1,5 +1,5 @@
# 环境 # 环境
NODE_ENV=production VITE_NODE_ENV=production
# 接口前缀 # 接口前缀
VITE_API_BASE_PATH= VITE_API_BASE_PATH=
@ -21,3 +21,18 @@ VITE_OUT_DIR=dist-dev
# 标题 # 标题
VITE_APP_TITLE=ElementAdmin VITE_APP_TITLE=ElementAdmin
# 是否包分析
VITE_USE_BUNDLE_ANALYZER=false
# 是否全量引入element-plus样式
VITE_USE_ALL_ELEMENT_PLUS_STYLE=false
# 是否开启mock
VITE_USE_MOCK=true
# 是否切割css
VITE_USE_CSS_SPLIT=true
# 是否使用在线图标
VITE_USE_ONLINE_ICON=true

View File

@ -1,5 +1,5 @@
# 环境 # 环境
NODE_ENV=production VITE_NODE_ENV=production
# 接口前缀 # 接口前缀
VITE_API_BASE_PATH= VITE_API_BASE_PATH=
@ -21,3 +21,18 @@ VITE_OUT_DIR=dist-pro
# 标题 # 标题
VITE_APP_TITLE=ElementAdmin VITE_APP_TITLE=ElementAdmin
# 是否包分析
VITE_USE_BUNDLE_ANALYZER=false
# 是否全量引入element-plus样式
VITE_USE_ALL_ELEMENT_PLUS_STYLE=false
# 是否开启mock
VITE_USE_MOCK=true
# 是否切割css
VITE_USE_CSS_SPLIT=true
# 是否使用在线图标
VITE_USE_ONLINE_ICON=true

View File

@ -1,5 +1,5 @@
# 环境 # 环境
NODE_ENV=production VITE_NODE_ENV=production
# 接口前缀 # 接口前缀
VITE_API_BASE_PATH= VITE_API_BASE_PATH=
@ -21,3 +21,18 @@ VITE_OUT_DIR=dist-pro
# 标题 # 标题
VITE_APP_TITLE=ElementAdmin VITE_APP_TITLE=ElementAdmin
# 是否包分析
VITE_USE_BUNDLE_ANALYZER=true
# 是否全量引入element-plus样式
VITE_USE_ALL_ELEMENT_PLUS_STYLE=false
# 是否开启mock
VITE_USE_MOCK=true
# 是否切割css
VITE_USE_CSS_SPLIT=true
# 是否使用在线图标
VITE_USE_ONLINE_ICON=true

View File

@ -1,8 +1,8 @@
# 环境 # 环境
NODE_ENV=production VITE_NODE_ENV=production
# 接口前缀 # 接口前缀
VITE_API_BASE_PATH=test VITE_API_BASE_PATH=
# 打包路径 # 打包路径
VITE_BASE_PATH=/dist-test/ VITE_BASE_PATH=/dist-test/
@ -21,3 +21,15 @@ VITE_OUT_DIR=dist-test
# 标题 # 标题
VITE_APP_TITLE=ElementAdmin VITE_APP_TITLE=ElementAdmin
# 是否包分析
VITE_USE_BUNDLE_ANALYZER=false
# 是否全量引入element-plus样式
VITE_USE_ALL_ELEMENT_PLUS_STYLE=false
# 是否开启mock
VITE_USE_MOCK=true
# 是否切割css
VITE_USE_CSS_SPLIT=false

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ dist-ssr
/dist* /dist*
*-lock.* *-lock.*
pnpm-debug pnpm-debug
stats.html

View File

@ -61,7 +61,6 @@
"@commitlint/config-conventional": "^18.4.3", "@commitlint/config-conventional": "^18.4.3",
"@iconify/json": "^2.2.153", "@iconify/json": "^2.2.153",
"@intlify/unplugin-vue-i18n": "^1.5.0", "@intlify/unplugin-vue-i18n": "^1.5.0",
"@purge-icons/generated": "^0.10.0",
"@types/fs-extra": "^11.0.4", "@types/fs-extra": "^11.0.4",
"@types/inquirer": "^9.0.7", "@types/inquirer": "^9.0.7",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
@ -97,6 +96,7 @@
"prettier": "^3.1.0", "prettier": "^3.1.0",
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"rollup": "^4.6.1", "rollup": "^4.6.1",
"rollup-plugin-visualizer": "^5.12.0",
"stylelint": "^15.11.0", "stylelint": "^15.11.0",
"stylelint-config-html": "^1.1.0", "stylelint-config-html": "^1.1.0",
"stylelint-config-recommended": "^13.0.0", "stylelint-config-recommended": "^13.0.0",

View File

@ -25,6 +25,11 @@ const symbolId = computed(() => {
return unref(isLocal) ? `#icon-${props.icon.split('svg-icon:')[1]}` : props.icon return unref(isLocal) ? `#icon-${props.icon.split('svg-icon:')[1]}` : props.icon
}) })
// 使线
const isUseOnline = computed(() => {
return import.meta.env.VITE_USE_ONLINE_ICON === 'true'
})
const getIconifyStyle = computed(() => { const getIconifyStyle = computed(() => {
const { color, size } = props const { color, size } = props
return { return {
@ -40,7 +45,10 @@ const getIconifyStyle = computed(() => {
<use :xlink:href="symbolId" /> <use :xlink:href="symbolId" />
</svg> </svg>
<Icon v-else :icon="icon" :style="getIconifyStyle" /> <template v-else>
<Icon v-if="isUseOnline" :icon="icon" :style="getIconifyStyle" />
<div v-else :class="`${icon} iconify`" :style="getIconifyStyle"></div>
</template>
</ElIcon> </ElIcon>
</template> </template>
@ -49,11 +57,18 @@ const getIconifyStyle = computed(() => {
.@{prefix-cls}, .@{prefix-cls},
.iconify { .iconify {
&:hover {
:deep(svg) { :deep(svg) {
&:hover {
// stylelint-disable-next-line // stylelint-disable-next-line
color: v-bind(hoverColor) !important; color: v-bind(hoverColor) !important;
} }
} }
} }
.iconify {
&:hover {
// stylelint-disable-next-line
color: v-bind(hoverColor) !important;
}
}
</style> </style>

View File

@ -1,12 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { ElDrawer, ElDivider, ElMessage } from 'element-plus' import { ElDrawer, ElDivider, ElMessage } from 'element-plus'
import { ref, unref, computed, watch } from 'vue' import { ref, unref, computed } from 'vue'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { ThemeSwitch } from '@/components/ThemeSwitch' import { ThemeSwitch } from '@/components/ThemeSwitch'
import { colorIsDark, lighten, hexToRGB } from '@/utils/color' import { colorIsDark, lighten, hexToRGB } from '@/utils/color'
import { useCssVar } from '@vueuse/core' import { useCssVar } from '@vueuse/core'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { trim, setCssVar } from '@/utils' import { trim, setCssVar, getCssVar } from '@/utils'
import ColorRadioPicker from './components/ColorRadioPicker.vue' import ColorRadioPicker from './components/ColorRadioPicker.vue'
import InterfaceDisplay from './components/InterfaceDisplay.vue' import InterfaceDisplay from './components/InterfaceDisplay.vue'
import LayoutRadioPicker from './components/LayoutRadioPicker.vue' import LayoutRadioPicker from './components/LayoutRadioPicker.vue'
@ -95,17 +95,17 @@ const setMenuTheme = (color: string) => {
} }
// layout // layout
watch( // watch(
() => layout.value, // () => layout.value,
(n) => { // (n) => {
if (n === 'top' && !appStore.getIsDark) { // if (n === 'top' && !appStore.getIsDark) {
headerTheme.value = '#fff' // headerTheme.value = '#fff'
setHeaderTheme('#fff') // setHeaderTheme('#fff')
} else { // } else {
setMenuTheme(unref(menuTheme)) // setMenuTheme(unref(menuTheme))
} // }
} // }
) // )
// //
const copyConfig = async () => { const copyConfig = async () => {
@ -192,6 +192,12 @@ const clear = () => {
storageClear() storageClear()
window.location.reload() window.location.reload()
} }
const themeChange = () => {
const color = getCssVar('--el-bg-color')
setMenuTheme(color)
setHeaderTheme(color)
}
</script> </script>
<template> <template>
@ -211,7 +217,7 @@ const clear = () => {
<div class="text-center"> <div class="text-center">
<!-- 主题 --> <!-- 主题 -->
<ElDivider>{{ t('setting.theme') }}</ElDivider> <ElDivider>{{ t('setting.theme') }}</ElDivider>
<ThemeSwitch /> <ThemeSwitch @change="themeChange" />
<!-- 布局 --> <!-- 布局 -->
<ElDivider>{{ t('setting.layout') }}</ElDivider> <ElDivider>{{ t('setting.layout') }}</ElDivider>

View File

@ -7,6 +7,8 @@ import { useDesign } from '@/hooks/web/useDesign'
const { getPrefixCls } = useDesign() const { getPrefixCls } = useDesign()
const emit = defineEmits(['change'])
const prefixCls = getPrefixCls('theme-switch') const prefixCls = getPrefixCls('theme-switch')
const Sun = useIcon({ icon: 'emojione-monotone:sun', color: '#fde047' }) const Sun = useIcon({ icon: 'emojione-monotone:sun', color: '#fde047' })
@ -23,6 +25,7 @@ const blackColor = 'var(--el-color-black)'
const themeChange = (val: boolean) => { const themeChange = (val: boolean) => {
appStore.setIsDark(val) appStore.setIsDark(val)
emit('change', val)
} }
</script> </script>

View File

@ -1,13 +1,13 @@
import { useAppStoreWithOut } from '@/store/modules/app' import { useAppStoreWithOut } from '@/store/modules/app'
const appStore = useAppStoreWithOut()
export const usePageLoading = () => { export const usePageLoading = () => {
const loadStart = () => { const loadStart = () => {
const appStore = useAppStoreWithOut()
appStore.setPageLoading(true) appStore.setPageLoading(true)
} }
const loadDone = () => { const loadDone = () => {
const appStore = useAppStoreWithOut()
appStore.setPageLoading(false) appStore.setPageLoading(false)
} }

View File

@ -3,10 +3,10 @@ import { isString } from '@/utils/is'
import { useAppStoreWithOut } from '@/store/modules/app' import { useAppStoreWithOut } from '@/store/modules/app'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
const appStore = useAppStoreWithOut()
export const useTitle = (newTitle?: string) => { export const useTitle = (newTitle?: string) => {
const { t } = useI18n() const { t } = useI18n()
const appStore = useAppStoreWithOut()
const title = ref( const title = ref(
newTitle ? `${appStore.getTitle} - ${t(newTitle as string)}` : appStore.getTitle newTitle ? `${appStore.getTitle} - ${t(newTitle as string)}` : appStore.getTitle
) )

View File

@ -41,8 +41,7 @@ export default defineComponent({
id={`${variables.namespace}-tool-header`} id={`${variables.namespace}-tool-header`}
class={[ class={[
prefixCls, prefixCls,
'h-[var(--top-tool-height)] relative px-[var(--top-tool-p-x)] flex items-center justify-between', 'h-[var(--top-tool-height)] relative px-[var(--top-tool-p-x)] flex items-center justify-between'
'dark:bg-[var(--el-bg-color)]'
]} ]}
> >
{layout.value !== 'top' ? ( {layout.value !== 'top' ? (

View File

@ -12,7 +12,13 @@ export const setupElementPlus = (app: App<Element>) => {
app.use(plugin) app.use(plugin)
}) })
// 为了开发环境启动更快,一次性引入所有样式
if (import.meta.env.VITE_USE_ALL_ELEMENT_PLUS_STYLE === 'true') {
import('element-plus/dist/index.css')
return
}
components.forEach((component) => { components.forEach((component) => {
app.component(component.name, component) app.component(component.name!, component)
}) })
} }

View File

@ -1,3 +1 @@
import 'virtual:svg-icons-register' import 'virtual:svg-icons-register'
import '@purge-icons/generated'

View File

@ -1,6 +1,7 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { store } from '../index' import { store } from '../index'
import { setCssVar, humpToUnderline } from '@/utils' import { setCssVar, humpToUnderline } from '@/utils'
import { mix } from '@/utils/color'
import { ElMessage, ComponentSize } from 'element-plus' import { ElMessage, ComponentSize } from 'element-plus'
interface AppState { interface AppState {
@ -239,6 +240,7 @@ export const useAppStore = defineStore('app', {
document.documentElement.classList.add('light') document.documentElement.classList.add('light')
document.documentElement.classList.remove('dark') document.documentElement.classList.remove('dark')
} }
this.setPrimaryLight()
}, },
setCurrentSize(currentSize: ComponentSize) { setCurrentSize(currentSize: ComponentSize) {
this.currentSize = currentSize this.currentSize = currentSize
@ -253,9 +255,21 @@ export const useAppStore = defineStore('app', {
for (const key in this.theme) { for (const key in this.theme) {
setCssVar(`--${humpToUnderline(key)}`, this.theme[key]) setCssVar(`--${humpToUnderline(key)}`, this.theme[key])
} }
this.setPrimaryLight()
}, },
setFooter(footer: boolean) { setFooter(footer: boolean) {
this.footer = footer 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 persist: true

View File

@ -151,3 +151,22 @@ const subtractLight = (color: string, amount: number) => {
const c = cc < 0 ? 0 : cc const c = cc < 0 ? 0 : cc
return c.toString(16).length > 1 ? c.toString(16) : `0${c.toString(16)}` 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
}

View File

@ -47,6 +47,10 @@ export const setCssVar = (prop: string, val: any, dom = document.documentElement
dom.style.setProperty(prop, val) dom.style.setProperty(prop, val)
} }
export const getCssVar = (prop: string, dom = document.documentElement) => {
return getComputedStyle(dom).getPropertyValue(prop)
}
/** /**
* *
* @param {Array} ary * @param {Array} ary

View File

@ -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' 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({ export default defineConfig({
// ...UnoCSS options // ...UnoCSS options
rules: [ rules: [
[
/^overflow-ellipsis$/,
([], { rawSelector }) => {
const selector = e(rawSelector)
return `
${selector} {
text-overflow: ellipsis;
}
`
}
],
[ [
/^custom-hover$/, /^custom-hover$/,
([], { rawSelector }) => { ([], { rawSelector }) => {
@ -100,6 +124,11 @@ ${selector}:after {
} }
] ]
], ],
presets: [presetUno({ dark: 'class', attributify: false })], presets: [presetUno({ dark: 'class', attributify: false }), ...createPresetIcons()],
transformers: [transformerVariantGroup()] transformers: [transformerVariantGroup()],
content: {
pipeline: {
include: [/\.(vue|svelte|[jt]sx|mdx?|astro|elm|php|phtml|html|ts)($|\?)/]
}
}
}) })

View File

@ -12,6 +12,7 @@ import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite"
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons' import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import { createStyleImportPlugin, ElementPlusResolve } from 'vite-plugin-style-import' import { createStyleImportPlugin, ElementPlusResolve } from 'vite-plugin-style-import'
import UnoCSS from 'unocss/vite' import UnoCSS from 'unocss/vite'
import { visualizer } from 'rollup-plugin-visualizer'
// https://vitejs.dev/config/ // https://vitejs.dev/config/
const root = process.cwd() const root = process.cwd()
@ -39,9 +40,11 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
}), }),
VueJsx(), VueJsx(),
progress(), progress(),
createStyleImportPlugin({ env.VITE_USE_ALL_ELEMENT_PLUS_STYLE === 'false'
? createStyleImportPlugin({
resolves: [ElementPlusResolve()], resolves: [ElementPlusResolve()],
libs: [{ libs: [
{
libraryName: 'element-plus', libraryName: 'element-plus',
esModule: true, esModule: true,
resolveStyle: (name) => { resolveStyle: (name) => {
@ -50,8 +53,10 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
} }
return `element-plus/es/components/${name.replace(/^el-/, '')}/style/css` return `element-plus/es/components/${name.replace(/^el-/, '')}/style/css`
} }
}] }
}), ]
})
: undefined,
EslintPlugin({ EslintPlugin({
cache: false, cache: false,
include: ['src/**/*.vue', 'src/**/*.ts', 'src/**/*.tsx'] // 检查的文件 include: ['src/**/*.vue', 'src/**/*.ts', 'src/**/*.tsx'] // 检查的文件
@ -67,7 +72,8 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
svgoOptions: true svgoOptions: true
}), }),
PurgeIcons(), PurgeIcons(),
viteMockServe({ env.VITE_USE_MOCK === 'true'
? viteMockServe({
ignore: /^\_/, ignore: /^\_/,
mockPath: 'mock', mockPath: 'mock',
localEnabled: !isBuild, localEnabled: !isBuild,
@ -77,7 +83,8 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
setupProdMockServer() setupProdMockServer()
` `
}), })
: undefined,
ViteEjsPlugin({ ViteEjsPlugin({
title: env.VITE_APP_TITLE title: env.VITE_APP_TITLE
}), }),
@ -106,18 +113,28 @@ 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: { build: {
minify: 'terser', target: 'es2015',
outDir: env.VITE_OUT_DIR || 'dist', outDir: env.VITE_OUT_DIR || 'dist',
sourcemap: env.VITE_SOURCEMAP === 'true' ? 'inline' : false, sourcemap: env.VITE_SOURCEMAP === 'true',
// brotliSize: false, // brotliSize: false,
terserOptions: { rollupOptions: {
compress: { plugins: env.VITE_USE_BUNDLE_ANALYZER === 'true' ? [visualizer()] : undefined,
drop_debugger: env.VITE_DROP_DEBUGGER === 'true', // 拆包
drop_console: env.VITE_DROP_CONSOLE === 'true' 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: { server: {
port: 4000, port: 4000,
proxy: { proxy: {