瀏覽代碼

完成项目管理界面

hzj18279462576@163.com 1 年之前
父節點
當前提交
49cc6f098d
共有 55 個文件被更改,包括 14620 次插入1 次删除
  1. 24 0
      .gitignore
  2. 6 0
      .vs/VSWorkspaceState.json
  3. 二進制
      .vs/construction Management/v17/.wsuo
  4. 二進制
      .vs/slnx.sqlite
  5. 3 0
      .vscode/extensions.json
  6. 36 0
      README.en.md
  7. 39 1
      README.md
  8. 15 0
      index.html
  9. 5864 0
      package-lock.json
  10. 45 0
      package.json
  11. 33 0
      src/App.vue
  12. 87 0
      src/api/admin.js
  13. 14 0
      src/api/home.js
  14. 120 0
      src/api/system.js
  15. 110 0
      src/api/vidicons.js
  16. 二進制
      src/assets/icon/process.png
  17. 二進制
      src/assets/images/bg.png
  18. 二進制
      src/assets/images/chaoshibanjie.png
  19. 二進制
      src/assets/images/chaoshizaiban.png
  20. 二進制
      src/assets/images/inform.png
  21. 二進制
      src/assets/images/login.png
  22. 二進制
      src/assets/images/logo.png
  23. 二進制
      src/assets/images/xiangmuzongshu.png
  24. 二進制
      src/assets/images/yujingzaiban.png
  25. 二進制
      src/assets/images/zhengchangbanjie.png
  26. 二進制
      src/assets/images/zhengchangzaiban.png
  27. 二進制
      src/assets/logo.png
  28. 1 0
      src/assets/svgIcon/home.svg
  29. 1 0
      src/assets/svgIcon/project.svg
  30. 1 0
      src/assets/svgIcon/role.svg
  31. 1 0
      src/assets/svgIcon/system.svg
  32. 1 0
      src/assets/svgIcon/user.svg
  33. 43 0
      src/components/SvgIcon.vue
  34. 165 0
      src/layout/index.vue
  35. 831 0
      src/layout/sidebar/Navbar.vue
  36. 509 0
      src/layout/sidebar/NavbarBG.vue
  37. 208 0
      src/layout/sidebar/SidevarItem.vue
  38. 31 0
      src/main.js
  39. 88 0
      src/router/index.js
  40. 5 0
      src/store/getters.js
  41. 22 0
      src/store/index.js
  42. 174 0
      src/store/modules/user.js
  43. 89 0
      src/style.css
  44. 1 0
      src/style/mixin.scss
  45. 74 0
      src/utils/http.js
  46. 23 0
      src/utils/loadMap.js
  47. 47 0
      src/utils/request.js
  48. 22 0
      src/utils/rsa.js
  49. 1648 0
      src/views/home/home.vue
  50. 511 0
      src/views/login/index.vue
  51. 1437 0
      src/views/project/project.vue
  52. 781 0
      src/views/role/role.vue
  53. 615 0
      src/views/system/system.vue
  54. 810 0
      src/views/user/user.vue
  55. 85 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?

+ 6 - 0
.vs/VSWorkspaceState.json

@@ -0,0 +1,6 @@
+{
+  "ExpandedNodes": [
+    ""
+  ],
+  "PreviewInSolutionExplorer": false
+}

二進制
.vs/construction Management/v17/.wsuo


二進制
.vs/slnx.sqlite


+ 3 - 0
.vscode/extensions.json

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

+ 36 - 0
README.en.md

@@ -0,0 +1,36 @@
+# 校园报修系统
+
+#### 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/)

+ 39 - 1
README.md

@@ -1 +1,39 @@
-#Project_process_manage
+# 校园报修系统
+
+#### 介绍
+{**以下是 Gitee 平台说明,您可以替换此简介**
+Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
+无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
+
+#### 软件架构
+软件架构说明
+
+
+#### 安装教程
+
+1.  xxxx
+2.  xxxx
+3.  xxxx
+
+#### 使用说明
+
+1.  xxxx
+2.  xxxx
+3.  xxxx
+
+#### 参与贡献
+
+1.  Fork 本仓库
+2.  新建 Feat_xxx 分支
+3.  提交代码
+4.  新建 Pull Request
+
+
+#### 特技
+
+1.  使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
+2.  Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
+3.  你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
+4.  [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
+5.  Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
+6.  Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

+ 15 - 0
index.html

@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="zh">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="./src/assets/icon/process.png" />
+    <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0" /> -->
+    <meta name="referrer" content="no-referrer" />
+    <title>项目过程管理系统</title>
+  </head>
+
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.js"></script>
+  </body>
+</html>

文件差異過大導致無法顯示
+ 5864 - 0
package-lock.json


+ 45 - 0
package.json

@@ -0,0 +1,45 @@
+{
+  "name": "vite-project",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "@easydarwin/easyplayer": "^5.0.7",
+    "@element-plus/icons-vue": "^2.0.10",
+    "@liveqing/liveplayer-v3": "^3.7.10",
+    "@vue-office/docx": "^1.6.1",
+    "@wangeditor/editor": "^5.1.23",
+    "@wangeditor/editor-for-vue": "^5.1.12",
+    "axios": "^1.3.2",
+    "cos-js-sdk-v5": "^1.5.0",
+    "dayjs": "^1.11.8",
+    "echarts": "^5.2.0",
+    "echarts-gl": "^2.0.9",
+    "element-plus": "^2.2.29",
+    "fast-glob": "^3.3.2",
+    "hls.js": "^1.3.4",
+    "jsencrypt": "^3.3.1",
+    "lodash": "^4.17.21",
+    "qrcode.vue": "^3.4.1",
+    "sass": "^1.58.0",
+    "sass-loader": "^13.2.0",
+    "terser": "^5.16.5",
+    "vite-plugin-svg-icons": "^2.0.1",
+    "vue": "^3.2.45",
+    "vue-demi": "^0.14.7",
+    "vue-router": "^4.0.10",
+    "vue3-pdf-app": "^1.0.3",
+    "vue3-seamless-scroll": "^2.0.1",
+    "vuex": "^4.0.2",
+    "vuex-persistedstate": "^4.1.0"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^4.0.0",
+    "vite": "^4.1.0"
+  }
+}

+ 33 - 0
src/App.vue

@@ -0,0 +1,33 @@
+<script setup>
+import { ref, reactive, nextTick, onBeforeMount, onUnmounted } from "vue";
+import { useRouter } from "vue-router";
+import { useStore } from "vuex";
+const store = useStore();
+const router = useRouter();
+onBeforeMount(() => {
+  // store.dispatch("pathSelect");
+  // store.dispatch("busNum");
+  // store.dispatch("newsAsync");
+  // store.dispatch("sm_time");
+});
+</script>
+
+<template>
+  <router-view></router-view>
+</template>
+
+<style lang="scss">
+#app {
+  max-width: 1920px;
+  width: 1920px;
+  min-height: 100vh;
+  max-height: 1080px;
+  margin: 0;
+  padding: 0;
+  text-align: unset;
+  // width: 100%;
+  // height: 100%;
+  // margin: 0;
+  // padding: 0;
+}
+</style>

+ 87 - 0
src/api/admin.js

@@ -0,0 +1,87 @@
+import request from "@/utils/request";
+const requests = '/api'// 线上
+// const requests = '/video/transcoding/transcoding'//线下
+export default {
+    // 登录账号
+    // async adminLogin(data) {
+    //     let requestData = await request({
+    //         url: `${requests}/carBook/adminlogin.action`,
+    //         method: "POST",
+    //         // headers: { "Content-Type": "application/json;charset=utf-8" },
+    //         data: data,
+    //     });
+    //     return requestData;
+    // },
+
+    // 账号列表
+    // async adminList(params) {
+    //     let requestData = await request({
+    //         url: `${requests}/carBook/adminlist.action`,
+    //         method: "GET",
+    //         headers: { "Content-Type": "multipart/form-data" },
+    //         params,
+    //     });
+    //     return requestData;
+    // },
+
+    // 新增管理员
+    async adminAdd(data) {
+        let requestData = await request({
+            url: `${requests}/carBook/admininsert.action`,
+            method: "POST",
+            data,
+        });
+        return requestData;
+    },
+    // 修改管理员
+    async adminUpdate(data) {
+        let requestData = await request({
+            url: `${requests}/carBook/adminupdate.action`,
+            method: "POST",
+            data,
+        });
+        return requestData;
+    },
+    // 修改密码
+    // async updatePassword(data) {
+    //     let requestData = await request({
+    //         url: `${requests}/videoAdmin/updatePassword`,
+    //         method: "PUT",
+    //         data,
+    //     });
+    //     return requestData;
+    // },
+    // 删除管理员
+    async adminDel(data) {
+        let requestData = await request({
+            url: `${requests}/carBook/admindel.action`,
+            method: "POST",
+            data,
+        });
+        return requestData;
+    },
+
+    // 导出管理员信息
+    async download(params) {
+        let requestData = await request({
+            url: `${requests}/carBook/admintoExcel.action`,
+            method: "GET",
+            headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8" },
+            responseType: "blob",
+            params,
+        });
+        return requestData;
+    },
+
+    // 导入监控信息
+    // async importByExcel(data) {
+    //     let requestData = await request({
+    //         // url: "/api/video/videoAdmin/importByExcel",
+    //         url: `${requests}/camera/importByExcel`,
+    //         method: "POST",
+    //         headers: { "Content-Type": "multipart/form-data" },
+    //         data: data,
+    //     });
+    //     return requestData;
+    // },
+};

+ 14 - 0
src/api/home.js

@@ -0,0 +1,14 @@
+import request from "@/utils/request";
+// const request='/api'
+export default {
+    /*搜索*/
+
+    async getHomeList(data) {
+        let requestData = await request({
+            url: "/api",
+            method: "POST",
+            data: data,
+        });
+        return requestData;
+    }
+}

+ 120 - 0
src/api/system.js

@@ -0,0 +1,120 @@
+import request from "@/utils/request";
+const requests = "/video/transcoding"; // 线上
+// const requests = '/video/transcoding/transcoding'// 线下
+export default {
+  // 添加监控信息
+  async systemList(data) {
+    let requestData = await request({
+      url: `${requests}/config/showCfg`,
+      method: "get",
+      headers: {
+        "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
+      },
+      params: data,
+    });
+    return requestData;
+  },
+
+  // 删除监控信息
+  async delCamera(data) {
+    let requestData = await request({
+      url: `${requests}/camera/delete`,
+      method: "DELETE",
+      data,
+    });
+    return requestData;
+  },
+
+  // 修改监控信息
+  async updateCamera(data) {
+    let requestData = await request({
+      url: `${requests}/camera/updateCamera`,
+      method: "PUT",
+      headers: { "Content-Type": "application/json; charset=utf-8" },
+      data,
+    });
+    return requestData;
+  },
+
+  // 监控信息列表
+  async queryCamera(data) {
+    let requestData = await request({
+      url: `${requests}/camera/queryCamera`,
+      method: "POST",
+      headers: {
+        "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
+      },
+      params: data,
+    });
+    return requestData;
+  },
+  // 导出监控信息
+  async download(params) {
+    let requestData = await request({
+      url: `${requests}/camera/download`,
+      method: "GET",
+      headers: {
+        "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
+      },
+      responseType: "blob",
+      params,
+    });
+    return requestData;
+  },
+  // 导入监控信息
+  async importByExcel(data) {
+    let requestData = await request({
+      url: `${requests}/camera/importByExcel`,
+      method: "POST",
+      headers: { "Content-Type": "multipart/form-data" },
+      data,
+    });
+    return requestData;
+  },
+
+  // 使用Stream推拉流
+  async addStreamSource(data) {
+    let requestData = await request({
+      url: `${requests}/stream/addStreamSource`,
+      method: "POST",
+      headers: { "Content-Type": "application/json; charset=utf-8" },
+      data,
+    });
+    return requestData;
+  },
+
+  // 关闭Stream推拉流
+  async delStreamSource(data) {
+    let requestData = await request({
+      url: `${requests}/stream/delStreamSource`,
+      method: "POST",
+      headers: { "Content-Type": "application/json; charset=utf-8" },
+      data,
+    });
+    return requestData;
+  },
+  // 更新输出链接
+  async updateCameraLink(params) {
+    let requestData = await request({
+      url: `${requests}/camera/updateCameraLink`,
+      method: "GET",
+      headers: {
+        "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
+      },
+      params,
+    });
+    return requestData;
+  },
+  // 关闭当前播放回写剩下的播放链接、
+  async delAndUpdateLink(params) {
+    let requestData = await request({
+      url: `${requests}/stream/delAndUpdateLink`,
+      method: "GET",
+      headers: {
+        "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
+      },
+      params,
+    });
+    return requestData;
+  },
+};

+ 110 - 0
src/api/vidicons.js

@@ -0,0 +1,110 @@
+import request from "@/utils/request";
+const requests = '/video/transcoding'// 线上
+// const requests = '/video/transcoding/transcoding'// 线下
+export default {
+    // 添加监控信息
+    async addCamera(data) {
+        let requestData = await request({
+            url: `${requests}/camera/addCamera`,
+            method: "POST",
+            headers: { "Content-Type": "application/json; charset=utf-8" },
+            data: data,
+        });
+        return requestData;
+    },
+
+    // 删除监控信息
+    async delCamera(data) {
+        let requestData = await request({
+            url: `${requests}/camera/delete`,
+            method: "DELETE",
+            data
+        });
+        return requestData;
+    },
+
+    // 修改监控信息
+    async updateCamera(data) {
+        let requestData = await request({
+            url: `${requests}/camera/updateCamera`,
+            method: "PUT",
+            headers: { "Content-Type": "application/json; charset=utf-8" },
+            data,
+        });
+        return requestData;
+    },
+
+    // 监控信息列表
+    async queryCamera(data) {
+        let requestData = await request({
+            url: `${requests}/camera/queryCamera`,
+            method: "POST",
+            headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8" },
+            params: data,
+        });
+        return requestData;
+    },
+    // 导出监控信息
+    async download(params) {
+        let requestData = await request({
+            url: `${requests}/camera/download`,
+            method: "GET",
+            headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8" },
+            responseType: "blob",
+            params,
+        });
+        return requestData;
+    },
+    // 导入监控信息
+    async importByExcel(data) {
+        let requestData = await request({
+            url: `${requests}/camera/importByExcel`,
+            method: "POST",
+            headers: { "Content-Type": "multipart/form-data" },
+            data,
+        });
+        return requestData;
+    },
+
+    // 使用Stream推拉流
+    async addStreamSource(data) {
+        let requestData = await request({
+            url: `${requests}/stream/addStreamSource`,
+            method: "POST",
+            headers: { "Content-Type": "application/json; charset=utf-8" },
+            data,
+        });
+        return requestData;
+    },
+
+    // 关闭Stream推拉流
+    async delStreamSource(data) {
+        let requestData = await request({
+            url: `${requests}/stream/delStreamSource`,
+            method: "POST",
+            headers: { "Content-Type": "application/json; charset=utf-8" },
+            data,
+        });
+        return requestData;
+    },
+    // 更新输出链接
+    async updateCameraLink(params) {
+        let requestData = await request({
+            url: `${requests}/camera/updateCameraLink`,
+            method: "GET",
+            headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8" },
+            params,
+        });
+        return requestData;
+    },
+    // 关闭当前播放回写剩下的播放链接、
+    async delAndUpdateLink(params) {
+        let requestData = await request({
+            url: `${requests}/stream/delAndUpdateLink`,
+            method: "GET",
+            headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8" },
+            params,
+        });
+        return requestData;
+    },
+};

二進制
src/assets/icon/process.png


二進制
src/assets/images/bg.png


二進制
src/assets/images/chaoshibanjie.png


二進制
src/assets/images/chaoshizaiban.png


二進制
src/assets/images/inform.png


二進制
src/assets/images/login.png


二進制
src/assets/images/logo.png


二進制
src/assets/images/xiangmuzongshu.png


二進制
src/assets/images/yujingzaiban.png


二進制
src/assets/images/zhengchangbanjie.png


二進制
src/assets/images/zhengchangzaiban.png


二進制
src/assets/logo.png


文件差異過大導致無法顯示
+ 1 - 0
src/assets/svgIcon/home.svg


文件差異過大導致無法顯示
+ 1 - 0
src/assets/svgIcon/project.svg


文件差異過大導致無法顯示
+ 1 - 0
src/assets/svgIcon/role.svg


文件差異過大導致無法顯示
+ 1 - 0
src/assets/svgIcon/system.svg


文件差異過大導致無法顯示
+ 1 - 0
src/assets/svgIcon/user.svg


+ 43 - 0
src/components/SvgIcon.vue

@@ -0,0 +1,43 @@
+<template>
+    <svg
+      class="svg-icon"
+      :class="className"
+      aria-hidden="true"
+      :style="{ width: size + 'px', height: size + 'px', fill: color }"
+    >
+      <use :href="iconName" />
+    </svg>
+  </template>
+  
+  <script setup>
+  import { computed } from "vue";
+  
+  const props = defineProps({
+    icon: {
+      type: String,
+      default: "",
+    },
+    className: {
+      type: String,
+      default: "",
+    },
+    size: {
+      type: Number,
+      default: 20,
+    },
+    color: {
+      type: String,
+      default: "green",
+    },
+  });
+  
+  const iconName = computed(() => `#icon-${props.icon}`);
+  </script>
+  
+  <style lang="scss" scoped>
+  // .svg-icon {
+  //   fill: currentColor;
+  // }
+ 
+  </style>
+  

+ 165 - 0
src/layout/index.vue

@@ -0,0 +1,165 @@
+<template>
+  <div class="body-box">
+    <SidevarItem></SidevarItem>
+    <div
+      class="content"
+    >
+      <Navbar></Navbar>
+      <!-- <NavbarBG></NavbarBG> -->
+      <router-view></router-view>
+      <!-- <div class="policeNum">
+        <div class="rigthContent">
+          <span> 赣公网安备36092502000028号</span>
+          <span> &nbsp;&nbsp;&nbsp;&nbsp;</span>
+          <span>赣ICP备2023009877号-2</span>
+        </div>
+      </div> -->
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onBeforeMount, onMounted, watch } from "vue";
+import Navbar from "./sidebar/Navbar.vue";
+import NavbarBG from "./sidebar/NavbarBG.vue";
+import SidevarItem from "./sidebar/SidevarItem.vue";
+import { useStore } from "vuex";
+import axios from "axios";
+import { useRouter } from "vue-router";
+const store = useStore();
+const router = useRouter();
+const api = ref("");
+const flag = ref(true);
+watch(
+  () => router.currentRoute.value.fullPath,
+  (newValue, oldValue) => {
+    console.log(newValue);
+    sessionStorage.setItem("sidevarItem", newValue);
+  }
+);
+const tableData = reactive({
+  list: [],
+});
+
+onBeforeMount(async () => {
+  api.value = store.state.user.api;
+});
+</script>
+
+<style scoped lang="scss">
+.body-box {
+  display: flex;
+
+  .content {
+    // max-width: calc(100vw - 81px);
+    width: 100%;
+    height: calc(100vh);
+    background-color: rgba(238, 238, 238, 1);
+
+    :deep(.el-dialog__header .el-dialog__headerbtn) {
+      outline: none;
+    }
+    .policeNum {
+      position: absolute;
+      bottom: 8px;
+      width: 100vw;
+      color: rgba(0, 0, 0, 0.5);
+      font-size: 12px;
+
+      .rigthContent {
+        min-width: 600px;
+        width: calc(100vw - 200px);
+        text-align: center;
+      }
+    }
+  }
+  .contentBG {
+    background-color: transparent;
+  }
+}
+</style>
+<style lang="scss">
+.is-message-box {
+  .el-message-box__header {
+    .el-message-box__headerbtn {
+      outline: none;
+    }
+  }
+
+  .el-message-box__btns {
+    .el-button--primary {
+      background: linear-gradient(
+        90deg,
+        rgb(38, 151, 255) 0%,
+        rgb(102, 181, 255) 100%
+      );
+      border: none !important;
+    }
+
+    .el-button--primary:hover {
+      background-color: rgb(119, 133, 239);
+      border: 0.5px solid rgb(119, 133, 239);
+    }
+  }
+}
+
+.el-dialog__body {
+  .el-input {
+    .el-input__suffix-inner {
+      color: rgba(61, 81, 232, 1);
+    }
+  }
+}
+
+.pw {
+  .el-dialog__body {
+    .el-form {
+      .el-form-item {
+        .queding {
+          background-color: rgba(61, 81, 232, 1);
+          border: 0.5px solid rgba(61, 81, 232, 1);
+        }
+
+        .queding:hover {
+          background-color: rgb(119, 133, 239);
+          border: 0.5px solid rgb(119, 133, 239);
+        }
+      }
+    }
+  }
+}
+
+.tangram-suggestion-main {
+  z-index: 999999;
+
+  .tangram-suggestion {
+    height: 150px;
+    overflow: auto;
+  }
+}
+
+#notification_1 {
+  .el-notification__group {
+    .el-notification__title {
+      color: red;
+    }
+  }
+}
+
+*::-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;
+}
+</style>

+ 831 - 0
src/layout/sidebar/Navbar.vue

@@ -0,0 +1,831 @@
+<template>
+  <el-menu
+    :default-active="activeIndex"
+    mode="horizontal"
+    background-color=""
+    text-color="#000"
+    active-text-color="#4392f7"
+    @select="handleSelect"
+  >
+    <!-- <el-icon
+      :size="20"
+      v-show="!menuclose"
+      class="fold"
+      @click="SpreadMenu(true)"
+      ><Fold
+    /></el-icon>
+    <el-icon
+      :size="20"
+      v-show="menuclose"
+      class="fold"
+      @click="SpreadMenu(false)"
+      ><Expand
+    /></el-icon> -->
+
+    <div class="logo">
+      <!-- <img src="@/assets/nanchang.png" style="width: 30px; height: 30px" /> -->
+      <span>项目过程管理系统</span>
+    </div>
+    <div class="login">
+      <span class="dateTime">{{ dateTime }}</span>
+      <!-- <el-badge :value="titlenumber" class="item" v-if="titlenumber">
+        <img
+          src="@/assets/news.png"
+          @click="newsClick"
+          style="width: 20px; height: 20px; cursor: pointer"
+          alt=""
+        />
+      </el-badge> -->
+
+      <div class="flex flex-wrap items-center" style="cursor: pointer">
+        <el-dropdown :hide-on-click="false" trigger="click">
+          <el-avatar :size="20">
+            <el-icon :size="20" color=""><QuestionFilled /></el-icon>
+          </el-avatar>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <el-dropdown-item @click="operateClick">
+                <el-icon class="switchButton" :size="20" color="#000">
+                  <img
+                    src="@/assets/operate.png"
+                    style="width: 20px; height: 20px"
+                    alt=""
+                  />
+                </el-icon>
+
+                <span>操作说明</span>
+              </el-dropdown-item>
+              <el-dropdown-item @click="contactClick">
+                <el-icon class="switchButton" :size="20" color="#000">
+                  <img
+                    src="@/assets/contact.png"
+                    style="width: 20px; height: 20px"
+                    alt=""
+                  />
+                </el-icon>
+                <span>联系我们</span>
+              </el-dropdown-item>
+              <el-dialog
+                :append-to-body="true"
+                custom-class="pw"
+                v-model="uppasswordShow"
+                title="修改密码"
+                width="500"
+                :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">
+                    <el-input
+                      v-model="ruleForm.correctpass"
+                      placeholder="请再次输入新密码"
+                      clearable
+                    />
+                  </el-form-item>
+                  <p
+                    style="
+                      padding-bottom: 20px;
+                      font-size: 12px;
+                      color: rgb(43, 151, 252);
+                      border-bottom: 1px solid rgba(230, 230, 230, 1);
+                    "
+                  >
+                    注:密码格式为8-20位正确密码(大小写字母·字符·数字)
+                  </p>
+                  <el-form-item class="options">
+                    <el-button class="queding" @click="submitForm(ruleFormRef)">
+                      确定
+                    </el-button>
+                    <el-button class="congzhi" @click="resetForm(ruleFormRef)"
+                      >重置</el-button
+                    >
+                  </el-form-item>
+                </el-form>
+                <!-- <template #footer>
+                  <span class="dialog-footer">
+                    <el-button @click="dialogVisible = false">取消</el-button>
+                    <el-button type="primary" @click="dialogVisible = false">
+                      确定
+                    </el-button>
+                  </span>
+                </template> -->
+              </el-dialog>
+
+              <el-dialog
+                class="operate"
+                v-model="operateShow"
+                title="操作说明"
+                width="500"
+                :before-close="operateClose"
+                align-center
+                :close-on-click-modal="false"
+                :close-on-press-escape="false"
+              >
+                <div class="operateUl">
+                  <p>
+                    <span @click="lookFile(0)">建设单位_操作说明</span>
+                    <span @click="downFile(0)">下载</span>
+                  </p>
+                  <p>
+                    <span @click="lookFile(1)">县(区)管理员_操作说明</span
+                    ><span @click="downFile(1)">下载</span>
+                  </p>
+                  <p>
+                    <span @click="lookFile(2)">市级管理员_操作说明</span>
+                    <span @click="downFile(2)">下载</span>
+                  </p>
+                  <p>
+                    <span @click="lookFile(3)">省级管理员_操作说明</span>
+                    <span @click="downFile(3)">下载</span>
+                  </p>
+                </div>
+
+                <div class="options">
+                  <el-button class="queding" @click="operateClose">
+                    关闭
+                  </el-button>
+                </div>
+              </el-dialog>
+
+              <el-dialog
+                class="operate"
+                v-model="contactShow"
+                title="联系我们"
+                width="500"
+                :before-close="contactClose"
+                align-center
+                :close-on-click-modal="false"
+                :close-on-press-escape="false"
+              >
+                <div class="operateUl">
+                  <div style="margin: 15px 0">
+                    <span>联系人:</span>
+                    <span>邱工</span>
+                  </div>
+                  <div>
+                    <span>联系电话:</span
+                    ><span>0791-88514176 &nbsp;&nbsp; 18896551060</span>
+                  </div>
+                </div>
+                <div class="options">
+                  <el-button class="queding" @click="contactClose">
+                    关闭
+                  </el-button>
+                </div>
+              </el-dialog>
+
+              <el-dialog
+                class="preview"
+                v-model="previewShow"
+                title="预览文档"
+                width="1200"
+                :before-close="previewClose"
+                align-center
+                :close-on-click-modal="false"
+                :close-on-press-escape="false"
+              >
+                <div class="pre" v-loading="previewLoading">
+                  <vue-office-docx
+                    v-if="lookDocx"
+                    :src="lookDocx"
+                    @rendered="renderedHandler"
+                    @error="errorHandler"
+                  />
+                </div>
+              </el-dialog>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
+      </div>
+      <div class="flex flex-wrap items-center" style="cursor: pointer">
+        <el-dropdown :hide-on-click="false" trigger="click">
+          <el-avatar :size="30">
+            <img
+              src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
+            />
+          </el-avatar>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <el-dropdown-item @click="uppassword">
+                <el-icon class="switchButton" :size="20" color="#000">
+                  <Lock />
+                </el-icon>
+                <span>修改密码</span>
+              </el-dropdown-item>
+              <el-dialog
+                :append-to-body="true"
+                custom-class="pw"
+                v-model="uppasswordShow"
+                title="修改密码"
+                width="500"
+                :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">
+                    <el-input
+                      v-model="ruleForm.correctpass"
+                      placeholder="请再次输入新密码"
+                      clearable
+                    />
+                  </el-form-item>
+                  <p
+                    style="
+                      padding-bottom: 20px;
+                      font-size: 12px;
+                      color: rgb(43, 151, 252);
+                      border-bottom: 1px solid rgba(230, 230, 230, 1);
+                    "
+                  >
+                    注:密码格式为8-20位正确密码(大小写字母·字符·数字)
+                  </p>
+                  <el-form-item class="options">
+                    <el-button class="queding" @click="submitForm(ruleFormRef)">
+                      确定
+                    </el-button>
+                    <el-button class="congzhi" @click="resetForm(ruleFormRef)"
+                      >重置</el-button
+                    >
+                  </el-form-item>
+                </el-form>
+                <!-- <template #footer>
+                  <span class="dialog-footer">
+                    <el-button @click="dialogVisible = false">取消</el-button>
+                    <el-button type="primary" @click="dialogVisible = false">
+                      确定
+                    </el-button>
+                  </span>
+                </template> -->
+              </el-dialog>
+              <el-dropdown-item @click="loginOut">
+                <el-icon class="switchButton" :size="20" color="#000">
+                  <SwitchButton />
+                </el-icon>
+                <span>退出登录</span>
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
+      </div>
+      <span class="name">{{ username }}</span>
+    </div>
+  </el-menu>
+</template>
+
+<script setup>
+import { ref, reactive, onBeforeMount, onMounted, watch } from "vue";
+import { ElMessage, ElMessageBox, ElNotification } from "element-plus";
+import { useStore } from "vuex";
+import { useRouter } from "vue-router";
+import { dayjs } from "element-plus";
+import axios from "axios";
+//引入VueOfficeDocx组件
+import VueOfficeDocx from "@vue-office/docx";
+//引入相关样式
+import "@vue-office/docx/lib/index.css";
+
+const api = ref();
+const store = useStore();
+const router = useRouter();
+const username = ref(); // 账号名称
+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 validatePass = (rule, value, callback) => {
+//   if (
+//     !/^(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*,\._\+(){}])[0-9a-zA-Z!@#$%^&*,\\._\+(){}]{8,20}$/.test(
+//       value
+//     )
+//   ) {
+//     callback(new Error("请输入8-20位正确密码(大小写字母·字符·数字)"));
+//   } else if (ruleForm.nowpass != ruleForm.correctpass) {
+//     callback(new Error("输入的两次密码不一致"));
+//   } else {
+//     callback();
+//   }
+// };
+const rules = reactive({
+  oldpass: [
+    {
+      required: true,
+      message: "原密码不能为空",
+      trigger: "blur",
+    },
+  ],
+  nowpass: [
+    {
+      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",
+    // },
+  ],
+  correctpass: [
+    {
+      required: true,
+      message: "确认密码不能为空",
+      trigger: "blur",
+    },
+    // { validator: validatePass, trigger: "blur" },
+  ],
+});
+
+// 操作说明
+const operateShow = ref(false);
+const operateList = ref();
+const lookDocx = ref(); // 预览的这个文件
+
+// 预览文档
+const previewShow = ref(false);
+const previewLoading = ref(false);
+
+// 联系我们
+const contactShow = ref(false);
+
+// 监控消息通知数量
+// watch(
+//   () => store.state.user.activeIndex,
+//   (newValue, oldValue) => {
+//     titlenumber.value = newValue;
+//     console.log(newValue, "路由地址");
+//   }
+// );
+
+const dateTime = ref(
+  dayjs().format("YYYY-MM-DD") + ` 星期四 ` + dayjs().format("HH:mm:ss")
+); // 时间
+
+// 选择菜单
+// const handleSelect = (key, keyPath) => {
+// store.commit("navbarUpdata", key);
+// activeIndex.value = key;
+// };
+
+// 展开按钮
+const SpreadMenu = (flag) => {
+  store.commit("menuClose", flag);
+  menuclose.value = flag;
+};
+
+// 修改密码
+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,
+      };
+      let resUpdata = await axios({
+        method: "post",
+        url: api.value + "/login/ChangePassword",
+        headers: {
+          token: sessionStorage.getItem("token"),
+          user_head: sessionStorage.getItem("userhead"),
+        },
+        data: data,
+      });
+      if (resUpdata.data.code == 200) {
+        ElMessage({
+          type: "success",
+          showClose: true,
+          message: resUpdata.data.message,
+          center: true,
+        });
+        sessionStorage.removeItem("token");
+        sessionStorage.removeItem("userName");
+        sessionStorage.removeItem("token");
+        sessionStorage.removeItem("roleId");
+        sessionStorage.removeItem("userhead");
+        sessionStorage.removeItem("provinceId");
+        sessionStorage.removeItem("cityId");
+        sessionStorage.removeItem("districtId");
+        sessionStorage.removeItem("address");
+        sessionStorage.removeItem("addressId");
+        uppasswordShow.value = false;
+        ruleFormRef.value.resetFields();
+        router.removeRoute("jianguanCloud");
+        ElNotification({
+          type: "warning",
+          title: "提示!!!",
+          message: "密码已修改,2s后退出到登录页面",
+          // duration: 0,
+          // position: "top-right",
+        });
+        setTimeout(() => {
+          router.push({ path: "/" });
+        }, 2000);
+      } else {
+        ElMessage({
+          type: "error",
+          showClose: true,
+          message: resUpdata.data.message,
+          center: true,
+        });
+      }
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+};
+// 重置表单
+const resetForm = (formEl) => {
+  // console.log("重置表单");
+  if (!formEl) return;
+  formEl.resetFields();
+};
+
+// 操作说明
+const operateClick = async () => {
+  operateShow.value = true;
+  let res = await axios({
+    method: "get",
+    url: api.value + "/login/operatingInstructions",
+    headers: {
+      token: sessionStorage.getItem("token"),
+      user_head: sessionStorage.getItem("userhead"),
+    },
+  });
+  console.log(res, "操作说明列表");
+  if (res.data.code == 200) {
+    operateList.value = res.data.data;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  }
+};
+const downFile = (flag) => {
+  window.location.href = operateList.value[flag].url;
+};
+const operateClose = () => {
+  operateShow.value = false;
+};
+// 预览文档弹窗
+const lookFile = (flag) => {
+  let urls="https://jgy-1325577833.cos.ap-guangzhou.myqcloud.com/%E7%9C%81%E7%BA%A7%E7%AE%A1%E7%90%86%E5%91%98_%E6%93%8D%E4%BD%9C%E8%AF%B4%E6%98%8E.docx"
+  // lookDocx.value = urls;
+  lookDocx.value = operateList.value[flag].url;
+  previewShow.value = true;
+  previewLoading.value = true;
+};
+const previewClose = () => {
+  previewShow.value = false;
+  lookDocx.value = "";
+};
+// 预览功能
+const renderedHandler = () => {
+  console.log("渲染完成");
+  previewLoading.value = false;
+};
+const errorHandler = () => {
+  console.log("渲染失败");
+  previewLoading.value = false;
+  ElMessage({
+    type: "error",
+    showClose: true,
+    message: "预览失败",
+    center: true,
+  });
+};
+
+// 联系我们
+const contactClick = () => {
+  contactShow.value = true;
+};
+const contactClose = () => {
+  contactShow.value = false;
+};
+const loginOut = () => {
+  ElMessageBox.confirm("是否退出登录?", "提示", {
+    confirmButtonText: "确认",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(() => {
+      sessionStorage.removeItem("token");
+      sessionStorage.removeItem("userName");
+      sessionStorage.removeItem("token");
+      sessionStorage.removeItem("roleId");
+      sessionStorage.removeItem("userhead");
+      sessionStorage.removeItem("provinceId");
+      sessionStorage.removeItem("cityId");
+      sessionStorage.removeItem("districtId");
+      sessionStorage.removeItem("address");
+      sessionStorage.removeItem("addressId");
+      router.push({
+        path: `/login`,
+      });
+      router.removeRoute("jianguanCloud");
+      ElMessage({
+        type: "success",
+        message: "退出成功",
+      });
+    })
+    .catch(() => {
+      ElMessage({
+        type: "info",
+        message: "已取消登录",
+      });
+    });
+};
+
+onMounted(() => {
+  api.value = store.state.user.api;
+  activeIndex.value = store.state.user.navbar;
+  username.value = sessionStorage.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: 64px;
+  border-bottom: 1px solid rgba(255, 255, 255, 0.2);
+  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: 20px;
+      font-size: 24px;
+      font-weight: 600;
+    }
+  }
+  .login {
+    color: #000;
+    display: flex;
+    align-items: center;
+    position: absolute;
+    right: 10px;
+    top: 17px;
+
+    .dateTime {
+      margin-right: 0px;
+    }
+    .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;
+    }
+    .link {
+      display: flex;
+      align-items: center;
+    }
+    .switchButton {
+      cursor: pointer;
+    }
+  }
+  .login:focus {
+    outline: none;
+  }
+  .fold {
+    width: 46px;
+    height: 65px;
+  }
+  .fold:hover {
+    cursor: pointer;
+    background-color: rgba(67, 146, 249, 0.1);
+  }
+}
+.cockpit {
+  background-color: #000;
+}
+.el-dialog__body {
+  .options {
+    display: flex;
+    flex-direction: row-reverse;
+    :deep(.el-form-item__content) {
+      margin-left: 0 !important;
+      flex: none;
+    }
+    :deep(.queding) {
+      background: linear-gradient(
+        90deg,
+        rgb(38, 151, 255) 0%,
+        rgb(102, 181, 255) 100%
+      );
+      border: none !important;
+      color: #fff;
+      // border: 0.5px solid rgba(9, 101, 98, 1) !important;
+    }
+  }
+}
+// 操作说明
+:deep(.operate) {
+  //   height: 420px;
+  border-radius: 11px;
+
+  .el-dialog__header {
+    border-radius: 11px 11px 0 0;
+    background: rgba(237, 241, 245, 1);
+    font-weight: 600;
+    margin: 0;
+
+    .el-dialog__headerbtn {
+      outline: none;
+    }
+  }
+
+  .el-dialog__body {
+    padding: 0 30px 0 30px;
+    .operateUl {
+      p {
+        width: 220px;
+        color: rgba(46, 176, 241, 1);
+        display: flex;
+        justify-content: space-between;
+        span {
+          cursor: pointer;
+        }
+      }
+    }
+    .options {
+      margin: 20px 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);
+      }
+    }
+  }
+}
+// 预览文档
+:deep(.preview) {
+  border-radius: 11px;
+
+  .el-dialog__header {
+    border-radius: 11px 11px 0 0;
+    background: rgba(237, 241, 245, 1);
+    font-weight: 600;
+    margin: 0;
+
+    .el-dialog__headerbtn {
+      outline: none;
+    }
+  }
+
+  .el-dialog__body {
+    padding: 20px;
+    height: 800px;
+    .pre {
+      height: 100%;
+      .vue-office-docx {
+        height: 100%;
+        .vue-office-docx-main{
+          height: 100%;
+        }
+      }
+    }
+  }
+}
+</style>
+<style lang="scss">
+.el-overlay {
+  .el-message-box__btns {
+    .el-button--primary {
+      background: linear-gradient(
+        90deg,
+        rgb(38, 151, 255) 0%,
+        rgb(102, 181, 255) 100%
+      );
+    }
+  }
+  .el-overlay-dialog {
+    .pw {
+      border-radius: 11px;
+
+      .el-dialog__header {
+        border-radius: 11px 11px 0 0;
+        background: rgba(237, 241, 245, 1);
+        font-weight: 600;
+        margin: 0;
+        .el-dialog__headerbtn {
+          outline: none;
+        }
+      }
+      .el-dialog__body {
+        padding: 30px 20px 10px 20px;
+        .el-input {
+          width: 200px;
+        }
+        .options {
+          margin-left: 170px;
+        }
+      }
+    }
+  }
+}
+</style>

+ 509 - 0
src/layout/sidebar/NavbarBG.vue

@@ -0,0 +1,509 @@
+<template>
+  <el-menu
+    :default-active="activeIndex"
+    mode="horizontal"
+    background-color=""
+    text-color="#000"
+    active-text-color="#4392f7"
+    @select="handleSelect"
+  >
+    <!-- <el-icon
+      :size="20"
+      v-show="!menuclose"
+      class="fold"
+      @click="SpreadMenu(true)"
+      ><Fold
+    /></el-icon>
+    <el-icon
+      :size="20"
+      v-show="menuclose"
+      class="fold"
+      @click="SpreadMenu(false)"
+      ><Expand
+    /></el-icon> -->
+
+    <div class="logo">
+      <!-- <img src="@/assets/nanchang.png" style="width: 30px; height: 30px" /> -->
+      <span>江西装配建管云平台</span>
+    </div>
+    <div class="login">
+      <div class="dateTime">
+        <sapn class="dateTime">{{ dateTime }}</sapn>
+        <sapn class="time">{{ time }}</sapn>
+      </div>
+      <!-- <el-badge :value="titlenumber" class="item" v-if="titlenumber">
+        <img
+          src="@/assets/news.png"
+          @click="newsClick"
+          style="width: 20px; height: 20px; cursor: pointer"
+          alt=""
+        />
+      </el-badge> -->
+
+      <!-- <el-tooltip content="操作手册" placement="bottom" effect="light">
+        <a
+          target="_Blank"
+          class="link"
+          href="https://www.jinganrenjiams.com/illustrate/"
+        >
+          <img
+            src="@/assets/operation.png"
+            @click="newsClick"
+            style="width: 25px; height: 25px; cursor: pointer"
+            alt=""
+          />
+        </a>
+      </el-tooltip> -->
+      <!-- <div class="flex flex-wrap items-center" style="cursor: pointer">
+        <el-dropdown :hide-on-click="false" trigger="click">
+          <el-avatar :size="30">
+            <img
+              src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
+            />
+          </el-avatar>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <el-dropdown-item @click="uppassword">
+                <el-icon class="switchButton" :size="20" color="#000">
+                  <Lock />
+                </el-icon>
+                <span>修改密码</span>
+              </el-dropdown-item>
+              <el-dialog
+                :append-to-body="true"
+                custom-class="pw"
+                v-model="uppasswordShow"
+                title="修改密码"
+                width="500"
+                :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">
+                    <el-input
+                      v-model="ruleForm.correctpass"
+                      placeholder="请再次输入新密码"
+                      clearable
+                    />
+                  </el-form-item>
+                  <p
+                    style="
+                      padding-bottom: 20px;
+                      font-size: 12px;
+                      color: rgb(43, 151, 252);
+                      border-bottom: 1px solid rgba(230, 230, 230, 1);
+                    "
+                  >
+                    注:密码格式为8-20位正确密码(大小写字母·字符·数字)
+                  </p>
+                  <el-form-item class="options">
+                    <el-button class="queding" @click="submitForm(ruleFormRef)">
+                      确定
+                    </el-button>
+                    <el-button class="congzhi" @click="resetForm(ruleFormRef)"
+                      >重置</el-button
+                    >
+                  </el-form-item>
+                </el-form>
+              </el-dialog>
+              <el-dropdown-item @click="loginOut">
+                <el-icon class="switchButton" :size="20" color="#000">
+                  <SwitchButton />
+                </el-icon>
+                <span>退出登录</span>
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
+      </div>
+      <span class="name">{{ username }}</span> -->
+    </div>
+  </el-menu>
+</template>
+
+<script setup>
+import { ref, reactive, onBeforeMount, onMounted, watch } from "vue";
+import { ElMessage, ElMessageBox, ElNotification } from "element-plus";
+import { useStore } from "vuex";
+import { useRouter } from "vue-router";
+import { dayjs } from "element-plus";
+import axios from "axios";
+
+const api = ref();
+const store = useStore();
+const router = useRouter();
+const username = ref(); // 账号名称
+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",
+    },
+    {
+      min: 8,
+      max: 20,
+      pattern:
+        /^(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*,\._\+(){}])[0-9a-zA-Z!@#$%^&*,\\._\+(){}]{8,20}$/,
+      message: "请输入8-20位正确密码(大小写字母·字符·数字)",
+      trigger: "blur",
+    },
+  ],
+  correctpass: [
+    {
+      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",
+    },
+  ],
+  // desc: [{ required: true, message: "Please input activity form", trigger: "blur" }],
+});
+
+// 监控消息通知数量
+// watch(
+//   () => store.state.user.activeIndex,
+//   (newValue, oldValue) => {
+//     titlenumber.value = newValue;
+//     console.log(newValue, "路由地址");
+//   }
+// );
+
+const dateTime = ref(dayjs().format("YYYY-MM-DD") + ` 星期四 `); // 时间
+const time = ref(dayjs().format("HH:mm:ss"));
+
+// 选择菜单
+// const handleSelect = (key, keyPath) => {
+// store.commit("navbarUpdata", key);
+// activeIndex.value = key;
+// };
+
+// 展开按钮
+const SpreadMenu = (flag) => {
+  store.commit("menuClose", flag);
+  menuclose.value = flag;
+};
+
+// 修改密码
+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,
+      };
+      let resUpdata = await axios({
+        method: "post",
+        url: api.value + "/login/ChangePassword",
+        headers: {
+          token: sessionStorage.getItem("token"),
+          user_head: sessionStorage.getItem("userhead"),
+        },
+        data: data,
+      });
+      if (resUpdata.data.code == 200) {
+        ElMessage({
+          type: "success",
+          showClose: true,
+          message: resUpdata.data.message,
+          center: true,
+        });
+        sessionStorage.removeItem("token");
+        sessionStorage.removeItem("userName");
+        sessionStorage.removeItem("token");
+        sessionStorage.removeItem("roleId");
+        sessionStorage.removeItem("userhead");
+        sessionStorage.removeItem("provinceId");
+        sessionStorage.removeItem("cityId");
+        sessionStorage.removeItem("districtId");
+        sessionStorage.removeItem("address");
+        sessionStorage.removeItem("addressId");
+        uppasswordShow.value = false;
+        ruleFormRef.value.resetFields();
+        router.removeRoute("jianguanCloud");
+        ElNotification({
+          type: "warning",
+          title: "提示!!!",
+          message: "密码已修改,2s后退出到登录页面",
+          // duration: 0,
+          // position: "top-right",
+        });
+        setTimeout(() => {
+          router.push({ path: "/" });
+        }, 2000);
+      } else {
+        ElMessage({
+          type: "error",
+          showClose: true,
+          message: resUpdata.data.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(() => {
+      sessionStorage.removeItem("token");
+      sessionStorage.removeItem("userName");
+      sessionStorage.removeItem("token");
+      sessionStorage.removeItem("roleId");
+      sessionStorage.removeItem("userhead");
+      sessionStorage.removeItem("provinceId");
+      sessionStorage.removeItem("cityId");
+      sessionStorage.removeItem("districtId");
+      sessionStorage.removeItem("address");
+      sessionStorage.removeItem("addressId");
+      router.push({
+        path: `/login`,
+      });
+      router.removeRoute("jianguanCloud");
+      ElMessage({
+        type: "success",
+        message: "退出成功",
+      });
+    })
+    .catch(() => {
+      ElMessage({
+        type: "info",
+        message: "已取消登录",
+      });
+    });
+};
+
+onMounted(() => {
+  api.value = store.state.user.api;
+  activeIndex.value = store.state.user.navbar;
+  username.value = sessionStorage.getItem("userName");
+  setInterval(() => {
+    var week = ["日", "一", "二", "三", "四", "五", "六"];
+    var datas = dayjs().day();
+    dateTime.value = dayjs().format("YYYY-MM-DD") + ` 星期${week[datas]} `;
+    time.value = dayjs().format("HH:mm:ss");
+  }, 1000);
+});
+</script>
+
+<style scoped lang="scss">
+.el-menu--horizontal {
+  border-bottom: none;
+  display: flex;
+  align-items: center;
+  width: 100%;
+  height: 64px;
+  border-bottom: 1px solid rgba(255, 255, 255, 0.2);
+  // background-color: rgba(16, 46, 93, 0.8);
+  background-color: transparent;
+  background-image: url("../../assets/navber.png");
+  background-size: 100% 140%;
+  background-position: center;
+  background-repeat: no-repeat;
+  // box-shadow: 5px 5px 10px 0px rgba(213, 228, 252, 1);
+  .logo {
+    left: 280px;
+    bottom: 12px;
+    // display: flex;
+    // align-items: center;
+    position: absolute;
+    span {
+      // margin-left: ;
+      // transform: translateX(280px);
+      // margin-bottom: 12px;
+      font-size: 33px;
+      font-weight: 600;
+      color: #fff;
+    }
+  }
+  .login {
+    color: #fff;
+    display: flex;
+    align-items: center;
+    position: absolute;
+    right: 10px;
+    top: 17px;
+
+    .dateTime {
+      margin-right: 0px;
+      color: rgba(0, 233, 254, 1);
+      text-shadow: 0 0 1px rgba(0, 233, 254, 0.8),
+        0 0 2px rgba(0, 233, 254, 0.8), 0 0 6px rgba(0, 233, 254, 0.8);
+    }
+    .time {
+      margin: 0 10px;
+      color: rgba(255, 255, 255, 1);
+      font-size: 18px;
+      text-shadow: 0 0 1px rgba(255, 255, 255, 0.8),
+        0 0 2px rgba(255, 255, 255, 0.8), 0 0 6px rgba(255, 255, 255, 0.8);
+    }
+    .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;
+    }
+    .link {
+      display: flex;
+      align-items: center;
+    }
+    .switchButton {
+      cursor: pointer;
+    }
+  }
+  .login:focus {
+    outline: none;
+  }
+  .fold {
+    width: 46px;
+    height: 65px;
+  }
+  .fold:hover {
+    cursor: pointer;
+    background-color: rgba(67, 146, 249, 0.1);
+  }
+}
+.cockpit {
+  background-color: #000;
+}
+.el-dialog__body {
+  .options {
+    display: flex;
+    flex-direction: row-reverse;
+    :deep(.el-form-item__content) {
+      margin-left: 0 !important;
+      flex: none;
+    }
+    :deep(.queding) {
+      background: linear-gradient(
+        90deg,
+        rgb(38, 151, 255) 0%,
+        rgb(102, 181, 255) 100%
+      );
+      border: none !important;
+      color: #fff;
+    }
+  }
+}
+</style>
+<style lang="scss">
+.el-overlay {
+  .el-message-box__btns {
+    .el-button--primary {
+      background: linear-gradient(
+        90deg,
+        rgb(38, 151, 255) 0%,
+        rgb(102, 181, 255) 100%
+      );
+    }
+  }
+  .el-overlay-dialog {
+    .pw {
+      border-radius: 11px;
+
+      .el-dialog__header {
+        border-radius: 11px 11px 0 0;
+        background: rgba(237, 241, 245, 1);
+        font-weight: 600;
+        margin: 0;
+        .el-dialog__headerbtn {
+          outline: none;
+        }
+      }
+      .el-dialog__body {
+        padding: 30px 20px 10px 20px;
+        .el-input {
+          width: 200px;
+        }
+        .options {
+          margin-left: 170px;
+        }
+      }
+    }
+  }
+}
+</style>

+ 208 - 0
src/layout/sidebar/SidevarItem.vue

@@ -0,0 +1,208 @@
+<template>
+  <div class="box-item">
+    <el-menu
+      class="el-menu-vertical-demo"
+      text-color="#fff"
+      :default-active="activeIndex"
+      :collapse="menuClose"
+      @select="handleSelect"
+      :router="true"
+    >
+      <!-- :default-openeds="[34]" -->
+      <div class="logo el-menu-item">
+        <el-icon :size="20"
+          ><img src="@/assets/images/logo.png" style="width: 40px; height: 40px"
+        /></el-icon>
+      </div>
+      <el-menu-item :index="`/process/${item.name}`" v-for="item in roleList" :key="item.name">
+        <div class="itemSon">
+          <SvgIcon :icon="item.meta.icon" :size="22" />
+          <span>{{ item.meta.title }}</span>
+        </div>
+      </el-menu-item>
+    </el-menu>
+  </div>
+</template>
+
+<script setup>
+import { ref, onBeforeMount, onMounted, watch, reactive } from "vue";
+import { useStore } from "vuex";
+
+import { useRouter } from "vue-router";
+
+const store = useStore();
+const router = useRouter();
+const roleList = ref();
+
+const menuClose = ref(false);
+const activeIndex = ref('/process/user');
+const acitveItems = reactive({ list: [] });
+
+watch(
+  () => store.state.user.collapse,
+  (newValue, oldValue) => {
+    menuClose.value = newValue;
+  }
+);
+watch(
+  () => router.currentRoute.value.fullPath,
+  (newValue, oldValue) => {
+    console.log(newValue);
+    store.commit("indexUp", newValue);
+    activeIndex.value = newValue;
+  }
+);
+
+const handleSelect = (key, keyPath) => {
+  // store.commit("indexUp", key);
+  // activeIndex.value = key;
+  // sessionStorage.setItem("sidevarItem", key);
+  console.log(key, keyPath);
+  router.push({
+    path: `${key}`,
+  });
+
+  activeIndex.value = key;
+  store.commit("indexUp", key);
+  // if (activeIndex.value == 1) {
+  //   router.push({
+  //     path: `/jianguanCloud/order`,
+  //   });
+  // }
+};
+onBeforeMount(() => {
+  activeIndex.value = sessionStorage.getItem("sidevarItem");
+  roleList.value = store.state.user.roleList;
+});
+</script>
+
+<style lang="scss" scoped>
+.el-menu-vertical-demo:not(.el-menu--collapse) {
+  width: 220px;
+  // min-height: 400px;
+}
+.box-item {
+  width: 220px;
+  height: calc(100vh);
+}
+.el-menu {
+  width: 220px;
+  height: 100%;
+  overflow: auto;
+  // background: rgba(55, 105, 173, 1);
+  background: rgba(46, 64, 89, 1);
+  box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.16);
+  border: none;
+  .logo {
+    height: 64px;
+    color: rgb(255, 255, 255);
+    cursor: default;
+    display: flex;
+    justify-content: space-around;
+    align-items: center;
+    border-bottom: 1px solid rgba(255, 255, 255, 0.2);
+  }
+
+  .logo:hover {
+    background-color: transparent !important;
+  }
+  .itemSon {
+    width: 180px;
+    height: 40px;
+    border-radius: 10px;
+    display: flex;
+    // justify-content: space-around;
+    align-items: center;
+    // background: linear-gradient(
+    //   90deg,
+    //   rgba(38, 151, 255, 1) 0%,
+    //   rgba(102, 182, 255, 1) 100%
+    // );
+    .svg-icon{
+      margin:0 15px;
+    }
+  }
+  .el-sub-menu {
+    padding: 8px 0;
+    .el-menu-item {
+      padding: 0;
+      background: rgba(46, 64, 89, 1);
+      color: #fff;
+      display: flex;
+      justify-content: center;
+      .img {
+        width: 18px;
+        margin: 0 20px;
+      }
+      .itemSon:hover {
+        background: rgba(55, 105, 173, 1);
+        color: rgba(99, 180, 255, 1);
+      }
+    }
+    .el-menu-item:hover {
+      background: rgba(46, 64, 89, 1);
+      color: #fff;
+      .itemSon {
+        background: rgba(55, 105, 173, 1);
+        color: rgba(99, 180, 255, 1);
+      }
+    }
+    .el-menu-item.is-active {
+      .itemSon {
+        background: rgba(55, 105, 173, 1);
+        color: rgba(99, 180, 255, 1);
+      }
+    }
+    // 系统设置栏样式
+    :deep(.el-sub-menu__title) {
+      padding: 0;
+      margin: 0 auto;
+      width: 180px;
+      height: 40px;
+      border-radius: 10px;
+      display: flex;
+      align-items: center;
+      background: rgba(55, 105, 173, 1);
+      color: #fff;
+      .el-icon {
+        width: 18px;
+      }
+    }
+  }
+  .el-sub-menu.is-active {
+    :deep(.el-sub-menu__title) {
+      background: linear-gradient(
+        90deg,
+        rgba(38, 151, 255, 1) 0%,
+        rgba(102, 182, 255, 1) 100%
+      );
+      color: fff;
+    }
+  }
+
+  // 单个菜单项样式
+  .el-menu-item {
+    padding: 10px 0 !important;
+    display: flex;
+    justify-content: center;
+  }
+
+  .el-menu-item:hover {
+    background: rgba(46, 64, 89, 1);
+    .itemSon {
+      background: rgba(55, 105, 173, 1);
+      color: rgba(99, 180, 255, 1);
+    }
+  }
+  .el-menu-item.is-active {
+    .itemSon {
+      background: linear-gradient(
+        90deg,
+        rgba(38, 151, 255, 1) 0%,
+        rgba(102, 182, 255, 1) 100%
+      );
+      color: #fff;
+    }
+  }
+}
+</style>

+ 31 - 0
src/main.js

@@ -0,0 +1,31 @@
+import { createApp } from "vue";
+import "./style.css";
+import ElementPlus from "element-plus";
+import zhCn from "element-plus/dist/locale/zh-cn.mjs";
+import "element-plus/dist/index.css";
+import * as ElementPlusIconsVue from "@element-plus/icons-vue";
+import App from "./App.vue";
+
+import router from "@/router";
+import store from "@/store";
+
+import SvgIcon from '@/components/SvgIcon.vue'
+//引入注册脚本
+import 'virtual:svg-icons-register';
+
+const app = createApp(App);
+for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+  app.component(key, component);
+}
+
+app.use(ElementPlus, {
+  locale: zhCn,
+});
+app.use(router);
+app.use(store);
+
+// 注册全局SvgIcon组件
+app.component('SvgIcon',SvgIcon)
+
+app.mount("#app");
+app.config.devtools = true;

+ 88 - 0
src/router/index.js

@@ -0,0 +1,88 @@
+import {
+  createRouter,
+  createWebHistory,
+  createWebHashHistory,
+} from "vue-router";
+import Layout from "@/layout/index.vue";
+import store from "@/store";
+
+import { ElMessage, ElMessageBox } from "element-plus";
+const routes = [
+  {
+    path: "/login",
+    component: () => import("@/views/login/index.vue"),
+  },
+  {
+    path: "/",
+    redirect: "/login",
+  },
+  {
+    path: "/process",
+    name: "process",
+    component: Layout,
+    children: [
+      {
+        path: "home",
+        name: "home",
+        component: () => import("@/views/home/home.vue"),
+        meta: { title: "首页", icon: "home" },
+      },
+      {
+        path: "project",
+        name: "project",
+        component: () => import("@/views/project/project.vue"),
+        meta: { title: "项目管理", icon: "project" },
+      },
+      {
+        path: "role",
+        name: "role",
+        component: () => import("@/views/role/role.vue"),
+        meta: { title: "角色管理", icon: "role" },
+      },
+      {
+        path: "user",
+        name: "user",
+        component: () => import("@/views/user/user.vue"),
+        meta: { title: "用户管理", icon: "user" },
+      },
+      {
+        path: "system",
+        name: "system",
+        component: () => import("@/views/system/system.vue"),
+        meta: { title: "系统管理", icon: "system" },
+      },
+    ],
+  },
+];
+
+const router = createRouter({
+  history: createWebHashHistory("/jgcloudManage"), // 发布地址
+  routes: routes,
+});
+export default router;
+router.beforeEach(async (to, from, next) => {
+  const token = sessionStorage.getItem("token");
+  const userhead = sessionStorage.getItem("userhead");
+  let hasRoutes = store.state.user.hasRoutes;
+  next()
+  // if (token && userhead) {
+  //   // next();
+  //   if (!hasRoutes) {
+  //     console.log("第一次登录也触发");
+  //     store.commit("SET_ROUTES_STATE", true);
+  //     store.commit("ROLELIST", "");
+  //     next({ ...to, replace: true });
+  //     // next({ path: "/index" });
+  //     // next({ ...to });
+  //   } else {
+  //     console.log("没刷新页面跳转");
+  //   }
+  //   next();
+  // } else {
+  //   if (to.path == "/login") {
+  //     next();
+  //   } else {
+  //     next(`/login`);
+  //   }
+  // }
+});

+ 5 - 0
src/store/getters.js

@@ -0,0 +1,5 @@
+//store/getters
+const getters = {
+    age: state => state.user.age //导出age
+}
+export default getters;

+ 22 - 0
src/store/index.js

@@ -0,0 +1,22 @@
+//store/index.js
+
+import createPersistedstate from 'vuex-persistedstate' //持久化插件
+import { createStore } from 'vuex'; //引入vuex
+import user from './modules/user'; //引入modules的方法;
+import getters from './getters' //引入getters
+const store = createStore({
+    modules: {
+        user
+    },
+    getters,
+    // plugins: [
+    //     createPersistedstate({
+    //         // key: 'userStore', //存储持久状态的键。(默认:vuex)
+    //         paths: ['user'], //部分持续状态的任何路径的数组。如果不加,默认所有。
+    //         storage: window.sessionStorage //默认存储到localStorage,想要存储到sessionStorage
+    //     })
+    // ]
+
+})
+
+export default store;

+ 174 - 0
src/store/modules/user.js

@@ -0,0 +1,174 @@
+import axios from "axios";
+import router from "@/router";
+import Layout from "@/layout/index.vue";
+
+// 声明变量
+const state = {
+  collapse: false,
+  activeIndex: "", // 记录当前路由
+  username: "",
+  password: "",
+  api: "/jgcloudApi/jgcloud/api", // 线上
+  // api: "/jgcloudApi/jgcloud/api", // 测试
+  roleList: [
+    {
+      path: "home",
+      name: "home",
+      component: () => import("@/views/home/home.vue"),
+      meta: { title: "首页", icon: "home" },
+    },
+    {
+      path: "project",
+      name: "project",
+      component: () => import("@/views/project/project.vue"),
+      meta: { title: "项目管理", icon: "project" },
+    },
+    {
+      path: "role",
+      name: "role",
+      component: () => import("@/views/role/role.vue"),
+      meta: { title: "角色管理", icon: "role" },
+    },
+    {
+      path: "user",
+      name: "user",
+      component: () => import("@/views/user/user.vue"),
+      meta: { title: "用户管理", icon: "user" },
+    },
+    {
+      path: "system",
+      name: "system",
+      component: () => import("@/views/system/system.vue"),
+      meta: { title: "系统管理", icon: "system" },
+    },
+  ],
+  roles: [
+    
+  ],
+  hasRoutes: false,
+};
+
+// 修改变量(state不能直接赋值修改,只能通过mutations)
+const mutations = {
+  menuClose: (state, value) => {
+    state.collapse = value;
+  },
+  indexUp: (state, value) => {
+    sessionStorage.setItem("sidevarItem", value);
+    state.activeIndex = value;
+    // if (!value) {
+    //   let roleId = sessionStorage.getItem("roleId");
+    //   state.user.roles.forEach((item) => {
+    //     if (item.roles == roleId) {
+    //       state.activeIndex = `/jianguanCloud/${item.list[0]}`;
+    //       sessionStorage.setItem(
+    //         "sidevarItem",
+    //         `/jianguanCloud/${item.list[0]}`
+    //       );
+    //     }
+    //   });
+    // } else {
+    //   sessionStorage.setItem("sidevarItem", value);
+    // }
+  },
+  SET_ROUTES_STATE: (state, value) => {
+    state.hasRoutes = value;
+  },
+  userName: (state, value) => {
+    state.username = value;
+  },
+  passWord: (state, value) => {
+    state.password = value;
+  },
+  ROLELIST: (state, value) => {
+    const roleId = sessionStorage.getItem("roleId");
+    console.log(roleId);
+
+    if (roleId) {
+      state.roles.forEach((item) => {
+        if (item.roles == roleId) {
+          let modules = import.meta.glob("../views/**/*.vue");
+          router.addRoute({
+            path: "/jianguanCloud",
+            name: "jianguanCloud",
+            // component: modules[`../views/${item.name}/${item.name}.vue`],
+            component: Layout,
+            children: [],
+          });
+          item.list.forEach((item) => {
+            let arr = {
+              path: item.name,
+              name: item.name,
+              // component: modules[`../views/${item.name}/${item.name}.vue`],
+              component: () =>
+                import(`../../views/${item.name}/${item.name}.vue`),
+            };
+            // state[0].roleList.children.push(arr);
+            router.addRoute("jianguanCloud", arr);
+          });
+        }
+      });
+    }
+  },
+};
+// mutations的值由actions传入(异步)
+const actions = {
+  newsAsync: async ({ commit, state }) => {
+    let data = new FormData();
+    let res = await axios({
+      method: "post",
+      url: state.api + "/carBook/usermesUnRead.action",
+      headers: {
+        token: sessionStorage.getItem("token"),
+      },
+      data: data,
+    });
+    console.log(res, "系统消息");
+
+    commit("newsUp", res.data.u_count);
+    sessionStorage.setItem("newsNum", res.data.u_count);
+    console.log("vuex", res.data.u_count);
+  },
+  // 系统扫码时间
+  login: async ({ commit, state }, data) => {
+    return new Promise(async (resolve, reject) => {
+      let res = await axios({
+        method: "post",
+        url: state.api + "/login/Login",
+        data: data,
+      });
+
+      console.log(res, "登录账号");
+      if (res.data.code == 200) {
+        sessionStorage.setItem("userName", res.data.data.userName);
+        sessionStorage.setItem("token", res.data.data.token);
+        sessionStorage.setItem("roleId", res.data.data.roleId);
+        sessionStorage.setItem("userhead", res.data.data.userhead);
+        sessionStorage.setItem("provinceId", res.data.data.provinceId);
+        sessionStorage.setItem("cityId", res.data.data.cityId);
+        sessionStorage.setItem("districtId", res.data.data.districtId);
+        sessionStorage.setItem("address", res.data.data.address);
+        sessionStorage.setItem("addressId", res.data.data.addressId);
+
+        commit("userName", res.data.data.userName);
+        commit("ROLELIST", "");
+        if (res.data.data.roleId == 1) {
+          router.push({ path: "/jianguanCloud/project" });
+          commit("indexUp", "/jianguanCloud/project");
+        } else {
+          router.push({ path: "/jianguanCloud/home" });
+          commit("indexUp", "/jianguanCloud/home");
+        }
+        resolve("success");
+      } else {
+        resolve({ name: "reject", data: res });
+      }
+    });
+  },
+};
+
+export default {
+  state,
+  mutations,
+  actions,
+};

+ 89 - 0
src/style.css

@@ -0,0 +1,89 @@
+:root {
+  font-family: Inter, 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;
+  -webkit-text-size-adjust: 100%;
+}
+
+a {
+  font-weight: 500;
+  color: #646cff;
+  text-decoration: inherit;
+}
+a:hover {
+  color: #535bf2;
+}
+
+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;
+}
+
+.card {
+  padding: 2em;
+}
+
+/* #app {
+  max-width: 1280px;
+  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/style/mixin.scss

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

+ 74 - 0
src/utils/http.js

@@ -0,0 +1,74 @@
+/****   http.js   ****/
+// 导入封装好的axios实例
+import request from "./request";
+
+const http = {
+  /**
+   * methods: 请求
+   * @param url 请求地址
+   * @param params 请求参数
+   */
+
+  get(url, params, responseType, ContentType) {
+    const config = {
+      method: "get",
+      url: url,
+      responseType: responseType ? responseType : "",
+      //   headers: ContentType
+      //     ? ContentType
+      //     : { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8" },
+    };
+    if (params) {
+      config.params = params;
+    }
+
+    return request(config);
+  },
+  post(url, params, responseType, ContentType) {
+    const config = {
+      method: "post",
+      url: url,
+      //   headers: ContentType
+      //     ? ContentType
+      //     : {
+      //         "Content-Type": "application/json; charset=utf-8",
+      //       },
+    };
+    if (params) config.data = params;
+    return request(config);
+  },
+  put(url, params, ContentType) {
+    const config = {
+      method: "put",
+      url: url,
+      headers: ContentType
+        ? ContentType
+        : {
+            "Content-Type": "application/json; charset=utf-8",
+          },
+    };
+    if (params) {
+      // config.params = params
+      config.data = params;
+    }
+    return request(config);
+  },
+  delete(url, params, ContentType) {
+    const config = {
+      method: "delete",
+      url: url,
+      headers: ContentType
+        ? ContentType
+        : {
+            "Content-Type": "application/json; charset=utf-8",
+          },
+    };
+    if (params) {
+      // config.params = params
+      config.data = params;
+    }
+    return request(config);
+  },
+};
+//导出
+export default http;

+ 23 - 0
src/utils/loadMap.js

@@ -0,0 +1,23 @@
+/**
+ * 动态加载百度地图api函数
+ * @param {String} ak  百度地图AK,必传
+ */
+export default function loadBMap(ak) {
+  return new Promise((resolve, reject) => {
+    if (typeof window.BMap !== "undefined") {
+      resolve(window.BMap);
+      return true;
+    }
+    window.onBMapCallback = function () {
+      resolve(window.BMap);
+      console.log("百度地图脚本初始化成功...");
+      return true;
+    };
+    const script = document.createElement("script");
+    script.type = "text/javascript";
+    script.src = `https://api.map.baidu.com/api?v=3.0&ak=${ak}&callback=onBMapCallback&s=1`;
+    script.onerror = reject;
+    document.head.appendChild(script);
+    return true;
+  });
+}

+ 47 - 0
src/utils/request.js

@@ -0,0 +1,47 @@
+import axios from "axios";
+const service = axios.create({
+  baseURL: import.meta.env.VITE_BASE_API, //接口统一域名
+  timeout: 6000, //设置超时
+});
+
+// request拦截器
+service.interceptors.request.use(
+  (config) => {
+    // config.data = JSON.stringify(config.data); //数据转化,也可以使用qs转换
+    // config.headers = {
+    //     'Content-Type': 'application/json' //配置请求头
+    // }
+    // config.headers = { 'Content-Type': 'application/x-www-form-urlencoded' } //配置请求头
+    //如有需要:注意使用token的时候需要引入cookie方法或者用本地localStorage等方法,推荐js-cookie
+    const token = sessionStorage.getItem("token"); //这里取token之前,你肯定需要先拿到token,存一下
+    const user_head = sessionStorage.getItem("userhead"); //这里取token之前,你肯定需要先拿到token,存一下
+    if (token && user_head) {
+      // config.params = { 'admin_token': token } //如果要求携带在参数中
+      config.headers["token"] = token; //如果要求携带在请求头中
+      config.headers["user_head"] = user_head; //如果要求携带在请求头中
+    }
+    return config;
+  },
+  (error) => {
+    Promise.reject(error);
+  }
+);
+// 响应拦截器
+service.interceptors.response.use(
+  (response) => {
+    const res = response.data;
+
+    return response.data;
+  },
+  (error) => {
+    /* Message({
+                      message: '服务器调用错误',
+                      type: 'error',
+                      duration: 5 * 1000
+                    }) */
+    console.log(error);
+
+    return Promise.reject(error);
+  }
+);
+export default service;

+ 22 - 0
src/utils/rsa.js

@@ -0,0 +1,22 @@
+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);
+}
+

文件差異過大導致無法顯示
+ 1648 - 0
src/views/home/home.vue


+ 511 - 0
src/views/login/index.vue

@@ -0,0 +1,511 @@
+<template>
+  <div class="box">
+    <div class="content_box">
+      <div class="left">
+        <div class="loginForm">
+          <div class="title">
+            <span>项目过程管理系统</span>
+          </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
+                name="user"
+                :prefix-icon="User"
+                v-model="ruleForm.user"
+                placeholder="请输入登录名"
+                autocomplete="off"
+              />
+            </el-form-item>
+            <el-form-item label="" prop="pass">
+              <el-input
+                name="password"
+                :prefix-icon="Lock"
+                show-password
+                v-model="ruleForm.pass"
+                type="password"
+                autocomplete="off"
+                placeholder="请输入登录密码"
+              />
+            </el-form-item>
+            <el-checkbox
+              name="checked"
+              v-model="checked"
+              class="remeberPwd el-form-item"
+              >记住密码</el-checkbox
+            >
+            <el-form-item class="options">
+              <el-button
+                :loading="loading"
+                type="primary"
+                color="rgba(59, 140, 241, 1)"
+                @click="submitForm(ruleFormRef)"
+                >登录</el-button
+              >
+            </el-form-item>
+          </el-form>
+        </div>
+      </div>
+    </div>
+    <div class="loginform"></div>
+    <el-dialog
+      class="infoContent"
+      v-model="infoContentVisible"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      title="详情"
+      align-center
+      width="800"
+      :before-close="cancelInfoContent"
+    >
+      <div class="content">
+        <el-tooltip
+          class="box-item"
+          effect="dark"
+          :content="infoList.fileTitle"
+          placement="top"
+          v-if="infoList.fileTitle.length > 16"
+        >
+          <h2>{{ infoList.fileTitle }}</h2>
+        </el-tooltip>
+        <h2 v-else>{{ infoList.fileTitle }}</h2>
+        <p class="time">{{ infoList.createTime }}</p>
+        <div class="info" v-html="infoList.fileContent"></div>
+        <div class="upload">
+          <span class="title">附件下载 : </span>
+          <ul>
+            <li v-for="i in infoList.fileUrls">
+              <span class="name">{{ pdfName(i) }}</span>
+              <span class="download">
+                <a :href="i" target="_blank">下载</a>
+              </span>
+            </li>
+          </ul>
+        </div>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import {
+  ref,
+  watch,
+  reactive,
+  onMounted,
+  onBeforeMount,
+  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 adminApi from "@/api/admin.js";
+import { useStore } from "vuex";
+import axios from "axios";
+import { defineComponent } from "vue";
+import { Vue3SeamlessScroll } from "vue3-seamless-scroll";
+
+const router = useRouter();
+const api = ref("");
+const roleId = ref();
+const store = useStore();
+
+const list = ref([]);
+const currentPage = ref(1); // 当前页
+const pageSize = ref(10);
+const fileTypeData = ref([
+  { name: "行业动态", id: 1 },
+  { name: "政策文件", id: 2 },
+]); // 文件类型
+const activeFile = ref(1); // 文件类型id
+
+// 详情弹窗
+const infoContentVisible = ref(false);
+const infoList = ref();
+
+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 getList = async () => {
+  let data = {
+    currentPage: currentPage.value, // 当前页
+    pageCount: pageSize.value, // 一页数据条数
+    fileLevel: 1, // 文件等级id
+    fileType: 1, // 文件类型id
+  };
+  console.log(data);
+  let res = await axios({
+    method: "get",
+    url: api.value + "/jgcloudFile/queryHomePageJgcloudFiles",
+    headers: {
+      token: sessionStorage.getItem("token"),
+      user_head: sessionStorage.getItem("userhead"),
+    },
+    params: data,
+  });
+  console.log(res, "首页文件分页");
+  if (res.data.code == 200) {
+    res.data.data.list.forEach((item) => {
+      let arr = item.createTime.split("-");
+      item.year = arr[0];
+      item.month = arr[1] + "-" + arr[2];
+    });
+    list.value = res.data.data.list;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  }
+};
+// 配置信息
+const info = async () => {
+  let res = await axios({
+    method: "get",
+    url: api.value + "/jgcloudSetting/homePageFileDatas",
+  });
+  console.log(res, "首页文件数据");
+  if (res.data.code == 200) {
+    fileTypeData.value = res.data.data;
+    res.data.data[0].files.forEach((item) => {
+      let arr = item.createTime.split("-");
+      item.year = arr[0];
+      item.month = arr[1] + "-" + arr[2];
+    });
+    list.value = res.data.data[0].files;
+    activeFile.value = res.data.data[0].id;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  }
+};
+const handleClick = (pane, ev) => {
+  console.log(pane.props.label, ev);
+  fileTypeData.value.forEach((item) => {
+    if (item.name == pane.props.label) {
+      item.files.forEach((item) => {
+        let arr = item.createTime.split("-");
+        item.year = arr[0];
+        item.month = arr[1] + "-" + arr[2];
+      });
+      list.value = item.files;
+    }
+  });
+};
+// cos桶中地址栏中中文地址乱码转换为中文
+const pdfName = (value) => {
+  let index = decodeURIComponent(value).lastIndexOf("/") + 1;
+  let name = decodeURIComponent(value).slice(index, value.length);
+  console.log(name);
+  return name;
+};
+// 详情点击
+const infoClick = (row) => {
+  infoContentVisible.value = true;
+  console.log(row);
+  infoList.value = row;
+};
+const cancelInfoContent = () => {
+  infoContentVisible.value = false;
+};
+const submitForm = (formEl) => {
+  // router.push({
+  //   path: `/seniorCelebrity/order`,
+  // });
+  // sessionStorage.setItem("sidevarItem", 1);
+  // store.commit("indexUp", 1);
+  if (!formEl) return;
+  formEl.validate(async (valid) => {
+    if (valid) {
+      loading.value = true;
+
+      let data = {
+        account: ruleForm.user,
+        password: ruleForm.pass,
+      };
+      let res;
+      try {
+        res = await store.dispatch("login", data);
+      } catch (e) {
+        console.log(e);
+      }
+      console.log(res);
+      let menuItem = sessionStorage.getItem("sidevarItem");
+      if (res == "success") {
+        if (checked.value == true) {
+          sessionStorage.setItem("user", ruleForm.user);
+          sessionStorage.setItem("pass", ruleForm.pass);
+        }
+        ElMessage({
+          type: "success",
+          showClose: true,
+          message: "登录成功",
+          center: true,
+        });
+      } else {
+        loading.value = false;
+        ElMessage({
+          type: "error",
+          showClose: true,
+          message: res.data.data.message,
+          center: true,
+        });
+      }
+    } else {
+      return false;
+    }
+  });
+};
+const Enters = (e) => {
+  // console.log("按键:", e.key);
+  if (e.key == "Enter") {
+    submitForm(ruleFormRef.value);
+  }
+};
+onBeforeMount(() => {
+  api.value = store.state.user.api;
+  if (sessionStorage.getItem("user") && sessionStorage.getItem("pass")) {
+    checked.value = true;
+    ruleForm.user = sessionStorage.getItem("user");
+    ruleForm.pass = sessionStorage.getItem("pass");
+  }
+
+  roleId.value = sessionStorage.getItem("roleId");
+  // getList();
+  info();
+  document.addEventListener("keyup", Enters);
+});
+onUnmounted(() => {
+  document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style lang="scss" scoped>
+.box {
+  width: 100%;
+  height: 100vh;
+  min-width: 1500px;
+  min-height: 800px;
+  display: flex;
+  align-items: center;
+  // justify-content: center;
+  background-image: url("../../assets/images/bg.png");
+  background-size: cover;
+  .content_box {
+    width: 50%;
+    height: 625px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    .left {
+      // box-shadow: 39px 30px 58px 0px rgba(8, 53, 115, 0.55);
+      width: 50%;
+      min-width: 525px;
+      height: 100%;
+      background: rgba(239, 246, 255, 1);
+      box-shadow: 0px 0px 0px rgba(0, 0, 0, 0.1),
+        0px 18.59px 39.36px rgba(23, 57, 222, 0.25);
+      border-top: 6px solid rgba(77, 105, 244, 1);
+      border-bottom-left-radius: 20px;
+      border-bottom-right-radius: 20px;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      .loginForm {
+        width: 470px;
+        // flex: 1;
+        height: 520px;
+        // background-color: #fff;
+        border-radius: 6px;
+        .title {
+          margin-top: 15px;
+          margin-bottom: 50px;
+          span {
+            font-size: 45px;
+            font-weight: 800;
+            letter-spacing: 2px;
+            color: rgb(255, 255, 255);
+          }
+        }
+        .el-form {
+          width: 100%;
+          display: flex;
+          flex-direction: column;
+          .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: 6px;
+                  // background: rgba(0, 0, 0, 0.1);
+                  .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;
+              }
+            }
+
+            .el-button--primary {
+              width: 380px;
+            }
+          }
+          .options {
+            margin-top: 50px;
+          }
+          .remeberPwd {
+            height: 30px;
+            margin-bottom: 30px;
+            color: #fff;
+            :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;
+              color: #fff;
+            }
+          }
+        }
+        .policeNum {
+          color: #fff;
+          white-space: nowrap;
+          transform: translateX(-30px);
+        }
+      }
+    }
+  }
+  .loginform {
+    width: 50%;
+    height: 100%;
+    background-image: url("../../assets/images/login.png");
+    background-size: cover;
+  }
+
+  // 详情
+  :deep(.infoContent) {
+    height: 720px;
+    border-radius: 11px;
+
+    .el-dialog__header {
+      border-radius: 11px 11px 0 0;
+      background: rgba(237, 241, 245, 1);
+      font-weight: 600;
+      margin: 0;
+      height: 32px;
+
+      .el-dialog__headerbtn {
+        outline: none;
+      }
+    }
+
+    .el-dialog__body {
+      padding: 0 30px;
+      color: #000;
+      height: calc(100% - 62px);
+      .content {
+        height: calc(100% - 30px);
+        overflow: auto;
+
+        h2 {
+          text-align: center;
+          margin: 20px auto;
+          width: 500px;
+          color: #000;
+          font-size: 26px;
+          white-space: nowrap;
+          text-overflow: ellipsis;
+          overflow: hidden;
+        }
+        .time {
+          font-size: 16px;
+          padding: 0;
+          margin: 0;
+        }
+        .info {
+          img {
+            max-width: 100%;
+          }
+        }
+        .upload {
+          .title {
+            padding: 10px 200px 10px 0;
+            border-top: 1px solid #000;
+          }
+          ul {
+            list-style: none;
+            li {
+              .name {
+                margin-right: 20px;
+              }
+              .download {
+                a {
+                  color: rgb(0, 149, 255);
+                  cursor: pointer;
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style>

文件差異過大導致無法顯示
+ 1437 - 0
src/views/project/project.vue


+ 781 - 0
src/views/role/role.vue

@@ -0,0 +1,781 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <el-icon :size="23" class="camera"><VideoCameraFilled /></el-icon> -->
+      <span class="cameratxt">角色管理</span>
+    </div>
+    <div class="middle">
+      <div class="filter">
+        <div class="condition">
+          <span>角色名称 </span>
+          <el-input
+            clearable
+            v-model="searchInput.name"
+            @clear="searchBtn"
+            class="w-50 m-2"
+            placeholder="请输入角色名称"
+            style="width: 250px"
+          />
+        </div>
+        <div class="condition">
+          <span>创建时间 </span>
+          <el-date-picker
+            v-model="searchInput.createTime"
+            type="daterange"
+            unlink-panels
+            range-separator="-"
+            start-placeholder="起始时间"
+            end-placeholder="截止时间"
+            format="YYYY-MM-DD"
+            value-format="YYYY-MM-DD"
+            :prefix-icon="Calendar"
+            placeholder="请选择日期"
+            style="width: 280px"
+            @change="searchBtn"
+          />
+        </div>
+        <el-button
+          style="margin-left: 20px"
+          type="primary"
+          class="search"
+          @click="searchBtn"
+          >查询</el-button
+        >
+        <el-button
+          style="margin-left: 20px"
+          class="reset"
+          @click="resetSearchBtn"
+          >重置</el-button
+        >
+      </div>
+      <!-- 按钮列表 -->
+      <div class="gongneng">
+        <el-button @click="addProjectbtn" class="add"
+          ><el-icon> <CirclePlus /> </el-icon><span>创建角色</span></el-button
+        >
+        <el-button @click="projectImportBtn" class="imp"
+          ><el-icon><Upload /></el-icon><span>批量导入</span></el-button
+        >
+        <!-- <el-button @click="projectExportBtn" class="add"
+          ><el-icon><Download /></el-icon><span>项目导出</span></el-button
+        > -->
+      </div>
+    </div>
+    <div class="scroll">
+      <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 type="selection" width="55" /> -->
+          <el-table-column align="center" prop="createTime" label="序号" />
+          <el-table-column align="center" prop="createTime" label="角色级别" />
+          <el-table-column align="center" prop="createUser" label="父级角色" />
+          <el-table-column align="center" prop="createUser" label="角色名称" />
+          <el-table-column align="center" prop="createTime" label="创建时间" />
+          <el-table-column align="center" label="操作" width="220">
+            <template #default="scope">
+              <div class="options">
+                <div class="reset" @click="reset(scope.row)">添加</div>
+                <div class="look" @click="edit(scope.row)">编辑</div>
+                <el-popconfirm
+                  width="220"
+                  confirm-button-text="确认"
+                  cancel-button-text="取消"
+                  :icon="InfoFilled"
+                  icon-color="#f89626"
+                  title="是否删除此数据?"
+                  @confirm="del(scope.row)"
+                  @cancel="cancelEvent"
+                >
+                  <template #reference>
+                    <div class="del">删除</div>
+                  </template>
+                </el-popconfirm>
+              </div>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <!-- 新增账号 -->
+        <el-dialog
+          class="account"
+          v-model="accountVisible"
+          :close-on-click-modal="false"
+          :close-on-press-escape="false"
+          :title="accountTitle"
+          align-center
+          width="600"
+          :before-close="cancelAccount"
+        >
+          <el-form
+            ref="accountRef"
+            :model="accountRuleForm"
+            :rules="accountRules"
+            class="demo-ruleForm"
+            label-width="80px"
+            :size="formSize"
+            label-position="right"
+            status-icon
+          >
+            <el-form-item label="角色" prop="role">
+              <el-select
+                v-model="accountRuleForm.role"
+                placeholder="请选择"
+                style="width: 220px"
+                @change="roleChange"
+                :disabled="roleId == 2 || roleId == 3 || roleId == 4"
+              >
+                <el-option
+                  v-for="item in roleData"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="用户名" prop="user">
+              <el-input
+                v-model="accountRuleForm.user"
+                placeholder="建设单位可填写单位名称"
+                clearable
+                style="width: 220px"
+              />
+            </el-form-item>
+            <!-- <span class="resetPassword"
+              >注:建设单位时用户名可填写单位名称</span
+            > -->
+            <el-form-item label="联系人" prop="">
+              <el-input
+                v-model="accountRuleForm.contacts"
+                placeholder="请输入联系人"
+                clearable
+              />
+            </el-form-item>
+            <el-form-item label="联系电话" prop="">
+              <el-input
+                v-model="accountRuleForm.phone"
+                placeholder="请输入联系电话"
+                clearable
+              />
+            </el-form-item>
+            <el-form-item label="账号" prop="account">
+              <el-input
+                v-model="accountRuleForm.account"
+                placeholder="请输入账号"
+                clearable
+              />
+            </el-form-item>
+            <span class="resetPassword"
+              >注:新增账号统一默认密码为JXSJG_123</span
+            >
+            <el-form-item class="options">
+              <el-button
+                class="queding"
+                type="primary"
+                @click="confirmAccount(accountRef)"
+              >
+                &nbsp;&nbsp;确认&nbsp;&nbsp;
+              </el-button>
+              <el-button class="quxiao" @click="cancelAccount"
+                >&nbsp;&nbsp;取消&nbsp;&nbsp;</el-button
+              >
+            </el-form-item>
+          </el-form>
+        </el-dialog>
+
+        <!-- 项目导入 -->
+        <el-dialog
+          class="projectImport"
+          v-model="projectImportVisible"
+          :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="projectRef"
+            :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(projectRef)"
+            >
+              &nbsp;&nbsp;确认导入&nbsp;&nbsp;
+            </el-button>
+            <el-button @click="cancelProjectImport"
+              >&nbsp;&nbsp;取消导入&nbsp;&nbsp;</el-button
+            >
+          </div>
+        </el-dialog>
+      </div>
+
+      <!-- 分页组件 -->
+      <el-pagination
+        background
+        v-model:current-page="currentPage"
+        v-model:page-size="pageSize"
+        :page-sizes="[10, 20, 30, 40]"
+        layout="total,sizes, prev, pager, next, jumper, slot"
+        :total="total"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import {
+  ref,
+  reactive,
+  nextTick,
+  onMounted,
+  watch,
+  onBeforeMount,
+  onUnmounted,
+} from "vue";
+import COS from "cos-js-sdk-v5";
+import { useRouter } from "vue-router";
+import { genFileId, ElMessage, ElMessageBox } from "element-plus";
+import { Calendar } from "@element-plus/icons-vue";
+import { dayjs } from "element-plus";
+import lodash, { reduce } from "lodash";
+import axios from "axios";
+import { useStore } from "vuex";
+
+const store = useStore();
+const router = useRouter();
+
+const tableData = reactive({
+  list: [{}],
+});
+
+const searchInput = reactive({
+  name: "", // 项目名称
+  createTime: "", // 创建时间
+}); // 搜索按钮数据
+
+const currentPage = ref(1); // 当前页
+const pageSize = ref(10);
+const total = ref(40); // 当前总数
+const selectData = reactive({
+  list: [],
+}); // 多选框选择的数据
+const api = ref("");
+
+// 新增账号 (--------------------------------------------------------)
+const accountVisible = ref(false);
+const accountTitle = ref();
+const accountRef = ref();
+
+const accountRuleForm = reactive({
+  role: "", // 角色
+  user: "", // 用户名
+  contacts: "", // 联系人
+  phone: "", // 联系电话
+  account: "", // 账号
+  id: "",
+});
+// 新增账号表单验证
+const accountRules = reactive({
+  role: [{ required: true, message: "角色不能为空", trigger: "blur" }],
+  user: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
+  account: [{ required: true, message: "账号不能为空", trigger: "blur" }],
+});
+
+// 项目导入
+const projectImportVisible = ref(false);
+const projectRef = ref();
+const projectFile = ref();
+
+// 获取账户列表
+const getList = async () => {
+  loading.value = true;
+  let data = {
+    currentPage: currentPage.value, // 当前页
+    pageCount: pageSize.value, // 一页数据条数
+    projectName: searchInput.name,
+    pageType: 1,
+    permitNum: searchInput.LicenseCode, // 施工许可证编码
+    projectUnit: searchInput.unit, // 建设单位
+    projectPurpose: searchInput.purpose, // 项目用途
+    structureType: searchInput.structureType, // 结构类型
+    projectType: searchInput.projectType, // 项目类型
+  };
+
+  if (searchInput.createTime) {
+    data.startDate = searchInput.createTime[0];
+    data.endDate = searchInput.createTime[1];
+  }
+  let res = await axios({
+    method: "get",
+    url: api.value + "/jgcloudProject/queryPageJgcloudProject",
+    headers: {
+      token: sessionStorage.getItem("token"),
+      user_head: sessionStorage.getItem("userhead"),
+    },
+    params: data,
+  });
+  console.log(res, "项目分页数据");
+  if (res.data.code == 200) {
+    tableData.list = res.data.data.list;
+    total.value = res.data.data.totalCount;
+    loading.value = false;
+  } else {
+    loading.value = false;
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  }
+};
+
+// 搜索功能
+const searchBtn = lodash.debounce(async () => {
+  getList();
+}, 300);
+
+// 角色新增
+const addProjectbtn = () => {
+  accountVisible.value = true;
+  accountTitle.value = "角色新增";
+};
+// 取消角色新增
+const cancelAccount = () => {
+  accountVisible.value = false;
+  accountRef.value.resetFields();
+};
+
+//编辑项目
+const edit = (row) => {
+  accountVisible.value = true;
+  accountTitle.value = "角色编辑";
+  console.log(row);
+};
+// 删除pdf文件 (装配式相关附件)
+const pdfDel = (value, uid) => {};
+
+// 导入项目
+const projectImportConfirm = async () => {
+  let data = new FormData();
+  data.set("file", projectFile.value);
+  data.set("pageType", 1);
+  let res = await axios({
+    method: "POST",
+    url: api.value + "/jgcloudProject/importExcelProject",
+    headers: {
+      token: sessionStorage.getItem("token"),
+      user_head: sessionStorage.getItem("userhead"),
+    },
+    data: data,
+  });
+  console.log(res, "导入项目");
+  if (res.data.code == 200) {
+    projectImportVisible.value = false;
+    getList();
+    if (projectRef.value) {
+      projectRef.value.clearFiles();
+    }
+    ElMessage({
+      type: "success",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  }
+};
+const cancelProjectImport = () => {
+  projectRef.value.clearFiles();
+  projectImportVisible.value = false;
+};
+
+// (--------------------------------------------------)
+// cos桶中地址栏中中文地址乱码转换为中文
+const pdfName = (value) => {
+  // console.log(decodeURIComponent(value));
+  // console.log(decodeURIComponent(value).length);
+  let index = decodeURIComponent(value).lastIndexOf("/") + 1;
+  let name = decodeURIComponent(value).slice(index, value.length);
+  console.log(name);
+  return name;
+};
+
+// 多选框功能
+const handleSelectionChange = (val) => {
+  console.log(val);
+  selectData.list = val;
+};
+
+// 表格斑马纹颜色修改
+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(() => {
+  api.value = store.state.user.api;
+});
+onUnmounted(() => {
+  // document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  min-width: 1000px;
+  width: calc(100vw - 260px);
+  height: calc(100vh - 105px);
+  margin: 20px auto;
+  background-color: #fff;
+  color: #fff;
+  display: flex;
+  flex-direction: column;
+  box-shadow: 0px 3px 10px rgba(0, 97, 255, 0.2);
+
+  .left {
+    // width: calc(100wh - 40px);
+    display: flex;
+    align-items: center;
+    height: 60px;
+    margin: 0 30px;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+
+    .cameratxt {
+      display: block;
+      height: 60px;
+      line-height: 60px;
+    }
+  }
+  .middle {
+    width: calc(100% - 60px);
+    margin: 0 auto;
+    color: #000;
+    
+    // border-bottom: 1px solid rgb(231, 231, 231);
+    .filter {
+      display: flex;
+      flex-wrap: wrap;
+      align-items: center;
+      margin: 10px 0 0 0;
+
+      .search {
+        margin-left: 0 !important;
+        color: #fff;
+        background: linear-gradient(
+          90deg,
+          rgba(33, 107, 255, 1) 0%,
+          rgba(102, 182, 255, 1) 100%
+        );
+        border: none;
+      }
+
+      .reset {
+        border: 1px solid rgba(38, 151, 255, 1);
+        color: rgba(38, 151, 255, 1);
+      }
+
+      .condition {
+        display: flex;
+        align-items: center;
+        margin: 10px 30px 10px 0;
+
+        :deep(.el-input .el-input__inner) {
+          font-size: 14px;
+        }
+
+        span {
+          margin: 0 10px 0 0;
+        }
+      }
+    }
+
+    .gongneng {
+      margin: 10px 0;
+
+      // .el-button {
+      //   color: #fff;
+      //   border: none;
+      // }
+      .add {
+        color: #fff;
+        border: none;
+        background: linear-gradient(
+          90deg,
+          rgba(33, 107, 255, 1) 0%,
+          rgba(102, 182, 255, 1) 100%
+        );
+      }
+      .imp {
+        border: 1px solid rgba(33, 107, 255, 1);
+        color: rgba(33, 107, 255, 1);
+      }
+    }
+  }
+  .scroll {
+    flex: 1;
+    overflow: auto;
+  }
+  .footer {
+    width: 96%;
+    height: calc(100% - 82px);
+    margin: 10px auto 10px;
+
+    .el-table--fit {
+      height: 100%;
+      :deep(.el-table__header-wrapper) {
+        background-color: #000;
+        font-size: 14px;
+        color: #000;
+
+        .cell {
+          color: #000;
+        }
+      }
+
+      :deep(.el-table__row) {
+        height: 50px;
+        font-size: 14px;
+        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;
+
+        .reset {
+          margin-right: 15px;
+          color: rgba(30, 125, 251, 1);
+          cursor: pointer;
+        }
+
+        .look {
+          margin-right: 15px;
+          color: rgba(30, 125, 251, 1);
+          cursor: pointer;
+        }
+
+        .del {
+          color: rgba(212, 48, 48, 1);
+          cursor: pointer;
+        }
+      }
+    }
+
+    // 项目新增样式
+    :deep(.account) {
+      // height: 300px;
+      border-radius: 11px;
+
+      .el-dialog__header {
+        border-radius: 11px 11px 0 0;
+        background: rgba(237, 241, 245, 1);
+        font-weight: 600;
+        margin: 0;
+        height: 32px;
+
+        .el-dialog__headerbtn {
+          outline: none;
+        }
+      }
+
+      .el-dialog__body {
+        padding: 30px 30px 0 30px;
+        .el-form {
+          .formLi {
+            width: 100%;
+            margin-bottom: 5px;
+            display: flex;
+            .el-form-item {
+              .el-input {
+                width: 120px;
+              }
+              &:nth-child(2) {
+                .el-form-item__label {
+                  display: none;
+                }
+                .el-form-item__content {
+                  margin-left: 10px !important;
+                }
+              }
+              &:nth-child(3) {
+                .el-form-item__label {
+                  display: none;
+                }
+                .el-form-item__content {
+                  margin-left: 10px !important;
+                }
+              }
+            }
+          }
+          .resetPassword {
+            color: rgba(43, 151, 252, 1);
+            padding-left: 40px;
+            display: inline-block;
+            margin-bottom: 15px;
+          }
+          .options {
+            margin: 60px 0 40px 0;
+            .el-form-item__content {
+              display: flex;
+              flex-direction: row-reverse;
+              .queding {
+                margin-left: 20px;
+                background: linear-gradient(
+                  90deg,
+                  rgba(33, 107, 255, 1) 0%,
+                  rgba(102, 182, 255, 1) 100%
+                );
+                border: none;
+              }
+              .quxiao {
+                border: 1px solid rgba(43, 151, 252, 1);
+                color: rgba(43, 151, 252, 1);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .el-pagination {
+    width: calc(100vw - 300px);
+    margin: 20px auto;
+    justify-content: flex-end;
+    :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;
+      width: 100px;
+    }
+
+    :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(111, 182, 184, 1);
+      color: rgba(0, 149, 255, 1);
+      border: 1px solid rgba(0, 149, 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;
+    }
+  }
+}
+
+.el-input {
+  width: 192px;
+}
+</style>
+<style lang="scss">
+.usePrefabricate {
+  height: 488px;
+  .el-select-dropdown__wrap {
+    max-height: 100%;
+  }
+}
+</style>

+ 615 - 0
src/views/system/system.vue

@@ -0,0 +1,615 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <el-icon :size="23" class="camera"><VideoCameraFilled /></el-icon> -->
+      <span class="cameratxt">系统设置</span>
+    </div>
+    <div class="middle">
+      <div class="roles">考核规则</div>
+      <div class="filter">
+        <el-checkbox v-model="checked1" label="提醒次数扣分" size="large" />
+        <el-input
+          v-model="accountRuleForm.contacts"
+          placeholder="请输入次数"
+          clearable
+        />
+        <span class="grade">分/次</span>
+      </div>
+      <div class="filter">
+        <el-checkbox v-model="checked1" label="预警次数扣分" size="large" />
+        <el-input
+          v-model="accountRuleForm.contacts"
+          placeholder="请输入次数"
+          clearable
+        />
+        <span class="grade">分/次</span>
+      </div>
+      <div class="roles">考核维度管理</div>
+      <!-- 按钮列表 -->
+      <div class="gongneng">
+        <el-button @click="addProjectbtn" class="add"
+          ><el-icon> <CirclePlus /> </el-icon
+          ><span>添加考核维度</span></el-button
+        >
+      </div>
+    </div>
+    <div class="scroll">
+      <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: '35px',
+            border: 0,
+          }"
+        >
+          <!-- <el-table-column type="selection" width="55" /> -->
+          <el-table-column align="center" prop="createTime" label="维度名称" />
+          <el-table-column align="center" prop="createTime" label="维度权重" />
+          <el-table-column align="center" prop="createUser" label="创建人" />
+          <el-table-column align="center" label="操作" width="220">
+            <template #default="scope">
+              <div class="options">
+                <div class="look" @click="edit(scope.row)">编辑</div>
+                <el-popconfirm
+                  width="150"
+                  confirm-button-text="确认"
+                  cancel-button-text="取消"
+                  :icon="InfoFilled"
+                  icon-color="#f89626"
+                  title="是否删除此数据?"
+                  @confirm="del(scope.row)"
+                  @cancel="cancelEvent"
+                >
+                  <template #reference>
+                    <div class="del">删除</div>
+                  </template>
+                </el-popconfirm>
+              </div>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <div class="gongneng">
+          <el-button @click="addProjectbtn" class="add"
+            ><span>修改</span></el-button
+          >
+          <el-button @click="projectImportBtn" class="imp"
+            ><span>取消</span></el-button
+          >
+          <el-button @click="addProjectbtn" class="add"
+            ><span>保存</span></el-button
+          >
+        </div>
+        <!-- 添加考核维度 -->
+        <el-dialog
+          class="account"
+          v-model="accountVisible"
+          :close-on-click-modal="false"
+          :close-on-press-escape="false"
+          :title="accountTitle"
+          align-center
+          width="600"
+          :before-close="cancelAccount"
+        >
+          <el-form
+            ref="accountRef"
+            :model="accountRuleForm"
+            :rules="accountRules"
+            class="demo-ruleForm"
+            label-width="120px"
+            :size="formSize"
+            label-position="right"
+            status-icon
+          >
+            <el-form-item label="维度名称" prop="user">
+              <el-input
+                v-model="accountRuleForm.user"
+                placeholder="请输入维度名称"
+                clearable
+                style="width: 220px"
+              />
+            </el-form-item>
+            <!-- <span class="resetPassword"
+              >注:建设单位时用户名可填写单位名称</span
+            > -->
+            <el-form-item label="权重" prop="">
+              <el-input
+                v-model="accountRuleForm.contacts"
+                placeholder="请输入权重"
+                clearable
+              />
+            </el-form-item>
+            <el-form-item label="是否属于进度" prop="account">
+              <el-radio-group v-model="radio1">
+                <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 class="options">
+              <el-button
+                class="queding"
+                type="primary"
+                @click="confirmAccount(accountRef)"
+              >
+                &nbsp;&nbsp;确认&nbsp;&nbsp;
+              </el-button>
+              <el-button class="quxiao" @click="cancelAccount"
+                >&nbsp;&nbsp;取消&nbsp;&nbsp;</el-button
+              >
+            </el-form-item>
+          </el-form>
+        </el-dialog>
+      </div>
+
+      <!-- 分页组件 -->
+      <!-- <el-pagination
+        background
+        v-model:current-page="currentPage"
+        v-model:page-size="pageSize"
+        :page-sizes="[10, 20, 30, 40]"
+        layout="total,sizes, prev, pager, next, jumper, slot"
+        :total="total"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      /> -->
+    </div>
+  </div>
+</template>
+
+<script setup>
+import {
+  ref,
+  reactive,
+  nextTick,
+  onMounted,
+  watch,
+  onBeforeMount,
+  onUnmounted,
+} from "vue";
+import COS from "cos-js-sdk-v5";
+import { useRouter } from "vue-router";
+import { genFileId, ElMessage, ElMessageBox } from "element-plus";
+import { Calendar } from "@element-plus/icons-vue";
+import { dayjs } from "element-plus";
+import lodash, { reduce } from "lodash";
+import axios from "axios";
+import { useStore } from "vuex";
+
+const store = useStore();
+const router = useRouter();
+
+const tableData = reactive({
+  list: [{}],
+});
+
+const searchInput = reactive({
+  name: "", // 项目名称
+  createTime: "", // 创建时间
+}); // 搜索按钮数据
+
+const currentPage = ref(1); // 当前页
+const pageSize = ref(10);
+const total = ref(40); // 当前总数
+const selectData = reactive({
+  list: [],
+}); // 多选框选择的数据
+const api = ref("");
+
+// 新增账号 (--------------------------------------------------------)
+const accountVisible = ref(false);
+const accountTitle = ref();
+const accountRef = ref();
+
+const accountRuleForm = reactive({
+  role: "", // 角色
+  user: "", // 用户名
+  contacts: "", // 联系人
+  phone: "", // 联系电话
+  account: "", // 账号
+  id: "",
+});
+// 新增账号表单验证
+const accountRules = reactive({
+  role: [{ required: true, message: "角色不能为空", trigger: "blur" }],
+  user: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
+  account: [{ required: true, message: "账号不能为空", trigger: "blur" }],
+});
+
+// 项目导入
+const projectImportVisible = ref(false);
+const projectRef = ref();
+const projectFile = ref();
+
+// 获取账户列表
+const getList = async () => {
+  loading.value = true;
+  let data = {
+    currentPage: currentPage.value, // 当前页
+    pageCount: pageSize.value, // 一页数据条数
+    projectName: searchInput.name,
+    pageType: 1,
+    permitNum: searchInput.LicenseCode, // 施工许可证编码
+    projectUnit: searchInput.unit, // 建设单位
+    projectPurpose: searchInput.purpose, // 项目用途
+    structureType: searchInput.structureType, // 结构类型
+    projectType: searchInput.projectType, // 项目类型
+  };
+
+  if (searchInput.createTime) {
+    data.startDate = searchInput.createTime[0];
+    data.endDate = searchInput.createTime[1];
+  }
+  let res = await axios({
+    method: "get",
+    url: api.value + "/jgcloudProject/queryPageJgcloudProject",
+    headers: {
+      token: sessionStorage.getItem("token"),
+      user_head: sessionStorage.getItem("userhead"),
+    },
+    params: data,
+  });
+  console.log(res, "项目分页数据");
+  if (res.data.code == 200) {
+    tableData.list = res.data.data.list;
+    total.value = res.data.data.totalCount;
+    loading.value = false;
+  } else {
+    loading.value = false;
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  }
+};
+
+// 搜索功能
+const searchBtn = lodash.debounce(async () => {
+  getList();
+}, 300);
+
+// 添加考核维度
+const addProjectbtn = () => {
+  accountVisible.value = true;
+  accountTitle.value = "添加考核维度";
+};
+// 取消添加考核维度
+const cancelAccount = () => {
+  accountVisible.value = false;
+  accountRef.value.resetFields();
+};
+
+//编辑项目
+const edit = (row) => {
+  accountVisible.value = true;
+  accountTitle.value = "编辑考核维度";
+  console.log(row);
+};
+
+// (--------------------------------------------------)
+// cos桶中地址栏中中文地址乱码转换为中文
+const pdfName = (value) => {
+  // console.log(decodeURIComponent(value));
+  // console.log(decodeURIComponent(value).length);
+  let index = decodeURIComponent(value).lastIndexOf("/") + 1;
+  let name = decodeURIComponent(value).slice(index, value.length);
+  console.log(name);
+  return name;
+};
+
+// 多选框功能
+const handleSelectionChange = (val) => {
+  console.log(val);
+  selectData.list = val;
+};
+
+// 表格斑马纹颜色修改
+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(() => {
+  api.value = store.state.user.api;
+});
+onUnmounted(() => {
+  // document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  min-width: 1000px;
+  width: calc(100vw - 260px);
+  height: calc(100vh - 105px);
+  margin: 20px auto;
+  background-color: #fff;
+  color: #fff;
+  display: flex;
+  flex-direction: column;
+  box-shadow: 0px 3px 10px rgba(0, 97, 255, 0.2);
+
+  .left {
+    // width: calc(100wh - 40px);
+    display: flex;
+    align-items: center;
+    height: 60px;
+    margin: 0 30px;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+
+    .cameratxt {
+      display: block;
+      height: 60px;
+      line-height: 60px;
+    }
+  }
+  .middle {
+    width: calc(100% - 60px);
+    margin: 0 auto;
+
+    color: #000;
+    .roles {
+      padding: 10px 0 10px 0;
+      font-weight: 600;
+      letter-spacing:1px;
+    }
+    // border-bottom: 1px solid rgb(231, 231, 231);
+    .filter {
+      display: flex;
+      flex-wrap: wrap;
+      align-items: center;
+      margin: 0 0 10px 0;
+      .el-checkbox {
+        margin-right: 15px;
+        color: #000;
+      }
+      .grade {
+        font-size: 14px;
+        padding: 0 0 0 15px;
+      }
+    }
+
+    .gongneng {
+      margin: 8px 0;
+      // .el-button {
+      //   color: #fff;
+      //   border: none;
+      // }
+      .add {
+        color: #fff;
+        border: none;
+        background: linear-gradient(
+          90deg,
+          rgba(33, 107, 255, 1) 0%,
+          rgba(102, 182, 255, 1) 100%
+        );
+      }
+      .imp {
+        border: 1px solid rgba(33, 107, 255, 1);
+        color: rgba(33, 107, 255, 1);
+      }
+    }
+  }
+  .scroll {
+    width: 700px;
+    height: 500px;
+  }
+  .footer {
+    width: calc(100% - 60px);
+    height: calc(100% - 82px);
+    margin: 10px auto 10px;
+
+    .el-table--fit {
+      height: 100%;
+      :deep(.el-table__header-wrapper) {
+        background-color: #000;
+        font-size: 14px;
+        color: #000;
+
+        .cell {
+          color: #000;
+        }
+      }
+
+      :deep(.el-table__row) {
+        height: 35px;
+        font-size: 14px;
+        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;
+
+        .reset {
+          margin-right: 15px;
+          color: rgba(30, 125, 251, 1);
+          cursor: pointer;
+        }
+
+        .look {
+          margin-right: 15px;
+          color: rgba(30, 125, 251, 1);
+          cursor: pointer;
+        }
+
+        .del {
+          color: rgba(212, 48, 48, 1);
+          cursor: pointer;
+        }
+      }
+    }
+    .gongneng {
+      margin: 15px 0;
+      // .el-button {
+      //   color: #fff;
+      //   border: none;
+      // }
+      .add {
+        color: #fff;
+        border: none;
+        background: linear-gradient(
+          90deg,
+          rgba(33, 107, 255, 1) 0%,
+          rgba(102, 182, 255, 1) 100%
+        );
+      }
+      .imp {
+        border: 1px solid rgba(33, 107, 255, 1);
+        color: rgba(33, 107, 255, 1);
+      }
+    }
+
+    // 项目新增样式
+    :deep(.account) {
+      // height: 300px;
+      border-radius: 11px;
+
+      .el-dialog__header {
+        border-radius: 11px 11px 0 0;
+        background: rgba(237, 241, 245, 1);
+        font-weight: 600;
+        margin: 0;
+        height: 32px;
+
+        .el-dialog__headerbtn {
+          outline: none;
+        }
+      }
+
+      .el-dialog__body {
+        padding: 25px 30px 0 30px;
+        .el-form {
+          .el-form-item {
+            display: flex;
+            align-items: center;
+          }
+          .resetPassword {
+            color: rgba(43, 151, 252, 1);
+            padding-left: 40px;
+            display: inline-block;
+            margin-bottom: 15px;
+          }
+          .options {
+            margin: 60px 0 40px 0;
+            .el-form-item__content {
+              display: flex;
+              flex-direction: row-reverse;
+              .queding {
+                margin-left: 20px;
+                background: linear-gradient(
+                  90deg,
+                  rgba(33, 107, 255, 1) 0%,
+                  rgba(102, 182, 255, 1) 100%
+                );
+                border: none;
+              }
+              .quxiao {
+                border: 1px solid rgba(43, 151, 252, 1);
+                color: rgba(43, 151, 252, 1);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .el-pagination {
+    width: calc(100vw - 300px);
+    margin: 20px auto;
+    justify-content: flex-end;
+    :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;
+      width: 100px;
+    }
+
+    :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(111, 182, 184, 1);
+      color: rgba(0, 149, 255, 1);
+      border: 1px solid rgba(0, 149, 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;
+    }
+  }
+}
+
+.el-input {
+  width: 192px;
+}
+</style>

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

@@ -0,0 +1,810 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <el-icon :size="23" class="camera"><VideoCameraFilled /></el-icon> -->
+      <span class="cameratxt">用户管理</span>
+    </div>
+    <div class="middle">
+      <div class="filter">
+        <div class="condition">
+          <span>姓名 </span>
+          <el-input
+            clearable
+            v-model="searchInput.name"
+            @clear="searchBtn"
+            class="w-50 m-2"
+            placeholder="请输入姓名"
+            style="width: 250px"
+          />
+        </div>
+        <div class="condition">
+          <span>角色 </span>
+          <el-input
+            clearable
+            v-model="searchInput.role"
+            @clear="searchBtn"
+            class="w-50 m-2"
+            placeholder="请输入角色"
+            style="width: 250px"
+          />
+        </div>
+        <div class="condition">
+          <span>创建时间 </span>
+          <el-date-picker
+            v-model="searchInput.createTime"
+            type="daterange"
+            unlink-panels
+            range-separator="-"
+            start-placeholder="起始时间"
+            end-placeholder="截止时间"
+            format="YYYY-MM-DD"
+            value-format="YYYY-MM-DD"
+            :prefix-icon="Calendar"
+            placeholder="请选择日期"
+            style="width: 280px"
+            @change="searchBtn"
+          />
+        </div>
+        <div class="condition">
+          <span>部门 </span>
+          <el-select
+            v-model="searchInput.department"
+            placeholder="请选择部门"
+            style="width: 220px"
+            clearable
+            @clear="searchBtn"
+          >
+            <el-option
+              v-for="item in departmentData"
+              :key="item.value"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </div>
+        <el-button
+          style="margin-left: 20px"
+          type="primary"
+          class="search"
+          @click="searchBtn"
+          >查询</el-button
+        >
+      </div>
+      <!-- 按钮列表 -->
+      <div class="gongneng">
+        <el-button @click="addProjectbtn" class="add"
+          ><el-icon> <CirclePlus /> </el-icon><span>创建用户</span></el-button
+        >
+        <el-button @click="projectImportBtn" class="imp"
+          ><span>更新</span></el-button
+        >
+        <!-- <el-button @click="projectExportBtn" class="add"
+          ><el-icon><Download /></el-icon><span>项目导出</span></el-button
+        > -->
+      </div>
+    </div>
+    <div class="scroll">
+      <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 type="selection" width="55" /> -->
+          <el-table-column align="center" prop="createTime" label="序号" />
+          <el-table-column align="center" prop="createTime" label="姓名" />
+          <el-table-column align="center" prop="createUser" label="工号" />
+          <el-table-column align="center" prop="createUser" label="角色" />
+          <el-table-column align="center" prop="createUser" label="部门" />
+          <el-table-column align="center" prop="createUser" label="手机号码" />
+          <el-table-column align="center" prop="createTime" label="创建时间" />
+          <el-table-column align="center" label="操作" width="220">
+            <template #default="scope">
+              <div class="options">
+                <div class="look" @click="edit(scope.row)">编辑</div>
+                <el-popconfirm
+                  width="200"
+                  confirm-button-text="确认"
+                  cancel-button-text="取消"
+                  :icon="InfoFilled"
+                  icon-color="#f89626"
+                  title="是否删除此数据?"
+                  @confirm="del(scope.row)"
+                  @cancel="cancelEvent"
+                >
+                  <template #reference>
+                    <div class="del">删除</div>
+                  </template>
+                </el-popconfirm>
+              </div>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <!-- 新增账号 -->
+        <el-dialog
+          class="account"
+          v-model="accountVisible"
+          :close-on-click-modal="false"
+          :close-on-press-escape="false"
+          :title="accountTitle"
+          align-center
+          width="600"
+          :before-close="cancelAccount"
+        >
+          <el-form
+            ref="accountRef"
+            :model="accountRuleForm"
+            :rules="accountRules"
+            class="demo-ruleForm"
+            label-width="80px"
+            :size="formSize"
+            label-position="right"
+            status-icon
+          >
+            <!-- <span class="resetPassword"
+              >注:建设单位时用户名可填写单位名称</span
+            > -->
+            <el-form-item label="姓名" prop="">
+              <el-input
+                v-model="accountRuleForm.contacts"
+                placeholder="请输入姓名"
+                clearable
+              />
+            </el-form-item>
+            <el-form-item label="工号" prop="account">
+              <el-input
+                v-model="accountRuleForm.account"
+                placeholder="请输入工号"
+                clearable
+              />
+            </el-form-item>
+            <el-form-item label="手机号码" prop="">
+              <el-input
+                v-model="accountRuleForm.phone"
+                placeholder="请输入手机号码"
+                clearable
+              />
+            </el-form-item>
+            <el-form-item label="角色" prop="role">
+              <el-select
+                v-model="accountRuleForm.role"
+                placeholder="请选择"
+                style="width: 220px"
+                @change="roleChange"
+              >
+                <el-option
+                  v-for="item in roleData"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="部门" prop="role">
+              <el-select
+                v-model="accountRuleForm.role"
+                placeholder="请选择"
+                style="width: 220px"
+                @change="roleChange"
+              >
+                <el-option
+                  v-for="item in roleData"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
+            <el-form-item class="options">
+              <el-button
+                class="queding"
+                type="primary"
+                @click="confirmAccount(accountRef)"
+              >
+                &nbsp;&nbsp;确认&nbsp;&nbsp;
+              </el-button>
+              <el-button class="quxiao" @click="cancelAccount"
+                >&nbsp;&nbsp;取消&nbsp;&nbsp;</el-button
+              >
+            </el-form-item>
+          </el-form>
+        </el-dialog>
+
+        <!-- 项目导入 -->
+        <el-dialog
+          class="projectImport"
+          v-model="projectImportVisible"
+          :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="projectRef"
+            :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(projectRef)"
+            >
+              &nbsp;&nbsp;确认导入&nbsp;&nbsp;
+            </el-button>
+            <el-button @click="cancelProjectImport"
+              >&nbsp;&nbsp;取消导入&nbsp;&nbsp;</el-button
+            >
+          </div>
+        </el-dialog>
+      </div>
+
+      <!-- 分页组件 -->
+      <el-pagination
+        background
+        v-model:current-page="currentPage"
+        v-model:page-size="pageSize"
+        :page-sizes="[10, 20, 30, 40]"
+        layout="total,sizes, prev, pager, next, jumper, slot"
+        :total="total"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import {
+  ref,
+  reactive,
+  nextTick,
+  onMounted,
+  watch,
+  onBeforeMount,
+  onUnmounted,
+} from "vue";
+import COS from "cos-js-sdk-v5";
+import { useRouter } from "vue-router";
+import { genFileId, ElMessage, ElMessageBox } from "element-plus";
+import { Calendar } from "@element-plus/icons-vue";
+import { dayjs } from "element-plus";
+import lodash, { reduce } from "lodash";
+import axios from "axios";
+import { useStore } from "vuex";
+
+const store = useStore();
+const router = useRouter();
+
+const tableData = reactive({
+  list: [{}],
+});
+
+const searchInput = reactive({
+  name: "", // 名称
+  role: "", // 角色
+  department: "", // 部门
+  createTime: "", // 创建时间
+}); // 搜索按钮数据
+const departmentData = ref(); // 部门数据
+
+const currentPage = ref(1); // 当前页
+const pageSize = ref(10);
+const total = ref(40); // 当前总数
+const selectData = reactive({
+  list: [],
+}); // 多选框选择的数据
+const api = ref("");
+
+// 新增账号 (--------------------------------------------------------)
+const accountVisible = ref(false);
+const accountTitle = ref();
+const accountRef = ref();
+
+const accountRuleForm = reactive({
+  role: "", // 角色
+  user: "", // 用户名
+  contacts: "", // 联系人
+  phone: "", // 联系电话
+  account: "", // 账号
+  id: "",
+});
+// 新增账号表单验证
+const accountRules = reactive({
+  role: [{ required: true, message: "角色不能为空", trigger: "blur" }],
+  user: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
+  account: [{ required: true, message: "账号不能为空", trigger: "blur" }],
+});
+
+// 项目导入
+const projectImportVisible = ref(false);
+const projectRef = ref();
+const projectFile = ref();
+
+// 获取账户列表
+const getList = async () => {
+  loading.value = true;
+  let data = {
+    currentPage: currentPage.value, // 当前页
+    pageCount: pageSize.value, // 一页数据条数
+    projectName: searchInput.name,
+    pageType: 1,
+    permitNum: searchInput.LicenseCode, // 施工许可证编码
+    projectUnit: searchInput.unit, // 建设单位
+    projectPurpose: searchInput.purpose, // 项目用途
+    structureType: searchInput.structureType, // 结构类型
+    projectType: searchInput.projectType, // 项目类型
+  };
+
+  if (searchInput.createTime) {
+    data.startDate = searchInput.createTime[0];
+    data.endDate = searchInput.createTime[1];
+  }
+  let res = await axios({
+    method: "get",
+    url: api.value + "/jgcloudProject/queryPageJgcloudProject",
+    headers: {
+      token: sessionStorage.getItem("token"),
+      user_head: sessionStorage.getItem("userhead"),
+    },
+    params: data,
+  });
+  console.log(res, "项目分页数据");
+  if (res.data.code == 200) {
+    tableData.list = res.data.data.list;
+    total.value = res.data.data.totalCount;
+    loading.value = false;
+  } else {
+    loading.value = false;
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  }
+};
+
+// 搜索功能
+const searchBtn = lodash.debounce(async () => {
+  getList();
+}, 300);
+
+// 角色新增
+const addProjectbtn = () => {
+  accountVisible.value = true;
+  accountTitle.value = "用户新增";
+};
+// 取消角色新增
+const cancelAccount = () => {
+  accountVisible.value = false;
+  accountRef.value.resetFields();
+};
+
+//编辑项目
+const edit = (row) => {
+  accountVisible.value = true;
+  accountTitle.value = "用户编辑";
+  console.log(row);
+};
+// 删除pdf文件 (装配式相关附件)
+const pdfDel = (value, uid) => {};
+
+// 导入项目
+const projectImportConfirm = async () => {
+  let data = new FormData();
+  data.set("file", projectFile.value);
+  data.set("pageType", 1);
+  let res = await axios({
+    method: "POST",
+    url: api.value + "/jgcloudProject/importExcelProject",
+    headers: {
+      token: sessionStorage.getItem("token"),
+      user_head: sessionStorage.getItem("userhead"),
+    },
+    data: data,
+  });
+  console.log(res, "导入项目");
+  if (res.data.code == 200) {
+    projectImportVisible.value = false;
+    getList();
+    if (projectRef.value) {
+      projectRef.value.clearFiles();
+    }
+    ElMessage({
+      type: "success",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  }
+};
+const cancelProjectImport = () => {
+  projectRef.value.clearFiles();
+  projectImportVisible.value = false;
+};
+
+// (--------------------------------------------------)
+// cos桶中地址栏中中文地址乱码转换为中文
+const pdfName = (value) => {
+  // console.log(decodeURIComponent(value));
+  // console.log(decodeURIComponent(value).length);
+  let index = decodeURIComponent(value).lastIndexOf("/") + 1;
+  let name = decodeURIComponent(value).slice(index, value.length);
+  console.log(name);
+  return name;
+};
+
+// 多选框功能
+const handleSelectionChange = (val) => {
+  console.log(val);
+  selectData.list = val;
+};
+
+// 表格斑马纹颜色修改
+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(() => {
+  api.value = store.state.user.api;
+});
+onUnmounted(() => {
+  // document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  min-width: 1000px;
+  width: calc(100vw - 260px);
+  height: calc(100vh - 105px);
+  margin: 20px auto;
+  background-color: #fff;
+  color: #fff;
+  display: flex;
+  flex-direction: column;
+  box-shadow: 0px 3px 10px rgba(0, 97, 255, 0.2);
+
+  .left {
+    // width: calc(100wh - 40px);
+    display: flex;
+    align-items: center;
+    height: 60px;
+    margin: 0 30px;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+
+    .cameratxt {
+      display: block;
+      height: 60px;
+      line-height: 60px;
+    }
+  }
+  .middle {
+    width: 96%;
+    margin: 0 auto;
+
+    color: #000;
+
+    // border-bottom: 1px solid rgb(231, 231, 231);
+    .filter {
+      display: flex;
+      flex-wrap: wrap;
+      align-items: center;
+      margin: 10px 0 0 0;
+
+      .search {
+        margin-left: 0 !important;
+        color: #fff;
+        background: linear-gradient(
+          90deg,
+          rgba(33, 107, 255, 1) 0%,
+          rgba(102, 182, 255, 1) 100%
+        );
+        border: none;
+      }
+
+      .reset {
+        border: 1px solid rgba(38, 151, 255, 1);
+        color: rgba(38, 151, 255, 1);
+      }
+
+      .condition {
+        display: flex;
+        align-items: center;
+        margin: 10px 30px 10px 0;
+
+        :deep(.el-input .el-input__inner) {
+          font-size: 14px;
+        }
+
+        span {
+          margin: 0 10px 0 0;
+        }
+      }
+    }
+
+    .gongneng {
+      margin: 10px 0;
+
+      .el-button {
+        color: #fff;
+        border: none;
+      }
+      .add {
+        background: linear-gradient(
+          90deg,
+          rgba(33, 107, 255, 1) 0%,
+          rgba(102, 182, 255, 1) 100%
+        );
+      }
+      .imp {
+        background: #fff;
+        border: 1px solid rgba(33, 107, 255, 1);
+        color: rgba(33, 107, 255, 1);
+      }
+    }
+  }
+  .scroll {
+    flex: 1;
+    overflow: auto;
+  }
+  .footer {
+    width: 96%;
+    height: calc(100% - 82px);
+    margin: 10px auto 10px;
+
+    .el-table--fit {
+      height: 100%;
+      :deep(.el-table__header-wrapper) {
+        background-color: #000;
+        font-size: 14px;
+        color: #000;
+
+        .cell {
+          color: #000;
+        }
+      }
+
+      :deep(.el-table__row) {
+        height: 50px;
+        font-size: 14px;
+        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;
+
+        .reset {
+          margin-right: 15px;
+          color: rgba(30, 125, 251, 1);
+          cursor: pointer;
+        }
+
+        .look {
+          margin-right: 15px;
+          color: rgba(30, 125, 251, 1);
+          cursor: pointer;
+        }
+
+        .del {
+          color: rgba(212, 48, 48, 1);
+          cursor: pointer;
+        }
+      }
+    }
+
+    // 项目新增样式
+    :deep(.account) {
+      // height: 300px;
+      border-radius: 11px;
+
+      .el-dialog__header {
+        border-radius: 11px 11px 0 0;
+        background: rgba(237, 241, 245, 1);
+        font-weight: 600;
+        margin: 0;
+        height: 32px;
+
+        .el-dialog__headerbtn {
+          outline: none;
+        }
+      }
+
+      .el-dialog__body {
+        padding: 30px 30px 0 30px;
+        .el-form {
+          .formLi {
+            width: 100%;
+            margin-bottom: 5px;
+            display: flex;
+            .el-form-item {
+              .el-input {
+                width: 120px;
+              }
+              &:nth-child(2) {
+                .el-form-item__label {
+                  display: none;
+                }
+                .el-form-item__content {
+                  margin-left: 10px !important;
+                }
+              }
+              &:nth-child(3) {
+                .el-form-item__label {
+                  display: none;
+                }
+                .el-form-item__content {
+                  margin-left: 10px !important;
+                }
+              }
+            }
+          }
+          .resetPassword {
+            color: rgba(43, 151, 252, 1);
+            padding-left: 40px;
+            display: inline-block;
+            margin-bottom: 15px;
+          }
+          .options {
+            margin: 60px 0 40px 0;
+            .el-form-item__content {
+              display: flex;
+              flex-direction: row-reverse;
+              .queding {
+                margin-left: 20px;
+                background: linear-gradient(
+                  90deg,
+                  rgba(33, 107, 255, 1) 0%,
+                  rgba(102, 182, 255, 1) 100%
+                );
+                border: none;
+              }
+              .quxiao {
+                border: 1px solid rgba(43, 151, 252, 1);
+                color: rgba(43, 151, 252, 1);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  .el-pagination {
+    width: calc(100vw - 300px);
+    margin: 20px auto;
+    justify-content: flex-end;
+    :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;
+      width: 100px;
+    }
+
+    :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(111, 182, 184, 1);
+      color: rgba(0, 149, 255, 1);
+      border: 1px solid rgba(0, 149, 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;
+    }
+  }
+}
+
+.el-input {
+  width: 192px;
+}
+</style>
+<style lang="scss">
+.usePrefabricate {
+  height: 488px;
+  .el-select-dropdown__wrap {
+    max-height: 100%;
+  }
+}
+</style>

+ 85 - 0
vite.config.js

@@ -0,0 +1,85 @@
+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(),
+    createSvgIconsPlugin({
+      iconDirs: [path.resolve(process.cwd(), "src/assets/svgIcon")], //指定需要转换的图标文件    夹
+      symbolId: "icon-[dir]-[name]",
+    })
+  ],
+  resolve: {
+    alias: {
+      "@": path.resolve("./src"), // @代替src
+    },
+  },
+  css: {
+    preprocessorOptions: {
+      scss: {
+        additionalData: `@import "./src/style/mixin.scss";`, // 此处全局的scss文件
+      },
+    },
+  },
+  server: {
+    host: "0.0.0.0",
+    port: 8044,
+    // 是否开启 https
+    // https: false,
+    proxy: {
+      // "/jgcloudApi/jgcloud/api": {
+      //   target: "http://203.195.168.81:82/jgcloudApi/jgcloud/api", // 正式环境
+      //   changeOrigin: true,
+      //   rewrite: (path) => path.replace(/^\/jgcloudApi\/jgcloud\/api/, ""),
+      // },
+      "/jgcloudApi/jgcloud/api": {
+        // target: "http://192.168.161.220:8080/jgcloud/api/", // 正式环境
+        target: "http://jxzp.jxjzcjzx.cn:6080/jgcloudApi/jgcloud/api", // 正式环境
+        changeOrigin: true,
+        rewrite: (path) => path.replace(/^\/jgcloudApi\/jgcloud\/api/, ""),
+      },
+    },
+  },
+  publicDir: "public",
+  base: "./",
+  // 打包配置
+  build: {
+    target: "modules", //设置最终构建的浏览器兼容目标  //es2015(编译成es5) | modules
+    outDir: "dist", // 构建得包名  默认:dist
+    assetsDir: "static", // 静态资源得存放路径文件名  assets
+    sourcemap: false, //构建后是否生成 source map 文件
+    // brotliSize: false, // 启用/禁用 brotli 压缩大小报告。 禁用该功能可能会提高大型项目的构建性能
+    chunkSizeWarningLimit: 1500, //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,
+      },
+    },
+  },
+});