Browse Source

完成我的请假,修改薪资管理得问题,需改用户信息

@sun-chaoqun 2 years ago
parent
commit
da372dd6ea

+ 0 - 0
.env.development


+ 5 - 1
.env.production

@@ -1 +1,5 @@
-VITE_BZD_ERP_APP_TITLE = 'ERP宝智达'
+VITE_BZD_ERP_APP_TITLE = 'ERP宝智达'
+
+VITE_BZD_ERP_APP_API = 'https://erp.baozhida.cn'
+
+VITE_DROP_CONSOLE = true

+ 6 - 0
README.md

@@ -10,6 +10,12 @@
 
 考勤管理 http://erp.baozhida.cn/ams
 
+用户管理 https://erp.baozhida.cn/api/user
+
+薪资管理 https://erp.baozhida.cn/api/salary
+
+考勤管理 https://erp.baozhida.cn/api/ams
+
 ### 原型
 
 https://58ftdz.axshare.com

+ 0 - 1
components.d.ts

@@ -39,7 +39,6 @@ declare module '@vue/runtime-core' {
     ElMain: typeof import('element-plus/es')['ElMain']
     ElMenu: typeof import('element-plus/es')['ElMenu']
     ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
-    ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup']
     ElOption: typeof import('element-plus/es')['ElOption']
     ElPagination: typeof import('element-plus/es')['ElPagination']
     ElRadio: typeof import('element-plus/es')['ElRadio']

+ 8 - 3
src/api/index.ts

@@ -1,6 +1,6 @@
 import axios from 'axios'
 import type { AxiosInstance, AxiosError, AxiosRequestConfig, InternalAxiosRequestConfig, AxiosResponse } from 'axios'
-import { ElMessage, ElLoading } from 'element-plus'
+import { ElMessage, ElLoading, ElNotification } from 'element-plus'
 import { ResultEnum, ResultData, ContentType } from './interface/index'
 import { GlobalStore } from '@/stores/index'
 import { useRouter } from 'vue-router'
@@ -63,11 +63,16 @@ class RequestHttp {
         // * 在请求结束后,并关闭请求 loading
         loadingInstance.close && loadingInstance.close()
         // * 登陆失效(code == 201)
-        if (data.Code == ResultEnum.OVERDUE) {
-          ElMessage.error(data.Msg)
+        if (data.Code === ResultEnum.OVERDUE) {
+          ElNotification.error({
+            title: '登陆失效',
+            message: data.Msg,
+            position: 'bottom-right'
+          })
           globalStore.SET_User_Tokey('')
           localStorage.clear()
           router.replace('/login')
+          console.log('201')
           return Promise.reject(data)
         }
         // * 全局错误信息拦截(防止下载文件得时候返回数据流,没有code,直接报错)

+ 2 - 0
src/api/user/index.ts

@@ -13,3 +13,5 @@ export const User_Del = (params: any) => $http.post('/api/user/User/Del', params
 export const User_Dept_List = (params?: any) => $http.post('/api/user/Dept/List', params)
 //岗位列表
 export const User_Post_List = (params?: any) => $http.post('/api/user/Post/List', params)
+// 用户信息
+export const User_Info = (params?: any) => $http.post('/api/user/User/Info', params)

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

@@ -0,0 +1,37 @@
+import $http from '../index'
+/**
+ * 请假
+ */
+// 员工请假列表
+export const Leave_User_list = (params: any) => $http.post('api/ams/Leave/User_list', params)
+// 请假类型
+export const LeaveType_List = (params: any) => $http.post('api/ams/LeaveType/List', params)
+// 请假列表
+export const Leave_List = (params: any) => $http.post('api/ams/Leave/List', params)
+// 请假申请
+export const Leave_Add = (params: any) => $http.post('api/ams/Leave/Add', params)
+// 扣除-财务权限
+export const Leave_Deduct = (params: any) => $http.post('api/ams/Leave/Deduct', params)
+// 编辑
+export const Leave_Edit = (params: any) => $http.post('api/ams/Leave/Edit', params)
+// 审批
+export const Leave_Approval = (params: any) => $http.post('api/ams/Leave/Approval', params)
+// 删除
+export const Leave_Del = (params: any) => $http.post('api/ams/Leave/Del', params)
+/**
+ * 加班
+ */
+// 加班列表
+export const Overtime_List = (params: any) => $http.post('api/ams/Overtime/List', params)
+// 员工加班列表
+export const Overtime_User_list = (params: any) => $http.post('api/ams/Overtime/User_list', params)
+// 加班申请
+export const Overtime_Add = (params: any) => $http.post('api/ams/Overtime/Add', params)
+// 编辑
+export const Overtime_Edit = (params: any) => $http.post('api/ams/Overtime/Edit', params)
+// 审批
+export const Overtime_Approval = (params: any) => $http.post('api/ams/Overtime/Approval', params)
+// 删除
+export const Overtime_Del = (params: any) => $http.post('api/ams/Overtime/Del', params)
+// 统计
+export const Overtime_Stat = (params: any) => $http.post('api/ams/Overtime/Stat', params)

BIN
src/assets/fdgClRuIWv9y.woff


BIN
src/assets/fdgClRuIWv9y.woff2


BIN
src/assets/images/avatar.jpg


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

@@ -1,6 +1,6 @@
 .table-header {
   background-color: #fff;
-  height: 5rem;
+  // height: 5rem;
   margin-bottom: 0.75rem;
   border: 1px solid var(--el-border-color-light);
   border-radius: 4px;
@@ -16,7 +16,7 @@
 }
 
 .card {
-  height: calc(100% - 5rem - 0.75rem);
+  // height: calc(100% - 5rem - 0.75rem);
   box-sizing: border-box;
   padding: 20px;
   overflow-x: hidden;

+ 21 - 4
src/components/TableBase/index.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import { ref, onMounted } from 'vue'
+import { ref, onMounted, onUnmounted } from 'vue'
 import Pagination from './components/Pagination.vue'
 import { useTable } from '@/hooks/useTable'
 import { ElTable, TableProps } from 'element-plus'
@@ -16,6 +16,8 @@ interface ProTableProps extends Partial<Omit<TableProps<any>, 'data'>> {
   border?: boolean // 是否带有纵向边框 ==> 非必传(默认为true)
   toolButton?: boolean // 是否显示表格功能按钮 ==> 非必传(默认为true)
   selectId?: string // 当表格数据多选时,所指定的 id ==> 非必传(默认为 id)
+  displayHeader?: boolean
+  onResize?: () => number
 }
 
 // 接受父组件参数,配置默认值
@@ -45,10 +47,25 @@ const {
 // 接收 columns 并设置为响应式
 const tableColumns = ref<ColumnProps[]>(props.columns)
 
+let cardHeight = 0
+const resize = () => {
+  if (props.onResize) {
+    cardHeight = props.onResize()
+  } else {
+    const height = document.documentElement.clientHeight
+    cardHeight = height - 80 - 12 - 60 - 12
+  }
+}
 onMounted(() => {
   getTableList()
 })
-
+onMounted(() => {
+  resize()
+  window.onresize = resize
+})
+onUnmounted(() => {
+  window.onresize = null
+})
 defineExpose({
   getTableList,
   searchTable
@@ -56,11 +73,11 @@ defineExpose({
 </script>
 
 <template>
-  <div class="table-header">
+  <div class="table-header" :style="{ display: displayHeader ? 'none' : '' }">
     <slot name="table-header"></slot>
   </div>
 
-  <div class="card table">
+  <div class="card table" :style="{ height: cardHeight + 'px' }">
     <el-table :data="tableData">
       <!-- 默认插槽 -->
       <slot></slot>

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

@@ -27,8 +27,11 @@ const logOut = () => {
       </el-col>
       <el-col :span="4">
         <el-dropdown>
-          <el-button type="primary">
-            退出登录<el-icon class="el-icon--right"><arrow-down /></el-icon>
+          <el-button link>
+            <el-avatar>
+              <img src="@/assets/images/avatar.jpg" />
+            </el-avatar>
+            <span style="padding-left: 16px">{{ globalStore.GET_User_Info.T_name }}</span>
           </el-button>
           <template #dropdown>
             <el-dropdown-menu>

+ 1 - 1
src/layouts/Main/index.scss

@@ -1,7 +1,7 @@
 .el-main {
   box-sizing: border-box;
   padding: 10px 12px;
-  overflow-x: hidden;
+  overflow: hidden;
   background-color: #f2f2f2;
   &::-webkit-scrollbar {
     background-color: #f0f2f5;

+ 7 - 9
src/layouts/Menu/SubMenu.vue

@@ -5,33 +5,31 @@ defineProps<{ menuList: Menu.MenuOptions[] }>()
 const handleClickMenu = (route: Menu.MenuOptions) => {
   router.push(route.path)
 }
-// const reg = /^[\u4e00-\u9fa5]+$/
-const reg = /^\\{1}/
+
+const reg = /\w/g
 const isUnicode = (val: string | undefined) => {
-  if (!val) return
-  console.log(val)
+  if (!val) return false
   return reg.test(val)
 }
-console.log(isUnicode('\ue663'))
 </script>
 
 <template>
   <template v-for="subItem in menuList" :key="subItem.path">
     <el-sub-menu v-if="subItem.children && subItem.children.length > 0" :index="subItem.path">
       <template #title>
-        <el-icon v-if="subItem.meta.icon">
+        <el-icon v-if="isUnicode(subItem.meta.icon)">
           <component :is="subItem.meta.icon"></component>
         </el-icon>
-        <i v-else class="iconfont">{{ subItem.meta.icon }}</i>
+        <i v-else class="iconfont el-icon">{{ subItem.meta.icon }}</i>
         <span>{{ subItem.meta.title }}</span>
       </template>
       <SubMenu :menuList="subItem.children" />
     </el-sub-menu>
     <el-menu-item v-else :index="subItem.path" @click="handleClickMenu(subItem)">
-      <el-icon v-if="subItem.meta.icon">
+      <el-icon v-if="isUnicode(subItem.meta.icon)">
         <component :is="subItem.meta.icon"></component>
       </el-icon>
-      <i v-else class="iconfont">{{ subItem.meta.unicode }}</i>
+      <i v-else class="iconfont el-icon">{{ subItem.meta.icon }}</i>
       <template #title>
         <span>{{ subItem.meta.title }}</span>
       </template>

+ 6 - 1
src/stores/index.ts

@@ -7,6 +7,7 @@ export const GlobalStore = defineStore({
     loading: false,
     isCollapse: false,
     breadcrumb: [],
+    userInfo: JSON.parse(localStorage.getItem('User_info') as any) || {},
     UserDeptList: [],
     UserPostList: [],
     User_tokey: localStorage.getItem('User_tokey') || ''
@@ -15,7 +16,8 @@ export const GlobalStore = defineStore({
     GET_User_tokey: state => state.User_tokey,
     GET_Dept_List: state => state.UserDeptList,
     GET_isCollapse: state => state.isCollapse,
-    GET_Breadcrumb: state => state.breadcrumb
+    GET_Breadcrumb: state => state.breadcrumb,
+    GET_User_Info: state => state.userInfo
   },
   actions: {
     SET_User_Tokey(payload: string) {
@@ -32,6 +34,9 @@ export const GlobalStore = defineStore({
       })
       this.breadcrumb = map
     },
+    SET_User_Info(payload: any) {
+      this.userInfo = payload
+    },
     async SET_User_Dept_List() {
       const res: any = await User_Dept_List()
       this.UserDeptList = res.Data

+ 22 - 2
src/style.scss

@@ -21,10 +21,15 @@ body,
 }
 
 .margin-left-0 {
-  margin-left: 0;
+  margin-left: 0 !important;
 }
+
+.margin-left-3 {
+  margin-left: 0.75rem;
+}
+
 .margin-right-0 {
-  margin-right: 0;
+  margin-right: 0 !important;
 }
 .margin-top-0 {
   margin-top: 0;
@@ -35,6 +40,11 @@ body,
 .padding-left-0 {
   padding-left: 0 !important;
 }
+
+.padding-left-3 {
+  padding-left: 12px !important;
+}
+
 .padding-right-0 {
   padding-right: 0 !important;
 }
@@ -57,6 +67,9 @@ body,
 .m-b-5 {
   margin-bottom: 1.25rem;
 }
+.f-1 {
+  flex: 1;
+}
 
 ::-webkit-scrollbar {
   width: 3px;
@@ -85,4 +98,11 @@ body,
   }
 }
 
+.my-date-picker {
+  // width: 100% !important;
+  .el-input__wrapper {
+    width: 100%;
+  }
+}
+
 @import '@/assets/iconfont.css';

+ 8 - 1
src/utils/common.ts

@@ -1,3 +1,10 @@
 import md5 from 'js-md5'
 
-export const fnMd5 = () => md5
+/**
+ * md5 加密
+ * @param val string
+ * @returns strng
+ */
+export const fnMd5 = (val: string) => md5(val)
+
+export const isEmptyObject = (obj: object) => Object.keys(obj).length > 0

+ 26 - 15
src/views/Index.vue

@@ -4,16 +4,24 @@ 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 { User_Info } from '@/api/user/index'
 import { GlobalStore } from '@/stores/index'
-
 import { useRoute } from 'vue-router'
-// import md5 from 'js-md5'
-// console.log(md5('123456'))
+import { isEmptyObject } from '@/utils/common'
+
 const globalStore = GlobalStore()
-const getMenu = async () => {
-  const res = await Menu_User_List({ User_tokey: globalStore.GET_User_tokey })
+// const getMenu = async () => {
+//   const res = await Menu_User_List({ User_tokey: globalStore.GET_User_tokey })
+// }
+// getMenu()
+const getUserInfo = async () => {
+  const res: any = await User_Info({ User_tokey: globalStore.GET_User_tokey })
+  if (res.Code === 200) {
+    globalStore.SET_User_Info(res.Data)
+    localStorage.setItem('User_info', JSON.stringify(res.Data))
+  }
 }
-getMenu()
+
 const routerList = [
   {
     path: '/home',
@@ -67,7 +75,7 @@ const routerList = [
     path: 'salary',
     name: '薪资管理',
     meta: {
-      icon: '\\ue663',
+      icon: '\ue663',
       title: '薪资管理',
       isKeepAlive: true
     },
@@ -76,7 +84,7 @@ const routerList = [
         path: '/salary',
         name: '薪资管理',
         meta: {
-          unicode: '\ue7cd',
+          icon: '\ue7cd',
           title: '薪资管理',
           isKeepAlive: true
         }
@@ -85,7 +93,7 @@ const routerList = [
         path: '/salaryCount',
         name: '薪资统计',
         meta: {
-          unicode: '\ue831',
+          icon: '\ue831',
           title: '薪资统计',
           isKeepAlive: true
         }
@@ -94,7 +102,7 @@ const routerList = [
         path: '/salaryMy',
         name: '我的薪资',
         meta: {
-          unicode: '\ue7d1',
+          icon: '\ue7d1',
           title: '我的薪资',
           isKeepAlive: true
         }
@@ -105,7 +113,7 @@ const routerList = [
     path: 'mangage',
     name: '考勤管理',
     meta: {
-      unicode: '\ue619',
+      icon: '\ue619',
       title: '考勤管理',
       isKeepAlive: true
     },
@@ -132,7 +140,7 @@ const routerList = [
         path: '/overtime',
         name: '加班审批',
         meta: {
-          unicode: '\ue690',
+          icon: '\ue690',
           title: '加班审批',
           isKeepAlive: true
         }
@@ -141,7 +149,7 @@ const routerList = [
         path: '/leave',
         name: '请假审批',
         meta: {
-          unicode: '\ue627',
+          icon: '\ue627',
           title: '请假审批',
           isKeepAlive: true
         }
@@ -150,7 +158,7 @@ const routerList = [
         path: '/myOvertime',
         name: '我的加班',
         meta: {
-          unicode: '\ue64c',
+          icon: '\ue64c',
           title: '我的加班',
           isKeepAlive: true
         }
@@ -159,7 +167,7 @@ const routerList = [
         path: '/myLeave',
         name: '我的请假',
         meta: {
-          unicode: '\ue6aa',
+          icon: '\ue6aa',
           title: '我的请假',
           isKeepAlive: true
         }
@@ -170,6 +178,9 @@ const routerList = [
 
 onMounted(() => {
   globalStore.SET_User_Dept_List()
+  if (!isEmptyObject(globalStore.GET_User_Info)) {
+    getUserInfo()
+  }
 })
 
 const route = useRoute()

+ 2 - 2
src/views/Login.vue

@@ -7,6 +7,7 @@ import { User, View, Lock } 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'
 const router = useRouter()
 const globalStore = GlobalStore()
 const ruleFormRef = ref<FormInstance>()
@@ -33,7 +34,7 @@ const submitForm = (formEl: FormInstance | undefined) => {
   formEl.validate(async valid => {
     if (valid) {
       let res: any = {}
-      res = await Login_verification({ bzd_username: ruleForm.username, bzd_password: ruleForm.password })
+      res = await Login_verification({ bzd_username: ruleForm.username, bzd_password: fnMd5(ruleForm.password) })
       if (res.Code === 200) {
         localStorage.setItem('User_tokey', res.Data)
         globalStore.SET_User_Tokey(res.Data)
@@ -45,7 +46,6 @@ const submitForm = (formEl: FormInstance | undefined) => {
         })
       }
     } else {
-      console.log('error submit!')
       return false
     }
   })

+ 2 - 3
src/views/account/roles/Roles.vue

@@ -146,11 +146,10 @@ const AddUserName = (formEl: FormInstance | undefined) => {
           drawerRef.value.closeDrawer()
           TableRef.value.getTableList()
           resetForm(ruleFormRef.value)
-          isNew.value = false
+          isNew.value = true
         })
       }
     } else {
-      console.log('error submit!')
       return false
     }
   })
@@ -197,7 +196,7 @@ const searchHandle = () => {
       <div class="input-suffix">
         <el-row :gutter="20">
           <el-col :span="12">
-            <span class="ml-3 w-35 text-gray-600 inline-flex items-center">角色名:</span>
+            <span class="inline-flex items-center">角色名:</span>
             <el-input v-model="search" type="text" class="w-50 m-2" />
             <el-button type="primary" @click="searchHandle">搜索</el-button>
           </el-col>

+ 8 - 0
src/views/account/users/components/DrawerFrom.vue

@@ -192,6 +192,8 @@ defineExpose({
       </el-form-item>
       <el-form-item label="入职时间:" :label-width="formLabelWidth" prop="T_entry_time">
         <el-date-picker
+          style="width: 100%"
+          class="my-date-picker"
           v-model="form.T_entry_time"
           type="date"
           placeholder="入职时间"
@@ -201,6 +203,8 @@ defineExpose({
       </el-form-item>
       <el-form-item label="转正时间:" :label-width="formLabelWidth" prop="T_positive_time">
         <el-date-picker
+          style="width: 100%"
+          class="my-date-picker"
           v-model="form.T_positive_time"
           type="date"
           placeholder="转正时间"
@@ -223,6 +227,8 @@ defineExpose({
       </el-form-item>
       <el-form-item label="劳动合同开始时间:" label-width="150px" prop="T_contract_start_time">
         <el-date-picker
+          style="width: 100%"
+          class="my-date-picker"
           v-model="form.T_contract_start_time"
           type="date"
           placeholder="劳动合同开始时间"
@@ -232,6 +238,8 @@ defineExpose({
       </el-form-item>
       <el-form-item label="劳动合同结束时间:" label-width="150px" prop="T_contract_end_time">
         <el-date-picker
+          style="width: 100%"
+          class="my-date-picker"
           v-model="form.T_contract_end_time"
           type="date"
           placeholder="劳动合同结束时间"

+ 63 - 59
src/views/salary/salary/Salary.vue

@@ -50,81 +50,85 @@ const userInfo = reactive({
   T_dept: '',
   T_post: ''
 })
+const onResize = () => {
+  const height = document.documentElement.clientHeight
+  const table_header = document.querySelector('.table-header') as HTMLDivElement
+  return height - table_header.clientHeight - 24 - 60 - 12
+}
 </script>
 
 <template>
-  <el-row :gutter="20" class="h-100">
-    <el-col :span="8" class="padding-right-0 h-100">
+  <div class="salary">
+    <div style="width: 290px">
       <TableBase
         ref="TableRef"
         :columns="columns"
         :requestApi="User_List"
         :initParam="initParam"
         layout="prev, pager, next"
+        :onResize="onResize"
       >
         <template #table-header>
-          <div class="input-suffix">
-            <el-row :gutter="24">
-              <el-col :span="10" class="d-flex padding-left-0 padding-right-0">
-                <span class="inline-flex">部门:</span>
-                <el-select v-model="searchs.T_dept" class="m-2" clearable placeholder="请选择部门">
-                  <el-option
-                    v-for="item in globalStore.GET_Dept_List"
-                    :key="item.Id"
-                    :label="item.T_name"
-                    :value="item.Id"
-                  />
-                </el-select>
-              </el-col>
-              <el-col :span="14" class="d-flex padding-right-0">
-                <span class="inline-flex">姓名:</span>
-                <el-input v-model="searchs.T_name" type="text" class="w-50 m-2" />
-                <el-button type="primary" @click="searchHandle">搜索</el-button>
-              </el-col>
-            </el-row>
-          </div>
+          <el-row :gutter="24" class="input-suffix margin-left-0 margin-right-0">
+            <el-col :span="24" class="d-flex padding-left-0 padding-right-0" style="margin-bottom: 5px">
+              <span class="inline-flex">部门:</span>
+              <el-select v-model="searchs.T_dept" clearable placeholder="请选择部门">
+                <el-option
+                  v-for="item in globalStore.GET_Dept_List"
+                  :key="item.Id"
+                  :label="item.T_name"
+                  :value="item.Id"
+                />
+              </el-select>
+            </el-col>
+            <el-col :span="24" class="d-flex padding-right-0 padding-left-0">
+              <span class="inline-flex">姓名:</span>
+              <el-input v-model="searchs.T_name" type="text" />
+              <el-button type="primary" @click="searchHandle">搜索</el-button>
+            </el-col>
+          </el-row>
         </template>
         <template #T_name="{ row }">
           <el-button type="primary" text @click="getSalary(row)">{{ 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>
-        </div>
-      </el-card>
-      <el-card class="m-b-3">
-        <el-row :gutter="20">
-          <el-col :span="10" class="d-flex">
-            <span class="demonstration">年:</span>
-            <el-date-picker v-model="salaryFromData.year" value-format="YYYY" type="year" placeholder="请选择年" />
-          </el-col>
-          <el-col :span="10" class="d-flex">
-            <span class="demonstration">月:</span>
-            <el-date-picker
-              v-model="salaryFromData.month"
-              popper-class="picker-date"
-              value-format="MM"
-              format="MM"
-              type="month"
-              placeholder="请选择月"
-            />
-          </el-col>
-        </el-row>
-      </el-card>
-      <SalaryFrom v-bind="salaryFromData"></SalaryFrom>
-    </el-col>
-  </el-row>
+        </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="请选择年" />
+              <span class="demonstration">月:</span>
+              <el-date-picker
+                v-model="salaryFromData.month"
+                popper-class="picker-date"
+                value-format="MM"
+                format="MM"
+                type="month"
+                placeholder="请选择月"
+              />
+            </el-col>
+          </el-row>
+        </el-card>
+        <SalaryFrom v-bind="salaryFromData"></SalaryFrom>
+      </el-col>
+    </el-row>
+  </div>
 </template>
 
 <style scoped lang="scss">

+ 1 - 1
src/views/salary/salary/SalaryFrom.vue

@@ -61,11 +61,11 @@ const props = defineProps<PropsType>()
 
 onMounted(() => {
   resize()
+  window.onresize = resize
 })
 onUnmounted(() => {
   window.onresize = null
 })
-window.onresize = resize
 </script>
 
 <template>

+ 9 - 0
src/views/salary/salary/index.scss

@@ -1,3 +1,8 @@
+.salary {
+  display: flex;
+  overflow: hidden;
+}
+
 .submit {
   padding: 0.5rem 1.875rem;
   margin-top: 1.25rem;
@@ -22,8 +27,12 @@
   align-items: center;
   flex-wrap: nowrap;
 }
+.justify-start {
+  justify-content: start;
+}
 .input-suffix {
   width: 100%;
+  flex-direction: column;
   .w-50 {
     width: 12.5rem;
   }

+ 269 - 5
src/views/workAttendance/MyLeave.vue

@@ -1,7 +1,271 @@
-<template>
-  <div>我的请假</div>
-</template>
+<script setup lang="ts">
+import { ref, reactive, onMounted, nextTick } from 'vue'
+import { Leave_User_list, LeaveType_List, Leave_Add, Leave_Edit, Leave_Del } from '@/api/workAttendance/index'
+import { User_List } from '@/api/user/index'
+import TableBase from '@/components/TableBase/index.vue'
+import Drawer from '@/components/Drawer/index.vue'
+import Dialog from '@/components/dialog/Dialog.vue'
+import { ColumnProps } from '@/components/TableBase/interface/index'
+import { GlobalStore } from '@/stores/index'
+import { Edit, Delete } from '@element-plus/icons-vue'
+import type { FormInstance, FormRules } from 'element-plus'
+import { ElMessageBox, ElMessage } from 'element-plus'
+const globalStore = GlobalStore()
+const TableRef = ref()
+const columns: ColumnProps[] = [
+  { prop: 'T_type_name', label: '请假类型' },
+  { prop: 'T_start_time', label: '开始时间' },
+  { prop: 'T_end_time', label: '结束时间' },
+  { prop: 'T_duration', label: '请假时长' },
+  { prop: 'T_text', label: '理由' },
+  { prop: 'T_State', label: '审核', name: 'T_State' },
+  { prop: 'operation', label: '操作', width: 200, fixed: 'right' }
+]
+const initParam = {
+  User_tokey: globalStore.GET_User_tokey
+}
+//drawer
+const drawerRef = ref()
+const ruleFormRef = ref<FormInstance>()
+type Fn = () => void
+const callbackDrawer = (done: Fn) => {
+  resetForm(ruleFormRef.value)
+  done()
+}
+const resetForm = (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  formEl.resetFields()
+}
+
+const openDrawerLeave = (str: string, row: any) => {
+  drawerRef.value.openDrawer()
+  if (str === 'edit') {
+    isNew = false
+    nextTick(() => {
+      form.value = { ...row }
+      uuid = row.T_approver
+      form.value.T_approver = row.T_user_name
+      form.value.T_id = row.Id
+    })
+  }
+}
+const LeaveDelete = (row: any) => {
+  ElMessageBox.confirm('您确定要删除请假申请吗?', '警告', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  })
+    .then(async () => {
+      const res: any = await Leave_Del({ User_tokey: globalStore.GET_User_tokey, T_id: row.Id })
+      if (res.Code === 200) {
+        ElMessage({
+          type: 'success',
+          message: '删除成功!'
+        })
+        nextTick(() => {
+          TableRef.value.getTableList()
+        })
+      }
+    })
+    .catch(() => {
+      ElMessage({
+        type: 'warning',
+        message: '取消成功!'
+      })
+    })
+}
+const AddLeave = (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  formEl.validate(async valid => {
+    if (valid) {
+      let res: any = {}
+      console.log(form.value, uuid)
+      if (isNew) {
+        res = await Leave_Add({ ...form.value, T_approver: uuid })
+      } else {
+        res = await Leave_Edit({ ...form.value, T_approver: uuid })
+      }
+      if (res.Code === 200) {
+        ElMessage({
+          type: 'success',
+          message: `${isNew ? '申请' : '修改'}成功!`
+        })
+        nextTick(() => {
+          drawerRef.value.closeDrawer()
+          TableRef.value.getTableList()
+          resetForm(ruleFormRef.value)
+          isNew = true
+        })
+      }
+    } else {
+      return false
+    }
+  })
+}
+const rules = reactive<FormRules>({
+  T_type: [{ required: true, message: '请选择请假类型', trigger: 'blur' }],
+  T_start_time: [{ required: true, message: '请选择开始时间', trigger: 'blur' }],
+  T_end_time: [{ required: true, message: '请选择结束时间', trigger: 'blur' }],
+  T_duration: [
+    { required: true, message: '请输入请假时长', trigger: 'blur' },
+    { type: 'number', message: '必须是整数' }
+  ],
+  T_approver: [{ required: true, message: '请选择审批人', trigger: 'blur' }]
+})
+let uuid = ''
+const form = ref({
+  T_type: '',
+  T_start_time: '',
+  T_end_time: '',
+  T_duration: '',
+  T_text: '',
+  T_approver: '',
+  T_id: ''
+})
+const LeaveType = ref<any>([])
+const getLeaveTypeList = async () => {
+  const res: any = await LeaveType_List({ User_tokey: globalStore.GET_User_tokey, page: 1, page_z: 999 })
+  if (res.Code === 200) {
+    LeaveType.value = res.Data
+  }
+}
+const formLabelWidth = ref('100px')
+let isNew = true
+const selectApprover = () => {
+  dialog.value.DialogOpen()
+}
 
-<script setup lang="ts"></script>
+// dialog
+const dialog = ref()
+const search = ref('')
+const tableApproverRef = ref()
+const Dialogcolumns: ColumnProps[] = [{ prop: 'T_name', label: '名字', name: 'T_name' }]
+const approverInitParam = {
+  User_tokey: globalStore.GET_User_tokey,
+  T_name: '',
+  T_dept_leader: 1
+}
+const searchHandle = () => {
+  approverInitParam.T_name = search.value
+  tableApproverRef.value.searchTable()
+}
+const getApproverInfo = (row: any) => {
+  uuid = row.T_uuid
+  form.value.T_approver = row.T_name
+  dialog.value.DialogClose()
+}
+onMounted(() => {
+  getLeaveTypeList()
+})
+</script>
 
-<style scoped></style>
+<template>
+  <TableBase ref="TableRef" :columns="columns" :requestApi="Leave_User_list" :initParam="initParam">
+    <template #table-header>
+      <el-row :gutter="24" class="input-suffix">
+        <el-col :span="8" :offset="16" class="d-flex padding-right-0">
+          <el-button type="primary" @click="openDrawerLeave">请假申请</el-button>
+        </el-col>
+      </el-row>
+    </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 === 0" type="warning">未通过 4</el-tag>
+      <el-tag v-else type="danger">待审核</el-tag>
+    </template>
+    <template #right="{ row }">
+      <el-button link type="primary" size="small" :icon="Edit" @click="openDrawerLeave('edit', row)">编辑</el-button>
+      <el-button link type="danger" size="small" :icon="Delete" @click="LeaveDelete(row)">删除</el-button>
+    </template>
+  </TableBase>
+  <Drawer ref="drawerRef" :handleClose="callbackDrawer">
+    <template #header="{ params }">
+      <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_type">
+        <el-select v-model="form.T_type" placeholder="请选择类型">
+          <el-option v-for="item in LeaveType" :key="item.Id" :label="item.T_name" :value="item.Id" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="开始时间:" :label-width="formLabelWidth" prop="T_start_time">
+        <el-date-picker
+          style="width: 100%"
+          class="my-date-picker"
+          v-model="form.T_start_time"
+          type="datetime"
+          placeholder="选择开始时间"
+          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">
+        <el-date-picker
+          style="width: 100%"
+          class="my-date-picker"
+          v-model="form.T_end_time"
+          type="datetime"
+          placeholder="选择结束时间"
+          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-form-item>
+      <el-form-item label="内容:" :label-width="formLabelWidth">
+        <el-input
+          v-model="form.T_text"
+          autocomplete="off"
+          type="textarea"
+          :autosize="{ minRows: 4, maxRows: 6 }"
+          placeholder="请输入内容"
+        />
+      </el-form-item>
+      <el-form-item label="审批:" :label-width="formLabelWidth" prop="T_approver">
+        <el-input v-model="form.T_approver" autocomplete="off" placeholder="审批人" @focus="selectApprover" />
+      </el-form-item>
+      <el-form-item :label-width="formLabelWidth">
+        <el-button v-if="isNew" color="#626aef" @click="AddLeave(ruleFormRef)">添加</el-button>
+        <el-button v-else color="#626aef" @click="AddLeave(ruleFormRef)">修改</el-button>
+      </el-form-item>
+    </el-form>
+  </Drawer>
+  <Dialog ref="dialog" width="30%">
+    <template #header>
+      <h3>选择审批人</h3>
+    </template>
+    <TableBase
+      ref="tableApproverRef"
+      :columns="Dialogcolumns"
+      :initParam="approverInitParam"
+      :requestApi="User_List"
+      layout="total, prev, pager, next"
+    >
+      <template #table-header>
+        <el-row :gutter="20">
+          <el-col :span="20" class="d-flex">
+            <span class="inline-flex">账户查询:</span>
+            <el-input v-model="search" type="text" class="w-50 m-2" />
+            <el-button type="primary" @click="searchHandle">搜索</el-button>
+          </el-col>
+        </el-row>
+      </template>
+      <template #T_name="{ row }">
+        <el-button type="primary" link @click="getApproverInfo(row)">{{ row.T_name }}</el-button>
+      </template>
+    </TableBase>
+  </Dialog>
+</template>
+<style scoped lang="scss">
+.input-suffix {
+  width: 100%;
+}
+.inline-flex {
+  white-space: nowrap;
+}
+.d-flex {
+  display: flex;
+  align-items: center;
+}
+</style>

+ 17 - 1
vite.config.ts

@@ -2,6 +2,7 @@ import { defineConfig, loadEnv, ConfigEnv, UserConfig } from 'vite'
 import { createHtmlPlugin } from 'vite-plugin-html'
 import vue from '@vitejs/plugin-vue'
 import { resolve } from 'path'
+import path from 'path'
 // import eslintPlugin from "vite-plugin-eslint";
 import AutoImport from 'unplugin-auto-import/vite'
 import Components from 'unplugin-vue-components/vite'
@@ -15,7 +16,8 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
     resolve: {
       alias: {
         '~': resolve(__dirname, './'),
-        '@': resolve(__dirname, './src')
+        '@': resolve(__dirname, './src'),
+        '@assets': path.resolve(__dirname, './assets/images')
       }
     },
     server: {
@@ -59,6 +61,20 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
     // * 打包去除 console.log && debugger
     esbuild: {
       pure: env.VITE_DROP_CONSOLE ? ['console.log', 'debugger'] : []
+    },
+    build: {
+      rollupOptions: {
+        output: {
+          // 打包后文件命名
+          assetFileNames: '[hash].[name].[ext]'
+        }
+      },
+      // 图片大小分界线:大于 4kb,图片正常打包。小于 4kb,图片会转化为 base64。
+      assetsInlineLimit: 4096, // 4kb
+      // 打包后名称,默认是 dist
+      outDir: 'testDir',
+      // 打包后静态目录名称
+      assetsDir: 'static'
     }
   }
 })