chauncey преди 1 година
родител
ревизия
73dfefa1d9
променени са 7 файла, в които са добавени 213 реда и са изтрити 27 реда
  1. BIN
      src/assets/images/user-avatar@1X.png
  2. 18 0
      src/assets/svg/arrow.svg
  3. 69 23
      src/components/Login.vue
  4. 30 4
      src/components/Nav.vue
  5. 57 0
      src/components/UserInfo.vue
  6. 28 0
      src/constant/nav.ts
  7. 11 0
      src/store/modules/mockUser.ts

BIN
src/assets/images/user-avatar@1X.png


Файловите разлики са ограничени, защото са твърде много
+ 18 - 0
src/assets/svg/arrow.svg


+ 69 - 23
src/components/Login.vue

@@ -3,35 +3,80 @@
     <div class="login-form">
       <img :src="exitIcon" alt="关闭" class="exit-icon" @click="emit('close')" />
       <header class="login-form__header">
-        <span>登录</span>
+        <span>{{ type !== 'modifyPassword' ? '登录' : '修改密码' }}</span>
       </header>
       <main class="login-form__main">
-        <el-input v-model="username" placeholder="请输入您的账号" class="el-input--default" />
-        <el-input
-          v-model="password"
-          placeholder="请输入您的密码"
-          type="password"
-          show-password
-          class="el-input--default"
-        />
-        <div class="login-form__code">
-          <el-input v-model="code" placeholder="验证码" class="el-input--default" />
-        </div>
+        <el-form ref="formRef" :model="login" label-width="auto" class="login-form__form">
+          <el-form-item prop="username" :rules="[{ required: true, message: '账号不能为空' }]">
+            <el-input
+              placeholder="请输入您的账号"
+              v-model="login.username"
+              type="text"
+              autocomplete="off"
+              clearable
+              class="el-input--default"
+            />
+          </el-form-item>
+          <el-form-item prop="password" :rules="[{ required: true, message: '密码不能为空' }]">
+            <el-input
+              placeholder="请输入您的密码"
+              v-model="login.password"
+              type="password"
+              autocomplete="off"
+              show-password
+              clearable
+              class="el-input--default"
+            />
+          </el-form-item>
+          <el-form-item
+            prop="code"
+            :rules="[{ required: true, message: '验证码不能为空' }]"
+            v-if="type !== 'modifyPassword'"
+          >
+            <el-input placeholder="验证码" v-model="login.code" clearable class="el-input--default" />
+          </el-form-item>
+        </el-form>
       </main>
       <footer class="login-form__footer">
-        <el-button class="login-form__button" @click="emit('close')">登录</el-button>
+        <el-button class="login-form__button" @click="handleLogin">
+          {{ type !== 'modifyPassword' ? '登录' : '修改密码' }}
+        </el-button>
       </footer>
     </div>
   </div>
 </template>
 
 <script lang="ts" setup>
-  import { ref } from 'vue';
+  import { ref, reactive } from 'vue';
   import exitIcon from 'assets/svg/exit.svg';
-  const username = ref('');
-  const password = ref('');
-  const code = ref('');
+  import type { FormInstance } from 'element-plus';
+  import { ElMessage } from 'element-plus';
+  import useMockUserStore from '@/store/modules/mockUser';
+  import { storeToRefs } from 'pinia';
+  const mockUserStore = useMockUserStore();
+  const { userInfo } = storeToRefs(mockUserStore);
+  const props = defineProps<{
+    type: 'login' | 'switchAccount' | 'modifyPassword';
+  }>();
+  const login = reactive({
+    username: '',
+    password: '',
+    code: '',
+  });
   const emit = defineEmits(['close']);
+  const formRef = ref<FormInstance>();
+  const handleLogin = () => {
+    if (!formRef.value) return;
+    formRef.value.validate((valid: boolean) => {
+      if (valid) {
+        userInfo.value = login.username;
+        const message =
+          props.type === 'login' ? '登录成功' : props.type === 'switchAccount' ? '切换账号成功' : '修改密码成功';
+        ElMessage.success(message);
+        emit('close');
+      }
+    });
+  };
 </script>
 
 <style lang="scss" scoped>
@@ -48,7 +93,6 @@
   .login-form {
     position: relative;
     width: 700cpx;
-    height: 514cpx;
     padding: 0 33cpx;
     background: $white-color;
     border-radius: 24cpx;
@@ -79,7 +123,7 @@
     &__footer {
       width: 100%;
       height: 100cpx;
-      margin-top: 36cpx;
+      margin-top: 18cpx;
       .login-form__button {
         width: 100%;
         height: 74cpx;
@@ -99,6 +143,12 @@
       cursor: pointer;
     }
   }
+  .login-form__form {
+    display: flex;
+    flex-direction: column;
+    gap: 2cpx;
+    width: 100%;
+  }
   .el-input--default {
     width: 100%;
     height: 72cpx;
@@ -107,8 +157,4 @@
       color: #999;
     }
   }
-  :deep(.el-icon svg) {
-    width: 30cpx;
-    height: 30cpx;
-  }
 </style>

+ 30 - 4
src/components/Nav.vue

@@ -17,16 +17,21 @@
       <div class="platform__right__search">
         <el-input v-model="searchValue" placeholder="搜索您想了解的" class="input-with-icon" clearable>
           <template #prepend>
-            <img :src="searchIcon" alt="search" class="search-icon" @click="handleSearch"/>
+            <img :src="searchIcon" alt="search" class="search-icon" @click="handleSearch" />
           </template>
         </el-input>
       </div>
-      <div class="platform__right__login" @click="showLogin = true">
-        <span>登录</span>
+      <div class="platform__right__login">
+        <span @click="handleLogin('login')" v-if="!userInfo">登录</span>
+        <UserInfo
+          v-else
+          @switchAccount="handleLogin('switchAccount')"
+          @modifyPassword="handleLogin('modifyPassword')"
+        />
       </div>
     </div>
   </header>
-  <Login v-if="showLogin" @close="showLogin = false" class="fadeIn" />
+  <Login v-if="showLogin" :type="loginType" @close="showLogin = false" class="fadeIn" />
 </template>
 
 <script lang="ts" setup>
@@ -36,16 +41,34 @@
   import logo from 'assets/images/home/comac-logo@1X.png';
   import searchIcon from 'assets/svg/search.svg';
   import Login from '@/components/Login.vue';
+  import UserInfo from '@/components/UserInfo.vue';
+  import useMockUserStore from '@/store/modules/mockUser';
+  import { storeToRefs } from 'pinia';
+  const mockUserStore = useMockUserStore();
+  const { userInfo } = storeToRefs(mockUserStore);
   const activeNav = ref(NAV_LIST[0].name);
   const router = useRouter();
   const showLogin = ref(false);
   const searchValue = ref('');
+  const loginType = ref<'login' | 'switchAccount' | 'modifyPassword'>('login');
 
   const handleSearch = () => {
     console.log('searchValue', searchValue.value);
   };
 
+  const handleLogin = (type: 'login' | 'switchAccount' | 'modifyPassword') => {
+    loginType.value = type;
+    showLogin.value = true;
+  };
+
   const handleNavClick = (item: { name: string; path: string }) => {
+    // 如果不是首页,先检查用户是否登录
+    if (item.name !== '首页' && !userInfo.value) {
+      handleLogin('login');
+      return;
+    }
+    if (!item.path) return;
+
     activeNav.value = item.name;
     router.push(item.path);
   };
@@ -69,6 +92,7 @@
         padding: 10cpx 20cpx;
         gap: 8cpx;
         font-size: 18cpx;
+        color: #333;
         border-radius: 4cpx;
         cursor: pointer;
         transition: all 0.3s ease-in-out;
@@ -103,6 +127,8 @@
       background: rgba($white-color, 0.4);
     }
     &__login {
+      @include flex-center;
+      gap: 10cpx;
       height: 100%;
       padding: 27cpx 40cpx 26cpx 40cpx;
       font-size: 18cpx;

+ 57 - 0
src/components/UserInfo.vue

@@ -0,0 +1,57 @@
+<template>
+  <el-dropdown trigger="click">
+    <div class="user-info">
+      <img :src="UserAvatar" class="user-avatar" />
+      <span class="user-name">{{ userInfo }}</span>
+      <img :src="ArrowIcon" class="dropdown-icon" />
+    </div>
+    <template #dropdown>
+      <el-dropdown-menu>
+        <el-dropdown-item @click="emit('switchAccount')">切换账号</el-dropdown-item>
+        <el-dropdown-item @click="emit('modifyPassword')">修改密码</el-dropdown-item>
+        <el-dropdown-item @click="userInfo = ''">退出登录</el-dropdown-item>
+      </el-dropdown-menu>
+    </template>
+  </el-dropdown>
+</template>
+
+<script lang="ts" setup>
+  import UserAvatar from 'assets/images/user-avatar@1X.png';
+  import ArrowIcon from 'assets/svg/arrow.svg';
+  import useMockUserStore from '@/store/modules/mockUser';
+  import { storeToRefs } from 'pinia';
+  const mockUserStore = useMockUserStore();
+  const { userInfo } = storeToRefs(mockUserStore);
+  const emit = defineEmits(['switchAccount', 'modifyPassword']);
+</script>
+
+<style lang="scss" scoped>
+  .user-info {
+    @include flex-center;
+    gap: 4cpx;
+  }
+  .user-avatar {
+    width: 30cpx;
+    height: 30cpx;
+  }
+  .user-name {
+    font-weight: 400;
+    font-size: 14cpx;
+    color: $text-color;
+  }
+  .dropdown-icon {
+    width: 25cpx;
+    height: 25cpx;
+  }
+  :deep(.el-dropdown-menu__item) {
+    padding: 4cpx 37cpx;
+    font-weight: 400;
+    font-size: 14cpx;
+    color: #606266;
+    &:hover {
+      background-color: $primary-color;
+      color: $white-color;
+      border-radius: 4cpx;
+    }
+  }
+</style>

+ 28 - 0
src/constant/nav.ts

@@ -8,8 +8,36 @@ export const NAV_LIST = [
     name: '首页',
     path: '/home',
   },
+  {
+    name: '院内安全态势',
+    path: '',
+  },
+  {
+    name: '生产安全',
+    path: '',
+  },
+  {
+    name: '交通安全',
+    path: '',
+  },
+  {
+    name: '保卫保密',
+    path: '',
+  },
   {
     name: '灾害防范',
     path: '/disaster-prevention',
   },
+  {
+    name: '应急管理',
+    path: '',
+  },
+  {
+    name: '智慧视觉',
+    path: '',
+  },
+  {
+    name: '物联集成',
+    path: '',
+  },
 ];

+ 11 - 0
src/store/modules/mockUser.ts

@@ -0,0 +1,11 @@
+import { defineStore } from 'pinia';
+import { ref } from 'vue';
+
+const useMockUserStore = defineStore('mockUser', () => {
+  const userInfo = ref('');
+  return {
+    userInfo,
+  };
+});
+
+export default useMockUserStore;