Browse Source

主题色配置

tongshangming 2 years ago
parent
commit
6af9acf7e3

+ 2 - 2
index.html

@@ -2,9 +2,9 @@
 <html lang="en">
   <head>
     <meta charset="UTF-8" />
-    <link rel="icon" href="/favicon.ico" />
+    <link rel="icon" href="/logo.png" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>Vite App</title>
+    <title></title>
   </head>
   <body>
     <div id="app"></div>

+ 0 - 0
src/assets/logo.png → public/logo.png


+ 4 - 0
src/App.vue

@@ -1,6 +1,10 @@
 <script setup lang="ts">
 import zhCn from 'element-plus/lib/locale/lang/zh-cn'
+import { useThemeStore } from '@/stores/theme'
 const locale = zhCn
+
+const themeStore = useThemeStore()
+themeStore.initThemeColor()
 </script>
 
 <template>

+ 2 - 2
src/assets/base.css

@@ -51,8 +51,8 @@
 } */
 
 :root {
-  --el-color-primary: #3454cd;
-  --menu-width: 200px;
+  /* --el-color-primary: #3454cd; */
+  --menu-width: 220px;
   --main-padding: 16px;
 }
 

+ 0 - 1
src/assets/logo.svg

@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"  xmlns:v="https://vecta.io/nano"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

+ 4 - 10
src/assets/main.css

@@ -4,15 +4,9 @@
   height: 100vh;
 }
 
-a,
-.green {
-  text-decoration: none;
-  color: hsla(160, 100%, 37%, 1);
-  transition: 0.4s;
+.el-menu-item [class^=el-icon]{
+  font-size: 20px;
 }
-
-@media (hover: hover) {
-  a:hover {
-    background-color: hsla(160, 100%, 37%, 0.2);
-  }
+.el-card{
+  border: none !important;
 }

+ 1 - 0
src/auto-import.d.ts

@@ -2,6 +2,7 @@
 export {}
 declare global {
   const EffectScope: typeof import('vue')['EffectScope']
+  const ElMessageBox: typeof import('element-plus/es')['ElMessageBox']
   const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
   const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
   const autoResetRef: typeof import('@vueuse/core')['autoResetRef']

+ 3 - 0
src/components.d.ts

@@ -15,6 +15,8 @@ declare module '@vue/runtime-core' {
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
     ElContainer: typeof import('element-plus/es')['ElContainer']
     ElDialog: typeof import('element-plus/es')['ElDialog']
+    ElDivider: typeof import('element-plus/es')['ElDivider']
+    ElDrawer: typeof import('element-plus/es')['ElDrawer']
     ElDropdown: typeof import('element-plus/es')['ElDropdown']
     ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
     ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
@@ -38,6 +40,7 @@ declare module '@vue/runtime-core' {
     GlobalFooter: typeof import('./components/GlobalFooter.vue')['default']
     GlobalHeader: typeof import('./components/GlobalHeader.vue')['default']
     GlobalMenu: typeof import('./components/GlobalMenu.vue')['default']
+    GlobalSetting: typeof import('./components/GlobalSetting.vue')['default']
     IEpArrowDown: typeof import('~icons/ep/arrow-down')['default']
     IEpLock: typeof import('~icons/ep/lock')['default']
     IEpRefresh: typeof import('~icons/ep/refresh')['default']

+ 30 - 13
src/components/GlobalHeader.vue

@@ -1,7 +1,9 @@
 <script setup>
 import { useMenuStore } from '@/stores/menu'
 import { useUserStore } from '@/stores/user'
-import logo from '@/assets/logo.png'
+import config from '@/config/defaultSetting'
+
+const iconSize = ref('22')
 
 // 菜单收缩展开切换
 const menuStore = useMenuStore()
@@ -14,27 +16,44 @@ const handleToggle = () => {
 // 全屏切换
 const { toggle: toggleScreen, isFullscreen } = useFullscreen()
 
+// 用户信息
 const userStore = useUserStore()
 const user = userStore.user
+
+const logout = () => {
+  ElMessageBox.confirm('您确定要退出登录吗', '提示', {
+    type: 'info',
+    closeOnClickModal: false,
+    confirmButtonText: '确定'
+  }).then(() => {
+    userStore.logout()
+  })
+}
+
+// 主题设置
+const visible = ref(false)
 </script>
 
 <template>
   <header class="layout-header">
     <div class="logo flex items-center justify-center" :class="{ collapse: value }">
-      <img :src="logo" />
-      <div class="ml-2" :class="{ hidden: value }">方是科技管理系统</div>
+      <img :src="config.logo" class="h-36px" />
+      <div class="ml-2" :class="{ hidden: value }">{{ config.title }}</div>
     </div>
     <div class="flex flex-grow items-center justify-between px-4">
       <el-space :size="20">
         <el-button link @click="handleToggle">
-          <icon-menu-unfold-one size="22" :strokeWidth="2" v-if="!value" />
-          <icon-menu-fold-one size="22" :strokeWidth="2" v-else />
+          <icon-menu-unfold-one :size="iconSize" :strokeWidth="2" v-if="!value" />
+          <icon-menu-fold-one :size="iconSize" :strokeWidth="2" v-else />
         </el-button>
       </el-space>
       <div class="flex items-center">
-        <el-button link @click="toggleScreen" class="mr-4">
-          <icon-off-screen size="22" v-if="isFullscreen" />
-          <icon-full-screen size="22" v-else />
+        <el-button link @click="toggleScreen" class="mr-2">
+          <icon-off-screen :size="iconSize" v-if="isFullscreen" />
+          <icon-full-screen :size="iconSize" v-else />
+        </el-button>
+        <el-button link class="mr-4" @click="visible = true">
+          <icon-setting-two :size="iconSize" />
         </el-button>
         <el-avatar class="mr-10px" :src="user.photo"></el-avatar>
         <el-dropdown>
@@ -50,7 +69,7 @@ const user = userStore.user
                 <el-icon><icon-setting-two></icon-setting-two></el-icon>
                 <div>个人设置</div>
               </el-dropdown-item>
-              <el-dropdown-item @click="userStore.logout">
+              <el-dropdown-item @click="logout">
                 <el-icon><icon-logout></icon-logout></el-icon>
                 <div>退出登录</div>
               </el-dropdown-item>
@@ -59,6 +78,8 @@ const user = userStore.user
         </el-dropdown>
       </div>
     </div>
+
+    <GlobalSetting v-model="visible"></GlobalSetting>
   </header>
 </template>
 
@@ -72,10 +93,6 @@ const user = userStore.user
 
   .logo {
     width: var(--menu-width);
-
-    img {
-      height: 36px;
-    }
   }
   .collapse {
     width: calc(var(--el-menu-icon-width) + var(--el-menu-base-level-padding) * 2);

+ 2 - 0
src/components/GlobalMenu.vue

@@ -70,6 +70,8 @@ const handleSelect = (index: string) => {
 .layout-menu {
   background-color: #fff;
   height: 100%;
+  border: none;
+  box-shadow: 2px 0 8px #1d23290d;
 }
 .layout-menu .el-menu-item.is-active {
   background-color: var(--el-color-primary);

+ 59 - 0
src/components/GlobalSetting.vue

@@ -0,0 +1,59 @@
+<script setup lang="ts">
+import { useThemeStore } from '@/stores/theme'
+const props = defineProps({
+  modelValue: Boolean
+})
+const emits = defineEmits(['update:modelValue'])
+
+const visible = computed({
+  get: () => props.modelValue,
+  set: value => emits('update:modelValue', value)
+})
+
+const themeStore = useThemeStore()
+const themeColor = computed(() => themeStore.themeColor)
+const themeColors = [
+  '#1890ff',
+  '#409EFF',
+  '#2d8cf0',
+  '#007AFF',
+  '#5ac8fa',
+  '#5856D6',
+  '#536dfe',
+  '#9c27b0',
+  '#AF52DE',
+  '#0096c7',
+  '#00C1D4',
+  '#34C759',
+  '#43a047',
+  '#7cb342',
+  '#c0ca33',
+  '#78DEC7',
+  '#e53935',
+  '#d81b60',
+  '#f4511e',
+  '#fb8c00',
+  '#ffb300',
+  '#fdd835',
+  '#6d4c41',
+  '#546e7a'
+]
+</script>
+
+<template>
+  <el-drawer v-model="visible" title="主题配置" size="350px">
+    <el-divider>系统主题 </el-divider>
+    <el-space wrap>
+      <div
+        class="flex items-center justify-center w-30px h-30px"
+        v-for="item in themeColors"
+        :style="{ background: item }"
+        @click="themeStore.setThemeColor(item)"
+      >
+        <el-icon size="22" color="#fff" v-if="themeColor === item"><icon-check></icon-check></el-icon>
+      </div>
+    </el-space>
+  </el-drawer>
+</template>
+
+<style lang="scss" scoped></style>

+ 1 - 1
src/components/ProTable.vue

@@ -130,7 +130,7 @@ const placeholder = (item: column) => {
   }
 }
 
-props.columns.forEach(item => {
+formList.value.forEach(item => {
   formData.value[item.prop] = item.value
 })
 

+ 5 - 0
src/config/defaultSetting.ts

@@ -0,0 +1,5 @@
+export default {
+  title: '方是科技管理系统',
+  logo: '/logo.png',
+  themeColor: '#1890ff'
+}

+ 1 - 1
src/router/asyncRouter.ts

@@ -33,7 +33,7 @@ const asyncRouter: RouteRecordRaw[] = [
         component: () => import('@/views/system/User.vue'),
         meta: {
           title: '用户管理',
-          icon: 'icon-user'
+          icon: 'icon-people'
         }
       },
       {

+ 5 - 2
src/router/constantRouter.ts

@@ -1,4 +1,4 @@
-import BlankLayout from '@/layouts/BlankLayout.vue'
+// import BlankLayout from '@/layouts/BlankLayout.vue'
 import BasicLayout from '@/layouts/BasicLayout.vue'
 import type { RouteRecordRaw } from 'vue-router'
 
@@ -13,7 +13,10 @@ const constantRouter: RouteRecordRaw[] = [
   {
     path: '/login',
     name: 'login',
-    component: () => import('@/views/user/Login.vue')
+    component: () => import('@/views/user/Login.vue'),
+    meta: {
+      title: '登录'
+    }
   },
   {
     path: '/:pathMatch(.*)*',

+ 2 - 1
src/router/guard.ts

@@ -23,8 +23,9 @@ const createRouterGuard = (router: Router) => {
         return from.path
       } else {
         const routerStore = useRouterStore()
+        // flag默认为false,为tru时表示已经获取过用户信息
         if (!userStore.flag) {
-          userStore.getUserInfo()
+          await userStore.getUserInfo()
 
           const accessedRouters = await routerStore.generatorRouter('')
           accessedRouters.forEach(route => {

+ 1 - 2
src/stores/menu.ts

@@ -1,4 +1,3 @@
-
 export const useMenuStore = defineStore({
   id: 'menu',
   state: () => ({
@@ -9,4 +8,4 @@ export const useMenuStore = defineStore({
       this.collapse = collapse
     }
   }
-})
+})

+ 26 - 0
src/stores/theme.ts

@@ -0,0 +1,26 @@
+import config from '@/config/defaultSetting'
+import colorUtil from '@/utils/color'
+
+export const useThemeStore = defineStore({
+  id: 'theme',
+  state: () => ({
+    themeColor: localStorage.getItem('themeColor') || config.themeColor
+  }),
+  actions: {
+    setThemeColor(themeColor: string) {
+      this.themeColor = themeColor
+      localStorage.setItem('themeColor', themeColor)
+      this.initThemeColor()
+    },
+    initThemeColor() {
+      const primary = this.themeColor
+      document.documentElement.style.setProperty('--el-color-primary', primary)
+      for (let i = 1; i <= 9; i++) {
+        document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, colorUtil.lighten(primary, i / 10))
+      }
+      for (let i = 1; i <= 9; i++) {
+        document.documentElement.style.setProperty(`--el-color-primary-dark-${i}`, colorUtil.darken(primary, i / 10))
+      }
+    }
+  }
+})

+ 4 - 3
src/stores/user.ts

@@ -9,7 +9,9 @@ export const useUserStore = defineStore({
     token: localStorage.getItem('token') || ''
   }),
   actions: {
-    getUserInfo() {
+    async getUserInfo() {
+      const userRes: any = await getUserInfo()
+      this.user = userRes.infos[0]
       this.flag = true
     },
     async login(data: any) {
@@ -17,8 +19,7 @@ export const useUserStore = defineStore({
       this.token = res.accessToken
       localStorage.setItem('token', this.token)
 
-      const userRes: any = await getUserInfo()
-      this.user = userRes.infos[0]
+      await this.getUserInfo()
 
       router.replace({ path: '/home' })
     },

+ 1 - 2
src/utils/request.ts

@@ -22,8 +22,7 @@ const errorHandler = (error: any) => {
       center: true,
       confirmButtonText: '重新登录'
     }).then(() => {
-      userStore.$reset()
-      router.replace({ path: '/login' })
+      userStore.logout()
     })
   } else if (status === 403) {
     ElMessage.error('无权限访问')

File diff suppressed because it is too large
+ 19 - 23
src/views/user/Login.vue


Some files were not shown because too many files changed in this diff