This commit is contained in:
zouzhibin 2022-08-28 22:11:05 +08:00
parent 0902a65fc8
commit 6c40c8c1ec
21 changed files with 375 additions and 121 deletions

View File

@ -1,7 +1,20 @@
<template>
<router-view></router-view>
<el-config-provider :size="globalComSize" :locale="zhCn">
<router-view></router-view>
</el-config-provider>
</template>
<script lang="ts" setup>
import {computed} from "vue";
import {useStore} from "vuex";
// element
import zhCn from 'element-plus/es/locale/lang/zh-cn'
const store = useStore()
//
const globalComSize = computed(():string=>store.state.setting.themeConfig.globalComSize)
</script>
<style lang="scss">
#app {
position: relative;
@ -22,6 +35,12 @@
.el-pager li:focus {
border: none;
}
.el-dropdown:focus {
border: none;
}
.svg-icon:focus {
border: none;
}
* {
margin: 0;
padding: 0;

View File

@ -13,7 +13,6 @@
className: { type: String },
},
setup(props) {
console.log(11111111)
const iconName = computed(() => {
return props.iconClass ? `#icon-${props.iconClass}` : '#icon'
})

View File

@ -102,7 +102,6 @@ const generateRoutes = (routes, basePath = '/', prefixTitle = [])=>{
}
const change=(val)=> {
console.log('===========',val)
if(val){
router.push({
path: val,
@ -113,10 +112,7 @@ const change=(val)=> {
isShowSearch.value = false
}
onMounted(()=>{
console.log(routes.value)
searchPool.value = generateRoutes(JSON.parse(JSON.stringify(routes.value)))
console.log('==========',searchPool)
})
const querySearch=(query)=> {

View File

@ -1,6 +1,6 @@
<template>
<div class="m-screenful">
<svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" @click="click" />
<svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" @click="click" class="full-screen"/>
</div>
</template>

View File

@ -12,11 +12,12 @@
</div>
</div>
</div>
<el-drawer v-model="drawer" title="主题配置" size="300px">
<!-- <div class="theme-item">-->
<!-- <label>标签</label>-->
<!-- <el-color-picker v-model="themeConfig.primary" :predefine="colorList" @change="changePrimary" />-->
<!-- </div>-->
<el-drawer
v-model="drawer" title="主题配置" size="300px">
<div class="theme-item">
<label>标签</label>
<el-color-picker v-model="primary" :predefine="predefineColor" @change="changePrimary" />
</div>
<div class="theme-item">
<label>暗黑模式</label>
<switch-dark></switch-dark>
@ -49,12 +50,28 @@
import {computed, ref} from 'vue'
import SwitchDark from './components/switchDark.vue'
import { useStore } from 'vuex'
import {ElMessage} from "element-plus";
import {PRIMARY_COLOR} from "@/config/index";
import {getDarkColor,getLightColor} from '@/utils/index'
const store = useStore()
const layout = ref(store.state.setting.themeConfig.mode)
const showTag = ref(store.state.setting.themeConfig.showTag)
const showLogo = ref(store.state.setting.themeConfig.showLogo)
const drawer = ref(false)
const primary = ref(store.state.setting.themeConfig.primary)
const drawer = computed({
get() {
return store.state.setting.themeConfig.showSetting;
},
set() {
changeSwitch('showSetting',!store.state.setting.themeConfig.showSetting)
}
})
//
const predefineColor = [
'#409EFF', '#1890ff', '#304156','#212121','#11a983', '#13c2c2', '#6959CD', '#f5222d'
];
const operator = (type) => {
switch (type) {
@ -69,6 +86,24 @@
const changeSwitch = (key,val) => {
store.dispatch('setting/setThemeConfig', {key, val})
}
//
const changePrimary = (val)=>{
if (!val) {
primary.value = val = PRIMARY_COLOR;
ElMessage({ type: "success", message: `主题颜色已重置为 ${PRIMARY_COLOR}` });
}
//
document.documentElement.style.setProperty("--el-color-primary-dark-2", `${getDarkColor(val, 0.1)}`);
document.documentElement.style.setProperty("--el-color-primary", val);
//
for (let i = 1; i <= 9; i++) {
document.documentElement.style.setProperty(
`--el-color-primary-light-${i}`,
`${getLightColor(val, i / 10)}`
);
}
changeSwitch('primary',val)
}
</script>

2
src/config/index.ts Normal file
View File

@ -0,0 +1,2 @@
// * 默认主题颜色
export const PRIMARY_COLOR: string = "#409eff";

1
src/icons/svg/size.svg Normal file
View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M0 54.857h54.796v18.286H36.531V128H18.265V73.143H0V54.857zm127.857-36.571H91.935V128H72.456V18.286H36.534V0h91.326l-.003 18.286z"/></svg>

After

Width:  |  Height:  |  Size: 211 B

View File

@ -0,0 +1,46 @@
<template>
<div class="m-setting">
<el-tooltip effect="dark" content="主题设置" placement="bottom">
<el-icon style="font-size: 22px;" class="bell"><Setting @click="changeSwitch('showSetting',true)"/></el-icon>
</el-tooltip>
</div>
</template>
<script lang="ts" setup>
import {useStore} from "vuex";
const store = useStore()
const changeSwitch = (key,val) => {
store.dispatch('setting/setThemeConfig', {key, val})
}
</script>
<style lang="scss" scoped>
.m-setting {
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s;
.item-info-pop {
display: flex;
align-items: center;
}
.bell{
color: black;
}
.item-child {
display: flex;
align-items: center;
font-size: 13px;
}
}
.transverseMenu {
.bell {
color: white;
}
}
</style>

View File

@ -0,0 +1,39 @@
<template>
<el-dropdown trigger="click" @command="setAssemblySize">
<svg-icon class-name="size-icon" icon-class="size" style="font-size: 22px;"/>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in assemblySizeList" :key="item" :disabled="globalComSize === item" :command="item">
{{ assemblySizeListCh[item] }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup lang="ts">
import { reactive, computed } from "vue";
import {useStore} from "vuex";
const store = useStore()
const globalComSize = computed(():string=>store.state.setting.themeConfig.globalComSize)
const assemblySizeListCh = reactive<{ [key: string]: any }>({
default: "默认",
large: "大型",
small: "小型"
});
const assemblySizeList = reactive<string[]>(["default", "large", "small"]);
const setAssemblySize = (item: string) => {
if (globalComSize.value === item) return;
store.dispatch('setting/setThemeConfig', {key:'globalComSize', val:item})
};
</script>
<style scoped lang="scss">
</style>

View File

@ -18,12 +18,16 @@
<u-hamburger />
</div>
<div class="right">
<global-com-size class="right-item-menu"/>
<u-header-search class="right-item-menu"/>
<u-info class="right-item-menu"/>
<u-screen-full class="right-item-menu"/>
<u-setting class="right-item-menu"/>
<el-dropdown @command="commandAction">
<span class="el-dropdown-link">
<el-avatar :icon="UserFilled" :size="30" style="margin-right: 6px" />{{
@ -51,7 +55,9 @@
import { UserFilled } from '@element-plus/icons-vue'
import Personal from './components/Personal.vue'
import TagViews from '../TagsView/index.vue'
import GlobalComSize from './components/globalComSize.vue'
import UHamburger from '@/components/u-Hamburger/index.vue'
import USetting from './components/Setting.vue'
import UScreenFull from '@/components/u-screenfull/index.vue'
import UInfo from '@/components/u-info/index.vue'
import UHeaderSearch from '@/components/u-headerSearch/index.vue'

View File

@ -23,12 +23,19 @@
</div>
</better-scroll>
</div>
<el-tooltip class="box-item" effect="dark" content="点击刷新" placement="bottom-end">
<div class="refresh" @click="refresh">
<div class="refresh-inner">
<el-icon><Refresh /> </el-icon> </div
></div>
</el-tooltip>
<el-dropdown trigger="click">
<el-button class="el-dropdown-link" type="primary">
更多 <el-icon class="el-icon--right"><arrow-down /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="refresh">刷新当页</el-dropdown-item>
<el-dropdown-item @click="closeCurrentTab">关闭当前</el-dropdown-item>
<el-dropdown-item @click="closeOtherTab">关闭其他</el-dropdown-item>
<el-dropdown-item @click="closeAllTab">关闭所有</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
@ -135,15 +142,35 @@
}
}
const closeSelectedTag = (event, view) => {
const closeSelectedTag = async (event, view) => {
if (tags.value.get(view.path)) {
tags.value.delete(view.path)
}
store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
if (isActive(view)) {
toLastView(visitedViews, view)
let { visitedViews } = await store.dispatch('tagsView/delView', view)
if (isActive(view)) {
toLastView(visitedViews, view)
}
}
//
const closeCurrentTab = (event)=>{
closeSelectedTag(event,route)
}
//
const closeOtherTab= async ()=>{
const { name } = route
for(let item of visitedViews.value){
if(item.name!==name){
await closeSelectedTag(null,item)
}
})
}
}
//
const closeAllTab = ()=>{
store.dispatch('tagsView/delAllViews')
router.push('/')
}
const routerGo = (tag) => {
@ -259,14 +286,13 @@
display: block;
}
&.active {
background-color: #42b983;
background-color: $primaryColor;
color: #fff;
border-color: #42b983;
border-color: $primaryColor;
}
}
.item-tag-wrap:hover {
border-color: #42b983;
//color: #42b983;
border-color: $primaryColor;
}
.tags-scroll-inner {
display: flex;

View File

@ -13,12 +13,13 @@ import SvgIcon from '@/components/SvgIcon/index.vue'// svg component
import ElementPlus from 'element-plus'
import UContainerLayout from '@/components/u-container-layout/index.vue'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
// 引入暗黑模式 element-plus 2.2 内置暗黑模式
import 'element-plus/theme-chalk/dark/css-vars.css'
// 自定义暗黑模式
import "@/styles/element-dark.scss";
const app = createApp(App)
app.component('svg-icon',SvgIcon)
app.component('u-container-layout',UContainerLayout)
@ -36,7 +37,4 @@ Object.keys(ElIconsModules).forEach((key) => {//循环遍历组件名称
app.use(store)
app.use(router)
app.use(ElementPlus,{
locale: zhCn,
}).mount('#app')
app.use(ElementPlus).mount('#app')

View File

@ -103,10 +103,11 @@ const zipRoutes = {
// // 异步组件
export const asyncRoutes = [
tableRouter,
chartsRouter,
tableRouter,
chatRouter,
componentsRouter,
// componentsRouter,
othersRouter,
nestedRouter,
excelRouter,

View File

@ -1,7 +1,8 @@
import {Module} from "vuex";
import {PRIMARY_COLOR} from "../../config";
const state = {
themeConfig:{
showSetting:false,
// 菜单展示模式 默认 vertical horizontal / vertical
mode: 'vertical',
// tagsView 是否展示 默认展示
@ -11,7 +12,11 @@ const state = {
// 深色模式 切换暗黑模式
isDark: false,
// 显示侧边栏Logo
showLogo:true
showLogo:true,
// 主题颜色
primary:PRIMARY_COLOR,
// element组件大小
globalComSize:'default'
},
}

View File

@ -33,19 +33,25 @@ const mutations = {
}
},
DEL_VISITED_VIEW: (state, view) => {
for (const [i, v] of state.visitedViews.entries()) {
if (v.path === view.path) {
state.visitedViews.splice(i, 1)
break
}
}
for (const i of state.cachedViews) {
if (i === view.name) {
const index = state.cachedViews.indexOf(i)
state.cachedViews.splice(index, 1)
break
}
}
state.visitedViews = state.visitedViews.filter(v=>{
return v.path !== view.path
})
state.cachedViews = state.cachedViews.filter(v=>{
return v.path !== view.path
})
// for (const [i, v] of state.visitedViews.entries()) {
// if (v.path === view.path) {
// state.visitedViews.splice(i, 1)
// break
// }
// }
// for (const i of state.cachedViews) {
// if (i === view.name) {
// const index = state.cachedViews.indexOf(i)
// state.cachedViews.splice(index, 1)
// break
// }
// }
},
DEL_CACHED_VIEW: (state, view) => {

View File

@ -136,5 +136,12 @@ html.dark {
color: var(--el-text-color-primary) !important;
}
// icon
.m-info .bell,
.m-setting .bell,
.m-headerSearch .bell{
color: white!important;
}
}

View File

@ -8,10 +8,13 @@ $tiffany: #4ab7bd;
$yellow: #fec171;
$panGreen: #30b08f;
/* 全局 css 变量 */
$primaryColor: var(--el-color-primary);
// sidebar
$menuText: #bfcbd9;
$menuActiveText: #409eff;
$subMenuActiveText: #f4f4f5; // https://github.com/ElemeFE/element/issues/12951
$subMenuActiveText: #f4f4f5;
$menuBg: #304156;
$menuHover: #263445;
@ -21,8 +24,6 @@ $subMenuHover: #001528;
$sideBarWidth: 210px;
// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
:export {
menuText: $menuText;
menuActiveText: $menuActiveText;
@ -32,4 +33,5 @@ $sideBarWidth: 210px;
subMenuBg: $subMenuBg;
subMenuHover: $subMenuHover;
sideBarWidth: $sideBarWidth;
primaryColor: $primaryColor;
}

View File

@ -423,3 +423,62 @@ export const deepObjClone = function (obj) {
}
return clone(obj)
}
import { ElMessage } from "element-plus";
/**
* hex颜色转rgb颜色
* @param str
* @returns
*/
export function hexToRgb(str: any) {
let hexs: any = "";
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(str)) return ElMessage.warning("输入错误的hex");
str = str.replace("#", "");
hexs = str.match(/../g);
for (let i = 0; i < 3; i++) hexs[i] = parseInt(hexs[i], 16);
return hexs;
}
/**
* rgb颜色转Hex颜色
* @param r
* @param g 绿
* @param b
* @returns
*/
export function rgbToHex(r: any, g: any, b: any) {
let reg = /^\d{1,3}$/;
if (!reg.test(r) || !reg.test(g) || !reg.test(b)) return ElMessage.warning("输入错误的rgb颜色值");
let hexs = [r.toString(16), g.toString(16), b.toString(16)];
for (let i = 0; i < 3; i++) if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`;
return `#${hexs.join("")}`;
}
/**
*
* @param color
* @param level 0-1
* @returns
*/
export function getDarkColor(color: string, level: number) {
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) return ElMessage.warning("输入错误的hex颜色值");
let rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.floor(rgb[i] * (1 - level));
return rgbToHex(rgb[0], rgb[1], rgb[2]);
}
/**
*
* @param color
* @param level 0-1
* @returns
*/
export function getLightColor(color: string, level: number) {
let reg = /^\#?[0-9A-Fa-f]{6}$/;
if (!reg.test(color)) return ElMessage.warning("输入错误的hex颜色值");
let rgb = hexToRgb(color);
for (let i = 0; i < 3; i++) rgb[i] = Math.floor((255 - rgb[i]) * level + rgb[i]);
return rgbToHex(rgb[0], rgb[1], rgb[2]);
}

View File

@ -1,4 +1,4 @@
import { defineConfig } from 'vite'
import { defineConfig,ConfigEnv, UserConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// import AutoImport from 'unplugin-auto-import/vite'
// import Components from 'unplugin-vue-components/vite'
@ -7,78 +7,85 @@ import path from 'path'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
// 增加 vue文件 script name值
import vueSetupExtend from 'vite-plugin-vue-setup-extend'
// import viteCompression from 'vite-plugin-compression'
import viteCompression from 'vite-plugin-compression'
function resolve (dir) {
return path.join(__dirname, '.', dir)
}
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(),
vueSetupExtend(),
// AutoImport({
// resolvers: [ElementPlusResolver()],
// }),
// Components({
// resolvers: [ElementPlusResolver()],
// }),
// * 使用 svg 图标
createSvgIconsPlugin({
// 指定需要缓存的图标文件夹
iconDirs: [path.resolve(process.cwd(), 'src/icons/svg')],
// 指定symbolId格式
symbolId: 'icon-[dir]-[name]',
}),
// gzip压缩 生产环境生成 .gz 文件
// viteCompression({
// verbose: true,
// disable: false,
// threshold: 10240,
// algorithm: 'gzip',
// ext: '.gz',
// }),
],
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "./src/styles/index.scss" as *;`
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
console.log('mode================',mode)
return {
plugins: [vue(),
vueSetupExtend(),
// AutoImport({
// resolvers: [ElementPlusResolver()],
// }),
// Components({
// resolvers: [ElementPlusResolver()],
// }),
// * 使用 svg 图标
createSvgIconsPlugin({
// 指定需要缓存的图标文件夹
iconDirs: [path.resolve(process.cwd(), 'src/icons/svg')],
// 指定symbolId格式
symbolId: 'icon-[dir]-[name]',
}),
// gzip压缩 生产环境生成 .gz 文件
mode==='production'&&viteCompression({
verbose: true,
disable: false,
threshold: 10240,
algorithm: 'gzip',
ext: '.gz',
}),
],
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "./src/styles/index.scss" as *;`
}
}
}
},
// 配置别名
resolve: {
alias: {
'@':resolve('src'),
'static':resolve('public/static'),
},
// 忽略后缀名的配置选项, 添加 .vue 选项时要记得原本默认忽略的选项也要手动写入
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
},
//启动服务配置
server: {
// 服务器主机名,如果允许外部访问,可设置为 "0.0.0.0" 也可设置成你的ip地址
host: '0.0.0.0',
port: 8100,
open: true,
https: false,
cors: true,
// 代理跨域(模拟示例)
proxy: {
// "/api": {
// target: "", // easymock
// changeOrigin: true,
// rewrite: path => path.replace(/^\/api/, "")
// }
}
},
// 生产环境打包配置
//去除 console debugger
// build: {
// terserOptions: {
// compress: {
// drop_console: true,
// drop_debugger: true,
// },
// },
// },
// 配置别名
resolve: {
alias: {
'@':resolve('src'),
'static':resolve('public/static'),
},
// 忽略后缀名的配置选项, 添加 .vue 选项时要记得原本默认忽略的选项也要手动写入
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
},
//启动服务配置
server: {
// 服务器主机名,如果允许外部访问,可设置为 "0.0.0.0" 也可设置成你的ip地址
host: '0.0.0.0',
port: 8100,
open: true,
https: false,
cors: true,
// 代理跨域(模拟示例)
proxy: {
// "/api": {
// target: "", // easymock
// changeOrigin: true,
// rewrite: path => path.replace(/^\/api/, "")
// }
}
},
esbuild: {
pure:mode==='production' ? ["console.log", "debugger"] : []
},
// 生产环境打包配置
//去除 console debugger
// build: {
// terserOptions: {
// compress: {
// drop_console: true,
// drop_debugger: true,
// },
// },
// },
}
})