Kaynağa Gözat

迎新接口已完成

hzj18279462576@163.com 1 yıl önce
ebeveyn
işleme
66d8bab065
80 değiştirilmiş dosya ile 26297 ekleme ve 1 silme
  1. 24 0
      .gitignore
  2. 3 0
      .vscode/extensions.json
  3. 36 0
      README.en.md
  4. 5 1
      README.md
  5. 13 0
      index.html
  6. 5833 0
      package-lock.json
  7. 31 0
      package.json
  8. 1 0
      public/vite.svg
  9. 174 0
      src/App.vue
  10. BIN
      src/assets/img/404.png
  11. BIN
      src/assets/img/add.png
  12. BIN
      src/assets/img/add_1.png
  13. BIN
      src/assets/img/bg.png
  14. BIN
      src/assets/img/jiaofei.png
  15. BIN
      src/assets/img/login3.png
  16. BIN
      src/assets/img/login_pass.png
  17. BIN
      src/assets/img/login_user.png
  18. BIN
      src/assets/img/logo.png
  19. BIN
      src/assets/img/luqu.png
  20. BIN
      src/assets/img/nanchang.png
  21. BIN
      src/assets/img/pass.png
  22. BIN
      src/assets/img/phone.png
  23. BIN
      src/assets/img/quit.png
  24. BIN
      src/assets/img/ruzhu.png
  25. BIN
      src/assets/img/yubaodao.png
  26. BIN
      src/assets/img/yuyue.png
  27. 6 0
      src/assets/svgs/add.svg
  28. 8 0
      src/assets/svgs/building.svg
  29. 8 0
      src/assets/svgs/caller.svg
  30. 6 0
      src/assets/svgs/car.svg
  31. 4 0
      src/assets/svgs/carNum.svg
  32. 6 0
      src/assets/svgs/dataOverview.svg
  33. 4 0
      src/assets/svgs/delete.svg
  34. 4 0
      src/assets/svgs/dorm.svg
  35. 6 0
      src/assets/svgs/dormitory.svg
  36. 4 0
      src/assets/svgs/entourage.svg
  37. 6 0
      src/assets/svgs/family.svg
  38. 6 0
      src/assets/svgs/filling_details.svg
  39. 4 0
      src/assets/svgs/jiaofei.svg
  40. 11 0
      src/assets/svgs/luqu.svg
  41. 9 0
      src/assets/svgs/pay.svg
  42. 6 0
      src/assets/svgs/quarterage.svg
  43. 6 0
      src/assets/svgs/role.svg
  44. 6 0
      src/assets/svgs/ruzhu.svg
  45. 8 0
      src/assets/svgs/student.svg
  46. 18 0
      src/assets/svgs/student1.svg
  47. 6 0
      src/assets/svgs/studentInfo.svg
  48. 6 0
      src/assets/svgs/system.svg
  49. 1 0
      src/assets/svgs/teacher.svg
  50. 11 0
      src/assets/svgs/yubao.svg
  51. 43 0
      src/components/HelloWorld.vue
  52. 41 0
      src/components/SvgIcon.vue
  53. 372 0
      src/layout/Navbar.vue
  54. 265 0
      src/layout/SidevarItem copy.vue
  55. 365 0
      src/layout/SidevarItem.vue
  56. 129 0
      src/layout/SidevarItem报修.vue
  57. 36 0
      src/main.js
  58. 116 0
      src/router/index.js
  59. 281 0
      src/stores/index.js
  60. 79 0
      src/style.css
  61. 1 0
      src/styles/mixin.scss
  62. 107 0
      src/utils/request.js
  63. 20 0
      src/utils/rsa.js
  64. 196 0
      src/views/404/404.vue
  65. 2237 0
      src/views/bed/bed.vue
  66. 973 0
      src/views/building/building.vue
  67. 835 0
      src/views/caller/caller.vue
  68. 798 0
      src/views/dataOverview/dataOverview.vue
  69. 1412 0
      src/views/dormitory/dormitory.vue
  70. 294 0
      src/views/login/login.vue
  71. 544 0
      src/views/quarterage/quarterage.vue
  72. 236 0
      src/views/richtext/richtext.vue
  73. 1306 0
      src/views/role/role.vue
  74. 824 0
      src/views/student/student.vue
  75. 3177 0
      src/views/studentInfo/studentInfo copy.vue
  76. 146 0
      src/views/studentInfo/studentInfo.java
  77. 3225 0
      src/views/studentInfo/studentInfo.vue
  78. 1064 0
      src/views/system/system.vue
  79. 808 0
      src/views/user/user.vue
  80. 87 0
      vite.config.js

+ 24 - 0
.gitignore

@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 3 - 0
.vscode/extensions.json

@@ -0,0 +1,3 @@
+{
+  "recommendations": ["Vue.volar"]
+}

+ 36 - 0
README.en.md

@@ -0,0 +1,36 @@
+# VUE3+vite+JS+pinia
+
+#### Description
+{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}
+
+#### Software Architecture
+Software architecture description
+
+#### Installation
+
+1.  xxxx
+2.  xxxx
+3.  xxxx
+
+#### Instructions
+
+1.  xxxx
+2.  xxxx
+3.  xxxx
+
+#### Contribution
+
+1.  Fork the repository
+2.  Create Feat_xxx branch
+3.  Commit your code
+4.  Create Pull Request
+
+
+#### Gitee Feature
+
+1.  You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
+2.  Gitee blog [blog.gitee.com](https://blog.gitee.com)
+3.  Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
+4.  The most valuable open source project [GVP](https://gitee.com/gvp)
+5.  The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
+6.  The most popular members  [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

+ 5 - 1
README.md

@@ -1 +1,5 @@
-#onlineNew_manage
+# Vue 3 + Vite
+
+This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
+
+Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).

+ 13 - 0
index.html

@@ -0,0 +1,13 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="./src/assets/img/logo.png" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>迎新宿舍管理系统</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.js"></script>
+  </body>
+</html>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 5833 - 0
package-lock.json


+ 31 - 0
package.json

@@ -0,0 +1,31 @@
+{
+  "name": "pack",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "@popperjs/core": "^2.11.8",
+    "axios": "^1.8.4",
+    "echarts": "^5.6.0",
+    "element-plus": "^2.9.7",
+    "jsencrypt": "^3.3.2",
+    "nprogress": "^0.2.0",
+    "pinia": "^3.0.2",
+    "vue": "^3.5.13",
+    "vue-router": "^4.5.0"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^5.2.2",
+    "fast-glob": "^3.3.3",
+    "sass": "^1.86.3",
+    "sass-loader": "^16.0.5",
+    "terser": "^5.43.1",
+    "vite": "^6.3.1",
+    "vite-plugin-svg-icons": "^2.0.1"
+  }
+}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
public/vite.svg


+ 174 - 0
src/App.vue

@@ -0,0 +1,174 @@
+<template>
+  <div class="body-box" v-if="isShowHeader">
+    <SidevarItem></SidevarItem>
+    <div class="content">
+      <Navbar></Navbar>
+      <!-- <router-view></router-view> -->
+
+      <router-view v-slot="{ Component, route }">
+        <transition name="animation" mode="out-in">
+          <component :is="Component" :key="route.path" />
+        </transition>
+      </router-view>
+    </div>
+  </div>
+  <div class="body-box" v-else>
+    <router-view></router-view>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onBeforeMount, onMounted, watch } from "vue";
+import Navbar from "@/layout/Navbar.vue";
+import SidevarItem from "@/layout/SidevarItem.vue";
+import { useRouter } from "vue-router";
+
+import { storeToRefs } from "pinia";
+import { useCounterStore } from "@/stores/index";
+const store = useCounterStore();
+
+const router = useRouter();
+const isShowHeader = ref(true);
+
+// 监听路由判断是否显示导航条
+watch(
+  () => router.currentRoute.value,
+  (newValue) => {
+    // console.log(newValue);
+    store.setItem(newValue.fullPath);
+    isShowHeader.value = newValue.meta.showHeader;
+  },
+  { immediate: true, deep: true }
+);
+
+const tableData = reactive({
+  list: [],
+});
+onBeforeMount(async () => {
+  // console.log('App/onBeforeMount');
+});
+</script>
+
+<style lang="scss">
+#app {
+  width: 100%;
+  height: 100%;
+  margin: 0;
+  padding: 0;
+  text-align: unset;
+  overflow: auto;
+}
+
+.el-dialog {
+  border-radius: 5px !important;
+  padding: 0 !important;
+  .el-dialog__header {
+    border-radius: 5px 5px 0 0;
+    background: rgba(237, 241, 245, 1);
+    font-weight: 600;
+    height: 56px;
+    margin: 0;
+    padding: 0 0 0 20px;
+    display: flex;
+    align-items: center;
+    .el-dialog__headerbtn {
+      outline: none;
+      top: 4px;
+    }
+  }
+  .el-dialog__body {
+    padding: 20px 20px 0 20px;
+    .el-input {
+      width: 220px;
+    }
+    .options {
+      display: flex;
+      flex-direction: row-reverse;
+      .el-form-item__content {
+        margin-left: 0 !important;
+        flex: none;
+      }
+    }
+  }
+}
+
+.is-message-box {
+  .el-message-box__header {
+    .el-message-box__headerbtn {
+      outline: none;
+    }
+  }
+
+  .el-message-box__btns {
+    .el-button {
+      outline: none;
+    }
+    .el-button--primary {
+      background-color: rgba(61, 81, 232, 1);
+      border: 0.5px solid rgba(61, 81, 232, 1);
+    }
+
+    .el-button--primary:hover {
+      background-color: rgb(119, 133, 239);
+      border: 0.5px solid rgb(119, 133, 239);
+    }
+  }
+}
+
+*::-webkit-scrollbar-track {
+  background-color: #fff;
+}
+
+*::-webkit-scrollbar {
+  background-color: #57b2ff;
+  height: 0;
+  width: 0;
+}
+
+*::-webkit-scrollbar-thumb {
+  background: #57b2ff;
+  border-radius: 4px;
+  height: 0;
+  width: 0;
+}
+
+// 进度条颜色
+#nprogress .bar {
+  background-color: rgb(111, 182, 184) !important;
+}
+</style>
+<style scoped lang="scss">
+.body-box {
+  display: flex;
+  width: 100%;
+
+  .content {
+    min-width: calc(100% - 200px);
+    max-width: calc(100% - 63px);
+    width: 100%;
+    height: 100vh;
+    background-color: rgba(238, 238, 238, 1);
+
+    :deep(.el-dialog__header .el-dialog__headerbtn) {
+      outline: none;
+    }
+  }
+}
+
+/* 过度动画配置代码 */
+.animation-enter-from,
+.animation-leave-to {
+	transform: translateX(20px);
+	opacity: 0;
+}
+.animation-enter-to,
+.animation-leave-from {
+	opacity: 1;
+}
+.animation-enter-active {
+	transition: all 0.7s ease;
+}
+.animation-leave-active {
+	transition: all 0.3s cubic-bezier(1, 0.6, 0.6, 1);
+}
+</style>

BIN
src/assets/img/404.png


BIN
src/assets/img/add.png


BIN
src/assets/img/add_1.png


BIN
src/assets/img/bg.png


BIN
src/assets/img/jiaofei.png


BIN
src/assets/img/login3.png


BIN
src/assets/img/login_pass.png


BIN
src/assets/img/login_user.png


BIN
src/assets/img/logo.png


BIN
src/assets/img/luqu.png


BIN
src/assets/img/nanchang.png


BIN
src/assets/img/pass.png


BIN
src/assets/img/phone.png


BIN
src/assets/img/quit.png


BIN
src/assets/img/ruzhu.png


BIN
src/assets/img/yubaodao.png


BIN
src/assets/img/yuyue.png


Dosya farkı çok büyük olduğundan ihmal edildi
+ 6 - 0
src/assets/svgs/add.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 8 - 0
src/assets/svgs/building.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 8 - 0
src/assets/svgs/caller.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 6 - 0
src/assets/svgs/car.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 4 - 0
src/assets/svgs/carNum.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 6 - 0
src/assets/svgs/dataOverview.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 4 - 0
src/assets/svgs/delete.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 4 - 0
src/assets/svgs/dorm.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 6 - 0
src/assets/svgs/dormitory.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 4 - 0
src/assets/svgs/entourage.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 6 - 0
src/assets/svgs/family.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 6 - 0
src/assets/svgs/filling_details.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 4 - 0
src/assets/svgs/jiaofei.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 11 - 0
src/assets/svgs/luqu.svg


+ 9 - 0
src/assets/svgs/pay.svg

@@ -0,0 +1,9 @@
+<svg t="1750908578163" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1717"
+    width="256" height="256">
+    <path
+        d="M643.328 568.32h-93.5424v-52.6848h93.5424a30.72 30.72 0 0 0 0-61.44h-56.32l62.6176-71.3728a30.72 30.72 0 1 0-46.08-40.5504l-84.48 96.0512-84.2752-96.0512a30.72 30.72 0 0 0-46.08 40.5504l62.5152 71.3728h-56.32a30.72 30.72 0 0 0 0 61.44h93.44V568.32H394.8032a30.72 30.72 0 0 0 0 61.44h93.5424v54.784a30.72 30.72 0 0 0 61.44 0V629.76h93.5424a30.72 30.72 0 0 0 0-61.44z"
+         p-id="1718"></path>
+    <path
+        d="M861.5936 927.8976h-352.768a414.3104 414.3104 0 1 1 379.904-248.4224 30.72 30.72 0 1 1-56.32-24.6272 353.28 353.28 0 1 0-323.6352 211.6096h278.6816l-83.5584-83.5584a30.72 30.72 0 0 1 43.4176-43.4176L883.3536 875.52a30.72 30.72 0 0 1-21.76 52.4288z"
+         p-id="1719"></path>
+</svg>

+ 6 - 0
src/assets/svgs/quarterage.svg

@@ -0,0 +1,6 @@
+<svg t="1750921052345" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
+    p-id="15949" width="200" height="200">
+    <path
+        d="M929 959H95a30 30 0 1 1 0-60h26V115a50 50 0 0 1 50-50h460a50 50 0 0 1 50 50v784h162V462a20 20 0 0 0-20-20h-43a30 30 0 0 1 0-60h73a50 50 0 0 1 50 50v467h26a30 30 0 0 1 0 60zM621 145a20 20 0 0 0-20-20H201a20 20 0 0 0-20 20v754h440V145z m-313 87h186a30 30 0 0 1 0 60H308a30 30 0 0 1 0-60z m0 150h186a30 30 0 0 1 0 60H308a30 30 0 0 1 0-60z m0 150h186a30 30 0 0 1 0 60H308a30 30 0 0 1 0-60z m0 150h186a30 30 0 0 1 0 60H308a30 30 0 0 1 0-60z"
+        fill="#000000" p-id="15950"></path>
+</svg>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 6 - 0
src/assets/svgs/role.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 6 - 0
src/assets/svgs/ruzhu.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 8 - 0
src/assets/svgs/student.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 18 - 0
src/assets/svgs/student1.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 6 - 0
src/assets/svgs/studentInfo.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 6 - 0
src/assets/svgs/system.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
src/assets/svgs/teacher.svg


Dosya farkı çok büyük olduğundan ihmal edildi
+ 11 - 0
src/assets/svgs/yubao.svg


+ 43 - 0
src/components/HelloWorld.vue

@@ -0,0 +1,43 @@
+<script setup>
+import { ref } from 'vue'
+
+defineProps({
+  message: String,
+})
+
+const count = ref(0)
+</script>
+
+<template>
+  <h1>{{ message }}</h1>
+
+  <div class="card">
+    <button type="button" @click="count++">count is {{ count }}</button>
+    <p>
+      Edit
+      <code>components/HelloWorld.vue</code> to test HMR
+    </p>
+  </div>
+
+  <p>
+    Check out
+    <a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
+      >create-vue</a
+    >, the official Vue + Vite starter
+  </p>
+  <p>
+    Learn more about IDE Support for Vue in the
+    <a
+      href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
+      target="_blank"
+      >Vue Docs Scaling up Guide</a
+    >.
+  </p>
+  <p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
+</template>
+
+<style scoped>
+.read-the-docs {
+  color: #888;
+}
+</style>

+ 41 - 0
src/components/SvgIcon.vue

@@ -0,0 +1,41 @@
+<script setup>
+import { computed } from "vue";
+
+let props = defineProps({
+  name: {
+    type: String,
+    required: false,
+  },
+  prefix: {
+    type: String,
+    default: "icon",
+  },
+  color: {
+    type: String,
+    default: "#000",
+  },
+  size: {
+    type: String,
+    default: "22",
+  },
+});
+
+const symbolId = computed(() => `#${props.prefix}-${props.name}`);
+</script>
+
+<template>
+  <svg
+    aria-hidden="true"
+    class="el-icon"
+    :style="{ 'width': size + 'px', 'height': size + 'px', 'fill': color }"
+  >
+    <use :xlink:href="symbolId" :fill="color" />
+  </svg>
+</template>
+
+<style lang="scss" scoped>
+.svg-icon {
+  width: 100%;
+  height: 100%;
+}
+</style>

+ 372 - 0
src/layout/Navbar.vue

@@ -0,0 +1,372 @@
+<template>
+  <el-menu
+    :default-active="activeIndex"
+    class="el-menu-demo"
+    mode="horizontal"
+    background-color=""
+    text-color="#000"
+    active-text-color="#4392f7"
+    @select="handleSelect"
+  >
+    <el-icon
+      :size="20"
+      v-show="!isCollapse"
+      class="fold"
+      @click="handleClick"
+      color="#000"
+      ><Fold
+    /></el-icon>
+    <el-icon :size="20" v-show="isCollapse" class="fold" @click="handleClick"
+      ><Expand
+    /></el-icon>
+    <div class="logo">
+      <!-- <img src="@/assets/img/nanchang.png" style="width: 30px; height: 30px" /> -->
+      <span>迎新宿舍管理系统</span>
+    </div>
+    <div class="login">
+      <span class="dateTime">{{ dateTime }}</span>
+      <!-- <el-badge :value="titlenumber" class="item">
+        <SvgIcon
+          name="news"
+          class="iconClass"
+          style="cursor: pointer"
+        ></SvgIcon>
+      </el-badge> -->
+      <div class="flex flex-wrap items-center" style="cursor: pointer">
+        <el-dropdown :hide-on-click="false" trigger="click">
+          <el-avatar :size="30">
+            <img src="@/assets/img/phone.png" />
+          </el-avatar>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <!-- <el-dropdown-item @click="uppassword">
+                <el-icon class="switchButton" :size="20" color="#000">
+                  <img style="width: 20px;height: 20px;" src="@/assets/img/pass.png" />
+                </el-icon>
+                <span>修改密码</span>
+              </el-dropdown-item> -->
+
+              <el-dropdown-item @click="loginOut">
+                <el-icon class="switchButton" :size="20" color="#000">
+                  <!-- <SwitchButton /> -->
+                  <img style="width: 20px;height: 20px;" src="@/assets/img/quit.png" />
+                </el-icon>
+                <span>退出登录</span>
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
+      </div>
+      <span class="name">{{ userName }}</span>
+    </div>
+  </el-menu>
+  <el-dialog
+    :append-to-body="true"
+    class="pw"
+    v-model="uppasswordShow"
+    title="修改密码"
+    width="520"
+    :before-close="uppasswordClose"
+    align-center
+    :close-on-click-modal="false"
+  >
+    <el-form
+      ref="ruleFormRef"
+      :model="ruleForm"
+      :rules="rules"
+      label-width="100px"
+      class="demo-ruleForm"
+      :size="formSize"
+      label-position="left"
+      status-icon
+    >
+      <el-form-item label="原密码" prop="oldpass">
+        <el-input
+          v-model="ruleForm.oldpass"
+          placeholder="请输入原密码"
+          clearable
+        />
+      </el-form-item>
+      <el-form-item label="新密码" prop="nowpass">
+        <el-input
+          v-model="ruleForm.nowpass"
+          placeholder="请输入新密码"
+          clearable
+        />
+      </el-form-item>
+      <el-form-item
+        label="确定新密码"
+        prop="correctpass"
+        style="
+          padding-bottom: 40px;
+          border-bottom: 1px solid rgba(230, 230, 230, 1);
+        "
+      >
+        <el-input
+          v-model="ruleForm.correctpass"
+          placeholder="请再次输入新密码"
+          clearable
+        />
+      </el-form-item>
+
+      <el-form-item class="options">
+        <el-button
+          class="queding"
+          type="primary"
+          @click="submitForm(ruleFormRef)"
+        >
+          确定
+        </el-button>
+        <el-button class="congzhi" @click="resetForm(ruleFormRef)"
+          >重置</el-button
+        >
+      </el-form-item>
+    </el-form>
+  </el-dialog>
+</template>
+
+<script setup>
+import { ref, reactive, onBeforeMount, onMounted, watch } from "vue";
+import { ElMessage, ElMessageBox, ElNotification } from "element-plus";
+import { useRouter } from "vue-router";
+import { dayjs } from "element-plus";
+import { Fold, Expand, Lock, SwitchButton } from "@element-plus/icons-vue";
+
+import { storeToRefs } from "pinia";
+import { useCounterStore } from "@/stores/index";
+
+const store = useCounterStore();
+const { isCollapse } = storeToRefs(store);
+const handleClick = () => {
+  console.log("触发");
+  
+  store.setInfo(!isCollapse.value);
+};
+const router = useRouter();
+const userName = ref(); // 账号名称
+const titlenumber = ref(1); //  消息条数
+const activeIndex = ref(); // 路由路径
+const loginOutDialogVisible = ref(false); // 退出按钮
+const menuclose = ref(false); // 左边状态栏展开按钮状态
+const uppasswordShow = ref(false); // 修改密码展示框
+const ruleForm = reactive({
+  oldpass: "",
+  nowpass: "",
+  correctpass: "",
+});
+const ruleFormRef = ref();
+// 表单验证
+const rules = reactive({
+  oldpass: [
+    {
+      required: true,
+      message: "密码不能为空",
+      trigger: "blur",
+    },
+  ],
+  nowpass: [
+    {
+      required: true,
+      message: "新密码不能为空",
+      trigger: "blur",
+    },
+  ],
+  correctpass: [
+    {
+      required: true,
+      message: "两次密码是否输入正确",
+      trigger: "blur",
+    },
+  ],
+  // desc: [{ required: true, message: "Please input activity form", trigger: "blur" }],
+});
+
+const dateTime = ref(
+  dayjs().format("YYYY-MM-DD") + ` 星期四 ` + dayjs().format("HH:mm:ss")
+); // 时间
+
+// 选择菜单
+const handleSelect = (key, keyPath) => {
+  activeIndex.value = key;
+};
+
+// 修改密码
+const uppassword = () => {
+  uppasswordShow.value = true;
+};
+const uppasswordClose = () => {
+  uppasswordShow.value = false;
+  ruleFormRef.value.resetFields();
+};
+// 提交修改密码表单
+const submitForm = async (formEl) => {
+  if (!formEl) return;
+  await formEl.validate(async (valid, fields) => {
+    if (valid) {
+      let data = {
+        oldPassword: ruleForm.oldpass,
+        newPassword: ruleForm.nowpass,
+        confirmPassword: ruleForm.correctpass,
+      };
+      if (ruleForm.nowpass == ruleForm.correctpass) {
+        // let resUpdata = await axios({
+        //   method: "post",
+        //   url: api.value + "/login/ChangePassword",
+        //   headers: {
+        //     token: sessionStorage.getItem("token"),
+        //     user_head: sessionStorage.getItem("userhead"),
+        //   },
+        //   data: data,
+        // });
+        // console.log(resUpdata);
+        // if (resUpdata.data.code == 200) {
+        //   ElMessage({
+        //     type: "success",
+        //     showClose: true,
+        //     message: resUpdata.data.message,
+        //     center: true,
+        //   });
+        //   sessionStorage.removeItem("token");
+        //   sessionStorage.removeItem("userhead");
+        //   localStorage.removeItem("pass");
+        //   uppasswordShow.value = false;
+        //   ruleFormRef.value.resetFields();
+        //   ElNotification({
+        //     type: "warning",
+        //     title: "提示!!!",
+        //     message: "密码已修改,请重新登录页面",
+        //     // duration: 0,
+        //     // position: "top-right",
+        //   });
+        // } else {
+        //   ElMessage({
+        //     type: "error",
+        //     showClose: true,
+        //     message: res.data.message,
+        //     center: true,
+        //   });
+        // }
+      } else {
+        ElMessage({
+          type: "error",
+          showClose: true,
+          message: "两次密码输入不正确",
+          center: true,
+        });
+      }
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+};
+// 重置表单
+const resetForm = (formEl) => {
+  // console.log("重置表单");
+  if (!formEl) return;
+  formEl.resetFields();
+};
+
+const loginOut = () => {
+  ElMessageBox.confirm("是否退出登录?", "提示", {
+    confirmButtonText: "确认",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(() => {
+      localStorage.removeItem("token");
+      localStorage.removeItem("userhead");
+      router.push({
+        path: `/login`,
+      });
+      ElMessage({
+        type: "success",
+        message: "退出成功",
+      });
+    })
+    .catch(() => {
+      ElMessage({
+        type: "info",
+        message: "已取消登录",
+      });
+    });
+};
+
+onMounted(() => {
+  userName.value = localStorage.getItem("userName");
+  setInterval(() => {
+    var week = ["日", "一", "二", "三", "四", "五", "六"];
+    var datas = dayjs().day();
+    dateTime.value =
+      dayjs().format("YYYY-MM-DD") +
+      ` 星期${week[datas]} ` +
+      dayjs().format("HH:mm:ss");
+  }, 1000);
+});
+</script>
+
+<style scoped lang="scss">
+.el-menu--horizontal {
+  border-bottom: none;
+  display: flex;
+  align-items: center;
+  height: 65px;
+  // width: 100%;
+  background-color: #fff;
+  box-shadow: 5px 5px 10px 0px rgba(213, 228, 252, 1);
+  .logo {
+    left: 90px;
+    display: flex;
+    align-items: center;
+    span {
+      margin-left: 10px;
+      font-size: 24px;
+      font-weight: 600;
+    }
+  }
+  .login {
+    color: #000;
+    display: flex;
+    align-items: center;
+    position: absolute;
+    right: 10px;
+    top: 17px;
+
+    .dateTime {
+      margin-right: 20px;
+    }
+    .el-badge {
+      display: flex;
+      align-items: center;
+      // :deep(.el-badge__content) {
+      //   width: 2px;
+      //   height: 14px;
+      // }
+    }
+    .items-center {
+      margin-left: 20px;
+    }
+    .name {
+      height: 20px;
+      line-height: 20px;
+      text-decoration: underline;
+      padding: 0 10px;
+      margin: 0 10px;
+      border-left: 2px solid #ccc;
+    }
+    .switchButton {
+      cursor: pointer;
+    }
+  }
+  .login:focus {
+    outline: none;
+  }
+  .fold {
+    width: 46px;
+    height: 65px;
+  }
+  .fold:hover {
+    cursor: pointer;
+    background-color: rgba(67, 146, 249, 0.1);
+  }
+}
+</style>

+ 265 - 0
src/layout/SidevarItem copy.vue

@@ -0,0 +1,265 @@
+<template>
+  <div class="box-item">
+    <el-menu
+      class="el-menu-vertical-demo"
+      background-color="#293749"
+      text-color="#fff"
+      active-text-color="#fff"
+      :default-active="sidevarItem"
+      :collapse="isCollapse"
+      @open="handleOpen"
+      @close="handleClose"
+      @select="handleSelect"
+    >
+      <div class="logo el-menu-item">
+        <el-icon :size="20"
+          ><img src="@/assets/img/logo.png" style="width: 40px; height: 40px"
+        /></el-icon>
+      </div>
+      <div>
+        <el-menu-item :index="i.path" v-for="i in roleList" :key="i.path">
+          <SvgIcon :name="i.icon" color="#fff" size="22"></SvgIcon>
+          <template #title>{{ i.name }}</template>
+        </el-menu-item>
+        <el-sub-menu index="1" popper-class="ceshi" :teleported="true">
+          <template #title>
+            <SvgIcon name="building" color="#fff" size="22"></SvgIcon>
+            <span>房源管理</span>
+          </template>
+          <el-menu-item index="/building">
+            <template #title>楼栋信息管理</template>
+          </el-menu-item>
+          <el-menu-item index="/dormitory">
+            <template #title>寝室信息管理</template>
+          </el-menu-item>
+          <el-menu-item index="/bed">
+            <template #title>床位信息管理</template>
+          </el-menu-item>
+        </el-sub-menu>
+      </div>
+
+      <!-- <el-menu-item index="/dataOverview">
+        <SvgIcon name="dataOverview" color="#fff" size="22"></SvgIcon>
+        <template #title>数据总览</template>
+      </el-menu-item>
+
+      <el-menu-item index="/studentInfo">
+        <SvgIcon name="studentInfo" color="#fff" size="22"></SvgIcon>
+        <template #title>学生信息管理</template>
+      </el-menu-item>
+
+      <el-sub-menu index="1" popper-class="ceshi" :teleported="true">
+        <template #title>
+          <SvgIcon name="building" color="#fff" size="22"></SvgIcon>
+          <span>房源管理</span>
+        </template>
+        <el-menu-item index="/building">
+          <template #title>楼栋信息管理</template>
+        </el-menu-item>
+        <el-menu-item index="/dormitory">
+          <template #title>寝室信息管理</template>
+        </el-menu-item>
+        <el-menu-item index="/bed">
+          <template #title>床位信息管理</template>
+        </el-menu-item>
+      </el-sub-menu>
+
+      <el-menu-item index="/student">
+        <SvgIcon name="student" color="#fff" size="22"></SvgIcon>
+        <template #title>学生住宿信息</template>
+      </el-menu-item>
+      <el-menu-item index="/quarterage">
+        <SvgIcon name="quarterage" color="#fff" size="22"></SvgIcon>
+        <template #title>住宿信息统计</template>
+      </el-menu-item>
+      <el-menu-item index="/caller">
+        <SvgIcon name="caller" color="#fff" size="22"></SvgIcon>
+        <template #title>访客信息管理</template>
+      </el-menu-item>
+      <el-sub-menu index="2" popper-class="ceshi" :teleported="true">
+        <template #title>
+          <SvgIcon name="role" color="#fff" size="22"></SvgIcon>
+          <span>权限管理</span>
+        </template>
+        <el-menu-item index="/user">
+          <template #title>用户管理</template>
+        </el-menu-item>
+        <el-menu-item index="/role">
+          <template #title>角色管理</template>
+        </el-menu-item>
+      </el-sub-menu>
+      <el-menu-item index="/system">
+        <SvgIcon name="system" color="#fff" size="22"></SvgIcon>
+        <template #title>系统设置</template>
+      </el-menu-item> -->
+    </el-menu>
+  </div>
+</template>
+
+<script setup>
+import { ref, onBeforeMount, onMounted, watch, reactive } from "vue";
+import {
+  Fold,
+  Expand,
+  Service,
+  Position,
+  View,
+  Open,
+} from "@element-plus/icons-vue";
+import { useRouter } from "vue-router";
+import { storeToRefs } from "pinia";
+import { useCounterStore } from "@/stores/index";
+
+const store = useCounterStore();
+const { isCollapse, sidevarItem, roleList } = storeToRefs(store);
+
+const router = useRouter();
+
+const acitveItems = reactive({ list: [] });
+
+const handleSelect = (key) => {
+  store.setItem(key);
+  router.push({
+    path: `${key}`,
+  });
+};
+const handleOpen = () => {};
+const handleClose = () => {};
+onBeforeMount(() => {
+  // console.log("SidevarItem/onBeforeMount");
+});
+</script>
+
+<style lang="scss" scoped>
+.el-menu-vertical-demo:not(.el-menu--collapse) {
+  width: 200px;
+  // min-height: 400px;
+  user-select: none;
+}
+.box-item {
+  height: 100vh;
+}
+:deep(.el-menu) {
+  height: 100%;
+  overflow: auto;
+  background-color: rgba(48, 65, 86, 1);
+  box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.16);
+
+  .logo {
+    height: 65px;
+    color: rgb(255, 255, 255);
+    cursor: default;
+    display: flex;
+    justify-content: space-around;
+    align-items: center;
+  }
+  .logo:hover {
+    background-color: transparent !important;
+  }
+
+  .el-sub-menu {
+    padding: 0 14px;
+    .el-sub-menu__title {
+      height: 40px;
+      margin: 0;
+      border-radius: 10px;
+    }
+    .el-menu-item {
+      height: 40px;
+      margin: 20px 0 !important;
+      border-radius: 10px;
+      .el-menu-tooltip__trigger {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+      }
+      &:last-child {
+        margin: 20px 0 0 0 !important;
+      }
+      &.is-active {
+        background: linear-gradient(
+          90deg,
+          rgba(38, 151, 255, 1) 0%,
+          rgba(102, 182, 255, 1) 100%
+        );
+      }
+    }
+    .el-menu.el-menu--inline {
+      box-shadow: none;
+    }
+    &.is-active.is-opened {
+      .el-sub-menu__title {
+        background: linear-gradient(
+          90deg,
+          rgba(38, 151, 255, 1) 0%,
+          rgba(102, 182, 255, 1) 100%
+        );
+      }
+    }
+  }
+
+  .el-menu-item:nth-child(n + 2) {
+    height: 40px;
+    margin: 20px 14px;
+    border-radius: 10px;
+    .el-menu-tooltip__trigger {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+    &.is-active {
+      background: linear-gradient(
+        90deg,
+        rgba(38, 151, 255, 1) 0%,
+        rgba(102, 182, 255, 1) 100%
+      );
+    }
+  }
+}
+:deep(.el-menu--collapse) {
+  .el-menu-item:nth-child(n + 2) {
+    padding: 0;
+    margin: 20px 10px;
+  }
+  .el-sub-menu.is-active {
+    .el-sub-menu__title {
+      background: linear-gradient(
+        90deg,
+        rgba(38, 151, 255, 1) 0%,
+        rgba(102, 182, 255, 1) 100%
+      );
+    }
+  }
+  .el-sub-menu {
+    padding: 0;
+    margin: 0 10px;
+    .el-sub-menu__title {
+      padding: 0 20px;
+      margin: 0;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+  }
+  .el-sub-menu {
+    .el-menu-item {
+      border: 1px solid red;
+    }
+  }
+}
+</style>
+
+<style lang="scss">
+.ceshi {
+  &.is-light {
+    border: none !important;
+  }
+  .el-menu-item.is-active {
+    background: linear-gradient(
+      90deg,
+      rgba(38, 151, 255, 1) 0%,
+      rgba(102, 182, 255, 1) 100%
+    );
+  }
+}
+</style>

+ 365 - 0
src/layout/SidevarItem.vue

@@ -0,0 +1,365 @@
+<template>
+  <div class="box-item">
+    <el-menu
+      class="el-menu-vertical-demo"
+      background-color="#293749"
+      text-color="#fff"
+      active-text-color="#fff"
+      :default-active="sidevarItem"
+      :collapse="isCollapse"
+      @open="handleOpen"
+      @close="handleClose"
+      @select="handleSelect"
+    >
+      <div class="logo el-menu-item">
+        <el-icon :size="20"
+          ><img src="@/assets/img/logo.png" style="width: 40px; height: 40px"
+        /></el-icon>
+      </div>
+      <div v-for="i in roles.list" :key="i.path">
+        <el-sub-menu
+          v-if="i.name == '房源管理' || i.name == '权限管理'"
+          :index="i.index"
+          popper-class="ceshi"
+          :teleported="true"
+        >
+          <template #title>
+            <SvgIcon :name="i.icon" color="#fff" size="22"></SvgIcon>
+            <span>{{ i.name }}</span>
+          </template>
+          <el-menu-item
+            v-for="item in i.children"
+            :index="item.path"
+            :key="item.paht"
+          >
+            <template #title>{{ item.name }}</template>
+          </el-menu-item>
+        </el-sub-menu>
+        <el-menu-item v-else :index="i.path">
+          <SvgIcon :name="i.icon" color="#fff" size="22"></SvgIcon>
+          <template #title>{{ i.name }}</template>
+        </el-menu-item>
+      </div>
+
+      <!-- <el-menu-item index="/dataOverview">
+        <SvgIcon name="dataOverview" color="#fff" size="22"></SvgIcon>
+        <template #title>数据总览</template>
+      </el-menu-item>
+
+      <el-menu-item index="/studentInfo">
+        <SvgIcon name="studentInfo" color="#fff" size="22"></SvgIcon>
+        <template #title>学生信息管理</template>
+      </el-menu-item>
+
+      <el-sub-menu index="1" popper-class="ceshi" :teleported="true">
+        <template #title>
+          <SvgIcon name="building" color="#fff" size="22"></SvgIcon>
+          <span>房源管理</span>
+        </template>
+        <el-menu-item index="/building">
+          <template #title>楼栋信息管理</template>
+        </el-menu-item>
+        <el-menu-item index="/dormitory">
+          <template #title>寝室信息管理</template>
+        </el-menu-item>
+        <el-menu-item index="/bed">
+          <template #title>床位信息管理</template>
+        </el-menu-item>
+      </el-sub-menu>
+
+      <el-menu-item index="/student">
+        <SvgIcon name="student" color="#fff" size="22"></SvgIcon>
+        <template #title>学生住宿信息</template>
+      </el-menu-item>
+      <el-menu-item index="/quarterage">
+        <SvgIcon name="quarterage" color="#fff" size="22"></SvgIcon>
+        <template #title>住宿信息统计</template>
+      </el-menu-item>
+      <el-menu-item index="/caller">
+        <SvgIcon name="caller" color="#fff" size="22"></SvgIcon>
+        <template #title>访客信息管理</template>
+      </el-menu-item>
+      <el-sub-menu index="2" popper-class="ceshi" :teleported="true">
+        <template #title>
+          <SvgIcon name="role" color="#fff" size="22"></SvgIcon>
+          <span>权限管理</span>
+        </template>
+        <el-menu-item index="/user">
+          <template #title>用户管理</template>
+        </el-menu-item>
+        <el-menu-item index="/role">
+          <template #title>角色管理</template>
+        </el-menu-item>
+      </el-sub-menu>
+      <el-menu-item index="/system">
+        <SvgIcon name="system" color="#fff" size="22"></SvgIcon>
+        <template #title>系统设置</template>
+      </el-menu-item> -->
+    </el-menu>
+  </div>
+</template>
+
+<script setup>
+import { ref, onBeforeMount, onMounted, watch, reactive } from "vue";
+import {
+  Fold,
+  Expand,
+  Service,
+  Position,
+  View,
+  Open,
+} from "@element-plus/icons-vue";
+import { useRouter } from "vue-router";
+import { storeToRefs } from "pinia";
+import { useCounterStore } from "@/stores/index";
+
+const store = useCounterStore();
+const { isCollapse, sidevarItem, roleList } = storeToRefs(store);
+
+const router = useRouter();
+const roles = reactive({
+  list: [],
+});
+
+const acitveItems = reactive({ list: [] });
+
+const handleSelect = (key) => {
+  console.log(key);
+
+  store.setItem(key);
+  router.push({
+    path: `${key}`,
+  });
+};
+const handleOpen = () => {};
+const handleClose = () => {};
+onMounted(() => {
+  console.log(roleList.value);
+  roleList.value.forEach((i) => {
+    if (i.path == "/building" || i.path == "/dormitory" || i.path == "/bed") {
+      const exists = roles.list.some((item) => item.name === "房源管理");
+      if (exists) {
+        roles.list.forEach((j) => {
+          if (j.name == "房源管理") {
+            j.children.push({
+              name: i.name,
+              path: i.path,
+              icon: i.icon,
+            });
+          }
+        });
+      } else {
+        roles.list.push({
+          name: "房源管理",
+          index: 1,
+          icon: "building",
+          children: [
+            {
+              name: i.name,
+              path: i.path,
+              icon: i.icon,
+            },
+          ],
+        });
+      }
+    } else if (i.path == "/user" || i.path == "/role") {
+      const exists = roles.list.some((item) => item.name === "权限管理");
+      if (exists) {
+        roles.list.forEach((j) => {
+          if (j.name == "权限管理") {
+            j.children.push({
+              name: i.name,
+              path: i.path,
+              icon: i.icon,
+            });
+          }
+        });
+      } else {
+        roles.list.push({
+          name: "权限管理",
+          index: 2,
+          icon: "role",
+          children: [
+            {
+              name: i.name,
+              path: i.path,
+              icon: i.icon,
+            },
+          ],
+        });
+      }
+    } else {
+      roles.list.push({
+        name: i.name,
+        path: i.path,
+        icon: i.icon,
+      });
+    }
+  });
+  console.log(roles.list);
+});
+onBeforeMount(() => {
+  // console.log("SidevarItem/onBeforeMount");
+});
+</script>
+
+<style lang="scss" scoped>
+.el-menu-vertical-demo:not(.el-menu--collapse) {
+  width: 200px;
+  // min-height: 400px;
+  user-select: none;
+}
+.box-item {
+  height: 100vh;
+}
+:deep(.el-menu) {
+  height: 100%;
+  overflow: auto;
+  background-color: rgba(48, 65, 86, 1);
+  box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.16);
+
+  .logo {
+    height: 65px;
+    color: rgb(255, 255, 255);
+    cursor: default;
+    display: flex;
+    justify-content: space-around;
+    align-items: center;
+    margin: 20px 0 !important;
+  }
+  .logo:hover {
+    background-color: transparent !important;
+  }
+
+  .el-sub-menu {
+    padding: 0 14px;
+    margin-bottom:20px ;
+    .el-sub-menu__title {
+      height: 40px;
+      // margin: 0 0 20px 0;
+      margin: 0;
+      border-radius: 10px;
+    }
+    .el-menu-item {
+      height: 40px;
+      margin: 20px 0 !important;
+      border-radius: 10px;
+      .el-menu-tooltip__trigger {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+      }
+      &:last-child {
+        margin: 20px 0 0 0 !important;
+      }
+      &.is-active {
+        background: linear-gradient(
+          90deg,
+          rgba(38, 151, 255, 1) 0%,
+          rgba(102, 182, 255, 1) 100%
+        );
+      }
+    }
+    .el-menu.el-menu--inline {
+      box-shadow: none;
+    }
+    &.is-active.is-opened {
+      .el-sub-menu__title {
+        background: linear-gradient(
+          90deg,
+          rgba(38, 151, 255, 1) 0%,
+          rgba(102, 182, 255, 1) 100%
+        );
+      }
+    }
+  }
+
+  .el-menu-item {
+    height: 40px;
+    margin: 20px 14px;
+    border-radius: 10px;
+    .el-menu-tooltip__trigger {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+    &.is-active {
+      background: linear-gradient(
+        90deg,
+        rgba(38, 151, 255, 1) 0%,
+        rgba(102, 182, 255, 1) 100%
+      );
+    }
+  }
+}
+:deep(.el-menu--collapse) {
+  .el-sub-menu {
+    padding: 0;
+    margin: 20px 10px;
+    .el-sub-menu__title {
+      span {
+        display: none;
+      }
+      .el-icon {
+        margin-right: 0;
+      }
+      .el-sub-menu__icon-arrow {
+        display: none;
+      }
+    }
+  }
+  .el-sub-menu.is-active {
+    .el-sub-menu__title {
+      background: linear-gradient(
+        90deg,
+        rgba(38, 151, 255, 1) 0%,
+        rgba(102, 182, 255, 1) 100%
+      );
+      span {
+        display: none;
+      }
+    }
+  }
+  .el-sub-menu {
+    padding: 0;
+    margin: 0 10px 20px !important;
+    .el-sub-menu__title {
+      width: 40px;
+      padding: 0 20px;
+      // margin: 0 0 20px 0;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+  }
+  .el-sub-menu {
+    .el-menu-item {
+      border: 1px solid red;
+    }
+  }
+  .el-menu-item {
+    width: 40px;
+    margin: 20px 10px;
+    .el-menu-tooltip__trigger {
+      .el-icon {
+        margin-right: 0;
+      }
+    }
+  }
+}
+</style>
+
+<style lang="scss">
+.ceshi {
+  &.is-light {
+    border: none !important;
+  }
+  .el-menu-item.is-active {
+    background: linear-gradient(
+      90deg,
+      rgba(38, 151, 255, 1) 0%,
+      rgba(102, 182, 255, 1) 100%
+    );
+  }
+}
+</style>

+ 129 - 0
src/layout/SidevarItem报修.vue

@@ -0,0 +1,129 @@
+<template>
+  <div class="box-item">
+    <el-menu
+      class="el-menu-vertical-demo"
+      background-color="#222d32"
+      text-color="#fff"
+      active-text-color="#fff"
+      :default-active="sidevarItem"
+      :collapse="isCollapse"
+      @open="handleOpen"
+      @close="handleClose"
+      @select="handleSelect"
+    >
+      <div class="logo el-menu-item">
+        <el-icon :size="20"
+          ><img src="@/assets/img/logo.png" style="width: 30px; height: 24px"
+        /></el-icon>
+      </div>
+      <el-sub-menu index="1">
+        <template #title>
+          <img
+            class="el-icon"
+            src="@/assets/svgs/apple.svg"
+            style="width: 22px; height: 22px"
+          />
+          <span>项目</span>
+        </template>
+        <el-menu-item index="/a">
+          <SvgIcon name="apple" color="#000" size="22"></SvgIcon>
+          <template #title>项目一</template>
+        </el-menu-item>
+        <el-menu-item index="/b">项目二</el-menu-item>
+        <el-menu-item index="/c">项目三</el-menu-item>
+      </el-sub-menu>
+      <el-menu-item index="/home">
+        <SvgIcon name="home" color="#000" size="22"></SvgIcon>
+        <template #title>首页</template>
+      </el-menu-item>
+    </el-menu>
+  </div>
+</template>
+
+<script setup>
+import { ref, onBeforeMount, onMounted, watch, reactive } from "vue";
+import {
+  Fold,
+  Expand,
+  Service,
+  Position,
+  View,
+  Open,
+} from "@element-plus/icons-vue";
+import { useRouter } from "vue-router";
+import { storeToRefs } from "pinia";
+import { useCounterStore } from "@/stores/index";
+
+const store = useCounterStore();
+const { isCollapse, sidevarItem, roleList } = storeToRefs(store);
+
+const router = useRouter();
+
+const acitveItems = reactive({ list: [] });
+
+const handleSelect = (key) => {
+  store.setItem(key);
+  router.push({
+    path: `${key}`,
+  });
+};
+const handleOpen = () => {};
+const handleClose = () => {};
+onBeforeMount(() => {
+  // console.log("SidevarItem/onBeforeMount");
+});
+</script>
+
+<style lang="scss" scoped>
+.el-menu-vertical-demo:not(.el-menu--collapse) {
+  width: 200px;
+  // min-height: 400px;
+}
+.box-item {
+  height: 100vh;
+}
+:deep(.el-menu) {
+  // width: 100%;
+  height: 100%;
+  overflow: auto;
+  // background: linear-gradient(
+  //   0deg,
+  //   rgba(134, 132, 255, 1) 0%,
+  //   rgba(60, 80, 232, 1) 100%
+  // );
+  background-color: rgba(34, 45, 50, 1);
+  box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.16);
+
+  .logo {
+    height: 65px;
+    color: rgb(255, 255, 255);
+    cursor: default;
+    display: flex;
+    justify-content: space-around;
+    align-items: center;
+  }
+  .logo:hover {
+    background-color: transparent !important;
+  }
+
+  .el-sub-menu.is-active {
+    .el-sub-menu__title {
+      background-color: rgba(111, 182, 184, 1);
+    }
+  }
+  // .el-sub-menu {
+  //   .el-menu-item {
+  //     // padding:0 0 0 65px !important;
+  //   }
+  // }
+  .el-icon {
+    margin-right: 10px;
+  }
+}
+
+.el-menu-item.is-active {
+  box-sizing: border-box;
+  background-color: rgba(111, 182, 184, 1);
+  color: #fff;
+}
+</style>

+ 36 - 0
src/main.js

@@ -0,0 +1,36 @@
+import { createApp } from "vue";
+import "./style.css";
+import App from "./App.vue";
+
+import ElementPlus from "element-plus";
+import "element-plus/dist/index.css";
+import zhCn from "element-plus/es/locale/lang/zh-cn";
+
+import router from "./router";
+
+import { createPinia } from "pinia";
+import { useCounterStore } from "@/stores/index";
+
+import "virtual:svg-icons-register";
+import SvgIcon from "@/components/SvgIcon.vue"; // 导入SvgIcon组件
+
+const addRouter = async () => {
+  const store = useCounterStore();
+  await store.ROLEAdd();
+};
+async function call() {
+  const app = createApp(App);
+
+  app.use(ElementPlus, {
+    locale: zhCn,
+  });
+
+  app.component("SvgIcon", SvgIcon); // 全局注册SvgIcon组件
+
+  app.use(createPinia());
+  await addRouter();// 动态添加路由的时候刷新页面会重新调用,防止刷新页面空白
+  app.use(router);
+
+  app.mount("#app");
+}
+call();

+ 116 - 0
src/router/index.js

@@ -0,0 +1,116 @@
+// src/router/index.ts
+import {
+  createRouter,
+  createWebHistory,
+  createWebHashHistory,
+} from "vue-router";
+import NProgress from "nprogress";
+import "nprogress/nprogress.css"; // 引入 NProgress 的 CSS 样式文件
+import { ElMessage } from "element-plus";
+const routes = [
+  {
+    path: "/login",
+    name: "login",
+    meta: { title: "", icon: "", showHeader: false },
+    component: () => import("@/views/login/login.vue"),
+  },
+  {
+    path: "/",
+    redirect: "/login",
+  },
+  ,
+  {
+    path: "/:pathMatch(.*)*",
+    redirect: "/404",
+  },
+  // {
+  //   path: "/dataOverview",
+  //   component: () => import("@/views/dataOverview/dataOverview.vue"),
+  //   meta: { title: "", icon: "", showHeader: true },
+  // },
+  // {
+  //   path: "/studentInfo",
+  //   component: () => import("@/views/studentInfo/studentInfo.vue"),
+  //   meta: { title: "", icon: "", showHeader: true },
+  // },
+  // {
+  //   path: "/building",
+  //   component: () => import("@/views/building/building.vue"),
+  //   meta: { title: "", icon: "", showHeader: true },
+  // },
+  // {
+  //   path: "/dormitory",
+  //   component: () => import("@/views/dormitory/dormitory.vue"),
+  //   meta: { title: "", icon: "", showHeader: true },
+  // },
+  // {
+  //   path: "/bed",
+  //   component: () => import("@/views/bed/bed.vue"),
+  //   meta: { title: "", icon: "", showHeader: true },
+  // },
+  // {
+  //   path: "/student",
+  //   component: () => import("@/views/student/student.vue"),
+  //   meta: { title: "", icon: "", showHeader: true },
+  // },
+  // {
+  //   path: "/quarterage",
+  //   component: () => import("@/views/quarterage/quarterage.vue"),
+  //   meta: { title: "", icon: "", showHeader: true },
+  // },
+  // {
+  //   path: "/caller",
+  //   component: () => import("@/views/caller/caller.vue"),
+  //   meta: { title: "", icon: "", showHeader: true },
+  // },
+  // {
+  //   path: "/user",
+  //   component: () => import("@/views/user/user.vue"),
+  //   meta: { title: "", icon: "", showHeader: true },
+  // },
+  // {
+  //   path: "/role",
+  //   component: () => import("@/views/role/role.vue"),
+  //   meta: { title: "", icon: "", showHeader: true },
+  // },
+  // {
+  //   path: "/system",
+  //   component: () => import("@/views/system/system.vue"),
+  //   meta: { title: "", icon: "", showHeader: true },
+  // },
+];
+const router = createRouter({
+  history: createWebHashHistory("/alumnus/welcomeWeb"),
+  routes,
+});
+
+// 配置 NProgress
+NProgress.configure({ showSpinner: false }); // 根据需要配置选项
+
+// 在路由守卫中启动和停止 NProgress
+router.beforeEach(async (to, from, next) => {
+  NProgress.start(); // 开始加载进度条
+
+  let token = localStorage.getItem("token");
+  if (to.path == "/login") {
+    next();
+  } else {
+    if (token) {
+      next();
+    } else {
+      ElMessage({
+        type: "error",
+        showClose: true,
+        message: "无权限进入此页面",
+        center: true,
+      });
+      localStorage.removeItem("token");
+      localStorage.removeItem("userhead");
+      next("/login");
+    }
+  }
+});
+router.afterEach(() => {
+  NProgress.done(); // 结束加载进度条
+});
+export default router;

+ 281 - 0
src/stores/index.js

@@ -0,0 +1,281 @@
+// src/stores/counter.ts
+import { defineStore } from "pinia";
+import { https } from "@/utils/request"; // 绝对路径
+import router from "@/router";
+import { ElMessage } from "element-plus";
+export const useCounterStore = defineStore("user", {
+  state: () => {
+    return {
+      name: "兰陵王",
+      age: 24,
+      isCollapse: false, // 定义初始状态
+      sidevarItem: "/home",
+      roleList: [], // 权限列表
+      btnRole: [], // 功能按钮权限
+      collegeRole: [], // 院系权限
+    };
+  },
+  getters: {
+    realAge(state) {
+      return state.age * 10;
+    },
+  },
+  actions: {
+    setName(name) {
+      this.name = name;
+    },
+    setAge({ name, age }) {
+      // 直接通过this修改state
+      // this.name = name;
+      // this.age = age;
+      // or 批量更新state
+      this.$patch({
+        name,
+        age,
+      });
+    },
+    // 控制侧边栏的伸缩
+    setInfo(isCollapse) {
+      // 直接通过this修改state
+      this.isCollapse = isCollapse;
+      // or 批量更新state
+      // this.$patch({
+      //     isCollapse
+      // });
+    },
+    // 保存菜单项
+    setItem(sidevarItem) {
+      this.sidevarItem = sidevarItem;
+      sessionStorage.setItem("sidevarItem", sidevarItem);
+    },
+    // 动态添加路由
+    async ROLEAdd() {
+      // 获取菜单权限列表
+      this.roleList = [];
+      let datas = {
+        accountId: sessionStorage.getItem("accountId"),
+      };
+      const res = await https.get(
+        "/welcome/api/welcomeAccount/getAccountAuthority",
+        "params",
+        datas
+      ); // 使用封装的 get 方法
+      if (res.code == 200) {
+        console.log(res, "获取菜单权限列表");
+        this.collegeRole = res.data.welcomeOrgList;
+        let roles = res.data.welcomeRole;
+        let btn = [];
+        // 数据总览
+        if (roles.dataManagementSetting) {
+          let arr = roles.dataManagementSetting.split(",");
+          arr.forEach((i) => {
+            btn.push(`dataOverview${i}`);
+            if (i == 1) {
+              this.roleList.push({
+                name: "数据总览",
+                path: "/dataOverview",
+                icon: "dataOverview",
+              });
+            }
+          });
+        }
+        // 学生信息设置
+        if (roles.studentManagementSetting) {
+          let arr = roles.studentManagementSetting.split(",");
+          arr.forEach((i) => {
+            btn.push(`studentManagementSetting${i}`);
+            if (i == 7) {
+              this.roleList.push({
+                name: "学生信息管理",
+                path: "/studentInfo",
+                icon: "studentInfo",
+              });
+            }
+          });
+        }
+        // 楼栋管理设置
+        if (roles.buildingManagementSetting) {
+          let arr = roles.buildingManagementSetting.split(",");
+          arr.forEach((i) => {
+            btn.push(`buildingManagementSetting${i}`);
+            if (i == 6) {
+              this.roleList.push({
+                name: "楼栋信息管理",
+                path: "/building",
+                icon: "building",
+              });
+            }
+          });
+        }
+        // 宿舍管理设置
+        if (roles.dormitoryManagementSetting) {
+          let arr = roles.dormitoryManagementSetting.split(",");
+          arr.forEach((i) => {
+            btn.push(`dormitoryManagementSetting${i}`);
+            if (i == 10) {
+              this.roleList.push({
+                name: "寝室信息管理",
+                path: "/dormitory",
+                icon: "dormitory",
+              });
+            }
+          });
+        }
+        // 床位管理设置
+        if (roles.bedManagementSetting) {
+          let arr = roles.bedManagementSetting.split(",");
+          arr.forEach((i) => {
+            btn.push(`bedManagementSetting${i}`);
+            if (i == 13) {
+              this.roleList.push({
+                name: "床位信息管理",
+                path: "/bed",
+                icon: "bed",
+              });
+            }
+          });
+        }
+        // 学生住宿设置
+        if (roles.studentAccommodationSetting) {
+          let arr = roles.studentAccommodationSetting.split(",");
+          arr.forEach((i) => {
+            btn.push(`studentAccommodationSetting${i}`);
+            if (i == 2) {
+              this.roleList.push({
+                name: "学生住宿信息",
+                path: "/student",
+                icon: "student",
+              });
+            }
+          });
+        }
+        // 住宿信息统计
+        if (roles.accommodationStatisticsSetting) {
+          let arr = roles.accommodationStatisticsSetting.split(",");
+          arr.forEach((i) => {
+            btn.push(`accommodationStatisticsSetting${i}`);
+            if (i == 2) {
+              this.roleList.push({
+                name: "住宿信息统计",
+                path: "/quarterage",
+                icon: "quarterage",
+              });
+            }
+          });
+        }
+        // 访客信息管理
+        if (roles.visitorManagementSetting) {
+          let arr = roles.visitorManagementSetting.split(",");
+          arr.forEach((i) => {
+            btn.push(`visitorManagementSetting${i}`);
+            if (i == 5) {
+              this.roleList.push({
+                name: "访客信息管理 ",
+                path: "/caller",
+                icon: "caller",
+              });
+            }
+          });
+        }
+        // 用户管理
+        if (roles.accountManagementSetting) {
+          let arr = roles.accountManagementSetting.split(",");
+          arr.forEach((i) => {
+            btn.push(`accountManagementSetting${i}`);
+            if (i == 4) {
+              this.roleList.push({
+                name: "用户管理 ",
+                path: "/user",
+                icon: "user",
+              });
+            }
+          });
+        }
+        // 角色管理
+        if (roles.roleManagementSetting) {
+          let arr = roles.roleManagementSetting.split(",");
+          arr.forEach((i) => {
+            btn.push(`roleManagementSetting${i}`);
+            if (i == 4) {
+              this.roleList.push({
+                name: "角色管理 ",
+                path: "/role",
+                icon: "role",
+              });
+            }
+          });
+        }
+        // 系统设置
+        if (roles.settingManagementSetting) {
+          let arr = roles.settingManagementSetting.split(",");
+          arr.forEach((i) => {
+            btn.push(`settingManagementSetting${i}`);
+            if (i == 2) {
+              this.roleList.push({
+                name: "系统设置 ",
+                path: "/system",
+                icon: "system",
+              });
+            }
+          });
+        }
+        // sessionStorage.setItem("btnRole", btn.toString());
+        this.btnRole = btn;
+        const modules = import.meta.glob("../views/**/*.vue");
+        let roleList = this.roleList;
+        if (roleList) {
+          roleList.forEach((item) => {
+            let arr = {
+              path: item.path,
+              name: item.path,
+              meta: { isAuth: true, title: item.name, showHeader: true },
+              component: modules[`../views${item.path}${item.path}.vue`],
+            };
+            router.addRoute(arr);
+          });
+          router.addRoute({
+            path: "/404",
+            name: "NotFound",
+            component: () => import("@/views/404/404.vue"),
+          });
+
+          console.log("动态添加路由");
+        }
+        router.push({
+          path: roleList[0].path,
+        });
+      } else {
+        console.log("获取角色权限失败,请重新登录");
+        // router.push({
+        //   path: '/login',
+        // });
+        // ElMessage({
+        //   type: "error",
+        //   showClose: true,
+        //   message: "获取角色权限失败,请重新登录",
+        //   center: true,
+        // });
+      }
+      // const modules = import.meta.glob("../views/**/*.vue");
+      // let roleList = this.roleList;
+      // if (roleList) {
+      //   roleList.forEach((item) => {
+      //     let arr = {
+      //       path: item.path,
+      //       name: item.path,
+      //       meta: { isAuth: true, title: item.name, showHeader: true },
+      //       component: modules[`../views${item.path}${item.path}.vue`],
+      //     };
+      //     router.addRoute(arr);
+      //   });
+      //   console.log("动态添加路由");
+      // }
+    },
+    BtnRole(flag) {
+      let show = this.btnRole.some((i) => {
+        return i == flag;
+      });
+      return show;
+    },
+  },
+});

+ 79 - 0
src/style.css

@@ -0,0 +1,79 @@
+:root {
+  font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
+  line-height: 1.5;
+  font-weight: 400;
+
+  color-scheme: light dark;
+  color: rgba(255, 255, 255, 0.87);
+  background-color: #242424;
+
+  font-synthesis: none;
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+a {
+  font-weight: 500;
+  color: #646cff;
+  text-decoration: inherit;
+}
+a:hover {
+  color: #535bf2;
+}
+
+body {
+  margin: 0;
+  display: flex;
+  place-items: center;
+  min-width: 320px;
+  min-height: 100vh;
+}
+
+h1 {
+  font-size: 3.2em;
+  line-height: 1.1;
+}
+
+button {
+  border-radius: 8px;
+  border: 1px solid transparent;
+  padding: 0.6em 1.2em;
+  font-size: 1em;
+  font-weight: 500;
+  font-family: inherit;
+  background-color: #1a1a1a;
+  cursor: pointer;
+  transition: border-color 0.25s;
+}
+button:hover {
+  border-color: #646cff;
+}
+button:focus,
+button:focus-visible {
+  outline: 4px auto -webkit-focus-ring-color;
+  outline: none;
+}
+
+.card {
+  padding: 2em;
+}
+
+#app {
+  margin: 0 auto;
+  padding: 2rem;
+  text-align: center;
+}
+
+@media (prefers-color-scheme: light) {
+  :root {
+    color: #213547;
+    background-color: #ffffff;
+  }
+  a:hover {
+    color: #747bff;
+  }
+  button {
+    background-color: #f9f9f9;
+  }
+}

+ 1 - 0
src/styles/mixin.scss

@@ -0,0 +1 @@
+$default: red

+ 107 - 0
src/utils/request.js

@@ -0,0 +1,107 @@
+// utils/request.ts
+import axios from "axios";
+import { ElMessage, ElMessageBox } from "element-plus";
+import router from "@/router"; // 确保路径正确
+
+const instance = axios.create({
+  baseURL: import.meta.env.VITE_API_BASE_URL,
+  timeout: 5000,
+});
+// 请求拦截器
+instance.interceptors.request.use(
+  (config) => {
+    // 添加 token 等通用处理
+    // config.headers["Content-Type"] = "application/json;charset=utf-8";
+    const token = localStorage.getItem("token");
+    const userhead = localStorage.getItem("userhead");
+    if (token) {
+      config.headers["token"] = `${token}`;
+      config.headers["user_head"] = `${userhead}`;
+    } else {
+      console.log("无权限进入");
+      router.push({
+        path: `/login`,
+      });
+    }
+    return config;
+  },
+  (error) => {
+    return Promise.reject(error);
+  }
+);
+// 响应拦截器
+instance.interceptors.response.use(
+  (response) => {
+    // console.log(response);
+    if (
+      response.data.message == "登录凭证已过期,请重新登录!" ||
+      response.data.message == "无效token"
+    ) {
+      router.push({
+        path: `/login`,
+      });
+    }
+    return response.data;
+  },
+  (error) => {
+    // 统一错误处理
+    if (error.response?.status === 401) {
+      // 处理未授权情况
+    }
+    return Promise.reject(error);
+  }
+);
+
+// 封装常用的请求方法
+export const request = (
+  url,
+  flag,
+  data = {},
+  method = "GET",
+  responseType = null
+) => {
+  return instance({
+    url,
+    params: flag == "params" ? data : null,
+    data: flag == "data" ? data : null,
+    method,
+    responseType,
+  });
+};
+
+const api = "/welcome/welcome_api"; // 代理地址
+// const api = "/welcome/api"; // 代理地址
+// 请求封装
+export const https = {
+  get(url, flag, params = {}) {
+    return request(api + url, flag, params, "GET");
+  },
+  getBlob(url, flag, params = {}) {
+    return request(api + url, flag, params, "GET", "blob");
+  },
+  post(url, flag, data = {}) {
+    return request(api + url, flag, data, "POST");
+  },
+  put(url, flag, data = {}) {
+    return request(api + url, flag, data, "PUT");
+  },
+  delete(url, flag, params = {}) {
+    return request(api + url, flag, params, "DELETE");
+  },
+};
+// // 封装 POST 请求
+// export const post = (url: string, data: any = {}) => {
+//   return request(api+url, data, 'POST');
+// };
+// // 封装 GET 请求
+// export const get = (url: string, params: any = {}) => {
+//   return request(api+url, params, 'GET');
+// };
+// // 封装 POST 请求
+// export const put = (url: string, data: any = {}) => {
+//   return request(api+url, data, 'PUT');
+// };
+// // 封装 POST 请求
+// export const delete = (url: string, params: any = {}) => {
+//   return request(api+url, params, 'DELETE');
+// };

+ 20 - 0
src/utils/rsa.js

@@ -0,0 +1,20 @@
+import { JSEncrypt } from "jsencrypt";
+let publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMOcPB06u5yKyQsPjfVWiWgbEIrd14kiXNNihciaVKb6HnkQvq7zpQuZ80WEX94spnUMI3iOAl/GmIvHrpGwcbB4hJbznm+PajiwnUSPuCCXA68YJF640cJKb/8KeM7WVz69OFkIEPHhVxOy4FFF5QWe/kt6zOZ19HmE+ak+5x/QIDAQAB";
+let privateKey = "这里是封装的私钥";
+//加密方法
+export function RSAencrypt(pas) {
+    //实例化jsEncrypt对象
+    let jse = new JSEncrypt();
+    //设置公钥
+    jse.setPublicKey(publicKey);
+    // console.log("加密:" + jse.encrypt(pas));
+    return jse.encrypt(pas);
+}
+//解密方法
+export function RSAdecrypt(pas) {
+    let jse = new JSEncrypt();
+    // 私钥
+    jse.setPrivateKey(privateKey);
+    console.log("解密:" + jse.decrypt(pas));
+    return jse.decrypt(pas);
+}

+ 196 - 0
src/views/404/404.vue

@@ -0,0 +1,196 @@
+<template>
+  <div class="not-found-container">
+    <div class="not-found-content">
+      <!-- 404 图标或动画 -->
+      <!-- <div class="not-found-icon">
+        <svg
+          xmlns="http://www.w3.org/2000/svg"
+          width="120"
+          height="120"
+          viewBox="0 0 24 24"
+          fill="none"
+          stroke="currentColor"
+          stroke-width="2"
+          stroke-linecap="round"
+          stroke-linejoin="round"
+        >
+          <circle cx="12" cy="12" r="10"></circle>
+          <line x1="12" y1="8" x2="12" y2="12"></line>
+          <line x1="12" y1="16" x2="12.01" y2="16"></line>
+        </svg>
+      </div> -->
+
+      <!-- 标题和描述 -->
+      <h1 class="not-found-title">404</h1>
+      <h2 class="not-found-subtitle">页面未找到</h2>
+      <p class="not-found-description">
+        抱歉,您访问的页面不存在或已被移除。
+        <br />
+        请检查URL是否正确,或返回首页继续浏览。
+      </p>
+
+      <!-- 操作按钮 -->
+      <div class="not-found-actions">
+        <!-- <router-link to="/dataOverview" class="home-button">
+          <span>返回首页</span>
+        </router-link> -->
+        <button @click="goHome" class="home-button">
+          <span>返回首页</span>
+        </button>
+        <button @click="goBack" class="back-button">
+          <span>返回上一页</span>
+        </button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { useRouter } from "vue-router";
+
+import { storeToRefs } from "pinia";
+import { useCounterStore } from "@/stores/index";
+
+const router = useRouter();
+const store = useCounterStore();
+const { isCollapse, sidevarItem, roleList } = storeToRefs(store);
+const goHome = () => {
+  router.push({
+    path: roleList.value[0].path,
+  });
+};
+
+const goBack = () => {
+  if (window.history.length > 1) {
+    router.go(-1);
+  } else {
+    router.push("/");
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.not-found-container {
+  width: 100%;
+  height: 100vh;
+  margin-right: 25%;
+  display: flex;
+  flex-direction: row-reverse;
+  align-items: center;
+  // justify-content: space-around;
+  
+  background-image: url("../../assets/img/404.png");
+  /* 不重复平铺 */
+  background-repeat: no-repeat;
+  /* 背景图像充满整个页面 */
+  // background-size: cover; /* 可选:使图像覆盖整个背景 */
+  background-size: contain;
+  /* 可选:设置背景图像的位置 */
+  background-position: 30%; /* 居中 */
+}
+
+.not-found-content {
+  text-align: center;
+  max-width: 600px;
+  padding: 40px;
+  background-color: transparent;
+
+
+  // border-radius: 12px;
+  // box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
+}
+
+.not-found-icon {
+  margin-bottom: 24px;
+}
+
+.not-found-icon svg {
+  color: #6c757d;
+}
+
+.not-found-title {
+  font-size: 4rem;
+  font-weight: 700;
+  color: #343a40;
+  margin: 0 0 10px;
+}
+
+.not-found-subtitle {
+  font-size: 1.5rem;
+  font-weight: 500;
+  color: #495057;
+  margin: 0 0 20px;
+}
+
+.not-found-description {
+  font-size: 1rem;
+  color: #6c757d;
+  line-height: 1.6;
+  margin-bottom: 30px;
+}
+
+.not-found-actions {
+  display: flex;
+  justify-content: center;
+  gap: 16px;
+}
+
+.home-button,
+.back-button {
+  padding: 12px 24px;
+  border-radius: 6px;
+  font-weight: 500;
+  font-size: 1rem;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  text-decoration: none;
+  display: inline-flex;
+  align-items: center;
+}
+
+.home-button {
+  background-color: #3b82f6;
+  color: white;
+  border: none;
+}
+
+.home-button:hover {
+  background-color: #2563eb;
+}
+
+.back-button {
+  background-color: white;
+  color: #495057;
+  border: 1px solid #dee2e6;
+}
+
+.back-button:hover {
+  background-color: #f8f9fa;
+  border-color: #ced4da;
+}
+
+@media (max-width: 600px) {
+  .not-found-content {
+    padding: 30px 20px;
+  }
+
+  .not-found-title {
+    font-size: 3rem;
+  }
+
+  .not-found-subtitle {
+    font-size: 1.25rem;
+  }
+
+  .not-found-actions {
+    flex-direction: column;
+    gap: 12px;
+  }
+
+  .home-button,
+  .back-button {
+    width: 100%;
+    justify-content: center;
+  }
+}
+</style>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 2237 - 0
src/views/bed/bed.vue


+ 973 - 0
src/views/building/building.vue

@@ -0,0 +1,973 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <el-icon :size="23" class="camera"><VideoCameraFilled /></el-icon> -->
+      <span class="cameratxt">楼栋信息管理</span>
+    </div>
+    <div class="scroll">
+      <div class="middle">
+        <div class="filter">
+          <div class="condition">
+            <span>校区名称 :</span>
+            <el-select
+              clearable
+              @change="schoolChange"
+              v-model="searchInput.schoolId"
+              placeholder="请选择校区名称"
+            >
+              <el-option
+                v-for="i in schoolData"
+                :key="i.id"
+                :label="i.school"
+                :value="i.id"
+              />
+            </el-select>
+          </div>
+          <div class="condition">
+            <span>楼栋名称 :</span>
+            <el-select
+              clearable
+              v-model="searchInput.buildId"
+              placeholder="请选择楼栋名称"
+            >
+              <el-option
+                v-for="i in buildData"
+                :key="i.id"
+                :label="i.build"
+                :value="i.id"
+              />
+            </el-select>
+          </div>
+          <div class="condition">
+            <span>楼栋性别 :</span>
+            <el-select
+              clearable
+              v-model="searchInput.sex"
+              placeholder="请选择楼栋性别"
+            >
+              <el-option label="男" value="男" />
+              <el-option label="女" value="女" />
+            </el-select>
+          </div>
+          <el-button
+            style="margin-left: 20px"
+            color="rgba(38, 151, 255, 1)"
+            type="primary"
+            class="search"
+            @click="searchBtn"
+            ><span>查询</span></el-button
+          >
+          <el-button @click="resetBtn" plain color="rgba(43, 153, 255, 1)"
+            >重置</el-button
+          >
+        </div>
+        <!-- 按钮列表 -->
+        <div class="gongneng">
+          <el-button
+          v-if="store.BtnRole('buildingManagementSetting1')"
+            type="primary"
+            color="rgba(38, 151, 255, 1)"
+            @click="buildImportBtn"
+            >导入</el-button
+          >
+          <el-button
+          v-if="store.BtnRole('buildingManagementSetting2')"
+            type="primary"
+            style="margin-left: 0"
+            color="rgba(48, 201, 191, 1)"
+            @click="buildExportbtn"
+            >导出</el-button
+          >
+          <el-button
+          v-if="store.BtnRole('buildingManagementSetting3')"
+            type="primary"
+            style="margin-left: 0"
+            color="rgba(38, 151, 255, 1)"
+            @click="addlist"
+            >新增</el-button
+          >
+        </div>
+      </div>
+      <div class="footer" v-loading="loading">
+        <el-table
+          :row-class-name="tableRowClassName"
+          :data="tableData.list"
+          @selection-change="handleSelectionChange"
+          style="width: 100%"
+          :header-cell-style="{
+            background: 'rgba(240, 243, 247, 1)',
+            height: '50px',
+            border: 0,
+          }"
+        >
+          <el-table-column
+            width="120"
+            align="center"
+            label="序号"
+            type="index"
+            index="1"
+          />
+          <el-table-column align="center" prop="schoolName" label="校区名称" />
+          <el-table-column align="center" prop="build" label="楼栋名称" />
+          <el-table-column align="center" prop="sex" label="楼栋性别" />
+          <el-table-column align="center" prop="floors" label="楼栋层数">
+            <template #default="{ row }">
+              <span>{{ row.floors }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column align="center" prop="startFloor" label="起始层数" />
+          <el-table-column align="center" prop="remark" show-overflow-tooltip label="备注" />
+          <el-table-column align="center" label="操作" fixed="right">
+            <template #default="{ row }">
+              <div class="options">
+                <span v-if="store.BtnRole('buildingManagementSetting4')" class="edit" @click="updateS(row)">编辑</span>
+                <span v-if="store.BtnRole('buildingManagementSetting5')" class="delete" @click="deleteS(row)">删除</span>
+              </div>
+            </template>
+          </el-table-column>
+        </el-table>
+        <!-- 分页组件 -->
+        <div class="pageSize">
+          <span></span>
+          <el-pagination
+            background
+            :current-page="currentPage"
+            :page-size="pageSize"
+            :page-sizes="[10, 20, 30, 40]"
+            layout="total,sizes, prev, pager, next, jumper, slot"
+            :total="total"
+            @size-change="handleSizeChange"
+            @update:current-page="handleCurrentChange"
+          />
+        </div>
+      </div>
+    </div>
+    <!-- 添加账号弹窗 -->
+    <el-dialog
+      class="addStaff"
+      v-model="addDialogVisible"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      :title="dialongTitle"
+      align-center
+      width="560"
+      :before-close="cancelAdd"
+      destroy-on-close
+      draggable
+    >
+      <el-form
+        ref="ruleFormRef"
+        :model="ruleForm"
+        :rules="rules"
+        label-width="100px"
+        class="demo-ruleForm"
+        :size="formSize"
+        label-position="right"
+        status-icon
+      >
+        <el-form-item label="校区名称 :" prop="schoolId">
+          <el-select
+            clearable
+            v-model="ruleForm.schoolId"
+            placeholder="请选择校区名称"
+          >
+            <el-option
+              v-for="i in schoolData"
+              :key="i.id"
+              :label="i.school"
+              :value="`${i.school},${i.id}`"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="楼栋名称 :" prop="build">
+          <el-input
+            clearable
+            v-model.trim="ruleForm.build"
+            class="w-50 m-2"
+            placeholder="请输入楼栋名称"
+          />
+        </el-form-item>
+        <el-form-item label="楼栋性别 :" prop="sex">
+          <el-select v-model="ruleForm.sex" placeholder="请选择楼栋性别">
+            <el-option label="男" value="男" />
+            <el-option label="女" value="女" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="楼栋层数 :" prop="floors">
+          <el-input
+            clearable
+            v-model.trim="ruleForm.floors"
+            class="w-50 m-2"
+            placeholder="请输入楼栋层数"
+          />
+        </el-form-item>
+        <el-form-item label="起始层数 :" prop="startFloor">
+          <el-input
+            clearable
+            v-model.number="ruleForm.startFloor"
+            class="w-50 m-2"
+            placeholder="请输入起始层数"
+          />
+        </el-form-item>
+        <el-form-item label="备注 :" prop="">
+          <el-input
+            v-model.trim="ruleForm.remark"
+            placeholder="请输入备注"
+            clearable
+          />
+        </el-form-item>
+        <el-form-item class="options">
+          <el-button @click="cancelAdd">取消</el-button>
+          <el-button
+            color="rgba(0, 97, 255, 1)"
+            class="queding"
+            type="primary"
+            @click="submitAdd(ruleFormRef)"
+          >
+            确定
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </el-dialog>
+    <!-- 项目导入 -->
+    <el-dialog
+      class="projectImport"
+      v-model="buildImportVisible"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      title="楼栋信息导入"
+      align-center
+      width="600"
+      :before-close="cancelProjectImport"
+    >
+      <!-- <p class="title">当前只支持项目类型为“非装配式建筑项目”的项目导入</p> -->
+      <p class="down">
+        <span>楼栋信息导入模板下载</span
+        ><span @click="templateDown">模板下载</span>
+      </p>
+      <el-upload
+        class="avatar-uploader"
+        action="#"
+        :auto-upload="false"
+        :on-remove="projectImportRemove"
+        :on-change="projectImportChange"
+        :before-upload="beforeAvatarProImport"
+        ref="buildRef"
+        :limit="1"
+        :on-exceed="projectImportExceed"
+      >
+        <template #trigger>
+          <el-button class="queding" type="primary">
+            &nbsp;&nbsp;导入文件&nbsp;&nbsp;
+          </el-button>
+        </template>
+      </el-upload>
+      <div class="options">
+        <el-button
+          color="rgba(9, 101, 98, 1)"
+          class="queding"
+          type="primary"
+          @click="projectImportConfirm(buildRef)"
+        >
+          &nbsp;&nbsp;确认导入&nbsp;&nbsp;
+        </el-button>
+        <el-button @click="cancelProjectImport"
+          >&nbsp;&nbsp;取消导入&nbsp;&nbsp;</el-button
+        >
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import {
+  ref,
+  watch,
+  reactive,
+  nextTick,
+  onBeforeMount,
+  onUnmounted,
+} from "vue";
+import { useRouter } from "vue-router";
+import { genFileId, ElMessage, ElMessageBox } from "element-plus";
+import { dayjs } from "element-plus";
+import lodash from "lodash";
+import { https } from "@/utils/request"; // 绝对路径
+
+import { storeToRefs } from "pinia";
+import { useCounterStore } from "@/stores/index";
+
+const router = useRouter();
+const store = useCounterStore();
+
+// 为避免解构时失去响应性
+const { name, age, isCollapse, realAge } = storeToRefs(store);
+
+// 表格数据
+const loading = ref(false);
+const tableData = reactive({
+  list: [],
+});
+const dialongTitle = ref("新增账号"); // 弹窗标题
+// 搜索按钮数据
+const searchInput = reactive({
+  schoolId: null,
+  buildId: null,
+  sex: null,
+});
+
+const currentPage = ref(1); // 当前页
+const pageSize = ref(10);
+const total = ref(0); // 当前总数
+const selectIds = ref([]);
+
+const addDialogVisible = ref(false); // 控制添加账号弹窗
+
+// 表单数据
+const formSize = ref("default");
+const ruleFormRef = ref();
+const ruleForm = reactive({
+  schoolId: null, //校区名称
+  build: null, //楼栋名称
+  sex: null, //楼栋性别
+  floors: null, //楼栋层数
+  startFloor: null, //起始层数
+  remark: null, //备注
+  id: null,
+});
+// 表单验证
+const rules = reactive({
+  schoolId: [{ required: true, message: "校区名称不能为空", trigger: "blur" }],
+  build: [{ required: true, message: "楼栋名称不能为空", trigger: "blur" }],
+  sex: [{ required: true, message: "楼栋性别不能为空", trigger: "blur" }],
+  floors: [{ required: true, message: "楼栋层数不能为空", trigger: "blur" }],
+  startFloor: [
+    { required: true, message: "起始层数不能为空", trigger: "blur" },
+    { type: 'number', message: '请输入数字' },
+  ],
+});
+
+// 校区数据
+const schoolData = ref([]);
+
+// 楼栋数据
+const buildData = ref([]);
+
+// 导入
+const buildImportVisible = ref(false);
+const buildRef = ref();
+const buildFile = ref();
+
+const schoolList = async () => {
+  let res = await https.get("/welcome/api/welcome-build/schoolGroup", "params");
+  console.log(res, "校区数据");
+  if (res.code == 200) {
+    schoolData.value = res.data;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+
+const buildList = async () => {
+  let params = {
+    schoolId: searchInput.schoolId,
+  };
+  let res = await https.get(
+    "/welcome/api/welcome-build/buildGroup",
+    "params",
+    params
+  );
+  console.log(res, "楼栋数据");
+  if (res.code == 200) {
+    buildData.value = res.data;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+
+const schoolChange = async (val) => {
+  console.log(val);
+  searchInput.buildId = null;
+  if (!val) {
+    buildData.value = null;
+    return;
+  }else{
+    buildData.value = null;
+  }
+  buildList();
+};
+
+// 获取账户列表
+const getList = async () => {
+  loading.value = true;
+  let params = {
+    currentPage: currentPage.value, // 当前页
+    pageCount: pageSize.value, // 一页数据条数
+    schoolId: searchInput.schoolId,
+    buildId: searchInput.buildId,
+    sex: searchInput.sex,
+  };
+  console.log(params);
+
+  let res = await https.get(
+    "/welcome/api/welcome-build/listBuild",
+    "params",
+    params
+  );
+  console.log(res, "楼栋信息管理");
+  if (res.code == 200) {
+    tableData.list = res.data.list;
+    total.value = res.data.totalCount;
+    loading.value = false;
+  } else {
+    loading.value = false;
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+
+// 搜索功能
+const searchBtn = lodash.debounce(async () => {
+  getList();
+}, 300);
+const resetBtn = lodash.debounce(async () => {
+  searchInput.schoolId = null;
+  searchInput.buildId = null;
+  buildData.value = null;
+  searchInput.sex = null;
+  getList();
+}, 300);
+
+// 添加账号
+const addlist = () => {
+  dialongTitle.value = "新增楼栋信息";
+  addDialogVisible.value = true;
+  ruleForm.schoolId = null;
+  ruleForm.build = null;
+  ruleForm.sex = null;
+  ruleForm.floors = null;
+  ruleForm.startFloor = null;
+  ruleForm.remark = null;
+  ruleForm.id = null;
+};
+// 添加账号
+const updateS = (row) => {
+  dialongTitle.value = "编辑楼栋信息";
+  addDialogVisible.value = true;
+  ruleForm.schoolId = `${row.schoolName},${row.schoolId}`;
+  ruleForm.build = row.build;
+  ruleForm.sex = row.sex;
+  ruleForm.floors = row.floors;
+  ruleForm.startFloor = row.startFloor;
+  ruleForm.remark = row.remark;
+  ruleForm.id = row.id;
+};
+const deleteS = async (row) => {
+  ElMessageBox.confirm("是否删除此数据?", "提示!!!", {
+    confirmButtonText: "确认",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(async () => {
+      loading.value = true;
+      let data = {
+        buildId: row.id, // 当前页
+      };
+      let res = await https.get(
+        "/welcome/api/welcome-build/deleteBuild",
+        "params",
+        data
+      );
+      if (res.code == 200) {
+        ElMessage({
+          type: "success",
+          showClose: true,
+          message: res.message,
+          center: true,
+        });
+        loading.value = false;
+        getList();
+      } else {
+        loading.value = false;
+        ElMessage({
+          type: "error",
+          showClose: true,
+          message: res.message,
+          center: true,
+        });
+      }
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+
+// 确认添加员工
+const submitAdd = lodash.debounce(async (formEl) => {
+  if (!formEl) return;
+  await formEl.validate(async (valid, fields) => {
+    if (valid) {
+      let data = {
+        school:ruleForm.schoolId.split(',')[0],
+        schoolId: ruleForm.schoolId.split(',')[1],
+        build: ruleForm.build,
+        sex: ruleForm.sex,
+        floors: ruleForm.floors,
+        startFloor: ruleForm.startFloor,
+        remark: ruleForm.remark,
+      };
+      if (ruleForm.id) {
+        data.id = ruleForm.id;
+        let res = await https.post(
+          "/welcome/api/welcome-build/updateBuild",
+          "data",
+          data
+        );
+        if (res.code == 200) {
+          addDialogVisible.value = false;
+          getList();
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: res.message,
+            center: true,
+          });
+        } else {
+          ElMessage({
+            type: "error",
+            showClose: true,
+            message: res.message,
+            center: true,
+          });
+        }
+      } else {
+        let res = await https.post(
+          "/welcome/api/welcome-build/saveBuild",
+          "data",
+          data
+        );
+        if (res.code == 200) {
+          addDialogVisible.value = false;
+          getList();
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: res.message,
+            center: true,
+          });
+        } else {
+          ElMessage({
+            type: "error",
+            showClose: true,
+            message: res.message,
+            center: true,
+          });
+        }
+      }
+    } else {
+      console.log("error submit!", fields);
+      loading.value = false;
+    }
+  });
+}, 1000);
+const cancelAdd = () => {
+  addDialogVisible.value = false;
+};
+
+// 表格斑马纹颜色修改
+const tableRowClassName = ({ row, rowIndex }) => {
+  if (rowIndex % 2 === 0) {
+    return "even";
+  } else if (rowIndex % 2 !== 0) {
+    return "odd";
+  }
+  return "";
+};
+// 每页显示条数
+const handleSizeChange = (value) => {
+  console.log(value, "每页显示条数");
+  pageSize.value = value;
+  getList();
+};
+// 分页
+const handleCurrentChange = (value) => {
+  // console.log(value);
+  currentPage.value = value;
+  getList();
+};
+
+// 楼栋导出
+const buildExportbtn = async () => {
+  let params = {
+    schoolId: searchInput.schoolId,
+    buildId: searchInput.buildId,
+    sex: searchInput.sex,
+  };
+  let res = await https.getBlob(
+    "/welcome/api/welcome-build/welcomeBuildExport",
+    "params",
+    params
+  );
+  console.log(res, "楼栋导出");
+  let name = `楼栋信息管理`;
+  var content = res;
+  var datas = new Blob([content]);
+  var downloadUrl = window.URL.createObjectURL(datas);
+  var anchor = document.createElement("a");
+  anchor.href = downloadUrl;
+  anchor.download = name + ".xlsx";
+  anchor.click();
+  window.URL.revokeObjectURL(datas);
+  ElMessage({
+    type: "success",
+    showClose: true,
+    message: "导出成功",
+    center: true,
+  });
+};
+// 楼栋导入 (--------------------------------------------------)
+const buildImportBtn = () => {
+  buildImportVisible.value = true;
+};
+// 模板下载
+const templateDown = async () => {
+  let res = await https.get(
+    "/welcome/api/welcome-build/downloadBuildExcel",
+    "params"
+  );
+  console.log(res, "模板下载");
+  if (res.code == 200) {
+    window.location.href = res.data;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+// 移出照片
+const projectImportRemove = (uploadFile, uploadFiles) => {
+  console.log(uploadFile, uploadFiles);
+  buildFile.value = "";
+};
+// 限制上传图片的大小
+// const beforeAvatarProImport = (rawFile) => {
+//   console.log(rawFile, 33333);
+//   if (rawFile.type !== "image/jpeg" && rawFile.type !== "image/png") {
+//     ElMessage.error("图片格式必须为JPG/PNG格式!");
+//     return false;
+//   } else if (rawFile.size / 1024 > 150) {
+//     ElMessage.error("图片的大小不能超过150kb!");
+//     return false;
+//   }
+//   return true;
+// };
+// 添加照片时往fileList列表中添加图片信息
+const projectImportChange = async (file, fileLists) => {
+  console.log(file, "1111");
+  buildFile.value = file.raw;
+};
+const projectImportExceed = (files) => {
+  buildRef.value.clearFiles();
+  const file = files[0];
+  file.uid = genFileId();
+  buildRef.value.handleStart(file);
+};
+// 导入项目
+const projectImportConfirm = async () => {
+  let data = new FormData();
+  data.set("file", buildFile.value);
+  let res = await https.post(
+    "/welcome/api/welcome-build/importBuildExcel",
+    "data",
+    data
+  );
+  console.log(res, "导入项目");
+  if (res.code == 200) {
+    buildImportVisible.value = false;
+    getList();
+    ElMessage({
+      type: "success",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+const cancelProjectImport = () => {
+  buildImportVisible.value = false;
+};
+
+onBeforeMount(() => {
+  getList();
+  schoolList();
+});
+onUnmounted(() => {
+  // document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  width: calc(100% - 40px);
+  height: calc(100% - 105px);
+  margin: 20px auto;
+  background-color: #fff;
+  color: #000;
+  display: flex;
+  flex-direction: column;
+
+  .svg {
+    width: 22px;
+    height: 22px;
+  }
+
+  .left {
+    width: calc(100% - 60px);
+    height: 60px;
+    margin: 0 auto;
+    display: flex;
+    align-items: center;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+    .camera {
+      margin-right: 15px;
+      color: #4392f7;
+    }
+  }
+  .scroll {
+    width: calc(100% - 60px);
+    height: calc(100% - 61px);
+    margin: 0 auto;
+    display: flex;
+    flex-direction: column;
+
+    .middle {
+      width: calc(100%);
+      color: #000;
+      .filter {
+        display: flex;
+        flex-wrap: wrap;
+        align-items: center;
+        .search {
+          margin-left: 0 !important;
+          color: #fff;
+        }
+        .condition {
+          display: flex;
+          align-items: center;
+          margin: 10px 30px 10px 0;
+          :deep(.el-input .el-input__inner) {
+            font-size: 14px;
+          }
+          .el-select {
+            width: 220px;
+          }
+          span {
+            margin: 0 10px 0 0;
+          }
+        }
+      }
+      .gongneng {
+        margin: 10px 0;
+        .el-button {
+          color: #fff;
+          margin-right: 15px;
+        }
+      }
+    }
+    .footer {
+      width: calc(100%);
+      flex: 1;
+      margin: 0 auto;
+      overflow: auto;
+
+      .el-table--fit {
+        height: calc(100% - 60px);
+        :deep(.el-table__header-wrapper) {
+          background-color: #000;
+          font-size: 15px;
+          color: #000;
+          .cell {
+            color: #000;
+          }
+        }
+        :deep(.el-table__row) {
+          height: 50px;
+          font-size: 15px;
+          color: #000;
+        }
+        :deep(.el-table__row td) {
+          padding: 0;
+          border: 0;
+        }
+
+        .el-button--primary {
+          margin-left: 5px;
+        }
+        :deep(.el-table__body .even) {
+          background-color: #fff;
+        }
+        :deep(.el-table__body .odd) {
+          background-color: rgba(240, 243, 247, 1);
+        }
+        :deep(.options) {
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          .edit {
+            margin: 0 15px 0 0;
+            color: rgba(0, 186, 173, 1);
+            cursor: pointer;
+          }
+          .delete {
+            color: rgba(212, 48, 48, 1);
+            cursor: pointer;
+          }
+        }
+      }
+
+      .pageSize {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        margin: 0 30px;
+        height: 60px;
+
+        span {
+          color: #000;
+        }
+
+        .el-pagination {
+          // width: 1600px;
+          :deep(.el-pagination__total) {
+            color: #000;
+          }
+
+          :deep(.el-pagination__goto) {
+            color: #000;
+          }
+
+          :deep(.el-pagination__classifier) {
+            color: #000;
+          }
+
+          :deep(.el-input__wrapper) {
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            box-shadow: none;
+          }
+
+          :deep(.el-pager li) {
+            margin: 0 5px;
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            background-color: transparent;
+          }
+
+          :deep(.el-pager li.is-active) {
+            // background-color: rgba(0, 97, 255, 0.8);
+            border: 1px solid rgba(0, 97, 255, 1);
+            color: rgba(0, 97, 255, 1);
+          }
+
+          :deep(.btn-prev) {
+            margin-right: 5px;
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            background-color: transparent;
+          }
+
+          :deep(.btn-next) {
+            margin-left: 5px;
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            background-color: transparent;
+          }
+        }
+      }
+    }
+  }
+
+  // 添加员工弹窗样式
+  :deep(.addStaff) {
+    .el-dialog__body {
+      padding: 20px 20px 10px 20px;
+      .el-input {
+        width: 400px;
+        .el-input__suffix-inner {
+          color: rgba(61, 81, 232, 1);
+        }
+      }
+      .el-select {
+        width: 400px;
+      }
+      .el-tree {
+        width: 400px;
+      }
+    }
+  }
+  // 导入
+  :deep(.projectImport) {
+    .el-dialog__body {
+      padding: 20px 30px 0 30px;
+      .title {
+        color: red;
+      }
+      .down {
+        span:nth-child(1) {
+          color: rgba(127, 127, 127, 1);
+        }
+        span:nth-child(2) {
+          margin-left: 25px;
+          color: rgba(2, 167, 240, 1);
+          cursor: pointer;
+        }
+      }
+      .options {
+        margin: 50px 0 30px 0;
+        display: flex;
+        flex-direction: row-reverse;
+        .queding {
+          margin-left: 20px;
+          background: linear-gradient(
+            90deg,
+            rgba(38, 151, 255, 1) 0%,
+            rgba(102, 181, 255, 1) 100%
+          );
+          border: none;
+        }
+        .quxiao {
+          border: 1px solid rgba(43, 151, 252, 1);
+          color: rgba(43, 151, 252, 1);
+        }
+      }
+    }
+  }
+}
+</style>

+ 835 - 0
src/views/caller/caller.vue

@@ -0,0 +1,835 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <el-icon :size="23" class="camera"><VideoCameraFilled /></el-icon> -->
+      <span class="cameratxt">访客信息管理</span>
+    </div>
+    <div class="scroll">
+      <div class="middle">
+        <div class="filter">
+          <div class="condition">
+            <span>车牌号 :</span>
+            <el-input
+              clearable
+              v-model.trim="searchInput.carNumber"
+              class="w-50 m-2"
+              placeholder="请输入车牌号"
+              style="width: 180px"
+              @clear="searchBtn"
+            />
+          </div>
+          <div class="condition">
+            <span>学生姓名 :</span>
+            <el-input
+              clearable
+              v-model.trim="searchInput.name"
+              class="w-50 m-2"
+              placeholder="请输入学生姓名"
+              style="width: 180px"
+              @clear="searchBtn"
+            />
+          </div>
+          <div class="condition">
+            <span>校区名称 :</span>
+            <el-select
+              clearable
+              @change="schoolChange"
+              v-model="searchInput.school"
+              placeholder="请选择校区名称"
+            >
+              <el-option
+                v-for="i in schoolData"
+                :key="i.id"
+                :label="i.school"
+                :value="i.school"
+              />
+            </el-select>
+          </div>
+          <el-button
+            style="margin-left: 20px"
+            color="rgba(38, 151, 255, 1)"
+            type="primary"
+            class="search"
+            @click="searchBtn"
+            ><span>查询</span></el-button
+          >
+          <el-button @click="resetBtn" plain color="rgba(43, 153, 255, 1)">重置</el-button>
+        </div>
+        <!-- 按钮列表 -->
+        <div class="gongneng">
+          <el-button
+           v-if="store.BtnRole('visitorManagementSetting1')"
+            type="primary"
+            color="rgba(48, 201, 191, 1)"
+            @click="buildExportbtn"
+            >导出</el-button
+          >
+          <el-button
+          v-if="store.BtnRole('visitorManagementSetting2')"
+            type="primary"
+            style="margin-left: 0"
+            color="rgba(38, 151, 255, 1)"
+            @click="addlist"
+            >新增</el-button
+          >
+        </div>
+      </div>
+      <div class="footer" v-loading="loading">
+        <el-table
+          :row-class-name="tableRowClassName"
+          :data="tableData.list"
+          @selection-change="handleSelectionChange"
+          style="width: 100%"
+          :header-cell-style="{
+            background: 'rgba(240, 243, 247, 1)',
+            height: '50px',
+            border: 0,
+          }"
+        >
+          <el-table-column
+            width="80"
+            align="center"
+            label="序号"
+            type="index"
+            index="1"
+          />
+          <el-table-column
+            align="center"
+            prop="name"
+            label="学生姓名"
+          />
+          <el-table-column
+            align="center"
+            prop="studentCard"
+            label="学号/录取号"
+          />
+          <el-table-column
+            align="center"
+            prop="phone"
+            label="手机号码"
+          />
+          <el-table-column
+            align="center"
+            tooltip-formatter
+            prop="visitorReason"
+            label="访问事由"
+          />
+          <el-table-column
+            align="center"
+            prop="school"
+            label="校区名称"
+          />
+          <el-table-column
+            align="center"
+            prop="startTime"
+            label="到访开始时间"
+          />
+          <el-table-column
+            align="center"
+            prop="endTime"
+            label="到访结束时间"
+          />
+          <el-table-column
+            align="center"
+            prop="carNumber"
+            width="100"
+            label="车牌号"
+          />
+          <el-table-column
+            align="center"
+            prop="peerNum"
+            width="100"
+            label="陪同人数"
+          />
+          <el-table-column
+            align="center"
+            tooltip-formatter
+            width="200"
+            prop="remark"
+            label="备注"
+          />
+          <el-table-column align="center" label="操作" fixed="right" min-width="120">
+            <template #default="{ row }">
+              <div class="options">
+                <span v-if="store.BtnRole('visitorManagementSetting3')" class="edit" @click="updateS(row)">编辑</span>
+                <span v-if="store.BtnRole('visitorManagementSetting4')" class="delete" @click="deleteS(row)">删除</span>
+              </div>
+            </template>
+          </el-table-column>
+        </el-table>
+        <!-- 分页组件 -->
+        <div class="pageSize">
+          <span></span>
+          <el-pagination
+            background
+            :current-page="currentPage"
+            :page-size="pageSize"
+            :page-sizes="[10, 20, 30, 40]"
+            layout="total,sizes, prev, pager, next, jumper, slot"
+            :total="total"
+            @size-change="handleSizeChange"
+            @update:current-page="handleCurrentChange"
+          />
+        </div>
+      </div>
+    </div>
+    <!-- 添加账号弹窗 -->
+    <el-dialog
+      class="addStaff"
+      v-model="addDialogVisible"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      :title="dialongTitle"
+      align-center
+      width="580"
+      :before-close="cancelAdd"
+      destroy-on-close
+      draggable
+    >
+      <el-form
+        ref="ruleFormRef"
+        :model="ruleForm"
+        :rules="rules"
+        label-width="120px"
+        class="demo-ruleForm"
+        :size="formSize"
+        label-position="right"
+        status-icon
+      >
+        <el-form-item label="学生姓名 :" prop="name">
+          <el-input
+            v-model.trim="ruleForm.name"
+            placeholder="请输入学生姓名"
+            clearable
+          />
+        </el-form-item>
+        <el-form-item label="学号/录取号 :" prop="studentCard">
+          <el-input
+            v-model.trim="ruleForm.studentCard"
+            placeholder="请输入学号或录取号"
+            clearable
+          />
+        </el-form-item>
+        <el-form-item label="手机号码 :" prop="phone">
+          <el-input
+            v-model.trim="ruleForm.phone"
+            placeholder="请输入手机号码"
+            clearable
+          />
+        </el-form-item>
+        <el-form-item label="校区名称 :" prop="school">
+          <el-select
+            clearable
+            v-model="ruleForm.school"
+            placeholder="请选择校区名称"
+          >
+            <el-option
+              v-for="i in schoolData"
+              :key="i.id"
+              :label="i.school"
+              :value="`${i.school},${i.id}`"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item style="width: 520px" label="到访时间 :" prop="startTime">
+          <el-date-picker
+            v-model="ruleForm.startTime"
+            type="datetimerange"
+            range-separator="-"
+            start-placeholder="起始时间"
+            end-placeholder="结束时间"
+            format="YYYY-MM-DD HH:mm:ss"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            placeholder="请选择日期"
+          />
+        </el-form-item>
+        <el-form-item label="车牌号 :" prop="carNumber">
+          <el-input
+            v-model.trim="ruleForm.carNumber"
+            placeholder="请输入车牌号"
+            clearable
+          />
+        </el-form-item>
+        <el-form-item label="同行人数 :" prop="peerNum">
+          <el-input
+            v-model.trim="ruleForm.peerNum"
+            placeholder="请输入同行人数"
+            clearable
+          />
+        </el-form-item>
+        <el-form-item label="访问事由 :" prop="visitorReason">
+          <el-input
+            v-model.trim="ruleForm.visitorReason"
+            placeholder="请输入访问事由"
+            :autosize="{ minRows: 2, maxRows: 4 }"
+            type="textarea"
+            style="width: 400px"
+          />
+        </el-form-item>
+        <!-- <el-form-item label="备注 :" prop="remark">
+          <el-input
+            v-model.trim="ruleForm.remark"
+            placeholder="请输入备注"
+            clearable
+          />
+        </el-form-item> -->
+        <el-form-item class="options">
+          <el-button @click="cancelAdd">取消</el-button>
+          <el-button
+            color="rgba(0, 97, 255, 1)"
+            class="queding"
+            type="primary"
+            @click="submitAdd(ruleFormRef)"
+          >
+            确定
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import {
+  ref,
+  watch,
+  reactive,
+  nextTick,
+  onBeforeMount,
+  onUnmounted,
+} from "vue";
+import { useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { dayjs } from "element-plus";
+import lodash from "lodash";
+import { https } from "@/utils/request"; // 绝对路径
+
+import { storeToRefs } from "pinia";
+import { useCounterStore } from "@/stores/index";
+
+const router = useRouter();
+const store = useCounterStore();
+
+// 为避免解构时失去响应性
+const { name, age, isCollapse, realAge } = storeToRefs(store);
+
+// 表格数据
+const loading = ref(false);
+const tableData = reactive({
+  list: [{}],
+});
+const activeIndex = ref(); // 默认跳转路由
+const dialongTitle = ref("新增账号"); // 弹窗标题
+
+const searchInput = reactive({
+  name: "",
+  carNumber: "",
+  school:""
+}); // 搜索按钮数据
+
+const currentPage = ref(1); // 当前页
+const pageSize = ref(10);
+const total = ref(); // 当前总数
+const selectIds = ref([]);
+
+const addDialogVisible = ref(false); // 控制添加账号弹窗
+
+// 表单数据
+const formSize = ref("default");
+const ruleFormRef = ref();
+const ruleForm = reactive({
+  phone: "13011111111", // 驾驶员手机号
+  studentCard: "1234", // 驾驶员证件号
+  name: "测试", // 学生姓名
+  school: "墨轩湖校区", // 校区名称不能为空 只能为墨轩湖校区或黄家湖校区
+  startTime: "2025-06-16 10:00:00", // 到访开始时间
+  carNumber: "赣A0AY39", // 车牌号
+  peerNum: 1, // 同行人数不能为空
+  visitorReason: "测试", // 访问事由
+  // remark: "测试", // 访问事由
+  id: "",
+});
+// 表单验证
+const rules = reactive({
+  driverName: [{ required: true, message: "驾驶员姓名不能为空", trigger: "blur" }],
+  phone: [{ required: true, message: "驾驶员手机号不能为空", trigger: "blur" }],
+  name: [{ required: true, message: "学生姓名不能为空", trigger: "blur" }],
+  studentCard: [{ required: true, message: "学生证件号不能为空", trigger: "blur" }],
+  cardId: [{ required: true, message: "证件号不能为空", trigger: "blur" }],
+  school: [{ required: true, message: "校区名称不能为空", trigger: "blur" }],
+  startTime: [{ required: true, message: "到访时间不能为空", trigger: "blur" }],
+  carNumber: [{ required: true, message: "车牌号不能为空", trigger: "blur" }],
+  peerNum: [{ required: true, message: "同行人数不能为空", trigger: "blur" }],
+  visitorReason: [
+    { required: true, message: "访问事由不能为空", trigger: "blur" },
+  ],
+});
+
+const schoolData = ref();
+
+// 获取账户列表
+const getList = async () => {
+  loading.value = true;
+  let params = {
+    page: currentPage.value, // 当前页
+    limit: pageSize.value, // 一页数据条数
+    name: searchInput.name,
+    carNumber: searchInput.carNumber,
+    school: searchInput.school,
+  };
+
+  let res = await https.get(
+    "/welcome/api/visitor/admin/page",
+    "params",
+    params
+  );
+  console.log(res, "驾驶员信息管理");
+  if (res.code == 200) {
+    tableData.list = res.data.list;
+    total.value = res.data.totalCount;
+    loading.value = false;
+  } else {
+    loading.value = false;
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+const schoolList = async () => {
+  let res = await https.get("/welcome/api/welcome-build/schoolGroup", "params");
+  console.log(res, "校区数据");
+  if (res.code == 200) {
+    schoolData.value = res.data;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+// 搜索功能
+const searchBtn = lodash.debounce(async () => {
+  getList();
+}, 300);
+const resetBtn = lodash.debounce(async () => {
+  searchInput.name=null
+  searchInput.carNumber=null
+  searchInput.school=null
+  getList();
+}, 300);
+
+// 添加账号
+const addlist = () => {
+  dialongTitle.value = "新增访客信息";
+  addDialogVisible.value = true;
+  ruleForm.phone = null; // 手机号
+  ruleForm.name = null; // 学生姓名
+  ruleForm.studentCard=null
+  ruleForm.school = null; // 校区名称不能为空 只能为墨轩湖校区或黄家湖校区
+  ruleForm.startTime = null; // 到访开始时间
+  ruleForm.carNumber = null; // 车牌号
+  ruleForm.peerNum = null; // 同行人数不能为空
+  ruleForm.visitorReason = null; // 访问事由
+  // ruleForm.remark = null; // 访问事由
+};
+// 添加账号
+const updateS = (row) => {
+  dialongTitle.value = "编辑访客信息";
+  console.log(row);
+  
+  addDialogVisible.value = true;
+  ruleForm.phone = row.phone;
+  ruleForm.studentCard=row.studentCard
+  ruleForm.name = row.name;
+  ruleForm.school = `${row.school},${row.schoolId}`;
+  ruleForm.startTime = [row.startTime,row.endTime];
+  ruleForm.carNumber = row.carNumber;
+  ruleForm.peerNum = row.peerNum;
+  ruleForm.visitorReason = row.visitorReason;
+  // ruleForm.remark = row.remark;
+  ruleForm.id = row.id;
+};
+const deleteS = async (row) => {
+  ElMessageBox.confirm("是否删除此数据?", "提示!!!", {
+    confirmButtonText: "确认",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(async () => {
+      let data = [row.id];
+      let res = await https.delete("/welcome/api/visitor/admin/batch-del", "data", data);
+      if (res.code == 200) {
+        ElMessage({
+          type: "success",
+          showClose: true,
+          message: res.message,
+          center: true,
+        });
+        getList();
+      } else {
+        ElMessage({
+          type: "error",
+          showClose: true,
+          message: res.message,
+          center: true,
+        });
+      }
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+
+// 确认添加员工
+const submitAdd = lodash.debounce(async (formEl) => {
+  if (!formEl) return;
+  await formEl.validate(async (valid, fields) => {
+    if (valid) {
+      let data = {
+        phone: ruleForm.phone,
+        name: ruleForm.name,
+        studentCard:ruleForm.studentCard,
+        school: ruleForm.school.split(',')[0],
+        schoolId: ruleForm.school.split(',')[1],
+        startTime: ruleForm.startTime[0],
+        endTime: ruleForm.startTime[1],
+        carNumber: ruleForm.carNumber,
+        peerNum: ruleForm.peerNum,
+        visitorReason: ruleForm.visitorReason,
+        // remark: ruleForm.remark,
+      };
+      console.log(data);
+
+      if (ruleForm.id) {
+        data.id = ruleForm.id;
+        let res = await https.put(
+          "/welcome/api/visitor/admin/update",
+          "data",
+          data
+        );
+        if (res.code == 200) {
+          addDialogVisible.value = false;
+          getList();
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: res.message,
+            center: true,
+          });
+        } else {
+          ElMessage({
+            type: "error",
+            showClose: true,
+            message: res.message,
+            center: true,
+          });
+        }
+      } else {
+        let res = await https.post(
+          "/welcome/api/visitor/admin/add",
+          "data",
+          data
+        );
+        if (res.code == 200) {
+          addDialogVisible.value = false;
+          getList();
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: res.message,
+            center: true,
+          });
+        } else {
+          ElMessage({
+            type: "error",
+            showClose: true,
+            message: res.message,
+            center: true,
+          });
+        }
+      }
+    } else {
+      console.log("error submit!", fields);
+      loading.value = false;
+    }
+  });
+}, 1000);
+const cancelAdd = () => {
+  addDialogVisible.value = false;
+  // ruleFormRef.value.resetFields();
+};
+// 多选框功能
+const handleSelectionChange = (val) => {
+  console.log(val);
+  selectIds.value = val.map((i) => i.id);
+};
+
+// 表格斑马纹颜色修改
+const tableRowClassName = ({ row, rowIndex }) => {
+  if (rowIndex % 2 === 0) {
+    return "even";
+  } else if (rowIndex % 2 !== 0) {
+    return "odd";
+  }
+  return "";
+};
+// 每页显示条数
+const handleSizeChange = (value) => {
+  console.log(value, "每页显示条数");
+  pageSize.value = value;
+  getList();
+};
+// 分页
+const handleCurrentChange = (value) => {
+  // console.log(value);
+  currentPage.value = value;
+  getList();
+};
+// 楼栋导出
+const buildExportbtn = async () => {
+  let params = {
+    name: searchInput.name,
+    carNumber: searchInput.carNumber,
+    school: searchInput.school
+  };
+  let res = await https.getBlob(
+    "/welcome/api/visitor/admin/export",
+    "params",
+    params
+  );
+  console.log(res, "床位信息管理导出");
+  let name = `访客信息管理`;
+  var content = res;
+  var datas = new Blob([content]);
+  var downloadUrl = window.URL.createObjectURL(datas);
+  var anchor = document.createElement("a");
+  anchor.href = downloadUrl;
+  anchor.download = name + ".xlsx";
+  anchor.click();
+  window.URL.revokeObjectURL(datas);
+  ElMessage({
+    type: "success",
+    showClose: true,
+    message: "导出成功",
+    center: true,
+  });
+};
+
+onBeforeMount(() => {
+  getList();
+  schoolList();
+});
+onUnmounted(() => {
+  // document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  width: calc(100% - 40px);
+  height: calc(100% - 105px);
+  margin: 20px auto;
+  background-color: #fff;
+  color: #000;
+  display: flex;
+  flex-direction: column;
+
+  .svg {
+    width: 22px;
+    height: 22px;
+  }
+
+  .left {
+    width: calc(100% - 60px);
+    height: 60px;
+    margin: 0 auto;
+    display: flex;
+    align-items: center;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+    .camera {
+      margin-right: 15px;
+      color: #4392f7;
+    }
+  }
+  .scroll {
+    width: calc(100% - 60px);
+    height: calc(100% - 61px);
+    margin: 0 auto;
+    display: flex;
+    flex-direction: column;
+
+    .middle {
+      width: calc(100%);
+      color: #000;
+      .filter {
+        display: flex;
+        flex-wrap: wrap;
+        align-items: center;
+        .search {
+          margin-left: 0 !important;
+          color: #fff;
+        }
+        .condition {
+          display: flex;
+          align-items: center;
+          margin: 10px 30px 10px 0;
+          :deep(.el-input .el-input__inner) {
+            font-size: 14px;
+          }
+          .el-select{
+            width: 220px;
+          }
+          span {
+            margin: 0 10px 0 0;
+          }
+        }
+      }
+      .gongneng {
+        margin: 10px 0;
+        .el-button {
+          color: #fff;
+          margin-right: 15px;
+        }
+      }
+    }
+    .footer {
+      width: calc(100%);
+      flex: 1;
+      margin: 0 auto;
+      overflow: auto;
+
+      .el-table--fit {
+        height: calc(100% - 60px);
+        :deep(.el-table__header-wrapper) {
+          background-color: #000;
+          font-size: 15px;
+          color: #000;
+          .cell {
+            color: #000;
+          }
+        }
+        :deep(.el-table__row) {
+          height: 50px;
+          font-size: 15px;
+          color: #000;
+        }
+        :deep(.el-table__row td) {
+          padding: 0;
+          border: 0;
+        }
+
+        .el-button--primary {
+          margin-left: 5px;
+        }
+        :deep(.el-table__body .even) {
+          background-color: #fff;
+        }
+        :deep(.el-table__body .odd) {
+          background-color: rgba(240, 243, 247, 1);
+        }
+        :deep(.options) {
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          .edit {
+            margin: 0 15px 0 0;
+            color: rgba(0, 186, 173, 1);
+            cursor: pointer;
+          }
+          .delete {
+            color: rgba(212, 48, 48, 1);
+            cursor: pointer;
+          }
+        }
+      }
+
+      .pageSize {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        margin: 0 30px;
+        height: 60px;
+
+        span {
+          color: #000;
+        }
+
+        .el-pagination {
+          // width: 1600px;
+          :deep(.el-pagination__total) {
+            color: #000;
+          }
+
+          :deep(.el-pagination__goto) {
+            color: #000;
+          }
+
+          :deep(.el-pagination__classifier) {
+            color: #000;
+          }
+
+          :deep(.el-input__wrapper) {
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            box-shadow: none;
+          }
+
+          :deep(.el-pager li) {
+            margin: 0 5px;
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            background-color: transparent;
+          }
+
+          :deep(.el-pager li.is-active) {
+            // background-color: rgba(0, 97, 255, 0.8);
+            border: 1px solid rgba(0, 97, 255, 1);
+            color: rgba(0, 97, 255, 1);
+          }
+
+          :deep(.btn-prev) {
+            margin-right: 5px;
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            background-color: transparent;
+          }
+
+          :deep(.btn-next) {
+            margin-left: 5px;
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            background-color: transparent;
+          }
+        }
+      }
+    }
+  }
+
+  // 添加员工弹窗样式
+  :deep(.addStaff) {
+    .el-dialog__body {
+      padding: 20px 20px 10px 20px;
+      .el-input {
+        width: 400px;
+        .el-input__suffix-inner {
+          color: rgba(61, 81, 232, 1);
+        }
+      }
+      .el-select {
+        width: 400px;
+      }
+      .el-tree {
+        width: 400px;
+      }
+    }
+  }
+}
+</style>

+ 798 - 0
src/views/dataOverview/dataOverview.vue

@@ -0,0 +1,798 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <el-icon :size="23" class="camera"><VideoCameraFilled /></el-icon> -->
+      <div class="cameratxt">数据总览</div>
+    </div>
+    <!-- 处置情况 -->
+    <!-- <div class="system_title">
+      <h4>处置情况</h4>
+    </div> -->
+    <ul class="dispose" style="margin-top: 20px">
+      <li v-for="i in disposeList" :key="i.icon">
+        <SvgIcon :name="i.icon" color="#fff" size="55"></SvgIcon>
+        <div class="dispose_content">
+          <p style="color: rgba(128, 128, 128, 1); font-size: 18px">
+            {{ i.title }}
+          </p>
+
+          <p style="font-weight: 600; font-size: 30px">
+            <span>{{ i.num }}</span>
+            <span v-if="i.numTotal">&nbsp;/&nbsp;</span>
+            <span v-if="i.numTotal">{{ i.numTotal }}</span>
+          </p>
+        </div>
+      </li>
+      <li v-if="carSchool == 'huang'">
+        <SvgIcon name="carNum" color="#fff" size="55"></SvgIcon>
+        <div class="dispose_content">
+          <p
+            class="yuyue"
+            style="color: rgba(128, 128, 128, 1); font-size: 18px"
+          >
+            <span
+              @click="carSchoolChange('huang')"
+              class="active"
+              style="margin-right: 20px"
+              >黄家湖</span
+            >
+            <span @click="carSchoolChange('mo')">墨轩湖</span>
+          </p>
+          <p style="color: rgba(128, 128, 128, 1); font-size: 18px">
+            今日约车牌数 / 剩余车位数
+          </p>
+          <p style="font-weight: 600; font-size: 30px">
+            <span
+              @click="callerClick"
+              style="color: rgba(0, 97, 255, 1); cursor: pointer"
+              >{{ carArr.huang.num }}</span
+            >
+            <span>&nbsp;/&nbsp;</span>
+            <span>{{ carArr.huang.numTotal }}</span>
+          </p>
+        </div>
+      </li>
+      <li v-if="carSchool == 'mo'">
+        <SvgIcon name="carNum" color="#fff" size="55"></SvgIcon>
+        <div class="dispose_content">
+          <p
+            class="yuyue"
+            style="color: rgba(128, 128, 128, 1); font-size: 18px"
+          >
+            <span @click="carSchoolChange('huang')" style="margin-right: 20px"
+              >黄家湖</span
+            >
+            <span class="active" @click="carSchoolChange('mo')">墨轩湖</span>
+          </p>
+          <p style="color: rgba(128, 128, 128, 1); font-size: 18px">
+            今日约车牌数 / 剩余车位数
+          </p>
+          <p style="font-weight: 600; font-size: 30px">
+            <span
+              @click="callerClick"
+              style="color: rgba(0, 97, 255, 1); cursor: pointer"
+              >{{ carArr.mo.num }}</span
+            >
+            <span>&nbsp;/&nbsp;</span>
+            <span>{{ carArr.mo.numTotal }}</span>
+          </p>
+        </div>
+      </li>
+    </ul>
+
+    <!-- 学院新生报到 -->
+    <div class="echarts">
+      <div class="echarts_register">
+        <div class="system_title">
+          <h4>学院新生报到情况</h4>
+        </div>
+        <div id="echarts_register"></div>
+      </div>
+      <div class="echarts_traffic">
+        <div class="system_title">
+          <h4>交通方式情况</h4>
+        </div>
+        <div id="echarts_traffic"></div>
+      </div>
+    </div>
+
+    <!-- 性别比例 -->
+    <div class="system_title">
+      <h4>学院新生性别比例</h4>
+    </div>
+    <div class="gender">
+      <div id="gender" ref="genderRef"></div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import {
+  ref,
+  watch,
+  reactive,
+  nextTick,
+  onMounted,
+  onBeforeMount,
+  onBeforeUnmount,
+  onUnmounted,
+} from "vue";
+import { useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { dayjs } from "element-plus";
+import lodash from "lodash";
+import * as echarts from "echarts";
+import { https } from "@/utils/request"; // 绝对路径
+
+import { storeToRefs } from "pinia";
+import { useCounterStore } from "@/stores/index";
+import { right } from "@popperjs/core";
+
+const api = ref("");
+const router = useRouter();
+const store = useCounterStore();
+
+const tableData = reactive({
+  list: [],
+});
+const currentPage = ref(1); // 当前页
+const pageSize = ref(10);
+const total = ref(0); // 当前总数
+
+// 为避免解构时失去响应性
+const { name, age, isCollapse, realAge } = storeToRefs(store);
+
+// 处置情况
+const disposeList = ref([
+  {
+    icon: "luqu",
+    num: 1,
+    title: "录取人数",
+  },
+  {
+    icon: "yubao",
+    num: 1,
+    title: "预报到人数",
+  },
+  {
+    icon: "jiaofei",
+    num: 1,
+    title: "已缴费人数",
+  },
+  {
+    icon: "ruzhu",
+    num: "1 / 10",
+    title: "入住寝室数/总数",
+  },
+  {
+    icon: "yuyue",
+    num: "2 / 12",
+    title: "今日预约车牌数/总数",
+  },
+]);
+const carSchool = ref("huang");
+const carArr = reactive({
+  huang: {},
+  mo: {},
+});
+const carSchoolChange = (title) => {
+  carSchool.value = title;
+  console.log(disposeList.value);
+
+  disposeList.value.forEach((i) => {
+    if (i.icon == "carNum") {
+      if (carSchool.value == "huang") {
+        i = carArr.huang;
+      } else if (carSchool.value == "mo") {
+        i = carArr.mo;
+      }
+    }
+  });
+};
+
+// 学院新生报到情况
+let registerEcharts = null;
+
+// 学院新生报到情况
+let trafficEcharts = null;
+
+// 性别比例
+let genderEcharts = null;
+
+// 处置情况
+const studentOverview = async () => {
+  let res = await https.get(
+    "/welcome/api/welcomeStudent/studentOverview",
+    "params"
+  );
+  console.log(res, "处置情况");
+  if (res.code == 200) {
+    carArr.huang = {
+      icon: "carNum",
+      num: res.data.hvisitorTotal,
+      numTotal: res.data.hcarTotal,
+      title: "今日约车牌数 / 剩余车位数",
+    };
+    carArr.mo = {
+      icon: "carNum",
+      num: res.data.mvisitorTotal,
+      numTotal: res.data.mcarTotal,
+      title: "今日约车牌数 / 剩余车位数",
+    };
+
+    disposeList.value = [
+      {
+        icon: "luqu",
+        num: res.data.enrollmentTotal,
+        title: "录取人数",
+      },
+      {
+        icon: "jiaofei",
+        num: res.data.payCount,
+        title: "已缴费人数",
+      },
+      {
+        icon: "ruzhu",
+        num: res.data.checkInBedTotal,
+        numTotal: `${res.data.registrationRate}%`,
+        title: "已入住床位数 / 报到率",
+      },
+    ];
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+// 跳转到访客信息管理
+const callerClick = () => {
+  router.push({
+    path: `/caller`,
+  });
+};
+
+// 表格斑马纹颜色修改
+const tableRowClassName = ({ row, rowIndex }) => {
+  if (rowIndex % 2 === 0) {
+    return "even";
+  } else if (rowIndex % 2 !== 0) {
+    return "odd";
+  }
+  return "";
+};
+// 每页显示条数
+const handleSizeChange = (value) => {
+  console.log(value, "每页显示条数");
+  pageSize.value = value;
+};
+// 分页
+const handleCurrentChange = (value) => {
+  // console.log(value);
+  currentPage.value = value;
+};
+
+const register = async () => {
+  let dom = document.getElementById("echarts_register");
+  registerEcharts = echarts.init(dom);
+  let res = await https.get(
+    "/welcome/api/welcomeStudent/studentRegister",
+    "params"
+  );
+  console.log(res, "新生报到情况");
+  if (res.code == 200) {
+    // const data = genData(20);
+    const data = {
+      legendData: [],
+      seriesData: [],
+    };
+    let arr = res.data.slice(0, -1);
+    data.seriesData = arr.map((i) => {
+      data.legendData.push(i.collegeName);
+      return {
+        name: i.collegeName,
+        value: i.count,
+      };
+    });
+    const option = {
+      tooltip: {
+        trigger: "item",
+        formatter: "{a} <br/>{b} : {c}人 ({d}%)",
+      },
+      legend: {
+        type: "scroll",
+        orient: "vertical",
+        right: "10%",
+        top: 10,
+        bottom: 30,
+        data: data.legendData,
+      },
+      series: [
+        {
+          name: "报到人数",
+          type: "pie",
+          radius: "60%",
+          center: ["35%", "50%"],
+          data: data.seriesData,
+          emphasis: {
+            itemStyle: {
+              shadowBlur: 10,
+              shadowOffsetX: 0,
+              shadowColor: "rgba(0, 0, 0, 0.5)",
+            },
+          },
+        },
+      ],
+    };
+    registerEcharts.setOption(option);
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+const traffic = async () => {
+  var chartDom = document.getElementById("echarts_traffic");
+  trafficEcharts = echarts.init(chartDom);
+  var option;
+
+  let res = await https.get(
+    "/welcome/api/welcomeStudent/studentTraffic",
+    "params"
+  );
+  console.log(res, "学生交通方式");
+  if (res.code == 200) {
+    let data = [];
+    data = res.data.map((i) => {
+      return { value: i.count, name: i.trafficMethod };
+    });
+    option = {
+      tooltip: {
+        trigger: "item",
+      },
+      // legend: {
+      //   top: "5%",
+      //   left: "center",
+      // },
+
+      series: [
+        {
+          name: "交通方式",
+          type: "pie",
+          radius: ["50%", "70%"],
+          avoidLabelOverlap: false,
+          padAngle: 5,
+          itemStyle: {
+            borderRadius: 10,
+          },
+          label: {
+            show: true,
+            // position: "center",
+            // alignTo: "edge",
+            formatter: "{name|{b}}\n{time|{c} ({d}%)}",
+            // minMargin: 5,
+            // edgeDistance: 10,
+            lineHeight: 18,
+            rich: {
+              time: {
+                // fontSize: 10,
+                color: "#999",
+              },
+              percent: {
+                // fontSize: 10,
+                color: "#999",
+              },
+            },
+          },
+          emphasis: {
+            label: {
+              show: true,
+              fontSize: 20,
+              fontWeight: "bold",
+            },
+          },
+          labelLine: {
+            show: false,
+          },
+          data: data,
+        },
+      ],
+    };
+
+    trafficEcharts.setOption(option);
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+const gender = async () => {
+  var chartDom = document.getElementById("gender");
+  genderEcharts = echarts.init(chartDom);
+  let res = await https.get(
+    "/welcome/api/welcomeStudent/studentSexRatio",
+    "params"
+  );
+  console.log(res, "学生性别比例");
+  if (res.code == 200) {
+    let data = [];
+    res.data.slice(0, -1).forEach((i) => {
+      if (!i.girlCount) {
+        i.girlCount = 0;
+      } else if (!i.manCount) {
+        i.manCount = 0;
+      }
+      if (i) {
+        data.push(i);
+      }
+    });
+    console.log(data);
+
+    const option = {
+      legend: {
+        right: "5%",
+        padding: [
+          10, // 上
+          10, // 右
+          5, // 下
+          10, // 左
+        ],
+      },
+      tooltip: {},
+      grid: {
+        left: "5%",
+        right: "5%",
+        top: "18%",
+        bottom: "2%",
+        containLabel: true,
+      },
+      dataset: {
+        dimensions: ["collegeName", "manCount", "girlCount"],
+        source: data,
+      },
+      xAxis: {
+        type: "category",
+        axisLine: {
+          show: false,
+        },
+        axisTick: {
+          show: false,
+        },
+      },
+      yAxis: {
+        name: "人",
+        // nameLocation:""
+      },
+      series: [
+        {
+          type: "bar",
+          name: "男",
+          barMinWidth: 20,
+          barMaxWidth: 30,
+          label: {
+            show: true,
+            position: "top",
+          },
+          itemStyle: {
+            color: "rgba(0, 97, 255, 1)",
+          },
+        },
+        {
+          type: "bar",
+          name: "女",
+          barMinWidth: 20,
+          barMaxWidth: 30,
+          label: {
+            show: true,
+            position: "top",
+          },
+          itemStyle: {
+            color: "rgba(0, 186, 173, 1)",
+          },
+        },
+      ],
+    };
+    genderEcharts.setOption(option);
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+// 学生住宿情况
+const studentStay = async () => {
+  let res = await https.get(
+    "/welcome/api/welcomeStudent/studentStay",
+    "params"
+  );
+  console.log(res, "学生住宿情况");
+  if (res.code == 200) {
+    tableData.list = res.data;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+
+const updateChartSize = () => {
+  if (registerEcharts) {
+    registerEcharts.resize();
+  }
+  if (trafficEcharts) {
+    trafficEcharts.resize();
+  }
+  if (genderEcharts) {
+    genderEcharts.resize();
+  }
+};
+onMounted(() => {
+  studentOverview();
+  register();
+  traffic();
+  gender();
+  studentStay();
+  window.addEventListener("resize", updateChartSize);
+});
+
+onBeforeMount(() => {
+  // studentOverview();
+  // studentRegister();
+  // studentTraffic();
+  // studentSexRatio();
+  // studentStay();
+});
+onUnmounted(() => {
+  // document.removeEventListener("keyup", Enters);
+});
+onBeforeUnmount(() => {
+  if (registerEcharts) {
+    registerEcharts.dispose();
+  }
+  if (trafficEcharts) {
+    trafficEcharts.dispose();
+  }
+  if (genderEcharts) {
+    genderEcharts.dispose();
+  }
+  window.removeEventListener("resize", updateChartSize);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  // min-width: calc(100% - 40px);
+  width: calc(100% - 40px);
+  height: calc(100% - 105px);
+  margin: 20px auto;
+  background-color: #fff;
+  color: #000;
+  display: flex;
+  flex-direction: column;
+  overflow: auto;
+  .left {
+    height: 60px;
+    margin: 0 30px;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+
+    .cameratxt {
+      margin-right: 15px;
+      height: 60px;
+      line-height: 60px;
+    }
+  }
+  .system_title {
+    // width: 100%;
+    height: 45px;
+    opacity: 1;
+    border-radius: 0px 12px 0px 0px;
+    background: rgb(243, 249, 255);
+    margin: 15px 30px;
+    display: flex;
+    align-items: center;
+    // justify-content: center;
+    h4 {
+      font-size: 15px;
+      padding: 0 5px 0 20px;
+    }
+    .wain {
+      font-size: 13px;
+      color: rgba(212, 48, 48, 1);
+    }
+  }
+  // 处置情况
+  .dispose {
+    margin: 0 30px;
+    display: flex;
+    justify-content: space-between;
+    list-style: none;
+    padding: 0;
+    li {
+      flex: 1;
+      margin: 0 10px;
+      height: 133px;
+      border: 1px solid rgba(143, 143, 143, 1);
+      border-radius: 6.98px;
+      background: rgba(250, 252, 255, 1);
+      display: flex;
+      align-items: center;
+      // justify-content: space-around;
+      .el-icon {
+        margin: 0 30px;
+      }
+      .dispose_content {
+        p {
+          margin: 15px 0;
+        }
+      }
+      .yuyue {
+        span {
+          cursor: pointer;
+        }
+        .active {
+          color: rgb(248, 130, 11);
+        }
+      }
+    }
+  }
+
+  // 学院新生报到情况
+  .echarts {
+    display: flex;
+    .echarts_register {
+      flex: 1;
+      #echarts_register {
+        width: calc(100% - 60px);
+        height: 300px;
+        margin: 0 auto;
+      }
+    }
+    .echarts_traffic {
+      flex: 1;
+
+      #echarts_traffic {
+        width: calc(100% - 60px);
+        height: 300px;
+        margin: 0 auto;
+      }
+    }
+  }
+
+  .gender {
+    width: calc(100% - 60px);
+    height: 300px;
+    margin: 0 auto 10px;
+    #gender {
+      width: 100%;
+      height: 300px;
+    }
+  }
+
+  .footer {
+    width: 100%;
+    margin-bottom: 25px;
+    .el-table--fit {
+      width: calc(100% - 60px);
+      margin: 0 auto;
+      :deep(.el-table__header-wrapper) {
+        background-color: #000;
+        font-size: 15px;
+        color: #000;
+        .cell {
+          color: #000;
+        }
+      }
+      :deep(.el-table__row) {
+        height: 50px;
+        font-size: 15px;
+        color: #000;
+      }
+      :deep(.el-table__row td) {
+        padding: 0;
+        border: 0;
+      }
+
+      .el-button--primary {
+        margin-left: 5px;
+      }
+      :deep(.el-table__body .even) {
+        background-color: #fff;
+      }
+      :deep(.el-table__body .odd) {
+        background-color: rgba(240, 243, 247, 1);
+      }
+      :deep(.options) {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        .edit {
+          margin: 0 15px 0 0;
+          color: rgba(0, 186, 173, 1);
+          cursor: pointer;
+        }
+        .delete {
+          color: rgba(212, 48, 48, 1);
+          cursor: pointer;
+        }
+      }
+    }
+
+    .pageSize {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      margin: 0 30px;
+      height: 60px;
+
+      span {
+        color: #000;
+      }
+
+      .el-pagination {
+        // width: 1600px;
+        :deep(.el-pagination__total) {
+          color: #000;
+        }
+
+        :deep(.el-pagination__goto) {
+          color: #000;
+        }
+
+        :deep(.el-pagination__classifier) {
+          color: #000;
+        }
+
+        :deep(.el-input__wrapper) {
+          border: 1px solid rgba(0, 0, 0, 1);
+          border-radius: 5px;
+          box-shadow: none;
+        }
+
+        :deep(.el-pager li) {
+          margin: 0 5px;
+          border: 1px solid rgba(0, 0, 0, 1);
+          border-radius: 5px;
+          background-color: transparent;
+        }
+
+        :deep(.el-pager li.is-active) {
+          // background-color: rgba(0, 97, 255, 0.8);
+          border: 1px solid rgba(0, 97, 255, 1);
+          color: rgba(0, 97, 255, 1);
+        }
+
+        :deep(.btn-prev) {
+          margin-right: 5px;
+          border: 1px solid rgba(0, 0, 0, 1);
+          border-radius: 5px;
+          background-color: transparent;
+        }
+
+        :deep(.btn-next) {
+          margin-left: 5px;
+          border: 1px solid rgba(0, 0, 0, 1);
+          border-radius: 5px;
+          background-color: transparent;
+        }
+      }
+    }
+  }
+}
+</style>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1412 - 0
src/views/dormitory/dormitory.vue


+ 294 - 0
src/views/login/login.vue

@@ -0,0 +1,294 @@
+<template>
+  <div class="box">
+    <!-- <div class="middle"></div> -->
+    <div class="left">
+      <div class="loginForm">
+        <div
+          style="
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            justify-content: center;
+            margin-top: 104px;
+            margin-bottom: 61px;
+          "
+        >
+          <img
+            src="@/assets/img/logo.png"
+            alt=""
+            style="width: 95px; height: 95px; margin-bottom: 15px"
+          />
+          <div
+            style="
+              font-size: 30px;
+              font-weight: 600;
+              letter-spacing: 0px;
+              color: rgba(0, 0, 0, 1);
+            "
+          >
+            迎新系统管理平台
+          </div>
+        </div>
+        <el-form
+          ref="ruleFormRef"
+          :model="ruleForm"
+          status-icon
+          :rules="rules"
+          label-width="120px"
+          class="demo-ruleForm"
+        >
+          <el-form-item label="" prop="user">
+            <el-input
+              :prefix-icon="User"
+              v-model="ruleForm.user"
+              placeholder="请输入登录名"
+              autocomplete="off"
+            />
+          </el-form-item>
+          <el-form-item label="" prop="pass">
+            <el-input
+              :prefix-icon="Lock"
+              show-password
+              v-model="ruleForm.pass"
+              type="password"
+              autocomplete="off"
+              placeholder="请输入登录密码"
+            />
+          </el-form-item>
+          <el-checkbox v-model="checked" class="remeberPwd el-form-item"
+            >记住密码</el-checkbox
+          >
+          <el-form-item>
+            <el-button
+              :loading="loading"
+              type="primary"
+              @click="submitForm(ruleFormRef)"
+              >登录</el-button
+            >
+          </el-form-item>
+        </el-form>
+      </div>
+      <div class="rLogin">
+        <img src="@/assets/img/login3.png" alt="" />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, onUnmounted } from "vue";
+import { User, Lock } from "@element-plus/icons-vue";
+import { useRouter } from "vue-router";
+import { ElMessage } from "element-plus";
+import { JSEncrypt } from "jsencrypt"; // 加密密码
+import { https } from "@/utils/request"; // 接口请求封装
+import { useCounterStore } from "@/stores/index";
+
+const store = useCounterStore();
+
+const router = useRouter();
+const loading = ref(false);
+const ruleFormRef = ref();
+const ruleForm = reactive({
+  user: "",
+  pass: "",
+});
+const checked = ref(false); // 记住密码
+
+const rules = reactive({
+  user: [{ required: true, message: "请填写用户名", trigger: "blur" }],
+  pass: [{ required: true, message: "请填写密码", trigger: "blur" }],
+});
+
+const submitForm = (formEl) => {
+  if (!formEl) return;
+  formEl.validate(async (valid) => {
+    if (valid) {
+      loading.value = true;
+      // let publicKey =
+      //   "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMOcPB06u5yKyQsPjfVWiWgbEIrd14kiXNNihciaVKb6HnkQvq7zpQuZ80WEX94spnUMI3iOAl/GmIvHrpGwcbB4hJbznm+PajiwnUSPuCCXA68YJF640cJKb/8KeM7WVz69OFkIEPHhVxOy4FFF5QWe/kt6zOZ19HmE+ak+5x/QIDAQAB";
+      // let encryptor = new JSEncrypt(); // 新建JSEncrypt对象
+      // encryptor.setPublicKey(publicKey); // 设置公钥
+      // let rsaPassWord = encryptor.encrypt(ruleForm.pass); // 对密码进行加密
+      // console.log(rsaPassWord);
+      let data = {
+        account: ruleForm.user,
+        password: ruleForm.pass,
+      };
+      // let data = new FormData();
+      // data.set("number", ruleForm.user);
+      // data.set("password", ruleForm.pass); //前面的key记得对应!
+      const res = await https.post("/welcome/api/home/login", "data", data); // 使用封装的 get 方法
+      console.log(res, "登录获取数据");
+      if (res.code == 200) {
+        sessionStorage.setItem("accountId",res.data.accountId)
+        if (checked.value) {
+          localStorage.setItem("user", ruleForm.user);
+          localStorage.setItem("pass", ruleForm.pass);
+        } else {
+          localStorage.removeItem("user", ruleForm.user);
+          localStorage.removeItem("pass", ruleForm.pass);
+        }
+        localStorage.setItem("token", res.data.token);
+        localStorage.setItem("userName", res.data.userName);
+        store.ROLEAdd()
+        
+        ElMessage({
+          type: "success",
+          showClose: true,
+          message: "登录成功",
+          center: true,
+        });
+      } else {
+        ElMessage({
+          type: "error",
+          showClose: true,
+          message: "登录失败",
+          center: true,
+        });
+        loading.value = false;
+      }
+    } else {
+      return false;
+    }
+  });
+};
+const Enters = (e) => {
+  // console.log("按键:", e.key);
+  if (e.key == "Enter") {
+    submitForm(ruleFormRef.value);
+  }
+};
+onMounted(() => {
+  if (localStorage.getItem("pass") && localStorage.getItem("user")) {
+    ruleForm.user = localStorage.getItem("user");
+    ruleForm.pass = localStorage.getItem("pass");
+    checked.value = true;
+  }
+  if (localStorage.getItem("user")) {
+    ruleForm.user = localStorage.getItem("user");
+  }
+  document.addEventListener("keyup", Enters);
+});
+onUnmounted(() => {
+  document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style lang="scss" scoped>
+.box {
+  width: 100%;
+  height: 100vh;
+  background: url(@/assets/img/bg.png) 100% 100%;
+  background-size: cover;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  // .middle {
+  //   width: 1180px;
+  //   height: 740px;
+  //   position: absolute;
+
+  // }
+  .left {
+    box-shadow: 39px 30px 58px 0px rgba(8, 53, 115, 0.55);
+
+    width: 1180px;
+    height: 740px;
+    border-radius: 18px;
+    display: flex;
+    align-items: center;
+    background-size: 110% 116%;
+    background-position: -19px -28px;
+    .loginForm {
+      width: 497px;
+      height: 740px;
+      background-color: #fff;
+      border-radius: 18px 0 0 18px;
+
+      .el-form {
+        width: 500px;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+        .el-form-item {
+          width: 420px;
+          height: 60px;
+          // margin-bottom: 40px;
+
+          :deep(.el-form-item__content) {
+            margin-left: 0 !important;
+
+            .el-input {
+              height: 60px;
+
+              .el-input__wrapper {
+                border-radius: 12px;
+                .el-input__prefix {
+                  margin: 0 10px 0 12px;
+                }
+              }
+
+              .el-icon {
+                width: 24px;
+                height: 24px;
+                svg {
+                  width: 24px;
+                  height: 24px;
+                }
+              }
+              .el-input__inner {
+                height: 100%;
+                font-size: 24px;
+              }
+            }
+            .el-form-item__error {
+              font-size: 16px;
+            }
+            .el-button {
+              width: 100%;
+              height: 60px;
+              font-size: 24px;
+              border-radius: 12px;
+            }
+          }
+
+          .el-button--primary {
+            width: 380px;
+          }
+        }
+        .remeberPwd {
+          height: 30px;
+          margin-bottom: 30px;
+          :deep(.el-checkbox__input) {
+            .el-checkbox__inner {
+              width: 18px;
+              height: 18px;
+            }
+            .el-checkbox__inner::after {
+              width: 6px;
+              height: 12px;
+              top: -1px;
+            }
+          }
+          :deep(.el-checkbox__label) {
+            font-size: 18px;
+          }
+        }
+      }
+    }
+    .rLogin {
+      width: 683px;
+      height: 740px;
+      border-radius: 0 18px 18px 0;
+      display: flex;
+      background: rgba(41, 142, 243, 1);
+      img {
+        width: 683px;
+        align-self: flex-end;
+      }
+    }
+  }
+}
+</style>

+ 544 - 0
src/views/quarterage/quarterage.vue

@@ -0,0 +1,544 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <span class="cameratxt">住宿信息统计</span> -->
+      <el-tabs v-model="activeName" class="demo-tabs" @tab-change="handleClick">
+        <el-tab-pane label="床位分配统计" name="first"></el-tab-pane>
+        <el-tab-pane label="床位入住统计" name="second"></el-tab-pane>
+      </el-tabs>
+    </div>
+
+    <div class="scroll">
+      <div class="middle">
+        <div class="filter">
+          <div class="condition">
+            <span>所属学院 :</span>
+            <el-select
+              @change="collegeChange"
+              v-model="searchInput.collegeId"
+              placeholder="请选择所属学院"
+              clearable
+            >
+              <el-option
+                v-for="i in collegeData"
+                :key="i.id"
+                :label="i.name"
+                :value="i.id"
+              />
+            </el-select>
+          </div>
+          <div class="condition">
+            <span>性别 :</span>
+            <el-select
+              clearable
+              v-model="searchInput.sex"
+              placeholder="请选择性别"
+            >
+              <el-option label="男" value="男" />
+              <el-option label="女" value="女" />
+            </el-select>
+          </div>
+          <el-button
+            style="margin-left: 20px"
+            color="rgba(38, 151, 255, 1)"
+            type="primary"
+            class="search"
+            @click="searchBtn"
+            ><span>查询</span></el-button
+          >
+          <el-button @click="resetBtn" plain color="rgba(43, 153, 255, 1)"
+            >重置</el-button
+          >
+        </div>
+        <!-- 按钮列表 -->
+        <div class="gongneng">
+          <el-button
+          v-if="store.BtnRole('accommodationStatisticsSetting1')"
+            type="primary"
+            color="rgba(48, 201, 191, 1)"
+            @click="buildExportbtn"
+            >导出</el-button
+          >
+        </div>
+      </div>
+      <div class="footer" v-loading="loading" v-if="activeName == 'first'">
+        <el-table
+          :row-class-name="tableRowClassName"
+          :data="tableData.list"
+          @selection-change="handleSelectionChange"
+          style="width: 100%"
+          :header-cell-style="{
+            background: 'rgba(240, 243, 247, 1)',
+            height: '50px',
+            border: 0,
+          }"
+        >
+          <el-table-column
+            width="80"
+            align="center"
+            label="序号"
+            type="index"
+            index="1"
+          />
+          <el-table-column align="center" prop="college" label="学院名称" />
+          <el-table-column align="center" prop="sex" label="性别">
+            <template #default="{ row }">
+              <span>{{ row.sex }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column align="center" prop="total" label="总人数" />
+          <el-table-column
+            align="center"
+            prop="assignedBed"
+            label="已分配床位数"
+          />
+          <el-table-column
+            align="center"
+            tooltip-formatter
+            prop="assignedDormitory"
+            label="已分配寝室数"
+          />
+          <el-table-column
+            align="center"
+            prop="assignedBuild"
+            label="已分配楼栋数"
+          />
+        </el-table>
+      </div>
+      <div class="footer" v-loading="loading" v-if="activeName == 'second'">
+        <el-table
+          :row-class-name="tableRowClassName"
+          :data="tableData.list"
+          @selection-change="handleSelectionChange"
+          style="width: 100%"
+          :header-cell-style="{
+            background: 'rgba(240, 243, 247, 1)',
+            height: '50px',
+            border: 0,
+          }"
+        >
+          <el-table-column
+            width="80"
+            align="center"
+            label="序号"
+            type="index"
+            index="1"
+          />
+          <el-table-column align="center" prop="college" label="学院名称" />
+          <el-table-column align="center" prop="sex" label="性别">
+            <template #default="{ row }">
+              <span>{{ row.sex }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column align="center" prop="totalData" label="总人数" />
+          <el-table-column align="center" prop="sleeper" label="已入住人数" />
+          <el-table-column
+            align="center"
+            tooltip-formatter
+            prop="unSleeper"
+            label="未入住人数"
+          />
+          <el-table-column align="center" prop="totalBed" label="总床位数" />
+          <el-table-column
+            align="center"
+            prop="sleepBed"
+            label="已入住床位数"
+          />
+          <el-table-column
+            align="center"
+            prop="unSleepBed"
+            label="未入住床位数"
+          />
+        </el-table>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import {
+  ref,
+  watch,
+  reactive,
+  nextTick,
+  onBeforeMount,
+  onUnmounted,
+} from "vue";
+import { useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { dayjs } from "element-plus";
+import lodash from "lodash";
+import { https } from "@/utils/request"; // 绝对路径
+
+import { storeToRefs } from "pinia";
+import { useCounterStore } from "@/stores/index";
+
+const router = useRouter();
+const store = useCounterStore();
+
+// 为避免解构时失去响应性
+const { name, age, isCollapse, realAge } = storeToRefs(store);
+
+const activeName = ref("first");
+
+// 表格数据
+const loading = ref(false);
+const tableData = reactive({
+  list: [{}],
+});
+
+const searchInput = reactive({
+  sex: "",
+  collegeId: "",
+}); // 搜索按钮数据
+
+// 学院
+const collegeData = ref([]);
+
+const collegeList = async () => {
+  let res = await https.get("/welcome/api/welcomeOrg/getColleges", "params");
+  console.log(res, "学院数据");
+  if (res.code == 200) {
+    collegeData.value = res.data;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+// 获取账户列表
+const getList = async () => {
+  loading.value = true;
+  let params = {
+    collegeId: searchInput.collegeId,
+    sex: searchInput.sex,
+  };
+  let res = null;
+  if (activeName.value == "first") {
+    res = await https.get(
+      "/welcome/api/census/queryBedTotal",
+      "params",
+      params
+    );
+  } else if (activeName.value == "second") {
+    res = await https.get(
+      "/welcome/api/census/queryCheckTotal",
+      "params",
+      params
+    );
+  }
+
+  console.log(res, "住宿信息统计");
+  if (res.code == 200) {
+    tableData.list = res.data;
+    loading.value = false;
+  } else {
+    loading.value = false;
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+const handleClick = (val) => {
+  activeName.value = val;
+  searchInput.sex = null;
+  searchInput.collegeId = null;
+  getList();
+};
+// 搜索功能
+const searchBtn = lodash.debounce(async () => {
+  getList();
+}, 300);
+const resetBtn = lodash.debounce(async () => {
+  searchInput.sex = null;
+  searchInput.collegeId = null;
+  getList();
+}, 300);
+
+// 多选框功能
+const handleSelectionChange = (val) => {
+  console.log(val);
+  selectIds.value = val.map((i) => i.id);
+};
+
+// 表格斑马纹颜色修改
+const tableRowClassName = ({ row, rowIndex }) => {
+  if (rowIndex % 2 === 0) {
+    return "even";
+  } else if (rowIndex % 2 !== 0) {
+    return "odd";
+  }
+  return "";
+};
+
+// 楼栋导出
+const buildExportbtn = async () => {
+  let params = {
+    sex: searchInput.sex,
+    collegeId: searchInput.collegeId,
+  };
+  let res = null;
+  let name = null;
+  if (activeName.value == "first") {
+    res = await https.getBlob(
+      "/welcome/api/census/bedTotalExport",
+      "params",
+      params
+    );
+    name = `床位分配统计`;
+  } else if (activeName.value == "second") {
+    res = await https.getBlob(
+      "/welcome/api/census/checkTotalExport",
+      "params",
+      params
+    );
+    name = `床位入住统计`;
+  }
+  console.log(res, "床位信息管理导出");
+
+  var content = res;
+  var datas = new Blob([content]);
+  var downloadUrl = window.URL.createObjectURL(datas);
+  var anchor = document.createElement("a");
+  anchor.href = downloadUrl;
+  anchor.download = name + ".xlsx";
+  anchor.click();
+  window.URL.revokeObjectURL(datas);
+  ElMessage({
+    type: "success",
+    showClose: true,
+    message: "导出成功",
+    center: true,
+  });
+};
+
+onBeforeMount(() => {
+  getList();
+  collegeList();
+});
+onUnmounted(() => {
+  // document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  width: calc(100% - 40px);
+  height: calc(100% - 105px);
+  margin: 20px auto;
+  background-color: #fff;
+  color: #000;
+  display: flex;
+  flex-direction: column;
+
+  .svg {
+    width: 22px;
+    height: 22px;
+  }
+
+  .left {
+    width: calc(100% - 60px);
+    height: 60px;
+    margin: 0 auto;
+    display: flex;
+    align-items: center;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+    :deep(.el-tabs) {
+      .el-tabs__header {
+        margin: 0;
+      }
+      .el-tabs__nav-wrap {
+        &::after {
+          display: none;
+        }
+        .el-tabs__item {
+          color: #000;
+          font-size: 18px;
+          font-weight: 600;
+        }
+      }
+    }
+  }
+  .scroll {
+    width: calc(100% - 60px);
+    height: calc(100% - 61px);
+    margin: 0 auto;
+    display: flex;
+    flex-direction: column;
+
+    .middle {
+      width: calc(100%);
+      color: #000;
+      .filter {
+        display: flex;
+        flex-wrap: wrap;
+        align-items: center;
+        .search {
+          margin-left: 0 !important;
+          color: #fff;
+        }
+        .condition {
+          display: flex;
+          align-items: center;
+          margin: 10px 30px 10px 0;
+          :deep(.el-input .el-input__inner) {
+            font-size: 14px;
+          }
+          .el-select {
+            width: 220px;
+          }
+          span {
+            margin: 0 10px 0 0;
+          }
+        }
+      }
+      .gongneng {
+        margin: 10px 0;
+        .el-button {
+          color: #fff;
+          margin-right: 15px;
+        }
+      }
+    }
+    .footer {
+      width: calc(100%);
+      flex: 1;
+      margin: 0 auto;
+      overflow: auto;
+
+      .el-table--fit {
+        height: calc(100% - 60px);
+        :deep(.el-table__header-wrapper) {
+          background-color: #000;
+          font-size: 15px;
+          color: #000;
+          .cell {
+            color: #000;
+          }
+        }
+        :deep(.el-table__row) {
+          height: 50px;
+          font-size: 15px;
+          color: #000;
+        }
+        :deep(.el-table__row td) {
+          padding: 0;
+          border: 0;
+        }
+
+        .el-button--primary {
+          margin-left: 5px;
+        }
+        :deep(.el-table__body .even) {
+          background-color: #fff;
+        }
+        :deep(.el-table__body .odd) {
+          background-color: rgba(240, 243, 247, 1);
+        }
+        :deep(.options) {
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          .edit {
+            margin: 0 15px 0 0;
+            color: rgba(0, 186, 173, 1);
+            cursor: pointer;
+          }
+          .delete {
+            color: rgba(212, 48, 48, 1);
+            cursor: pointer;
+          }
+        }
+      }
+
+      .pageSize {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        margin: 0 30px;
+        height: 60px;
+
+        span {
+          color: #000;
+        }
+
+        .el-pagination {
+          // width: 1600px;
+          :deep(.el-pagination__total) {
+            color: #000;
+          }
+
+          :deep(.el-pagination__goto) {
+            color: #000;
+          }
+
+          :deep(.el-pagination__classifier) {
+            color: #000;
+          }
+
+          :deep(.el-input__wrapper) {
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            box-shadow: none;
+          }
+
+          :deep(.el-pager li) {
+            margin: 0 5px;
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            background-color: transparent;
+          }
+
+          :deep(.el-pager li.is-active) {
+            // background-color: rgba(0, 97, 255, 0.8);
+            border: 1px solid rgba(0, 97, 255, 1);
+            color: rgba(0, 97, 255, 1);
+          }
+
+          :deep(.btn-prev) {
+            margin-right: 5px;
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            background-color: transparent;
+          }
+
+          :deep(.btn-next) {
+            margin-left: 5px;
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            background-color: transparent;
+          }
+        }
+      }
+    }
+  }
+
+  // 添加员工弹窗样式
+  :deep(.addStaff) {
+    .el-dialog__body {
+      padding: 20px 20px 10px 20px;
+      .el-input {
+        width: 400px;
+        .el-input__suffix-inner {
+          color: rgba(61, 81, 232, 1);
+        }
+      }
+      .el-select {
+        width: 400px;
+      }
+      .el-tree {
+        width: 400px;
+      }
+    }
+  }
+}
+</style>

+ 236 - 0
src/views/richtext/richtext.vue

@@ -0,0 +1,236 @@
+<template>
+  <div class="richText">
+    <div style="border: 1px solid #ccc">
+      <Toolbar
+        style="border-bottom: 1px solid #ccc"
+        :editor="editorRef"
+        :defaultConfig="toolbarConfig"
+        :mode="mode"
+      />
+      <Editor
+        style="height: 200px; overflow-y: hidden"
+        v-model="valueHtml"
+        :defaultConfig="editorConfig"
+        :mode="mode"
+        @onCreated="handleCreated"
+        @onChange="handleChange"
+        @onDestroyed="handleDestroyed"
+        @onFocus="handleFocus"
+        @onBlur="handleBlur"
+        @customAlert="customAlert"
+        @customPaste="customPaste"
+      />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { Calendar } from "@element-plus/icons-vue";
+import { dayjs } from "element-plus";
+import lodash, { reduce } from "lodash";
+
+import "@wangeditor/editor/dist/css/style.css"; // 引入 css
+
+import { onBeforeUnmount, nextTick, ref, shallowRef, onMounted } from "vue";
+import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
+import { DomEditor, createToolbar } from "@wangeditor/editor";
+
+// 编辑器实例,必须用 shallowRef
+const editorRef = shallowRef();
+
+const props = defineProps({
+  fatherMessage: {},
+  fatherMessages: {},
+});
+const emit = defineEmits(["richtextClick"]);
+
+// 内容 HTML
+const valueHtml = ref();
+const toolbarConfig = {
+  // JS 语法
+  /* 工具栏配置 */
+  excludeKeys: [
+    "insertLink",
+    "insertVideo",
+    "insertTable",
+    "fullScreen",
+    "insertImage",
+    "group-image",
+    "todo",
+  ],
+};
+const uploadImg = (file, insertFn) => {
+  let imgData = new FormData();
+  console.log(file);
+  // const cos = new COS({
+  //   SecretId: "AKID3HGLVgpx128DNdxOvyDb1mWLHELWt7Sw",
+  //   SecretKey: "n7uhSfzAdCmWQRWUMCYfvP3sv3DMg6aD",
+  //   SecurityToken: "1325577833",
+  //   Bucket: "jgy-1325577833",
+  //   Region: "ap-guangzhou",
+  // });
+
+  // var compressImgFile = file;
+  // const compressKey = file.name; // 设置上传到 COS 后的文件名
+
+  // cos.putObject(
+  //   {
+  //     Bucket: "jgy-1325577833",
+  //     Region: "ap-guangzhou",
+  //     Key: compressKey,
+  //     Body: compressImgFile,
+  //     onProgress: function (progressData) {
+  //       console.log(JSON.stringify(progressData));
+  //     },
+  //   },
+  //   function (err, data) {
+  //     if (err) {
+  //       console.error(err);
+  //     } else {
+  //       console.log(data, "压缩图片获取成功");
+  //       let href = "https://" + data.Location;
+  //       insertFn(href);
+  //     }
+  //   }
+  // );
+};
+const editorConfig = {
+  placeholder: "请输入内容......",
+  MENU_CONF: {
+    uploadImage: {
+      // 自定义上传图片 方法
+      customUpload: uploadImg,
+      // 自定义插入图片 方法
+      // customInsert: insertImg,
+      //上传图片配置
+      // server: "https://	jgy-1325577833.cos.ap-guangzhou.myqcloud.com", //上传接口地址
+      fieldName: "file", //上传文件名
+      methods: "post",
+      metaWithUrl: false, // 参数拼接到 url 上
+      // 单个文件上传成功之后
+      // onSuccess(file, res) {
+      //   console.log(file, res);
+      // },
+      // 自定义插入图片
+      customInsert(res, insertFn) {
+        console.log(res);
+        // insertFn(res.url)
+      },
+    },
+    // insertImage: {
+    //   onInsertedImage(imageNode) {
+    //     if (imageNode == null) return;
+    //     const { src, alt, url, href } = imageNode;
+    //     console.log("inserted image", src, alt, url, href);
+    //   },
+    //   checkImage: customCheckImageFn, // 也支持 async 函数
+    //   parseImageSrc: customParseImageSrc, // 也支持 async 函数
+    // },
+    // editImage: {
+    //   onUpdatedImage(imageNode) {
+    //     if (imageNode == null) return;
+    //     const { src, alt, url } = imageNode;
+    //     console.log("updated image", src, alt, url);
+    //   },
+    //   checkImage: customCheckImageFn, // 也支持 async 函数
+    //   parseImageSrc: customParseImageSrc, // 也支持 async 函数
+    // },
+  },
+};
+// const mode = ref("default"); // 默认模式
+const mode = ref("simple"); // 简易模式
+
+const handleChange = (editor) => {
+  // console.log("change:", editor);
+  // console.log("change:", editor.getText());
+  // console.log("change:", editor.getHtml());
+  emit("richtextClick", {
+    html: editor.getHtml(),
+    text: editor.getText(),
+  });
+};
+const handleDestroyed = (editor) => {
+  console.log("destroyed", editor);
+};
+const handleFocus = (editor) => {
+  console.log("focus", editor);
+  const toolbar = DomEditor.getToolbar(editor);
+
+  const curToolbarConfig = toolbar.getConfig();
+  console.log(curToolbarConfig.toolbarKeys); // 当前菜单排序和分组
+};
+const handleBlur = (editor) => {
+  console.log("blur", editor);
+};
+const customAlert = (info, type) => {
+  alert(`【自定义提示】${type} - ${info}`);
+};
+// 粘贴事件对象
+const customPaste = (editor, event, callback) => {
+  console.log("ClipboardEvent 粘贴事件对象", event);
+  const html = event.clipboardData.getData("text/html"); // 获取粘贴的 html
+  const text = event.clipboardData.getData("text/plain"); // 获取粘贴的纯文本
+  const rtf = event.clipboardData.getData("text/rtf"); // 获取 rtf 数据(如从 word wsp 复制粘贴)
+  console.log(html);
+
+  // 自定义插入内容
+  // editor.insertText("自定义插入内容");
+
+  // 返回 false ,阻止默认粘贴行为
+  // event.preventDefault();
+  // callback(false); // 返回值(注意,vue 事件的返回值,不能用 return)
+
+  // 返回 true ,继续默认的粘贴行为
+  callback(true);
+};
+
+// // 自定义图片上传
+// editorConfig.MENU_CONF["uploadImage"] = {
+//   async customUpload(file, insertFn) {
+//     let formData = new FormData();
+//     formData.append("files", file);
+//     try {
+//       // 这里结合实际场景写自己上传图片的逻辑,此处代码仅为示例
+//       const { data } = await upload(formData);
+//       // 对图片进行处理,同样需要结合实际场景
+//       data.forEach((item) => {
+//         insertFn(item, "image", item);
+//       });
+//     } catch (error) {
+//       console.log(error);
+//     }
+//   },
+// };
+
+const handleCreated = (editor) => {
+  editorRef.value = editor; // 记录 editor 实例,重要!
+  console.log(props.fatherMessage);
+  editor.setHtml(props.fatherMessage.data);
+  if (props.fatherMessage.flag) {
+    editor.enable();
+  } else {
+    editor.disable();
+  }
+};
+
+// 模拟 ajax 异步获取内容
+onMounted(() => {
+  // console.log(props.fatherMessage.data);
+});
+
+// 组件销毁时,也及时销毁编辑器
+onBeforeUnmount(() => {
+  console.log("销毁组件");
+  const editor = editorRef.value;
+  if (editor == null) return;
+  editor.destroy();
+});
+</script>
+
+<style scoped lang="scss">
+.richText {
+  width: 100%;
+}
+</style>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1306 - 0
src/views/role/role.vue


+ 824 - 0
src/views/student/student.vue

@@ -0,0 +1,824 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <el-icon :size="23" class="camera"><VideoCameraFilled /></el-icon> -->
+      <span class="cameratxt">学生住宿信息</span>
+    </div>
+    <div class="scroll">
+      <div class="middle">
+        <div class="filter">
+          <div class="condition">
+            <span>校区名称 :</span>
+            <el-select
+              clearable
+              @change="schoolChange"
+              v-model="searchInput.schoolId"
+              placeholder="请选择校区名称"
+            >
+              <el-option
+                v-for="i in schoolData"
+                :key="i.id"
+                :label="i.school"
+                :value="i.id"
+              />
+            </el-select>
+          </div>
+          <div class="condition">
+            <span>楼栋名称 :</span>
+            <el-select
+              clearable
+              v-model="searchInput.buildId"
+              placeholder="请选择楼栋名称"
+              @change="buildChange"
+            >
+              <el-option
+                v-for="i in buildData"
+                :key="i.id"
+                :label="i.build"
+                :value="i.id"
+              />
+            </el-select>
+          </div>
+          <div class="condition">
+            <span>寝室号 :</span>
+            <el-select
+              v-model="searchInput.dormitoryId"
+              placeholder="请选择寝室号"
+              clearable
+            >
+              <el-option
+                v-for="i in dormitoryData"
+                :key="i.id"
+                :label="i.dormitory"
+                :value="i.id"
+              />
+            </el-select>
+          </div>
+          <div class="condition">
+            <span>所属学院 :</span>
+            <el-select
+              @change="collegeChange"
+              v-model="searchInput.collegeId"
+              placeholder="请选择所属学院"
+              clearable
+            >
+              <el-option
+                v-for="i in collegeData"
+                :key="i.id"
+                :label="i.name"
+                :value="i.id"
+              />
+            </el-select>
+          </div>
+          <div class="condition">
+            <span>专业 :</span>
+            <el-select
+              @change="majorChange"
+              clearable
+              v-model="searchInput.majorId"
+              placeholder="请选择专业"
+            >
+              <el-option
+                v-for="i in majorData"
+                :key="i.id"
+                :label="i.name"
+                :value="i.id"
+              />
+            </el-select>
+          </div>
+          <div class="condition">
+            <span>所属班级 :</span>
+            <el-select
+              clearable
+              v-model="searchInput.classstrId"
+              placeholder="请选择所属班级"
+            >
+              <el-option
+                v-for="i in classstrData"
+                :key="i.id"
+                :label="i.name"
+                :value="i.id"
+              />
+            </el-select>
+          </div>
+          <div class="condition">
+            <span>学生姓名 :</span>
+            <el-input
+              clearable
+              v-model.trim="searchInput.name"
+              class="w-50 m-2"
+              placeholder="请输入学生姓名"
+              style="width: 180px"
+            />
+          </div>
+          <el-button
+            style="margin-left: 20px"
+            color="rgba(38, 151, 255, 1)"
+            type="primary"
+            class="search"
+            @click="searchBtn"
+            ><span>查询</span></el-button
+          >
+          <el-button @click="resetBtn" plain color="rgba(43, 153, 255, 1)"
+            >重置</el-button
+          >
+        </div>
+        <!-- 按钮列表 -->
+        <div class="gongneng">
+          <el-button
+          v-if="store.BtnRole('studentAccommodationSetting1')"
+            type="primary"
+            style="margin-left: 0"
+            color="rgba(48, 201, 191, 1)"
+            @click="buildExportbtn"
+            >导出</el-button
+          >
+        </div>
+      </div>
+      <div class="footer" v-loading="loading">
+        <el-table
+          :row-class-name="tableRowClassName"
+          :data="tableData.list"
+          @selection-change="handleSelectionChange"
+          style="width: 100%"
+          :header-cell-style="{
+            background: 'rgba(240, 243, 247, 1)',
+            height: '50px',
+            border: 0,
+          }"
+        >
+          <el-table-column
+            width="80"
+            align="center"
+            label="序号"
+            type="index"
+            index="1"
+          />
+          <el-table-column
+            align="center"
+            prop="cardNum"
+            width="120"
+            label="录取号"
+          />
+          <el-table-column
+            align="center"
+            prop="name"
+            width="120"
+            label="学生姓名"
+          />
+          <el-table-column
+            align="center"
+            prop="school"
+            width="120"
+            label="校区名称"
+          />
+          <el-table-column
+            align="center"
+            prop="build"
+            width="140"
+            label="楼栋名称"
+          />
+          <el-table-column
+            align="center"
+            prop="dormitory"
+            width="120"
+            label="寝室号"
+          />
+          <el-table-column align="center" prop="number" label="床位号" />
+          <el-table-column align="center" prop="sex" width="100" label="性别" />
+          <el-table-column
+            align="center"
+            prop="college"
+            width="130"
+            label="所属学院"
+          />
+          <el-table-column align="center" prop="major" width="180" label="专业">
+          </el-table-column>
+          <el-table-column
+            align="center"
+            prop="classstr"
+            width="180"
+            label="所属班级"
+          />
+
+          <el-table-column
+            align="center"
+            prop="instructor"
+            width="120"
+            label="辅导员"
+          />
+        </el-table>
+        <!-- 分页组件 -->
+        <div class="pageSize">
+          <span></span>
+          <el-pagination
+            background
+            :current-page="currentPage"
+            :page-size="pageSize"
+            :page-sizes="[10, 20, 30, 40]"
+            layout="total,sizes, prev, pager, next, jumper, slot"
+            :total="total"
+            @size-change="handleSizeChange"
+            @update:current-page="handleCurrentChange"
+          />
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import {
+  ref,
+  watch,
+  reactive,
+  nextTick,
+  onBeforeMount,
+  onUnmounted,
+} from "vue";
+import { useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { dayjs } from "element-plus";
+import lodash from "lodash";
+import { https } from "@/utils/request"; // 绝对路径
+
+import { storeToRefs } from "pinia";
+import { useCounterStore } from "@/stores/index";
+
+const router = useRouter();
+const store = useCounterStore();
+
+// 为避免解构时失去响应性
+const { name, age, isCollapse, realAge } = storeToRefs(store);
+
+// 表格数据
+const loading = ref(false);
+const tableData = reactive({
+  list: [],
+});
+const dialongTitle = ref("新增账号"); // 弹窗标题
+
+const searchInput = reactive({
+  schoolId: null,
+  buildId: null,
+  dormitoryId: null,
+  name: null,
+  majorId: null,
+  collegeId: null,
+  classstrId: null,
+}); // 搜索按钮数据
+
+const currentPage = ref(1); // 当前页
+const pageSize = ref(10);
+const total = ref(); // 当前总数
+const selectIds = ref([]);
+
+const addDialogVisible = ref(false); // 控制添加账号弹窗
+
+// 表单数据
+const formSize = ref("default");
+const ruleFormRef = ref();
+const ruleForm = reactive({
+  school: "男", //校区名称
+  build: "1", //楼栋名称
+  dormitory: "", //宿舍名称
+  sex: "", //床位性别
+  college: "",
+  major: "",
+  classstr: "",
+  number: "", //床位号
+  isCheck: "",
+  cardNum: "",
+  name: "",
+  remark: "",
+  id: "",
+});
+// 表单验证
+const rules = reactive({
+  school: [{ required: true, message: "校区名称不能为空", trigger: "blur" }],
+  build: [{ required: true, message: "楼栋名称不能为空", trigger: "blur" }],
+  dormitory: [{ required: true, message: "寝室号不能为空", trigger: "blur" }],
+  sex: [{ required: true, message: "床位性别不能为空", trigger: "blur" }],
+  // college: [{ required: true, message: "所属学院不能为空", trigger: "blur" }],
+  // major: [{ required: true, message: "专业不能为空", trigger: "blur" }],
+  // classstr: [{ required: true, message: "所属班级不能为空", trigger: "blur" }],
+  number: [{ required: true, message: "床位号不能为空", trigger: "blur" }],
+});
+
+// 校区数据
+const schoolData = ref([]);
+// 楼栋数据
+const buildData = ref([]);
+// 寝室号数据
+const dormitoryData = ref([]);
+// 学院
+const collegeData = ref([]);
+// 专业
+const majorData = ref([]);
+// 班级
+const classstrData = ref([]);
+
+const schoolList = async () => {
+  let res = await https.get("/welcome/api/welcome-build/schoolGroup", "params");
+  console.log(res, "校区数据");
+  if (res.code == 200) {
+    schoolData.value = res.data;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+const buildList = async (flag) => {
+  let params = {
+    schoolId: searchInput.schoolId,
+  };
+  let res = await https.get(
+    "/welcome/api/welcome-build/buildGroup",
+    "params",
+    params
+  );
+  console.log(res, "楼栋数据");
+  if (res.code == 200) {
+    buildData.value = res.data;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+const dormitoryList = async (flag) => {
+  let params = {
+    schoolId: searchInput.schoolId,
+    buildId: searchInput.buildId,
+  };
+  let res = await https.get(
+    "/welcome/api/welcome-dormitory/dormitoryGroup",
+    "params",
+    params
+  );
+  console.log(res, "寝室号数据");
+  if (res.code == 200) {
+    dormitoryData.value = res.data;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+const collegeList = async () => {
+  let res = await https.get("/welcome/api/welcomeOrg/getColleges", "params");
+  console.log(res, "学院数据");
+  if (res.code == 200) {
+    collegeData.value = res.data;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+const majorList = async (flag) => {
+  let params = {
+    collegeId: searchInput.collegeId,
+  };
+  let res = await https.get(
+    "/welcome/api/welcomeOrg/getMajors",
+    "params",
+    params
+  );
+  console.log(res, "专业数据");
+  if (res.code == 200) {
+    majorData.value = res.data;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+const classstrList = async (flag) => {
+  let params = {
+    majorId: searchInput.majorId,
+  };
+  let res = await https.get(
+    "/welcome/api/welcomeOrg/getClasss",
+    "params",
+    params
+  );
+  console.log(res, "班级数据");
+  if (res.code == 200) {
+    classstrData.value = res.data;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+
+const schoolChange = async (val) => {
+  console.log(val);
+  searchInput.buildId = null;
+  searchInput.dormitoryId = null;
+  if (!val) {
+    buildData.value = null;
+    dormitoryData.value = null;
+    return;
+  } else {
+    buildData.value = null;
+    dormitoryData.value = null;
+  }
+  buildList();
+};
+
+const buildChange = async (val) => {
+  searchInput.dormitoryId = null;
+  if (!val) {
+    dormitoryData.value = null;
+    return;
+  } else {
+    dormitoryData.value = null;
+  }
+  dormitoryList();
+};
+
+const collegeChange = async (val) => {
+  console.log(val);
+  searchInput.majorId = null;
+  searchInput.classstrId = null;
+  if (!val) {
+    majorData.value = null;
+    classstrData.value = null;
+    return;
+  } else {
+    majorData.value = null;
+    classstrData.value = null;
+  }
+  majorList();
+};
+
+const majorChange = async (val) => {
+  console.log(val);
+  searchInput.classstrId = null;
+  if (!val) {
+    classstrData.value = null;
+    return;
+  } else {
+    classstrData.value = null;
+  }
+  classstrList();
+};
+
+// 获取账户列表
+const getList = async () => {
+  loading.value = true;
+  let params = {
+    currentPage: currentPage.value, // 当前页
+    pageCount: pageSize.value, // 一页数据条数
+    schoolId: searchInput.schoolId,
+    buildId: searchInput.buildId,
+    dormitoryId: searchInput.dormitoryId,
+    collegeId: searchInput.collegeId,
+    majorId: searchInput.majorId,
+    classstrId: searchInput.classstrId,
+    name: searchInput.name,
+  };
+
+  let res = await https.get(
+    "/welcome/api/welcomeBed/studentAccommodationPage",
+    "params",
+    params
+  );
+  console.log(res, "床位信息");
+  if (res.code == 200) {
+    tableData.list = res.data.list;
+    total.value = res.data.totalCount;
+    loading.value = false;
+  } else {
+    loading.value = false;
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+
+// 搜索功能
+const searchBtn = lodash.debounce(async () => {
+  getList();
+}, 300);
+
+const resetBtn = lodash.debounce(async () => {
+  searchInput.schoolId = null;
+  searchInput.buildId = null;
+  buildData.value = [];
+  searchInput.dormitoryId = null;
+  dormitoryData.value = [];
+  searchInput.name = null;
+  searchInput.collegeId = null;
+  searchInput.majorId = null;
+  majorData.value = [];
+  searchInput.classstrId = null;
+  classstrData.value = [];
+  getList();
+}, 300);
+
+// 多选框功能
+const handleSelectionChange = (val) => {
+  console.log(val);
+  selectIds.value = val.map((i) => i.id);
+};
+
+// 表格斑马纹颜色修改
+const tableRowClassName = ({ row, rowIndex }) => {
+  if (rowIndex % 2 === 0) {
+    return "even";
+  } else if (rowIndex % 2 !== 0) {
+    return "odd";
+  }
+  return "";
+};
+// 每页显示条数
+const handleSizeChange = (value) => {
+  console.log(value, "每页显示条数");
+  pageSize.value = value;
+  getList();
+};
+// 分页
+const handleCurrentChange = (value) => {
+  // console.log(value);
+  currentPage.value = value;
+  getList();
+};
+
+// 楼栋导出
+const buildExportbtn = async () => {
+  let params = {
+    schoolId: searchInput.schoolId,
+    buildId: searchInput.buildId,
+    dormitoryId: searchInput.dormitoryId,
+    name: searchInput.name,
+    collegeId: searchInput.collegeId,
+    majorId: searchInput.majorId,
+    classstrId: searchInput.classstrId,
+  };
+  let res = await https.getBlob(
+    "/welcome/api/welcomeBed/studentAccommodationListExport",
+    "params",
+    params
+  );
+  console.log(res, "学生住宿信息");
+  let name = `学生住宿信息`;
+  var content = res;
+  var datas = new Blob([content]);
+  var downloadUrl = window.URL.createObjectURL(datas);
+  var anchor = document.createElement("a");
+  anchor.href = downloadUrl;
+  anchor.download = name + ".xlsx";
+  anchor.click();
+  window.URL.revokeObjectURL(datas);
+  ElMessage({
+    type: "success",
+    showClose: true,
+    message: "导出成功",
+    center: true,
+  });
+};
+
+onBeforeMount(() => {
+  getList();
+  schoolList();
+  collegeList();
+});
+onUnmounted(() => {
+  // document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  width: calc(100% - 40px);
+  height: calc(100% - 105px);
+  margin: 20px auto;
+  background-color: #fff;
+  color: #000;
+  display: flex;
+  flex-direction: column;
+
+  .svg {
+    width: 22px;
+    height: 22px;
+  }
+
+  .left {
+    width: calc(100% - 60px);
+    height: 60px;
+    margin: 0 auto;
+    display: flex;
+    align-items: center;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+    .camera {
+      margin-right: 15px;
+      color: #4392f7;
+    }
+  }
+  .scroll {
+    width: calc(100% - 60px);
+    height: calc(100% - 61px);
+    margin: 0 auto;
+    display: flex;
+    flex-direction: column;
+
+    .middle {
+      width: calc(100%);
+      color: #000;
+      .filter {
+        display: flex;
+        flex-wrap: wrap;
+        align-items: center;
+        .search {
+          margin-left: 0 !important;
+          color: #fff;
+        }
+        .condition {
+          display: flex;
+          align-items: center;
+          margin: 10px 30px 10px 0;
+          :deep(.el-input .el-input__inner) {
+            font-size: 14px;
+          }
+          .el-select {
+            width: 220px;
+          }
+          span {
+            margin: 0 10px 0 0;
+          }
+        }
+      }
+      .gongneng {
+        margin: 10px 0;
+        .el-button {
+          color: #fff;
+          margin-right: 15px;
+        }
+      }
+    }
+    .footer {
+      width: calc(100%);
+      flex: 1;
+      margin: 0 auto;
+      overflow: auto;
+
+      .el-table--fit {
+        height: calc(100% - 60px);
+        :deep(.el-table__header-wrapper) {
+          background-color: #000;
+          font-size: 15px;
+          color: #000;
+          .cell {
+            color: #000;
+          }
+        }
+        :deep(.el-table__row) {
+          height: 50px;
+          font-size: 15px;
+          color: #000;
+        }
+        :deep(.el-table__row td) {
+          padding: 0;
+          border: 0;
+        }
+
+        .el-button--primary {
+          margin-left: 5px;
+        }
+        :deep(.el-table__body .even) {
+          background-color: #fff;
+        }
+        :deep(.el-table__body .odd) {
+          background-color: rgba(240, 243, 247, 1);
+        }
+        :deep(.options) {
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          .open {
+            color: #2697ff;
+            cursor: pointer;
+            margin: 0 15px 0 0;
+          }
+          .edit {
+            margin: 0 15px 0 0;
+            color: rgba(0, 186, 173, 1);
+            cursor: pointer;
+          }
+          .delete {
+            color: rgba(212, 48, 48, 1);
+            cursor: pointer;
+          }
+        }
+      }
+
+      .pageSize {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        margin: 0 30px;
+        height: 60px;
+
+        span {
+          color: #000;
+        }
+
+        .el-pagination {
+          // width: 1600px;
+          :deep(.el-pagination__total) {
+            color: #000;
+          }
+
+          :deep(.el-pagination__goto) {
+            color: #000;
+          }
+
+          :deep(.el-pagination__classifier) {
+            color: #000;
+          }
+
+          :deep(.el-input__wrapper) {
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            box-shadow: none;
+          }
+
+          :deep(.el-pager li) {
+            margin: 0 5px;
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            background-color: transparent;
+          }
+
+          :deep(.el-pager li.is-active) {
+            // background-color: rgba(0, 97, 255, 0.8);
+            border: 1px solid rgba(0, 97, 255, 1);
+            color: rgba(0, 97, 255, 1);
+          }
+
+          :deep(.btn-prev) {
+            margin-right: 5px;
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            background-color: transparent;
+          }
+
+          :deep(.btn-next) {
+            margin-left: 5px;
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            background-color: transparent;
+          }
+        }
+      }
+    }
+  }
+
+  // 添加员工弹窗样式
+  :deep(.addStaff) {
+    .el-dialog__body {
+      padding: 20px 20px 10px 20px;
+      .el-input {
+        width: 400px;
+        .el-input__suffix-inner {
+          color: rgba(61, 81, 232, 1);
+        }
+      }
+      .el-select {
+        width: 400px;
+      }
+      .el-tree {
+        width: 400px;
+      }
+    }
+  }
+}
+</style>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 3177 - 0
src/views/studentInfo/studentInfo copy.vue


+ 146 - 0
src/views/studentInfo/studentInfo.java

@@ -0,0 +1,146 @@
+package com.template.model.request;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * <p>
+ *
+ * </p>
+ *
+ * @author ceshi
+ * @since 2025-06-13
+ */
+@Data
+public class updateStudentRequest implements Serializable {
+
+    @ApiModelProperty(value = "数据ID")
+    private Integer id;
+
+    @ApiModelProperty(value = "录取号")
+    private String admissNum;
+
+    @ApiModelProperty(value = "姓名")
+    private String name;
+
+    @ApiModelProperty(value = "照片")
+    private String picture;
+
+    @ApiModelProperty(value = "身份证号")
+    private String cardId;
+
+    @ApiModelProperty(value = "性别")
+    private String sex;
+
+    @ApiModelProperty(value = "出生年月")
+    private String birthday;
+
+    @ApiModelProperty(value = "校区")
+    private String school;
+
+    @ApiModelProperty(value = "院系")
+    private String college;
+
+    @ApiModelProperty(value = "专业")
+    private String major;
+
+    @ApiModelProperty(value = "班级")
+    private String classstr;
+
+    @ApiModelProperty(value = "院系ID")
+    private Integer collegeId;
+
+    @ApiModelProperty(value = "专业ID")
+    private Integer majorId;
+
+    @ApiModelProperty(value = "班级ID")
+    private Integer classstrId;
+
+    @ApiModelProperty(value = "考生号")
+    private String examNum;
+
+    @ApiModelProperty(value = "学制")
+    private String eduSystem;
+
+    @ApiModelProperty(value = "毕业中学")
+    private String graduationSchool;
+
+    @ApiModelProperty(value = "批次")
+    private String batchValue;
+
+    @ApiModelProperty(value = "政治面貌")
+    private String politicalStatu;
+
+    @ApiModelProperty(value = "民族")
+    private String nationality;
+
+    @ApiModelProperty(value = "手机号码")
+    private String phone;
+
+    @ApiModelProperty(value = "籍贯省")
+    private Integer oprovinceId;
+
+    @ApiModelProperty(value = "籍贯市")
+    private Integer ocityId;
+
+    @ApiModelProperty(value = "籍贯区")
+    private Integer odistrictId;
+
+    @ApiModelProperty(value = "省ID")
+    private Integer provinceId;
+
+    @ApiModelProperty(value = "市ID")
+    private Integer cityId;
+
+    @ApiModelProperty(value = "区ID")
+    private Integer districtId;
+
+    @ApiModelProperty(value = "家庭住址")
+    private String address;
+
+    @ApiModelProperty(value = "交通方式")
+    private String trafficMethod;
+
+    @ApiModelProperty(value = "到站地点")
+    private String arrive;
+
+    @ApiModelProperty(value = "到站日期")
+    private Date arrvieDate;
+
+    @ApiModelProperty(value = "到站时间")
+    private String arriveTime;
+
+//    @ApiModelProperty(value = "应缴金额")
+//    private BigDecimal amountPayable;
+//
+//    @ApiModelProperty(value = "实付金额")
+//    private BigDecimal payAmount;
+//
+//    @ApiModelProperty(value = "是否自驾")
+//    private Integer isDrive;
+//
+//    @ApiModelProperty(value = "车牌号")
+//    private String carNumber;
+
+    //家庭成员
+    private List<InsertFamilyRequest> familys;
+
+    //陪同人员
+    private List<InsertAccompanyRequest> accompanys;
+
+    //楼栋
+    private String bilding;
+
+    //寝室号
+    private String dormitory;
+
+    //床位号
+    private String bed;
+
+
+
+}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 3225 - 0
src/views/studentInfo/studentInfo.vue


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1064 - 0
src/views/system/system.vue


+ 808 - 0
src/views/user/user.vue

@@ -0,0 +1,808 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <el-icon :size="23" class="camera"><VideoCameraFilled /></el-icon> -->
+      <span class="cameratxt">用户管理</span>
+    </div>
+    <div class="scroll">
+      <div class="middle">
+        <div class="filter">
+          <div class="condition">
+            <span>状态 :</span>
+            <el-select
+              clearable
+              v-model="searchInput.status"
+              placeholder="请选择状态"
+            >
+              <el-option label="正常" :value="1" />
+              <el-option label="冻结" :value="2" />
+            </el-select>
+          </div>
+          <div class="condition">
+            <span>姓名 :</span>
+            <el-input
+              clearable
+              v-model.trim="searchInput.keyWord"
+              class="w-50 m-2"
+              placeholder="请输入姓名"
+              style="width: 180px"
+              @clear="searchBtn"
+            />
+          </div>
+          <div class="condition">
+            <span>创建时间 :</span>
+            <el-date-picker
+              v-model="searchInput.createTime"
+              type="datetimerange"
+              range-separator="-"
+              start-placeholder="起始时间"
+              end-placeholder="结束时间"
+              format="YYYY-MM-DD HH:mm:ss"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              placeholder="请选择日期"
+            />
+          </div>
+          <el-button
+            style="margin-left: 20px"
+            color="rgba(38, 151, 255, 1)"
+            type="primary"
+            class="search"
+            @click="searchBtn"
+            ><span>查询</span></el-button
+          >
+          <el-button @click="resetBtn" plain color="rgba(43, 153, 255, 1)"
+            >重置</el-button
+          >
+        </div>
+        <!-- 按钮列表 -->
+        <div class="gongneng">
+          <el-button
+            v-if="store.BtnRole('accountManagementSetting1')"
+            type="primary"
+            style="margin-left: 0"
+            color="rgba(38, 151, 255, 1)"
+            @click="addlist"
+            >添加用户</el-button
+          >
+        </div>
+      </div>
+      <div class="footer" v-loading="loading">
+        <el-table
+          :row-class-name="tableRowClassName"
+          :data="tableData.list"
+          @selection-change="handleSelectionChange"
+          style="width: 100%"
+          :header-cell-style="{
+            background: 'rgba(240, 243, 247, 1)',
+            height: '50px',
+            border: 0,
+          }"
+        >
+          <el-table-column
+            width="80"
+            align="center"
+            label="序号"
+            type="index"
+            index="1"
+          />
+          <el-table-column align="center" prop="account" label="账号" />
+          <el-table-column align="center" prop="roleName" label="角色" />
+          <el-table-column
+            align="center"
+            prop="departmentList"
+            label="可管理部门"
+            width="300"
+            ><template #default="{ row }">
+              <div class="ul" style="display: flex; flex-wrap: wrap">
+                <el-tag
+                  v-for="i in row.welcomeOrgList"
+                  :key="i.id"
+                  style="margin: 5px"
+                  type="primary"
+                  >{{ i.name }}</el-tag
+                >
+              </div>
+            </template>
+          </el-table-column>
+          <el-table-column align="center" prop="name" label="真实姓名" />
+          <el-table-column align="center" prop="phone" label="手机号" />
+          <el-table-column align="center" prop="createTime" label="创建时间" />
+          <el-table-column align="center" prop="name" label="状态">
+            <template #default="{ row }">
+              <span v-if="row.status == 2" style="color: #fa9e19">冻结</span>
+              <span v-if="row.status == 1" style="color: #2697ff">正常</span>
+            </template>
+          </el-table-column>
+          <el-table-column
+            align="center"
+            label="操作"
+            width="160"
+            fixed="right"
+          >
+            <template #default="{ row }">
+              <div class="options">
+                <span
+                  v-if="store.BtnRole('accountManagementSetting2')"
+                  class="edit"
+                  @click="updateS(row)"
+                  >编辑</span
+                >
+                <span
+                  v-if="store.BtnRole('accountManagementSetting3')"
+                  class="delete"
+                  @click="deleteS(row)"
+                  >删除</span
+                >
+              </div>
+            </template>
+          </el-table-column>
+        </el-table>
+        <!-- 分页组件 -->
+        <div class="pageSize">
+          <span></span>
+          <el-pagination
+            background
+            :current-page="currentPage"
+            :page-size="pageSize"
+            :page-sizes="[10, 20, 30, 40]"
+            layout="total,sizes, prev, pager, next, jumper, slot"
+            :total="total"
+            @size-change="handleSizeChange"
+            @update:current-page="handleCurrentChange"
+          />
+        </div>
+      </div>
+    </div>
+    <!-- 添加账号弹窗 -->
+    <el-dialog
+      class="addStaff"
+      v-model="addDialogVisible"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      :title="dialongTitle"
+      align-center
+      width="580"
+      :before-close="cancelAdd"
+      destroy-on-close
+      draggable
+    >
+      <el-form
+        ref="ruleFormRef"
+        :model="ruleForm"
+        :rules="rules"
+        label-width="100px"
+        class="demo-ruleForm"
+        :size="formSize"
+        label-position="right"
+        status-icon
+      >
+        <el-form-item label="姓名 :" prop="name">
+          <el-input
+            v-model.trim="ruleForm.name"
+            placeholder="请输入姓名"
+            clearable
+          />
+        </el-form-item>
+        <el-form-item label="手机号码 :" prop="phone">
+          <el-input
+            v-model.trim="ruleForm.phone"
+            placeholder="请输入手机号码"
+            clearable
+          />
+        </el-form-item>
+        <el-form-item label="用户名 :" prop="account">
+          <el-input
+            v-model.trim="ruleForm.account"
+            placeholder="请输入用户名"
+            clearable
+          />
+        </el-form-item>
+        <el-form-item label="密码 :" prop="password">
+          <el-input
+            v-model.trim="ruleForm.password"
+            placeholder="请输入密码"
+            clearable
+          />
+        </el-form-item>
+        <el-form-item label="状态 :" prop="status">
+          <el-radio-group v-model="ruleForm.status">
+            <el-radio :value="1" size="large">正常</el-radio>
+            <el-radio :value="2" size="large">冻结</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="角色 :" prop="roleId">
+          <el-select
+            clearable
+            v-model="ruleForm.roleId"
+            placeholder="请选择角色"
+          >
+            <el-option
+              v-for="i in roleData"
+              :key="i.id"
+              :label="i.roleName"
+              :value="i.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="部门 :" prop="collegeId">
+          <el-select
+            @change="collegeFormChange"
+            v-model="ruleForm.collegeId"
+            clearable
+            multiple
+            placeholder="请选择部门"
+          >
+            <el-option
+              v-for="i in collegeData"
+              :key="i.id"
+              :label="i.name"
+              :value="`${i.id}`"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item class="options">
+          <el-button @click="cancelAdd">取消</el-button>
+          <el-button
+            color="rgba(0, 97, 255, 1)"
+            class="queding"
+            type="primary"
+            @click="submitAdd(ruleFormRef)"
+          >
+            确定
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import {
+  ref,
+  watch,
+  reactive,
+  nextTick,
+  onBeforeMount,
+  onUnmounted,
+} from "vue";
+import { useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { dayjs } from "element-plus";
+import lodash from "lodash";
+import { https } from "@/utils/request"; // 绝对路径
+
+import { storeToRefs } from "pinia";
+import { useCounterStore } from "@/stores/index";
+
+const router = useRouter();
+const store = useCounterStore();
+
+// 为避免解构时失去响应性
+const { name, age, isCollapse, realAge } = storeToRefs(store);
+
+// 表格数据
+const loading = ref(false);
+const tableData = reactive({
+  list: [{}],
+});
+const activeIndex = ref(); // 默认跳转路由
+const dialongTitle = ref("新增账号"); // 弹窗标题
+
+const searchInput = reactive({
+  keyWord: "",
+  status: "",
+  createTime: "",
+}); // 搜索按钮数据
+
+const currentPage = ref(1); // 当前页
+const pageSize = ref(10);
+const total = ref(); // 当前总数
+const selectIds = ref([]);
+
+const addDialogVisible = ref(false); // 控制添加账号弹窗
+// 学院
+const collegeData = ref([]);
+const roleData = ref([]);
+
+// 表单数据
+const formSize = ref("default");
+const ruleFormRef = ref();
+const ruleForm = reactive({
+  name: "", //名称
+  account: "", //账号
+  phone: "", //手机号
+  status: 1, //状态 2:冻结 ,1正常
+  password: "", //密码
+  roleId: "", //角色id
+  collegeId: [], //学院id,多个用逗号隔开
+  id: "",
+});
+// 表单验证
+const rules = reactive({
+  name: [{ required: true, message: "姓名不能为空", trigger: "blur" }],
+  account: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
+  phone: [{ required: true, message: "手机号码不能为空", trigger: "blur" }],
+  status: [{ required: true, message: "状态不能为空", trigger: "blur" }],
+  password: [
+    { required: true, message: "密码不能为空", trigger: "blur" },
+    {
+      min: 8,
+      max: 20,
+      pattern:
+        /^(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*,\._\+(){}])[0-9a-zA-Z!@#$%^&*,\\._\+(){}]{8,20}$/,
+      message: "请输入8-20位正确密码(大小写字母·字符·数字)",
+      trigger: "blur",
+    },
+  ],
+  roleId: [{ required: true, message: "角色不能为空", trigger: "blur" }],
+  collegeId: [{ required: true, message: "部门不能为空", trigger: "blur" }],
+});
+
+const schoolData = ref();
+
+// 获取账户列表
+const getList = async () => {
+  loading.value = true;
+  let params = {
+    currentPage: currentPage.value, // 当前页
+    pageCount: pageSize.value, // 一页数据条数
+    keyWord: searchInput.keyWord,
+    status: searchInput.status,
+  };
+  if (searchInput.createTime) {
+    params.startTime = searchInput.createTime[0];
+    params.endTime = searchInput.createTime[1];
+  }
+
+  let res = await https.get(
+    "/welcome/api/welcomeAccount/listAccount",
+    "params",
+    params
+  );
+  console.log(res, "账号信息");
+  if (res.code == 200) {
+    res.data.list.forEach((i) => {
+      if (i.welcomeRole) {
+        i.roleName = i.welcomeRole.roleName;
+      }
+    });
+    tableData.list = res.data.list;
+    total.value = res.data.totalCount;
+    loading.value = false;
+  } else {
+    loading.value = false;
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+const roleList = async () => {
+  let res = await https.get("/welcome/api/welcomeRole/roleGroup", "params");
+  console.log(res, "角色数据");
+  if (res.code == 200) {
+    roleData.value = res.data;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+const collegeList = async () => {
+  let res = await https.get("/welcome/api/welcomeOrg/getColleges", "params");
+  console.log(res, "学院数据");
+  if (res.code == 200) {
+    collegeData.value = res.data;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.message,
+      center: true,
+    });
+  }
+};
+
+// 搜索功能
+const searchBtn = lodash.debounce(async () => {
+  getList();
+}, 300);
+const resetBtn = lodash.debounce(async () => {
+  searchInput.name = null;
+  searchInput.carNumber = null;
+  getList();
+}, 300);
+
+// 添加账号
+const addlist = () => {
+  dialongTitle.value = "新增用户";
+  addDialogVisible.value = true;
+  ruleForm.id = null;
+  ruleForm.name = null;
+  ruleForm.account = null;
+  ruleForm.phone = null;
+  ruleForm.status = 1;
+  ruleForm.password = null;
+  ruleForm.roleId = null;
+  ruleForm.collegeId = null;
+};
+// 添加账号
+const updateS = (row) => {
+  console.log(row, row.collegeId.split(","));
+
+  dialongTitle.value = "编辑用户";
+  addDialogVisible.value = true;
+  ruleForm.id = row.id;
+  ruleForm.name = row.name;
+  ruleForm.account = row.account;
+  ruleForm.phone = row.phone;
+  ruleForm.status = row.status;
+  ruleForm.password = row.password;
+  ruleForm.roleId = row.roleId;
+  ruleForm.collegeId = row.collegeId.split(",");
+};
+const deleteS = async (row) => {
+  ElMessageBox.confirm("是否删除此数据?", "提示!!!", {
+    confirmButtonText: "确认",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(async () => {
+      let data = {
+        accountId: row.id,
+      };
+      let res = await https.get(
+        "/welcome/api/welcomeAccount/deleteAccount",
+        "params",
+        data
+      );
+      if (res.code == 200) {
+        ElMessage({
+          type: "success",
+          showClose: true,
+          message: res.message,
+          center: true,
+        });
+        getList();
+      } else {
+        ElMessage({
+          type: "error",
+          showClose: true,
+          message: res.message,
+          center: true,
+        });
+      }
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+
+// 确认添加员工
+const submitAdd = lodash.debounce(async (formEl) => {
+  if (!formEl) return;
+  await formEl.validate(async (valid, fields) => {
+    if (valid) {
+      let data = {
+        name: ruleForm.name,
+        account: ruleForm.account,
+        phone: ruleForm.phone,
+        status: ruleForm.status,
+        password: ruleForm.password,
+        roleId: ruleForm.roleId,
+        collegeId: ruleForm.collegeId.join(","),
+      };
+      if (ruleForm.id) {
+        data.id = ruleForm.id;
+        let res = await https.post(
+          "/welcome/api/welcomeAccount/updateAccount",
+          "data",
+          data
+        );
+        if (res.code == 200) {
+          addDialogVisible.value = false;
+          getList();
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: res.message,
+            center: true,
+          });
+        } else {
+          ElMessage({
+            type: "error",
+            showClose: true,
+            message: res.message,
+            center: true,
+          });
+        }
+      } else {
+        let res = await https.post(
+          "/welcome/api/welcomeAccount/saveAccount",
+          "data",
+          data
+        );
+        if (res.code == 200) {
+          addDialogVisible.value = false;
+          getList();
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: res.message,
+            center: true,
+          });
+        } else {
+          ElMessage({
+            type: "error",
+            showClose: true,
+            message: res.message,
+            center: true,
+          });
+        }
+      }
+    } else {
+      console.log("error submit!", fields);
+      loading.value = false;
+    }
+  });
+}, 1000);
+const cancelAdd = () => {
+  addDialogVisible.value = false;
+  // ruleFormRef.value.resetFields();
+};
+// 多选框功能
+const handleSelectionChange = (val) => {
+  console.log(val);
+  selectIds.value = val.map((i) => i.id);
+};
+
+// 表格斑马纹颜色修改
+const tableRowClassName = ({ row, rowIndex }) => {
+  if (rowIndex % 2 === 0) {
+    return "even";
+  } else if (rowIndex % 2 !== 0) {
+    return "odd";
+  }
+  return "";
+};
+// 每页显示条数
+const handleSizeChange = (value) => {
+  console.log(value, "每页显示条数");
+  pageSize.value = value;
+  getList();
+};
+// 分页
+const handleCurrentChange = (value) => {
+  // console.log(value);
+  currentPage.value = value;
+  getList();
+};
+
+onBeforeMount(() => {
+  getList();
+  roleList();
+  collegeList();
+});
+onUnmounted(() => {
+  // document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  width: calc(100% - 40px);
+  height: calc(100% - 105px);
+  margin: 20px auto;
+  background-color: #fff;
+  color: #000;
+  display: flex;
+  flex-direction: column;
+
+  .svg {
+    width: 22px;
+    height: 22px;
+  }
+
+  .left {
+    width: calc(100% - 60px);
+    height: 60px;
+    margin: 0 auto;
+    display: flex;
+    align-items: center;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+    .camera {
+      margin-right: 15px;
+      color: #4392f7;
+    }
+  }
+  .scroll {
+    width: calc(100% - 60px);
+    height: calc(100% - 61px);
+    margin: 0 auto;
+    display: flex;
+    flex-direction: column;
+
+    .middle {
+      width: calc(100%);
+      color: #000;
+      .filter {
+        display: flex;
+        flex-wrap: wrap;
+        align-items: center;
+        .search {
+          margin-left: 0 !important;
+          color: #fff;
+        }
+        .condition {
+          display: flex;
+          align-items: center;
+          margin: 10px 30px 10px 0;
+          :deep(.el-input .el-input__inner) {
+            font-size: 14px;
+          }
+          .el-select {
+            width: 220px;
+          }
+          span {
+            margin: 0 10px 0 0;
+          }
+        }
+      }
+      .gongneng {
+        margin: 10px 0;
+        .el-button {
+          color: #fff;
+          margin-right: 15px;
+        }
+      }
+    }
+    .footer {
+      width: calc(100%);
+      flex: 1;
+      margin: 0 auto;
+      overflow: auto;
+
+      .el-table--fit {
+        height: calc(100% - 60px);
+        :deep(.el-table__header-wrapper) {
+          background-color: #000;
+          font-size: 15px;
+          color: #000;
+          .cell {
+            color: #000;
+          }
+        }
+        :deep(.el-table__row) {
+          height: 50px;
+          font-size: 15px;
+          color: #000;
+        }
+        :deep(.el-table__row td) {
+          padding: 0;
+          border: 0;
+        }
+
+        .el-button--primary {
+          margin-left: 5px;
+        }
+        :deep(.el-table__body .even) {
+          background-color: #fff;
+        }
+        :deep(.el-table__body .odd) {
+          background-color: rgba(240, 243, 247, 1);
+        }
+        :deep(.options) {
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          .edit {
+            margin: 0 15px 0 0;
+            color: rgba(0, 186, 173, 1);
+            cursor: pointer;
+          }
+          .delete {
+            color: rgba(212, 48, 48, 1);
+            cursor: pointer;
+          }
+        }
+      }
+
+      .pageSize {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        margin: 0 30px;
+        height: 60px;
+
+        span {
+          color: #000;
+        }
+
+        .el-pagination {
+          // width: 1600px;
+          :deep(.el-pagination__total) {
+            color: #000;
+          }
+
+          :deep(.el-pagination__goto) {
+            color: #000;
+          }
+
+          :deep(.el-pagination__classifier) {
+            color: #000;
+          }
+
+          :deep(.el-input__wrapper) {
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            box-shadow: none;
+          }
+
+          :deep(.el-pager li) {
+            margin: 0 5px;
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            background-color: transparent;
+          }
+
+          :deep(.el-pager li.is-active) {
+            // background-color: rgba(0, 97, 255, 0.8);
+            border: 1px solid rgba(0, 97, 255, 1);
+            color: rgba(0, 97, 255, 1);
+          }
+
+          :deep(.btn-prev) {
+            margin-right: 5px;
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            background-color: transparent;
+          }
+
+          :deep(.btn-next) {
+            margin-left: 5px;
+            border: 1px solid rgba(0, 0, 0, 1);
+            border-radius: 5px;
+            background-color: transparent;
+          }
+        }
+      }
+    }
+  }
+
+  // 添加员工弹窗样式
+  :deep(.addStaff) {
+    .el-dialog__body {
+      padding: 20px 20px 10px 20px;
+      .el-input {
+        width: 400px;
+        .el-input__suffix-inner {
+          color: rgba(61, 81, 232, 1);
+        }
+      }
+      .el-select {
+        width: 400px;
+      }
+      .el-tree {
+        width: 400px;
+      }
+      .el-form {
+        .el-form-item {
+          display: flex;
+          align-items: center;
+        }
+        .options {
+          margin-top: 30px;
+        }
+      }
+    }
+  }
+}
+</style>

+ 87 - 0
vite.config.js

@@ -0,0 +1,87 @@
+import { defineConfig } from "vite";
+import vue from "@vitejs/plugin-vue";
+import path from "path";
+import { resolve, join } from "path";
+import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+  plugins: [
+    vue(), //配置svg全局可用
+    createSvgIconsPlugin({
+      iconDirs: [path.resolve(process.cwd(), 'src/assets/svgs')], //修改为你存放svg图片的实际路径
+      symbolId: "icon-[dir]-[name]",
+    }),
+  ],
+  resolve: {
+    alias: {
+      "@": path.resolve("./src"), // @代替src
+    },
+  },
+  css: {
+    preprocessorOptions: {
+      scss: {
+        additionalData: `@use "@/styles/mixin.scss";` // 例如,导入全局变量文件
+      },
+    },
+  },
+  server: {
+    host: "0.0.0.0",
+    port: 8585,
+    // 是否开启 https
+    // https: false,
+    proxy: {
+      "/welcome/welcome_api": {
+        target: "https://chtech.ncjti.edu.cn/welcome/welcome_api", // 线上环境
+        changeOrigin: true,
+        rewrite: (path) => path.replace(/^\/welcome\/welcome_api/, ""),
+      },
+    },
+    // proxy: {
+    //   "/welcome/api": {
+    //     target: "http://192.168.161.190:8080", // 线上环境
+    //     changeOrigin: true,
+    //     rewrite: (path) => path.replace(/^\/welcome\/api/, ""),
+    //   },
+    // },
+  },
+  publicDir: "public",
+  base: "./",
+  // 打包配置
+  build: {
+    target: "modules", //设置最终构建的浏览器兼容目标  //es2015(编译成es5) | modules
+    outDir: "dist", // 构建得包名  默认:dist
+    assetsDir: "static", // 静态资源得存放路径文件名  assets
+    sourcemap: false, //构建后是否生成 source map 文件
+    // brotliSize: false, // 启用/禁用 brotli 压缩大小报告。 禁用该功能可能会提高大型项目的构建性能
+    chunkSizeWarningLimit: 1000, //chunk 大小警告的限制(以 kbs 为单位)默认:500
+    // cssTarget: 'chrome61' //防止 vite 将 rgba() 颜色转化为 #RGBA 十六进制符号的形式  (要兼容的场景是安卓微信中的 webview 时,它不支持 CSS 中的 #RGBA 十六进制颜色符号)
+    rollupOptions: {
+      input: {
+        index: resolve(__dirname, "index.html"),
+      },
+      output: {
+        manualChunks(id) {
+          if (id.includes("node_modules")) {
+            return id
+              .toString()
+              .split("node_modules/")[1]
+              .split("/")[0]
+              .toString();
+          }
+        },
+        chunkFileNames: "static/js/[name]-[hash].js",
+        entryFileNames: "static/js/[name]-[hash].js",
+        assetFileNames: "static/[ext]/name-[hash].[ext]",
+      },
+    },
+    minify: "terser", // 项目压缩 :boolean | 'terser' | 'esbuild'
+    terserOptions: {
+      // 生产环境移除console
+      compress: {
+        drop_console: true,
+        drop_debugger: true,
+      },
+    },
+  },
+});