瀏覽代碼

Merge remote-tracking branch 'origin/master'

yusm 1 月之前
父節點
當前提交
cc22c7a4bf
共有 27 個文件被更改,包括 11643 次插入1661 次删除
  1. 9701 61
      fhKeeper/formulahousekeeper/course-manager/courseManager.log
  2. 1 1
      fhKeeper/formulahousekeeper/course-manager/src/main/java/com/management/platform/controller/CommonUploadController.java
  3. 2 1
      fhKeeper/formulahousekeeper/course-manager/src/main/java/com/management/platform/controller/CourseTypeController.java
  4. 21 0
      fhKeeper/formulahousekeeper/course-manager/src/main/java/com/management/platform/controller/SysUserController.java
  5. 11 0
      fhKeeper/formulahousekeeper/course-manager/src/main/java/com/management/platform/entity/CourseInfo.java
  6. 46 0
      fhKeeper/formulahousekeeper/course-manager/src/main/java/com/management/platform/entity/SysUser.java
  7. 16 0
      fhKeeper/formulahousekeeper/course-manager/src/main/java/com/management/platform/mapper/SysUserMapper.java
  8. 16 0
      fhKeeper/formulahousekeeper/course-manager/src/main/java/com/management/platform/service/SysUserService.java
  9. 20 0
      fhKeeper/formulahousekeeper/course-manager/src/main/java/com/management/platform/service/impl/SysUserServiceImpl.java
  10. 3 3
      fhKeeper/formulahousekeeper/course-manager/src/main/resources/application-dev.yml
  11. 1 1
      fhKeeper/formulahousekeeper/course-manager/src/main/resources/application.yml
  12. 18 0
      fhKeeper/formulahousekeeper/course-manager/src/main/resources/mapper/SysUserMapper.xml
  13. 112 0
      fhKeeper/formulahousekeeper/course-pc/src/components/TinymceEditor.vue
  14. 1039 1487
      fhKeeper/formulahousekeeper/course-pc/src/views/coursemanagement/list.vue
  15. 4 0
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/team/api.ts
  16. 214 26
      fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/team/index.vue
  17. 53 5
      fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/request.ts
  18. 2 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java
  19. 160 14
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportLogController.java
  20. 6 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportLogDetailMapper.java
  21. 3 11
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  22. 22 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/FinanceMonthlyWorktimeMapper.xml
  23. 21 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/FmwDetailMapper.xml
  24. 84 31
      fhKeeper/formulahousekeeper/timesheet/src/components/taskComponent.vue
  25. 47 8
      fhKeeper/formulahousekeeper/timesheet/src/views/project/gantt.vue
  26. 1 0
      fhKeeper/formulahousekeeper/timesheet/src/views/project/list.vue
  27. 19 10
      fhKeeper/formulahousekeeper/timesheet/src/views/project/project_gantt.vue

File diff suppressed because it is too large
+ 9701 - 61
fhKeeper/formulahousekeeper/course-manager/courseManager.log


+ 1 - 1
fhKeeper/formulahousekeeper/course-manager/src/main/java/com/management/platform/controller/CommonUploadController.java

@@ -56,7 +56,7 @@ public class CommonUploadController {
             }
             inputStream.close();
             outputStream.close();
-            msg.data = serverName;
+            msg.data = "/upload/"+serverName;
         } catch (Exception exception) {
             exception.printStackTrace();
             log.error(exception.getMessage());

+ 2 - 1
fhKeeper/formulahousekeeper/course-manager/src/main/java/com/management/platform/controller/CourseTypeController.java

@@ -73,6 +73,7 @@ public class CourseTypeController {
             }
         }
         courseTypeService.saveOrUpdate(courseType);
+        httpRespMsg.data = courseType;
         return httpRespMsg;
 
     }
@@ -135,7 +136,7 @@ public class CourseTypeController {
             courseType.setId(id);
             courseType.setCoverImage("/upload/"+serverName);
             courseTypeService.saveOrUpdate(courseType);
-            msg.data = serverName;
+            msg.data = courseType.getCoverImage();
         } catch (Exception exception) {
             exception.printStackTrace();
         }

+ 21 - 0
fhKeeper/formulahousekeeper/course-manager/src/main/java/com/management/platform/controller/SysUserController.java

@@ -0,0 +1,21 @@
+package com.management.platform.controller;
+
+
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ *  前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-04-16
+ */
+@RestController
+@RequestMapping("/sys-user")
+public class SysUserController {
+
+}
+

+ 11 - 0
fhKeeper/formulahousekeeper/course-manager/src/main/java/com/management/platform/entity/CourseInfo.java

@@ -7,9 +7,12 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import java.time.LocalDateTime;
 import com.baomidou.mybatisplus.annotation.TableField;
 import java.io.Serializable;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
 
 /**
  * <p>
@@ -90,6 +93,8 @@ public class CourseInfo extends Model<CourseInfo> {
      * 上传日期
      */
     @TableField("create_date")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
     private LocalDateTime createDate;
 
     /**
@@ -116,6 +121,12 @@ public class CourseInfo extends Model<CourseInfo> {
     @TableField("course_url")
     private String courseUrl;
 
+    /**
+     * 课程分类名称
+     */
+    @TableField(exist = false)
+    private String courseTypeName;
+
 
     @Override
     protected Serializable pkVal() {

+ 46 - 0
fhKeeper/formulahousekeeper/course-manager/src/main/java/com/management/platform/entity/SysUser.java

@@ -0,0 +1,46 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.TableField;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-04-16
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class SysUser extends Model<SysUser> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @TableField("username")
+    private String username;
+
+    @TableField("pwd")
+    private String pwd;
+
+    @TableField("indate")
+    private LocalDateTime indate;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 16 - 0
fhKeeper/formulahousekeeper/course-manager/src/main/java/com/management/platform/mapper/SysUserMapper.java

@@ -0,0 +1,16 @@
+package com.management.platform.mapper;
+
+import com.management.platform.entity.SysUser;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ *  Mapper 接口
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-04-16
+ */
+public interface SysUserMapper extends BaseMapper<SysUser> {
+
+}

+ 16 - 0
fhKeeper/formulahousekeeper/course-manager/src/main/java/com/management/platform/service/SysUserService.java

@@ -0,0 +1,16 @@
+package com.management.platform.service;
+
+import com.management.platform.entity.SysUser;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ *  服务类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-04-16
+ */
+public interface SysUserService extends IService<SysUser> {
+
+}

+ 20 - 0
fhKeeper/formulahousekeeper/course-manager/src/main/java/com/management/platform/service/impl/SysUserServiceImpl.java

@@ -0,0 +1,20 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.entity.SysUser;
+import com.management.platform.mapper.SysUserMapper;
+import com.management.platform.service.SysUserService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-04-16
+ */
+@Service
+public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
+
+}

+ 3 - 3
fhKeeper/formulahousekeeper/course-manager/src/main/resources/application-dev.yml

@@ -1,5 +1,5 @@
 server:
-  port: 10018
+  port: 10031
   tomcat:
     uri-encoding: utf-8
     max-http-form-post-size: -1
@@ -13,7 +13,7 @@ spring:
       max-request-size: 100MB
   datasource:
     driver-class-name: com.mysql.cj.jdbc.Driver
-    url: jdbc:mysql://1.94.62.58:17089/man_dev?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&useSSL=false
+    url: jdbc:mysql://localhost:17089/course_manager?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&useSSL=false
     username: root
     password: P011430@Huoshi*
     hikari:
@@ -54,7 +54,7 @@ logging:
     #打印sql语句
     com.management.platform.mapper: error
   path: /log/
-  file: octopus.log
+  file: courseManager.log
 ##########
 mybatis-plus:
   #  mapper-locations: classpath:mapper/*/*.xml

+ 1 - 1
fhKeeper/formulahousekeeper/course-manager/src/main/resources/application.yml

@@ -95,7 +95,7 @@ referer:
     - localhost
     - ttkuaiban.com
     - ops.ttkuaiban.com
-    - 47.101.180.183
+    - 1.94.62.58
     - mldworktime.ttkuaiban.com
 excludeUrls: /wxcorp/*,/wxcorp/*/*,/dingding/*,/error,/testClient,/corpWXAuth,/wx-corp-info/*,/clean/*,/innerRoles/*,/operation-record/*
 syncDDMembUrl: http://worktime.ttkuaiban.com/api/dingding/syncCorpMembs

+ 18 - 0
fhKeeper/formulahousekeeper/course-manager/src/main/resources/mapper/SysUserMapper.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.management.platform.mapper.SysUserMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.SysUser">
+        <id column="id" property="id" />
+        <result column="username" property="username" />
+        <result column="pwd" property="pwd" />
+        <result column="indate" property="indate" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, username, pwd, indate
+    </sql>
+
+</mapper>

+ 112 - 0
fhKeeper/formulahousekeeper/course-pc/src/components/TinymceEditor.vue

@@ -0,0 +1,112 @@
+<template>
+  <div class="tinymce-editor">
+    <textarea :id="id" style="visibility:hidden;"></textarea>
+  </div>
+</template>
+
+<script>
+// Use self-hosted TinyMCE
+const loadTinyMCE = () => {
+  if (window.tinymce) return Promise.resolve(window.tinymce)
+  
+  return new Promise((resolve, reject) => {
+    const script = document.createElement('script')
+    script.src = '/static/tinymce/tinymce.min.js'
+    script.onload = () => {
+      if (window.tinymce) {
+        resolve(window.tinymce)
+      } else {
+        reject(new Error('Failed to load TinyMCE'))
+      }
+    }
+    script.onerror = reject
+    document.head.appendChild(script)
+  })
+}
+
+export default {
+  name: 'TinymceEditor',
+  props: {
+    value: {
+      type: String,
+      default: ''
+    },
+    init: {
+      type: Object,
+      default: () => ({})
+    },
+    placeholder: {
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      id: 'tinymce-' + Date.now() + Math.floor(Math.random() * 1000),
+      editor: null,
+      hasChange: false
+    }
+  },
+  watch: {
+    value(val) {
+      if (!this.hasChange && this.editor) {
+        this.editor.setContent(val)
+      }
+    }
+  },
+  mounted() {
+    this.initEditor()
+  },
+  beforeDestroy() {
+    if (this.editor) {
+      this.editor.destroy()
+    }
+  },
+  methods: {
+    async initEditor() {
+      try {
+        const tinymce = await loadTinyMCE()
+        
+        const config = {
+          selector: `#${this.id}`,
+          language_url: this.init.language_url || '/static/tinymce/zh_CN.js',
+          language: this.init.language || 'zh_CN',
+          height: this.init.height || 300,
+          plugins: this.init.plugins || 'link lists image code table wordcount',
+          toolbar: this.init.toolbar || 'undo redo | formatselect | bold italic | alignleft aligncenter alignright | bullist numlist outdent indent | link image | code',
+          images_upload_handler: this.init.images_upload_handler || null,
+          placeholder: this.placeholder,
+          skin_url: '/static/tinymce/skins/ui/oxide',
+          content_css: '/static/tinymce/skins/content/default/content.css',
+          setup: editor => {
+            editor.on('init', () => {
+              editor.setContent(this.value)
+            })
+            editor.on('change keyup', () => {
+              this.hasChange = true
+              this.$emit('input', editor.getContent())
+            })
+          }
+        }
+
+        tinymce.init(config).then(editors => {
+          this.editor = editors[0]
+        }).catch(error => {
+          console.error('TinyMCE init error:', error)
+          this.$message.error('富文本编辑器初始化失败')
+        })
+      } catch (error) {
+        console.error('Failed to load TinyMCE:', error)
+        this.$message.error('加载富文本编辑器失败')
+      }
+    }
+  }
+}
+</script>
+
+<style>
+.tox-tinymce {
+  border-radius: 4px;
+  border: 1px solid #dcdfe6 !important;
+}
+</style>

File diff suppressed because it is too large
+ 1039 - 1487
fhKeeper/formulahousekeeper/course-pc/src/views/coursemanagement/list.vue


+ 4 - 0
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/team/api.ts

@@ -16,3 +16,7 @@ export const DEACTIVEUSER = `/user/deactiveUser`
 export const SETACTIVE = `/user/setActive`
 export const EXPOERTUSER = `/user/exportUsers`
 export const URL_IMPORTUSER = `/user/importUser`
+export const GET_COMPANY_WEI_XIN = `/wechat-account/getCompanyWeiXin`
+export const SAVE_OR_UPDATE = `/wechat-account/saveOrUpdate`
+export const ONE_CLICK_GENERATION = `/wechat-account/oneClickGeneration`
+export const EXPORT_QR_CODE = `/wechat-account/export`

+ 214 - 26
fhKeeper/formulahousekeeper/customerBuler-crm/src/pages/team/index.vue

@@ -28,7 +28,7 @@
             <el-button :icon="Search" @click="getTableData()" />
           </template>
         </el-input>
-        
+
         <div class="formItem mr-6 flex items-center">
           <div class="text-nowrap">状态:</div>
           <el-select v-model="teamForm.status" placeholder="请选择" size="default" style="width: 100px"
@@ -43,8 +43,10 @@
             <el-option v-for="item in roleList" :key="item.id" :label="item.rolename" :value="item.id" />
           </el-select>
         </div>
-        <el-button type="primary" v-if="userInfo.userNameNeedTranslate == 1" @click="transitionOperation('exportUser', '')" v-permission="['teamExport']">导出人员</el-button>
-        <el-button type="primary" v-if="userInfo.userNameNeedTranslate == 1" @click="dialogFrom.newSyncWithCorpWxDayloadVisable = true">同步企微通讯录</el-button>
+        <el-button type="primary" v-if="userInfo.userNameNeedTranslate == 1"
+          @click="transitionOperation('exportUser', '')" v-permission="['teamExport']">导出人员</el-button>
+        <el-button type="primary" v-if="userInfo.userNameNeedTranslate == 1"
+          @click="dialogFrom.newSyncWithCorpWxDayloadVisable = true">同步企微通讯录</el-button>
         <el-dropdown v-if="userInfo.userNameNeedTranslate != 1">
           <el-button type="primary">
             更多操作<el-icon class="el-icon--right"><arrow-down /></el-icon>
@@ -52,8 +54,19 @@
           <template #dropdown>
             <el-dropdown-menu>
               <el-dropdown-item @click="addPersone(false)" v-permission="['teamAdd']">添加人员</el-dropdown-item>
-              <el-dropdown-item @click="transitionOperation('exportUser', '')" v-permission="['teamExport']">导出人员</el-dropdown-item>
-              <el-dropdown-item @click="transitionOperation('importUser', '')" v-permission="['teamImport']">批量导入</el-dropdown-item>
+              <el-dropdown-item @click="transitionOperation('exportUser', '')"
+                v-permission="['teamExport']">导出人员</el-dropdown-item>
+              <el-dropdown-item @click="transitionOperation('importUser', '')"
+                v-permission="['teamImport']">批量导入</el-dropdown-item>
+              <el-dropdown-item @click="officialAccountSetting()">
+                {{ officialAccountInformation.id ? '修改公众号配置' : '配置公众号' }}
+              </el-dropdown-item>
+              <el-dropdown-item @click="oneClickGenerationOfQrCode()" v-if="officialAccountInformation.id">
+                一键生成员工二维码
+              </el-dropdown-item>
+              <el-dropdown-item @click="exportQrCode()" v-if="officialAccountInformation.id">
+                导出员工二维码
+              </el-dropdown-item>
             </el-dropdown-menu>
           </template>
         </el-dropdown>
@@ -67,11 +80,17 @@
             <el-tree style="max-width: 600px" :data="deptList" :props="treeProps" @node-click="treeNode">
               <template #default="{ node, data }">
                 <div class="flex justify-between treeContent">
-                  <div class="custom-tree-node" @mouseleave="mouseleave(data,$event)" @mouseover="mouseover(data,$event)">
-                    <div class="treeLabel"><TextTranslation translationTypes="departmentName" :translationValue="node.label"></TextTranslation></div>
+                  <div class="custom-tree-node" @mouseleave="mouseleave(data, $event)"
+                    @mouseover="mouseover(data, $event)">
+                    <div class="treeLabel">
+                      <TextTranslation translationTypes="departmentName" :translationValue="node.label">
+                      </TextTranslation>
+                    </div>
                     <div class="treeIcon nodeEle" id="treeIcon" v-if="data.id > 0">
-                      <el-link type="primary" :icon="CirclePlus" :underline="false" @click.stop="dialogFromCli('addDeptDialogVisible', data, true)"></el-link>
-                      <el-link type="primary" :icon="Delete" :underline="false" style="margin-left: 6px;" @click.stop="deteleDept(data)"></el-link>
+                      <el-link type="primary" :icon="CirclePlus" :underline="false"
+                        @click.stop="dialogFromCli('addDeptDialogVisible', data, true)"></el-link>
+                      <el-link type="primary" :icon="Delete" :underline="false" style="margin-left: 6px;"
+                        @click.stop="deteleDept(data)"></el-link>
                     </div>
                   </div>
                 </div>
@@ -88,14 +107,26 @@
               <el-table-column type="selection" width="55" />
               <el-table-column label="姓名" property="name" width="150">
                 <template #default="scope">
-                  <TextTranslation translationTypes="userName" :translationValue="scope.row.name"></TextTranslation>
+                  <div class="flex items-center">
+                    <TextTranslation translationTypes="userName" :translationValue="scope.row.name"></TextTranslation>
+                    <template v-if="officialAccountInformation.id && scope.row.wxImgUrlWithTicket">
+                      <el-tooltip class="box-item" effect="dark" content="点击查看销售二维码" placement="top">
+                        <div class="ml-2 cursor-pointer" @click="viewQrCode(scope.row)">
+                          <el-icon color="#075985">
+                            <PictureFilled />
+                          </el-icon>
+                        </div>
+                      </el-tooltip>
+                    </template>
+                  </div>
                 </template>
               </el-table-column>
               <el-table-column label="手机" property="phone"></el-table-column>
               <el-table-column label="工号" property="jobNumber"></el-table-column>
               <el-table-column label="部门" property="departmentName">
                 <template #default="scope">
-                  <TextTranslation translationTypes="departmentName" :translationValue="scope.row.departmentName"></TextTranslation>
+                  <TextTranslation translationTypes="departmentName" :translationValue="scope.row.departmentName">
+                  </TextTranslation>
                 </template>
               </el-table-column>
               <el-table-column label="角色" property="roleName"></el-table-column>
@@ -159,7 +190,8 @@
         <el-form ref="deptRuleFormRef" style="max-width: 500px" :model="deptForm" :rules="deptRules" label-width="140px"
           size="large" status-icon>
           <el-form-item label="部门名称" prop="name">
-            <el-input v-model="deptForm.name" placeholder="请输入部门名称" clearable :disabled="userInfo.userNameNeedTranslate == 1" />
+            <el-input v-model="deptForm.name" placeholder="请输入部门名称" clearable
+              :disabled="userInfo.userNameNeedTranslate == 1" />
           </el-form-item>
           <el-form-item label="主要负责人">
             <!-- <el-select v-model="deptForm.managerId" placeholder="请选择" style="width: 100%" clearable>
@@ -171,7 +203,8 @@
             <!-- <el-select v-model="deptForm.otherManagerIds" placeholder="请选择" style="width: 100%" multiple clearable>
               <el-option v-for="item in userList" :key="item.id" :label="item.name" :value="item.id" />
             </el-select> -->
-            <personnel-search v-model="deptForm.otherManagerIds" :size="''" multiple placeholder="请选择"></personnel-search>
+            <personnel-search v-model="deptForm.otherManagerIds" :size="''" multiple
+              placeholder="请选择"></personnel-search>
           </el-form-item>
         </el-form>
       </div>
@@ -247,13 +280,15 @@
     </el-dialog>
 
     <!-- 同步企业微信通讯录 -->
-    <el-dialog v-model="dialogFrom.newSyncWithCorpWxDayloadVisable" width="600" :show-close="false" :before-close="handleClose">
+    <el-dialog v-model="dialogFrom.newSyncWithCorpWxDayloadVisable" width="600" :show-close="false"
+      :before-close="handleClose">
       <template #header="{ close, titleId, titleClass }">
         <div class="flex justify-between items-center border-b pb-3 dialog-header">
           <h4 :id="titleId">同步企微通讯录</h4>
           <div class="flex">
             <el-button @click="dialogFrom.newSyncWithCorpWxDayloadVisable = false">取消</el-button>
-            <el-button type="primary" :loading="loadingFrom.newSyncWithCorpWxDayloadLoading" @click="newSyncWithCorpWx()">
+            <el-button type="primary" :loading="loadingFrom.newSyncWithCorpWxDayloadLoading"
+              @click="newSyncWithCorpWx()">
               开始同步
             </el-button>
           </div>
@@ -264,6 +299,38 @@
       </div>
     </el-dialog>
 
+    <!-- 公众号配置 -->
+    <el-dialog v-model="dialogFrom.officialAccountSettingVisable" width="600" :show-close="false"
+      :before-close="handleClose" :close="closeOfficialAccountInformation(officialAccountInformationRef)">
+      <template #header="{ close, titleId, titleClass }">
+        <div class="flex justify-between items-center border-b pb-3 dialog-header">
+          <h4 :id="titleId">公众号配置</h4>
+          <div class="flex">
+            <el-button @click="dialogFrom.officialAccountSettingVisable = false">取消</el-button>
+            <el-button type="primary" :loading="loadingFrom.officialAccountSettingLoading"
+              @click="configureOfficialAccountInformation(officialAccountInformationRef)">
+              配置
+            </el-button>
+          </div>
+        </div>
+      </template>
+      <div class="pt-4 px-12 py-2">
+        <el-form ref="officialAccountInformationRef" style="max-width: 600px" :model="officialAccountInformationFrom"
+          status-icon :rules="officialAccountInformationFromRules" label-width="auto" class="demo-ruleForm">
+          <el-form-item label="名称:" prop="companyName">
+            <el-input v-model.trim="officialAccountInformationFrom.companyName" placeholder="请输入" />
+          </el-form-item>
+          <el-form-item label="appId:" prop="appId">
+            <el-input v-model.trim="officialAccountInformationFrom.appId" placeholder="请输入" />
+          </el-form-item>
+          <el-form-item label="appSecret:" prop="appSecret">
+            <el-input v-model.trim="officialAccountInformationFrom.appSecret" placeholder="请输入" />
+          </el-form-item>
+        </el-form>
+        <el-text class="mx-1" type="warning">请联系客服, 获取ip白名单参数, 配置后方可生效</el-text>
+      </div>
+    </el-dialog>
+
     <!-- 新增人员 -->
     <AddPersonnelModal :data="{
       addPersonnelDialogVisible: dialogFrom.addPersonnelDialogVisible,
@@ -275,17 +342,26 @@
     <!-- 批量操作 -->
     <BatchOperation :batchData="visibleData" :batchNode="batchTableData" :visibleText="allText.batchText"
       :popup="visibleType" :batchOperationVisible="dialogFrom.batchOperationVisible" @close="closeModal" />
+
+    <!-- 图片预览 -->
+    <el-image-viewer
+      v-if="showPreview"
+      :url-list="previewSrcList"
+      show-progress
+      :initial-index="0"
+      @close="showPreview = false"
+    />
   </div>
 </template>
 
 <script lang="ts" setup>
 import { ref, reactive, onMounted, onBeforeMount, inject } from 'vue';
-import { UploadRequestOptions, dayjs, ElLoading } from 'element-plus'
-import { Search, CirclePlusFilled, Edit, CirclePlus, Delete } from '@element-plus/icons-vue'
+import { UploadRequestOptions, dayjs, ElLoading, ElNotification } from 'element-plus'
+import { Search, CirclePlusFilled, Edit, CirclePlus, Delete, PictureFilled } from '@element-plus/icons-vue'
 import { FormInstance, FormRules, ElMessageBox } from 'element-plus'
 import { useStore } from '@/store/index'
-import { GET_DATA_LIST, DETELE_DEPT, MOD, GET_USERINFO, GET_ROUTELIST, DEACTIVEUSER, SETACTIVE, GET_DEPTLIST, BACTHSERROLE, GET_USERLIST, GET_ADDDEPT, ADD_USER, SETRESETPWD, EXPOERTUSER, EDIT_ADDDEPT } from './api'
-import { post, uploadFile } from "@/utils/request";
+import { GET_DATA_LIST, DETELE_DEPT, MOD, GET_USERINFO, GET_ROUTELIST, DEACTIVEUSER, SETACTIVE, GET_DEPTLIST, BACTHSERROLE, GET_USERLIST, GET_ADDDEPT, ADD_USER, SETRESETPWD, EXPOERTUSER, EDIT_ADDDEPT, GET_COMPANY_WEI_XIN, SAVE_OR_UPDATE, ONE_CLICK_GENERATION, EXPORT_QR_CODE } from './api'
+import { post, uploadFile, downloadFileRequest } from "@/utils/request";
 import { getFromValue, updateDepTreeData, resetFromValue, confirmAction, downloadFile } from '@/utils/tools'
 import { storeToRefs } from 'pinia';
 
@@ -310,6 +386,13 @@ interface deptRuleForm { // 部门表单类型
   otherManagerIds: string[] | number[],
 }
 
+interface officialAccountInformationInterface { // 公众号类型
+  id?: string | number,
+  companyName: string,
+  appId: string,
+  appSecret: string,
+}
+
 // 固定数据
 const stateOptions = [{ value: '3', label: '全部' }, { value: '1', label: '在职' }, { value: '0', label: '停用' }]
 
@@ -325,7 +408,8 @@ const loadingFrom = reactive({ // 所有加载状态
   resignationLoading: false,
   exportUserLoading: false,
   importLoading: false,
-  newSyncWithCorpWxDayloadLoading: false
+  newSyncWithCorpWxDayloadLoading: false,
+  officialAccountSettingLoading: false,
 })
 const dialogFrom: any = reactive({ // 所有弹窗状态
   addDeptDialogVisible: false,
@@ -334,7 +418,8 @@ const dialogFrom: any = reactive({ // 所有弹窗状态
   resignationVisible: false,
   exportUserVisible: false,
   importVisible: false,
-  newSyncWithCorpWxDayloadVisable: false
+  newSyncWithCorpWxDayloadVisable: false,
+  officialAccountSettingVisable: false,
 });
 const allText = reactive({
   batchText: '批量操作'
@@ -375,12 +460,61 @@ const treeProps = { // 部门树配置
   children: 'children',
   label: 'label',
 }
+const officialAccountInformation = ref<officialAccountInformationInterface>({
+  id: '',
+  companyName: '',
+  appId: '',
+  appSecret: '',
+})
+
+const officialAccountInformationFrom = ref<officialAccountInformationInterface>({
+  id: '',
+  companyName: '',
+  appId: '',
+  appSecret: '',
+})
+const officialAccountInformationRef = ref<FormInstance>()
+const showPreview = ref(false)
+const previewSrcList = ref<string[]>([])
 
 // 定义校验规则
 const deptRules = reactive<FormRules<typeof deptForm>>({ // 部门表单校验规则
   name: [{ required: true, trigger: 'blur', message: '请输入部门名称' }]
 })
 
+const officialAccountInformationFromRules = reactive<FormRules<typeof officialAccountInformation>>({
+  companyName: [{ required: true, trigger: 'blur', message: '请输入' }],
+  appId: [{ required: true, trigger: 'blur', message: '请输入' }],
+  appSecret: [{ required: true, trigger: 'blur', message: '请输入' }]
+})
+
+function viewQrCode(row: any) {
+  const { wxImgUrlWithTicket = '' } = row
+  previewSrcList.value = [wxImgUrlWithTicket]
+  showPreview.value = true
+}
+
+function exportQrCode() {
+  ElNotification({
+    title: '提示',
+    message: '二维码导出中...',
+    type: 'info',
+    showClose: false,
+    duration: 0
+  })
+  downloadFileRequest(EXPORT_QR_CODE, {}).then((blob) => {
+    const url = window.URL.createObjectURL(blob);
+    const link = document.createElement('a');
+    link.href = url;
+    link.setAttribute('download', '员工销售二维码.zip'); // 根据需要设定文件名
+    document.body.appendChild(link);
+    link.click();
+    link.remove();
+    window.URL.revokeObjectURL(url);
+    globalPopup?.showSuccess('下载中 请稍后')
+  })
+}
+
 // 同步企业微信通讯录
 function newSyncWithCorpWx() {
   const loading = ElLoading.service({
@@ -419,6 +553,58 @@ async function importUser(param: UploadRequestOptions) {
   globalPopup?.showError(res.msg || '')
 }
 
+// 获取公众号配置信息
+function getCompanyWeiXin() {
+  post(GET_COMPANY_WEI_XIN, {}).then((res) => {
+    const { companyName = '', id = '', appId = '', appSecret = '' } = res.data
+    officialAccountInformation.value = {
+      id, companyName, appId, appSecret
+    }
+  })
+}
+
+function oneClickGenerationOfQrCode() {
+  ElNotification({
+    title: '提示',
+    message: '生成二维码中...',
+    type: 'info',
+    showClose: false,
+    duration: 0
+  })
+  post(ONE_CLICK_GENERATION, {}).then(() => {
+    globalPopup?.showSuccess('生成成功')
+    getTableData()
+  })
+}
+
+async function configureOfficialAccountInformation(formEl: FormInstance | undefined) {
+  if (!formEl) return
+  await formEl.validate((valid) => {
+    if (valid) {
+      confirmAction('确定保存当前公众号的配置吗?', '公众号配置', 'warning').then(() => {
+        loadingFrom.officialAccountSettingLoading = true
+        post(SAVE_OR_UPDATE, getFromValue(officialAccountInformationFrom.value)).then(() => {
+          globalPopup?.showSuccess('配置成功')
+          closeModal('officialAccountSettingVisable')
+          getCompanyWeiXin()
+        }).finally(() => {
+          loadingFrom.officialAccountSettingLoading = false
+        })
+      })
+    }
+  })
+}
+
+function closeOfficialAccountInformation(formEl: FormInstance | undefined) {
+  if (!formEl) return
+  formEl.resetFields()
+}
+
+function officialAccountSetting() {
+  officialAccountInformationFrom.value = JSON.parse(JSON.stringify(officialAccountInformation.value))
+  dialogFrom.officialAccountSettingVisable = true
+}
+
 function exportUser() {
   loadingFrom.exportUserLoading = true
   post(EXPOERTUSER, { containInvalid: exportRadio.value }).then((res) => {
@@ -490,7 +676,7 @@ function addPersone(item: any) {
   post(GET_USERINFO, { userId: item.id }).then(res => {
     const { id, name, phone, jobNumber, roleId, departmentCascade, departmentId, inductionDate } = res.data
     let newData = {
-      id, name, phone, jobNumber, roleId, 
+      id, name, phone, jobNumber, roleId,
       // departmentId: departmentCascade && departmentCascade.split(',').map(Number).reverse(),
       departmentId: departmentId,
       inductionDate
@@ -511,7 +697,7 @@ async function personnelModalConfirm(data: any, modelType: string) {
     globalPopup?.showSuccess('添加成功')
     getTableData()
   }).catch(_err => {
-    dialogFrom[modelType] = false 
+    dialogFrom[modelType] = false
   })
 }
 
@@ -707,6 +893,7 @@ onMounted(() => {
   getUserList()
   getTableData()
   getDeptList()
+  getCompanyWeiXin()
 });
 </script>
 
@@ -751,6 +938,7 @@ onMounted(() => {
       align-items: center;
       justify-self: flex-end;
     }
+
     .nodeEle {
       display: none;
     }
@@ -763,7 +951,7 @@ onMounted(() => {
 </style>
 
 <style>
-  .temaClass .el-tree-node {
-    padding: 4px 0;
-  }
+.temaClass .el-tree-node {
+  padding: 4px 0;
+}
 </style>

+ 53 - 5
fhKeeper/formulahousekeeper/customerBuler-crm/src/utils/request.ts

@@ -30,17 +30,20 @@ instance.interceptors.request.use(
 instance.interceptors.response.use(
   (response: AxiosResponse) => {
     // 对响应数据进行处理
-    if (response.status !== 200 || response.data.code === 'error') {
-      ElNotification.closeAll()
+    if (response.status !== 200 || response.data.code === "error") {
+      ElNotification.closeAll();
       ElNotification({
-        message: response.status !== 200 ? showMessage(response.status) : response.data.msg,
+        message:
+          response.status !== 200
+            ? showMessage(response.status)
+            : response.data.msg,
         type: "error",
       });
     }
     return response;
   },
   (error: AxiosError) => {
-    ElNotification.closeAll()
+    ElNotification.closeAll();
     ElNotification({
       message: showMessage(error.request.status), // 传入响应码,匹配响应码对应信息,
       type: "error",
@@ -51,7 +54,11 @@ instance.interceptors.response.use(
 );
 
 // 封装GET请求
-export async function get(url: string, params?: any, file: boolean = false): Promise<any> {
+export async function get(
+  url: string,
+  params?: any,
+  file: boolean = false
+): Promise<any> {
   return new Promise((resolve, reject) => {
     instance
       .get(url, { params })
@@ -114,3 +121,44 @@ export async function uploadFile(url: string, data?: any): Promise<any> {
       });
   });
 }
+
+// 封装文件流请求(下载)
+export async function downloadFileRequest(url: string, params?: any): Promise<Blob> {
+  return new Promise((resolve, reject) => {
+    instance
+      .get(url, {
+        params,
+        responseType: "blob", // 设置响应类型为 blob 以处理文件流
+      })
+      .then((response: AxiosResponse<Blob>) => {
+        // 判断 blob 是否为错误信息(如后端返回错误内容也可能是 JSON)
+        const contentType = response.headers["content-type"];
+        if (contentType && contentType.includes("application/json")) {
+          const reader = new FileReader();
+          reader.onload = () => {
+            const result = reader.result as string;
+            try {
+              const json = JSON.parse(result);
+              ElNotification({
+                message: json.msg || "下载失败",
+                type: "error",
+              });
+              reject(json);
+            } catch (e) {
+              resolve(response.data);
+            }
+          };
+          reader.readAsText(response.data);
+        } else {
+          resolve(response.data);
+        }
+      })
+      .catch((error) => {
+        ElNotification({
+          message: showMessage(error.request.status),
+          type: "error",
+        });
+        reject(error);
+      });
+  });
+}

+ 2 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java

@@ -2016,7 +2016,7 @@ public class ReportController {
         Optional<LocalDate> min = dateList.stream().min(LocalDate::compareTo);
         //获取景昱的考勤和加班时长,进行比对,如果有不一致的,返回错误
         //错误信息格式:有加班工资的情况下,返回 异常:2024-04-18日填报工时(6h)不等于考勤总时长(8h);无加班工资情况下,返回 异常:2024-04-18日填报工时(8h)少于考勤工时(10h)
-        List<UserFvTime> userFvTimeList = userFvTimeMapper.selectList(new LambdaQueryWrapper<UserFvTime>().between(UserFvTime::getWorkDate, df.format(min.get()), df.format(max.get())));
+        List<UserFvTime> userFvTimeList = userFvTimeMapper.selectList(new LambdaQueryWrapper<UserFvTime>().between(UserFvTime::getWorkDate, df.format(min.get()), df.format(max.get())).eq(UserFvTime::getCompanyId, user.getCompanyId()).eq(UserFvTime::getUserId, userId));
         StringBuilder warningMsg = new StringBuilder();
         for (int i = 0; i < array.size(); i++) {
             JSONObject jsonObject = array.getJSONObject(i);
@@ -2030,7 +2030,7 @@ public class ReportController {
                 workingTime = jsonObject.getDouble("workingTime");
             }
 
-            Optional<UserFvTime> first = userFvTimeList.stream().filter(u -> u.getWorkDate().isEqual(date) && u.getUserId().equals(userId)).findFirst();
+            Optional<UserFvTime> first = userFvTimeList.stream().filter(u -> u.getWorkDate().isEqual(date)).findFirst();
             if(first.isPresent()){
                 if(first.get().getWorkHours()!=null){
                     Optional<User> optional = userList.stream().filter(u -> u.getId().equals(userId)).findFirst();

+ 160 - 14
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportLogController.java

@@ -21,6 +21,7 @@ import org.apache.poi.xssf.usermodel.XSSFRow;
 import org.apache.poi.xssf.usermodel.XSSFSheet;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.hamcrest.core.Is;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -34,10 +35,7 @@ import java.io.*;
 import java.lang.reflect.Array;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Optional;
+import java.util.*;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.stream.Collectors;
@@ -76,6 +74,10 @@ public class ReportLogController {
     private ReportLogDetailService reportLogDetailService;
     @Resource
     private WxCorpInfoService wxCorpInfoService;
+    @Autowired
+    private ReportLogMapper reportLogMapper;
+    @Autowired
+    private ReportLogDetailMapper reportLogDetailMapper;
 
     @RequestMapping("/get")
     public HttpRespMsg get(String creatorId, String createDate) {
@@ -86,16 +88,29 @@ public class ReportLogController {
         CompanyDingding dingding = companyDingdingMapper.selectOne(new LambdaQueryWrapper<CompanyDingding>().eq(CompanyDingding::getCompanyId, user.getCompanyId()));
         List<User> users = userMapper.selectList(new QueryWrapper<User>().eq("company_id",user.getCompanyId()));
         for (ReportLog reportLog : list) {
-            String operatorId = reportLog.getOperatorId();
-            for (User item : users) {
-                if (item.getId().equals(operatorId)){
-                    String name = item.getName();
-                    if (wxCorpInfo!=null && wxCorpInfo.getSaasSyncContact() == 1){
-                        reportLog.setMsg(reportLog.getMsg().replace(name,"$userName=" + item.getCorpwxUserid() + "$"));
-                    }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
-                        reportLog.setMsg(reportLog.getMsg().replace(name,"$userName=" + item.getDingdingUserid() + "$"));
+            if (reportLog.getMsg().endsWith("提交了日报")) {
+                String reportCreatorId = reportLog.getCreatorId();
+                for (User item : users) {
+                    if (item.getId().equals(reportCreatorId)){
+                        String name = item.getName();
+                        if (wxCorpInfo!=null && wxCorpInfo.getSaasSyncContact() == 1){
+                            reportLog.setMsg(reportLog.getMsg().replace(name,"$userName=" + item.getCorpwxUserid() + "$"));
+                        }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
+                            reportLog.setMsg(reportLog.getMsg().replace(name,"$userName=" + item.getDingdingUserid() + "$"));
+                        }
+                    }
+                }
+            } else {
+                String operatorId = reportLog.getOperatorId();
+                for (User item : users) {
+                    if (item.getId().equals(operatorId)){
+                        String name = item.getName();
+                        if (wxCorpInfo!=null && wxCorpInfo.getSaasSyncContact() == 1){
+                            reportLog.setMsg(reportLog.getMsg().replace(name,"$userName=" + item.getCorpwxUserid() + "$"));
+                        }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
+                            reportLog.setMsg(reportLog.getMsg().replace(name,"$userName=" + item.getDingdingUserid() + "$"));
+                        }
                     }
-
                 }
             }
         }
@@ -247,7 +262,7 @@ public class ReportLogController {
                         List<Integer> reportSubIds = Arrays.asList(split).stream().map(s -> Integer.valueOf(s)).collect(Collectors.toList());
                         reportIdList.addAll(reportSubIds);
                     });
-                    List<Report> reportList = reportMapper.selectList(new LambdaQueryWrapper<Report>().in(Report::getId, reportIdList));
+                    List<Report> reportList = reportMapper.selectList(new LambdaQueryWrapper<Report>().select(Report::getId, Report::getCreateTime, Report::getProjectAuditTime).in(Report::getId, reportIdList));
                     List<ReportLogDetail> allReportLogDetails = reportLogDetailService.list(new LambdaQueryWrapper<ReportLogDetail>().select(ReportLogDetail::getMsg,ReportLogDetail::getId).in(ReportLogDetail::getReportId, reportIdList));
                     StringBuilder sb=new StringBuilder();
                     List<Report> needUpdateReportList=new ArrayList<>();
@@ -402,5 +417,136 @@ public class ReportLogController {
         }
         return msg;
     }
+
+    @RequestMapping("/changeReportLogAuditor")
+    public HttpRespMsg changeReportLogAuditor(HttpServletRequest request){
+        HttpRespMsg msg = new HttpRespMsg();
+//        try {
+//            String auditorId = "8523550798754947072";
+//            List<ReportLog> reportLogs = reportLogMapper.selectList(new QueryWrapper<ReportLog>().eq("company_id", 7544).eq("operator_id", auditorId).like("msg", "审核通过了"));
+//
+//            List<ReportLog> updateReportLogs = new ArrayList<>();
+//            List<Integer> reportIdList = new ArrayList<>();
+//            for (ReportLog reportLog : reportLogs) {
+//                String reportIds = reportLog.getReportIds();
+//                List<Integer> ids = Arrays.asList(reportIds.split(",")).stream().map(s -> Integer.valueOf(s)).collect(Collectors.toList());
+//                //只去第一个项目
+//                Integer rid = ids.get(0);
+//                reportIdList.add(rid);
+//            }
+//            if (reportIdList.size() == 0) {
+//                msg.data = "没有数据";
+//                return msg;
+//            }
+//            List<Report> reports = reportMapper.selectList(new QueryWrapper<Report>().select("id, project_id").in("id", reportIdList));
+//            List<Integer> collect = reports.stream().map(Report::getProjectId).collect(Collectors.toList());
+//            List<Project> projects = projectMapper.selectList(new QueryWrapper<Project>().select("id, incharger_id").in("id", collect));
+//            List<User> allUsers = userMapper.selectList(new QueryWrapper<User>().select("id, name").eq("company_id", 7544));
+//            for (ReportLog reportLog : reportLogs) {
+//                String reportIds = reportLog.getReportIds();
+//                List<Integer> ids = Arrays.asList(reportIds.split(",")).stream().map(s -> Integer.valueOf(s)).collect(Collectors.toList());
+//                //只去第一个项目
+//                Integer rid = ids.get(0);
+//                Report report = reports.stream().filter(r -> r.getId().equals(rid)).findFirst().get();
+//                //查找project的inchargerId
+//                Project project = projects.stream().filter(p -> p.getId().equals(report.getProjectId())).findFirst().get();
+//                String inChargerId = project.getInchargerId();
+//                reportLog.setOperatorId(inChargerId);
+//                ReportLog item = new ReportLog();
+//                item.setId(reportLog.getId());
+//                item.setOperatorId(inChargerId);
+//                //woy9TkCAAAapCHjv-2GgW0l7xw4pKDtQ审核通过了日报
+//                allUsers.stream().filter(u -> u.getId().equals(inChargerId)).findFirst().ifPresent(u -> item.setMsg(u.getName()+"审核通过了日报"));
+//                System.out.println("更新》》" + reportLog.getId()+", inchargerId="+inChargerId+", msg="+item.getMsg());
+//                updateReportLogs.add(item);
+//            }
+//            if (updateReportLogs.size() > 0) {
+//                //更新
+//                System.out.println("更新了" + updateReportLogs.size() + "条数据");
+//                reportLogService.updateBatchById(updateReportLogs);
+//            }
+//            msg.setMsg("更新完成");
+//        } catch (Exception e) {
+//            e.printStackTrace();
+//            msg.setError(MessageUtils.message("other.error"));
+//            return msg;
+//        }
+        try {
+            String auditorId = "8523550798754947072";
+            List<ReportLogDetail> reportLogs = reportLogDetailMapper.selectList(new QueryWrapper<ReportLogDetail>().eq("company_id", 7544).eq("operator_id", auditorId).like("msg", "审核通过了"));
+
+            List<ReportLogDetail> updateReportLogs = new ArrayList<>();
+            List<Integer> reportIdList = new ArrayList<>();
+            for (ReportLogDetail reportLog : reportLogs) {
+                reportIdList.add(reportLog.getReportId());
+            }
+            if (reportIdList.size() == 0) {
+                msg.data = "没有数据";
+                return msg;
+            }
+            List<Report> reports = reportMapper.selectList(new QueryWrapper<Report>().select("id, project_id").in("id", reportIdList));
+            List<Integer> collect = reports.stream().map(Report::getProjectId).collect(Collectors.toList());
+            List<Project> projects = projectMapper.selectList(new QueryWrapper<Project>().select("id, incharger_id").in("id", collect));
+            List<User> allUsers = userMapper.selectList(new QueryWrapper<User>().select("id, name").eq("company_id", 7544));
+            for (ReportLogDetail reportLog : reportLogs) {
+                Integer rid = reportLog.getReportId();
+                Report report = reports.stream().filter(r -> r.getId().equals(rid)).findFirst().get();
+                //查找project的inchargerId
+                Project project = projects.stream().filter(p -> p.getId().equals(report.getProjectId())).findFirst().get();
+                String inChargerId = project.getInchargerId();
+                reportLog.setOperatorId(inChargerId);
+                ReportLogDetail item = new ReportLogDetail();
+                item.setId(reportLog.getId());
+                item.setOperatorId(inChargerId);
+                //woy9TkCAAAapCHjv-2GgW0l7xw4pKDtQ审核通过了日报
+                allUsers.stream().filter(u -> u.getId().equals(inChargerId)).findFirst().ifPresent(u -> item.setMsg(u.getName()+"审核通过了日报"));
+                System.out.println("更新》》" + reportLog.getId()+", inchargerId="+inChargerId+", msg="+item.getMsg());
+                updateReportLogs.add(item);
+            }
+            if (updateReportLogs.size() > 0) {
+                //更新
+                System.out.println("更新了" + updateReportLogs.size() + "条数据");
+                reportLogDetailService.updateBatchById(updateReportLogs);
+            }
+            msg.setMsg("更新完成");
+        } catch (Exception e) {
+            e.printStackTrace();
+            msg.setError(MessageUtils.message("other.error"));
+            return msg;
+        }
+        return msg;
+    }
+
+
+    @RequestMapping("/changeReportLogDetail")
+    public HttpRespMsg changeReportLogDetail(HttpServletRequest request){
+        HttpRespMsg msg = new HttpRespMsg();
+        String auditorId = "8523550798754947072";
+        Integer companyId = 7544;
+        List<ReportLogDetail> reportLogDetails = reportLogDetailMapper.getErrorData();
+        if (reportLogDetails.size() == 0) {
+            msg.data = "无数据需要更新";
+            return msg;
+        }
+        System.out.println("问题数据:" + reportLogDetails.size());
+        List<Integer> collect = reportLogDetails.stream().map(ReportLogDetail::getReportId).collect(Collectors.toList());
+        List<Report> reportList = reportMapper.selectList(new QueryWrapper<Report>().select("id, creator_id").in("id", collect));
+        List<ReportLogDetail> updateList = new ArrayList<>();
+        for (ReportLogDetail item : reportLogDetails) {
+            ReportLogDetail updateItem = new ReportLogDetail();
+            updateItem.setId(item.getId());
+            Optional<Report> first = reportList.stream().filter(r -> r.getId().equals(item.getReportId())).findFirst();
+            if (first.isPresent()) {
+                updateItem.setOperatorId(first.get().getCreatorId());
+                updateList.add(updateItem);
+            }
+        }
+        System.out.println("需更新:"+updateList.size());
+        if (updateList.size() > 0) {
+            reportLogDetailService.updateBatchById(updateList);
+        }
+
+        return msg;
+    }
 }
 

+ 6 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportLogDetailMapper.java

@@ -2,6 +2,9 @@ package com.management.platform.mapper;
 
 import com.management.platform.entity.ReportLogDetail;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
 
 /**
  * <p>
@@ -13,4 +16,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  */
 public interface ReportLogDetailMapper extends BaseMapper<ReportLogDetail> {
 
+    @Select("SELECT report_log_detail.* FROM report_log_detail LEFT JOIN report ON report_log_detail.report_id = report.`id` \n" +
+            "WHERE report_log_detail.`company_id` = 7544 AND report_log_detail.`msg` LIKE '%提交了%' AND operator_id <> report.`creator_id`;\n")
+    List<ReportLogDetail> getErrorData();
 }

+ 3 - 11
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java

@@ -1491,23 +1491,15 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 //去重
                 Optional<ReportLog> oldLog = addLogList.stream().filter(add -> add.getCreateDate().isEqual(log.getCreateDate()) && add.getCreatorId().equals(log.getCreatorId())).findFirst();
                 if (!oldLog.isPresent()) {
-                    log.setOperatorId(report.getFillUserid()==null?report.getCreatorId():report.getFillUserid());
+                    //2025-04-17修改:系统不再记录代填人,提交时的操作人就是日报归属人
+                    log.setOperatorId(report.getCreatorId());
                     Optional<User> first = userList.stream().filter(u -> u.getId().equals(log.getCreatorId())).findFirst();
                     String reportOwner = "";
                     if (first.isPresent()) {
                         reportOwner = first.get().getName();
                     }
+                    //日报提交记录
                     String msg = MessageUtils.message("profession.submit",reportOwner);
-//                    if (report.getFillUserid()==null) {
-//                        //自己填写自己的
-//                        //msg = reportOwner+"提交了日报";
-////                        msg = MessageUtils.message("profession.submit",reportOwner);
-//                    } else {
-//                        //代填的
-////                        first = userList.stream().filter(u -> u.getId().equals(log.getOperatorId())).findFirst();
-//                        //msg = first.get().getName()+"为" + reportOwner+"代填了日报";
-////                        msg = MessageUtils.message("profession.submitReplace",first.get().getName(),reportOwner);
-//                    }
                     log.setMsg(msg);
                     log.setCompanyId(companyId);
                     log.setReportIds(report.getId()+"");

+ 22 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/FinanceMonthlyWorktimeMapper.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.management.platform.mapper.FinanceMonthlyWorktimeMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.FinanceMonthlyWorktime">
+        <id column="id" property="id" />
+        <result column="company_id" property="companyId" />
+        <result column="ymonth" property="ymonth" />
+        <result column="status" property="status" />
+        <result column="is_send" property="isSend" />
+        <result column="last_send_time" property="lastSendTime" />
+        <result column="user_id" property="userId" />
+        <result column="timesheet_date" property="timesheetDate" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, company_id, ymonth, status, is_send, last_send_time, user_id, timesheet_date
+    </sql>
+
+</mapper>

+ 21 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/FmwDetailMapper.xml

@@ -2,7 +2,28 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
 <mapper namespace="com.management.platform.mapper.FmwDetailMapper">
+<!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.FmwDetail">
+        <id column="id" property="id" />
+        <result column="fmw_id" property="fmwId" />
+        <result column="dept_id" property="deptId" />
+        <result column="dept_name" property="deptName" />
+        <result column="dept_code" property="deptCode" />
+        <result column="project_id" property="projectId" />
+        <result column="project_code" property="projectCode" />
+        <result column="extra_field4" property="extraField4" />
+        <result column="extra_field5" property="extraField5" />
+        <result column="maintance_time" property="maintanceTime" />
+        <result column="debug_time" property="debugTime" />
+        <result column="waiting_time" property="waitingTime" />
+        <result column="assist_time" property="assistTime" />
+        <result column="public_time" property="publicTime" />
+    </resultMap>
 
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, fmw_id, dept_id, dept_name, dept_code, project_id, project_code, extra_field4, extra_field5, maintance_time, debug_time, waiting_time, assist_time, public_time
+    </sql>
     <select id="getTisTime" resultType="com.management.platform.entity.vo.TisTimeVO">
         select extra_field4 as orderId,extra_field5 as line
              ,sum(maintance_time+debug_time+waiting_time+assist_time+public_time) as workTime

+ 84 - 31
fhKeeper/formulahousekeeper/timesheet/src/components/taskComponent.vue

@@ -491,19 +491,37 @@
     <div slot="footer" class="dialog-footer foooot">
         <template v-if="!showMmeiLaiDe">
             <el-button v-if="(user.id == addForm.createrId || currentProject.inchargerId == user.id || currentProject.creatorId == user.id || permissions.projectManagement) && canAddTask" @click.native="deleteTask()" style="float:left;">{{ $t('btn.delete') }}</el-button>
+            <el-button type="primary" v-if="user.userNameNeedTranslate == 1 && !meetingId && !integrationTask.stage && addForm.executorListFront[0].executorId" @click="meetingCli()">{{ $t('faQiHuiYi') }}</el-button>
+            <el-button @click.native="closeBounceds()">{{ $t('btn.cancel') }}</el-button>
+            <el-button type="primary" @click="submitInsert" :loading="addLoading">{{ $t('btn.submit') }}</el-button>
         </template>
+        
         <template v-if="showMmeiLaiDe">
-            <el-button v-if="[0, 1, 2, 3, 5, 6].includes(showMmeiLaiDeData.taskStatus)" @click.native="deleteTask()" style="float:left;">{{ $t('btn.delete') }}</el-button>
-        </template>
-        <el-button type="primary" v-if="user.userNameNeedTranslate == 1 && !meetingId && !integrationTask.stage && addForm.executorListFront[0].executorId" @click="meetingCli()">{{ $t('faQiHuiYi') }}</el-button>
-        <el-button @click.native="closeBounceds()">{{ $t('btn.cancel') }}</el-button>
-        <template v-if="showMmeiLaiDe && addForm.id && millerSReviewer.includes(user.id) && [3, 4].includes(showMmeiLaiDeData.taskStatus) && showMmeiLaiDeData.leaderOrManager == 2">
-            <template v-if="(showMmeiLaiDeData.taskStatus == 3 && user.id == showMmeiLaiDeData.checkFirstId) || (showMmeiLaiDeData.taskStatus == 4 && user.id == showMmeiLaiDeData.checkSecondId)">
-                <el-button type="danger" @click="planOperation(false)" :loading="addLoading">驳回</el-button>
-                <el-button type="success" @click="planOperation(true)" :loading="addLoading">通过</el-button>
+            <el-button v-if="(user.id == showMmeiLaiDeData.checkFirstId && showMmeiLaiDeData.taskStatus == 3) || (user.id == showMmeiLaiDeData.checkSecondId && [4, '4', '1', 1].includes(showMmeiLaiDeData.taskStatus))" @click.native="deleteTask()" style="float:left;">{{ $t('btn.delete') }}</el-button>
+            <el-button v-if="showMmeiLaiDeData.taskPlan && showMmeiLaiDeData.taskStatus == 2" @click.native="deleteTask()" style="float:left;">删除</el-button>
+
+            <el-button @click.native="closeBounceds()">{{ $t('btn.cancel') }}</el-button>
+
+            <template v-if="(showMmeiLaiDeData.leaderIdList || []).includes(user.id) && showMmeiLaiDeData.taskStatus == 3">
+                <el-button @click.native="planToWithdraw()">撤回</el-button>
+            </template>
+
+            <template v-if="addForm.id && millerSReviewer.includes(user.id) && [3, 4].includes(showMmeiLaiDeData.taskStatus)">
+                <template v-if="(showMmeiLaiDeData.taskStatus == 3 && user.id == showMmeiLaiDeData.checkFirstId) || (showMmeiLaiDeData.taskStatus == 4 && user.id == showMmeiLaiDeData.checkSecondId)">
+                    <el-button type="danger" @click="planOperation(false)" :loading="addLoading">驳回</el-button>
+                    <el-button type="success" @click="planOperation(true)" :loading="addLoading">通过</el-button>
+                </template>
+            </template>
+            
+            <template>
+                <el-button type="primary" @click="submitInsert" :loading="addLoading">
+                    {{ showMmeiLaiDeData.taskPlan == 1 && showMmeiLaiDeData.taskStatus == 2 ? '重新提交' : '提交' }}
+                </el-button>
             </template>
         </template>
-        <el-button type="primary" @click="submitInsert" :loading="addLoading">{{ $t('btn.submit') }}</el-button>
+        
+        
+        
     </div>
     
     <!-- <div slot="title" v-if="addForm.parentTid != null || isRelationItem" >
@@ -1065,6 +1083,37 @@ export default {
     }
   },
   methods: {
+    planToWithdraw() {
+        this.$confirm(`确定撤回该计划吗?`, {
+            type: 'warning'
+        }).then(() => {
+            this.http.post('/task/withdrawalTaskPlan',{taskId: this.addForm.id},
+            res => {
+                if (res.code == "ok") {
+                    this.$message({
+                        message: '撤回成功',
+                        type: 'success'
+                    });
+                    let obj = {
+                        submitInsert: true,
+                        showOrNot: this.showOrNot
+                    }
+                    this.$emit('closeBounced', obj)
+                } else {
+                    this.$message({
+                    message: res.msg,
+                    type: "error"
+                    });
+                }
+            },
+            error => {
+                this.$message({
+                    message: error,
+                    type: "error"
+                });
+            });
+        })
+    },
     setDisabledPermission() {
         const { taskStatus } = this.showMmeiLaiDeData
         this.doYouWantToDisableAll = taskStatus == 3 || taskStatus == 4
@@ -1142,33 +1191,37 @@ export default {
         });
     },
     planOperation(type) {
-        this.http.post('/task/taskPlanPassOrReject', {
-            taskId: this.addForm.id, type
-        },
-        res => {
-            if (res.code == "ok") {
-                this.$message({
-                    message: '操作成功',
-                    type: "success"
-                });
-                let obj = {
-                    submitInsert: true,
-                    showOrNot: this.showOrNot
+        this.$confirm(`确定${type ? '通过' : '驳回'}该计划吗?`, {
+            type: 'warning'
+        }).then(() => {
+            this.http.post('/task/taskPlanPassOrReject', {
+                taskId: this.addForm.id, type
+            },
+            res => {
+                if (res.code == "ok") {
+                    this.$message({
+                        message: '操作成功',
+                        type: "success"
+                    });
+                    let obj = {
+                        submitInsert: true,
+                        showOrNot: this.showOrNot
+                    }
+                    this.$emit('closeBounced', obj)
+                } else {
+                    this.$message({
+                        message: res.msg,
+                        type: "error"
+                    });
                 }
-                this.$emit('closeBounced', obj)
-            } else {
+            },
+            error => {
                 this.$message({
-                    message: res.msg,
+                    message: error,
                     type: "error"
                 });
-            }
-        },
-        error => {
-            this.$message({
-                message: error,
-                type: "error"
             });
-        });
+        })
     },
     // 触发外层的会议
     meetingCli() {

+ 47 - 8
fhKeeper/formulahousekeeper/timesheet/src/views/project/gantt.vue

@@ -258,12 +258,24 @@ export default {
 
     //设置任务条样式
     gantt.templates.task_class = function (start, end, item) {
+      const { taskPlan, taskStatus } = item
+      if(taskStatus == 5 || taskStatus == 6) {
+        return "reject_line"
+      }
+      if(taskPlan == 1 && taskStatus == 0) {
+        return "success_line"
+      }
+      if(taskPlan == 1 && taskStatus == 2) {
+        return "draft_line"
+      }
       return item.taskPlanType == 3 ? "error_line" : "person_line"
     };
 
+    const that = this
     // 设置任务条内容显示
     gantt.templates.task_text = function (start, end, task) {
-      const { leaderOrManager, taskPlan, taskStatus } = task
+      const { leaderOrManager, taskPlan, taskStatus, checkFirstId, checkSecondId } = task
+      const userIds = that.user.id
       // 都审核通过
       if(taskPlan == 1 && taskStatus == 0 && task.text == '请假') {
         return `<div class="task_text">
@@ -294,12 +306,21 @@ export default {
       // 审核人
       // if (userInfo.projectLeaderType == 2) {
       if (leaderOrManager == 2) {
-        return `<div class="task_text statuss">
+        const oneRight = `<div class="exclamation-circle circle" style="color: ${task.taskPlanType == 3 ? '#f56c6c' : '#8ecaf8'}">V</div> <div class="circle"></div>`
+        const twoPairs = `<div class="exclamation-circle circle" style="color: ${task.taskPlanType == 3 ? '#f56c6c' : '#8ecaf8'}">V</div> <div class="circle exclamation-circle" style="color: ${task.taskPlanType == 3 ? '#f56c6c' : '#8ecaf8'}">V</div>`
+        const toExamine = `<div class="task_text statuss">
             ${task.taskStatus == 3 || task.taskStatus == 4 ? `<div class="circle">
               <div class="pendingReviewOfCornerMarkers">!</div>
-            </div>` : ''}
-            <div>${task.text}</div>
-          </div>`
+            </div>` : ''}`
+        const twoEmptyCircles = `<div class="circle"></div><div class="circle"></div>`
+        return `<div class="task_text">
+          ${(userIds == checkFirstId && task.taskStatus == 3) ? toExamine : (userIds == checkFirstId && task.taskStatus == 4) ? oneRight : ''}
+          ${(userIds == checkSecondId && task.taskStatus == 4) ? toExamine : (userIds == checkSecondId && task.taskStatus == 3) ? twoEmptyCircles : ''}
+          ${task.taskStatus == 5 ? `<div class="exclamation-circle circle" style="color: ${task.taskPlanType == 3 ? '#f56c6c' : '#8ecaf8'}">!</div> <div class="circle"></div>` : ''}
+          ${task.taskStatus == 6 ? `<div class="exclamation-circle circle" style="color: ${task.taskPlanType == 3 ? '#f56c6c' : '#8ecaf8'}">V</div> <div class="exclamation-circle circle" style="color: ${task.taskPlanType == 3 ? '#f56c6c' : '#8ecaf8'}">!</div>` : ''}
+          <div>${task.text}</div>
+          <div>${task.text}</div>
+        </div>`
       }
 
       // 正常人
@@ -336,7 +357,7 @@ export default {
     // 双击事件
     this.$refs.ganttContainer.addEventListener('dblclick', (event) => {
       const taskIdStr = gantt.locate(event);
-      if(userInfo.projectLeaderType != 1 && userInfo.projectLeaderType != 2) {
+      if(userInfo.projectLeaderType != 1 && userInfo.projectLeaderType != 2 && userInfo.projectLeaderType != 3) {
         return
       }
       this.showMmeiLaiDeData = {}
@@ -350,7 +371,7 @@ export default {
           this.handleEmptyClick({ taskId })
         }
       } else {
-        if(userInfo.projectLeaderType != 1) {
+        if(userInfo.projectLeaderType == 2) {
           return
         }
         // 获取 gantt_grid_data 容器
@@ -429,7 +450,25 @@ export default {
 .error_line {
   background: #ff5757;
   border: 1px solid;
-  border: #ff5757;
+  border-color: #ff5757;
+}
+
+.success_line {
+  background: #5cb87a;
+  border: 1px solid;
+  border-color: #5cb87a;
+}
+
+.draft_line {
+  background: #909399;
+  border: 1px solid;
+  border-color: #909399;
+}
+
+.reject_line {
+  background: #FFBD4D;
+  border: 1px solid;
+  border-color: #FFBD4D;
 }
 
 .task_text {

+ 1 - 0
fhKeeper/formulahousekeeper/timesheet/src/views/project/list.vue

@@ -5081,6 +5081,7 @@ a {
                 this.projectManagerEdit = false
                 this.contractAmountReasonShow = false
                 if(i == -1) {
+                    this.chosenMembCount = 0
                     if(this.user.companyId == '936') {
                         this.isShowProjectName = false
                     }

+ 19 - 10
fhKeeper/formulahousekeeper/timesheet/src/views/project/project_gantt.vue

@@ -2,19 +2,20 @@
   <div class="container">
     <div class="gantt_head">
       <div class="head_RorX">
-        
-      <el-radio-group v-model="radio1" @change="selChange()" size="small" style="margin-right:9px;width:280px">
-        <el-radio-button :label="$t('an-ren-yuan-cha-kan')" value="renyuan"></el-radio-button>
-        <el-radio-button :label="$t('an-xiang-mu-cha-kan')" value="xiangmu"></el-radio-button>
-        <el-radio-button :label="$t('zi-yuan-xu-qiu')" value="demand"></el-radio-button>
-      </el-radio-group>
-      
+        <div class="filter-lefts">
+          <el-radio-group v-model="radio1" @change="selChange()" size="small">
+            <el-radio-button :label="$t('an-ren-yuan-cha-kan')" value="renyuan"></el-radio-button>
+            <el-radio-button :label="$t('an-xiang-mu-cha-kan')" value="xiangmu"></el-radio-button>
+            <el-radio-button :label="$t('zi-yuan-xu-qiu')" value="demand"></el-radio-button>
+          </el-radio-group>
+          <el-checkbox v-if="radio1 != $t('zi-yuan-xu-qiu') && user.companyId == 876" v-model="justWaitForMe" style="margin-left: 14px;" @change="selChange()">待我审核</el-checkbox>
+        </div>
       </div>
       <!-- 时间段筛选  -->
       <div class="head_date" v-if="isDataLoaded">
       <span>{{ $t('message.period') }}</span>
       <el-date-picker
-        style="margin-left:9px;width:17vw"
+        style="margin-left:9px;width:15vw"
         size="small"
         v-model="valueDate"
         type="daterange"
@@ -220,6 +221,7 @@ export default {
       valuex2: '',
 
       user: JSON.parse(sessionStorage.getItem("user")),
+      justWaitForMe: false
     };
   },
   computed: {},
@@ -515,13 +517,16 @@ export default {
             getlistcs.userId = this.valuex
           }
         }
+        if(this.justWaitForMe) {
+          getlistcs.justWaitForMe = 1
+        }
         var urls = ''
         if(this.user.userNameNeedTranslate == '1') {
           urls = '/project/getGanttDataNew'
         } else {
           urls = '/project/getGanttData'
         }
-        this.http.post(urls, getlistcs ,
+        this.http.post(urls, getlistcs,
         // this.http.post('/project/getGanttDataNew', getlistcs,
                         res => {
                             if (res.code == "ok") {
@@ -638,6 +643,10 @@ export default {
     height: 100%;
     width: 100%;
   }
+  .filter-lefts {
+    display: flex;
+    align-items: center;
+  }
   .left-container {
     overflow: hidden;
     position: relative;
@@ -656,7 +665,7 @@ export default {
     align-items: center;
   }
   .gantt_head .head_RorX{
-    width: 280px;
+    width: 380px;
   }
   .gantt_head .head_date{
     width: 22vw;