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=
@ -9,3 +9,12 @@ VITE_BASE_PATH=/
# 标题
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=
@ -21,3 +21,18 @@ VITE_OUT_DIR=dist-dev
# 标题
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=
@ -21,3 +21,18 @@ VITE_OUT_DIR=dist-pro
# 标题
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=
@ -21,3 +21,18 @@ VITE_OUT_DIR=dist-pro
# 标题
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/
@ -21,3 +21,15 @@ VITE_OUT_DIR=dist-test
# 标题
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*
*-lock.*
pnpm-debug
stats.html

View File

@ -61,7 +61,6 @@
"@commitlint/config-conventional": "^18.4.3",
"@iconify/json": "^2.2.153",
"@intlify/unplugin-vue-i18n": "^1.5.0",
"@purge-icons/generated": "^0.10.0",
"@types/fs-extra": "^11.0.4",
"@types/inquirer": "^9.0.7",
"@types/lodash-es": "^4.17.12",
@ -97,6 +96,7 @@
"prettier": "^3.1.0",
"rimraf": "^5.0.5",
"rollup": "^4.6.1",
"rollup-plugin-visualizer": "^5.12.0",
"stylelint": "^15.11.0",
"stylelint-config-html": "^1.1.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
})
// 使线
const isUseOnline = computed(() => {
return import.meta.env.VITE_USE_ONLINE_ICON === 'true'
})
const getIconifyStyle = computed(() => {
const { color, size } = props
return {
@ -40,7 +45,10 @@ const getIconifyStyle = computed(() => {
<use :xlink:href="symbolId" />
</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>
</template>
@ -49,11 +57,18 @@ const getIconifyStyle = computed(() => {
.@{prefix-cls},
.iconify {
&:hover {
:deep(svg) {
:deep(svg) {
&:hover {
// stylelint-disable-next-line
color: v-bind(hoverColor) !important;
}
}
}
.iconify {
&:hover {
// stylelint-disable-next-line
color: v-bind(hoverColor) !important;
}
}
</style>

View File

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

View File

@ -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)
}
</script>

View File

@ -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)
}

View File

@ -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
)

View File

@ -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' ? (

View File

@ -12,7 +12,13 @@ export const setupElementPlus = (app: App<Element>) => {
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)
})
}

View File

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

View File

@ -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

View File

@ -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
}

View File

@ -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

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'
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)($|\?)/]
}
}
})

View File

@ -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,