iZaiZaiA 3 rokov pred
rodič
commit
112e518f3d

+ 15 - 6
src/App.vue

@@ -59,12 +59,21 @@ const setUserColor = () => {
     const el = document.documentElement
     el.style.setProperty('--el-color-primary', color)
     // 设置 css 渐变 变量
-    for (let i = 0; i < 9; i++) {
-        let num = i + 1, amount = (8 - num) / 10;
-        if (num < 8) {
-            const val = toColor('#FFFFFF',color, amount)
-            el.style.setProperty(`--el-color-primary-light-${num}`, val)
+    const numArr = [3,5,7,8,9]
+    numArr.forEach(item => {
+        let amount = 0
+        if (item === 3) {
+            amount = 0.9
+        } else if (item === 5) {
+            amount = 0.7
+        } else if (item >= 7) {
+            amount = amount = (10 - item) / 10
         }
-    }
+        const val = toColor('#FFFFFF', color , amount)
+        el.style.setProperty(`--el-color-primary-light-${item}`, val)
+    })
+    //生成深主色颜色
+    const val = toColor('#000000', color , 0.9)
+    el.style.setProperty('--el-color-primary-dark-2', val)
 }
 </script>

+ 9 - 11
src/config/theme.js

@@ -3,18 +3,16 @@ import config from '~src/config/index';
 //主题配置
 export default {
     color: [
-        {name: 'green', color: '#1ECC95'}, {name: 'blue', color: '#0081ff'},
-        {name: 'cyan', color: '#37c0fe'}, {name: 'purple', color: '#8044de'},
-        {name: 'mauve', color: '#b745cb'}, {name: 'pink', color: '#e03997'},
-        {name: 'red', color: '#e54d42'}, {name: 'orange', color: '#f37b1d'},
-        {name: 'yellow', color: '#fbbd08'}, {name: 'brown', color: '#a5673f'}
+        {name: 'green', color: '#1ECC95', label: '森绿'}, {name: 'blue', color: '#0081ff', label: '海蓝'},
+        {name: 'cyan', color: '#37c0fe', label: '天青'}, {name: 'purple', color: '#8044de', label: '姹紫'},
+        {name: 'mauve', color: '#b745cb', label: '木槿'}, {name: 'pink', color: '#e03997', label: '桃粉'},
+        {name: 'red', color: '#e54d42', label: '嫣红'}, {name: 'orange', color: '#f37b1d', label: '橘橙'},
+        {name: 'yellow', color: '#fbbd08', label: '明黄'}, {name: 'brown', color: '#a5673f', label: '棕褐'}
     ],
     home: [
-        {name: 'theme-1', bg: `${config.ossUrl}/upload/20220623/1eec681ebe2581d2e69752f0491010a8.jpg`},
-        {name: 'theme-2', bg: `${config.ossUrl}/upload/20220623/cea206d73a6c36281f077e5c2a1ea6e3.jpg`},
-        {name: 'theme-3', bg: `${config.ossUrl}/upload/20220623/6c071a535c96d3b76f741941d6679b35.jpg`},
-        {name: 'theme-4', bg: `${config.ossUrl}/upload/20220623/813eecd1e81fafcbc571f029c8091c56.jpg`},
-        {name: 'theme-5', bg: `${config.ossUrl}/upload/20220623/9d1ef990730a823b31f0e786da034647.jpg`},
-        {name: 'theme-6', bg: `${config.ossUrl}/upload/20220623/856f229d1b6298eeafdeaa010ba8b80f.jpg`}
+        {name: 'theme-1', bg: `${config.ossUrl}/upload/20220805/75cbfb3c007fb35bcefe91a1b9a8ce75.png`},
+        {name: 'theme-2', bg: `${config.ossUrl}/upload/20220623/1eec681ebe2581d2e69752f0491010a8.jpg`},
+        {name: 'theme-3', bg: `${config.ossUrl}/upload/20220623/cea206d73a6c36281f077e5c2a1ea6e3.jpg`},
+        {name: 'theme-4', bg: `${config.ossUrl}/upload/20220623/6c071a535c96d3b76f741941d6679b35.jpg`}
     ],
 }

+ 113 - 0
src/global/components/hc-card/index.vue

@@ -0,0 +1,113 @@
+<template>
+    <el-card class="hc-card-box" :class="[(isSlotHeader || title || isSlotExtra || extraText)?'is-header':'', ui]" shadow="never">
+        <template #header v-if="isSlotHeader || title || isSlotExtra || extraText">
+            <div class="hc-card-header-box">
+                <div class="hc-card-header">
+                    <div class="title text-xl" v-if="!isSlotHeader && title">{{ title }}</div>
+                    <slot v-if="isSlotHeader" name='header'/>
+                </div>
+                <div class="hc-card-header-extra" v-if="isSlotExtra || extraText">
+                    <div class="extra" v-if="!isSlotExtra && extraText">{{ extraText }}</div>
+                    <slot v-if="isSlotExtra" name='extra'/>
+                </div>
+            </div>
+        </template>
+        <div class="hc-card-main-box" :class="isSlotAction?'is-action':''">
+            <template v-if="scrollbar">
+                <el-scrollbar>
+                    <slot></slot>
+                </el-scrollbar>
+            </template>
+            <template v-else>
+                <slot></slot>
+            </template>
+        </div>
+        <div class="hc-card-action-box" v-if="isSlotAction">
+            <slot name='action'/>
+        </div>
+    </el-card>
+</template>
+
+<script setup>
+import {useSlots} from "vue";
+const props = defineProps({
+    ui: {
+        type: String,
+        default: ''
+    },
+    title: {
+        type: [String,Number],
+        default: ''
+    },
+    extraText: {
+        type: [String,Number],
+        default: ''
+    },
+    scrollbar: {
+        type: Boolean,
+        default: true
+    },
+})
+
+//判断<slot>是否有传值
+const isSlotHeader = !!useSlots().header;
+const isSlotExtra = !!useSlots().extra;
+const isSlotAction = !!useSlots().action;
+</script>
+
+<style lang="scss">
+.hc-card-box.el-card {
+     height: 100%;
+     position: relative;
+     --el-card-padding: 24px;
+     --el-card-bg-color: #f1f5f8;
+     --el-card-border-radius: 10px;
+     --el-text-color-primary: #1A1A1A;
+     box-shadow: -2px 0 10px 0 rgba(32,37,50,0.03), 0 10px 21px 20px rgba(32,37,50,0.03);
+    border: 0;
+    .el-card__header {
+        padding: 20px 24px;
+        border-bottom: 1px solid #e9e9e9;
+    }
+    .hc-card-header-box {
+        position: relative;
+        display: flex;
+        .hc-card-header {
+            position: relative;
+            flex: 1;
+        }
+        .hc-card-header-extra {
+            position: relative;
+            flex: 1;
+        }
+    }
+    .el-card__body {
+        position: relative;
+        .hc-card-main-box {
+            position: relative;
+            height: 100%;
+            &.is-action {
+                height: calc(100% - 68px);
+            }
+        }
+        .hc-card-action-box {
+            position: absolute;
+            height: auto;
+            margin: -24px;
+            width: 100%;
+            bottom: 24px;
+            padding: 20px 24px;
+            border-top: 1px solid #e9e9e9;
+            background-color: #f1f5f8;
+        }
+    }
+    &.is-header .el-card__body {
+        height: calc(100% - 69px);
+    }
+}
+.hc-card-box.el-card:not(.is-header) {
+    .el-card__body {
+        height: 100%;
+    }
+}
+</style>

+ 2 - 0
src/global/components/index.js

@@ -1,6 +1,8 @@
 import HcIcon from './hc-icon/index.vue'
+import HcCard from './hc-card/index.vue'
 
 //注册全局组件
 export const setupComponents = (App) => {
     App.component('HcIcon', HcIcon)
+    App.component('HcCard', HcCard)
 }

+ 23 - 12
src/layout/index.vue

@@ -36,9 +36,7 @@
                 </div>
             </el-header>
             <el-main class="hc-main-box" id="hc-main-box">
-                <el-scrollbar>
-                    <router-view v-if="reloadRouter"/>
-                </el-scrollbar>
+                <router-view v-if="reloadRouter"/>
             </el-main>
         </el-container>
     </el-container>
@@ -64,7 +62,7 @@ const useAppState = useAppStore()
 //路由参数
 const routerQuery = useRoutes?.query;
 const reloadRouter = ref(true)
-const BarMenuKey = routerQuery?.MenuBarKey || 'home-index';
+const BarMenuKey = routerQuery?.MenuBarKey ?? 'home-index';
 
 //主题和色调变量
 const HomeTheme = ref(useAppState.getHomeTheme);
@@ -102,9 +100,9 @@ watch(() => [
     useAppState.getHomeTheme,
     useAppState.getColor,
     useRoutes?.name,
-], ([projectContractArr,userMenus,BarMenuKey,theme,ColorVal,RouteName]) => {
+], ([projectContractArr,userMenus,BarMenuKeys,theme,ColorVal,RouteName]) => {
     MenuBarData.value = userMenus
-    MenuBarKey.value = BarMenuKey
+    MenuBarKey.value = BarMenuKeys ?? 'home-index'
     HomeTheme.value = theme
     AppColor.value = ColorVal
     RoutesName.value = RouteName
@@ -221,16 +219,17 @@ const logoClick = () => {
         }
         .el-cascader .el-input .el-input__wrapper {
             padding: 4px 15px;
-            background-color: #00000000;
-            border: 1px solid white;
-            box-shadow: initial;
+            border: 1px solid #00000000;
             border-radius: 100px;
+            background: #f1f5f8;
+            color: #202532;
+            box-shadow: 4px 4px 8px 0 rgba(54,92,167,0.15), -4px -4px 8px 0px #ffffff;
             .el-input__inner, .el-input__suffix {
-                color: white;
+                color: #202532;
             }
         }
         .el-cascader .el-input.is-focus .el-input__wrapper {
-            box-shadow: initial;
+            box-shadow: 4px 4px 8px 0 rgba(54,92,167,0.15), -4px -4px 8px 0px #ffffff;
         }
         .el-cascader .el-input .icon-arrow-down {
             font-size: 18px;
@@ -238,7 +237,19 @@ const logoClick = () => {
         }
     }
     &.home {
-
+        .hc-header-view .hc-header-content .hc-header-cascader-box {
+            .el-cascader .el-input .el-input__wrapper {
+                background-color: #00000000;
+                border: 1px solid white;
+                box-shadow: initial;
+                .el-input__inner, .el-input__suffix {
+                    color: white;
+                }
+            }
+            .el-cascader .el-input.is-focus .el-input__wrapper {
+                box-shadow: initial;
+            }
+        }
     }
 }
 </style>

+ 9 - 4
src/layout/layout.scss

@@ -98,9 +98,6 @@
     .hc-container-view {
         position: relative;
         z-index: 1;
-        &.home {
-            color: #ffffff;
-        }
         .hc-header-view {
             position: relative;
             display: flex;
@@ -109,7 +106,7 @@
             --el-header-height: 100px;
             .hc-header-page-name {
                 position: relative;
-                color: #edeef0;
+                color: #cccccc;
                 font-size: 22px;
             }
             .hc-header-content {
@@ -136,6 +133,14 @@
             position: relative;
             overflow: hidden;
             height: 100%;
+            --el-main-padding: 24px;
+            margin-top: -24px;
+        }
+        &.home {
+            color: #ffffff;
+            .hc-header-view .hc-header-page-name {
+                color: #edeef0;
+            }
         }
     }
 }

+ 8 - 249
src/layout/modules/ConfigBar.vue

@@ -2,84 +2,11 @@
     <div class="header-icon-bar" @click="toConfigClick">
         <HcIcon name="settings" class="header-icon"/>
     </div>
-    <!--div class="config-icon-bar" @click="ConfigBarActive">
-        <i class="cicon-set-o"/>
-    </div>
-    <n-drawer v-model:show="ConfigActive" :width="360" placement="right">
-        <n-drawer-content title="系统设置" class="theme-drawer-content">
-            <n-tabs class="theme-drawer-tabs" type="segment" v-model:value="UserTheme" @update:value="ThemeTabsUpdate">
-                <n-tab name="light">浅色主题</n-tab>
-                <n-tab name="auto">跟随系统</n-tab>
-                <n-tab name="dark">深色主题</n-tab>
-            </n-tabs>
-            <div class="theme-drawer-card">
-                <div class="title">主题色调</div>
-                <n-grid :x-gap="15" :y-gap="15" :cols="5">
-                    <n-grid-item v-for="item in ColorConfigData">
-                        <div class="color-config-item" :class="`bg-${item.name}`" @click="ColorConfigClick(item)">
-                            <i class="_icon-check" :class="UserColor.name === item.name?'cur':''"/>
-                        </div>
-                    </n-grid-item>
-                </n-grid>
-            </div>
-            <div class="theme-drawer-card">
-                <div class="title">首页主题</div>
-                <n-grid :x-gap="15" :y-gap="15" :cols="3">
-                    <n-grid-item v-for="item in homeConfigData">
-                        <div class="Home-theme-item" :class="HomeTheme.name === item.name?'cur':''" @click="homeConfigClick(item)">
-                            <img :src="item.bg" alt="" crossOrigin="anonymous">
-                        </div>
-                    </n-grid-item>
-                </n-grid>
-            </div>
-            <div class="theme-drawer-card">
-                <div class="title">截图设置</div>
-                <div class="hc-list-box">
-                    <div class="item">
-                        <div class="label">WebRtc:</div>
-                        <n-popover trigger="hover">
-                            <template #trigger>
-                                <n-switch size="small" v-model:value="webRtcVal" @update:value="webRtcUpdate" checked-value="1" unchecked-value="0"/>
-                            </template>
-                            <div>
-                                <div>是否启用WebRtc方式截图,WebRtc方式不会错版,但需要同意授权,调用的是浏览器的共享屏幕API (可能在某些浏览器上,不支持)。</div>
-                                <div class="mt-2 mb-2">不启用WebRtc时,采用 html2canvas 实现截图,但支持的并不好,容易出现错版。</div>
-                                <div>但目前市面上,网页截图的仅有这两种方案,推荐使用第三方截图来手动上传图片。</div>
-                            </div>
-                        </n-popover>
-                    </div>
-                    <div class="item">
-                        <div class="label">全屏截图:</div>
-                        <n-popover trigger="hover">
-                            <template #trigger>
-                                <n-switch size="small" v-model:value="fullScreenVal" @update:value="fullScreenUpdate" checked-value="1" unchecked-value="0"/>
-                            </template>
-                            <div>单击截全屏的启用状态</div>
-                        </n-popover>
-                    </div>
-                </div>
-            </div>
-
-        </n-drawer-content>
-    </n-drawer-->
 </template>
 
 <script setup>
-import {ref,watch,nextTick} from "vue";
-import {useAppStore} from "~src/store/index";
-import themeData from '~src/config/theme';
-import {useOsTheme} from "naive-ui";
-import {userConfigSave} from "~api/other";
 import router from '~src/router/index';
 
-//初始变量
-const useAppState = useAppStore()
-const UserTheme = ref(useAppState.getTheme)
-const UserColor = ref(useAppState.getColor)
-const HomeTheme = ref(useAppState.getHomeTheme)
-const webRtcVal = ref(useAppState.getShotWebRtc)
-const fullScreenVal = ref(useAppState.getFullScreen)
-
 //跳转到系统设置页面
 const toConfigClick = () => {
     router.push({
@@ -87,72 +14,6 @@ const toConfigClick = () => {
         query: {MenuBarKey: 'config'}
     });
 }
-
-//监听
-watch(() => [
-    useAppState.getTheme,
-    useAppState.getColor,
-    useAppState.getHomeTheme,
-    useAppState.getShotWebRtc,
-    useAppState.getFullScreen,
-], ([Theme,ColorVal,homeTheme,WebRtc,FullScreen]) => {
-    UserTheme.value = Theme
-    UserColor.value = ColorVal
-    HomeTheme.value = homeTheme
-    webRtcVal.value = WebRtc
-    fullScreenVal.value = FullScreen
-})
-
-//颜色
-const ColorConfigData = ref(themeData.color)
-
-//更改主色调
-const ColorConfigClick = (item) => {
-    useAppState.setColor(item)
-    userConfigSave({color: item?.name})
-    nextTick(() => {
-        UserColor.value = item
-    })
-    //主色调需要刷新页面
-    window?.location?.reload()
-}
-
-//更改主题设置
-const ThemeTabsUpdate = (val) => {
-    UserTheme.value = val
-    useAppState.setTheme(val)
-    if (val === 'auto') {
-        useAppState.setThemeVal(useOsTheme().value)
-    } else {
-        useAppState.setThemeVal(val)
-    }
-    document.documentElement.setAttribute('class',`theme-${val} ${val}`)
-    userConfigSave({theme: val})
-}
-
-//更改首页主题
-const homeConfigData = ref(themeData.home)
-const homeConfigClick = (item) => {
-    useAppState.setHomeTheme(item)
-    userConfigSave({homeTheme: item?.name})
-}
-
-//更改截图方式
-const webRtcUpdate = (val) => {
-    useAppState.setShotWebRtc(val)
-    userConfigSave({shotWebRtc: val})
-}
-//更改全屏截图方式
-const fullScreenUpdate = (val) => {
-    useAppState.setFullScreen(val)
-    userConfigSave({fullScreen: val})
-}
-
-//监听用户块菜单被点击
-const ConfigActive = ref(false)
-const ConfigBarActive = () => {
-    ConfigActive.value = true
-}
 </script>
 
 <style lang="scss" scoped>
@@ -165,119 +26,17 @@ const ConfigBarActive = () => {
     justify-content: center;
     align-items: center;
     cursor: pointer;
-    color: inherit;
     margin-right: 30px;
-    border: 1px solid white;
     font-size: 26px;
+    border: 1px solid #00000000;
+    background: #f1f5f8;
+    color: #202532;
+    box-shadow: 4px 4px 8px 0 rgba(54,92,167,0.15), -4px -4px 8px 0px #ffffff;
 }
-
-
-
-
-.config-icon-bar {
-    position: relative;
-    display: flex;
-    align-items: center;
-    height: 100%;
-    cursor: pointer;
-    padding: 0 20px;
+.hc-layout-box .hc-container-view.home .hc-header-view .hc-header-content .header-icon-bar {
+    border: 1px solid white;
     color: inherit;
-    transition: color 0.3s, background-color 0.3s;
-    i {
-        font-size: 24px;
-    }
-    &:hover {
-        color: var(--hc-primary);
-        background-color: var(--hc-primary-light-7);
-    }
-}
-.theme-drawer-card {
-    position: relative;
-    background: white;
-    padding: 20px;
-    border-radius: 3px;
-    margin-bottom: 24px;
-    .title {
-        position: relative;
-        font-size: 18px;
-        font-weight: 500;
-        margin-bottom: 20px;
-    }
-    .color-config-item {
-        position: relative;
-        display: flex;
-        justify-content: center;
-        align-items: center;
-        height: 30px;
-        width: 100%;
-        cursor: pointer;
-        border-radius: 3px;
-        transition: opacity 0.3s;
-        i {
-            display: none;
-        }
-        i.cur {
-            display: block;
-        }
-        &:hover {
-            opacity: 0.8;
-        }
-    }
-    .Home-theme-item {
-        display: flex;
-        justify-content: center;
-        align-items: center;
-        border: 2px solid #eee;
-        cursor: pointer;
-        transition: border-color 0.3s;
-        &.cur {
-            border-color: var(--hc-primary);
-        }
-        &:hover {
-            border-color: var(--hc-primary);
-        }
-        img {
-            width: 100%;
-            height: 100%;
-            object-fit: cover;
-        }
-    }
-}
-.home-theme-1, .home-theme-2, .home-theme-3 {
-    .config-icon-bar:hover {
-        color: white;
-        background-color: rgba(255, 255, 255, 0.1);
-    }
-}
-html.theme-dark {
-    .theme-drawer-card {
-        background: #222222;
-    }
-    .config-icon-bar:hover {
-        color: var(--hc-primary);
-        background-color: #212121;
-    }
+    box-shadow: initial;
+    background: initial;
 }
 </style>
-
-<style lang="scss">
-.n-drawer .n-drawer-content.theme-drawer-content .n-drawer-body-content-wrapper {
-    background: #F5F5F5;
-}
-.n-tabs.theme-drawer-tabs {
-    margin-bottom: 24px;
-    .n-tabs-rail {
-        background-color: #E8E8E8;
-    }
-}
-
-html.theme-dark {
-    .n-drawer .n-drawer-content.theme-drawer-content .n-drawer-body-content-wrapper {
-        background: var(--hc-bg-color);
-    }
-    .n-tabs.theme-drawer-tabs .n-tabs-rail {
-        background-color: #000000;
-    }
-}
-</style>
-

+ 10 - 2
src/layout/modules/HelpInfoBar.vue

@@ -128,11 +128,19 @@ const getAssetsHomeFile = (url) => {
     display: flex;
     justify-content: center;
     align-items: center;
-    border: 1px solid white;
     font-size: 26px;
     cursor: pointer;
-    color: inherit;
     margin-right: 30px;
+    border: 1px solid #00000000;
+    background: #f1f5f8;
+    color: #202532;
+    box-shadow: 4px 4px 8px 0 rgba(54,92,167,0.15), -4px -4px 8px 0px #ffffff;
+}
+.hc-layout-box .hc-container-view.home .hc-header-view .hc-header-content .header-icon-bar {
+    border: 1px solid white;
+    color: inherit;
+    box-shadow: initial;
+    background: initial;
 }
 
 .header-pover-menu-list {

+ 4 - 4
src/layout/modules/MenuBar.vue

@@ -204,7 +204,7 @@ const MenuClick = (item) => {
     .el-sub-menu:not(.is-active):hover,
     .el-menu-item:not(.is-active):hover {
         .hc-aside-menu-item {
-            background-color: var(--el-color-primary-light-7);
+            background-color: var(--el-color-primary-light-9);
             color: var(--el-color-primary);
         }
     }
@@ -272,12 +272,12 @@ const MenuClick = (item) => {
         vertical-align: initial;
     }
     .el-sub-menu:not(.is-active) .el-sub-menu__title:hover {
-        background-color: var(--el-color-primary-light-7);
+        background-color: var(--el-color-primary-light-9);
         color: var(--el-color-primary);
     }
     .el-menu-item:not(.is-active):hover {
         .hc-aside-menu-item {
-            background-color: var(--el-color-primary-light-7);
+            background-color: var(--el-color-primary-light-9);
             color: var(--el-color-primary);
         }
     }
@@ -287,7 +287,7 @@ const MenuClick = (item) => {
         }
     }
     .el-sub-menu.is-active .el-sub-menu__title {
-        background-color: var(--el-color-primary-light-7);
+        background-color: var(--el-color-primary-light-9);
         color: var(--el-color-primary);
     }
 }

+ 25 - 3
src/layout/modules/UserInfoBar.vue

@@ -95,7 +95,7 @@ const handleSelect = (key) => {
     align-items: center;
     height: 100%;
     cursor: pointer;
-    color: inherit;
+    padding-left: 24px;
     .user-avatar {
         height: 40px;
         border-radius: 50%;
@@ -104,13 +104,35 @@ const handleSelect = (key) => {
     .user-name {
         font-size: 16px;
         margin-left: 10px;
-        color: white;
+        color: #202532;
     }
     .arrow-icon {
         font-size: 30px;
-        color: #fff;
+        color: #202532;
+    }
+    &::before {
+        position: absolute;
+        content: '';
+        left: 0;
+        width: 0;
+        height: 24px;
+        border-left: 1px solid #ccd0de;
+    }
+}
+.hc-layout-box .hc-container-view.home .hc-header-view .hc-header-content .user-info-bar {
+    color: inherit;
+    .user-name {
+        color: white;
+    }
+    .arrow-icon {
+        color: white;
+    }
+    &::before {
+        border-left: 1px solid white;
     }
 }
+
+
 .hc-dropdown-item {
     display: flex;
     align-items: center;

+ 28 - 4
src/styles/app/main.scss

@@ -32,17 +32,41 @@ html, body, #app {
     }
 }
 
+.us-se-no {
+    user-select: none;
+}
+
 .el-button--large[block] {
     width: 100%;
 }
 
+//单选框、多选框
+.el-checkbox.size-xl .el-checkbox__inner,
+.el-radio.size-xl .el-radio__inner{
+    width: 18px;
+    height: 18px;
+}
+.el-checkbox.size-xl .el-checkbox__label,
+.el-radio.size-xl .el-radio__label {
+    font-size: 16px;
+    font-weight: 400;
+}
 
-.n-layout .n-layout-scroll-container {
-    background-color: #F5F5F5;
+.el-checkbox.size-xl .el-checkbox__inner::after {
+    height: 10px;
+    left: 6px;
+    top: 1px;
+}
+.el-radio.size-xl .el-radio__inner::after {
+    width: 8px;
+    height: 8px;
 }
 
-.us-se-no {
-    user-select: none;
+
+
+
+.n-layout .n-layout-scroll-container {
+    background-color: #F5F5F5;
 }
 
 .n-dropdown-menu .n-dropdown-option .n-dropdown-option-body .n-dropdown-option-body__prefix,

+ 87 - 0
src/styles/view/config.scss

@@ -0,0 +1,87 @@
+.hc-theme-box {
+    position: relative;
+    .item {
+        position: relative;
+        width: 233px;
+        height: 133px;
+        border: 1px solid #e8e8e8;
+        border-radius: 5px;
+        background-color: white;
+        display: inline-block;
+        margin-right: 20px;
+        .demo {
+            position: relative;
+            height: 90px;
+            background-color: white;
+            border-radius: 5px 5px 0 0;
+            img {
+                width: 100%;
+            }
+            &.auto {
+                background: linear-gradient(-45deg, #333333 115px,#ffffff 0);
+                img {
+                    opacity: 0.8;
+                }
+            }
+            &.light {
+                img {
+                    opacity: 0.3;
+                }
+            }
+            &.dark {
+                background-color: #333333;
+            }
+        }
+        .action {
+            position: relative;
+            background-color: white;
+            border-top: 1px solid #e8e8e8;
+            border-radius: 0 0 5px 5px;
+            padding: 0 16px;
+            transition: background-color 0.2s;
+            cursor: pointer;
+        }
+        &.active {
+            box-shadow: 4px 4px 8px 0 rgba(54,92,167,0.15), 0 0 8px 0 #ffffff;
+            .action {
+                cursor: default;
+                background-color: var(--el-color-primary-light-9);
+            }
+        }
+    }
+    &.color-box .item {
+        height: 137.5px;
+        .demo {
+            height: 94.5px;
+        }
+    }
+    &.home-bg-box .item {
+        display: inline-flex;
+        width: 160px;
+        height: 100px;
+        border-radius: 8px;
+        border: 3px solid #00000000;
+        transition: border-color 0.2s;
+        cursor: pointer;
+        img {
+            width: 100%;
+            border-radius: 5px;
+        }
+        &.active {
+            border: 3px solid var(--el-color-primary);
+        }
+    }
+}
+
+.hc-screenshot-box {
+    position: relative;
+    .item {
+        display: inline-flex;
+        align-items: center;
+        margin-right: 50px;
+        .label {
+            color: #50545E;
+        }
+    }
+}
+

+ 185 - 6
src/views/home/config.vue

@@ -1,21 +1,200 @@
 <template>
-    111
+    <HcCard>
+        <div class="text-lg font-medium mb-4">主题模式<span class="text-sm text-slate-400 font-light ml-4">深色模式还未适配,暂不推荐使用深色模式</span></div>
+        <div class="hc-theme-box mb-8">
+            <el-radio-group v-model="UserTheme" @change="ThemeTabsUpdate">
+                <template v-for="item in ThemeDatas">
+                    <div class="item" :class="UserTheme === item?.key ? 'active' : ''">
+                        <div class="demo" :class="item?.key">
+                            <img :src="ImgTheme" alt="">
+                        </div>
+                        <div class="action" @click="ThemeConfigClick(item)">
+                            <el-radio :label="item?.key" size="large" class="size-xl">{{item?.name}}</el-radio>
+                        </div>
+                    </div>
+                </template>
+            </el-radio-group>
+        </div>
+
+        <div class="text-lg font-medium mb-4">主题色</div>
+        <div class="hc-theme-box color-box mb-4">
+            <template v-for="(item,index) in ColorConfigData">
+                <div class="item" :class="UserColorNmae === item.name ? 'active' : ''" v-if="index < 5">
+                    <div class="demo" :class="`bg-${item.name}`">
+                        <img :src="ImgColor" alt="">
+                    </div>
+                    <div class="action" @click="ColorConfigClick(item)">
+                        <el-radio v-model="UserColorNmae" :label="item?.name" size="large" class="size-xl">{{item?.label}}</el-radio>
+                    </div>
+                </div>
+            </template>
+        </div>
+        <div class="hc-theme-box color-box mb-8">
+            <template v-for="(item,index) in ColorConfigData">
+                <div class="item" :class="UserColorNmae === item.name ? 'active' : ''" v-if="index >= 5">
+                    <div class="demo" :class="`bg-${item.name}`">
+                        <img :src="ImgColor" alt="">
+                    </div>
+                    <div class="action" @click="ColorConfigClick(item)">
+                        <el-radio v-model="UserColorNmae" :label="item?.name" size="large" class="size-xl">{{item?.label}}</el-radio>
+                    </div>
+                </div>
+            </template>
+        </div>
+
+        <div class="text-lg font-medium mb-4">首页背景</div>
+        <div class="hc-theme-box home-bg-box mb-8">
+            <template v-for="item in homeConfigData">
+                <div class="item" :class="HomeTheme.name === item?.name ? 'active' : ''" @click="homeConfigClick(item)">
+                    <img :src="item.bg" alt="" crossOrigin="anonymous">
+                </div>
+            </template>
+        </div>
+
+        <div class="text-lg font-medium mb-4">截图设置</div>
+        <div class="hc-screenshot-box mb-4">
+            <div class="item">
+                <div class="label">WebRtc:</div>
+                <el-popover placement="top-start" trigger="hover" :width="400">
+                    <template #reference>
+                        <el-switch v-model="webRtcVal" size="large" active-value="1" inactive-value="0" @change="webRtcUpdate"/>
+                    </template>
+                    <div>
+                        <div>是否启用WebRtc方式截图,WebRtc方式不会错版,但需要同意授权,调用的是浏览器的共享屏幕API (可能在某些浏览器上,不支持)。</div>
+                        <div class="mt-2 mb-2">不启用WebRtc时,采用 html2canvas 实现截图,但支持的并不好,容易出现错版。</div>
+                        <div>但目前市面上,网页截图的仅有这两种方案,推荐使用第三方截图来手动上传图片。</div>
+                    </div>
+                </el-popover>
+            </div>
+            <div class="item">
+                <div class="label">全屏截图:</div>
+                <el-popover placement="top-start" trigger="hover" :width="180">
+                    <template #reference>
+                        <el-switch v-model="fullScreenVal" size="large" active-value="1" inactive-value="0" @change="fullScreenUpdate"/>
+                    </template>
+                    <div>单击截全屏的启用状态</div>
+                </el-popover>
+            </div>
+        </div>
+        <template #action>
+            <el-button size="large">取消</el-button>
+            <el-button type="primary" size="large">保存配置</el-button>
+        </template>
+    </HcCard>
 </template>
 
 <script setup>
-import {ref,watch} from "vue";
+import {ref,watch,nextTick} from "vue";
 import {useAppStore} from "~src/store/index";
+import themeData from '~src/config/theme';
+import {userConfigSave} from "~api/other";
+import ImgTheme from "~src/assets/images/theme.png";
+import ImgColor from "~src/assets/images/color.png";
+import {utilsTo} from "vue-utils-plus"
+import {useOsTheme} from 'vooks'
 
-//变量
+//初始变量
 const useAppState = useAppStore()
-const HomeTheme = ref(useAppState.getHomeTheme);
+const { toColor } = utilsTo()
+const UserTheme = ref(useAppState.getTheme)
+const UserColor = ref(useAppState.getColor)
+const HomeTheme = ref(useAppState.getHomeTheme)
+const webRtcVal = ref(useAppState.getShotWebRtc)
+const fullScreenVal = ref(useAppState.getFullScreen)
+const UserColorNmae = ref(UserColor.value?.name || 'green')
 
 //监听
 watch(() => [
+    useAppState.getTheme,
+    useAppState.getColor,
     useAppState.getHomeTheme,
-], ([theme]) => {
-    HomeTheme.value = theme
+    useAppState.getShotWebRtc,
+    useAppState.getFullScreen,
+], ([Theme,ColorVal,homeTheme,WebRtc,FullScreen]) => {
+    UserTheme.value = Theme
+    UserColor.value = ColorVal
+    HomeTheme.value = homeTheme
+    webRtcVal.value = WebRtc
+    fullScreenVal.value = FullScreen
+    UserColorNmae.value = ColorVal?.name || 'green'
 })
+
+//颜色
+const ColorConfigData = ref(themeData.color)
+
+//更改主色调
+const ColorConfigClick = (item) => {
+    useAppState.setColor(item)
+    UserColorNmae.value = item?.name
+    userConfigSave({color: item?.name})
+    setUserColor(item?.color)
+    nextTick(() => {
+        UserColor.value = item
+    })
+}
+
+//设置主色调
+const setUserColor = (color) => {
+    const el = document.documentElement
+    el.style.setProperty('--el-color-primary', color)
+    // 设置 css 渐变 变量
+    const numArr = [3,5,7,8,9]
+    numArr.forEach(item => {
+        let amount = 0
+        if (item === 3) {
+            amount = 0.9
+        } else if (item === 5) {
+            amount = 0.7
+        } else if (item >= 7) {
+            amount = amount = (10 - item) / 10
+        }
+        const val = toColor('#FFFFFF', color , amount)
+        el.style.setProperty(`--el-color-primary-light-${item}`, val)
+    })
+    //生成深主色颜色
+    const val = toColor('#000000', color , 0.9)
+    el.style.setProperty('--el-color-primary-dark-2', val)
+}
+
+//更改主题设置
+const ThemeDatas = ref([
+    {key: 'auto', name: '跟随系统'},
+    {key: 'light', name: '浅色模式'},
+    {key: 'dark', name: '深色模式'}
+])
+const ThemeConfigClick = (item) => {
+    ThemeTabsUpdate(item?.key)
+}
+const ThemeTabsUpdate = (val) => {
+    UserTheme.value = val
+    useAppState.setTheme(val)
+    if (val === 'auto') {
+        useAppState.setThemeVal(useOsTheme().value)
+    } else {
+        useAppState.setThemeVal(val)
+    }
+    let colorName = UserColorNmae.value || 'green'
+    document.documentElement.setAttribute('class',`${val} color-${colorName}`)
+    userConfigSave({theme: val})
+}
+
+//更改首页主题
+const homeConfigData = ref(themeData.home)
+const homeConfigClick = (item) => {
+    useAppState.setHomeTheme(item)
+    userConfigSave({homeTheme: item?.name})
+}
+
+//更改截图方式
+const webRtcUpdate = (val) => {
+    useAppState.setShotWebRtc(val)
+    userConfigSave({shotWebRtc: val})
+}
+//更改全屏截图方式
+const fullScreenUpdate = (val) => {
+    useAppState.setFullScreen(val)
+    userConfigSave({fullScreen: val})
+}
 </script>
 
 <style lang="scss" scoped>