xiongzhu 4 years ago
parent
commit
177afa5436

+ 2 - 0
package.json

@@ -9,11 +9,13 @@
     "dev": "vite"
   },
   "dependencies": {
+    "apexcharts": "^3.27.2",
     "core-js": "^3.6.5",
     "element-plus": "^1.0.2-beta.55",
     "feather-icons": "^4.28.0",
     "vue": "^3.0.0",
     "vue-router": "^4.0.0-0",
+    "vue3-apexcharts": "^1.4.0",
     "vuex": "^4.0.0-0"
   },
   "devDependencies": {

BIN
src/assets/avatar_female.png


BIN
src/assets/avatar_male.png


+ 32 - 0
src/assets/award.svg

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="77px" height="132px" viewBox="0 0 77 132" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>Badge</title>
+    <defs>
+        <polygon id="path-1" points="0.312172032 0.154635448 65.6696326 0.154635448 65.6696326 73.9489392 0.312172032 73.9489392"></polygon>
+        <linearGradient x1="74.1940398%" y1="12.4118999%" x2="34.4559913%" y2="95.3358506%" id="linearGradient-3">
+            <stop stop-color="#FFDE78" offset="0%"></stop>
+            <stop stop-color="#FFBA3B" offset="100%"></stop>
+        </linearGradient>
+        <linearGradient x1="84.4444309%" y1="12.8705377%" x2="24.6029157%" y2="88.5369465%" id="linearGradient-4">
+            <stop stop-color="#FFD06D" offset="0%"></stop>
+            <stop stop-color="#F99912" offset="100%"></stop>
+        </linearGradient>
+    </defs>
+    <g id="⚙️-Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="28)-Widgets/1)-Advanced/Congratulations" transform="translate(-236.000000, 0.000000)">
+            <g id="Badge" transform="translate(236.000000, 0.000000)">
+                <polygon id="Fill-1" fill="#6359CF" points="56.5002052 0 32 62 51.0750015 62 77 0"></polygon>
+                <g id="Group-4" transform="translate(7.000000, 58.000000)">
+                    <mask id="mask-2" fill="white">
+                        <use xlink:href="#path-1"></use>
+                    </mask>
+                    <g id="Clip-3"></g>
+                    <path d="M20.2910182,10.9655595 L20.2910182,5.56810862 C20.2910182,4.69058121 21.0033488,3.97574147 21.8787085,3.97574147 L44.1030961,3.97574147 C44.9784558,3.97574147 45.6907864,4.69058121 45.6907864,5.56810862 L45.6907864,10.9655595 C41.7866903,9.31238991 37.4950929,8.39829886 32.9909023,8.39829886 C28.4867117,8.39829886 24.1951143,9.31238991 20.2910182,10.9655595 M49.5010793,12.8828912 L49.5010793,5.56810862 C49.5010793,2.58303645 47.079401,0.154635448 44.1030961,0.154635448 L21.8787085,0.154635448 C18.9024036,0.154635448 16.4811349,2.58303645 16.4811349,5.56810862 L16.4811349,12.8828912 C6.80752947,18.5736729 0.312172032,29.11304 0.312172032,41.1737012 C0.312172032,59.275169 14.9425991,73.9491035 32.9909023,73.9491035 C51.0387959,73.9491035 65.6696326,59.275169 65.6696326,41.1737012 C65.6696326,29.11304 59.1746848,18.5736729 49.5010793,12.8828912" id="Fill-2" fill="url(#linearGradient-3)" mask="url(#mask-2)"></path>
+                </g>
+                <path d="M39.9995835,126 C25.1114916,126 13,114.112612 13,99.5004088 C13,84.8873882 25.1114916,73 39.9995835,73 C54.8876753,73 67,84.8873882 67,99.5004088 C67,114.112612 54.8876753,126 39.9995835,126" id="Fill-5" fill="url(#linearGradient-4)"></path>
+                <path d="M45.7719543,108.738408 L40.4431045,106.496656 L40.4431045,106.496656 L35.0936333,108.689266 C34.0715832,109.108178 32.9034526,108.619238 32.4845405,107.597188 C32.3656011,107.307003 32.3165706,106.992914 32.3414082,106.680285 L32.805967,100.83291 L32.805967,100.83291 L29.0552357,96.3246475 C28.3487918,95.4755239 28.4644565,94.2144874 29.3135801,93.5080435 C29.5490081,93.3121748 29.8262731,93.1729885 30.1239971,93.1012162 L35.7742958,91.7390987 L35.7742958,91.7390987 L38.8248494,86.7903241 C39.4044616,85.8500457 40.6365763,85.5576679 41.5768548,86.13728 C41.8477492,86.3042664 42.0748864,86.5334839 42.2393961,86.8058894 L45.2452326,91.783142 L45.2452326,91.783142 L50.8831663,93.1971466 C51.9545537,93.4658526 52.6052555,94.552213 52.3365494,95.6236003 C52.2620537,95.9206304 52.1203449,96.1965871 51.9223473,96.4301968 L48.130941,100.903531 L48.130941,100.903531 L48.5425573,106.754541 C48.6200718,107.856387 47.7896868,108.812448 46.6878405,108.889962 C46.374884,108.911979 46.0611372,108.860062 45.7719543,108.738408 Z" id="Fill-105" fill="#FFEFB9"></path>
+                <polygon id="Fill-116" fill="#7367F0" points="1.5262791e-13 0 22.7693525 0 52 63 30.6168186 63"></polygon>
+            </g>
+        </g>
+    </g>
+</svg>

+ 22 - 7
src/components/FeatherIcon.vue

@@ -3,33 +3,48 @@
 </template>
 <script>
 import feather from 'feather-icons';
-
+import { watch, ref } from 'vue';
 export default {
     props: {
         name: { required: true },
         color: String,
-        strokeWidth: Number,
-        size: Number
+        strokeWidth: {},
+        size: {}
+    },
+    data() {
+        return {};
     },
     setup(props, ctx) {
+        let svg = '';
         if (props.name) {
             const icon = feather.icons[props.name];
             if (icon) {
                 icon.attrs.width = props.size || icon.attrs.width;
                 icon.attrs.height = props.size || icon.attrs.width;
-                return {
-                    svg: icon.toSvg({ 'stroke-width': props.strokeWidth || 2, color: props.color || '#000000' })
-                };
+                svg = icon.toSvg({ 'stroke-width': props.strokeWidth || 2, color: props.color || '#000000' });
             }
         }
         return {
-            svg: ''
+            svg
         };
+    },
+    watch: {
+        name(name) {
+            if (name) {
+                const icon = feather.icons[name];
+                if (icon) {
+                    icon.attrs.width = this.size || icon.attrs.width;
+                    icon.attrs.height = this.size || icon.attrs.width;
+                    this.svg = icon.toSvg({ 'stroke-width': this.strokeWidth || 2, color: this.color || '#000000' });
+                }
+            }
+        }
     }
 };
 </script>
 <style lang="less">
 .feather-icon {
     display: inline-block;
+    font-size: 0;
 }
 </style>

+ 51 - 0
src/components/dashboard/Award.vue

@@ -0,0 +1,51 @@
+<template>
+    <el-col class="dashboard-col">
+        <div class="dashboard-card card-award">
+            <div class="card-body">
+                <div class="title">恭喜您 🎉 !</div>
+                <div class="desc">You have won gold medal</div>
+                <div class="number">$48.9k</div>
+                <el-button class="btn" size="medium" type="primary">View Sales</el-button>
+                <img src="../../assets/award.svg" />
+            </div>
+        </div>
+    </el-col>
+</template>
+<script>
+export default {
+    data() {
+        return {};
+    }
+};
+</script>
+<style lang="less" scoped>
+@import url(./card.less);
+.card-award {
+    .card-body {
+        display: flex;
+        flex-direction: column;
+        position: relative;
+        .title {
+            font-size: 18px;
+        }
+        .desc {
+            font-size: 14px;
+        }
+        .number {
+            color: var(--primary-color);
+            font-weight: 700;
+            font-size: 20px;
+            margin-top: 20px;
+        }
+        .btn {
+            margin-top: 10px;
+            width: 160px;
+        }
+        img {
+            position: absolute;
+            top: 0;
+            right: 20px;
+        }
+    }
+}
+</style>

+ 86 - 0
src/components/dashboard/OrderChart.vue

@@ -0,0 +1,86 @@
+<template>
+    <el-col class="dashboard-col">
+        <div class="dashboard-card">
+            <apexchart type="bar" height="350" stacked="true" :options="chartOptions" :series="series"></apexchart>
+        </div>
+    </el-col>
+</template>
+<script>
+export default {
+    data() {
+        return {
+            series: [
+                {
+                    name: '2020',
+                    data: [45, 85, 65, 45, 65]
+                }
+            ],
+            chartOptions: {
+                chart: {
+                    type: 'bar',
+                    stacked: true,
+                    toolbar: {
+                        show: false
+                    }
+                },
+                grid: {
+                    show: false,
+                    padding: {
+                        left: 0,
+                        right: 0,
+                        top: -15,
+                        bottom: -15
+                    }
+                },
+                plotOptions: {
+                    bar: {
+                        horizontal: false,
+                        columnWidth: '10%',
+                        startingShape: 'rounded',
+                        borderRadius: 10,
+                        colors: {
+                            backgroundBarColors: [
+                                'rgba(0, 0, 0, 0.05)',
+                                'rgba(0, 0, 0, 0.15)',
+                                'rgba(0, 0, 0, 0.15)',
+                                'rgba(0, 0, 0, 0.15)',
+                                'rgba(0, 0, 0, 0.15)'
+                            ],
+                            backgroundBarRadius: 10
+                        }
+                    }
+                },
+                legend: {
+                    show: false
+                },
+                dataLabels: {
+                    enabled: false
+                },
+                colors: ['rgba(255, 159, 67, 0.85)'],
+                xaxis: {
+                    labels: {
+                        show: false
+                    },
+                    axisBorder: {
+                        show: false
+                    },
+                    axisTicks: {
+                        show: false
+                    }
+                },
+                yaxis: {
+                    show: false
+                },
+                tooltip: {
+                    x: {
+                        show: false
+                    }
+                }
+            }
+        };
+    }
+};
+</script>
+<style lang="less" scoped>
+@import url(./card.less);
+</style>

+ 13 - 0
src/components/dashboard/Profit.vue

@@ -0,0 +1,13 @@
+<template>
+    <el-col>
+        <apexchart type="area" height="350" :options="chartOptions" :series="series"></apexchart>
+    </el-col>
+</template>
+<script>
+export default {
+    data() {
+        return {};
+    }
+};
+</script>
+<style lang="less" scoped></style>

+ 101 - 0
src/components/dashboard/Statistics.vue

@@ -0,0 +1,101 @@
+<template>
+    <el-col class="dashboard-col">
+        <div class="dashboard-card">
+            <div class="card-header">
+                <div class="card-header__text">Statistics</div>
+            </div>
+            <div class="card-body">
+                <el-row class="wrapper">
+                    <el-col
+                        v-for="item in statistics"
+                        :key="item.name"
+                        class="statistics-item"
+                        :xs="24"
+                        :md="12"
+                        :lg="6"
+                    >
+                        <div class="icon-bg" :style="{ background: item.bg }">
+                            <feather-icon :name="item.icon" :color="item.color" class="icon"></feather-icon>
+                        </div>
+                        <div class="content">
+                            <div class="value">{{ item.value }}</div>
+                            <div class="name">{{ item.name }}</div>
+                        </div>
+                    </el-col>
+                </el-row>
+            </div>
+        </div>
+    </el-col>
+</template>
+<script>
+export default {
+    data() {
+        return {
+            statistics: [
+                {
+                    name: 'Sales',
+                    value: '230k',
+                    icon: 'trending-up',
+                    color: '#7367f0',
+                    bg: 'rgba(115,103,240,.12)'
+                },
+                {
+                    name: 'Customers',
+                    value: '8.549k',
+                    icon: 'user',
+                    color: '#00cfe8',
+                    bg: 'rgba(0,207,232,.12)'
+                },
+                {
+                    name: 'Products',
+                    value: '1.423k',
+                    icon: 'box',
+                    color: '#ea5455',
+                    bg: 'rgba(234,84,85,.12)'
+                },
+                {
+                    name: 'Revenue',
+                    value: '$9745',
+                    icon: 'dollar-sign',
+                    color: '#28c76f',
+                    bg: 'rgba(40,199,111,.12)'
+                }
+            ]
+        };
+    }
+};
+</script>
+<style lang="less" scoped>
+@import url(./card.less);
+.wrapper {
+    margin-left: -20px;
+    margin-right: -20px;
+}
+.statistics-item {
+    display: flex;
+    align-items: center;
+    padding: 0 20px;
+    margin-bottom: 20px;
+    .icon-bg {
+        width: 48px;
+        height: 48px;
+        min-width: 48px;
+        border-radius: 50%;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+    }
+    .content {
+        margin-left: 20px;
+        .value {
+            color: var(--text-color-2);
+            font-size: 18px;
+            font-weight: 700;
+        }
+        .name {
+            color: var(--text-color-2);
+            font-size: 14px;
+        }
+    }
+}
+</style>

+ 21 - 0
src/components/dashboard/card.less

@@ -0,0 +1,21 @@
+.dashboard-col {
+    display: flex;
+    flex-direction: column;
+}
+.dashboard-card {
+    flex: 1 1 auto;
+    border-radius: 6px;
+    background: var(--background-color-1);
+    box-shadow: 0 0 9px 0 rgba(0, 0, 0, 0.05);
+    margin-bottom: 20px;
+    .card-header {
+        padding: 20px;
+        .card-header__text {
+            font-size: 18px;
+            font-weight: 500;
+        }
+    }
+    .card-body {
+        padding: 20px;
+    }
+}

+ 2 - 0
src/main.js

@@ -4,6 +4,7 @@ import router from './router';
 import store from './store';
 import ElementPlus from 'element-plus';
 import FeatherIcon from './components/FeatherIcon.vue';
+import apexchart from 'vue3-apexcharts';
 
 import './style/theme/index.css';
 import './style/theme/display.css';
@@ -16,4 +17,5 @@ const app = createApp(App)
     .use(router)
     .use(ElementPlus, { size: 'small' })
     .component('feather-icon', FeatherIcon)
+    .component('apexchart', apexchart)
     .mount('#app');

+ 8 - 1
src/router/index.js

@@ -5,7 +5,14 @@ const routes = [
     {
         path: '/',
         name: 'Index',
-        component: Index
+        component: Index,
+        children: [
+            {
+                path: '/dashboard',
+                name: 'Dashboard',
+                component: () => import(/* webpackChunkName: "dashboard" */ '../views/Dashboard.vue')
+            }
+        ]
     },
     {
         path: '/login',

+ 3 - 1
src/store/index.js

@@ -1,7 +1,9 @@
 import { createStore } from 'vuex';
 
 export default createStore({
-    state: {},
+    state: {
+        theme: 'light'
+    },
     mutations: {},
     actions: {},
     modules: {}

+ 32 - 4
src/style/app.less

@@ -11,32 +11,60 @@
     --border-color-3: #ebeef5;
     --border-color-4: #f2f6fc;
 }
+@font-face {
+    font-family: 'Montserrat';
+    src: url('./fonts/Montserrat/Montserrat-Thin.ttf') format('truetype');
+    font-weight: 100;
+}
+@font-face {
+    font-family: 'Montserrat';
+    src: url('./fonts/Montserrat/Montserrat-ExtraLight.ttf') format('truetype');
+    font-weight: 200;
+}
+@font-face {
+    font-family: 'Montserrat';
+    src: url('./fonts/Montserrat/Montserrat-Light.ttf') format('truetype');
+    font-weight: 300;
+}
 @font-face {
     font-family: 'Montserrat';
     src: url('./fonts/Montserrat/Montserrat-Regular.ttf') format('truetype');
     font-weight: 400;
 }
+@font-face {
+    font-family: 'Montserrat';
+    src: url('./fonts/Montserrat/Montserrat-Medium.ttf') format('truetype');
+    font-weight: 500;
+}
+@font-face {
+    font-family: 'Montserrat';
+    src: url('./fonts/Montserrat/Montserrat-SemiBold.ttf') format('truetype');
+    font-weight: 600;
+}
 @font-face {
     font-family: 'Montserrat';
     src: url('./fonts/Montserrat/Montserrat-Bold.ttf') format('truetype');
-    font-weight: bold;
+    font-weight: 700;
 }
 html,
 body {
     margin: 0;
     padding: 0;
-    font-family: -apple-system, 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei',
-        '微软雅黑', Arial, sans-serif;
+    font-family: 'Montserrat', -apple-system, 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB',
+        'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
     -webkit-font-smoothing: antialiased;
     -moz-osx-font-smoothing: grayscale;
     height: 100%;
     -webkit-tap-highlight-color: transparent;
     background-color: var(--background-color-2);
+    color: var(--text-color-1);
 }
 #app {
     height: 100%;
 }
-
+button {
+    font-family: inherit;
+}
 .el-form--label-top .el-form-item__label {
     padding: 0 !important;
 }

+ 2 - 1
src/style/common.less

@@ -1,5 +1,6 @@
 @prim: #665df5;
-@bg-light: #f8f8f8;
+@bg1-light: #ffffff;
+@bg2-light: #f8f8f8;
 @text1-light: #303133;
 @text2-light: #606266;
 @text3-light: #909399;

+ 23 - 0
src/views/Dashboard.vue

@@ -0,0 +1,23 @@
+<template>
+    <div>
+        <el-row :gutter="20">
+            <award :xs="24" :md="24" :lg="8"></award>
+            <statistics :xs="24" :md="24" :lg="16"></statistics>
+        </el-row>
+        <el-row :gutter="20">
+            <order-chart></order-chart>
+        </el-row>
+    </div>
+</template>
+<script>
+import Statistics from '../components/dashboard/Statistics.vue';
+import Award from '../components/dashboard/Award.vue';
+import OrderChart from '../components/dashboard/OrderChart.vue';
+export default {
+    components: { Statistics, Award, OrderChart },
+    data() {
+        return {};
+    }
+};
+</script>
+<style lang="less" scoped></style>

+ 188 - 43
src/views/Index.vue

@@ -4,44 +4,9 @@
             <div class="logo-wrapper">
                 <img src="../assets/logo-title-l.png" class="logo" />
             </div>
-            <el-menu default-active="2">
-                <el-submenu index="1">
-                    <template #title>
-                        <i class="el-icon-location"></i>
-                        <span>导航一</span>
-                    </template>
-                    <el-menu-item-group>
-                        <template #title>分组一</template>
-                        <el-menu-item index="1-1">选项1</el-menu-item>
-                        <el-menu-item index="1-2">选项2</el-menu-item>
-                    </el-menu-item-group>
-                    <el-menu-item-group title="分组2">
-                        <el-menu-item index="1-3">选项3</el-menu-item>
-                    </el-menu-item-group>
-                    <el-submenu index="1-4">
-                        <template #title>选项4</template>
-                        <el-menu-item index="1-4-1">选项1</el-menu-item>
-                    </el-submenu>
-                </el-submenu>
-                <el-menu-item index="2">
-                    <i class="el-icon-menu"></i>
-                    <template #title>导航二</template>
-                </el-menu-item>
-                <el-menu-item index="3" disabled>
-                    <i class="el-icon-document"></i>
-                    <template #title>导航三</template>
-                </el-menu-item>
-                <el-menu-item index="4">
-                    <i class="el-icon-setting"></i>
-                    <template #title>导航四</template>
-                </el-menu-item>
-                <el-menu-item-group>
-                    <template #title>分组三</template>
-                    <el-menu-item index="5">
-                        <i class="el-icon-document"></i>
-                        <template #title>导航三</template>
-                    </el-menu-item>
-                    <el-submenu index="6">
+            <div class="menu-wrapper">
+                <el-menu default-active="2">
+                    <el-submenu index="1">
                         <template #title>
                             <i class="el-icon-location"></i>
                             <span>导航一</span>
@@ -59,12 +24,70 @@
                             <el-menu-item index="1-4-1">选项1</el-menu-item>
                         </el-submenu>
                     </el-submenu>
-                </el-menu-item-group>
-            </el-menu>
+                    <el-menu-item index="2">
+                        <i class="el-icon-menu"></i>
+                        <template #title>导航二</template>
+                    </el-menu-item>
+                    <el-menu-item index="3" disabled>
+                        <i class="el-icon-document"></i>
+                        <template #title>导航三</template>
+                    </el-menu-item>
+                    <el-menu-item index="4">
+                        <i class="el-icon-setting"></i>
+                        <template #title>导航四</template>
+                    </el-menu-item>
+                    <el-menu-item-group>
+                        <template #title>分组三</template>
+                        <el-menu-item index="5">
+                            <i class="el-icon-document"></i>
+                            <template #title>导航三</template>
+                        </el-menu-item>
+                        <el-submenu index="6">
+                            <template #title>
+                                <i class="el-icon-location"></i>
+                                <span>导航一</span>
+                            </template>
+                            <el-menu-item-group>
+                                <template #title>分组一</template>
+                                <el-menu-item index="1-1">选项1</el-menu-item>
+                                <el-menu-item index="1-2">选项2</el-menu-item>
+                            </el-menu-item-group>
+                            <el-menu-item-group title="分组2">
+                                <el-menu-item index="1-3">选项3</el-menu-item>
+                            </el-menu-item-group>
+                            <el-submenu index="1-4">
+                                <template #title>选项4</template>
+                                <el-menu-item index="1-4-1">选项1</el-menu-item>
+                            </el-submenu>
+                        </el-submenu>
+                    </el-menu-item-group>
+                </el-menu>
+            </div>
+            <div class="menu-mask"></div>
         </el-aside>
-        <el-main>
-            <feather-icon name="activity" strokeWidth="2" color="red" size="48"></feather-icon>
-            <feather-icon name="airplay"></feather-icon>
+        <el-main class="index-main">
+            <div class="index-header__wrapper">
+                <div class="index-header__main">
+                    <div class="badge-icon__wrapper">
+                        <feather-icon name="moon" size="21"></feather-icon>
+                    </div>
+                    <div class="badge-icon__wrapper">
+                        <el-badge :value="9" class="badge-icon">
+                            <feather-icon name="bell" size="21"></feather-icon>
+                        </el-badge>
+                    </div>
+                    <div class="name__wrapper">
+                        <div class="nickname">超级管理员</div>
+                        <div class="username">admin</div>
+                    </div>
+                    <div class="avatar__wrapper">
+                        <img src="../assets/avatar_male.png" class="avatar" />
+                    </div>
+                </div>
+            </div>
+            <div class="index__content">
+                <router-view></router-view>
+            </div>
         </el-main>
     </el-container>
 </template>
@@ -81,6 +104,28 @@ export default {
 }
 .aside-menu {
     background-color: var(--background-color-1);
+    display: flex;
+    flex-direction: column;
+    position: relative;
+    .logo-wrapper {
+    }
+    .menu-wrapper {
+        flex-basis: 0;
+        flex-grow: 1;
+        position: relative;
+        padding-top: 20px;
+        overflow: auto;
+    }
+    .menu-mask {
+        position: absolute;
+        top: 70px;
+        left: 0;
+        width: 100%;
+        height: 30px;
+    }
+    ::-webkit-scrollbar {
+        display: none;
+    }
 }
 .logo-wrapper {
     display: flex;
@@ -90,4 +135,104 @@ export default {
         margin-top: 20px;
     }
 }
+.index-main {
+    padding: 0;
+    position: relative;
+    .index-header__wrapper {
+        padding: 20px 20px 20px 20px;
+        position: absolute;
+        top: 0;
+        right: 0;
+        left: 0;
+        .index-header__main {
+            height: 60px;
+            background-color: @bg1-light;
+            border-radius: 6px;
+            box-shadow: 0 0 9px 0 rgba(0, 0, 0, 0.05);
+            display: flex;
+            align-items: center;
+            justify-content: flex-end;
+            padding: 0 20px;
+            .avatar__wrapper {
+                position: relative;
+                cursor: pointer;
+                .avatar {
+                    width: 40px;
+                    height: 40px;
+                    object-fit: cover;
+                    border-radius: 20px;
+                }
+                &::after {
+                    content: '';
+                    position: absolute;
+                    right: 0;
+                    bottom: 1px;
+                    width: 9px;
+                    height: 9px;
+                    border-radius: 7px;
+                    border: 1px solid #ffffff;
+                    background-color: #28c76f;
+                }
+            }
+            .name__wrapper {
+                text-align: right;
+                margin-right: 15px;
+                margin-left: 15px;
+                .nickname {
+                    font-size: 14px;
+                }
+                .username {
+                    font-size: 12px;
+                }
+            }
+            .badge-icon__wrapper {
+                padding: 0 15px;
+                cursor: pointer;
+                user-select: none;
+                .badge-icon {
+                    font-size: 0;
+                }
+            }
+        }
+    }
+    .index__content {
+        padding: 100px 20px 20px 20px;
+        box-sizing: border-box;
+        overflow: auto;
+        height: 100%;
+    }
+}
+</style>
+<style lang="less">
+[theme='light'] {
+    .aside-menu {
+        box-shadow: 0 0 9px 0 rgba(0, 0, 0, 0.05);
+    }
+    .menu-mask {
+        background: linear-gradient(180deg, @bg1-light, fade(@bg1-light, 0%));
+    }
+    .index-header__wrapper {
+        background: linear-gradient(
+            180deg,
+            fade(@bg2-light, 90%) 0%,
+            fade(@bg2-light, 100%) 73%,
+            fade(@bg2-light, 50%)
+        );
+    }
+    .index-header__main {
+        .name__wrapper {
+            .nickname {
+                color: var(--text-color-2);
+            }
+            .username {
+                color: var(--text-color-3);
+            }
+        }
+        .feather-icon {
+            svg {
+                color: var(--text-color-2);
+            }
+        }
+    }
+}
 </style>

+ 72 - 0
yarn.lock

@@ -1842,6 +1842,18 @@ anymatch@^3.0.0, anymatch@~3.1.2:
     normalize-path "^3.0.0"
     picomatch "^2.0.4"
 
+apexcharts@^3.27.2:
+  version "3.27.2"
+  resolved "https://registry.yarnpkg.com/apexcharts/-/apexcharts-3.27.2.tgz#95598439158c7d2aa0ef3909606a935c891bb621"
+  integrity sha512-FjW0OW6Oq9kbWJiF6wIZEsMNNHme6WNv3eh+p4azuWVELNY/LigXU+01s4TP+Obplhy6byV9bw9x7GhrbN2YsQ==
+  dependencies:
+    svg.draggable.js "^2.2.2"
+    svg.easing.js "^2.0.0"
+    svg.filter.js "^2.0.2"
+    svg.pathmorphing.js "^0.1.3"
+    svg.resize.js "^1.4.3"
+    svg.select.js "^3.0.1"
+
 append-buffer@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/append-buffer/-/append-buffer-1.0.2.tgz#d8220cf466081525efea50614f3de6514dfa58f1"
@@ -9506,6 +9518,61 @@ svg-tags@^1.0.0:
   resolved "https://registry.nlark.com/svg-tags/download/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
   integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=
 
+svg.draggable.js@^2.2.2:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz#c514a2f1405efb6f0263e7958f5b68fce50603ba"
+  integrity sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==
+  dependencies:
+    svg.js "^2.0.1"
+
+svg.easing.js@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/svg.easing.js/-/svg.easing.js-2.0.0.tgz#8aa9946b0a8e27857a5c40a10eba4091e5691f12"
+  integrity sha1-iqmUawqOJ4V6XEChDrpAkeVpHxI=
+  dependencies:
+    svg.js ">=2.3.x"
+
+svg.filter.js@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/svg.filter.js/-/svg.filter.js-2.0.2.tgz#91008e151389dd9230779fcbe6e2c9a362d1c203"
+  integrity sha1-kQCOFROJ3ZIwd5/L5uLJo2LRwgM=
+  dependencies:
+    svg.js "^2.2.5"
+
+svg.js@>=2.3.x, svg.js@^2.0.1, svg.js@^2.2.5, svg.js@^2.4.0, svg.js@^2.6.5:
+  version "2.7.1"
+  resolved "https://registry.yarnpkg.com/svg.js/-/svg.js-2.7.1.tgz#eb977ed4737001eab859949b4a398ee1bb79948d"
+  integrity sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==
+
+svg.pathmorphing.js@^0.1.3:
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz#c25718a1cc7c36e852ecabc380e758ac09bb2b65"
+  integrity sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==
+  dependencies:
+    svg.js "^2.4.0"
+
+svg.resize.js@^1.4.3:
+  version "1.4.3"
+  resolved "https://registry.yarnpkg.com/svg.resize.js/-/svg.resize.js-1.4.3.tgz#885abd248e0cd205b36b973c4b578b9a36f23332"
+  integrity sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==
+  dependencies:
+    svg.js "^2.6.5"
+    svg.select.js "^2.1.2"
+
+svg.select.js@^2.1.2:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/svg.select.js/-/svg.select.js-2.1.2.tgz#e41ce13b1acff43a7441f9f8be87a2319c87be73"
+  integrity sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==
+  dependencies:
+    svg.js "^2.2.5"
+
+svg.select.js@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/svg.select.js/-/svg.select.js-3.0.1.tgz#a4198e359f3825739226415f82176a90ea5cc917"
+  integrity sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==
+  dependencies:
+    svg.js "^2.6.5"
+
 svgo@^1.0.0:
   version "1.3.2"
   resolved "https://registry.nlark.com/svgo/download/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167"
@@ -10268,6 +10335,11 @@ vue-template-es2015-compiler@^1.9.0:
   resolved "https://registry.npm.taobao.org/vue-template-es2015-compiler/download/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
   integrity sha1-HuO8mhbsv1EYvjNLsV+cRvgvWCU=
 
+vue3-apexcharts@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/vue3-apexcharts/-/vue3-apexcharts-1.4.0.tgz#922b7773803277376afc4c1bccddee52460140e3"
+  integrity sha512-9YyuDJTS/V0bRIuIVukIqTjzcaGf0YwZOlGTACCuSXtAjOp7/WHhvgYaP4oGY4KfbmIF+cspQw3UPSPigGJOHg==
+
 vue@^3.0.0:
   version "3.1.5"
   resolved "https://registry.nlark.com/vue/download/vue-3.1.5.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fvue%2Fdownload%2Fvue-3.1.5.tgz#12879b11d0685ee4478c8869551799630a52f9fe"