ソースを参照

完成加班审批、统筹管理,优化了登录页面

@sun-chaoqun 2 年 前
コミット
5bf21955d9

+ 98 - 3
src/layouts/Header/Breadcrumb.vue

@@ -1,4 +1,4 @@
-<script setup lang="ts">
+<!-- <script setup lang="ts">
 import { ArrowRight } from '@element-plus/icons-vue'
 import { GlobalStore } from '@/stores/index'
 const globalStore = GlobalStore()
@@ -10,9 +10,104 @@ const globalStore = GlobalStore()
       <el-breadcrumb-item v-for="(item, index) in globalStore.GET_Breadcrumb" :key="index">{{
         item
       }}</el-breadcrumb-item>
-      <!-- <el-breadcrumb-item>promotion management</el-breadcrumb-item> -->
     </el-breadcrumb>
   </div>
 </template>
 
-<style scoped></style>
+<style scoped></style> -->
+
+<template>
+  <div class="breadcrumb-box">
+    <el-breadcrumb :separator-icon="ArrowRight">
+      <transition-group name="breadcrumb" mode="out-in">
+        <el-breadcrumb-item v-for="(item, index) in breadcrumbList" :key="item.path">
+          <div class="el-breadcrumb__inner is-link" @click="onBreadcrumbClick(item, index)">
+            <el-icon class="breadcrumb-icon" v-show="item.meta.icon">
+              <component :is="item.meta.icon"></component>
+            </el-icon>
+            <span class="breadcrumb-title">{{ item.meta.title }}</span>
+          </div>
+        </el-breadcrumb-item>
+      </transition-group>
+    </el-breadcrumb>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed } from 'vue'
+import { GlobalStore } from '@/stores/index'
+// import { AuthStore } from '@/stores/modules/auth'
+import { ArrowRight } from '@element-plus/icons-vue'
+import { useRoute, useRouter } from 'vue-router'
+// import { HOME_URL } from '@/config/config'
+
+const route = useRoute()
+const router = useRouter()
+// const authStore = AuthStore()
+const globalStore = GlobalStore()
+// const themeConfig = computed(() => globalStore.themeConfig)
+console.log(route, route.matched.length - 1)
+
+const breadcrumbList = computed(() => {
+  // let breadcrumbData: any = []
+  console.log(route.matched[route.matched.length - 1].path)
+  if (!globalStore.allBreadcrumbList) return []
+
+  // let breadcrumbData: any = globalStore.GET_Menu_List[] ?? []
+  let breadcrumbData = globalStore.allBreadcrumbList[route.matched[route.matched.length - 1].path] ?? []
+  console.log(breadcrumbData)
+  console.log(globalStore.allBreadcrumbList[''])
+  // 🙅‍♀️不需要首页面包屑可删除以下判断
+  if (breadcrumbData.length > 0 && breadcrumbData[0].T_name !== route.meta.title) {
+    breadcrumbData = [{ path: globalStore.path, meta: { icon: 'HomeFilled', title: '首页' } }, ...breadcrumbData]
+  }
+  return []
+})
+
+const onBreadcrumbClick = (item: Menu.MenuOptions, index: number) => {
+  if (index !== breadcrumbList.value.length - 1) router.push(item.path)
+}
+</script>
+
+<style scoped lang="scss">
+.breadcrumb-box {
+  display: flex;
+  align-items: center;
+  padding-right: 50px;
+  overflow: hidden;
+  mask-image: linear-gradient(90deg, #000000 0%, #000000 calc(100% - 50px), transparent);
+  .el-breadcrumb {
+    white-space: nowrap;
+    .el-breadcrumb__item {
+      position: relative;
+      display: inline-block;
+      float: none;
+      .el-breadcrumb__inner {
+        display: inline-flex;
+        .breadcrumb-icon {
+          margin-top: 2px;
+          margin-right: 6px;
+          font-size: 16px;
+        }
+        .breadcrumb-title {
+          margin-top: 3px;
+        }
+      }
+      :deep(.el-breadcrumb__separator) {
+        position: relative;
+        top: -1px;
+      }
+    }
+  }
+}
+.no-icon {
+  .el-breadcrumb {
+    .el-breadcrumb__item {
+      top: -2px;
+      :deep(.el-breadcrumb__separator) {
+        top: 2px;
+      }
+    }
+  }
+}
+</style>

+ 21 - 2
src/layouts/Header/index.vue

@@ -3,6 +3,7 @@ import { useRouter } from 'vue-router'
 import { GlobalStore } from '@/stores/index'
 import Breadcrumb from './Breadcrumb.vue'
 import { Expand, Fold } from '@element-plus/icons-vue'
+import 'element-plus/theme-chalk/src/dropdown.scss'
 const globalStore = GlobalStore()
 const router = useRouter()
 const logOut = () => {
@@ -15,7 +16,7 @@ const logOut = () => {
 <template>
   <div class="header">
     <el-row>
-      <el-col :span="20" class="d-flex">
+      <el-col :span="18" class="d-flex">
         <el-button
           class="isCollapse"
           type="primary"
@@ -23,7 +24,17 @@ const logOut = () => {
           link
           @click="globalStore.SET_isCollapse"
         ></el-button>
-        <Breadcrumb></Breadcrumb>
+        <!-- <Breadcrumb></Breadcrumb> -->
+      </el-col>
+      <el-col :span="2" class="notice">
+        <el-dropdown>
+          <el-icon :size="25"><Bell /></el-icon>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <el-dropdown-item @click="logOut">退出登录</el-dropdown-item>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
       </el-col>
       <el-col :span="4">
         <el-dropdown>
@@ -36,6 +47,8 @@ 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>
@@ -47,6 +60,12 @@ const logOut = () => {
 <style scoped lang="scss">
 .header {
   width: 100%;
+  .notice {
+    cursor: pointer;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
   .d-flex {
     display: flex;
     align-items: center;

+ 20 - 2
src/stores/index.ts

@@ -1,6 +1,8 @@
 import { defineStore, createPinia } from 'pinia'
 import { GlobalState } from './interface/index'
 import { User_Dept_List, User_Post_List } from '@/api/user/index'
+import { Menu_User_List } from '@/api/role/index'
+import { getAllBreadcrumbList, hanlderMenuList } from '@/utils/common'
 export const GlobalStore = defineStore({
   id: 'GlobalState',
   state: (): GlobalState => ({
@@ -10,14 +12,18 @@ export const GlobalStore = defineStore({
     userInfo: JSON.parse(localStorage.getItem('User_info') as any) || {},
     UserDeptList: [],
     UserPostList: [],
-    User_tokey: localStorage.getItem('User_tokey') || ''
+    User_tokey: localStorage.getItem('User_tokey') || '',
+    MenuList: [],
+    path: '',
+    allBreadcrumbList: null
   }),
   getters: {
     GET_User_tokey: state => state.User_tokey,
     GET_Dept_List: state => state.UserDeptList,
     GET_isCollapse: state => state.isCollapse,
     GET_Breadcrumb: state => state.breadcrumb,
-    GET_User_Info: state => state.userInfo
+    GET_User_Info: state => state.userInfo,
+    GET_Menu_List: state => state.MenuList
   },
   actions: {
     SET_User_Tokey(payload: string) {
@@ -37,6 +43,9 @@ export const GlobalStore = defineStore({
     SET_User_Info(payload: any) {
       this.userInfo = payload
     },
+    SET_Path(payload: string) {
+      this.path = payload
+    },
     async SET_User_Dept_List() {
       const res: any = await User_Dept_List()
       this.UserDeptList = res.Data
@@ -44,6 +53,15 @@ export const GlobalStore = defineStore({
     async SET_User_Post_List() {
       const res = await User_Post_List()
       console.log(res)
+    },
+    async SET_Menu_User_List() {
+      const res: any = await Menu_User_List({ User_tokey: this.GET_User_tokey })
+      if (res.Code === 200) {
+        this.MenuList = res.Data.Data
+        hanlderMenuList(res.Data.Data)
+        this.allBreadcrumbList = getAllBreadcrumbList(res.Data.Data)
+        console.log(this.allBreadcrumbList)
+      }
     }
   }
 })

+ 22 - 0
src/stores/interface/index.ts

@@ -8,8 +8,30 @@ export interface GlobalState {
   language?: string
   UserPostList: string[]
   UserDeptList: UserDeptType[]
+  MenuList: Menu.MenuOptions[]
+  path: string
+  allBreadcrumbList: any
 }
 
+// export interface MenuType {
+//   Children?: MenuType[]
+//   Id: number
+//   T_State: number
+//   T_icon: string
+//   T_mid: number
+//   T_name: string
+//   T_permission: string
+//   T_sort: number
+//   T_type: string
+//   meta?: MetaType
+// }
+
+// interface MetaType {
+//   icon: string
+//   title: string
+//   isKeepAlive: boolean
+// }
+
 export interface UserDeptType {
   Id: number
   T_State: number

+ 34 - 0
src/utils/common.ts

@@ -14,4 +14,38 @@ export const fnMd5 = (val: string) => md5(val)
  */
 export const isEmptyObject = (obj: object) => Object.keys(obj).length > 0
 
+/**
+ * 时间的处理
+ * @param date new Date
+ * @returns string
+ */
 export const dayJs = (date: Date | string) => dayjs(date)
+
+/**
+ *
+ * @param menuList 菜单列表
+ * @param result 输出得结果
+ * @param parent 子菜单
+ * @returns object
+ */
+export const getAllBreadcrumbList = (menuList: any, result: { [key: string]: any } = {}, parent = []) => {
+  for (const item of menuList) {
+    result[item.T_permission.trim()] = [...parent, item]
+    if (item.Children) getAllBreadcrumbList(item.Children, result, result[item.T_permission])
+  }
+  return result
+}
+
+// {
+//     path: '/home',
+//     name: 'Home',
+//     meta: {
+//       icon: 'HomeFilled',
+//       title: '首页',
+//       isKeepAlive: true
+//     }
+//   },
+
+export const hanlderMenuList = (menuList: any) => {
+  console.log(menuList)
+}

+ 5 - 3
src/views/Index.vue

@@ -3,7 +3,7 @@ import { ref, onMounted } from 'vue'
 import SubMenu from '@/layouts/Menu/SubMenu.vue'
 import Main from '@/layouts/Main/index.vue'
 import Header from '@/layouts/Header/index.vue'
-import { Menu_User_List } from '@/api/role/index'
+// import { Menu_User_List } from '@/api/role/index'
 import { User_Info } from '@/api/user/index'
 import { GlobalStore } from '@/stores/index'
 import { useRoute } from 'vue-router'
@@ -12,6 +12,7 @@ import { isEmptyObject } from '@/utils/common'
 const globalStore = GlobalStore()
 // const getMenu = async () => {
 //   const res = await Menu_User_List({ User_tokey: globalStore.GET_User_tokey })
+//   console.log(res)
 // }
 // getMenu()
 const getUserInfo = async () => {
@@ -178,6 +179,7 @@ const routerList = [
 
 onMounted(() => {
   globalStore.SET_User_Dept_List()
+  globalStore.SET_Menu_User_List()
   if (!isEmptyObject(globalStore.GET_User_Info)) {
     getUserInfo()
   }
@@ -187,12 +189,12 @@ const route = useRoute()
 const defaultActive = ref<string>(route.path)
 
 const handleOpen = (key: string, keyPath: string[]) => {
-  console.log(key, keyPath)
+  // console.log(key, keyPath)
   // keyPath.push(defaultActive.value)
   globalStore.SET_Breadcrumb(keyPath)
 }
 const handleClose = (key: string, keyPath: string[]) => {
-  console.log(key, keyPath)
+  // console.log(key, keyPath)
 }
 </script>
 

+ 6 - 2
src/views/login.scss

@@ -1,12 +1,16 @@
 .login {
-  background-color: #091215;
+  // background-image: linear-gradient(0deg, #402565, #30be96);
+  background: linear-gradient(#141e30, #243b55);
+  // background-color: #091215;
   height: 100%;
   display: flex;
   justify-content: center;
   align-items: center;
   // background: #eee;
   .content {
-    background: #eee;
+    // background: #eee;
+    background-image: linear-gradient(180deg, #737dfe, #ffcac9);
+    // background-image: linear-gradient(0deg, #402565, #30be96);
     width: 26.25rem;
     padding: 1.5rem;
     padding-top: 35px;

+ 1 - 1
src/views/workAttendance/Leave.vue

@@ -69,7 +69,7 @@ const LeaveUser = async (T_State: number) => {
 let cardHeight = ref(0)
 const resize = () => {
   const height = document.documentElement.clientHeight
-  cardHeight.value = height - 4 * 12 - 140 - 60
+  cardHeight.value = height - 3 * 12 - 140 - 60
 }
 onMounted(() => {
   resize()

+ 4 - 4
src/views/workAttendance/MyLeave.vue

@@ -204,8 +204,8 @@ onMounted(() => {
             v-model="form.T_start_time"
             type="datetime"
             placeholder="选择开始时间"
-            format="YYYY-MM-DD hh:mm:ss"
-            value-format="YYYY-MM-DD hh:mm:ss"
+            format="YYYY-MM-DD HH:mm:ss"
+            value-format="YYYY-MM-DD HH:mm:ss"
           />
         </el-form-item>
         <el-form-item label="结束时间:" :label-width="formLabelWidth" prop="T_end_time">
@@ -215,8 +215,8 @@ onMounted(() => {
             v-model="form.T_end_time"
             type="datetime"
             placeholder="选择结束时间"
-            format="YYYY-MM-DD hh:mm:ss"
-            value-format="YYYY-MM-DD hh:mm:ss"
+            format="YYYY-MM-DD HH:mm:ss"
+            value-format="YYYY-MM-DD HH:mm:ss"
           />
         </el-form-item>
         <el-form-item label="请假时长:" :label-width="formLabelWidth" prop="T_duration">

+ 46 - 36
src/views/workAttendance/MyOvertime.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { reactive, ref, nextTick } from 'vue'
+import { reactive, ref, nextTick, computed } from 'vue'
 import { ColumnProps } from '@/components/TableBase/interface/index'
 import {
   Overtime_User_list,
@@ -22,9 +22,10 @@ import type {
   UploadRawFile,
   UploadFiles
 } from 'element-plus'
-import { ElMessageBox, ElMessage } from 'element-plus'
+import { ElMessageBox, ElMessage, genFileId } from 'element-plus'
 import { floatReg } from '@/views/salary/salary/relus'
 import { GlobalStore } from '@/stores/index'
+import { dayJs } from '@/utils/common'
 const globalStore = GlobalStore()
 
 const TableRef = ref()
@@ -63,7 +64,8 @@ const AddOvertime = (formEl: FormInstance | undefined) => {
   formEl.validate(async valid => {
     if (valid) {
       let res: any = {}
-      form.value.T_duration = form.value.T_duration * 60
+      // form.value.T_duration = form.value.T_duration
+      form.value.T_duration = duration.value * 60
       if (isNew) {
         res = await Overtime_Add({ ...form.value, T_approver: uuid })
       } else {
@@ -117,8 +119,16 @@ const rules = reactive<FormRules>({
   T_approver: [{ required: true, message: '请选择审批人', trigger: 'blur' }]
 })
 let uuid = ''
+const duration = computed(() => {
+  const end_time = dayJs(form.value.T_end_time)
+  if (!form.value.T_end_time) {
+    return 0
+  }
+  let count: number = end_time.diff(form.value.T_start_time, 'hour', true)
+  let num: number = Number(count.toFixed(2))
+  return Math.round(num * 10) / 10
+})
 const form = ref({
-  T_type: '',
   T_start_time: '',
   T_end_time: '',
   T_duration: 0,
@@ -158,13 +168,13 @@ const onResize = () => {
 }
 
 // upload file
+const upload = ref()
 let uploadData = { token: '' }
 const dialogImageUrl = ref('')
 const dialogVisible = ref(false)
-const disabled = ref(false)
-// const fileList = ref<UploadUserFile[]>([])
+const fileList = ref([])
 const handlePictureCardPreview = (file: UploadFile) => {
-  console.log(file)
+  console.log(file, fileList)
   dialogImageUrl.value = file.url as string
   dialogVisible.value = true
 }
@@ -182,11 +192,19 @@ const beforeUpload = async (file: any) => {
 }
 const handleExceed: UploadProps['onExceed'] = (files: any) => {
   console.log(files)
-  // upload.value!.clearFiles()
-  // const file = files[0] as UploadRawFile
-  // file.uid = genFileId()
-  // upload.value!.handleStart(file)
+  upload.value.clearFiles()
+  const file = files[0] as UploadRawFile
+  file.uid = genFileId()
+  console.log(file)
+  upload.value.handleStart(file)
+
+  submitUpload()
+}
+
+const submitUpload = () => {
+  upload.value.submit()
 }
+
 const onSuccess = (response: any, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
   console.log(response, uploadFile, uploadFiles)
 }
@@ -197,7 +215,7 @@ const onError = (error: Error, uploadFile: UploadFile, uploadFiles: UploadFiles)
 //   console.log(file)
 // }
 const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
-  console.log(uploadFile, uploadFiles)
+  console.log(uploadFile, uploadFiles, fileList)
 }
 </script>
 
@@ -251,7 +269,7 @@ const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
     </el-col>
     <Drawer ref="drawerRef" :handleClose="callbackDrawer">
       <template #header="{ params }">
-        <h4 :id="params.titleId" :class="params.titleClass">{{ isNew ? '添加' : '编辑' }} - 角色</h4>
+        <h4 :id="params.titleId" :class="params.titleClass">{{ isNew ? '添加' : '编辑' }} - 申请</h4>
       </template>
       <el-form ref="ruleFormRef" :model="form" :rules="rules">
         <el-form-item label="开始时间:" :label-width="formLabelWidth" prop="T_start_time">
@@ -261,8 +279,8 @@ const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
             v-model="form.T_start_time"
             type="datetime"
             placeholder="选择开始时间"
-            format="YYYY-MM-DD hh:mm:ss"
-            value-format="YYYY-MM-DD hh:mm:ss"
+            format="YYYY-MM-DD HH:mm:ss"
+            value-format="YYYY-MM-DD HH:mm:ss"
           />
         </el-form-item>
         <el-form-item label="结束时间:" :label-width="formLabelWidth" prop="T_end_time">
@@ -272,15 +290,17 @@ const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
             v-model="form.T_end_time"
             type="datetime"
             placeholder="选择结束时间"
-            format="YYYY-MM-DD hh:mm:ss"
-            value-format="YYYY-MM-DD hh:mm:ss"
+            format="YYYY-MM-DD HH:mm:ss"
+            value-format="YYYY-MM-DD HH:mm:ss"
           />
         </el-form-item>
         <el-form-item label="加班时长:" :label-width="formLabelWidth" prop="T_duration">
-          <el-input v-model.number="form.T_duration" autocomplete="off" placeholder="请假时长" />
+          <el-input v-model.number="duration" autocomplete="off" :disabled="true" placeholder="请假时长" />
         </el-form-item>
-        <el-form-item label="取证:" :label-width="formLabelWidth" prop="T_duration">
+        <el-form-item label="取证:" :label-width="formLabelWidth">
           <el-upload
+            ref="upload"
+            v-model:file-list="fileList"
             action="https://up-z2.qiniup.com"
             list-type="picture-card"
             :limit="1"
@@ -294,23 +314,6 @@ const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
             :data="uploadData"
           >
             <el-icon><Plus /></el-icon>
-
-            <!-- <template #file="{ file }">
-              <div>
-                <img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
-                <span class="el-upload-list__item-actions">
-                  <span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
-                    <el-icon><zoom-in /></el-icon>
-                  </span>
-                  <span v-if="!disabled" class="el-upload-list__item-delete" @click="handleDownload(file)">
-                    <el-icon><Download /></el-icon>
-                  </span>
-                  <span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file)">
-                    <el-icon><Delete /></el-icon>
-                  </span>
-                </span>
-              </div>
-            </template> -->
           </el-upload>
           <el-dialog v-model="dialogVisible">
             <img w-full :src="dialogImageUrl" class="full-img" alt="Preview Image" />
@@ -374,4 +377,11 @@ const handleRemove: UploadProps['onRemove'] = (uploadFile, uploadFiles) => {
   width: 100%;
   height: 100%;
 }
+.d-flex {
+  display: flex;
+  align-items: center;
+  .inline-flex {
+    white-space: nowrap;
+  }
+}
 </style>

+ 221 - 4
src/views/workAttendance/Overtime.vue

@@ -1,7 +1,224 @@
+<script setup lang="ts">
+import { ref, nextTick, onMounted, onUnmounted } from 'vue'
+import { UserFilled } from '@element-plus/icons-vue'
+import { ElMessage } from 'element-plus'
+import TableBase from '@/components/TableBase/index.vue'
+import { ColumnProps } from '@/components/TableBase/interface/index'
+import { Overtime_List, Overtime_Approval } from '@/api/workAttendance/index'
+import { GlobalStore } from '@/stores/index'
+
+const globalStore = GlobalStore()
+const TableRef = ref()
+const columns: ColumnProps[] = [{ prop: 'T_user_name', label: '姓名', name: 'T_user_name' }]
+interface UserInfoIn {
+  T_user_name: string
+  T_dept: string
+  T_post: string
+  T_type_name: string
+  T_start_time: string
+  T_end_time: string
+  T_text: string
+  Id: string
+  T_duration: string
+}
+const userInfo = ref<UserInfoIn>({
+  T_user_name: '',
+  T_dept: '',
+  T_post: '',
+  T_type_name: '',
+  T_start_time: '',
+  T_end_time: '',
+  T_text: '',
+  Id: '',
+  T_duration: ''
+})
+const initParam = {
+  User_tokey: globalStore.GET_User_tokey
+}
+const getSalaryParams = (row: any) => {
+  userInfo.value = { ...row }
+}
+
+const LeaveUser = async (T_State: number) => {
+  if (!userInfo.value.Id) return
+  const params = {
+    User_tokey: globalStore.GET_User_tokey,
+    T_id: userInfo.value.Id,
+    T_State
+  }
+  const res: any = await Overtime_Approval(params)
+  if (res.Code === 200) {
+    if (T_State) {
+      ElMessage({
+        type: 'success',
+        message: '审核通过!'
+      })
+    } else {
+      ElMessage({
+        type: 'warning',
+        message: '审核不通过!'
+      })
+    }
+    nextTick(() => {
+      TableRef.value.getTableList()
+      userInfo.value = {} as UserInfoIn
+    })
+  }
+}
+
+let cardHeight = ref(0)
+const resize = () => {
+  const height = document.documentElement.clientHeight
+  cardHeight.value = height - 3 * 12 - 140 - 60 - 6
+}
+onMounted(() => {
+  resize()
+  window.onresize = resize
+})
+onUnmounted(() => {
+  window.onresize = null
+})
+</script>
+
 <template>
-  <div>加班审批</div>
+  <div class="Leave">
+    <div style="width: 290px">
+      <TableBase
+        ref="TableRef"
+        :columns="columns"
+        :requestApi="Overtime_List"
+        :initParam="initParam"
+        layout="prev, pager, next"
+        :rowClick="getSalaryParams"
+      >
+        <template #table-header>
+          <h3 class="title">待处理</h3>
+        </template>
+        <template #T_user_name="{ row }">
+          <el-button type="primary" text @click="getSalaryParams(row)">{{ row.T_user_name }}</el-button>
+        </template>
+      </TableBase>
+    </div>
+    <el-row class="h-100 f-1 margin-left-3">
+      <el-col :span="24" class="h-100" style="overflow: hidden">
+        <el-card class="m-b-3 b-show-0">
+          <h3 class="title m-b-5">员工基本信息</h3>
+          <div class="info-content">
+            <el-avatar shape="square" size="large" :icon="UserFilled" />
+            <div class="info-name">
+              <h4 class="m-b-3">名字:{{ userInfo.T_user_name }}</h4>
+            </div>
+          </div>
+        </el-card>
+        <el-card class="m-b-3 b-show-0" :style="{ height: cardHeight + 'px' }">
+          <!-- <el-descriptions title="请假申请" :column="1" size="large" border>
+            <el-descriptions-item label="请假类型:"
+              ><el-text class="mx-1" type="primary">{{
+                userInfo.T_type_name ? userInfo.T_type_name : '-'
+              }}</el-text></el-descriptions-item
+            >
+            <el-descriptions-item label="开始时间:"
+              ><el-text class="mx-1" type="primary">{{
+                userInfo.T_start_time ? userInfo.T_type_name : '-'
+              }}</el-text></el-descriptions-item
+            >
+            <el-descriptions-item label="结束时间:" :span="2"
+              ><el-text class="mx-1" type="primary">{{
+                userInfo.T_end_time ? userInfo.T_end_time : '-'
+              }}</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-descriptions-item>
+            <el-descriptions-item label="内容:">
+              <el-text class="mx-1" type="primary">{{ userInfo.T_text ? userInfo.T_text : '-' }}</el-text>
+            </el-descriptions-item>
+          </el-descriptions> -->
+          <el-row>
+            <el-col :span="12"
+              ><div>
+                开始时间<span>{{ userInfo.T_start_time }}</span>
+              </div></el-col
+            >
+            <el-col :span="12"
+              ><div>
+                结束时间:<span>{{ userInfo.T_end_time }}</span>
+              </div></el-col
+            >
+          </el-row>
+          <el-row>
+            <el-col :span="12"
+              ><div>
+                加班时长:<span>{{ userInfo.T_duration }}小时</span>
+              </div></el-col
+            >
+          </el-row>
+          <el-row>
+            <el-col style="display: flex">
+              取证:
+              <img src="@/assets/images/avatar.jpg" class="img" />
+            </el-col>
+          </el-row>
+          <el-row>
+            <el-col>
+              <span class="ml-3 w-35 text-gray-600 inline-flex items-center">内容:</span>
+              <el-input
+                v-model="userInfo.T_text"
+                :autosize="{ minRows: 4, maxRows: 6 }"
+                type="textarea"
+                placeholder="Please input"
+              />
+            </el-col>
+          </el-row>
+          <div class="btn">
+            <el-button type="primary" @click="LeaveUser(1)">通过</el-button>
+            <el-button type="danger" @click="LeaveUser(0)">不通过</el-button>
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+  </div>
 </template>
 
-<script setup lang="ts"></script>
-
-<style scoped></style>
+<style scoped lang="scss">
+.Leave {
+  display: flex;
+  overflow: hidden;
+  .img {
+    width: 200px;
+  }
+  .b-show-0 {
+    box-shadow: none;
+    font-size: 18px;
+    font-weight: 600;
+    .el-row {
+      margin-bottom: 10px;
+      .el-col {
+        padding: 15px;
+      }
+    }
+  }
+  .title {
+    width: 100%;
+    text-align: center;
+  }
+  .btn {
+    margin-top: 2rem;
+    display: flex;
+    justify-content: space-around;
+  }
+  .info-content {
+    display: flex;
+    color: #303133;
+    .info-name {
+      display: flex;
+      // flex-direction: column;
+      align-items: center;
+      padding-left: 0.75rem;
+      span:first-child {
+        margin-right: 2rem;
+      }
+    }
+  }
+}
+</style>

+ 231 - 3
src/views/workAttendance/RecordsFinance.vue

@@ -1,7 +1,235 @@
+<script setup lang="ts">
+import { ref, reactive } 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()
+
+const TableRef = ref()
+const salaryFromRef = 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: '审核' }
+]
+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({
+  year: year + '',
+  month: (month < 10 ? '0' : '') + month,
+  T_uuid: ''
+})
+
+const initParam = {
+  User_tokey: globalStore.GET_User_tokey,
+  T_name: '',
+  T_dept: ''
+}
+const getSalaryParams = (row: any) => {
+  userInfo.name = row.T_name
+  userInfo.T_dept = row.T_dept_name
+  userInfo.T_post = row.T_post_name
+
+  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)
+}
+const salarDateMonthChange = (val: string) => {
+  if (!val) return
+  if (!salaryFromData.T_uuid) {
+    ElMessage({
+      message: '请选择员工!!!',
+      type: 'warning'
+    })
+  }
+  getSalary()
+}
+const salarDateYearChange = (val: string) => {
+  if (!val) return
+  if (!salaryFromData.T_uuid) {
+    ElMessage({
+      message: '请选择员工!!!',
+      type: 'warning'
+    })
+  }
+  getSalary()
+}
+const userInfo = reactive({
+  squareUrl: 'https://cube.elemecdn.com/9/c2/f0ee8a3c7c9638a54940382568c9dpng.png',
+  name: '',
+  T_dept: '',
+  T_post: ''
+})
+const onResize = () => {
+  const height = document.documentElement.clientHeight
+  return height - 140 - 74 - 60 - 4 * 12
+}
+</script>
+
 <template>
-  <div>统筹管理-财务</div>
+  <div class="salary">
+    <div style="width: 290px">
+      <TableBase
+        ref="TableRef"
+        :columns="columns"
+        :requestApi="User_List"
+        :initParam="initParam"
+        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>
+    </div>
+    <el-row class="h-100 f-1 margin-left-3">
+      <el-col :span="24" class="h-100" style="overflow: hidden">
+        <el-card class="m-b-3">
+          <h3 class="title m-b-5">员工基本信息</h3>
+          <div class="info-content">
+            <el-avatar shape="square" size="large" :src="userInfo.squareUrl" />
+            <div class="info-name">
+              <h4 class="m-b-3">名字:{{ userInfo.name }}</h4>
+              <h4>
+                <span>部门:{{ userInfo.T_dept }}</span>
+                <span>岗位:{{ userInfo.T_post }}</span>
+              </h4>
+            </div>
+          </div>
+        </el-card>
+        <el-card class="m-b-3">
+          <el-row :gutter="20">
+            <el-col :span="10" class="d-flex justify-start">
+              <span class="demonstration">年:</span>
+              <el-date-picker
+                v-model="salaryFromData.year"
+                value-format="YYYY"
+                type="year"
+                placeholder="请选择年"
+                @change="salarDateYearChange"
+              />
+              <span class="demonstration">月:</span>
+              <el-date-picker
+                v-model="salaryFromData.month"
+                popper-class="picker-date"
+                value-format="MM"
+                format="MM"
+                type="month"
+                placeholder="请选择月"
+                @change="salarDateMonthChange"
+              />
+            </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>
+      </el-col>
+    </el-row>
+  </div>
 </template>
 
-<script setup lang="ts"></script>
+<style scoped lang="scss">
+.salary {
+  display: flex;
+  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;
+  align-items: center;
+  flex-wrap: nowrap;
+}
+.justify-start {
+  justify-content: start;
+}
+.input-suffix {
+  width: 100%;
+  flex-direction: column;
+  .w-50 {
+    width: 12.5rem;
+  }
 
-<style scoped></style>
+  .inline-flex {
+    white-space: nowrap;
+    display: inline-flex;
+  }
+}
+.title {
+  text-align: center;
+}
+.info-content {
+  display: flex;
+  color: #303133;
+  .info-name {
+    display: flex;
+    flex-direction: column;
+    padding-left: 0.75rem;
+    span:first-child {
+      margin-right: 2rem;
+    }
+  }
+}
+</style>

+ 144 - 27
src/views/workAttendance/records/Records.vue

@@ -1,11 +1,21 @@
 <script setup lang="ts">
-import { reactive } from 'vue'
+import { ref, reactive, onMounted, onUnmounted } from 'vue'
 import { ColumnProps } from '@/components/TableBase/interface/index'
+import TableBase from '@/components/TableBase/index.vue'
 import { User_List } from '@/api/user/index'
 import { GlobalStore } from '@/stores/index'
 const globalStore = GlobalStore()
 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' }
+]
+const remainingTimeColums: ColumnProps[] = []
+const leaveColums: ColumnProps[] = []
 const initParam = {
   User_tokey: globalStore.GET_User_tokey,
   T_name: '',
@@ -17,44 +27,151 @@ const userInfo = reactive({
   T_dept: '',
   T_post: ''
 })
+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))
+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
+}
 </script>
 
 <template>
-  <el-row :gutter="20" class="h-100">
-    <el-col :span="8" class="padding-right-0 h-100">
+  <div class="records">
+    <div style="width: 290px">
       <TableBase
         ref="TableRef"
         :columns="columns"
         :requestApi="User_List"
         :initParam="initParam"
-        layout="prev, pager, next"
+        layout="total, prev, pager, next"
+        :onResize="onResize"
       >
-        <template #table-header> 待处理 </template>
+        <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-col
-    >
-    <el-col :span="16" class="h-100" style="overflow: hidden">
-      <el-card class="m-b-3">
-        <h3 class="title m-b-5">员工基本信息</h3>
-        <div class="info-content">
-          <el-avatar shape="square" size="large" :src="userInfo.squareUrl" />
-          <div class="info-name">
-            <h4 class="m-b-3">名字:{{ userInfo.name }}</h4>
-            <h4>
-              <span>部门:{{ userInfo.T_dept }}</span>
-              <span>岗位:{{ userInfo.T_post }}</span>
-            </h4>
+      </TableBase>
+    </div>
+    <el-row class="h-100 f-1 margin-left-3">
+      <el-col :span="24" class="h-100" style="overflow: hidden">
+        <el-card class="m-b-3">
+          <h3 class="title m-b-5">员工基本信息</h3>
+          <div class="info-content">
+            <el-avatar shape="square" size="large" :src="userInfo.squareUrl" />
+            <div class="info-name">
+              <h4 class="m-b-3">名字:{{ userInfo.name }}</h4>
+              <h4>
+                <span>部门:{{ userInfo.T_dept }}</span>
+                <span>岗位:{{ userInfo.T_post }}</span>
+              </h4>
+            </div>
+          </div>
+        </el-card>
+        <div class="content" :style="{ height: clientHeight + 'px' }">
+          <div>
+            <div class="content-table-item">
+              <el-text class="font-large" size="large" type="success">加班记录:</el-text>
+
+              <TableBase
+                ref="TableRef"
+                border
+                :columns="overtimeColums"
+                :requestApi="User_List"
+                :initParam="initParam"
+                :onResize="onResizeItem"
+                :displayHeader="true"
+                layout="prev, pager, next"
+              >
+                <template #T_name="{ row }">
+                  <el-button type="primary" text>{{ row.T_name }}</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"
+                :onResize="onResizeItem"
+                :displayHeader="true"
+                layout="prev, pager, next"
+              >
+                <template #T_name="{ row }">
+                  <el-button type="primary" text>{{ row.T_name }}</el-button>
+                </template>
+              </TableBase>
+            </div>
+            <div class="content-table-item">
+              <el-text class="font-large" size="large" type="danger">请假记录:</el-text>
+              <TableBase
+                ref="TableRef"
+                border
+                :columns="overtimeColums"
+                :requestApi="User_List"
+                :initParam="initParam"
+                :onResize="onResizeItem"
+                :displayHeader="true"
+                layout="prev, pager, next"
+              >
+                <template #T_name="{ row }">
+                  <el-button type="primary" text>{{ row.T_name }}</el-button>
+                </template>
+              </TableBase>
+            </div>
           </div>
         </div>
-      </el-card>
-      <el-card class="m-b-3"> </el-card>
-      <el-card class="m-b-3"> </el-card>
-      <el-card class="m-b-3"> </el-card>
-      <!-- <SalaryFrom v-bind="salaryFromData"></SalaryFrom> -->
-    </el-col>
-  </el-row>
+      </el-col>
+    </el-row>
+  </div>
 </template>
 
-<style scoped></style>
+<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;
+      }
+    }
+  }
+}
+</style>