ZaiZai há 3 anos atrás
pai
commit
db193efc4f

+ 14 - 27
src/App.vue

@@ -8,7 +8,8 @@
 import {nextTick, ref, watch} from "vue";
 import {useAppStore} from "~src/store/index";
 import zhCn from 'element-plus/es/locale/lang/zh-cn'
-import {utilsTo} from "vue-utils-plus"
+import { ElMessage, ElLoading, ElNotification,ElMessageBox } from 'element-plus'
+import {setMainColor} from "~src/utils/tools";
 import config from '~src/config/index';
 import {useOsTheme} from 'vooks'
 
@@ -17,6 +18,16 @@ const appStore = useAppStore()
 const UserTheme = ref(appStore.getTheme)
 const appColor = ref(appStore.getColor);
 
+// 将ui的函数挂载在windows对象上
+function registerElTools () {
+    window['$ElLoading'] = ElLoading;
+    window['$ElMessageBox'] = ElMessageBox;
+    window['$ElMessage'] = ElMessage;
+    window['$ElNotification'] = ElNotification;
+}
+
+registerElTools()
+
 //监听
 watch(() => [
     appStore.getTheme,
@@ -30,7 +41,8 @@ watch(() => [
 
 nextTick(()=> {
     setUserTheme(appStore.getThemeVal)
-    setUserColor()
+    //设置主色调
+    setMainColor(appColor.value?.color)
     //打印开发版本号
     console.log(
         `%c 客户端 启动成功 %c 当前开发版本 V` + config.dev_version + `%c`,
@@ -51,29 +63,4 @@ const setUserTheme = (theme) => {
     }
     document.documentElement.setAttribute('class',`${theme} color-${colorName}`)
 }
-
-//设置主色调
-const { toColor } = utilsTo()
-const setUserColor = () => {
-    let color = appColor.value?.color || '#1ECC95';
-    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)
-}
 </script>

+ 3 - 3
src/api/request/index.js

@@ -48,18 +48,18 @@ axios.interceptors.response.use(res => {
     }
     //如果是401则跳转到登录页面
     if (status === 401) {
-        window.$message?.error(message || '身份失效!');
+        window.$ElMessage?.error(message || '身份失效!');
         router.push({path: '/login'})
         //store.dispatch('FedLogOut').then(() => router.push({path: '/login'}));
     }
     // 如果请求为非200否者默认统一处理
     if (status !== 200) {
-        window.$message?.error(message || '操作失败!');
+        window.$ElMessage?.error(message || '操作失败!');
         return Promise.reject(new Error(message))
     }
     return res;
 }, error => {
-    window.$message?.error('请求异常!');
+    window.$ElMessage?.error('请求异常!');
     return Promise.reject(new Error(error));
 });
 

+ 32 - 27
src/components/home/SearchInput.vue

@@ -1,16 +1,14 @@
 <template>
     <div class="home-input-view" :class="[searchKey?'tip-list':'', isFocus?'input-focus':'']">
         <div class="hc-home-search-input-box">
-            <input ref="inputRef" class="hc-home-search-input" :placeholder="placeholder" :value="modelValue" @blur="blur" @change="change" @focus="focus" @input="input" @keyup.enter="enter"/>
-            <div class="cu-input-icon" v-if="clearable" @click="clearClick">
-                <i class="_icon-close"/>
-            </div>
-            <div class="cu-input-icon" v-if="mic" @click="micClick">
-                <i class="cicon-mic"/>
+            <div class="cu-input-icon">
+                <HcIcon name="search"/>
             </div>
-            <div class="cu-input-icon search">
-                <i class="_icon-search"/>
+            <input ref="inputRef" class="hc-home-search-input" :placeholder="placeholder" :value="modelValue" @blur="blur" @change="change" @focus="focus" @input="input" @keyup.enter="enter"/>
+            <div class="cu-input-icon hover" v-if="clearable" @click="clearClick">
+                <HcIcon name="close"/>
             </div>
+            <div class="cu-input-search">搜索</div>
         </div>
         <div class="home-input-tip-list-box">
             <div class="tip-list-item">列表1</div>
@@ -49,11 +47,6 @@ const props = defineProps({
         type: String,
         default: ""
     },
-    //语音输入
-    mic: {
-        type: Boolean,
-        default: false
-    }
 })
 
 //变量
@@ -104,11 +97,6 @@ const clearClick = () => {
     clearable.value = false;
 }
 
-//语音被点击
-const micClick = () => {
-
-}
-
 //更新值
 const upModelValue = (value = '') => {
     searchKey.value = value
@@ -130,7 +118,7 @@ const upModelValue = (value = '') => {
         display: flex;
         align-items: center;
         background: white;
-        padding: 0 24px;
+        padding-left: 10px;
         border-radius: 50px;
         box-shadow: 0 0.5em 1em rgba(0, 0, 0,.15);
         .hc-home-search-input {
@@ -166,15 +154,32 @@ const upModelValue = (value = '') => {
             display: flex;
             justify-content: center;
             align-items: center;
-            font-size: 20px;
-            cursor: pointer;
+            font-size: 26px;
             color: #999999;
-            transition: color 0.3s;
-            &:hover {
-                color: #666666;
+            &.hover {
+                cursor: pointer;
+                transition: color 0.3s;
+                &:hover {
+                    color: #666666;
+                }
             }
             &.search:hover {
-                color: var(--hc-primary);
+                color: var(--el-color-primary);
+            }
+        }
+        .cu-input-search {
+            position: relative;
+            height: 55px;
+            display: flex;
+            padding: 0 26px;
+            align-items: center;
+            justify-content: center;
+            border-radius: 0 40px 40px 0;
+            background-color: var(--el-color-primary);
+            cursor: pointer;
+            transition: 0.2s;
+            &:hover {
+                background-color: var(--el-color-primary-dark-2);
             }
         }
     }
@@ -200,8 +205,8 @@ const upModelValue = (value = '') => {
             cursor: pointer;
             transition: color 0.3s, background-color 0.3s;
             &:hover {
-                color: var(--hc-primary);
-                background-color: var(--hc-primary-light-7);
+                color: var(--el-color-primary);
+                background-color: var(--el-color-primary-light-9);
             }
         }
         .tip-list-item + .tip-list-item {

+ 50 - 17
src/global/components/hc-card/index.vue

@@ -1,9 +1,16 @@
 <template>
-    <el-card class="hc-card-box" :class="[(isSlotHeader || title || isSlotExtra || extraText)?'is-header':'', ui]" shadow="never">
+    <el-card class="hc-card-box" shadow="never"
+             :class="[
+                 (isSlotHeader || title || isSlotExtra || extraText)?'is-header':'',
+                  isSlotSearchBar?'is-search-bar':'',
+                   `is-action-${actionSize}`,
+                   ui
+             ]"
+    >
         <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>
+                    <div class="title text-lg" v-if="!isSlotHeader && title">{{ title }}</div>
                     <slot v-if="isSlotHeader" name='header'/>
                 </div>
                 <div class="hc-card-header-extra" v-if="isSlotExtra || extraText">
@@ -12,6 +19,9 @@
                 </div>
             </div>
         </template>
+        <div class="hc-card-search-bar" v-if="isSlotSearchBar">
+            <slot name='search'/>
+        </div>
         <div class="hc-card-main-box" :class="isSlotAction?'is-action':''">
             <template v-if="scrollbar">
                 <el-scrollbar>
@@ -29,7 +39,7 @@
 </template>
 
 <script setup>
-import {useSlots} from "vue";
+import {ref,useSlots,watch} from "vue";
 const props = defineProps({
     ui: {
         type: String,
@@ -47,47 +57,58 @@ const props = defineProps({
         type: Boolean,
         default: true
     },
+    actionSize: {
+        type: [String,Number],
+        default: 'df'
+    },
 })
 
+const slots = useSlots()
+
 //判断<slot>是否有传值
-const isSlotHeader = !!useSlots().header;
-const isSlotExtra = !!useSlots().extra;
-const isSlotAction = !!useSlots().action;
+const isSlotHeader = ref(!!slots.header);
+const isSlotExtra = ref(!!slots.extra);
+const isSlotAction = ref(!!slots.action);
+const isSlotSearchBar = ref(!!slots.search);
 </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);
+    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;
+        padding: 14px 24px;
         border-bottom: 1px solid #e9e9e9;
     }
     .hc-card-header-box {
         position: relative;
         display: flex;
+        align-items: center;
         .hc-card-header {
             position: relative;
             flex: 1;
         }
         .hc-card-header-extra {
             position: relative;
-            flex: 1;
         }
     }
     .el-card__body {
         position: relative;
+        .hc-card-search-bar {
+            position: relative;
+            margin-bottom: 20px;
+        }
         .hc-card-main-box {
             position: relative;
             height: 100%;
             &.is-action {
-                height: calc(100% - 68px);
+                height: calc(100% - 57px);
             }
         }
         .hc-card-action-box {
@@ -102,7 +123,19 @@ const isSlotAction = !!useSlots().action;
         }
     }
     &.is-header .el-card__body {
-        height: calc(100% - 69px);
+        height: calc(100% - 57px);
+    }
+    &.is-action-df .el-card__body .hc-card-main-box.is-action {
+        height: calc(100% - 124px);
+    }
+    &.is-action-lg .el-card__body .hc-card-main-box.is-action {
+        height: calc(100% - 80px);
+    }
+    &.is-search-bar.is-action-df .el-card__body .hc-card-main-box {
+        height: calc(100% - 40px);
+        &.is-action {
+            height: calc(100% - 124px);
+        }
     }
 }
 .hc-card-box.el-card:not(.is-header) {

+ 61 - 0
src/global/components/hc-date-picker/index.vue

@@ -0,0 +1,61 @@
+<template>
+    <el-date-picker class="hc-date-picker" v-model="betweenDate" :type="isType" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :clearable="isClearable" :value-format="isFormat" @change="betweenDateUpdate"/>
+</template>
+
+<script setup>
+import { ref,watch } from "vue";
+const props = defineProps({
+    dates: {
+        type: Array,
+        default: () => ([])
+    },
+    clearable: {
+        type: Boolean,
+        default: false
+    },
+    type: {
+        type: String,
+        default: 'daterange'
+    },
+    format: {
+        type: String,
+        default: 'YYYY-MM-DD'
+    },
+})
+
+//初始变量
+const betweenDate = ref(props.dates)
+const isType = ref(props.type)
+const isFormat = ref(props.format)
+const isClearable = ref(props.clearable)
+
+//监听
+watch(() => [
+    props.dates,
+    props.type,
+    props.format,
+    props.clearable,
+], ([dates,type,format,clearable]) => {
+    betweenDate.value = dates
+    isType.value = type
+    isFormat.value = format
+    isClearable.value = clearable
+})
+
+//事件
+const emit = defineEmits(['change'])
+const betweenDateUpdate = (arr) => {
+    let res = arr ?? []
+    let obj = {start: null, end: null}
+    if (res.length > 0) {
+        obj = {start: res[0], end: res[1]}
+    }
+    emit('change', {val: obj, arr: res})
+}
+</script>
+
+<style lang="scss">
+.hc-date-picker.el-range-editor.el-input__wrapper {
+    width: 100%;
+}
+</style>

+ 10 - 3
src/global/components/hc-icon/index.vue

@@ -1,5 +1,5 @@
 <template>
-    <span class="material-symbols-rounded" :class="classVal">{{nameVal}}</span>
+    <span class="material-symbols-rounded" :class="[isFill?'fill':'', classVal]">{{nameVal}}</span>
 </template>
 
 <script setup>
@@ -12,19 +12,26 @@ const props = defineProps({
     name: {
         type: [String,Number],
         default: ''
-    }
+    },
+    fill: {
+        type: Boolean,
+        default: false
+    },
 })
 
 //初始变量
 const classVal = ref(props.ui)
 const nameVal = ref(props.name)
+const isFill = ref(props.fill)
 
 //监听
 watch(() => [
     props.ui,
     props.name,
-], ([ui,name]) => {
+    props.fill,
+], ([ui,name,fill]) => {
     classVal.value = ui;
     nameVal.value = name;
+    isFill.value = fill;
 })
 </script>

+ 109 - 0
src/global/components/hc-menu-simple/index.vue

@@ -0,0 +1,109 @@
+<template>
+    <div class="hc-menu-simple-box" :class="ui">
+        <template v-for="item in datas">
+            <div class="item-box" :class="item?.key === keysValue ? 'active' : ''" @click="MenuClick(item)">
+                <div class="icon-box" v-if="item?.icon">
+                    <HcIcon :name="item?.icon" fill/>
+                </div>
+                <div class="label-box truncate">{{item?.label}}</div>
+            </div>
+        </template>
+    </div>
+</template>
+
+<script setup>
+import { ref,watch } from "vue";
+const props = defineProps({
+    ui: {
+        type: String,
+        default: ''
+    },
+    datas: {
+        type: Array,
+        default: () => ([])
+    },
+    keys: {
+        type: [String,Number],
+        default: ''
+    },
+})
+
+//初始变量
+const keysValue = ref(props.keys)
+
+//监听
+watch(() => [
+    props.keys
+], ([keys]) => {
+    keysValue.value = keys
+})
+
+//事件
+const emit = defineEmits(['change'])
+const MenuClick = (item) => {
+    if (item?.key !== keysValue.value) {
+        emit('change', item)
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.hc-menu-simple-box {
+    position: relative;
+    padding: 20px;
+    .item-box {
+        position: relative;
+        display: flex;
+        align-items: center;
+        background: #f1f5f8;
+        border-radius: 6px;
+        padding: 8px 12px;
+        margin-bottom: 10px;
+        transition: 0.2s;
+        .icon-box {
+            position: relative;
+            width: 22px;
+            height: 22px;
+            border-radius: 5px;
+            background-color: var(--el-color-primary-light-8);
+            color: var(--el-color-primary-light-5);
+            font-size: 18px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            margin-right: 14px;
+            transition: 0.2s;
+        }
+        .label-box {
+            position: relative;
+            color: #838791;
+            font-size: 14px;
+            transition: 0.2s;
+        }
+        &:not(.active) {
+            cursor: pointer;
+        }
+        &:hover {
+            .icon-box {
+                color: #ffffff;
+                background-color: var(--el-color-primary);
+            }
+            .label-box {
+                color: #1a1a1a;
+                font-weight: 500;
+            }
+        }
+        &.active {
+            box-shadow: var(--hc-shadow);
+            .icon-box {
+                color: #ffffff;
+                background-color: var(--el-color-primary);
+            }
+            .label-box {
+                color: #1a1a1a;
+                font-weight: 500;
+            }
+        }
+    }
+}
+</style>

+ 67 - 0
src/global/components/hc-new-switch/index.vue

@@ -0,0 +1,67 @@
+<template>
+    <div class="hc-new-switch">
+        <template v-for="item in datas">
+            <div class="switch-bg" :class="item?.key == keyVal?'dots':''" @click="switchClick(item)">{{item?.name}}</div>
+        </template>
+    </div>
+</template>
+
+<script setup>
+import {ref,watch} from "vue";
+const props = defineProps({
+    datas: {
+        type: Array,
+        default: () => ([])
+    },
+    keys: {
+        type: [String,Number],
+        default: ''
+    },
+})
+
+//监听
+const keyVal = ref(props.keys)
+watch(() => [
+    props.keys
+], ([keys]) => {
+    keyVal.value = keys;
+})
+
+//事件
+const emit = defineEmits(['change'])
+const switchClick = (item) => {
+    if (item?.key == keyVal.value) return;
+    emit('change', item)
+}
+</script>
+
+<style lang="scss" scoped>
+.hc-new-switch {
+    isolation: isolate;
+    position: relative;
+    overflow: hidden;
+    height: 32px;
+    font-size: 14px;
+    background: #f1f5f8;
+    border-radius: 40px;
+    display: flex;
+    align-items: center;
+    padding: 0 4px;
+    box-shadow: 4px 4px 8px 0 rgba(54,92,167,0.15) inset, -4px -4px 8px 0 #ffffff inset;
+    .switch-bg {
+        color: #838791;
+        padding: 2px 12px;
+        border-radius: 80px;
+        height: 24px;
+        &.dots {
+            color: #ffffff;
+            background: var(--el-color-primary);
+            box-shadow: 4px 4px 8px 0 rgba(54,92,167,0.15), -3px -2px 8px 0 #ffffff;
+            transition: background 0.3s;
+        }
+        &:not(.dots) {
+            cursor: pointer;
+        }
+    }
+}
+</style>

+ 91 - 0
src/global/components/hc-page/index.vue

@@ -0,0 +1,91 @@
+<template>
+    <div class="card-page-box" :style="`justify-content:${align};`">
+        <el-pagination background layout="total, prev, pager, next, sizes, jumper" :page-sizes="sizes" :total="totalv" :page-size="sizev" :current-page="currentv" @size-change="sizesUpdate" @current-change="pagesUpdate"/>
+    </div>
+</template>
+
+<script setup>
+import {ref,watch} from "vue";
+const props = defineProps({
+    sizes: {
+        type: Array,
+        default: () => ([10, 20, 30, 40, 50])
+    },
+    pages: {
+        type: Object,
+        default: () => ({
+            current: 1,
+            size: 10,
+            total: 0
+        })
+    },
+    align: {
+        type: String,
+        default: 'flex-end'
+    },
+})
+
+//变量
+const objData = ref(JSON.parse(JSON.stringify(props.pages)))
+const currentv = ref(objData.value?.current ?? 0)
+const sizev = ref(objData.value?.size ?? 0)
+const totalv = ref(objData.value?.total ?? 0)
+
+//监听
+watch(() => [
+    props.pages?.current,
+    props.pages?.size,
+    props.pages?.total
+], ([current, size, total]) => {
+    currentv.value = current ?? 0;
+    sizev.value = size ?? 0;
+    totalv.value = total ?? 0;
+})
+
+//事件
+const emit = defineEmits(['change'])
+const pagesUpdate = (val) => {
+    //currentv.value = val;
+    emit('change', {
+        current: val,
+        size: sizev.value
+    })
+}
+
+//条数改变
+const sizesUpdate = (val) => {
+    //sizev.value = val;
+    emit('change', {
+        current: currentv.value,
+        size: val
+    })
+}
+</script>
+
+<style lang="scss" scoped>
+.card-page-box {
+    position: relative;
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+    flex: 1;
+}
+</style>
+
+<style lang="scss">
+.card-page-box {
+    .el-pagination.is-background .btn-next, .el-pagination.is-background .btn-prev, .el-pagination.is-background .el-pager li {
+        border: 1px solid #e2e2e2;
+    }
+    .el-pagination.is-background .el-pager li:not(.is-disabled).is-active {
+        border: 1px solid var(--el-color-primary);
+    }
+    .el-pagination .el-select .el-input {
+        width: 100px;
+    }
+    .el-pagination__sizes {
+        margin: 0 0 0 16px;
+    }
+}
+
+</style>

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

@@ -1,8 +1,16 @@
 import HcIcon from './hc-icon/index.vue'
 import HcCard from './hc-card/index.vue'
+import HcPages from './hc-page/index.vue'
+import HcMenuSimple from './hc-menu-simple/index.vue'
+import HcDatePicker from './hc-date-picker/index.vue'
+import HcNewSwitch from './hc-new-switch/index.vue'
 
 //注册全局组件
 export const setupComponents = (App) => {
     App.component('HcIcon', HcIcon)
     App.component('HcCard', HcCard)
+    App.component('HcPages', HcPages)
+    App.component('HcMenuSimple', HcMenuSimple)
+    App.component('HcDatePicker', HcDatePicker)
+    App.component('HcNewSwitch', HcNewSwitch)
 }

+ 47 - 12
src/layout/index.vue

@@ -24,7 +24,7 @@
         </el-aside>
         <el-container class="hc-container-view" :class="MenuBarKey === 'home-index'?'home':''">
             <el-header class="hc-header-view">
-                <div class="hc-header-page-name">首页</div>
+                <div class="hc-header-page-name">{{RoutesTitle}}</div>
                 <div class="hc-header-content">
                     <div class="hc-header-cascader-box">
                         <div class="project-name-box">{{projectInfo.projectAlias}} / {{contractInfo.name}}</div>
@@ -36,7 +36,13 @@
                 </div>
             </el-header>
             <el-main class="hc-main-box" id="hc-main-box">
-                <router-view v-if="reloadRouter"/>
+                <router-view v-slot="{ Component }" v-if="reloadRouter">
+                    <transition name="fade-transform">
+                        <keep-alive include="ProductList">
+                            <component :is="Component" />
+                        </keep-alive>
+                    </transition>
+                </router-view>
             </el-main>
         </el-container>
     </el-container>
@@ -44,9 +50,8 @@
 
 <script setup>
 import {onMounted, ref, nextTick, watch} from "vue";
-import {useRoute} from 'vue-router'
+import { useRouter, useRoute } from 'vue-router'
 import {useAppStore} from "~src/store/index";
-import router from '~src/router/index';
 import MenuBar from "./modules/MenuBar.vue"
 import HelpInfoBar from "./modules/HelpInfoBar.vue"
 import UserInfoBar from "./modules/UserInfoBar.vue"
@@ -56,13 +61,15 @@ import NameDark from "~src/assets/logo/name-dark.png";
 import NameWhite from "~src/assets/logo/name-white.png";
 
 //初始组合式
+const router = useRouter()
 const useRoutes = useRoute()
 const useAppState = useAppStore()
 
 //路由参数
 const routerQuery = useRoutes?.query;
 const reloadRouter = ref(true)
-const BarMenuKey = routerQuery?.MenuBarKey ?? 'home-index';
+const BarMenuKey = useRoutes?.name ?? 'home-index';
+const BarMenuTitle = useRoutes?.meta?.title ?? '';
 
 //主题和色调变量
 const HomeTheme = ref(useAppState.getHomeTheme);
@@ -70,7 +77,8 @@ const AppColor = ref(useAppState.getColor);
 
 //顶部菜单数据和相关处理
 const MenuBarKey = ref(BarMenuKey);
-const RoutesName = ref(useRoutes?.name);
+const RoutesName = ref(BarMenuKey);
+const RoutesTitle = ref(BarMenuTitle);
 const MenuBarData = ref(useAppState.getMenus)
 
 //项目合同段
@@ -96,16 +104,17 @@ onMounted(() => {
 watch(() => [
     useAppState.getProjectContract,
     useAppState.getMenus,
-    useRoutes?.query?.MenuBarKey,
     useAppState.getHomeTheme,
     useAppState.getColor,
     useRoutes?.name,
-], ([projectContractArr,userMenus,BarMenuKeys,theme,ColorVal,RouteName]) => {
+    useRoutes?.meta?.title,
+], ([projectContractArr, userMenus, theme, ColorVal, RouteName, RouteTitle]) => {
     MenuBarData.value = userMenus
-    MenuBarKey.value = BarMenuKeys ?? 'home-index'
     HomeTheme.value = theme
     AppColor.value = ColorVal
-    RoutesName.value = RouteName
+    RoutesName.value = RouteName ?? 'home-index'
+    MenuBarKey.value = RouteName ?? 'home-index'
+    RoutesTitle.value = RouteTitle ?? ''
     projectContractData(projectContractArr || []);
 })
 
@@ -189,7 +198,7 @@ const MenuBarChange = (item) => {
     MenuBarKey.value = item.key;
     router.push({
         name: item?.key || '/',
-        query: {MenuBarKey: key}
+        //query: {MenuBarKey: key}
     });
 }
 
@@ -223,7 +232,7 @@ const logoClick = () => {
             border-radius: 100px;
             background: #f1f5f8;
             color: #202532;
-            box-shadow: 4px 4px 8px 0 rgba(54,92,167,0.15), -4px -4px 8px 0px #ffffff;
+            box-shadow: var(--hc-shadow);
             .el-input__inner, .el-input__suffix {
                 color: #202532;
             }
@@ -252,4 +261,30 @@ const logoClick = () => {
         }
     }
 }
+
+.fade-enter-active,
+.fade-leave-active {
+    transition: opacity 0.5s ease;
+}
+
+.fade-enter-from,
+.fade-leave-to {
+    opacity: 0;
+}
+
+/* fade-transform */
+.fade-transform-leave-active,
+.fade-transform-enter-active {
+    transition: all 0.5s;
+}
+
+.fade-transform-enter-from {
+    opacity: 0;
+    transform: translateX(-30px);
+}
+
+.fade-transform-leave-to {
+    opacity: 0;
+    transform: translateX(30px);
+}
 </style>

+ 1 - 1
src/layout/layout.scss

@@ -139,7 +139,7 @@
         &.home {
             color: #ffffff;
             .hc-header-view .hc-header-page-name {
-                color: #edeef0;
+                color: #CCD0DE;
             }
         }
     }

+ 5 - 4
src/layout/modules/ConfigBar.vue

@@ -5,13 +5,14 @@
 </template>
 
 <script setup>
-import router from '~src/router/index';
+import { useRouter } from 'vue-router'
+
+const router = useRouter()
 
 //跳转到系统设置页面
 const toConfigClick = () => {
     router.push({
-        name: 'home-config',
-        query: {MenuBarKey: 'config'}
+        path: '/home/config'
     });
 }
 </script>
@@ -31,7 +32,7 @@ const toConfigClick = () => {
     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;
+    box-shadow: var(--hc-shadow);
 }
 .hc-layout-box .hc-container-view.home .hc-header-view .hc-header-content .header-icon-bar {
     border: 1px solid white;

+ 3 - 3
src/layout/modules/HelpInfoBar.vue

@@ -37,12 +37,12 @@
 
 <script setup>
 import {ref,watch,nextTick} from "vue";
-import {useRoute} from 'vue-router'
-import router from '~src/router/index';
+import { useRouter, useRoute } from 'vue-router'
 import {useAppStore} from "~src/store/index";
 import ScreenShot from "js-web-screen-shot";
 
 //初始变量
+const router = useRouter()
 const useRoutes = useRoute()
 const useAppState = useAppStore()
 const bubbleVal = ref(useAppState.getBubble);
@@ -134,7 +134,7 @@ const getAssetsHomeFile = (url) => {
     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;
+    box-shadow: var(--hc-shadow);
 }
 .hc-layout-box .hc-container-view.home .hc-header-view .hc-header-content .header-icon-bar {
     border: 1px solid white;

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

@@ -1,12 +1,12 @@
 <template>
     <el-menu class="hc-aside-menu" :default-active="curKey" :collapse="isCollapse" unique-opened>
-        <el-menu-item index="home-index">
+        <el-menu-item index="home-index" @click="MenuClick({key: 'home-index'})">
             <div class="hc-aside-menu-item">
                 <HcIcon name="home" class="hc-menu-icon" :ui="curKey === 'home-index'?'fill':''"/>
                 <div class="name">首页</div>
             </div>
         </el-menu-item>
-        <MenuItem :datas="datas" :cur="curKey" @change="MenuClick"/>
+        <MenuItem :datas="datas" :cur="curKey" :collapse="isCollapse" @change="MenuClick"/>
     </el-menu>
 </template>
 

+ 13 - 4
src/layout/modules/MenuItem.vue

@@ -5,7 +5,8 @@
                 <div class="hc-aside-menu-item">
                     <!-- v-if="item?.icon"-->
                     <HcIcon name="home" class="hc-menu-icon" :ui="curKey === item?.key?'fill':''"/>
-                    <div class="name truncate">{{item?.name.substring(0,2)}}</div>
+                    <div class="name truncate" v-if="isCollapse">{{ item?.name.substring(0,2) }}</div>
+                    <div class="name truncate" v-else>{{ item?.name }}</div>
                     <el-badge :value="20" v-if="item?.key === 'tasks'"/>
                 </div>
                 <HcIcon name="arrow_drop_down" ui="el-icon el-sub-menu__icon-arrow"/>
@@ -15,7 +16,8 @@
         <el-menu-item :index="item?.key" v-else @click="MenuClick(item)">
             <div class="hc-aside-menu-item">
                 <HcIcon name="home" class="hc-menu-icon" :ui="curKey === item?.key?'fill':''" v-if="item?.icon"/>
-                <div class="name truncate">{{item?.name.substring(0,2)}}</div>
+                <div class="name truncate" v-if="isCollapse">{{ item?.name.substring(0,2) }}</div>
+                <div class="name truncate" v-else>{{ item?.name }}</div>
                 <el-badge :value="12" v-if="item?.key === 'tasks-data' || item?.key === 'message-data'"/>
             </div>
         </el-menu-item>
@@ -34,15 +36,22 @@ const props = defineProps({
         type: String,
         default: ''
     },
+    collapse: {
+        type: Boolean,
+        default: false
+    },
 })
 //初始变量
 const curKey = ref(props.cur);
+const isCollapse = ref(props.collapse);
 
 //监听
 watch(() => [
-    props.cur
-], ([cur]) => {
+    props.cur,
+    props.collapse
+], ([cur,collapse]) => {
     curKey.value = cur
+    isCollapse.value = collapse
 })
 
 //事件

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

@@ -1,5 +1,5 @@
 <template>
-    <el-dropdown size="large" trigger="click">
+    <el-dropdown size="large">
         <div class="header-bar user-info-bar">
             <img class="user-avatar" :src="userInfo['avatar'] || avatarPng" :alt="userInfo['account']">
             <span class="user-name">{{userInfo['real_name']}}</span>
@@ -20,7 +20,7 @@
 
 <script setup>
 import {onMounted, ref, watch} from "vue";
-import router from '~src/router/index';
+import { useRouter } from 'vue-router'
 import {useAppStore} from "~src/store/index";
 import {getStore} from "~src/utils/lib/storage";
 import {calcDate} from "~src/utils/lib/date";
@@ -30,6 +30,7 @@ import avatarPng from '~src/assets/images/avatar.png';
 import {RefreshToken,LogOut} from "~sto/user";
 
 //变量
+const router = useRouter()
 const userStore = useAppStore()
 const userInfo = ref(userStore.getUserInfo);
 const refreshLock = ref(false);

+ 5 - 5
src/styles/app/_text.scss

@@ -73,28 +73,28 @@
         cursor: pointer;
         transition: color 0.2s ease-out;
         &:hover {
-            color: var(--hc-primary-light-2);
+            color: var(--el-color-primary);
         }
     }
     &-link {
         cursor: pointer;
-        color: var(--hc-primary);
+        color: var(--el-color-primary);
         transition: color 0.3s;
         outline: none;
         border: 0;
         &:hover {
-            color: var(--hc-primary-light-2);
+            color: var(--el-color-primary-dark-2);
         }
     }
     &-link-line {
         cursor: pointer;
-        color: var(--hc-primary);
+        color: var(--el-color-primary);
         text-decoration: underline;
         transition: color 0.3s;
         outline: none;
         border: 0;
         &:hover {
-            color: var(--hc-primary-light-2);
+            color: var(--el-color-primary-dark-2);
         }
     }
 }

+ 146 - 0
src/styles/app/element.scss

@@ -0,0 +1,146 @@
+//饿了么UI组件的样式重绘
+
+.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;
+}
+
+.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;
+}
+
+//消息提示
+.el-message {
+    --el-message-padding: 10px 20px;
+    border: 0;
+    box-shadow: 0 3px 6px -4px rgba(0, 0, 0,  0.012), 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
+}
+
+//按钮重绘
+.el-button[hc-btn] {
+    border-radius: 6px;
+    padding: 8px 20px;
+    font-weight: 400;
+    height: 40px;
+    border: 0;
+    box-shadow: var(--hc-shadow);
+    &.is-loading:not([class*='el-button--']) {
+        --el-button-bg-color: #f3f3f3;
+        --el-button-text-color: #8c9099;
+    }
+    &.is-disabled:not([class*='el-button--']) {
+        --el-button-disabled-text-color: #bfbfbf;
+        --el-button-disabled-bg-color: #f3f3f3;
+    }
+    &:not([class*='el-button--']) {
+        --el-button-bg-color: #f1f5f8;
+        --el-button-text-color: #838791;
+        --el-button-hover-text-color: #838791;
+        --el-button-active-text-color: #838791;
+        --el-button-hover-bg-color: var(--el-color-primary-light-9);
+        --el-button-active-bg-color: var(--el-color-primary-light-8);
+    }
+    &.el-button--primary.is-plain {
+        --el-button-text-color: var(--el-color-primary);
+        --el-button-bg-color: var(--el-color-primary-light-9);
+        --el-button-hover-text-color: var(--el-color-primary);
+        --el-button-hover-bg-color: var(--el-color-primary-light-8);
+        --el-button-active-text-color: var(--el-color-primary);
+        --el-button-active-bg-color: var(--el-color-primary-light-7);
+    }
+    .material-symbols-rounded {
+        font-size: 18px;
+        margin-right: 4px;
+    }
+}
+.el-button + .el-button {
+    margin-left: 20px;
+}
+
+//表单
+.el-form {
+    position: relative;
+    overflow: hidden;
+}
+
+//滚动条
+.el-scrollbar {
+    overflow: initial;
+    .el-scrollbar__bar.is-vertical {
+        right: -16px;
+    }
+    .el-scrollbar__bar.is-horizontal {
+        bottom: -20px;
+    }
+}
+
+//个人中心项目列表
+.hc-project-menu.el-menu {
+    --el-menu-level: 0;
+    background-color: initial;
+    border: initial;
+    .el-sub-menu {
+        border: 1px solid #e9e9e9;
+        border-radius: 3px;
+        margin-bottom: 20px;
+        .el-sub-menu__title .hc-menu-icon {
+            font-size: 20px;
+            margin-right: 10px;
+        }
+        &.is-opened .el-sub-menu__title {
+            color: var(--el-color-primary);
+            background-color: var(--el-color-primary-light-8);
+        }
+        .el-menu {
+            background-color: initial;
+        }
+        .el-menu-item {
+            padding-left: 50px;
+        }
+    }
+    .el-menu-item.is-active {
+        color: var(--el-menu-active-color);
+        background-color: var(--el-color-primary-light-9);
+        .hc-menu-icon {
+            position: absolute;
+            font-size: 22px;
+            left: 22px;
+        }
+    }
+}
+
+//表格
+.el-table[hc] {
+    border: 1px solid #e9e9e9;
+    --el-table-bg-color: initial;
+    --el-table-header-bg-color: #DAE8F3;
+    --el-table-header-text-color: #50545E;
+    --el-table-tr-bg-color: #F1F5F8;
+    --el-fill-color-lighter: #E7EEF4;
+    --el-table-text-color: #666666;
+    --el-table-row-hover-bg-color: var(--el-color-primary-light-9);
+    th.el-table__cell>.cell {
+        font-weight: 400;
+    }
+    .el-table__cell {
+        padding: 12px 0;
+    }
+}
+

+ 0 - 26
src/styles/app/main.scss

@@ -36,32 +36,6 @@ html, body, #app {
     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;
-}
-
-.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;
-}
-
 
 
 

+ 2 - 0
src/styles/app/theme.scss

@@ -6,6 +6,7 @@ html {
     --hc-body-bg-color: #F5F5F5;
     --hc-bg-color: #ffffff;
     --hc-text-color: #333333;
+    --hc-shadow: 4px 4px 8px 0 rgba(54,92,167,0.15), -4px -4px 8px 0 #ffffff;
 }
 
 //深色主题设置
@@ -15,6 +16,7 @@ html.dark {
     --hc-body-bg-color: #101014;
     --hc-bg-color: #18181c;
     --hc-text-color: rgba(255, 255, 255, 0.82);
+    --hc-shadow: 4px 4px 8px 0 rgba(54,92,167,0.15), -4px -4px 8px 0 #ffffff;
     .n-layout .n-layout-scroll-container {
         background-color: inherit;
     }

+ 1 - 0
src/styles/index.scss

@@ -1,4 +1,5 @@
 @import './app/color';
 @import './app/text';
 @import './app/main';
+@import './app/element';
 @import './app/theme';

+ 90 - 0
src/styles/user/index.scss

@@ -0,0 +1,90 @@
+.hc-layout-box {
+    position: relative;
+    height: 100%;
+    display: flex;
+    .hc-layout-left-box {
+        position: relative;
+        width: 260px;
+        height: 100%;
+        color: #1A1A1A;
+        border-radius: 10px;
+        background-color: #f1f5f8;
+        box-shadow: -2px 0 10px 0 rgba(32,37,50,0.03), 0 10px 21px 20px rgba(32,37,50,0.03);
+        .user-avatar-box {
+            position: relative;
+            text-align: center;
+            padding: 24px;
+            .user-avatar {
+                position: relative;
+                height: 100px;
+                width: 100px;
+                border-radius: 50%;
+                background: #d8d8d8;
+                margin: 24px auto auto;
+                border: 2px solid #ffffff;
+                box-shadow: 7px 7px 8px 0 rgba(54,92,167,0.15), -7px -7px 8px 0 #ffffff, 4px 4px 8px 0 rgba(54,92,167,0.15), -4px -4px 8px 0 #ffffff;
+                img {
+                    display: block;
+                    object-fit: cover;
+                    height: 100%;
+                    width: 100%;
+                    border-radius: 50%;
+                }
+                .user-avatar-upload {
+                    position: absolute;
+                    right: 0;
+                    bottom: 0;
+                    width: 24px;
+                    height: 24px;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    background: #f1f5f8;
+                    border-radius: 50%;
+                    color: #838791;
+                    cursor: pointer;
+                    box-shadow: 4px 4px 8px 0 rgba(54,92,167,0.15);
+                    transition: color 0.2s;
+                    &:hover {
+                        color: var(--el-color-primary);
+                    }
+                }
+            }
+            .user-name {
+                position: relative;
+                margin-top: 18px;
+                font-weight: bold;
+                color: #1a1a1a;
+                font-size: 16px;
+            }
+        }
+        .user-menu-box {
+            position: relative;
+            height: calc(100% - 230px);
+        }
+    }
+    .hc-layout-content-box {
+        flex: 1;
+        position: relative;
+        margin-left: 24px;
+        .basic-hight {
+            height: calc(100% - 75px);
+        }
+        .hc-card-foot-box {
+            position: absolute;
+            height: 80px;
+            width: 100%;
+            bottom: -24px;
+            background: #f1f5f8;
+            border-radius: 8px 8px 0 0;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            box-shadow: -2px 0 10px 0 rgba(32,37,50,0.03), 0 -10px 21px 3px rgba(32,37,50,0.03);
+        }
+        .foot-recycle {
+            position: relative;
+            display: flex;
+        }
+    }
+}

+ 17 - 57
src/styles/view/home.scss

@@ -1,22 +1,7 @@
-.home-foot-bg-box {
-    position: absolute;
-    bottom: 0;
-    left: 0;
-    right: 0;
-    top: 0;
-    z-index: 1;
-    display: flex;
-    .foot-bg-img {
-        width: 100%;
-        height: 100%;
-        object-fit: cover;
-    }
-}
 .home-styles-box {
     position: relative;
     height: 100%;
     overflow: hidden;
-    background: white;
     .hc-home-input-box {
         position: relative;
         height: 100%;
@@ -33,55 +18,30 @@
                 text-align: center;
                 font-size: 3.5rem;
                 color: white;
-                i + i {
-                    margin-left: 2rem;
+                img {
+                    height: 60px;
                 }
             }
             .home-link-box {
                 position: relative;
-                text-align: center;
-                margin-top: 2rem;
+                margin-top: 1.5rem;
                 padding: 0 4rem;
+                display: flex;
+                align-items: center;
+                font-size: 14px;
+                color: white;
                 .home-link {
-                    color: white;
                     cursor: pointer;
-                    transition: color 0.3s;
-                }
-                .home-link + .home-link {
-                    margin-left: 4rem;
-                }
-            }
-        }
-    }
-    &.theme-1, &.theme-2, &.theme-3 {
-        .hc-home-input-box .hc-home-content .home-link-box .home-link{
-            color: white;
-        }
-    }
-    &.theme-3 .hc-home-input-box .hc-home-content {
-        .hc-slogan-icon {
-            i + i {
-                color: var(--hc-primary);
-            }
-        }
-        .home-link-box .home-link{
-            &:hover {
-                color: var(--hc-primary);
-            }
-        }
-    }
-    &.theme-4, &.theme-5, &.theme-6 {
-        .hc-home-input-box .hc-home-content {
-            .hc-slogan-icon {
-                color: inherit;
-                i + i {
-                    color: var(--hc-primary);
-                }
-            }
-            .home-link-box .home-link{
-                color: #999999;
-                &:hover {
-                    color: var(--hc-primary);
+                    border: 1px solid white;
+                    border-radius: 50px;
+                    padding: 2px 10px;
+                    margin-left: 15px;
+                    transition: 0.2s;
+                    &:hover {
+                        color: var(--el-color-primary);
+                        border-color: var(--el-color-primary);
+                        background-color: var(--el-color-primary-light-9);
+                    }
                 }
             }
         }

+ 26 - 0
src/utils/tools.js

@@ -0,0 +1,26 @@
+import {utilsTo} from "vue-utils-plus"
+const { toColor } = utilsTo()
+
+//设置主色调
+export const setMainColor = (color) => {
+    color = color ?? '#1ECC95'
+    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)
+}

+ 49 - 34
src/views/home/config.vue

@@ -1,5 +1,5 @@
 <template>
-    <HcCard>
+    <HcCard actionSize="lg">
         <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">
@@ -77,25 +77,39 @@
             </div>
         </div>
         <template #action>
-            <el-button size="large">取消</el-button>
-            <el-button type="primary" size="large">保存配置</el-button>
+            <el-popover placement="top-start" trigger="hover" :width="180">
+                <template #reference>
+                    <el-button size="large" @click="CancelClick">取消</el-button>
+                </template>
+                <div>如果没有保存配置,下次登录后,将恢复到上次保存的配置</div>
+            </el-popover>
+            <el-popover placement="top" trigger="hover" :width="180">
+                <template #reference>
+                    <el-button type="primary" size="large" :loading="saveLoading" @click="SaveConfigClick">保存配置</el-button>
+                </template>
+                <div>下次登录后,会自动同步,并启用当前配置</div>
+            </el-popover>
         </template>
     </HcCard>
 </template>
 
 <script setup>
 import {ref,watch,nextTick} from "vue";
+import { useRouter, useRoute } from 'vue-router'
 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 {setMainColor} from "~src/utils/tools";
 import {useOsTheme} from 'vooks'
 
 //初始变量
+const router = useRouter()
+const useRoutes = useRoute()
 const useAppState = useAppStore()
-const { toColor } = utilsTo()
+
+//配置变量
 const UserTheme = ref(useAppState.getTheme)
 const UserColor = ref(useAppState.getColor)
 const HomeTheme = ref(useAppState.getHomeTheme)
@@ -119,6 +133,7 @@ watch(() => [
     UserColorNmae.value = ColorVal?.name || 'green'
 })
 
+
 //颜色
 const ColorConfigData = ref(themeData.color)
 
@@ -126,36 +141,13 @@ const ColorConfigData = ref(themeData.color)
 const ColorConfigClick = (item) => {
     useAppState.setColor(item)
     UserColorNmae.value = item?.name
-    userConfigSave({color: item?.name})
-    setUserColor(item?.color)
+    //设置主色调
+    setMainColor(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: '跟随系统'},
@@ -175,25 +167,48 @@ const ThemeTabsUpdate = (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})
+}
+
+//取消配置
+const CancelClick = () => {
+    router.back()
+}
+
+//保存配置
+const saveLoading = ref(false)
+const SaveConfigClick = async () => {
+    //发起请求
+    saveLoading.value = true
+    const { data } = await userConfigSave({
+        theme: UserTheme.value,
+        color: UserColorNmae.value,
+        homeTheme: HomeTheme.value?.name,
+        shotWebRtc: webRtcVal.value,
+        fullScreen: fullScreenVal.value
+    })
+    //判断状态
+    saveLoading.value = false
+    if (data && data.code === 200) {
+        window?.$ElMessage?.success('保存成功')
+        router.back()
+    } else {
+        window?.$ElMessage?.error('保存失败,请稍后再试')
+    }
 }
 </script>
 

+ 8 - 10
src/views/home/index.vue

@@ -1,20 +1,17 @@
 <template>
-    <div class="home-foot-bg-box">
-        <img class="foot-bg-img" :src="HomeTheme.bg" alt="" v-if="HomeTheme.bg" crossOrigin="anonymous">
-    </div>
-    <div class="home-styles-box" :class="HomeTheme.name">
+    <div class="home-styles-box">
         <div class="hc-home-input-box">
             <div class="hc-home-content">
                 <div class="hc-slogan-icon">
-                    <i class="hcicon-rangmeiyigeshuju"/>
-                    <i class="hcicon-a-gengyoujiazhi"/>
+                    <img class="logo" :src="ImgLogo" alt="">
                 </div>
                 <SearchInput placeholder="请输入您想查找的内容" v-model="searchKey" @input="SearchKeyInput"/>
                 <div class="home-link-box">
-                    <span class="home-link">资料填报</span>
-                    <span class="home-link">资料查询</span>
-                    <span class="home-link">操作手册</span>
-                    <span class="home-link">进度查询</span>
+                    <span>他们都在搜索:</span>
+                    <div class="home-link">资料填报</div>
+                    <div class="home-link">资料查询</div>
+                    <div class="home-link">操作手册</div>
+                    <div class="home-link">进度查询</div>
                 </div>
             </div>
         </div>
@@ -25,6 +22,7 @@
 import {ref,watch} from "vue";
 import {useAppStore} from "~src/store/index";
 import SearchInput from '~com/home/SearchInput.vue'
+import ImgLogo from "~src/assets/logo/logo-1.png";
 
 //变量
 const useAppState = useAppStore()

+ 5 - 6
src/views/login/index.vue

@@ -56,7 +56,7 @@
                     <div :class="account?'':'active'" @click="accountClick">游客登录</div>
                 </div>
                 <div class="form-box mt-4" v-if="account">
-                    <el-form ref="formRef" :model="formValue" :rules="formRules" label-width="auto" label-position="left" size="large">
+                    <el-form ref="formRef" :model="formValue" :rules="formRules" label-width="0px" label-position="left" size="large">
                         <el-form-item prop="username">
                             <el-input v-model="formValue.username" placeholder="账号" clearable/>
                         </el-form-item>
@@ -94,7 +94,6 @@
 
 <script setup>
 import {ref} from "vue";
-import { ElMessageBox } from 'element-plus'
 import router from '~src/router/index';
 import {useAppStore} from "~src/store/index";
 import {useAppLogin} from "~sto/user";
@@ -132,13 +131,13 @@ const passwordKeyUp = (e) => {
 
 //登录
 const loading = ref(false)
-const formValidateClick = async () => {
-    await formRef.value.validate((valid) => {
+const formValidateClick = () => {
+    formRef.value.validate((valid) => {
         if (valid) {
             loading.value = true;
             useAppLogin(formValue.value).then(({data}) => {
                 loading.value = false;
-                window.$message?.success('登录成功');
+                window?.$ElMessage?.success('登录成功');
                 router.push({path: '/home/index'});
             }).catch(() => {
                 loading.value = false;
@@ -168,7 +167,7 @@ const touristsValidateClick = async () => {
 //忘记密码
 const clickableClick = () => {
     const val = '<div style="font-size: 16px;">忘记密码请不要紧张,联系您项目上的专属客服人员电话 <span style="color:#1ECC95;">18423665354</span> ,提供身份证明信息即可初始化密码,建议初始化之后由您单独去更改密码</div>'
-    ElMessageBox.alert(val, '联系项目客服', {
+    window?.$ElMessageBox?.alert(val, '联系项目客服', {
         confirmButtonText: '确定',
         dangerouslyUseHTMLString: true
     })

+ 319 - 328
src/views/user/index.vue

@@ -2,141 +2,211 @@
     <div class="hc-layout-box">
         <div class="hc-layout-left-box">
             <div class="user-avatar-box">
-                <n-upload :action="action" :accept="accept" :headers="getTokenHeader()" :data="upData" @before-upload="beforeUpload" @finish="uploadFinish">
-                    <n-avatar round :size="110" :src="userInfo['avatar'] || avatarPng"/>
-                </n-upload>
-                <div class="text-sm text-gray-400">点击头像更换</div>
+                <div class="user-avatar" v-loading="avatarLoading">
+                    <img :src="userInfo['avatar'] || avatarPng" alt="" crossOrigin="anonymous"/>
+                    <div class="user-avatar-upload">
+                        <el-upload class="upload-dom" :action="action" :accept="accept" :headers="getTokenHeader()" :data="upData" :show-file-list="false" :on-success="uploadFinish" :on-error="uploadError" :before-upload="beforeUpload">
+                            <HcIcon name="photo_camera" fill/>
+                        </el-upload>
+                    </div>
+                </div>
+                <div class="user-name truncate">{{ userInfo['real_name'] || '游客' }}</div>
             </div>
             <div class="user-menu-box">
-                <n-menu :options="menuOptions" v-model:value="menuKey" accordion @update:value="handleMenuValue"/>
+                <el-scrollbar>
+                    <HcMenuSimple :datas="menuOptions" :keys="menuKey" @change="handleMenuValue"/>
+                </el-scrollbar>
             </div>
         </div>
         <div class="hc-layout-content-box">
-            <n-card class="hc-card-overflow-p-box" :segmented="{content: true}">
-                <template #header>
-                    <div class="hc-card-header flex items-center">
-                        <div class="card-title" v-if="menuKey === 'basic'">基础信息</div>
-                        <div class="card-title" v-if="menuKey === 'password'">密码设置</div>
-                        <div class="card-title" v-if="menuKey === 'project'">参见项目</div>
-                        <div class="card-title" v-if="menuKey === 'log'">操作日志</div>
-                        <div class="card-title" v-if="menuKey === 'recycle'">回收站</div>
-                    </div>
+            <HcCard :title="menuItem.label" :ui="basicHight?'basic-hight':''" v-if="menuKey !== 'log' && menuKey !== 'recycle'">
+                <template #extra>
+                    <span class="text-link" v-if="menuKey === 'basic' && !basicFormEdit" @click="basicFormEditClick">编辑</span>
+                    <span class="text-link" v-if="menuKey === 'project'" @click="setDefaultProjectClick">设置默认项目</span>
                 </template>
-                <template #header-extra>
-                    <n-button type="primary" strong secondary class="px-4" v-if="menuKey === 'project'" @click="setDefaultProjectClick">设置默认项目</n-button>
-                    <HcTabs :datas="tabTypeTab" :keys="tabTypeKey" @change="tabTypeChange" v-if="menuKey === 'recycle'"/>
+                <template v-if="menuKey === 'basic'">
+                    <el-form ref="formUserRef" :model="formUserModel" :rules="formUserRules" size="large" label-position="top">
+                        <el-row :gutter="20">
+                            <el-col :span="12">
+                                <el-form-item label="用户名称" :prop="basicFormEdit?'real_name':''">
+                                    <el-input v-model="formUserModel.real_name" placeholder="请输入用户名称" :disabled="!basicHight"/>
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="12">
+                                <el-form-item label="登录账号">
+                                    <el-input v-model="formUserModel.account" placeholder="" disabled/>
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="12">
+                                <el-form-item label="身份证号">
+                                    <el-input v-model="formUserModel.idNumber" placeholder="" disabled/>
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="12">
+                                <el-form-item label="绑定手机">
+                                    <el-input v-model="formUserModel.phone" placeholder="" disabled/>
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="12">
+                                <el-form-item label="所属角色">
+                                    <el-input v-model="formUserModel.role_name" placeholder="" disabled/>
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="12">
+                                <el-form-item label="所属部门">
+                                    <el-input v-model="formUserModel.deptId" placeholder="" disabled/>
+                                </el-form-item>
+                            </el-col>
+                            <el-col :span="12">
+                                <el-form-item label="CA签字体">
+                                    <!--el-image style="height: 60px" src="" :preview-src-list="srcList" :initial-index="0" fit="cover"/-->
+                                    开发中...
+                                </el-form-item>
+                            </el-col>
+                        </el-row>
+                    </el-form>
                 </template>
-                <div class="card-form-box" v-if="menuKey === 'basic'">
-                    <n-form ref="formUserRef" :model="formUserModel" :rules="formUserRules" label-placement="left" label-width="auto" size="large">
-                        <n-form-item label="用户名称" path="nick_name">
-                            <n-input v-model:value="formUserModel.real_name" placeholder="请输入用户名称"/>
-                        </n-form-item>
-                        <n-form-item label="登录账号">
-                            <n-input v-model:value="formUserModel.account" disabled placeholder=""/>
-                        </n-form-item>
-                        <n-form-item label="身份证号">
-                            <n-input v-model:value="formUserModel.idNumber" disabled placeholder=""/>
-                        </n-form-item>
-                        <n-form-item label="绑定手机">
-                            <n-input v-model:value="formUserModel.phone" disabled placeholder=""/>
-                        </n-form-item>
-                        <n-form-item label="所属角色">
-                            <n-input v-model:value="formUserModel.role_name" disabled placeholder=""/>
-                        </n-form-item>
-                        <n-form-item label="所属部门">
-                            <n-input v-model:value="formUserModel.deptId" disabled placeholder=""/>
-                        </n-form-item>
-                        <n-form-item label="CA签字体">
-                            <n-image height="60" src="https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg"/>
-                        </n-form-item>
-                    </n-form>
-                </div>
-                <div class="card-form-box" v-if="menuKey === 'password'">
-                    <n-form ref="formUserPassRef" :model="formUserPassModel" :rules="formUserPassRules" label-placement="left" label-width="auto" size="large">
-                        <n-form-item label="原始密码" path="oldPassword">
-                            <n-input v-model:value="formUserPassModel.oldPassword" placeholder="请输入原始密码"/>
-                        </n-form-item>
-                        <n-form-item label="新的密码" path="newPassword">
-                            <n-input v-model:value="formUserPassModel.newPassword" placeholder="请输入新的密码"/>
-                        </n-form-item>
-                        <n-form-item label="确认新密码" path="newPassword1">
-                            <n-input v-model:value="formUserPassModel.newPassword1" placeholder="请输入确认新密码"/>
-                        </n-form-item>
-                    </n-form>
-                </div>
-                <div class="card-form-box project-menu" v-if="menuKey === 'project'">
-                    <n-menu ref="projectMenuRef" :options="projectMenu" :icon-size="16" v-model:value="projectKey" accordion @update:value="projectMenuValue"/>
-                </div>
-                <div class="card-form-box" v-if="menuKey === 'log'">
+                <template v-if="menuKey === 'password'">
+                    <el-form ref="formUserPassRef" :model="formUserPassModel" :rules="formUserPassRules" size="large" label-position="top" style="max-width: 400px; margin: auto;">
+                        <el-form-item label="原始密码" prop="oldPassword">
+                            <el-input type="password" v-model="formUserPassModel.oldPassword" placeholder="请输入原始密码" show-password/>
+                        </el-form-item>
+                        <el-form-item label="新的密码" prop="newPassword">
+                            <el-input type="password" v-model="formUserPassModel.newPassword" placeholder="请输入新的密码" show-password/>
+                        </el-form-item>
+                        <el-form-item label="确认新密码" prop="newPassword1">
+                            <el-input type="password" v-model="formUserPassModel.newPassword1" placeholder="请输入确认新密码" show-password/>
+                        </el-form-item>
+                    </el-form>
+                </template>
+                <template v-if="menuKey === 'project'">
+                    <el-menu :default-active="projectKey" class="hc-project-menu" unique-opened>
+                        <el-sub-menu v-for="item in projectContractArr" :index="item.id">
+                            <template #title>
+                                <HcIcon name="topic" class="hc-menu-icon"/>
+                                <span>{{ item?.name }}</span>
+                            </template>
+                            <el-menu-item v-for="items in item?.contractInfoList ?? []" :index="items?.id" @click="projectMenuValue(item,items)">
+                                <HcIcon name="star" class="hc-menu-icon" fill v-if="projectKey === items?.id"/>
+                                <span>{{ items?.name }}</span>
+                            </el-menu-item>
+                        </el-sub-menu>
+                    </el-menu>
+                </template>
+            </HcCard>
+            <div class="hc-card-foot-box" v-if="basicFormEdit">
+                <el-button type="primary" hc-btn :loading="saveUserLoading" @click="saveUserInfoClick">
+                    <HcIcon name="save"/>
+                    <span>保存</span>
+                </el-button>
+                <el-button hc-btn @click="cancelUserClick">
+                    <HcIcon name="cancel"/>
+                    <span>取消</span>
+                </el-button>
+            </div>
+            <HcCard :title="menuItem.label" v-if="menuKey === 'log'">
+                <template #search>
                     <div class="flex items-center">
                         <div class="w-32">
-                            <n-select v-model:value="searchLogForm.operationModule" :options="operationModuleData" placeholder="业务模块" clearable label-field="dictValue" value-field="dictKey" @update:value="BusinessModuleValue"/>
+                            <el-select v-model="searchLogForm.operationModule" placeholder="业务模块" clearable @change="BusinessModuleValue">
+                                <el-option v-for="item in operationModuleData" :key="item.value" :label="item?.dictValue" :value="item?.dictKey"/>
+                            </el-select>
                         </div>
                         <div class="w-32 ml-2">
-                            <n-select v-model:value="searchLogForm.operationView" :options="operationViewData" placeholder="页面" clearable label-field="dictValue" value-field="dictKey" @update:value="OperationViewValue"/>
+                            <el-select v-model="searchLogForm.operationView" placeholder="页面" clearable @change="OperationViewValue">
+                                <el-option v-for="item in operationViewData" :key="item.value" :label="item?.dictValue" :value="item?.dictKey"/>
+                            </el-select>
                         </div>
-                        <div class="w-44 ml-2">
-                            <n-select v-model:value="searchLogForm.operationType" :options="operationTypeData" placeholder="操作类型" clearable label-field="dictValue" value-field="dictKey"/>
+                        <div class="w-40 ml-2">
+                            <el-select v-model="searchLogForm.operationType" placeholder="操作类型" clearable>
+                                <el-option v-for="item in operationTypeData" :key="item.value" :label="item?.dictValue" :value="item?.dictKey"/>
+                            </el-select>
                         </div>
-                        <div class="w-24 ml-2">
-                            <n-select v-model:value="searchLogForm.operationMedium" :options="deviceData" placeholder="设备" clearable/>
+                        <div class="w-20 ml-2">
+                            <el-select v-model="searchLogForm.operationMedium" placeholder="设备" clearable>
+                                <el-option v-for="item in deviceData" :key="item.value" :label="item?.label" :value="item?.value"/>
+                            </el-select>
                         </div>
-                        <div class="w-72 ml-2">
-                            <n-date-picker v-model:formatted-value="betweenTime" value-format="yyyy-MM-dd" type="daterange" clearable @update:value="betweenTimeUpdate"/>
+                        <div class="w-64 ml-2">
+                            <HcDatePicker :dates="betweenTime" clearable @change="betweenDateUpdate"/>
                         </div>
                         <div class="w-60 ml-2">
-                            <n-input v-model:value="searchLogForm.queryValue" type="text" placeholder="请输入名称关键词检索" @keyup="keyUpEvent" clearable/>
+                            <el-input v-model="searchLogForm.queryValue" placeholder="请输入名称关键词检索" clearable @keyup="keyUpEvent"/>
                         </div>
                         <div class="ml-2">
-                            <n-button type="primary" class="px-5" @click="searchClick">搜索</n-button>
+                            <el-button type="primary" @click="searchClick">搜索</el-button>
                         </div>
                     </div>
-                    <div class="card-data-table">
-                        <n-data-table :columns="logTableColumns" :data="logTableData" :single-line="false" :loading="logTableLoading" striped/>
-                    </div>
-                </div>
-                <div class="card-form-box" v-if="menuKey === 'recycle'">
-                    <n-data-table :columns="recycleTableColumns" :data="recycleTableData" :row-key="row => row.name" :single-line="false" striped/>
-                </div>
-                <template #action v-if="menuKey !== 'project'">
-                    <div class="text-center" v-if="menuKey === 'basic' || menuKey === 'password'">
-                        <n-button type="primary" class="px-8" :loading="saveUserLoading" @click="saveUserInfoClick" v-if="menuKey === 'basic'">保存</n-button>
-                        <n-button type="primary" class="px-8" :loading="userPassLoading" @click="saveUserPassClick" v-if="menuKey === 'password'">保存</n-button>
-                    </div>
-                    <HcPage :pages="searchLogForm" @change="pageLogChange" v-if="menuKey === 'log'"/>
-                    <div class="foot-recycle" v-if="menuKey === 'recycle'">
-                        <n-button type="primary" class="px-8">恢复</n-button>
-                        <HcPage :pages="searchRecycleForm" @change="pageRecycleChange"/>
+                </template>
+                <el-table hc :data="logTableData" :loading="logTableLoading" stripe>
+                    <el-table-column prop="num" label="序号" width="80">
+                        <template #default="scope">
+                            {{scope.$index + 1}}
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="operationModule" label="业务模块" width="180"/>
+                    <el-table-column prop="operationTypeValue" label="操作类型"/>
+                    <el-table-column prop="operationMedium" label="设备" width="80"/>
+                    <el-table-column prop="operationContent" label="操作内容" />
+                    <el-table-column prop="createTime" label="操作时间" width="180"/>
+                </el-table>
+                <template #action>
+                    <HcPages :pages="searchLogForm" @change="pageLogChange"/>
+                </template>
+            </HcCard>
+            <HcCard :title="menuItem.label" v-if="menuKey === 'recycle'" actionSize="lg">
+                <template #extra>
+                    <HcNewSwitch :datas="tabTypeTab" :keys="tabTypeKey" @change="tabTypeChange"/>
+                </template>
+                <el-table ref="recycleTableRef" hc :data="recycleTableData" stripe @selection-change="recycleTableSelectionChange">
+                    <el-table-column type="selection" width="50" />
+                    <el-table-column prop="num" label="序号" width="80">
+                        <template #default="scope">
+                            {{scope.$index + 1}}
+                        </template>
+                    </el-table-column>
+                    <el-table-column prop="title" label="删除内容"/>
+                    <el-table-column prop="name" label="父节点名称"/>
+                    <el-table-column prop="date" label="删除时间"/>
+                </el-table>
+                <template #action>
+                    <div class="foot-recycle">
+                        <el-button type="primary" hc-btn>
+                            <HcIcon name="reply"/>
+                            <span>恢复</span>
+                        </el-button>
+                        <HcPages :pages="searchRecycleForm" @change="pageRecycleChange"/>
                     </div>
                 </template>
-            </n-card>
+            </HcCard>
         </div>
     </div>
 </template>
 
 <script setup>
-import {ref, watch, onMounted, nextTick} from "vue";
-import {useRoute} from 'vue-router'
-import router from '~src/router/index';
+import {ref, watch, onMounted} from "vue";
+import {useRouter, useRoute} from 'vue-router'
 import {useAppStore} from "~src/store/index";
 import {deepClone} from "~src/utils/lib/util";
-import {HcIcon} from "~src/plugins/renderele";
 import avatarPng from '~src/assets/images/avatar.png';
 import {getTokenHeader} from '~src/api/request/header';
 import userApi from "~api/userInfo/index"
-import HcPage from "~com/plugins/naive/HcPage.vue"
-import HcTabs from "~com/plugins/naive/HcTabs.vue"
-import tasksDataApi from '~api/tasks/data';
+import {utilsArray} from "vue-utils-plus"
 import md5 from 'js-md5';
 
-//变量
+//初始变量
+const router = useRouter()
 const useRoutes = useRoute()
 const useAppState = useAppStore()
+const { getIndex } = utilsArray()
+
+//全局变量信息
 const userInfo = ref(useAppState.getUserInfo);
 const projectId = ref(useAppState.getProjectId);
 const contractId = ref(useAppState.getContractId);
 const projectContractArr = ref(useAppState.getProjectContract);
 
+//路由参数数据
 const routerQuery = useRoutes?.query;
 let MenuType = routerQuery?.MenuType || 'basic'
 
@@ -153,77 +223,95 @@ watch(() => [
     projectContractArr.value = projectContract
 })
 
+
 //上传组件参数
 const action = '/api/blade-resource/oss/endpoint/put-file';
 const accept = 'image/png,image/jpg,image/jpeg';
 const upData = ref({})
-
 //上传前
-const loadingReactive = ref(null)
+const avatarLoading = ref(false)
 const beforeUpload = () => {
-    loadingReactive.value = window?.$message?.loading('上传中...', {duration: 0})
+    avatarLoading.value = true
     return true
 }
-
 //上传完成
-const uploadFinish = ({event}) => {
-    const res = JSON.parse(event?.target?.response);
-    const link = res?.data?.link || '';
+const uploadFinish = (res) => {
+    const link = res?.data?.link ?? '';
+    const user_id = userInfo.value?.user_id ?? '';
     if (link) {
         userApi.updateUserInfo({
             avatar: link,
-            id: userInfo.value?.user_id
+            id: user_id
         }).then(({data}) => {
-            loadingReactive.value.destroy()
-            loadingReactive.value = null
+            avatarLoading.value = false
             if (data.code === 200) {
-                window?.$message?.success('更换头像成功')
+                window?.$ElMessage?.success('更换头像成功')
                 userInfo.value.avatar = link
                 useAppState.setUserInfo(userInfo.value)
             }
         }).catch(() => {
-            loadingReactive.value.destroy()
-            loadingReactive.value = null
+            avatarLoading.value = false
         })
     } else {
-        window?.$message?.warning('上传头像异常,请稍后再试')
-        loadingReactive.value.destroy()
-        loadingReactive.value = null
+        window?.$ElMessage?.warning('上传头像异常,请稍后再试')
+        avatarLoading.value = false
     }
 }
+//上传失败
+const uploadError = () => {
+    avatarLoading.value = false
+    window?.$ElMessage?.warning('上传头像失败')
+}
+
 
 //左侧菜单
 const menuKey = ref(MenuType)
+const menuItem = ref({})
 const menuOptions = ref([
-    {key: 'basic', label: '基础信息', icon: HcIcon('cicon-my-o')},
-    {key: 'password', label: '密码设置', icon: HcIcon('cicon-lock-open')},
-    {key: 'project', label: '参建项目', icon: HcIcon('cicon-folder-o')},
-    {key: 'log', label: '操作日志', icon: HcIcon('cicon-file-text-o')},
-    {key: 'recycle', label: '回收站', icon: HcIcon('cicon-delete-line-o')},
+    {key: 'basic', label: '基础信息', icon: 'person'},
+    {key: 'password', label: '密码设置', icon: 'lock_open'},
+    {key: 'project', label: '参建项目', icon: 'home_storage'},
+    {key: 'log', label: '操作日志', icon: 'package'},
+    {key: 'recycle', label: '回收站', icon: 'delete'},
 ]);
-
+//获取菜单对象数据
+const menuObjItem = () => {
+    const index = getIndex(menuOptions.value, 'key', menuKey.value)
+    menuItem.value = menuOptions.value[index]
+}
 //菜单被点击
-const handleMenuValue = (_, item) => {
+const handleMenuValue = (item) => {
+    menuItem.value = item
+    menuKey.value = item?.key
     router.push({
         path: useRoutes.path,
         query: {
-            MenuType: item.key
+            MenuType: item?.key
         }
     })
-    getPageTypeData(item.key)
+    getPageTypeData(item?.key)
 }
 
 //渲染完成
 onMounted(() => {
+    menuObjItem()
     getPageTypeData(menuKey.value)
 })
 
 //根据类型,获取相关数据
 const getPageTypeData = (key) => {
+    //编辑状态
+    if (key === 'password') {
+        basicFormEdit.value = true
+        basicHight.value = true
+    } else {
+        basicFormEdit.value = false
+        basicHight.value = false
+    }
+    //请求数据
     if (key === 'basic') {
         queryCurrentUserData()
     } else if (key === 'project') {
-        setProjectMenu(projectContractArr.value)
         getDefaultProject()
     } else if (key === 'log') {
         queryBusinessModule()
@@ -233,21 +321,61 @@ const getPageTypeData = (key) => {
     }
 }
 
-
+//是否编辑
+const basicFormEdit = ref(false)
+const basicHight = ref(false)
 //基础信息表单
 const formUserRef = ref(null)
 const formUserModel = ref(deepClone(userInfo.value))
 const formUserRules = {
     real_name: {
         required: true,
-        trigger: ["blur", "input"],
+        trigger: "blur",
         message: "请输入用户名称"
     },
 }
+//获取用户信息
+const queryCurrentUserData = () => {
+    userApi.queryCurrentUserData().then(({data}) => {
+        if (data.code === 200) {
+            formUserModel.value.deptId = data.data?.deptId || ''
+            formUserModel.value.idNumber = data.data?.idNumber || ''
+        }
+    })
+}
+
+//切换编辑模式
+const basicFormEditClick = () => {
+    basicFormEdit.value = true
+    basicHight.value = true
+}
+
+//保存数据
 const saveUserLoading = ref(false)
 const saveUserInfoClick = () => {
-    formUserRef.value?.validate(erro => {
-        if (!erro) {
+    const key = menuKey.value
+    if (key === 'basic') {
+        saveUserInfoData()
+    } else if (key === 'password') {
+        saveUpdatePassword()
+    }
+}
+
+//取消修改
+const cancelUserClick = () => {
+    const key = menuKey.value
+    if (key === 'basic') {
+        basicFormEdit.value = false
+        basicHight.value = false
+    } else {
+        console.log('我也不知道这个点了干什么,反正UI图上有,至于有什么作用,不知道。。')
+    }
+}
+
+//保存用户信息
+const saveUserInfoData = () => {
+    formUserRef.value.validate((valid) => {
+        if (valid) {
             const form = formUserModel.value
             saveUserLoading.value = true
             userApi.updateUserInfo({
@@ -256,140 +384,93 @@ const saveUserInfoClick = () => {
             }).then(({data}) => {
                 saveUserLoading.value = false
                 if (data.code === 200) {
-                    window?.$message?.success('保存成功')
+                    window?.$ElMessage?.success('保存成功')
                     userInfo.value.real_name = form?.real_name
                     useAppState.setUserInfo(userInfo.value)
                 }
             }).catch(() => {
                 saveUserLoading.value = false
             })
-        }
-    });
-}
-
-//获取用户信息
-const queryCurrentUserData = () => {
-    userApi.queryCurrentUserData().then(({data}) => {
-        if (data.code === 200) {
-            formUserModel.value.deptId = data.data?.deptId || ''
-            formUserModel.value.idNumber = data.data?.idNumber || ''
+        } else {
+            return false
         }
     })
 }
 
-
 //密码设置表单
 const formUserPassRef = ref(null)
-const userPassLoading = ref(false)
 const formUserPassModel = ref({oldPassword: '', newPassword: '', newPassword1: ''})
 const formUserPassRules = {
     oldPassword: {
         required: true,
-        trigger: ["blur", "input"],
+        trigger: "blur",
         message: "请输入原始密码"
     },
     newPassword: {
         required: true,
-        validator(rule, value) {
+        validator(rule, value, callback) {
             const pass = formUserPassModel.value.newPassword1;
             if (!value) {
-                return new Error("请输入新的密码");
+                callback(new Error("请输入新的密码"))
             } else if (pass && value !== pass) {
-                return new Error("新的密码和确认新密码不一致");
+                callback(new Error("新的密码和确认新密码不一致"))
             }
-            return true;
+            callback()
         },
-        trigger: ["blur", "input"]
+        trigger: "blur"
     },
     newPassword1: {
         required: true,
-        validator(rule, value) {
+        validator(rule, value, callback) {
             const pass = formUserPassModel.value.newPassword;
             if (!value) {
-                return new Error("请输入确认新密码");
+                callback(new Error("请输入确认新密码"))
             } else if (pass && value !== pass) {
-                return new Error("新的密码和确认新密码不一致");
+                callback(new Error("新的密码和确认新密码不一致"))
             }
-            return true;
+            callback()
         },
-        trigger: ["blur", "input"]
+        trigger: "blur"
     }
 }
-const saveUserPassClick = () => {
-    formUserPassRef.value?.validate(erro => {
-        if (!erro) {
-            userPassLoading.value = true
+
+//更新密码
+const saveUpdatePassword = () => {
+    formUserPassRef.value.validate((valid) => {
+        if (valid) {
             const form = formUserPassModel.value;
+            saveUserLoading.value = true
             userApi.updatePassword({
-                oldPassword: md5(form.oldPassword),
-                newPassword: md5(form.newPassword),
-                newPassword1: md5(form.newPassword1),
-                plaintextPassword: form.newPassword
+                oldPassword: md5(form?.oldPassword),
+                newPassword: md5(form?.newPassword),
+                newPassword1: md5(form?.newPassword1),
+                plaintextPassword: form?.newPassword
             }).then(({data}) => {
-                userPassLoading.value = false
+                saveUserLoading.value = false
                 if (data.code === 200) {
-                    window?.$message?.success('密码修改成功')
+                    window?.$ElMessage?.success('密码修改成功')
                     formUserPassModel.value = {oldPassword: '', newPassword: '', newPassword1: ''}
                 } else {
-                    window?.$message?.warning(data.msg || '密码修改失败')
+                    window?.$ElMessage?.warning(data.msg || '密码修改失败')
                 }
-            }).catch(erro => {
-                userPassLoading.value = false
+            }).catch(() => {
+                saveUserLoading.value = false
             })
+        } else {
+            return false
         }
-    });
-}
-
-//项目列表
-const projectKey = ref(null)
-const projectMenuRef = ref(null)
-const projectMenu = ref([]);
-//设置项目列表
-const projectIcon = HcIcon('hcicon-xiangmu');
-const setProjectMenu = (arr) => {
-    if (arr.length > 0) {
-        let newArr = [];
-        arr.forEach(item => {
-            //项目数据
-            let itemArr = {
-                key: item.id,
-                label: `${item.projectAlias} (${item.name})`,
-                icon: projectIcon,
-                children: []
-            }
-            //合同段数据处理
-            let children = item['contractInfoList'] || []
-            if (children.length > 0) {
-                children.forEach(items => {
-                    itemArr.children.push({
-                        key: items.id,
-                        parent: {
-                            key: item.id,
-                            label: item.projectAlias
-                        },
-                        label: items.name
-                    })
-                })
-            }
-            newArr.push(itemArr)
-        })
-        projectMenu.value = newArr
-    } else {
-        projectMenu.value = []
-    }
+    })
 }
 
 //获取默认项目
+const projectKey = ref(null)
 const getDefaultProject = () => {
     userApi.getDefaultProject().then(({data}) => {
         if (data.code === 200) {
             let res = data['data'] || {}
             projectKey.value = res['contractId']
-            nextTick(() => {
-                projectMenuRef.value.showOption(res['contractId'])
-            })
         } else {
-            window?.$message?.error(data.msg || '设置出错')
+            window?.$ElMessage?.error(data.msg || '设置出错')
         }
     })
 }
@@ -397,9 +478,10 @@ const getDefaultProject = () => {
 //项目被选择
 const menuProjectId = ref('')
 const menuContractId = ref('')
-const projectMenuValue = (_,item) => {
-    menuContractId.value = item?.key
-    menuProjectId.value = item?.parent?.key
+const projectMenuValue = (item,items) => {
+    menuProjectId.value = item?.id
+    menuContractId.value = items?.id
+    projectKey.value = items?.id
 }
 
 //设置为默认项目
@@ -412,13 +494,13 @@ const setDefaultProjectClick = () => {
             contractId: cid
         }).then(({data}) => {
             if (data.code === 200) {
-                window?.$message?.success('设置成功')
+                window?.$ElMessage?.success('设置成功')
             } else {
-                window?.$message?.error(data.msg || '设置出错')
+                window?.$ElMessage?.error(data.msg || '设置出错')
             }
         })
     } else {
-        window?.$message?.warning('请先在下方选择一个项目合同段')
+        window?.$ElMessage?.warning('请先在下方选择一个项目合同段')
     }
 }
 
@@ -426,7 +508,7 @@ const setDefaultProjectClick = () => {
 const searchLogForm = ref({
     operationModule: null, operationView: null, operationType: null, operationMedium: null,
     queryValue: null, startTime: null, endTime: null,
-    current: 1, size: 20, total: 0
+    current: 1, size: 10, total: 0
 })
 
 //业务模块
@@ -482,33 +564,15 @@ const operationTypeStatus = () => {
 
 //设备
 const deviceData = ref([{label: "APP", value: "APP"}, {label: "PC", value: "PC"}])
-//表格表头
-const logTableColumns = [
-    {title: '序号', key: 'num', width: 80, align: 'center',
-        render(_, index) {
-            return index + 1
-        }
-    },
-    {title: '业务模块', key: 'operationModule'},
-    {title: '操作类型', key: 'operationTypeValue'},
-    {title: '设备', key: 'operationMedium', width: 80, align: 'center'},
-    {title: '操作内容', key: 'operationContent'},
-    {title: '操作时间', key: 'createTime', width: 180, align: 'center'}
-]
 //表格数据
 const logTableData = ref([]);
 
 //日期时间被选择
 const betweenTime = ref(null)
-const betweenTimeUpdate = (_,res) => {
-    res = res || []
-    if (res.length > 0) {
-        searchLogForm.value.startTime = res[0]
-        searchLogForm.value.endTime = res[1]
-    } else {
-        searchLogForm.value.startTime = null
-        searchLogForm.value.endTime = null
-    }
+const betweenDateUpdate = ({val,arr}) => {
+    betweenTime.value = arr
+    searchLogForm.value.startTime = val?.start
+    searchLogForm.value.endTime = val?.end
 }
 
 //回车搜索
@@ -526,8 +590,9 @@ const searchClick = () => {
 }
 
 //分页被点击
-const pageLogChange = (res) => {
-    searchLogForm.value = res
+const pageLogChange = ({current, size}) => {
+    searchLogForm.value.current = current
+    searchLogForm.value.size = size
     getLogTableData()
 }
 
@@ -549,40 +614,27 @@ const getLogTableData = () => {
             logTableData.value = []
             searchLogForm.value.total = 0
         }
-    }).catch(erro => {
+    }).catch(() => {
         logTableLoading.value = false
     })
 }
 
-
-
 //结构类型tab数据和相关处理
 const tabTypeKey = ref('file')
 const tabTypeTab = ref([
     {key:'file',  name: '文件资料'},
     {key:'project', name: '工程划分'}
 ]);
-const tabTypeChange = (value) => {
-    tabTypeKey.value = value;
+const tabTypeChange = (item) => {
+    tabTypeKey.value = item?.key;
 }
 
 //搜索和分页数据
 const searchRecycleForm = ref({
     contractId: contractId.value, current: 1, size: 20, total: 0
 })
-//表格表头
-const recycleTableColumns = [
-    {type: 'selection'},
-    {title: '序号', key: 'num', width: 80, align: 'center',
-        render(_, index) {
-            return index + 1
-        }
-    },
-    {title: '删除内容', key: 'title'},
-    {title: '父节点名称', key: 'name'},
-    {title: '删除时间', key: 'date'}
-]
 //表格数据
+const recycleTableRef = ref(null)
 const recycleTableData = ref([
     { title: '-', name: 'test1', date: "-" },
     { title: '-', name: 'test2', date: "-" },
@@ -591,91 +643,30 @@ const recycleTableData = ref([
 ]);
 
 //分页被点击
-const pageRecycleChange = (res) => {
-    searchRecycleForm.value = res
+const pageRecycleChange = ({current, size}) => {
+    searchRecycleForm.value.current = current
+    searchRecycleForm.value.size = size
     getRecycleTableData()
 }
 //获取数据
 const getRecycleTableData = () => {
 
+}
+//多选
+const recycleTableSelectionChange = (val) => {
+    console.log(val)
 }
 </script>
 
 <style lang="scss" scoped>
-.hc-layout-box {
-    position: relative;
-    height: 100%;
-    max-width: 1600px;
-    display: flex;
-    padding: 36px 0;
-    margin: 0 auto;
-    .hc-layout-left-box {
-        position: relative;
-        color: var(--n-text-color);
-        background-color: var(--hc-bg-color);
-        width: 260px;
-        margin-left: 24px;
-        border-radius: 12px;
-        .user-avatar-box {
-            position: relative;
-            text-align: center;
-            margin: 20px;
-            padding-top: 4px;
-            padding-bottom: 20px;
-            border-bottom: 1px solid var(--n-color);
-        }
-        .user-menu-box {
-            position: relative;
-            padding: 0 12px;
-            height: calc(100% - 210px);
-            border-radius: 0 0 12px 12px;
-            overflow: auto;
-        }
-    }
-    .hc-layout-content-box {
-        flex: 1;
-        overflow: auto;
-        position: relative;
-        padding: 0 24px 0 24px;
-        .card-form-box {
-            position: relative;
-            height: 100%;
-            &.project-menu {
-                margin: 0 -5px;
-            }
-            .card-data-table {
-                position: relative;
-                margin-top: 24px;
-                height: calc(100% - 50px);
-                overflow: auto;
-            }
-        }
-        .foot-recycle {
-            position: relative;
-            display: flex;
-        }
-    }
-}
-.hc-card-header {
-    position: relative;
-    font-size: initial;
-    font-weight: initial;
-}
+@import "../../styles/user/index.scss";
 </style>
 
-<style lang="scss" scoped>
-.hc-layout-box .hc-layout-left-box .user-menu-box .n-menu {
-    --n-font-size: 18px !important;
-    --n-item-height: 48px !important;
-}
-.hc-layout-box .hc-layout-content-box .n-card.hc-card-overflow-p-box {
-    border: initial;
-    border-radius: 12px;
-}
-.hc-layout-content-box .n-image {
-    border: 1px solid #eee;
-}
-.foot-recycle .card-page-box {
-    flex: 1;
+<style lang="scss">
+.user-avatar-upload {
+    .upload-dom, .upload-dom .el-upload {
+        height: 100%;
+        width: 100%;
+    }
 }
 </style>