Browse Source

feat: ✨ 完成考勤管理

@sun-chaoqun 2 years ago
parent
commit
667f0ba14a

+ 1 - 1
components.d.ts

@@ -35,6 +35,7 @@ declare module '@vue/runtime-core' {
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
     ElHeader: typeof import('element-plus/es')['ElHeader']
     ElIcon: typeof import('element-plus/es')['ElIcon']
+    ElImage: typeof import('element-plus/es')['ElImage']
     ElInput: typeof import('element-plus/es')['ElInput']
     ElMain: typeof import('element-plus/es')['ElMain']
     ElMenu: typeof import('element-plus/es')['ElMenu']
@@ -52,7 +53,6 @@ declare module '@vue/runtime-core' {
     ElTag: typeof import('element-plus/es')['ElTag']
     ElText: typeof import('element-plus/es')['ElText']
     ElUpload: typeof import('element-plus/es')['ElUpload']
-    Files: typeof import('./src/components/files.vue')['default']
     Loading: typeof import('./src/components/Loading/index.vue')['default']
     Pagination: typeof import('./src/components/TableBase/components/Pagination.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']

+ 190 - 95
index.html

@@ -1,102 +1,197 @@
 <!DOCTYPE html>
 <html lang="en">
-  <head>
-    <meta charset="UTF-8" />
-    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title><%- title %></title>
-  </head>
-  <body>
-    <div id="app">
-      <style>
-        html,
-        body,
-        #app {
-          width: 100%;
-          height: 100%;
-          padding: 0;
-          margin: 0;
+
+<head>
+  <meta charset="UTF-8" />
+  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <title><%- title %></title>
+</head>
+
+<body>
+  <div id="app">
+    <style>
+      #loader-wrapper {
+        position: fixed;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 100%;
+        z-index: 1000;
+      }
+
+      #loader {
+        display: block;
+        position: relative;
+        left: 50%;
+        top: 50%;
+        width: 150px;
+        height: 150px;
+        margin: -75px 0 0 -75px;
+        border-radius: 50%;
+        border: 3px solid transparent;
+        border-top-color: #3498db;
+        -webkit-animation: spin 2s linear infinite;
+        /* Chrome, Opera 15+, Safari 5+ */
+        animation: spin 2s linear infinite;
+        /* Chrome, Firefox 16+, IE 10+, Opera */
+        z-index: 1001;
+      }
+
+      #loader-logo {
+        display: block;
+        position: absolute;
+        left: 48%;
+        top: 46%;
+        background: url(../images/user-bg-2.jpg) no-repeat center center;
+        z-index: 1001;
+      }
+
+      #loader:before {
+        content: "";
+        position: absolute;
+        top: 5px;
+        left: 5px;
+        right: 5px;
+        bottom: 5px;
+        border-radius: 50%;
+        border: 3px solid transparent;
+        border-top-color: #e74c3c;
+        -webkit-animation: spin 3s linear infinite;
+        /* Chrome, Opera 15+, Safari 5+ */
+        animation: spin 3s linear infinite;
+        /* Chrome, Firefox 16+, IE 10+, Opera */
+      }
+
+      #loader:after {
+        content: "";
+        position: absolute;
+        top: 15px;
+        left: 15px;
+        right: 15px;
+        bottom: 15px;
+        border-radius: 50%;
+        border: 3px solid transparent;
+        border-top-color: #f9c922;
+        -webkit-animation: spin 1.5s linear infinite;
+        /* Chrome, Opera 15+, Safari 5+ */
+        animation: spin 1.5s linear infinite;
+        /* Chrome, Firefox 16+, IE 10+, Opera */
+      }
+
+      @-webkit-keyframes spin {
+        0% {
+          -webkit-transform: rotate(0deg);
+          /* Chrome, Opera 15+, Safari 3.1+ */
+          -ms-transform: rotate(0deg);
+          /* IE 9 */
+          transform: rotate(0deg);
+          /* Firefox 16+, IE 10+, Opera */
         }
-        .loading-box {
-          display: flex;
-          flex-direction: column;
-          align-items: center;
-          justify-content: center;
-          width: 100%;
-          height: 100%;
+
+        100% {
+          -webkit-transform: rotate(360deg);
+          /* Chrome, Opera 15+, Safari 3.1+ */
+          -ms-transform: rotate(360deg);
+          /* IE 9 */
+          transform: rotate(360deg);
+          /* Firefox 16+, IE 10+, Opera */
         }
-        .loading-box .loading-wrap {
-          display: flex;
-          align-items: center;
-          justify-content: center;
-          padding: 98px;
+      }
+
+      @keyframes spin {
+        0% {
+          -webkit-transform: rotate(0deg);
+          /* Chrome, Opera 15+, Safari 3.1+ */
+          -ms-transform: rotate(0deg);
+          /* IE 9 */
+          transform: rotate(0deg);
+          /* Firefox 16+, IE 10+, Opera */
         }
-        .dot {
-          position: relative;
-          box-sizing: border-box;
-          display: inline-block;
-          width: 128px;
-          height: 128px;
-          font-size: 32px;
-          transform: rotate(45deg);
-          animation: ant-rotate 1.2s infinite linear;
+
+        100% {
+          -webkit-transform: rotate(360deg);
+          /* Chrome, Opera 15+, Safari 3.1+ */
+          -ms-transform: rotate(360deg);
+          /* IE 9 */
+          transform: rotate(360deg);
+          /* Firefox 16+, IE 10+, Opera */
         }
-        .dot i {
-          position: absolute;
-          display: block;
-          width: 56px;
-          height: 56px;
-          background-color: #409eff;
-          border-radius: 100%;
-          opacity: 0.3;
-          transform: scale(0.75);
-          transform-origin: 50% 50%;
-          animation: ant-spin-move 1s infinite linear alternate;
-        }
-        .dot i:nth-child(1) {
-          top: 0;
-          left: 0;
-        }
-        .dot i:nth-child(2) {
-          top: 0;
-          right: 0;
-          animation-delay: 0.4s;
-        }
-        .dot i:nth-child(3) {
-          right: 0;
-          bottom: 0;
-          animation-delay: 0.8s;
-        }
-        .dot i:nth-child(4) {
-          bottom: 0;
-          left: 0;
-          animation-delay: 1.2s;
-        }
-        @keyframes ant-rotate {
-          to {
-            transform: rotate(405deg);
-          }
-        }
-        @keyframes ant-spin-move {
-          to {
-            opacity: 1;
-          }
-        }
-      </style>
-      <div class="loading-box">
-        <div class="loading-wrap">
-          <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
-        </div>
-      </div>
+      }
+
+      #loader-wrapper .loader-section {
+        position: fixed;
+        top: 0;
+        width: 51%;
+        height: 100%;
+        background: #222222;
+        z-index: 1000;
+        -webkit-transform: translateX(0);
+        /* Chrome, Opera 15+, Safari 3.1+ */
+        -ms-transform: translateX(0);
+        /* IE 9 */
+        transform: translateX(0);
+        /* Firefox 16+, IE 10+, Opera */
+      }
+
+      #loader-wrapper .loader-section.section-left {
+        left: 0;
+      }
+
+      #loader-wrapper .loader-section.section-right {
+        right: 0;
+      }
+
+
+      /* Loaded */
+
+      .loaded #loader-wrapper .loader-section.section-left {
+        -webkit-transform: translateX(-100%);
+        /* Chrome, Opera 15+, Safari 3.1+ */
+        -ms-transform: translateX(-100%);
+        /* IE 9 */
+        transform: translateX(-100%);
+        /* Firefox 16+, IE 10+, Opera */
+        -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+        transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+      }
+
+      .loaded #loader-wrapper .loader-section.section-right {
+        -webkit-transform: translateX(100%);
+        /* Chrome, Opera 15+, Safari 3.1+ */
+        -ms-transform: translateX(100%);
+        /* IE 9 */
+        transform: translateX(100%);
+        /* Firefox 16+, IE 10+, Opera */
+        -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+        transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+      }
+
+      .loaded #loader {
+        opacity: 0;
+        -webkit-transition: all 0.3s ease-out;
+        transition: all 0.3s ease-out;
+      }
+
+      .loaded #loader-wrapper {
+        visibility: hidden;
+        -webkit-transform: translateY(-100%);
+        /* Chrome, Opera 15+, Safari 3.1+ */
+        -ms-transform: translateY(-100%);
+        /* IE 9 */
+        transform: translateY(-100%);
+        /* Firefox 16+, IE 10+, Opera */
+        -webkit-transition: all 0.3s 1s ease-out;
+        transition: all 0.3s 1s ease-out;
+      }
+    </style>
+    <div id="loader-wrapper">
+      <div id="loader"></div>
+      <div class="loader-section section-left"></div>
+      <div class="loader-section section-right"></div>
     </div>
-    <script>
-      const color = '#22c1c3'
-      const isDark = JSON.parse(window.localStorage.getItem('GlobalState'))?.themeConfig?.isDark
-      const dot = document.querySelectorAll('.dot i')
-      const html = document.querySelector('html')
-      dot.forEach(item => (item.style.background = color))
-      if (isDark) html.style.background = '#141414'
-    </script>
-    <script type="module" src="/src/main.ts"></script>
-  </body>
-</html>
+  </div>
+  <script type="module" src="/src/main.ts"></script>
+</body>
+
+</html>

+ 1 - 1
src/api/index.ts

@@ -14,7 +14,7 @@ let loadingInstance: LoadingType = {}
 
 const config = {
   // 默认地址请求地址,可在 .env.*** 文件中修改
-  // baseURL: import.meta.env.VITE_BZD_ERP_APP_API as string,
+  baseURL: import.meta.env.VITE_BZD_ERP_APP_API as string,
   // 设置超时时间(10s)
   timeout: ResultEnum.TIMEOUT as number,
   // 跨域时候允许携带凭证

+ 3 - 0
src/api/workAttendance/index.ts

@@ -20,6 +20,9 @@ export const Leave_Approval = (params: any) => $http.post('api/ams/Leave/Approva
 export const Leave_Del = (params: any) => $http.post('api/ams/Leave/Del', params)
 // 请假剩余时长
 export const Leave_DaysOff = (params: any) => $http.post('/api/ams/Leave/DaysOff', params)
+// 财务管理
+export const Leave_Finance_List = (params: any) => $http.post('/api/ams/Leave/Finance_List', params)
+
 /**
  * 加班
  */

+ 32 - 18
src/assets/iconfont.css

@@ -1,63 +1,77 @@
 @font-face {
-  font-family: "iconfont"; /* Project id 3967191 */
-  src: url('iconfont.woff2?t=1680060858543') format('woff2'),
-       url('iconfont.woff?t=1680060858543') format('woff'),
-       url('iconfont.ttf?t=1680060858543') format('truetype');
+  font-family: 'iconfont'; /* Project id 3967191 */
+  src: url('./iconfont.woff2?t=1680587659492') format('woff2'), url('./iconfont.woff?t=1680587659492') format('woff'),
+    url('./iconfont.ttf?t=1680587659492') format('truetype');
 }
 
 .iconfont {
-  font-family: "iconfont" !important;
+  font-family: 'iconfont' !important;
   font-size: 16px;
   font-style: normal;
   -webkit-font-smoothing: antialiased;
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-notice:before {
+  content: '\ue63e';
+}
+
+.icon-tuichuquanping:before {
+  content: '\ue603';
+}
+
+.icon-notice1:before {
+  content: '\ue636';
+}
+
+.icon-shuyi_quanping:before {
+  content: '\ue661';
+}
+
 .icon-jiaoseguanli:before {
-  content: "\e648";
+  content: '\ue648';
 }
 
 .icon-zhanghu:before {
-  content: "\e6a1";
+  content: '\ue6a1';
 }
 
 .icon-jiaoseguanli1:before {
-  content: "\e645";
+  content: '\ue645';
 }
 
 .icon-kaoqin:before {
-  content: "\e619";
+  content: '\ue619';
 }
 
 .icon-qingjia:before {
-  content: "\e627";
+  content: '\ue627';
 }
 
 .icon-jiaban:before {
-  content: "\e64c";
+  content: '\ue64c';
 }
 
 .icon-qingjiashenqing:before {
-  content: "\e6aa";
+  content: '\ue6aa';
 }
 
 .icon-jiabanshenqing:before {
-  content: "\e690";
+  content: '\ue690';
 }
 
 .icon-money:before {
-  content: "\e663";
+  content: '\ue663';
 }
 
 .icon-moneybag:before {
-  content: "\e7d1";
+  content: '\ue7d1';
 }
 
 .icon-moneycollect:before {
-  content: "\e7cd";
+  content: '\ue7cd';
 }
 
 .icon-money-funds:before {
-  content: "\e831";
+  content: '\ue831';
 }
-

BIN
src/assets/iconfont.ttf


BIN
src/assets/iconfont.woff


BIN
src/assets/iconfont.woff2


BIN
src/assets/images/1.jpg


+ 2 - 2
src/components/TableBase/index.vue

@@ -28,7 +28,7 @@ const props = withDefaults(defineProps<ProTableProps>(), {
   pagination: true,
   layout: 'total, sizes, prev, pager, next, jumper',
   initParam: {},
-  border: true,
+  border: false,
   toolButton: true,
   selectId: 'id',
   searchCol: () => ({ xs: 1, sm: 2, md: 2, lg: 3, xl: 4 })
@@ -75,7 +75,7 @@ defineExpose({
   </div>
 
   <div class="card table" :style="{ height: cardHeight + 'px' }">
-    <el-table :data="tableData" @row-click="props.rowClick">
+    <el-table :data="tableData" @row-click="props.rowClick" :border="border">
       <!-- 默认插槽 -->
       <slot></slot>
       <template v-for="item in tableColumns" :key="item">

+ 1 - 1
src/layouts/Header/Breadcrumb.vue

@@ -30,7 +30,7 @@ const onBreadcrumbClick = (item: any, index: number) => {
             <el-icon class="breadcrumb-icon" v-if="isUnicode(item.meta.icon as string)">
               <component :is="item.meta.icon"></component>
             </el-icon>
-            <i v-else class="iconfont el-icon">{{ item.meta.icon }}</i>
+            <i v-else class="iconfont el-icon breadcrumb-icon">{{ item.meta.icon }}</i>
             <span class="breadcrumb-title">{{ item.meta.title }}</span>
           </div>
         </el-breadcrumb-item>

+ 5 - 4
src/layouts/Header/index.vue

@@ -28,10 +28,13 @@ const logOut = () => {
       </el-col>
       <el-col :span="2" class="notice">
         <el-dropdown>
-          <el-icon :size="25"><Bell /></el-icon>
+          <!-- <el-icon :size="25"><Bell /></el-icon> -->
+          <el-button type="primary" circle>
+            <i class="iconfont el-icon">&#xe63e;</i>
+          </el-button>
           <template #dropdown>
             <el-dropdown-menu>
-              <el-dropdown-item @click="logOut">退出登录</el-dropdown-item>
+              <el-dropdown-item>Action 2</el-dropdown-item>
             </el-dropdown-menu>
           </template>
         </el-dropdown>
@@ -47,8 +50,6 @@ const logOut = () => {
           <template #dropdown>
             <el-dropdown-menu>
               <el-dropdown-item @click="logOut">退出登录</el-dropdown-item>
-              <el-dropdown-item>Action 2</el-dropdown-item>
-              <el-dropdown-item>Action 3</el-dropdown-item>
             </el-dropdown-menu>
           </template>
         </el-dropdown>

+ 3 - 7
src/layouts/Main/index.vue

@@ -1,6 +1,6 @@
 <script setup lang="ts">
-import { ref, provide, beforeRouteUpdate } from 'vue'
-import { useRoute } from 'vue-router'
+// import { ref, provide, beforeRouteUpdate } from 'vue'
+// import { useRoute } from 'vue-router'
 // import { KeepAliveStore } from '@/stores/modules/keepAlive'
 // const isRouterShow = ref(true)
 // const refreshCurrentPage = (val: boolean) => {
@@ -23,13 +23,10 @@ import { useRoute } from 'vue-router'
 //   this.$router.isBack = false
 //   next()
 // }
-const route = useRoute()
-const defaultActive = ref<string>(route.path)
-console.log(route.path)
 </script>
 
 <template>
-  <el-main v-if="route.path !== '/index'">
+  <el-main>
     <router-view v-slot="{ Component, route }">
       <transition appear name="fade-transform" mode="out-in">
         <component :is="Component" :key="route.path" />
@@ -40,7 +37,6 @@ console.log(route.path)
       </keep-alive> -->
     </router-view>
   </el-main>
-  <div v-else>123</div>
 </template>
 
 <style scoped lang="scss">

+ 23 - 12
src/router/modules/staticRouter.ts

@@ -8,7 +8,7 @@ export const staticRouter: RouteRecordRaw[] = [
     path: '/index',
     name: 'Index',
     component: () => import('@/views/Index.vue'),
-    // redirect: '/home',
+    redirect: '/home',
     meta: {
       title: '起始页'
     },
@@ -27,7 +27,8 @@ export const staticRouter: RouteRecordRaw[] = [
         name: 'Roles',
         component: () => import('@/views/account/roles/Roles.vue'),
         meta: {
-          title: '角色管理'
+          title: '角色管理',
+          icon: '\ue648'
         }
       },
       {
@@ -35,7 +36,8 @@ export const staticRouter: RouteRecordRaw[] = [
         name: 'Users',
         component: () => import('@/views/account/users/Users.vue'),
         meta: {
-          title: '账户管理'
+          title: '账户管理',
+          icon: '\ue6a1'
         }
       },
       {
@@ -43,7 +45,8 @@ export const staticRouter: RouteRecordRaw[] = [
         name: 'Salary',
         component: () => import('@/views/salary/salary/Salary.vue'),
         meta: {
-          title: '薪资管理'
+          title: '薪资管理',
+          icon: '\ue7cd'
         }
       },
       {
@@ -51,7 +54,8 @@ export const staticRouter: RouteRecordRaw[] = [
         name: 'SalaryCount',
         component: () => import('@/views/salary/SalaryCount.vue'),
         meta: {
-          title: '薪资统计'
+          title: '薪资统计',
+          icon: '\ue831'
         }
       },
       {
@@ -59,7 +63,8 @@ export const staticRouter: RouteRecordRaw[] = [
         name: 'SalaryMy',
         component: () => import('@/views/salary/SalaryMy.vue'),
         meta: {
-          title: '我的薪资'
+          title: '我的薪资',
+          icon: '\ue7d1'
         }
       },
       {
@@ -67,7 +72,8 @@ export const staticRouter: RouteRecordRaw[] = [
         name: 'Records',
         component: () => import('@/views/workAttendance/records/Records.vue'),
         meta: {
-          title: '统筹管理'
+          title: '统筹管理',
+          icon: 'DocumentCopy'
         }
       },
       {
@@ -75,7 +81,8 @@ export const staticRouter: RouteRecordRaw[] = [
         name: 'RecordsFinance',
         component: () => import('@/views/workAttendance/RecordsFinance.vue'),
         meta: {
-          title: '统筹管理(财务)'
+          title: '统筹管理(财务)',
+          icon: 'Notebook'
         }
       },
       {
@@ -83,7 +90,8 @@ export const staticRouter: RouteRecordRaw[] = [
         name: 'Overtime',
         component: () => import('@/views/workAttendance/Overtime.vue'),
         meta: {
-          title: '加班审批'
+          title: '加班审批',
+          icon: '\ue690'
         }
       },
       {
@@ -92,7 +100,8 @@ export const staticRouter: RouteRecordRaw[] = [
         // component: () => import('@/views/workAttendance/Leave.vue'),
         component: () => import('../../views/workAttendance/Leave.vue'),
         meta: {
-          title: '请假审批'
+          title: '请假审批',
+          icon: '\ue627'
         }
       },
       {
@@ -101,7 +110,8 @@ export const staticRouter: RouteRecordRaw[] = [
         // component: () => import('@/views/workAttendance/MyOvertime.vue'),
         component: () => import('../../views/workAttendance/MyOvertime.vue'),
         meta: {
-          title: '我的加班'
+          title: '我的加班',
+          icon: '\ue64c'
         }
       },
       {
@@ -109,7 +119,8 @@ export const staticRouter: RouteRecordRaw[] = [
         name: 'MyLeave',
         component: () => import('@/views/workAttendance/MyLeave.vue'),
         meta: {
-          title: '我的请假'
+          title: '我的请假',
+          icon: '\ue6aa'
         }
       }
     ]

+ 14 - 3
src/style.scss

@@ -19,10 +19,18 @@ body,
   height: 100%;
   width: 100%;
 }
-// body {
-//   background: #000;
-// }
+body {
+  line-height: 1.5em;
+  font-family: 'Trebuchet MS', Arial, Helvetica, sans-serif;
+  line-height: 25px;
+  letter-spacing: 1px;
+  font-size: 14px;
+  color: #4e4e4e;
+}
 
+.m-0 {
+  margin: 0 !important;
+}
 .margin-left-0 {
   margin-left: 0 !important;
 }
@@ -40,6 +48,9 @@ body,
 .margin-bottom-0 {
   margin-bottom: 0;
 }
+.p-0 {
+  margin: 0 !important;
+}
 .padding-left-0 {
   padding-left: 0 !important;
 }

+ 2 - 33
src/utils/common.ts

@@ -56,6 +56,7 @@ export const hanlderMenuList = (menuList: any) => {
  * @param time
  */
 export const getFormatDuration = (time: number) => {
+  console.log(time)
   let hour = 0
   let minute = 0
   let result = ''
@@ -63,7 +64,7 @@ export const getFormatDuration = (time: number) => {
     hour = time / 60
     minute = time % 60
   } else {
-    minute = time
+    result = '' + time + '分'
   }
   if (minute > 0) {
     result = '' + parseInt(minute + '') + '分' + result
@@ -103,9 +104,6 @@ export const qiniuUpLoadFun = (file: any, token: any) => {
     console.log('domin')
     resolve(params)
   })
-  // .then(function (params) {
-  //   return upLoadQiniu(params)
-  // })
 }
 export function upLoadQiniu(params: any) {
   let observable = qiniu.upload(params.file, params.key, params.token, params.putExtra, params.config)
@@ -126,32 +124,3 @@ export function upLoadQiniu(params: any) {
   }
   observable.subscribe(observer)
 }
-
-// export function upLoadQiniu(params: any) {
-//   //上传至七牛云
-//   return new Promise(function (resolve: any) {
-//     // let loading = layer.load(1, {
-//     //   //请求未成功时出现loading页 ,3代表一种效果
-//     //   shade: [0.1, '#fff'] //0.1透明度的白色背景
-//     // })
-//     // 上传回调
-//     function next(res: any) {
-//       // console.log(res);
-//       console.log('上传中')
-//     }
-//     function error(err: any) {
-//       console.log(err)
-//     }
-//     function complete(res: any) {
-//       // let url = 'http://xxxxxx.cn/' + res.key
-//       // layer.close(loading) //关闭loading效果
-//       console.log(res)
-//       // resolve(url)
-//     }
-//     console.log(qiniu.upload)
-//     let observable = qiniu.upload(params.file, params.key, params.token, params.putExtra, params.config)
-//     console.log(observable)
-//     // let subscription = observable.subscribe(next, error, complete) // 这样传参形式也可以
-//     // subscription.unsubscribe() // 上传取消
-//   })
-// }

+ 18 - 20
src/views/Index.vue

@@ -62,16 +62,16 @@ const routerList = [
       }
     ]
   },
-  {
-    path: '',
-    name: '仓库管理',
-    iocn: '',
-    meta: {
-      icon: 'HomeFilled',
-      title: '仓库管理',
-      isKeepAlive: true
-    }
-  },
+  // {
+  //   path: '',
+  //   name: '仓库管理',
+  //   iocn: '',
+  //   meta: {
+  //     icon: 'HomeFilled',
+  //     title: '仓库管理',
+  //     isKeepAlive: true
+  //   }
+  // },
   {
     path: 'salary',
     name: '薪资管理',
@@ -188,14 +188,14 @@ onMounted(() => {
 const route = useRoute()
 const defaultActive = ref<string>(route.path)
 
-const handleOpen = (key: string, keyPath: string[]) => {
-  // console.log(key, keyPath)
-  // keyPath.push(defaultActive.value)
-  globalStore.SET_Breadcrumb(keyPath)
-}
-const handleClose = (key: string, keyPath: string[]) => {
-  // console.log(key, keyPath)
-}
+// const handleOpen = (key: string, keyPath: string[]) => {
+//   // console.log(key, keyPath)
+//   // keyPath.push(defaultActive.value)
+//   globalStore.SET_Breadcrumb(keyPath)
+// }
+// const handleClose = (key: string, keyPath: string[]) => {
+//   // console.log(key, keyPath)
+// }
 </script>
 
 <template>
@@ -213,8 +213,6 @@ const handleClose = (key: string, keyPath: string[]) => {
             active-text-color="#ffd04b"
             text-color="#fff"
             background-color="#091215"
-            @open="handleOpen"
-            @close="handleClose"
           >
             <SubMenu :menu-list="routerList"></SubMenu>
           </el-menu>

+ 6 - 9
src/views/Login.vue

@@ -1,14 +1,13 @@
 <script setup lang="ts">
-import { reactive, ref, onMounted } from 'vue'
+import { reactive, ref } from 'vue'
 import { ElNotification } from 'element-plus'
 import type { FormInstance, FormRules } from 'element-plus'
 import type { InLogin } from '@/typings/config'
-import { User, View, Lock } from '@element-plus/icons-vue'
+import { User, View, Lock, Hide } from '@element-plus/icons-vue'
 import { Login_verification } from '@/api/user/index'
 import { useRouter } from 'vue-router'
 import { GlobalStore } from '@/stores/index'
 import { fnMd5 } from '@/utils/common'
-import StarrySky from '@/utils/loginBg'
 const router = useRouter()
 const globalStore = GlobalStore()
 const ruleFormRef = ref<FormInstance>()
@@ -56,11 +55,6 @@ let passType = ref('password')
 const changeType = () => {
   passType.value = passType.value === 'password' ? 'text' : 'password'
 }
-
-// onMounted(() => {
-//   const star = new StarrySky()
-//   star.onresize()
-// })
 </script>
 
 <template>
@@ -78,7 +72,10 @@ const changeType = () => {
           <el-input v-model="ruleForm.password" :type="passType" :prefix-icon="Lock" autocomplete="off">
             <template #suffix>
               <span class="view">
-                <el-icon class="el-input__icon" @click="changeType">
+                <el-icon v-if="passType === 'password'" class="el-input__icon" @click="changeType">
+                  <Hide />
+                </el-icon>
+                <el-icon v-else class="el-input__icon" @click="changeType">
                   <View />
                 </el-icon>
               </span>

+ 0 - 3
src/views/account/users/Users.vue

@@ -129,9 +129,6 @@ const initParam = {
 </template>
 
 <style scoped lang="scss">
-// .users {
-//   transition: height 1s ease;
-// }
 .input-suffix {
   width: 100%;
   .w-50 {

+ 20 - 2
src/views/home/index.vue

@@ -1,7 +1,25 @@
 <script setup lang="ts"></script>
 
 <template>
-  <div class="home">home</div>
+  <div class="home">
+    <!-- <img class="img" src="@/assets/images/1.jpg" /> -->
+    <h1>welcome</h1>
+  </div>
 </template>
 
-<style scoped lang="scss"></style>
+<style scoped lang="scss">
+.home {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  h1 {
+    font-size: 4rem;
+  }
+}
+.img {
+  width: 100%;
+  height: 100%;
+}
+</style>

+ 9 - 5
src/views/workAttendance/Leave.vue

@@ -5,6 +5,8 @@ import { ElMessage } from 'element-plus'
 import TableBase from '@/components/TableBase/index.vue'
 import { ColumnProps } from '@/components/TableBase/interface/index'
 import { Leave_List, Leave_Approval } from '@/api/workAttendance/index'
+import { getFormatDuration } from '@/utils/common'
+
 import { GlobalStore } from '@/stores/index'
 
 const globalStore = GlobalStore()
@@ -19,7 +21,7 @@ interface UserInfoIn {
   T_end_time: string
   T_text: string
   Id: string
-  T_duration: string
+  T_duration: number
 }
 const userInfo = ref<UserInfoIn>({
   T_user_name: '',
@@ -30,7 +32,7 @@ const userInfo = ref<UserInfoIn>({
   T_end_time: '',
   T_text: '',
   Id: '',
-  T_duration: ''
+  T_duration: 0
 })
 const initParam = {
   User_tokey: globalStore.GET_User_tokey
@@ -128,7 +130,9 @@ onUnmounted(() => {
               }}</el-text></el-descriptions-item
             >
             <el-descriptions-item label="请假时长:">
-              <el-text class="mx-1" type="primary">{{ userInfo.T_duration ? userInfo.T_duration : '-' }}</el-text>
+              <el-text class="mx-1" type="primary">{{
+                userInfo.T_duration ? getFormatDuration(userInfo.T_duration) : '-'
+              }}</el-text>
             </el-descriptions-item>
             <el-descriptions-item label="内容:">
               <el-text class="mx-1" type="primary">{{ userInfo.T_text ? userInfo.T_text : '-' }}</el-text>
@@ -153,12 +157,12 @@ onUnmounted(() => {
   }
   .title {
     width: 100%;
-    text-align: center;
+    text-align: left;
   }
   .btn {
     margin-top: 2rem;
     display: flex;
-    justify-content: space-around;
+    justify-content: end;
   }
   .info-content {
     display: flex;

+ 28 - 41
src/views/workAttendance/MyOvertime.vue

@@ -34,13 +34,13 @@ const TableStatRef = ref()
 const columns: ColumnProps[] = [
   { prop: 'T_start_time', label: '开始时间' },
   { prop: 'T_end_time', label: '结束时间' },
-  { prop: 'T_duration', label: '时长', name: 'T_duration' },
-  { prop: 'T_State', label: '审核', name: 'T_State' },
+  { prop: 'T_duration', label: '时长', width: '100px', name: 'T_duration' },
+  { prop: 'T_State', label: '审核', width: '80px', name: 'T_State' },
   { prop: 'operation', label: '操作', width: 200, fixed: 'right' }
 ]
 const columns_Stat: ColumnProps[] = [
-  { prop: 'T_duration', label: '时长' },
-  { prop: 'RemainingTime', label: '剩余时长' },
+  { prop: 'T_duration', label: '时长', width: '100px', name: 'T_duration' },
+  { prop: 'RemainingTime', label: '剩余时长', width: '100px', name: 'RemainingTime' },
   { prop: 'T_type_name', label: '事项' },
   { prop: 'T_approver_name', label: '处理人' },
   { prop: 'UpdateTime', label: '时间' }
@@ -57,6 +57,11 @@ const openDrawerOvertime = (str: string, row: any) => {
       uuid = row.T_approver
       form.value.T_approver = row.T_user_name
       form.value.T_id = row.Id
+      form.value.T_prove_img = 'https://erposs.baozhida.cn' + form.value.T_prove_img
+      fileList.value.push({
+        name: 'food.jpeg',
+        url: form.value.T_prove_img
+      })
     })
   }
 }
@@ -88,19 +93,13 @@ const OvertimeDelete = (row: any) => {
       }
     })
     .catch(() => {
-      ElMessage({
-        type: 'warning',
-        message: '取消成功!'
-      })
+      ElMessage.warning('取消成功!')
     })
 }
 const AddOvertime = (formEl: FormInstance | undefined) => {
   if (!formEl) return
   if (!prove_img) {
-    ElMessage({
-      type: 'warning',
-      message: `请等待图片上传完成`
-    })
+    ElMessage.warning('请等待图片上传完成')
     return
   }
   formEl.validate(async valid => {
@@ -113,10 +112,7 @@ const AddOvertime = (formEl: FormInstance | undefined) => {
         res = await Overtime_Edit({ ...form.value, T_approver: uuid })
       }
       if (res.Code === 200) {
-        ElMessage({
-          type: 'success',
-          message: `${isNew ? '申请' : '修改'}成功!`
-        })
+        ElMessage.success(`${isNew ? '申请' : '修改'}成功!`)
         nextTick(() => {
           drawerRef.value.closeDrawer()
           TableRef.value.getTableList()
@@ -135,6 +131,7 @@ const ruleFormRef = ref<FormInstance>()
 type Fn = () => void
 const callbackDrawer = (done: Fn) => {
   disabled.value = false
+  isNew = false
   nextTick(() => {
     resetForm(ruleFormRef.value)
     done()
@@ -142,6 +139,7 @@ const callbackDrawer = (done: Fn) => {
 }
 const resetForm = (formEl: FormInstance | undefined) => {
   if (!formEl) return
+  fileList.value = []
   formEl.resetFields()
 }
 const validate_float = (rule: any, value: any, callback: any) => {
@@ -217,7 +215,7 @@ const upload = ref()
 let uploadData = { token: '', key: '' }
 const dialogImageUrl = ref('')
 const dialogVisible = ref(false)
-const fileList = ref([])
+const fileList = ref<any>([])
 let prove_img = true
 const handlePictureCardPreview = async (file: any) => {
   console.log(file, fileList)
@@ -229,10 +227,7 @@ const beforeUpload = async (file: any) => {
 
   let reg = /^image/g
   if (!reg.test(file.type)) {
-    ElMessage({
-      type: 'error',
-      message: '必须上传图片!!'
-    })
+    ElMessage.error('必须上传图片!!')
     return
   }
   prove_img = false
@@ -242,13 +237,10 @@ const beforeUpload = async (file: any) => {
   uploadData.key = file.name
 }
 const handleExceed: UploadProps['onExceed'] = (files: any) => {
-  console.log(files)
   upload.value.clearFiles()
   const file = files[0] as UploadRawFile
   file.uid = genFileId()
-  console.log(file)
   upload.value.handleStart(file)
-
   submitUpload()
 }
 
@@ -260,25 +252,17 @@ const onSuccess = (response: any, uploadFile: UploadFile, uploadFiles: UploadFil
   console.log(response, uploadFile, uploadFiles)
   form.value.T_prove_img = response.key
   prove_img = true
-  ElMessage({
-    type: 'success',
-    message: '图片上传成功!!'
-  })
+  ElMessage.success('图片上传成功!!')
 }
+
 const onError = () => {
-  // console.log(error, uploadFile, uploadFiles)
-  ElMessage({
-    type: 'error',
-    message: '图片上传失败!!'
-  })
+  ElMessage.error('图片上传失败!!')
 }
+
 const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
   console.log(uploadFile, uploadFiles, fileList)
   form.value.T_prove_img = ''
 }
-// const handleProgress = (evt: UploadProgressEvent, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
-//   console.log(evt, uploadFile, uploadFiles)
-// }
 </script>
 
 <template>
@@ -325,7 +309,6 @@ const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
           <el-button v-if="row.T_State === 1" link type="success" size="small" :icon="View" @click="OvertimeView(row)"
             >查看</el-button
           >
-          <!-- <el-icon><View /></el-icon> -->
         </template>
       </TableBase></el-col
     >
@@ -339,12 +322,13 @@ const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
       >
         <template #table-header="{ pageable }">
           <el-button text
-            ><h4>剩余总时长:{{ pageable.RemainingTime }}</h4></el-button
+            ><h4>
+              <el-text class="mx-1" type="primary">剩余总时长:{{ getFormatDuration(pageable.RemainingTime) }}</el-text>
+            </h4></el-button
           ></template
         >
-        <template #T_name="{ row }">
-          <el-button type="primary" text>{{ row.T_name }}</el-button>
-        </template>
+        <template #T_duration="{ row }">{{ getFormatDuration(row.T_duration) }} </template>
+        <template #RemainingTime="{ row }">{{ getFormatDuration(row.RemainingTime) }} </template>
       </TableBase>
     </el-col>
     <Drawer ref="drawerRef" :handleClose="callbackDrawer">
@@ -476,4 +460,7 @@ const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
     white-space: nowrap;
   }
 }
+.mx-1 {
+  letter-spacing: 1px;
+}
 </style>

+ 8 - 11
src/views/workAttendance/Overtime.vue

@@ -21,6 +21,7 @@ interface UserInfoIn {
   T_text: string
   Id: string
   T_duration: string
+  T_prove_img: string
 }
 const userInfo = ref<UserInfoIn>({
   T_user_name: '',
@@ -31,6 +32,7 @@ const userInfo = ref<UserInfoIn>({
   T_end_time: '',
   T_text: '',
   Id: '',
+  T_prove_img: '',
   T_duration: ''
 })
 
@@ -38,7 +40,8 @@ const initParam = {
   User_tokey: globalStore.GET_User_tokey
 }
 const getSalaryParams = (row: any) => {
-  let duration = (userInfo.value = { ...row })
+  console.log(row)
+  userInfo.value = { ...row, T_prove_img: 'https://erposs.baozhida.cn' + row.T_prove_img }
 }
 
 const LeaveUser = async (T_State: number) => {
@@ -51,15 +54,9 @@ const LeaveUser = async (T_State: number) => {
   const res: any = await Overtime_Approval(params)
   if (res.Code === 200) {
     if (T_State) {
-      ElMessage({
-        type: 'success',
-        message: '审核通过!'
-      })
+      ElMessage.success('审核通过!')
     } else {
-      ElMessage({
-        type: 'warning',
-        message: '审核不通过!'
-      })
+      ElMessage.warning('审核不通过!')
     }
     nextTick(() => {
       TableRef.value.getTableList()
@@ -141,7 +138,7 @@ onUnmounted(() => {
           <el-row>
             <el-col style="display: flex">
               取证:
-              <img src="@/assets/images/avatar.jpg" class="img" />
+              <img :src="userInfo.T_prove_img" class="img" />
             </el-col>
           </el-row>
           <el-row>
@@ -190,7 +187,7 @@ onUnmounted(() => {
   .btn {
     margin-top: 2rem;
     display: flex;
-    justify-content: space-around;
+    justify-content: end;
   }
   .info-content {
     display: flex;

+ 114 - 96
src/views/workAttendance/RecordsFinance.vue

@@ -1,84 +1,73 @@
 <script setup lang="ts">
-import { ref, reactive } from 'vue'
+import { ref, reactive, onMounted, computed, onUnmounted } from 'vue'
 import TableBase from '@/components/TableBase/index.vue'
 import { User_List } from '@/api/user/index'
-import { Salary_Get } from '@/api/salary/index'
 import { ColumnProps } from '@/components/TableBase/interface/index'
 import { GlobalStore } from '@/stores/index'
 import { ElMessage } from 'element-plus'
-// import SalaryFrom from './SalaryFrom.vue'
-const globalStore = GlobalStore()
+import { Leave_Finance_List } from '@/api/workAttendance/index'
+import { getFormatDuration } from '@/utils/common'
+import Pagination from '@/components/TableBase/components/Pagination.vue'
 
-const TableRef = ref()
-const salaryFromRef = ref()
+const globalStore = GlobalStore()
+const LeaveTableRef = ref()
 // 搜索以及参数
 const columns: ColumnProps[] = [
   { prop: 'T_name', label: '姓名', name: 'T_name' },
   { prop: 'T_post_name', label: '职位' }
 ]
 const userColums: ColumnProps[] = [
-  { prop: 'T_post_name', label: '请假类型' },
-  { prop: 'T_post_name', label: '开始时间' },
-  { prop: 'T_post_name', label: '结束时间' },
-  { prop: 'T_post_name', label: '请假时长' },
-  { prop: 'T_post_name', label: '理由' },
-  { prop: 'T_post_name', label: '审核' }
+  { prop: 'T_type_name', label: '请假类型' },
+  { prop: 'T_start_time', label: '开始时间' },
+  { prop: 'T_end_time', label: '结束时间' },
+  { prop: 'T_duration', label: '请假时长', name: 'T_duration' },
+  { prop: 'T_text', label: '理由' },
+  { prop: 'T_State', label: '审核', name: 'T_State' }
 ]
-const searchHandle = () => {
-  initParam.T_name = searchs.T_name
-  initParam.T_dept = searchs.T_dept
-  TableRef.value.searchTable()
-}
-const searchs = reactive({
-  T_name: '',
-  T_dept: ''
-})
 let date = new Date()
 const year = date.getFullYear()
 const month = date.getMonth()
-const salaryFromData = reactive({
+const salaryFromData = ref({
   year: year + '',
   month: (month < 10 ? '0' : '') + month,
   T_uuid: ''
 })
 
-const initParam = {
+const dateCom = computed(() => {
+  return `${salaryFromData.value.year}-${salaryFromData.value.month}`
+})
+
+const initParam = { User_tokey: globalStore.GET_User_tokey }
+const LeaveinitParam = reactive({
   User_tokey: globalStore.GET_User_tokey,
-  T_name: '',
-  T_dept: ''
-}
+  T_uuid: '',
+  T_month: dateCom.value
+})
+
 const getSalaryParams = (row: any) => {
   userInfo.name = row.T_name
   userInfo.T_dept = row.T_dept_name
   userInfo.T_post = row.T_post_name
+  LeaveinitParam.T_uuid = row.T_uuid
 
-  salaryFromData.T_uuid = row.T_uuid
-  getSalary()
-}
-const getSalary = async () => {
-  let T_date = `${salaryFromData.year}-${salaryFromData.month}`
-  const res = await Salary_Get({ T_uuid: salaryFromData.T_uuid, T_date })
-  salaryFromRef.value.DataEcho(res.Data)
+  LeaveTableRef.value.searchTable()
 }
+
 const salarDateMonthChange = (val: string) => {
   if (!val) return
-  if (!salaryFromData.T_uuid) {
-    ElMessage({
-      message: '请选择员工!!!',
-      type: 'warning'
-    })
+  if (!LeaveinitParam.T_uuid) {
+    ElMessage.warning('请选择员工!!!')
   }
-  getSalary()
+  LeaveinitParam.T_month = dateCom.value
+  LeaveTableRef.value.searchTable()
 }
 const salarDateYearChange = (val: string) => {
   if (!val) return
-  if (!salaryFromData.T_uuid) {
-    ElMessage({
-      message: '请选择员工!!!',
-      type: 'warning'
-    })
+  if (!LeaveinitParam.T_uuid) {
+    ElMessage.warning('请选择员工!!!')
   }
-  getSalary()
+  LeaveinitParam.T_month = dateCom.value
+  LeaveTableRef.value.searchTable()
 }
 const userInfo = reactive({
   squareUrl: 'https://cube.elemecdn.com/9/c2/f0ee8a3c7c9638a54940382568c9dpng.png',
@@ -90,26 +79,68 @@ const onResize = () => {
   const height = document.documentElement.clientHeight
   return height - 140 - 74 - 60 - 4 * 12
 }
+
+const TableData = ref()
+const getTableData = async () => {
+  const res: any = await User_List({ ...initParam })
+  TableData.value = res.Data.Data
+  pageable.total = res.Data.Num
+}
+onMounted(async () => {
+  await getTableData()
+  let userInfoFirst = TableData.value[0]
+  LeaveinitParam.T_uuid = userInfoFirst.T_uuid
+  userInfo.name = userInfoFirst.T_name
+  userInfo.T_dept = userInfoFirst.T_dept_name
+  userInfo.T_post = userInfoFirst.T_post_name
+  console.log(LeaveinitParam.T_uuid)
+})
+const pageable = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  total: 0,
+  small: false,
+  disabled: false
+})
+const handleSizeChange = (val: number) => {
+  pageable.pageSize = val
+  LeaveTableRef.value.getTableList()
+}
+
+const handleCurrentChange = (val: number) => {
+  pageable.pageNum = val
+  LeaveTableRef.value.getTableList()
+}
+let clientHeight = ref(0)
+const onContentResize = () => {
+  const height = document.documentElement.clientHeight
+  clientHeight.value = height - 140 - 60 - 3 * 12
+}
+onMounted(() => {
+  onContentResize()
+  window.onresize = onContentResize
+})
+onUnmounted(() => (window.onresize = null))
 </script>
 
 <template>
-  <div class="salary">
-    <div style="width: 290px">
-      <TableBase
-        ref="TableRef"
-        :columns="columns"
-        :requestApi="User_List"
-        :initParam="initParam"
+  <div class="RecordsFinance">
+    <div style="width: 290px; display: flex; padding: 20px; background: #fff; flex-direction: column">
+      <el-table class="h-100" style="width: 100%; height: 100%; flex: 1" :data="TableData" @row-click="getSalaryParams">
+        <el-table-column
+          align="center"
+          v-for="item in columns"
+          :label="item.label"
+          :prop="item.prop"
+          :key="item.prop"
+        />
+      </el-table>
+      <Pagination
         layout="total, prev, pager, next"
-        :rowClick="getSalaryParams"
-      >
-        <template #table-header>
-          <h3 class="title w-100">待处理</h3>
-        </template>
-        <template #T_name="{ row }">
-          <el-button type="primary" text @click="getSalaryParams(row)">{{ row.T_name }}</el-button>
-        </template>
-      </TableBase>
+        :pageable="pageable"
+        :handleSizeChange="handleSizeChange"
+        :handleCurrentChange="handleCurrentChange"
+      />
     </div>
     <el-row class="h-100 f-1 margin-left-3">
       <el-col :span="24" class="h-100" style="overflow: hidden">
@@ -150,52 +181,39 @@ const onResize = () => {
             </el-col>
           </el-row>
         </el-card>
-        <TableBase
-          ref="TableRef"
-          :columns="columns"
-          :requestApi="User_List"
-          :initParam="initParam"
-          layout="prev, pager, next"
-          :onResize="onResize"
-          :rowClick="getSalaryParams"
-          :displayHeader="true"
-        >
-          <template #T_name="{ row }">
-            <el-button type="primary" text @click="getSalaryParams(row)">{{ row.T_name }}</el-button>
-          </template>
-        </TableBase>
+        <div :style="{ height: clientHeight + 'px' }">
+          <TableBase
+            v-if="LeaveinitParam.T_uuid"
+            ref="LeaveTableRef"
+            :columns="userColums"
+            :requestApi="Leave_Finance_List"
+            :initParam="LeaveinitParam"
+            layout="total,prev, pager, next"
+            :onResize="onResize"
+            :displayHeader="true"
+          >
+            <template #T_duration="{ row }">{{ getFormatDuration(row.T_duration) }}</template>
+            <template #T_State="{ row }">
+              <el-tag v-if="row.T_State === 1" type="success">通过</el-tag>
+              <el-tag v-else-if="row.T_State === 2" type="warning">未通过</el-tag>
+              <el-tag v-else type="danger">待审核</el-tag>
+            </template>
+          </TableBase>
+        </div>
       </el-col>
     </el-row>
   </div>
 </template>
 
 <style scoped lang="scss">
-.salary {
+.RecordsFinance {
   display: flex;
+  height: 100%;
   overflow: hidden;
   .w-100 {
     width: 100%;
   }
 }
-
-.submit {
-  padding: 0.5rem 1.875rem;
-  margin-top: 1.25rem;
-}
-.form-card {
-  overflow: auto;
-  .form-card-right,
-  .form-card-left {
-    display: flex;
-    flex-direction: column;
-  }
-  .form-card-right {
-    align-items: end;
-  }
-  .form-card-left {
-    align-items: start;
-  }
-}
 .d-flex {
   display: flex;
   justify-content: center;
@@ -218,7 +236,7 @@ const onResize = () => {
   }
 }
 .title {
-  text-align: center;
+  text-align: left;
 }
 .info-content {
   display: flex;

+ 251 - 88
src/views/workAttendance/records/Records.vue

@@ -1,33 +1,92 @@
 <script setup lang="ts">
-import { ref, reactive, onMounted, onUnmounted } from 'vue'
-import { ColumnProps } from '@/components/TableBase/interface/index'
-import TableBase from '@/components/TableBase/index.vue'
+import { View } from '@element-plus/icons-vue'
+import { getFormatDuration } from '@/utils/common'
 import { User_List } from '@/api/user/index'
 import { GlobalStore } from '@/stores/index'
+import Drawer from '@/components/Drawer/index.vue'
+import TableBase from '@/components/TableBase/index.vue'
+import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue'
+import { ColumnProps } from '@/components/TableBase/interface/index'
+import { Leave_User_list, Overtime_User_list, Overtime_Stat } from '@/api/workAttendance/index'
+
+let clientHeight = ref(0)
+const TableData = ref([])
 const globalStore = GlobalStore()
+const TableRef = ref<InstanceType<typeof TableBase> | null>(null)
+const leaveRef = ref<InstanceType<typeof TableBase> | null>(null)
+const overtimeRef = ref<InstanceType<typeof TableBase> | null>(null)
+const remainingTimeRef = ref<InstanceType<typeof TableBase> | null>(null)
+const drawerLeaveRef = ref<InstanceType<typeof Drawer> | null>(null)
+const drawerOvertimeRef = ref<InstanceType<typeof Drawer> | null>(null)
+
 const columns: ColumnProps[] = [{ prop: 'T_name', label: '姓名', name: 'T_name' }]
 const overtimeColums: ColumnProps[] = [
-  { prop: 'T_name', label: '姓名', name: 'T_name' },
-  { prop: 'T_dept_name', label: '姓名', name: 'T_name' },
-  { prop: 'T_post_name', label: '姓名', name: 'T_name' },
-  { prop: 'T_phone', label: '姓名', name: 'T_name' },
-  { prop: 'T_dept', label: '姓名', name: 'T_name' },
-  { prop: 'T_post', label: '姓名', name: 'T_name' }
+  { prop: 'T_start_time', label: '开始时间' },
+  { prop: 'T_end_time', label: '结束时间' },
+  { prop: 'T_duration', label: '时长', name: 'T_duration' },
+  { prop: 'T_State', label: '审核', name: 'T_State' },
+  { prop: 'T_approver_name', label: '处理人' },
+  { prop: 'operation', label: '操作', width: 200, fixed: 'right' }
+]
+const remainingTimeColums: ColumnProps[] = [
+  { prop: 'T_duration', label: '时长', name: 'T_duration' },
+  { prop: 'RemainingTime', label: '剩余时长', name: 'RemainingTime' },
+  { prop: 'T_type_name', label: '事项' },
+  { prop: 'T_approver_name', label: '处理人' },
+  { prop: 'UpdateTime', label: '时间' }
+]
+const leaveColums: ColumnProps[] = [
+  { prop: 'T_type_name', label: '请假类型' },
+  { prop: 'T_start_time', label: '开始时间' },
+  { prop: 'T_end_time', label: '结束时间' },
+  { prop: 'T_duration', label: '请假时长', name: 'T_duration' },
+  { prop: 'T_text', label: '理由' },
+  { prop: 'T_State', label: '审核', name: 'T_State' },
+  { prop: 'T_approver_name', label: '处理人' },
+  { prop: 'operation', label: '操作', width: 200, fixed: 'right' }
 ]
-const remainingTimeColums: ColumnProps[] = []
-const leaveColums: ColumnProps[] = []
+
 const initParam = {
   User_tokey: globalStore.GET_User_tokey,
   T_name: '',
   T_dept: ''
 }
+const userInitParam = reactive({
+  User_tokey: globalStore.GET_User_tokey,
+  T_uuid: ''
+})
 const userInfo = reactive({
   squareUrl: 'https://cube.elemecdn.com/9/c2/f0ee8a3c7c9638a54940382568c9dpng.png',
   name: '',
   T_dept: '',
   T_post: ''
 })
-let clientHeight = ref(0)
+
+// 加班记录
+let url = ''
+let srcList: string[] = []
+const OvertimeEmptyObject = {
+  T_start_time: '',
+  T_end_time: '',
+  T_text: ''
+} as Record<string, unknown>
+const OvertimeInfo = ref(OvertimeEmptyObject)
+// 请假记录
+const LeaveInfo = ref<Record<string, string>>({
+  T_type_name: '',
+  T_start_time: '',
+  T_end_time: '',
+  T_duration: '',
+  T_text: ''
+})
+
+const userInfoAssign = (row: any) => {
+  userInitParam.T_uuid = row.T_uuid
+  userInfo.name = row.T_name
+  userInfo.T_dept = row.T_dept_name
+  userInfo.T_post = row.T_post_name
+}
+
 const onContentResize = () => {
   const height = document.documentElement.clientHeight
   clientHeight.value = height - 140 - 60 - 3 * 12
@@ -38,29 +97,97 @@ onMounted(() => {
 })
 onUnmounted(() => (window.onresize = null))
 const onResizeItem = () => 336
-const onResize = () => {
-  const height = document.documentElement.clientHeight
-  const table_header = document.querySelector('.table-header') as HTMLDivElement
-  return height - table_header.clientHeight - 20 - 60 - 12
+// 点击行
+const getSalaryParams = (row: any) => {
+  if (row.T_uuid === userInitParam.T_uuid) return
+  userInfoAssign(row)
+  leaveRef.value && leaveRef.value.searchTable()
+  overtimeRef.value && overtimeRef.value.searchTable()
+  remainingTimeRef.value && remainingTimeRef.value.searchTable()
+}
+// 获取数据
+const getTableData = async () => {
+  const res: any = await User_List({ ...initParam })
+  TableData.value = res.Data.Data
+  pageable.total = res.Data.Num
+}
+
+onMounted(async () => {
+  await getTableData()
+  let userInfoFirst: any = TableData.value[0]
+  userInfoAssign(userInfoFirst)
+})
+// 分页
+const pageable = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  total: 0,
+  small: false,
+  disabled: false
+})
+const handleSizeChange = (val: number) => {
+  pageable.pageSize = val
+  TableRef.value && TableRef.value.getTableList()
+}
+const handleCurrentChange = (val: number) => {
+  pageable.pageNum = val
+  TableRef.value && TableRef.value.getTableList()
+}
+// 查看加班记录
+const viewOvertime = (row: any) => {
+  url = 'https://erposs.baozhida.cn' + row.T_prove_img
+  srcList.push(url)
+  OvertimeInfo.value = { ...row }
+  console.log(OvertimeInfo.value)
+  drawerOvertimeRef.value && drawerOvertimeRef.value.openDrawer()
+}
+const closerOvertime = () => {
+  drawerOvertimeRef.value && drawerOvertimeRef.value.closeDrawer()
+  drawerLeaveRef.value && drawerLeaveRef.value.closeDrawer()
+  nextTick(() => {
+    url = ''
+    srcList = []
+    LeaveInfo.value = {}
+    OvertimeInfo.value = {}
+  })
+}
+// 查看请假
+const viewoLeave = (row: any) => {
+  LeaveInfo.value = { ...row }
+  console.log(LeaveInfo.value)
+  LeaveInfo.value.T_duration = getFormatDuration(row.T_duration)
+  drawerLeaveRef.value && drawerLeaveRef.value.openDrawer()
+}
+type Fn = () => void
+const callbackDrawer = (done: Fn) => {
+  done()
 }
 </script>
 
 <template>
   <div class="records">
-    <div style="width: 290px">
-      <TableBase
+    <div class="userTable">
+      <el-table
         ref="TableRef"
-        :columns="columns"
-        :requestApi="User_List"
-        :initParam="initParam"
-        layout="total, prev, pager, next"
-        :onResize="onResize"
+        class="h-100"
+        style="width: 100%; height: 100%; flex: 1"
+        :data="TableData"
+        @row-click="getSalaryParams"
       >
-        <template #table-header> <h4 class="title">待处理</h4> </template>
-        <template #T_name="{ row }">
-          <el-button type="primary" text>{{ row.T_name }}</el-button>
-        </template>
-      </TableBase>
+        <el-table-column
+          align="center"
+          v-for="item in columns"
+          :label="item.label"
+          :prop="item.prop"
+          :key="item.prop"
+        />
+      </el-table>
+      <Pagination
+        layout="total, prev, pager, next"
+        :pageable="pageable"
+        :handleSizeChange="handleSizeChange"
+        :handleCurrentChange="handleCurrentChange"
+      />
     </div>
     <el-row class="h-100 f-1 margin-left-3">
       <el-col :span="24" class="h-100" style="overflow: hidden">
@@ -80,55 +207,67 @@ const onResize = () => {
         <div class="content" :style="{ height: clientHeight + 'px' }">
           <div>
             <div class="content-table-item">
-              <el-text class="font-large" size="large" type="success">加班记录:</el-text>
-
+              <el-tag class="mx-1 font-large" type="success" effect="dark">加班记录:</el-tag>
               <TableBase
-                ref="TableRef"
-                border
+                ref="overtimeRef"
+                v-if="userInitParam.T_uuid"
                 :columns="overtimeColums"
-                :requestApi="User_List"
-                :initParam="initParam"
+                :requestApi="Overtime_User_list"
+                :initParam="userInitParam"
                 :onResize="onResizeItem"
                 :displayHeader="true"
                 layout="prev, pager, next"
               >
-                <template #T_name="{ row }">
-                  <el-button type="primary" text>{{ row.T_name }}</el-button>
+                <template #T_State="{ row }">
+                  <el-tag v-if="row.T_State === 1" type="success">通过</el-tag>
+                  <el-tag v-else-if="row.T_State === 2" type="warning">未通过</el-tag>
+                  <el-tag v-else type="danger">待审核</el-tag>
+                </template>
+                <template #T_duration="{ row }">{{ getFormatDuration(row.T_duration) }}</template>
+                <template #right="{ row }">
+                  <el-button type="success" size="small" link :icon="View" @click="viewOvertime(row)">查看</el-button>
                 </template>
               </TableBase>
             </div>
             <div class="content-table-item">
-              <el-text class="font-large" size="large" type="warning">剩余总时长:1小时 19分</el-text>
-
               <TableBase
-                ref="TableRef"
-                border
-                :columns="overtimeColums"
-                :requestApi="User_List"
-                :initParam="initParam"
+                ref="remainingTimeRef"
+                v-if="userInitParam.T_uuid"
+                :columns="remainingTimeColums"
+                :requestApi="Overtime_Stat"
+                :initParam="userInitParam"
                 :onResize="onResizeItem"
-                :displayHeader="true"
                 layout="prev, pager, next"
               >
-                <template #T_name="{ row }">
-                  <el-button type="primary" text>{{ row.T_name }}</el-button>
+                <template #table-header="{ pageable }">
+                  <el-tag class="mx-1" effect="dark">
+                    剩余总时长:{{ getFormatDuration(pageable.RemainingTime as number) }}
+                  </el-tag>
                 </template>
+                <template #T_duration="{ row }">{{ getFormatDuration(row.T_duration) }}</template>
+                <template #RemainingTime="{ row }">{{ getFormatDuration(row.RemainingTime) }}</template>
               </TableBase>
             </div>
             <div class="content-table-item">
-              <el-text class="font-large" size="large" type="danger">请假记录:</el-text>
+              <el-tag class="mx-1 font-large" type="danger" effect="dark">请假记录:</el-tag>
               <TableBase
-                ref="TableRef"
-                border
-                :columns="overtimeColums"
-                :requestApi="User_List"
-                :initParam="initParam"
+                ref="leaveRef"
+                v-if="userInitParam.T_uuid"
+                :columns="leaveColums"
+                :requestApi="Leave_User_list"
+                :initParam="userInitParam"
                 :onResize="onResizeItem"
                 :displayHeader="true"
                 layout="prev, pager, next"
               >
-                <template #T_name="{ row }">
-                  <el-button type="primary" text>{{ row.T_name }}</el-button>
+                <template #T_State="{ row }">
+                  <el-tag v-if="row.T_State === 1" type="success">通过</el-tag>
+                  <el-tag v-else-if="row.T_State === 2" type="warning">未通过</el-tag>
+                  <el-tag v-else type="danger">待审核</el-tag>
+                </template>
+                <template #T_duration="{ row }">{{ getFormatDuration(row.T_duration as number) }}</template>
+                <template #right="{ row }">
+                  <el-button type="success" size="small" link :icon="View" @click="viewoLeave(row)">查看</el-button>
                 </template>
               </TableBase>
             </div>
@@ -136,42 +275,66 @@ const onResize = () => {
         </div>
       </el-col>
     </el-row>
+    <Drawer ref="drawerOvertimeRef" :handleClose="callbackDrawer">
+      <template #header="{ params }">
+        <h4 :id="params.titleId" :class="params.titleClass">加班记录</h4>
+      </template>
+      <div>
+        <el-descriptions :column="1" size="large" border>
+          <el-descriptions-item label="开始时间:"
+            ><el-text class="mx-1" type="primary">{{ OvertimeInfo.T_start_time }}</el-text></el-descriptions-item
+          >
+          <el-descriptions-item label="结束时间:"
+            ><el-text class="mx-1" type="primary">{{ OvertimeInfo.T_end_time }}</el-text></el-descriptions-item
+          >
+          <el-descriptions-item label="取证:" :span="2">
+            <el-image
+              style="width: 100px; height: 100px"
+              :src="url"
+              :zoom-rate="1.2"
+              :preview-src-list="srcList"
+              :initial-index="4"
+              fit="cover"
+          /></el-descriptions-item>
+          <el-descriptions-item label="内容:">
+            <el-text class="mx-1" type="primary">{{ OvertimeInfo.T_text }}</el-text>
+          </el-descriptions-item>
+        </el-descriptions>
+        <div class="btn">
+          <el-button type="primary" @click="closerOvertime">关闭</el-button>
+        </div>
+      </div>
+    </Drawer>
+    <Drawer ref="drawerLeaveRef" :handleClose="callbackDrawer">
+      <template #header="{ params }">
+        <h4 :id="params.titleId" :class="params.titleClass">请假记录</h4>
+      </template>
+      <div>
+        <el-descriptions :column="1" size="large" border>
+          <el-descriptions-item label="请假类型:"
+            ><el-text class="mx-1" type="danger">{{ LeaveInfo.T_type_name }}</el-text></el-descriptions-item
+          >
+          <el-descriptions-item label="开始时间:"
+            ><el-text class="mx-1" type="primary">{{ LeaveInfo.T_start_time }}</el-text></el-descriptions-item
+          >
+          <el-descriptions-item label="结束时间:"
+            ><el-text class="mx-1" type="primary">{{ LeaveInfo.T_end_time }}</el-text></el-descriptions-item
+          >
+          <el-descriptions-item label="请假时长:"
+            ><el-text class="mx-1" type="primary">{{ LeaveInfo.T_duration }}</el-text></el-descriptions-item
+          >
+          <el-descriptions-item label="内容:">
+            <el-text class="mx-1" type="primary">{{ LeaveInfo.T_text }}</el-text>
+          </el-descriptions-item>
+        </el-descriptions>
+        <div class="btn">
+          <el-button type="primary" @click="closerOvertime">关闭</el-button>
+        </div>
+      </div>
+    </Drawer>
   </div>
 </template>
 
 <style scoped lang="scss">
-.records {
-  display: flex;
-  overflow: hidden;
-  .title {
-    width: 100%;
-    text-align: center;
-  }
-  .content {
-    width: 99.9%;
-    height: 700px;
-    background: #fff;
-    overflow-y: auto;
-    .content-table-item {
-      padding: 20px;
-      .font-large {
-        font-size: var(--el-font-size-extra-large);
-        margin-bottom: 10px;
-        display: inline-block;
-      }
-    }
-  }
-  .info-content {
-    display: flex;
-    color: #303133;
-    .info-name {
-      display: flex;
-      flex-direction: column;
-      padding-left: 0.75rem;
-      span:first-child {
-        margin-right: 2rem;
-      }
-    }
-  }
-}
+@import './index.scss';
 </style>

+ 51 - 0
src/views/workAttendance/records/index.scss

@@ -0,0 +1,51 @@
+.records {
+  display: flex;
+  height: 100%;
+  overflow: hidden;
+  :deep(.table-header) {
+    border: none;
+    padding: 0;
+  }
+  .btn {
+    margin-top: 2rem;
+    display: flex;
+    justify-content: center;
+  }
+  .userTable {
+    width: 290px;
+    display: flex;
+    padding: 20px;
+    background: #fff;
+    flex-direction: column;
+  }
+  .title {
+    width: 100%;
+    text-align: left;
+  }
+  .content {
+    width: 99.9%;
+    height: 700px;
+    background: #fff;
+    overflow-y: auto;
+    .content-table-item {
+      padding: 20px;
+      .font-large {
+        // font-size: var(--el-font-size-extra-large);
+        margin-bottom: 10px;
+        // display: inline-block;
+      }
+    }
+  }
+  .info-content {
+    display: flex;
+    color: #303133;
+    .info-name {
+      display: flex;
+      flex-direction: column;
+      padding-left: 0.75rem;
+      span:first-child {
+        margin-right: 2rem;
+      }
+    }
+  }
+}

+ 16 - 16
vite.config.ts

@@ -22,22 +22,22 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
     },
     server: {
       open: true,
-      host: '0.0.0.0',
-      proxy: {
-        '/api': {
-          target: 'https://erp.baozhida.cn',
-          changeOrigin: true
-          // rewrite: path => path.replace(/^\/api/, '')
-        },
-        '/salary': {
-          target: 'http://erp.baozhida.cn',
-          changeOrigin: true
-        },
-        '/ams': {
-          target: 'http://erp.baozhida.cn',
-          changeOrigin: true
-        }
-      }
+      host: '0.0.0.0'
+      // proxy: {
+      //   '/api': {
+      //     target: 'https://erp.baozhida.cn',
+      //     changeOrigin: true
+      //     // rewrite: path => path.replace(/^\/api/, '')
+      //   },
+      //   '/salary': {
+      //     target: 'http://erp.baozhida.cn',
+      //     changeOrigin: true
+      //   },
+      //   '/ams': {
+      //     target: 'http://erp.baozhida.cn',
+      //     changeOrigin: true
+      //   }
+      // }
     },
     plugins: [
       vue(),