2 Commitit 203719456b ... 3075b5f1b7

Tekijä SHA1 Viesti Päivämäärä
  lxy_01 3075b5f1b7 加班申请手机端 11 tuntia sitten
  lxy_01 824ed76a48 加班申请手机端 15 tuntia sitten

+ 2 - 1
fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/controller/FactoryController.java

@@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
 
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.annotation.Resource;
 import java.time.LocalDateTime;
 
 /**
@@ -24,7 +25,7 @@ import java.time.LocalDateTime;
 @RestController
 @RequestMapping("/factory")
 public class FactoryController {
-    @Autowired
+    @Resource
     private FactoryService factoryService;
     @RequestMapping("/getFactories")
     public HttpRespMsg getFactories(){

+ 32 - 5
fhKeeper/formulahousekeeper/timesheet-workshop-h5/src/components/chooseSomeone.vue

@@ -34,7 +34,7 @@
           @click="newGroupViewBackCli(true, groupView)"><van-icon name="arrow-left" />返回</div>
         <div class="treeBox_tree">
           <el-tree ref="tree" v-model="treeVal" show-checkbox node-key="id" :data="personnelTree" :props="defaultProps"
-            :filter-node-method="filterNode">
+            :filter-node-method="filterNode" @check-change="handleCheckChange">
             <span class="custom-tree-node" slot-scope="{ node }">
               <span>
                 {{ node.label }}
@@ -100,6 +100,17 @@ export default {
       type: Boolean,
       default:()=> true, 
     },
+    //仅在加班页面调用时使用
+    IsOverTime:{
+      type: Boolean,
+      default:()=> false, 
+    },
+    deptIdTree: {
+      type: Array,
+      default: () => []
+    },
+
+
   },
   components: {},
   data() {
@@ -141,11 +152,16 @@ export default {
       } else if (this.groupView == 2) {
         this.groupVal = newPpeopleListId
       } else if (this.groupView == 3) {
-        // tree 额外处理
-        console.log('tree 额外处理')
+        this.groupVal = newPpeopleListId
+
       }
     }
-    console.log(this.groupVal)
+    console.log("====================" + this.groupView)
+    if (this.groupView == 3 && this.IsOverTime) {
+      let newDeptIdTree = JSON.parse(JSON.stringify(this.deptIdTree))
+      this.setUserToDept(newDeptIdTree)
+      this.personnelTree = JSON.parse(JSON.stringify(newDeptIdTree))
+    }
     if (this.peopleList.length <= 0) {
       this.getPeople()
     }
@@ -168,6 +184,9 @@ export default {
       if (!value) return true;
       return data.label.indexOf(value) !== -1;
     },
+    handleCheckChange() {
+      this.treeVal = this.$refs.tree.getCheckedKeys()
+    },
     newGroupViewBackCli(flg, i) {
       this.newGroupViewBack = flg
       if(this.type != 1) {
@@ -215,7 +234,12 @@ export default {
             this.setUserToDept(res.data)
             this.personnelTree = list
             this.newPersonnelTree = JSON.parse(JSON.stringify(list))
-            console.log(this.personnelTree)
+            
+            if (this.IsOverTime) {
+                let newDeptIdTree = JSON.parse(JSON.stringify(this.deptIdTree))
+                this.setUserToDept(newDeptIdTree)
+                this.personnelTree = JSON.parse(JSON.stringify(newDeptIdTree))
+            }
           } else {
             this.$toast.clear();
             this.$toast.fail(res.msg);
@@ -243,6 +267,8 @@ export default {
     // 单选和复选点击确定触发的事件
     handClick() {
       let arr = this.groupView == 1 ? [this.radioVal] : this.groupVal
+      arr = this.groupView == 3 ? this.treeVal : arr
+      console.log(arr)
       let newArr = this.personnelList.filter(item => {
         return arr.includes(item.id)
       })
@@ -250,6 +276,7 @@ export default {
       // return
       this.loadingBtn = true
       this.$emit('ChooseSomeoneChanhe', newArr)
+      this.loadingBtn = false
     },
     treeHandClick() {
       if(this.newGroupView == 3) {

+ 8 - 0
fhKeeper/formulahousekeeper/timesheet-workshop-h5/src/router/index.js

@@ -111,6 +111,14 @@ const router = new Router({
               keepAlive: false
           }
       },
+      {
+        path: "/overtimeApplication",
+        component: () => import("@/views/overtime/overtimeApplication"),
+        meta: {
+            title: "加班管理",
+            keepAlive: false
+        }
+    },
      {
         path: "/MemberInfo",
         component: () => import("@/views/groupView/info"),

+ 8 - 0
fhKeeper/formulahousekeeper/timesheet-workshop-h5/src/views/index/index.vue

@@ -243,6 +243,14 @@ export default {
                 icon: 'balance-list-o',
                 fixed: true
               },
+              {
+                name: '加班管理',
+                moudelName: '加班管理',
+                url: '/overtimeApplication',
+                icon: 'balance-list-o',
+                fixed: true
+              },
+
             ]
             console.log(routersList, modelNameList)
             this.routers = routersList.filter(item => item.fixed || modelNameList.includes(item.moudelName));

+ 523 - 0
fhKeeper/formulahousekeeper/timesheet-workshop-h5/src/views/overtime/overtimeApplication.vue

@@ -0,0 +1,523 @@
+<template>
+  <div class="overtime-apply">
+    <van-card class="form-card">
+      <template #header>
+        <div class="clearfix">
+          <span>加班申请</span>
+        </div>
+      </template>
+      
+      <van-form ref="overtimeForm">
+        <!-- 加班日期 -->
+        <van-field
+          label="加班日期"
+          v-model="overtimeForm.overtimeDate"
+          readonly
+          clickable
+          placeholder="选择加班日期"
+          @click="showDatePicker = true"
+          :rules="[{ required: true, message: '请选择加班日期' }]"
+        />
+
+        <!-- 加班人员 -->
+        <van-field
+          label="加班人员"
+          v-model="overtimeForm.employees"
+          readonly
+          clickable
+          placeholder="请选择部门及人员"
+          @click="popupShow = true"
+          :rules="[{ required: true, message: '请选择加班人员' }]"
+        />
+
+        <!-- 用餐类别 -->
+        <van-field
+          label="用餐类别"
+          v-model="overtimeForm.mealTypeText"
+          readonly
+          clickable
+          placeholder="请选择用餐类别"
+          @click="showMealTypePicker = true"
+          :rules="[{ required: true, message: '请选择用餐类别' }]"
+        />
+
+        <van-popup v-model:show="showMealTypePicker" position="bottom" round closeable>
+          <div style="max-height: 80vh; overflow-y: auto; padding: 20px;">
+            <h3 style="font-size: 16px; margin-bottom: 15px;">选择餐型</h3>
+            <van-checkbox-group v-model="mealTypeIndex">
+              <van-checkbox
+                v-for="(item, index) in mealTypeColumns"
+                :key="index"
+                :name="item.value"
+                style="margin-bottom: 10px;"
+              >
+                {{ item.value }}
+              </van-checkbox>
+            </van-checkbox-group>
+          
+            <div style="margin-top: 20px; display: flex; gap: 10px;">
+              <van-button type="default" block @click="showMealTypePicker = false">取消</van-button>
+              <van-button type="primary" block @click="onMealTypeSubmit">确定</van-button>
+            </div>
+          </div>
+        </van-popup>
+
+        <!-- 加班时段 -->
+        <van-field
+          label="加班时段"
+          v-model="timeTypeText"
+          readonly
+          clickable
+          placeholder="选择开始时间"
+          @click="showTimeTypePickerClick"
+          :rules="[{ required: true, message: '请选择加班时段' }]"
+          class="time-field-with-button"
+        >
+        </van-field>
+        <van-popup v-model:show="showTimeTypePicker" position="bottom">
+          <van-picker
+            v-model="timeTypeIndex"
+            :columns="timeTypeColumnsText"
+            @change="handleTimeTypeChange"
+            @confirm="handleTimeTypeConfirm"
+            @cancel="showTimeTypePicker = false"
+          />
+        </van-popup>
+
+
+        
+
+        <!-- 工作内容 -->
+        <van-field
+          label="工作内容"
+          v-model="overtimeForm.workContent"
+          type="textarea"
+          :rows="4"
+          placeholder="请输入工作内容"
+        />
+
+        <!-- 提交按钮 -->
+        <div style="margin-top: 20px;">
+          <van-button type="primary" @click="submit" native-type="submit" block>提交申请</van-button>
+          <van-button @click="resetForm" plain block style="margin-top: 10px;">重置</van-button>
+        </div>
+      </van-form>
+    </van-card>
+
+     
+    <!-- 弹出层选人 -->
+    <van-popup v-model="popupShow" round position="bottom" :style="{ height: '80%',background: '#F4F4F4' }" >
+      <ChooseSomeone ref="ChooseSomeoneOne" :groupView="this.groupViewNum" :groupViewBack="true" :peopleList="peopleList" :IsOverTime="true" :deptIdTree="deptIdTree" @ChooseSomeoneChanhe="chooseSomeoneChanhe" :peopleListId="peopleListId" ></ChooseSomeone>
+    </van-popup>
+    
+    <!-- 加班日期 -->
+    <!-- 日期 -->
+    <van-calendar v-model="showDatePicker" ref="calendarRef" @confirm="dateOnConfirm" />
+  </div>
+</template>
+
+<script>
+
+import axios from 'axios';
+import ChooseSomeone from '../../components/chooseSomeone.vue'
+import { Dialog, Toast } from 'vant';
+export default {
+
+name: 'OvertimeApply',
+
+  components: {
+    ChooseSomeone,
+  },
+  data() {
+    return {overtimeForm: {
+        overtimeDate: '',       // 加班日期
+        employees: [],          // 选中的员工ID数组
+        employeeIds: [],        // 选中的员工ID数组
+        mealType: [],           // 选中的用餐类别
+        mealTypeId: [],         // 选中的用餐类别ID
+        timeType: '',           // 选中的时间段ID
+        workContent: '',        // 工作内容描述
+        startTime: '',          // 加班开始时间
+        endTime: '',            // 加班结束时间
+        duration: ''            // 加班时长
+      },
+      
+      // 用户信息和权限控制
+      user: JSON.parse(sessionStorage.getItem("user") || '{}'),       // 当前登录用户信息
+      permissions: JSON.parse(sessionStorage.getItem("permissions") || '[]'), // 用户权限列表
+      hasManagePermission: false,  // 是否有时间段管理权限
+      
+      // 日期选择器控制
+      showDatePicker: false,    // 日期选择器弹窗显示状态
+
+      // 部门及人员选择
+      popupShow: false,    // 日期选择器弹窗显示状态
+
+      
+      peopleList: [],   // 可选人员列表
+      peopleListId: [],
+      deptIdTree: [],//部门及人员树状结构数据
+      groupViewNum:3,
+      peopleType:0,
+      //用餐类别
+      mealTypeText: '',
+      // 用餐类别选择器
+      showMealTypePicker: false,
+      // 用餐类别数据
+      mealTypeColumns: [
+        { value: '中餐', id: 0 },
+        { value: '晚餐', id: 1 },
+        { value: '夜宵', id: 2 }
+      ],
+      // 选中的用餐类别索引
+        mealTypeIndex: [],
+      //时间段
+      timeTypeText: '',
+      // 时间段选择器
+      showTimeTypePicker: false,
+      // 时间段编辑器
+      showTimeEditDialog: false,
+      // 时间段数据
+      timeTypeColumns: [
+        { text: '8:00-12:00', value: 1 },
+        { text: '12:00-16:00', value: 2 },
+        { text: '16:00-20:00', value: 3 }
+      ],
+      // 选中的时间段索引
+      timeTypeIndex: 0,
+      showTimeFormDialog: false,
+
+      // 添加编辑时间段的中转数据
+      timeForm: {
+        startTime: '',
+        endTime: '',
+        hours: ''
+      },
+
+      // 开始时间选择器弹窗显示状态
+      showStartPicker: false,
+
+      // 结束时间选择器弹窗显示状态
+      showEndPicker: false,
+
+      // 时间段弹窗显示状态
+      showTimeDialog: false,
+
+      //部门id
+      desId: null,
+      
+    }
+  },
+
+  computed: {
+    timeTypeColumnsText() {
+      return this.timeTypeColumns.map(item => `${item.startTime} - ${item.endTime}, ${item.hours}小时`);
+    }
+  },
+  mounted() {
+    this.getPeople()
+    this.getTimeTypeList();
+  },
+  watch: {
+    // 监听开始时间和结束时间变化,自动计算时长
+    'overtimeForm.startTime'(newVal) {
+      this.calculateDuration();
+    },
+    'overtimeForm.endTime'(newVal) {
+      this.calculateDuration();
+    },
+    // 监听时间段表单的开始时间和结束时间变化,自动计算时长
+    'timeForm.startTime'(newVal) {
+      this.calculateTimeFormDuration();
+    },
+    'timeForm.endTime'(newVal) {
+      this.calculateTimeFormDuration();
+    }
+  },
+  methods: {
+    //时间选择相关
+    dateOnConfirm(date) {
+      this.overtimeForm.overtimeDate = this.formatDate(date);
+      this.showDatePicker = false;
+    },
+    
+
+    formatDate(date) {
+      // 中国标准时间转成 YYYY-MM-DD
+      const year = date.getFullYear();
+      const month = date.getMonth() + 1;
+      const day = date.getDate();
+      return `${year}-${month < 10 ? '0' + month : month}-${day < 10 ? '0' + day : day}`;
+    },
+
+    
+    // 选中人员
+    chooseSomeoneChanhe(item) {
+      console.log('当前点击的人员', item)
+      let arr = item.map(item => {
+        return item.id
+      })
+      let nameArr = item.map(item => {
+        return item.name
+      })
+      this.employeeIds = arr
+      
+      this.overtimeForm.employeeIds = arr
+      this.overtimeForm.employees = nameArr
+      this.popupShow = false
+      this.$emit('flashList')
+    },
+    // 时间段选择器确认事件
+    handleTimeTypeConfirm() {
+      this.timeTypeText = this.timeTypeColumns.find(item => item.value == this.overtimeForm.timeType).text;
+      this.showTimeTypePicker = false;
+    },
+    // 重置表单
+    resetForm() {   
+      this.overtimeForm = {
+        overtimeDate: '',       // 加班日期
+        employees: [],          // 选中的员工ID数组
+        mealType: [],           // 选中的用餐类别
+        timeType: '',           // 选中的时间段ID
+        workContent: '',        // 工作内容描述
+        startTime: '',          // 加班开始时间
+        endTime: '',            // 加班结束时间
+        duration: ''            // 加班时长
+      }
+      this.timeTypeText = ''
+      this.timeForm = {
+        startTime: '',
+        endTime: '',
+        hours: ''
+      }
+      this.mealTypeText = ''
+      this.mealTypeIndex = []
+      
+      if (this.$refs.calendarRef && this.$refs.calendarRef.reset) {
+        this.$refs.calendarRef.reset();
+      }
+    },
+
+    
+    getPeople() {
+      this.$axios.post('/department/userListInMyRange', {
+      })
+      .then(res => {
+        if (res.code == "ok") {
+          let list = res.data;
+          let newList = JSON.parse(JSON.stringify(list))
+          if(newList && newList.length > 0) {
+            this.desId = newList[0].id
+          }
+          this.deptIdTree = list
+          console.log(this.peopleList)
+        } else {
+          JSON.parse()
+          this.$toast.clear();
+          this.$toast.fail(res.msg);
+        }
+      }).catch(err => { this.$toast.clear(); });
+    },
+
+
+    showTimeForm() {
+      // 显示添加时间段表单的逻辑
+      console.log("显示添加时间段表单")
+    },
+
+    // 用餐类别 提交选择
+    onMealTypeSubmit() {
+      this.overtimeForm.mealTypeText = this.mealTypeIndex.join(',')
+      this.overtimeForm.mealTypeId = this.mealTypeIndex.map(item => item.id)
+      this.mealTypeText = this.mealTypeIndex.join(',')
+      this.showMealTypePicker = false;
+    },
+
+    // 加班时段选择器点击事件
+    showTimeTypePickerClick() {
+      this.showTimeTypePicker = true;
+    },
+
+    //时间段弹窗点击事件
+    handleTimeTypeChange(picker,value,index) {
+      console.log(value, index)
+      this.overtimeForm.startTime = this.timeTypeColumns[index].startTime
+      this.overtimeForm.endTime = this.timeTypeColumns[index].endTime
+      this.overtimeForm.timeType = this.timeTypeColumns[index].id
+      this.timeTypeText = this.overtimeForm.startTime + ' - ' + this.overtimeForm.endTime + ', ' + this.timeTypeColumns[index].hours + '小时';
+      this.showTimeTypePicker = false;
+    },
+
+    //加载时间段列表
+    getTimeTypeList() {
+      this.$axios.post('/overtime-setting/list',{
+          }).then(
+          res => {
+            if (res.code == "ok") {
+              this.timeTypeColumns = res.data
+            } else {
+              
+            }
+          },
+          error => {
+            console.error('获取时间段列表失败:', error);
+          }
+      ).catch(err => { this.$toast.clear(); });
+    },
+
+    //时间段编辑
+    editTimeType(item) {
+      this.showTimeFormDialog = true;
+      this.timeForm = { ...item }; // 复制当前时间段数据到表单
+      this.showStartPicker = false;
+      this.showEndPicker = false;
+    },
+
+    //时间段删除
+    deleteTimeType(item) {
+      Dialog.confirm({
+          title: '确认删除',
+          message: `确定要删除该时间段吗?\n此操作不可撤销`,
+          messageAlign: 'left',
+          confirmButtonText: '删除',
+          cancelButtonText: '取消',
+          theme: 'round-button',
+        })
+          .then(() => {
+            // 用户点击“删除”
+            this.$axios.post('/overtime-setting/delete', {
+              id: item.id
+            }).then(res => {
+              if (res.code == "ok") {
+                this.getTimeTypeList()
+                Toast.success('时间段删除成功');
+              } else {
+                JSON.parse()
+                this.$toast.clear();
+                this.$toast.fail(res.msg);
+              }
+            })
+          })
+          .catch(() => {
+            // 用户点击“取消”,什么也不做
+          });
+    },
+
+    // 开始时间选择器确认事件
+    onStartConfirm(value) {
+      this.timeForm.startTime = value;
+      this.showStartPicker = false;
+    },
+    // 结束时间选择器确认事件
+    onEndConfirm(value) {
+      this.timeForm.endTime = value;
+      this.showEndPicker = false;
+    },
+
+    //提交申请
+    submit() {
+      if (
+          this.overtimeForm.overtimeDate && this.overtimeForm.overtimeDate.trim() !== '' &&
+          this.overtimeForm.employeeIds && this.overtimeForm.employeeIds.length > 0 &&
+          this.overtimeForm.mealTypeText && this.overtimeForm.mealTypeText.trim() !== '' &&
+          this.overtimeForm.timeType &&
+          this.overtimeForm.workContent && this.overtimeForm.workContent.trim() !== '' &&
+          this.overtimeForm.startTime && this.overtimeForm.startTime.trim() !== '' &&
+          this.overtimeForm.endTime && this.overtimeForm.endTime.trim() !== '' &&
+          this.overtimeForm.duration && this.overtimeForm.duration.trim() !== ''
+        ) {
+        // 所有必填项均已填写,继续提交
+      } else {
+        this.$toast.fail('请填写完整的加班申请信息');
+        return;
+      } 
+      // 构建提交参数,映射到后端实体字段
+      const params = {
+        // applicant: 申请人 - 可以从用户信息中获取,这里暂时留空让后端处理
+        employeeIds: this.overtimeForm.employeeIds.join(','), // 加班员工ID列表,用逗号分隔
+        workDate: this.overtimeForm.overtimeDate, // 加班日期
+        startTime: this.overtimeForm.startTime, // 加班开始时间
+        endTime: this.overtimeForm.endTime, // 加班结束时间
+        hours: parseFloat(this.overtimeForm.duration), // 加班时长
+        content: this.overtimeForm.workContent, // 工作内容
+        timeTypeId: this.overtimeForm.timeType, // 加班时段ID
+        mealType: this.overtimeForm.mealTypeText, // 用餐类别,用逗号分隔
+      }
+      console.log(params)
+      this.$axios.post('/work-overtime/addOrUpdate', params)
+      .then(
+        res => {
+          if (res.code == "ok") {
+            console.log(res)
+            Toast.success('加班申请提交成功!');
+            this.resetForm(); // 重置表单
+          } else {
+            console.log(res)
+            Toast.fail(res.msg || '提交失败');
+          }
+        },
+
+      )
+      .catch(
+        error => {
+          console.log('catch 错误:', error)
+          Toast.fail('请求异常');
+        });
+    },
+
+    
+    calculateDuration() {
+      if (this.overtimeForm.startTime && this.overtimeForm.endTime) {
+        const start = this.overtimeForm.startTime.split(':');
+        const end = this.overtimeForm.endTime.split(':');
+
+        const startDate = new Date();
+        startDate.setHours(start[0], start[1], 0);
+
+        const endDate = new Date();
+        endDate.setHours(end[0], end[1], 0);
+
+        // 处理跨天情况
+        if (endDate < startDate) {
+          endDate.setDate(endDate.getDate() + 1);
+        }
+
+        const diff = (endDate - startDate) / (1000 * 60 * 60); // 转换为小时
+        this.overtimeForm.duration = diff.toFixed(2);
+      }
+    },
+ }
+}
+</script>
+
+<style scoped>
+.overtime-apply {
+  padding: 10px;
+}
+
+.form-card {
+  margin-bottom: 10px;
+}
+
+.clearfix::after {
+  content: "";
+  display: table;
+  clear: both;
+}
+
+.van-table {
+  --van-table-row-height: 40px;
+}
+
+.time-field-with-button {
+  position: relative;
+}
+
+.manage-time-btn {
+  position: absolute;
+  right: 10px;
+  top: 50%;
+  transform: translateY(-50%);
+  z-index: 1;
+}
+</style>