Explorar o código

feat: main 页面结构搭建

qinlaiqiao %!s(int64=3) %!d(string=hai) anos
pai
achega
d2678d883a

+ 26 - 20
package-lock.json

@@ -2796,18 +2796,18 @@
       "integrity": "sha1-U3RpYfcxqOpmbjMWJx6UQjjcMds="
     },
     "@vueuse/core": {
-      "version": "6.7.4",
-      "resolved": "http://192.168.1.90:4873/@vueuse%2fcore/-/core-6.7.4.tgz",
-      "integrity": "sha1-jhVSRFQ4ixHJ62/1mmdCYCuXbi8=",
+      "version": "6.1.0",
+      "resolved": "http://192.168.1.90:4873/@vueuse%2fcore/-/core-6.1.0.tgz",
+      "integrity": "sha1-gTfCkc9JsRwt7aTVB5CW5Vs2/Cg=",
       "requires": {
-        "@vueuse/shared": "6.7.4",
+        "@vueuse/shared": "6.1.0",
         "vue-demi": "*"
       }
     },
     "@vueuse/shared": {
-      "version": "6.7.4",
-      "resolved": "http://192.168.1.90:4873/@vueuse%2fshared/-/shared-6.7.4.tgz",
-      "integrity": "sha1-mqKWfSCGPPMlOv0IouboSmHSyJs=",
+      "version": "6.1.0",
+      "resolved": "http://192.168.1.90:4873/@vueuse%2fshared/-/shared-6.1.0.tgz",
+      "integrity": "sha1-E3X9Qazv5S+aGELzxqijSHhlNbo=",
       "requires": {
         "vue-demi": "*"
       }
@@ -5632,18 +5632,19 @@
       "dev": true
     },
     "element-plus": {
-      "version": "1.2.0-beta.1",
-      "resolved": "http://192.168.1.90:4873/element-plus/-/element-plus-1.2.0-beta.1.tgz",
-      "integrity": "sha1-d7/Zk3Qb4XMVQN2Zfb3g0D0GW14=",
+      "version": "1.1.0-beta.24",
+      "resolved": "http://192.168.1.90:4873/element-plus/-/element-plus-1.1.0-beta.24.tgz",
+      "integrity": "sha1-hYsFky68C+FUGdOXTRW+Kk9LaWw=",
       "requires": {
         "@element-plus/icons": "^0.0.11",
         "@popperjs/core": "^2.10.2",
-        "@vueuse/core": "^6.7.3",
-        "async-validator": "^4.0.7",
+        "@vueuse/core": "~6.1.0",
+        "async-validator": "^4.0.3",
         "dayjs": "^1.10.7",
         "lodash": "^4.17.21",
-        "memoize-one": "^6.0.0",
-        "normalize-wheel-es": "^1.1.0"
+        "memoize-one": "^5.2.1",
+        "normalize-wheel-es": "^1.1.0",
+        "resize-observer-polyfill": "^1.5.1"
       }
     },
     "elliptic": {
@@ -9922,9 +9923,9 @@
       "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
     },
     "memoize-one": {
-      "version": "6.0.0",
-      "resolved": "http://192.168.1.90:4873/memoize-one/-/memoize-one-6.0.0.tgz",
-      "integrity": "sha1-slkbhx7YKUiu5HJ9xqvO7qyMEEU="
+      "version": "5.2.1",
+      "resolved": "http://192.168.1.90:4873/memoize-one/-/memoize-one-5.2.1.tgz",
+      "integrity": "sha1-gzeqPEM1WBg57AHD1ZQJDOvo8A4="
     },
     "memory-fs": {
       "version": "0.4.1",
@@ -11948,6 +11949,11 @@
       "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
       "dev": true
     },
+    "resize-observer-polyfill": {
+      "version": "1.5.1",
+      "resolved": "http://192.168.1.90:4873/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+      "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
+    },
     "resolve": {
       "version": "1.20.0",
       "resolved": "http://192.168.1.90:4873/resolve/-/resolve-1.20.0.tgz",
@@ -14393,9 +14399,9 @@
       }
     },
     "vue-demi": {
-      "version": "0.12.0",
-      "resolved": "http://192.168.1.90:4873/vue-demi/-/vue-demi-0.12.0.tgz",
-      "integrity": "sha1-sgKgHN/GNUQ+QfrqaX1iTKrA+nM="
+      "version": "0.12.1",
+      "resolved": "http://192.168.1.90:4873/vue-demi/-/vue-demi-0.12.1.tgz",
+      "integrity": "sha1-9+GO++z/0RqwadFHLXoG4xm0F0w="
     },
     "vue-eslint-parser": {
       "version": "8.0.1",

+ 1 - 1
package.json

@@ -16,7 +16,7 @@
     "@midwayjs/koa": "^2.11.0",
     "animate.css": "^4.1.1",
     "animejs": "^3.2.1",
-    "element-plus": "^1.2.0-beta.1",
+    "element-plus": "^1.1.0-beta.24",
     "koa-bodyparser": "^4.3.0",
     "vue": "^3.2.16",
     "vue-router": "^4.0.12"

+ 2 - 0
readme.md

@@ -73,6 +73,8 @@ utils 目录用于存放相关工具方法,并拆分为了 3 个子目录:
 
 在使用 tailwindcss 的时候,为了保持 html 精简,建议在 `<style>` 标签内使用 `@apply` 命令加载 tailwindcss 的 class 类。
 
+VS Code 需要下载 `Tailwind CSS IntelliSense` 插件。
+
 #### 别名
 
 1. @ : src

+ 10 - 0
src/components/iconfont/Iconfont.vue

@@ -0,0 +1,10 @@
+<template>
+  <i class="iconfont" />
+</template>
+
+<script>
+// 封装iconfont
+export default {
+  name: 'iconfont',
+};
+</script>

+ 9 - 0
src/components/index.ts

@@ -0,0 +1,9 @@
+/**
+ * 全局默认会注册的组件
+ */
+import type { App } from 'vue';
+import Iconfont from './iconfont/Iconfont.vue';
+
+export default function (app: App<Element>) {
+    app.component(Iconfont.name, Iconfont);
+}

+ 2 - 0
src/main.ts

@@ -4,8 +4,10 @@ import ElementPlus from 'element-plus'
 import zhCn from 'element-plus/es/locale/lang/zh-cn'
 import '@/styles/index.scss';
 import router from './router';
+import register from '@/components';
 
 const app = createApp(App)
 app.use(ElementPlus, { size: 'mini', locale: zhCn, })
 app.use(router)
+register(app)
 app.mount('#app')

+ 19 - 2
src/router/index.ts

@@ -3,8 +3,25 @@ import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
 const routes: Array<RouteRecordRaw> = [
     {
         path: '/',
-        name: 'Home',
-        component: () => import(/* webpackChunkName: "home" */ '@/views/home/Home.vue'),
+        name: 'Main',
+        component: () => import(/* webpackChunkName: "main" */ '@/views/main/Main.vue'),
+        children: [
+            {
+                path: '',
+                name: 'Dashboard',
+                component: () => import(/* webpackChunkName: "main" */ '@/views/main/dashboard/Dashboard.vue'),
+            },
+            {
+                path: '/projects',
+                name: 'ProjectList',
+                component: () => import(/* webpackChunkName: "main" */ '@/views/main/project-list/ProjectList.vue'),
+            },
+            {
+                path: '/ration',
+                name: 'Ration',
+                component: () => import(/* webpackChunkName: "main" */ '@/views/main/ration/Ration.vue'),
+            }
+        ]
     },
     {
         path: '/login',

+ 1 - 0
src/styles/_element_plus.scss

@@ -22,6 +22,7 @@
       'base': $info,
     ),
   ),
+  $font-path: 'element-plus/theme-chalk/fonts'
 );
 
 @import "element-plus/theme-chalk/src/index.scss";

+ 0 - 18
src/views/home/Home.vue

@@ -1,18 +0,0 @@
-<script setup lang="ts">
-import { ref } from "vue";
-</script>
-
-<template>
-  <article class="home-page">
-    <h1 class="title">主页-HOME</h1>
-    <router-link to="/login">
-      <el-button size="large" type="primary">登录</el-button>
-    </router-link>
-  </article>
-</template>
-
-<style scoped>
-.home-page {
-  @apply flex flex-col items-center text-8xl mt-80;
-}
-</style>

+ 27 - 264
src/views/login/Login.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { onMounted, ref } from "vue";
+import {onMounted, ref} from "vue";
 import useRotateCanvas from "./scripts/rotateCanvas";
 import usePreloadImg from "./scripts/preloadImg";
 import useModuleItem from "./scripts/moduleItem";
@@ -8,34 +8,34 @@ import useModuleItem from "./scripts/moduleItem";
 const loginVideoRef = ref<HTMLVideoElement>();
 
 // 设置视频播放速度
-const setVideoRate = (rate = 0.75) => {
+const setVideoRate = (rate = 1) => {
   if (loginVideoRef.value) loginVideoRef.value.playbackRate = rate;
 };
 
 onMounted(() => {
   // 设置视频播放速度
-  setVideoRate();
+  // setVideoRate();
   // 预加载 模块item hover图片
   usePreloadImg();
 });
 
 // 底部旋转 canvas
-const { canvasRef } = useRotateCanvas();
-const { moduleRef } = useModuleItem();
+const {canvasRef} = useRotateCanvas();
+const {moduleRef} = useModuleItem();
 </script>
 
 <template>
   <article class="login-page">
     <!-- 视频背景 -->
     <video
-      ref="loginVideoRef"
-      class="video-bg"
-      poster="@/assets/login-bg-poster.jpg"
-      autoplay
-      loop
-      muted
+        ref="loginVideoRef"
+        class="video-bg"
+        poster="@/assets/login-bg-poster.jpg"
+        autoplay
+        loop
+        muted
     >
-      <source src="@/assets/login-bg-video.mp4" type="video/mp4" />
+      <source src="@/assets/login-bg-video.mp4" type="video/mp4"/>
     </video>
 
     <!-- 标题 -->
@@ -52,13 +52,13 @@ const { moduleRef } = useModuleItem();
       <div class="panel">
         <!-- 用户名 -->
         <div class="input-wrap username">
-          <input class="input" type="text" placeholder="用户名" />
-          <i class="line" />
+          <input class="input" type="text" placeholder="用户名"/>
+          <i class="line"/>
         </div>
         <!-- 密码 -->
         <div class="input-wrap password">
-          <input class="input" type="password" placeholder="密码" />
-          <i class="line" />
+          <input class="input" type="password" placeholder="密码"/>
+          <i class="line"/>
         </div>
         <!-- 登录按钮 -->
         <div class="login-btn">
@@ -77,14 +77,14 @@ const { moduleRef } = useModuleItem();
       <!-- 功能模块 -->
       <ul class="module" ref="moduleRef">
         <li
-          class="item budget animate__animated animate__bounceIn"
-          ref="budgetRef"
+            class="item budget animate__animated animate__bounceIn"
+            ref="budgetRef"
         >
           预算审核模块
         </li>
         <li
-          class="item estimate animate__animated animate__bounceIn"
-          ref="estimateRef"
+            class="item estimate animate__animated animate__bounceIn"
+            ref="estimateRef"
         >
           估/概算模块
         </li>
@@ -92,20 +92,20 @@ const { moduleRef } = useModuleItem();
           基础工具模块
         </li>
         <li
-          class="item settlement animate__animated animate__bounceIn"
-          ref="settlementRef"
+            class="item settlement animate__animated animate__bounceIn"
+            ref="settlementRef"
         >
           结算审核模块
         </li>
         <li
-          class="item final animate__animated animate__bounceIn"
-          ref="finalRef"
+            class="item final animate__animated animate__bounceIn"
+            ref="finalRef"
         >
           决算审核模块
         </li>
         <li
-          class="item check animate__animated animate__bounceIn"
-          ref="checkRef"
+            class="item check animate__animated animate__bounceIn"
+            ref="checkRef"
         >
           检测功能模块
         </li>
@@ -114,241 +114,4 @@ const { moduleRef } = useModuleItem();
   </article>
 </template>
 
-<style lang="scss" scoped>
-@keyframes aperture-rotate {
-  to {
-    transform: rotate(1turn);
-  }
-}
-
-@keyframes flash-change {
-  from {
-    left: -100%;
-  }
-
-  to {
-    left: 150%;
-  }
-}
-
-.login-page {
-  @apply w-full h-full overflow-hidden;
-  background-color: #011531;
-
-  .video-bg {
-    @apply fixed z-0 right-0 bottom-0 min-w-full min-h-full h-auto w-auto object-fill;
-    filter: brightness(0.45);
-  }
-
-  .header {
-    @apply flex justify-center w-full bg-contain bg-no-repeat bg-center text-center text-4xl select-none;
-    height: 68px;
-
-    .span {
-      background-size: 100% 100%;
-
-      &.left {
-        flex: 100 1 auto;
-        background-image: url("@/assets/login-header-bg1.png");
-      }
-
-      &.center {
-        flex: 1 0 590px;
-        background-image: url("@/assets/login-header-bg2.png");
-
-        .text {
-          @apply text-center font-bold bg-clip-text;
-          font-size: 36px;
-          line-height: 64px;
-          background-image: linear-gradient(0deg, #3084e8, #adf0f6 80%);
-          -webkit-text-fill-color: transparent;
-        }
-      }
-
-      &.right {
-        flex: 100 1 auto;
-        background-image: url("@/assets/login-header-bg3.png");
-      }
-    }
-  }
-
-  .main {
-    @apply relative flex justify-center items-center;
-    height: calc(100% - 68px);
-
-    .panel {
-      @apply relative z-40 flex items-center justify-center flex-col;
-      width: 487px;
-      height: 487px;
-      margin-top: -80px;
-      background: url("@/assets/login-panel.png") no-repeat;
-
-      .input-wrap {
-        @apply flex justify-center relative;
-        width: 250px;
-        height: 40px;
-        margin-top: 40px;
-        border-bottom: 2px solid #145da5;
-        background: no-repeat 0 center;
-        background-size: 31px 31px;
-
-        &.username {
-          background-image: url("@/assets/login-input-user-icon.svg");
-        }
-
-        &.password {
-          margin-top: 36px;
-          background-image: url("@/assets/login-input-password-icon.svg");
-        }
-
-        .input {
-          @apply w-full h-full outline-none bg-transparent;
-          font-size: 21px;
-          color: #8ae2ff;
-          padding-left: 50px;
-
-          &::-ms-input-placeholder {
-            color: #145da5;
-          }
-
-          &::-moz-placeholder {
-            color: #145da5;
-          }
-
-          &::-webkit-input-placeholder {
-            color: #145da5;
-          }
-
-          &:focus {
-            + .line {
-              @apply w-full;
-            }
-          }
-        }
-
-        .line {
-          @apply absolute w-0;
-          height: 2px;
-          content: " ";
-          bottom: -2px;
-          background-color: #459ef7;
-          transition: width 0.2s;
-        }
-      }
-
-      .login-btn {
-        @apply relative;
-        margin-top: 40px;
-
-        .button {
-          width: 120px;
-          height: 40px;
-          line-height: 40px;
-          background: url("@/assets/login-btn.png") no-repeat;
-          background-size: 100%;
-          color: #9ae1f9;
-          font-size: 18px;
-        }
-
-        .flash-wrap {
-          @apply absolute top-0 w-full h-full cursor-pointer;
-          clip-path: inset(0 0 round 10px);
-          transition: all 0.2s;
-
-          &:hover {
-            .flash {
-              animation: flash-change 1s ease 0s;
-            }
-          }
-
-          .flash {
-            @apply absolute top-0 w-1/3 h-full;
-            left: -100%;
-            background: linear-gradient(
-              to right,
-              rgba(255, 255, 255, 0) 0,
-              rgba(255, 255, 255, 0.4) 50%,
-              rgba(255, 255, 255, 0) 100%
-            );
-            transform: skewX(-45deg);
-            animation-iteration-count: infinite;
-          }
-        }
-      }
-    }
-
-    .aperture {
-      @apply absolute z-20;
-      width: 617px;
-      height: 617px;
-      margin-top: -80px;
-      background: url("@/assets/login-aperture.png") no-repeat center center;
-      background-size: contain;
-      animation: aperture-rotate 9s linear infinite;
-    }
-
-    .rotating-base {
-      @apply absolute z-10;
-      transform: scale(0.6);
-      margin-top: 450px;
-      filter: brightness(2);
-    }
-
-    .module {
-      @apply absolute z-30 flex justify-center items-center;
-      width: 1100px;
-      height: 500px;
-      margin-top: -60px;
-
-      .item {
-        @apply absolute flex justify-center cursor-pointer transform-gpu select-none opacity-95;
-        width: 180px;
-        height: 156px;
-        color: #44c4ef;
-        font-size: 22px;
-        padding-top: 40px;
-        background: url("@/assets/login-module-item-normal.png") no-repeat
-          center;
-        background-size: cover;
-        transition: all 0.26s;
-
-        &.can-hover:hover {
-          @apply opacity-100;
-          background-image: url("@/assets/login-module-item-hover.png");
-          transform: scale(1.06);
-        }
-
-        &.budget {
-          left: 40px;
-          top: 0;
-        }
-
-        &.estimate {
-          left: 0;
-          top: 170px;
-        }
-
-        &.util {
-          left: 80px;
-          top: 340px;
-        }
-
-        &.settlement {
-          right: 40px;
-          top: 0;
-        }
-
-        &.final {
-          right: 0;
-          top: 170px;
-        }
-
-        &.check {
-          right: 80px;
-          top: 340px;
-        }
-      }
-    }
-  }
-}
-</style>
+<style lang="scss" src="./style.scss" scoped></style>

+ 235 - 0
src/views/login/style.scss

@@ -0,0 +1,235 @@
+@keyframes aperture-rotate {
+    to {
+        transform: rotate(1turn);
+    }
+}
+
+@keyframes flash-change {
+    from {
+        left: -100%;
+    }
+
+    to {
+        left: 150%;
+    }
+}
+
+.login-page {
+    @apply w-full h-full overflow-hidden;
+    background-color: #011531;
+
+    .video-bg {
+        @apply fixed z-0 right-0 bottom-0 min-w-full min-h-full h-auto w-auto object-fill;
+        filter: brightness(0.7);
+    }
+
+    .header {
+        @apply flex justify-center w-full bg-contain bg-no-repeat bg-center text-center text-4xl select-none;
+        height: 68px;
+
+        .span {
+            background-size: 100% 100%;
+
+            &.left {
+                flex: 100 1 auto;
+                background-image: url("@/assets/login-header-bg1.png");
+            }
+
+            &.center {
+                flex: 1 0 590px;
+                background-image: url("@/assets/login-header-bg2.png");
+
+                .text {
+                    @apply text-center font-bold bg-clip-text;
+                    font-size: 36px;
+                    line-height: 64px;
+                    background-image: linear-gradient(0deg, #3084e8, #adf0f6 80%);
+                    -webkit-text-fill-color: transparent;
+                }
+            }
+
+            &.right {
+                flex: 100 1 auto;
+                background-image: url("@/assets/login-header-bg3.png");
+            }
+        }
+    }
+
+    .main {
+        @apply relative flex justify-center items-center;
+        height: calc(100% - 68px);
+
+        .panel {
+            @apply relative z-40 flex items-center justify-center flex-col;
+            width: 487px;
+            height: 487px;
+            margin-top: -80px;
+            background: url("@/assets/login-panel.png") no-repeat;
+
+            .input-wrap {
+                @apply flex justify-center relative;
+                width: 250px;
+                height: 40px;
+                margin-top: 40px;
+                border-bottom: 2px solid #145da5;
+                background: no-repeat 0 center;
+                background-size: 31px 31px;
+
+                &.username {
+                    background-image: url("@/assets/login-input-user-icon.svg");
+                }
+
+                &.password {
+                    margin-top: 36px;
+                    background-image: url("@/assets/login-input-password-icon.svg");
+                }
+
+                .input {
+                    @apply w-full h-full outline-none bg-transparent;
+                    font-size: 21px;
+                    color: #8ae2ff;
+                    padding-left: 50px;
+
+                    &::-ms-input-placeholder {
+                        color: #145da5;
+                    }
+
+                    &::-moz-placeholder {
+                        color: #145da5;
+                    }
+
+                    &::-webkit-input-placeholder {
+                        color: #145da5;
+                    }
+
+                    &:focus {
+                        + .line {
+                            @apply w-full;
+                        }
+                    }
+                }
+
+                .line {
+                    @apply absolute w-0;
+                    height: 2px;
+                    content: " ";
+                    bottom: -2px;
+                    background-color: #459ef7;
+                    transition: width 0.2s;
+                }
+            }
+
+            .login-btn {
+                @apply relative;
+                margin-top: 40px;
+
+                .button {
+                    width: 120px;
+                    height: 40px;
+                    line-height: 40px;
+                    background: url("@/assets/login-btn.png") no-repeat;
+                    background-size: 100%;
+                    color: #9ae1f9;
+                    font-size: 18px;
+                }
+
+                .flash-wrap {
+                    @apply absolute top-0 w-full h-full cursor-pointer;
+                    clip-path: inset(0 0 round 10px);
+                    transition: all 0.2s;
+
+                    &:hover {
+                        .flash {
+                            animation: flash-change 1s ease 0s;
+                        }
+                    }
+
+                    .flash {
+                        @apply absolute top-0 w-1/3 h-full;
+                        left: -100%;
+                        background: linear-gradient(
+                            to right,
+                            rgba(255, 255, 255, 0) 0,
+                            rgba(255, 255, 255, 0.4) 50%,
+                            rgba(255, 255, 255, 0) 100%
+                        );
+                        transform: skewX(-45deg);
+                        animation-iteration-count: infinite;
+                    }
+                }
+            }
+        }
+
+        .aperture {
+            @apply absolute z-20;
+            width: 617px;
+            height: 617px;
+            margin-top: -80px;
+            background: url("@/assets/login-aperture.png") no-repeat center center;
+            background-size: contain;
+            animation: aperture-rotate 12s linear infinite;
+        }
+
+        .rotating-base {
+            @apply absolute z-10;
+            transform: scale(0.6);
+            margin-top: 510px;
+            filter: brightness(2);
+        }
+
+        .module {
+            @apply absolute z-30 flex justify-center items-center;
+            width: 1100px;
+            height: 500px;
+            margin-top: -60px;
+
+            .item {
+                @apply absolute flex justify-center cursor-pointer transform-gpu select-none opacity-95;
+                width: 180px;
+                height: 156px;
+                color: #c1ecfa;
+                font-size: 22px;
+                padding-top: 40px;
+                background: url("@/assets/login-module-item-normal.png") no-repeat center;
+                background-size: cover;
+                transition: all 0.26s;
+
+                &.can-hover:hover {
+                    @apply opacity-100;
+                    background-image: url("@/assets/login-module-item-hover.png");
+                    transform: scale(1.06);
+                }
+
+                &.budget {
+                    left: 40px;
+                    top: 0;
+                }
+
+                &.estimate {
+                    left: 0;
+                    top: 170px;
+                }
+
+                &.util {
+                    left: 80px;
+                    top: 340px;
+                }
+
+                &.settlement {
+                    right: 40px;
+                    top: 0;
+                }
+
+                &.final {
+                    right: 0;
+                    top: 170px;
+                }
+
+                &.check {
+                    right: 80px;
+                    top: 340px;
+                }
+            }
+        }
+    }
+}

+ 54 - 0
src/views/main/Main.vue

@@ -0,0 +1,54 @@
+<script setup lang="ts">
+import { onMounted, ref } from "vue";
+</script>
+
+<template>
+  <article class="main-page">
+    <aside class="aside">
+      <section class="logo"></section>
+      <ul class="menu">
+        <router-link custom to="/" v-slot="{ navigate, isExactActive }">
+          <li
+            class="item dashboard"
+            :class="{ 'exact-active': isExactActive }"
+          >
+            <iconfont class="icon dsk-dashboard-one" />
+          </li>
+        </router-link>
+        <router-link custom to="/projects" v-slot="{ navigate, isExactActive }">
+          <li
+            class="item projects"
+            @click="navigate"
+            :class="{ 'exact-active': isExactActive }"
+          >
+            <iconfont class="icon dsk-city-one" />
+          </li>
+        </router-link>
+        <router-link custom to="/ration" v-slot="{ navigate, isExactActive }">
+          <li
+            class="item ration"
+            :class="{ 'exact-active': isExactActive }"
+          >
+            <iconfont class="icon dsk-data" />
+          </li>
+        </router-link>
+        <li class="item message">
+          <iconfont class="icon dsk-remind" />
+        </li>
+      </ul>
+      <ul class="tools">
+        <li class="item help">
+          <iconfont class="icon dsk-help" />
+        </li>
+        <li class="item avatar">
+          <iconfont class="icon dsk-me" />
+        </li>
+      </ul>
+    </aside>
+    <main class="main">
+      <router-view></router-view>
+    </main>
+  </article>
+</template>
+
+<style lang="scss" src="./style.scss" scoped></style>

+ 13 - 0
src/views/main/dashboard/Dashboard.vue

@@ -0,0 +1,13 @@
+<script setup lang="ts">
+import { onMounted, ref } from "vue";
+import { useRouter } from "vue-router";
+
+const router = useRouter();
+router.back();
+</script>
+
+<template>
+  <article class="dashboard-page">仪表盘</article>
+</template>
+
+<style lang="scss" src="./style.scss" scoped></style>

+ 0 - 0
src/views/main/dashboard/style.scss


+ 64 - 0
src/views/main/project-list/ProjectList.vue

@@ -0,0 +1,64 @@
+<script setup lang="ts">
+import { onMounted, reactive, ref } from "vue";
+const searchKey = ref("");
+const projectType = ref("");
+const projectTypeOptions = reactive([
+  {
+    label:'房建',
+    value: 'house'
+  },
+  {
+    label:'市政',
+    value: 'city'
+  }
+])
+</script>
+
+<template>
+  <article class="project-list-page">
+    <header class="header">投资项目</header>
+
+    <section class="filter-bar">
+      <el-input class="search-input" v-model="searchKey" placeholder="搜索项目">
+        <template #prefix>
+          <iconfont class="dsk-search" />
+        </template>
+      </el-input>
+      <el-select v-model="projectType" placeholder="工程类型">
+        <el-option
+          v-for="item in projectTypeOptions"
+          :key="item.value"
+          :label="item.label"
+          :value="item.value"
+        >
+        </el-option>
+      </el-select>
+      <el-select v-model="process" placeholder="项目流程">
+        <el-option
+          v-for="item in processOptions"
+          :key="item.value"
+          :label="item.label"
+          :value="item.value"
+        >
+        </el-option>
+      </el-select>
+      <el-select v-model="checkStep" placeholder="审核步骤">
+        <el-option
+          v-for="item in checkStepOptions"
+          :key="item.value"
+          :label="item.label"
+          :value="item.value"
+        >
+        </el-option>
+      </el-select>
+    </section>
+
+    <el-table :data="tableData">
+      <el-table-column prop="date" label="Date" width="180" />
+      <el-table-column prop="name" label="Name" width="180" />
+      <el-table-column prop="address" label="Address" />
+    </el-table>
+  </article>
+</template>
+
+<style lang="scss" src="./style.scss" scoped></style>

+ 25 - 0
src/views/main/project-list/style.scss

@@ -0,0 +1,25 @@
+.project-list-page {
+    .header {
+        @apply flex items-center font-bold;
+        height: 50px;
+        font-size: 20px;
+        padding-left: 10px;
+        border-bottom: 1px solid #e3e3e3;
+    }
+
+    .filter-bar {
+        @apply flex items-center;
+        height: 50px;
+        padding: 0 10px;
+        border-bottom: 1px solid #e3e3e3;
+        .search-input {
+            width: 150px;
+        }
+        .el-select {
+            width: 100px;
+        }
+    }
+
+    .el-table {
+    }
+}

+ 13 - 0
src/views/main/ration/Ration.vue

@@ -0,0 +1,13 @@
+<script setup lang="ts">
+import { onMounted, ref } from "vue";
+import { useRouter } from "vue-router";
+
+const router = useRouter();
+router.back();
+</script>
+
+<template>
+  <article class="ration-page">定额库</article>
+</template>
+
+<style lang="scss" src="./style.scss" scoped></style>

+ 0 - 0
src/views/main/ration/style.scss


+ 43 - 0
src/views/main/style.scss

@@ -0,0 +1,43 @@
+.main-page {
+    @apply flex w-full h-full;
+    .aside {
+        @apply flex flex-col items-center;
+        width: 49px;
+        border-right: 1px solid #e6e6e6;
+        .logo {
+            width: 49px;
+            height: 49px;
+            background: url("@/assets/logo.png") no-repeat center;
+            background-size: 34px 34px;
+        }
+        .menu {
+            @apply w-full;
+            flex: 1;
+            .item {
+                @apply flex justify-center items-center cursor-pointer;
+                height: 42px;
+                margin-top: 20px;
+                &.exact-active {
+                    background: #e6e6e6;
+                }
+                .icon {
+                    font-size: 18px;
+                }
+            }
+        }
+        .tools {
+            @apply w-full;
+            .item {
+                @apply flex justify-center items-center cursor-pointer;
+                margin-bottom: 10px;
+                .icon {
+                    font-size: 18px;
+                }
+            }
+        }
+    }
+
+    .main {
+        flex: 1;
+    }
+}