浏览代码

美莱德FTE报表导出修改

QuYueTing 5 天之前
父节点
当前提交
6659c1b3a8
共有 32 个文件被更改,包括 4659 次插入146 次删除
  1. 6 0
      fhKeeper/formulahousekeeper/buildAll-mld.bat
  2. 4 4
      fhKeeper/formulahousekeeper/collectdata/src/main/java/com/management/collectdata/controller/DataCollectController.java
  3. 7 0
      fhKeeper/formulahousekeeper/copy_mld_deploy_files.bat
  4. 1 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/build.bat
  5. 183 0
      fhKeeper/formulahousekeeper/management-crm-qrcode/src/main/resources/application-prod.yml
  6. 105 0
      fhKeeper/formulahousekeeper/management-crm-qrcodeNew/src/main/resources/application-dev.yml
  7. 183 0
      fhKeeper/formulahousekeeper/management-crm-qrcodeNew/src/main/resources/application-prod.yml
  8. 2 2
      fhKeeper/formulahousekeeper/management-platform-mld/src/main/java/com/management/platform/mapper/ProjectMapper.java
  9. 71 33
      fhKeeper/formulahousekeeper/management-platform-mld/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java
  10. 109 3
      fhKeeper/formulahousekeeper/management-platform-mld/src/main/resources/mapper/ProjectMapper.xml
  11. 107 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/MaskWordController.java
  12. 3436 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java~
  13. 11 5
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/U8Controller.java
  14. 65 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/MaskWord.java
  15. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/MaskWordMapper.java
  16. 7 4
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ExcelParserService.java
  17. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/MaskWordService.java
  18. 1 3
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/LeaveSheetServiceImpl.java
  19. 20 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/MaskWordServiceImpl.java
  20. 2 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java
  21. 4 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java
  22. 20 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/MaskWordMapper.xml
  23. 40 70
      fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/service/impl/AttendanceStaffServiceImpl.java
  24. 12 0
      fhKeeper/formulahousekeeper/timesheet/src/views/project/list.vue
  25. 2 2
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/weeklyCustomization.vue
  26. 2 0
      fhKeeper/formulahousekeeper/timesheet_mld/src/permissions.js
  27. 1 1
      fhKeeper/formulahousekeeper/timesheet_mld/src/views/corpreport/list.vue
  28. 137 10
      fhKeeper/formulahousekeeper/webttkuaiban/src/main/resources/static/css/personal.css
  29. 二进制
      fhKeeper/formulahousekeeper/webttkuaiban/src/main/resources/static/image/my.jpg
  30. 二进制
      fhKeeper/formulahousekeeper/webttkuaiban/src/main/resources/static/image/record.jpg
  31. 二进制
      fhKeeper/formulahousekeeper/webttkuaiban/src/main/resources/static/image/statistic.jpg
  32. 89 5
      fhKeeper/formulahousekeeper/webttkuaiban/src/main/resources/static/personal.html

+ 6 - 0
fhKeeper/formulahousekeeper/buildAll-mld.bat

@@ -0,0 +1,6 @@
+cd timesheet_mld
+call build.bat
+cd ../timesheet_h5
+call build.bat
+cd ../management-platform-mld
+call build_package.bat

+ 4 - 4
fhKeeper/formulahousekeeper/collectdata/src/main/java/com/management/collectdata/controller/DataCollectController.java

@@ -480,7 +480,7 @@ public class DataCollectController {
 
     @RequestMapping("/getLeaveSheetDataSum")
     public String getLeaveSheetDataSum() {
-        String sqlCount = "select count(*) from att_holiday_leave_order where audit_status = 3 and is_delete = 0 and create_date >= DATE_SUB(CURDATE(), INTERVAL 14 DAY)";
+        String sqlCount = "select count(*) from att_holiday_leave_order where audit_status = 3 and is_delete = 0 and ahlo.total_day > 0 and create_date >= DATE_SUB(CURDATE(), INTERVAL 14 DAY)";
         int totalCount = 0;
         try (Connection connection = mysqlDataSource.getConnection()) {
             PreparedStatement countStmt = connection.prepareStatement(sqlCount);
@@ -504,7 +504,7 @@ public class DataCollectController {
                 " from att_holiday_leave_order ahlo " +
                 " left join emp_info eie on ahlo.emp_origin_id = eie.origin_id " +
                 " left join emp_info eic on ahlo.create_id = eic.origin_id " +
-                " where ahlo.audit_status = 3 and ahlo.is_delete = 0 and ahlo.create_date >= DATE_SUB(CURDATE(), INTERVAL 14 DAY) order by ahlo.id limit ?,? ";
+                " where ahlo.audit_status = 3 and ahlo.is_delete = 0 and ahlo.total_day > 0 and ahlo.create_date >= DATE_SUB(CURDATE(), INTERVAL 14 DAY) order by ahlo.id limit ?,? ";
         SimpleDateFormat sdfAll = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         SimpleDateFormat sdfYmd = new SimpleDateFormat("yyyy-MM-dd");
         List<LeaveSheet> resList = new ArrayList<>();
@@ -569,7 +569,7 @@ public class DataCollectController {
                 " from att_holiday_leave_order ahlo " +
                 " left join emp_info eie on ahlo.emp_origin_id = eie.origin_id " +
                 " left join emp_info eic on ahlo.create_id = eic.origin_id " +
-                " where ahlo.audit_status = 3 and ahlo.is_delete = 0 and ahlo.end_time is not null and ahlo.total_day > 0 and ahlo.total_day < 1 and date_format(ahlo.begin_date,'%Y-%m-%d') between ? and ?";
+                " where ahlo.audit_status = 3 and ahlo.is_delete = 0 and ahlo.total_day > 0 and date_format(ahlo.begin_date,'%Y-%m-%d') between ? and ?";
         if (empNo != null) {
             sqlQuery += "and ahlo.emp_no = ?";
         }
@@ -612,7 +612,7 @@ public class DataCollectController {
                     leaveSheet.setRemark(resultSet.getString("reason"));
                     leaveSheet.setOperatorId(resultSet.getString("operator_id"));
                     leaveSheet.setTimeHours(null==resultSet.getBigDecimal("total_hour")?null:resultSet.getBigDecimal("total_hour").setScale(1,RoundingMode.HALF_UP).floatValue());
-                    leaveSheet.setTimeDays(null==resultSet.getBigDecimal("total_day")?null:resultSet.getBigDecimal("total_day").setScale(0,RoundingMode.HALF_UP).floatValue());
+                    leaveSheet.setTimeDays(null==resultSet.getBigDecimal("total_day")?null:resultSet.getBigDecimal("total_day").setScale(3,RoundingMode.HALF_UP).floatValue());
                     leaveSheet.setIndate(LocalDateTime.now());
                     leaveSheet.setTimeType(0);
                     leaveSheet.setTel(resultSet.getString("mobile"));

+ 7 - 0
fhKeeper/formulahousekeeper/copy_mld_deploy_files.bat

@@ -0,0 +1,7 @@
+xcopy timesheet_mld\dist\* timesheet_deploy\static_pc\dist\ /y /e /i /q
+xcopy timesheet_h5\dist\* timesheet_deploy\static_h5\dist\ /y /e /i /q
+del management-platform-mld\target\timesheet-3.4.0.jar
+rename management-platform-mld\target\timesheet-mld-3.4.0.jar timesheet-3.4.0.jar
+xcopy management-platform-mld\target\timesheet-3.4.0.jar timesheet_deploy\ /y
+echo "all deployment files are ready!"
+pause

+ 1 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/build.bat

@@ -0,0 +1 @@
+npm run build:prod

+ 183 - 0
fhKeeper/formulahousekeeper/management-crm-qrcode/src/main/resources/application-prod.yml

@@ -0,0 +1,183 @@
+server:
+  port: 10111
+  tomcat:
+    uri-encoding: utf-8
+    max-http-form-post-size: -1
+    connection-timeout: 18000000s
+spring:
+  servlet:
+    multipart:
+      # 配置上传文件的大小设置
+
+      # Single file max size  即单个文件大小
+      max-file-size: 100MB
+      max-request-size: 100MB
+      location: /upload/
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://47.100.37.243:7644/man_crm?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
+    username: root
+    password: Ziyu20141026!@@
+
+    hikari:
+      maximum-pool-size: 60
+      minimum-idle: 10
+      max-lifetime: 180000
+      # 数据库连接超时时间,默认30秒,即30000
+      connection-timeout: 60000
+      connection-test-query: SELECT 1
+  #######redis配置######
+    # redis
+    redis:
+      host: 127.0.0.1
+      port: 6379
+      timeout: 3
+      # password:
+      pool:
+        minIdle: 1
+        maxIdle: 10
+        maxWait: 3
+        maxActive: 8
+  ####全局配置时间返回格式#####
+  jackson:
+    #参数意义:
+    #JsonInclude.Include.ALWAYS       默认
+    #JsonInclude.Include.NON_DEFAULT   属性为默认值不序列化
+    #JsonInclude.Include.NON_EMPTY     属性为 空(””) 或者为 NULL 都不序列化
+    #JsonInclude.Include.NON_NULL      属性为NULL  不序列化
+    default-property-inclusion: ALWAYS
+    time-zone: GMT+8
+    date-format: yyyy-MM-dd HH:mm:ss
+  messages:
+    basename: i18n.messages #配置国际化资源文件路径
+    encoding: UTF-8
+##########日志配置
+logging:
+  level:
+    root: info
+    org.mybatis: debug
+    java.sql: debug
+    org.springframework.web: trace
+    #打印sql语句
+    com.management.platform.mapper: debug
+  path: /log/
+##########
+mybatis-plus:
+#  mapper-locations: classpath:mapper/*/*.xml
+#  #实体扫描,多个package用逗号或者分号分隔
+#  typeAliasesPackage: com.hssx.cloudmodel
+  global-config:
+    #主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
+    id-type: 0
+    #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
+    field-strategy: 2
+    db-column-underline: true
+    refresh-mapper:
+#################插入和更新非null判断
+    db-config:
+      insert-strategy: not_null
+      update-strategy: not_null
+  configuration:
+    map-underscore-to-camel-case: true
+    cache-enabled: false
+######mybstis配置#######
+mybatis:
+  type-aliases-package: com.management.platform.entity
+  mapper-locations: mappers/*Mapper.xml
+#####配置图片上传路径####
+upload:
+  path: C:/upload/
+#  file: C:/file/
+picrecongnize:
+  browser: C:/picrecongnize/browser/
+  develop: C:/picrecongnize/develop/
+  im: C:/picrecongnize/im/
+  design: C:/picrecongnize/design/
+# 智能客户管家公众号参数
+wx:
+  template_report_fill: tty9TkCAAADWNCMiTzLogJoWVSMsJ6XQ
+  app_id: wx1c1d8fc81bc073a8
+  app_secret: 17ad07f90ee845f99f4c1605647ef755
+  template_report_pass: dbMuR2v7lxXLwRaorIWQ4T6ilvn0vzqmDDkD_3ZsaXc
+  template_project_deadline: kY2Qzec64uOANNXA0yn-PV09ZnNjCeGXwWjTaVmQiLU
+  template_report_reject: TICiRkvCpF4NCbkPOjefXTpz7jXgpt0SZGkNjCMIt3M
+  template_report_week: lhwkaW9BKwCvMtCuoAxLw4lZoGgMaucL0Ap0Vz-5KOY
+#钉钉参数配置
+dingding:
+  appId: 71020
+##actuator健康检查配置
+management:
+  security:
+    enabled:false:
+  server:
+    port: 10112
+#  endpoints:
+#    web:
+#      exposure:
+#        include: "*"
+
+  health:
+    redis:
+      enabled: false
+    ldap:
+      enabled: false
+
+referer:
+  refererDomain:
+    - localhost
+    - crm.ttkuaiban.com
+    - mobcrm.ttkuaiban.com
+    - 47.100.37.243
+    - 1.94.62.58
+excludeUrls: /wxcorp/*,/wxcorp/*/*,/dingding/*,/feishu-info/*,/error,/testClient,/corpWXAuth,/corpWXScanningAuth,/corpInsideWXAuth,/wx-corp-info/*,/clean/*,/innerRoles/*,/wechat/*
+
+#企业微信相关参数
+suitId: wwdd1137a65ce0fc87
+suitSecret: whAD0XKOmaJW4SwrIpu6BBwKAJbqOriHfPIKrvJ02Ic
+loginSuitId: ww19f9868980b9bb43
+#平台作为服务商的参数
+corpId: wwf11426cf618e1703
+token: aXLyq2JL1nRFFLhblHL2RX23SN46z4Q5
+encodingAesKey: 6W8MswvI1HEgITZ7Wu9ffIWvEbMakgDnaQkGW48dQ17
+providerSecret: wlwGIUXskWKsNtCfKUsAfJ6ueba55rZnqZvcC-rUM6nQ-LnRDyYgISQ2BO-UlL_A
+
+configEnv:
+  isDev: true
+  # 是否是私有化部署,企业内部应用
+  isPrivateDeploy: false
+
+privateDeployURL:
+  pcUrl: http://dev.huoshishanxin.com/#/
+  mobUrl: http://dev.huoshishanxin.com/#/
+
+# SFTP上传配置
+sftp:
+  isEnabled: false
+  server: 101.132.166.205
+  port: 22022
+  remoteDir: /bkup/crm
+  user: root
+  password: Huoshi@2022
+
+defaultcommonmodules:
+  path: [/customer,/contacts,/tasks]
+
+supersonic:
+  use: true
+  ip: localhost
+  port: 9080
+  username: admin
+  password: e6+jQ26AESREiBBuKM1u1A==
+
+aiask:
+  fileaskurl: http://123.60.32.117:5000/analyze
+  askurl: http://123.60.32.117:5000/analyzeOnlyQuestion
+
+  #临时的整型参数值
+weixin:
+  ticket:
+    action_name:
+     QR_SCENE: QR_SCENE
+     QR_STR_SCENE: QR_STR_SCENE
+     QR_LIMIT_SCENE: QR_LIMIT_SCENE
+     QR_LIMIT_STR_SCENE: QR_LIMIT_STR_SCENE

+ 105 - 0
fhKeeper/formulahousekeeper/management-crm-qrcodeNew/src/main/resources/application-dev.yml

@@ -0,0 +1,105 @@
+server:
+  port: 10010
+  tomcat:
+    uri-encoding: utf-8
+    max-http-form-post-size: -1
+    connection-timeout: 18000000s
+spring:
+  servlet:
+    multipart:
+      # 配置上传文件的大小设置
+      # Single file max size  即单个文件大小
+      max-file-size: 100MB
+      max-request-size: 100MB
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://127.0.0.1:17089/man_dev?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&&useSSL=false
+    username: root
+    password: P011430@Huoshi*
+    hikari:
+      maximum-pool-size: 10
+      minimum-idle: 3
+      max-lifetime: 30000
+      connection-test-query: SELECT 1
+    #######redis配置######
+    # redis
+    redis:
+      host: 127.0.0.1
+      port: 6479
+      timeout: 3
+      # password:
+      pool:
+        minIdle: 1
+        maxIdle: 10
+        maxWait: 3
+        maxActive: 8
+    ####全局配置时间返回格式#####
+  jackson:
+    #参数意义:
+    #JsonInclude.Include.ALWAYS       默认
+    #JsonInclude.Include.NON_DEFAULT   属性为默认值不序列化
+    #JsonInclude.Include.NON_EMPTY     属性为 空(””) 或者为 NULL 都不序列化
+    #JsonInclude.Include.NON_NULL      属性为NULL  不序列化
+    default-property-inclusion: ALWAYS
+    time-zone: GMT+8
+    date-format: yyyy-MM-dd HH:mm:ss
+
+##########日志配置
+logging:
+  level:
+    root: info
+    org.mybatis: error
+    java.sql: error
+    org.springframework.web: error
+    #打印sql语句
+    com.management.platform.mapper: error
+  path: /log/
+  file: worktime.log
+##########
+mybatis-plus:
+  #  mapper-locations: classpath:mapper/*/*.xml
+  #  #实体扫描,多个package用逗号或者分号分隔
+  #  typeAliasesPackage: com.hssx.cloudmodel
+  global-config:
+    #主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
+    id-type: 0
+    #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
+    field-strategy: 2
+    db-column-underline: true
+    refresh-mapper:
+    #################插入和更新非null判断
+    db-config:
+      insert-strategy: not_null
+      update-strategy: not_null
+  configuration:
+    map-underscore-to-camel-case: true
+    cache-enabled: false
+######mybstis配置#######
+mybatis:
+  type-aliases-package: com.management.platform.entity
+  mapper-locations: mappers/*Mapper.xml
+#####配置图片上传路径####
+upload:
+  path: /www/staticproject/timesheet-crm/upload/
+
+
+
+
+
+##actuator健康检查配置
+management:
+  security:
+    enabled:false:
+  server:
+    port: 10011
+  #  endpoints:
+  #    web:
+  #      exposure:
+  #        include: "*"
+
+  health:
+    redis:
+      enabled: false
+
+configEnv:
+  isDev: true

+ 183 - 0
fhKeeper/formulahousekeeper/management-crm-qrcodeNew/src/main/resources/application-prod.yml

@@ -0,0 +1,183 @@
+server:
+  port: 10111
+  tomcat:
+    uri-encoding: utf-8
+    max-http-form-post-size: -1
+    connection-timeout: 18000000s
+spring:
+  servlet:
+    multipart:
+      # 配置上传文件的大小设置
+
+      # Single file max size  即单个文件大小
+      max-file-size: 100MB
+      max-request-size: 100MB
+      location: /upload/
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://47.100.37.243:7644/man_crm?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
+    username: root
+    password: Ziyu20141026!@@
+
+    hikari:
+      maximum-pool-size: 60
+      minimum-idle: 10
+      max-lifetime: 180000
+      # 数据库连接超时时间,默认30秒,即30000
+      connection-timeout: 60000
+      connection-test-query: SELECT 1
+  #######redis配置######
+    # redis
+    redis:
+      host: 127.0.0.1
+      port: 6379
+      timeout: 3
+      # password:
+      pool:
+        minIdle: 1
+        maxIdle: 10
+        maxWait: 3
+        maxActive: 8
+  ####全局配置时间返回格式#####
+  jackson:
+    #参数意义:
+    #JsonInclude.Include.ALWAYS       默认
+    #JsonInclude.Include.NON_DEFAULT   属性为默认值不序列化
+    #JsonInclude.Include.NON_EMPTY     属性为 空(””) 或者为 NULL 都不序列化
+    #JsonInclude.Include.NON_NULL      属性为NULL  不序列化
+    default-property-inclusion: ALWAYS
+    time-zone: GMT+8
+    date-format: yyyy-MM-dd HH:mm:ss
+  messages:
+    basename: i18n.messages #配置国际化资源文件路径
+    encoding: UTF-8
+##########日志配置
+logging:
+  level:
+    root: info
+    org.mybatis: debug
+    java.sql: debug
+    org.springframework.web: trace
+    #打印sql语句
+    com.management.platform.mapper: debug
+  path: /log/
+##########
+mybatis-plus:
+#  mapper-locations: classpath:mapper/*/*.xml
+#  #实体扫描,多个package用逗号或者分号分隔
+#  typeAliasesPackage: com.hssx.cloudmodel
+  global-config:
+    #主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
+    id-type: 0
+    #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
+    field-strategy: 2
+    db-column-underline: true
+    refresh-mapper:
+#################插入和更新非null判断
+    db-config:
+      insert-strategy: not_null
+      update-strategy: not_null
+  configuration:
+    map-underscore-to-camel-case: true
+    cache-enabled: false
+######mybstis配置#######
+mybatis:
+  type-aliases-package: com.management.platform.entity
+  mapper-locations: mappers/*Mapper.xml
+#####配置图片上传路径####
+upload:
+  path: C:/upload/
+#  file: C:/file/
+picrecongnize:
+  browser: C:/picrecongnize/browser/
+  develop: C:/picrecongnize/develop/
+  im: C:/picrecongnize/im/
+  design: C:/picrecongnize/design/
+# 智能客户管家公众号参数
+wx:
+  template_report_fill: tty9TkCAAADWNCMiTzLogJoWVSMsJ6XQ
+  app_id: wx1c1d8fc81bc073a8
+  app_secret: 17ad07f90ee845f99f4c1605647ef755
+  template_report_pass: dbMuR2v7lxXLwRaorIWQ4T6ilvn0vzqmDDkD_3ZsaXc
+  template_project_deadline: kY2Qzec64uOANNXA0yn-PV09ZnNjCeGXwWjTaVmQiLU
+  template_report_reject: TICiRkvCpF4NCbkPOjefXTpz7jXgpt0SZGkNjCMIt3M
+  template_report_week: lhwkaW9BKwCvMtCuoAxLw4lZoGgMaucL0Ap0Vz-5KOY
+#钉钉参数配置
+dingding:
+  appId: 71020
+##actuator健康检查配置
+management:
+  security:
+    enabled:false:
+  server:
+    port: 10112
+#  endpoints:
+#    web:
+#      exposure:
+#        include: "*"
+
+  health:
+    redis:
+      enabled: false
+    ldap:
+      enabled: false
+
+referer:
+  refererDomain:
+    - localhost
+    - crm.ttkuaiban.com
+    - mobcrm.ttkuaiban.com
+    - 47.100.37.243
+    - 1.94.62.58
+excludeUrls: /wxcorp/*,/wxcorp/*/*,/dingding/*,/feishu-info/*,/error,/testClient,/corpWXAuth,/corpWXScanningAuth,/corpInsideWXAuth,/wx-corp-info/*,/clean/*,/innerRoles/*,/wechat/*
+
+#企业微信相关参数
+suitId: wwdd1137a65ce0fc87
+suitSecret: whAD0XKOmaJW4SwrIpu6BBwKAJbqOriHfPIKrvJ02Ic
+loginSuitId: ww19f9868980b9bb43
+#平台作为服务商的参数
+corpId: wwf11426cf618e1703
+token: aXLyq2JL1nRFFLhblHL2RX23SN46z4Q5
+encodingAesKey: 6W8MswvI1HEgITZ7Wu9ffIWvEbMakgDnaQkGW48dQ17
+providerSecret: wlwGIUXskWKsNtCfKUsAfJ6ueba55rZnqZvcC-rUM6nQ-LnRDyYgISQ2BO-UlL_A
+
+configEnv:
+  isDev: true
+  # 是否是私有化部署,企业内部应用
+  isPrivateDeploy: false
+
+privateDeployURL:
+  pcUrl: http://dev.huoshishanxin.com/#/
+  mobUrl: http://dev.huoshishanxin.com/#/
+
+# SFTP上传配置
+sftp:
+  isEnabled: false
+  server: 101.132.166.205
+  port: 22022
+  remoteDir: /bkup/crm
+  user: root
+  password: Huoshi@2022
+
+defaultcommonmodules:
+  path: [/customer,/contacts,/tasks]
+
+supersonic:
+  use: true
+  ip: localhost
+  port: 9080
+  username: admin
+  password: e6+jQ26AESREiBBuKM1u1A==
+
+aiask:
+  fileaskurl: http://123.60.32.117:5000/analyze
+  askurl: http://123.60.32.117:5000/analyzeOnlyQuestion
+
+  #临时的整型参数值
+weixin:
+  ticket:
+    action_name:
+     QR_SCENE: QR_SCENE
+     QR_STR_SCENE: QR_STR_SCENE
+     QR_LIMIT_SCENE: QR_LIMIT_SCENE
+     QR_LIMIT_STR_SCENE: QR_LIMIT_STR_SCENE

+ 2 - 2
fhKeeper/formulahousekeeper/management-platform-mld/src/main/java/com/management/platform/mapper/ProjectMapper.java

@@ -188,8 +188,8 @@ public interface ProjectMapper extends BaseMapper<Project> {
 
     List<Project> selectNearProject(String userId);
 
-    List<Map<String, Object>> getFTEData(Integer companyId, String startDate, String endDate, Integer start, Integer size, String area,String userId,String sortProp, Integer sortOrder, List<Integer> branchDepartment, List<Integer> deptIds,Integer departmentId);
-
+    List<Map<String, Object>> getFTEData(Integer companyId, String startDate, String endDate, Integer start, Integer size, String area,String userId,String sortProp, Integer sortOrder, List<Integer> branchDepartment, List<Integer> deptIds,Integer departmentId, boolean onlyInchargeProject,String leaderId);
+    Integer getFTEDataCount(Integer companyId, String startDate, String endDate, Integer start, Integer size, String area,String userId,String sortProp, Integer sortOrder, List<Integer> branchDepartment, List<Integer> deptIds,Integer departmentId, boolean onlyInchargeProject,String leaderId);
     List<Map<String, Object>> getTimeCostByToken(Integer companyId, String startDate, String endDate);
 
     List<UserCateTimeVo> getMembProjectCateTime(Integer companyId, String startDate, String endDate,Integer departmentId,String subUserCustomName,Integer plateString);

+ 71 - 33
fhKeeper/formulahousekeeper/management-platform-mld/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java

@@ -13537,10 +13537,13 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
         User targetUser = userMapper.selectById(request.getHeader("token"));
         List<SysRichFunction> functionAllList = sysFunctionMapper.getRoleFunctions(targetUser.getRoleId(), "全部部门FTE报表");
         List<SysRichFunction> functionDeptList = sysFunctionMapper.getRoleFunctions(targetUser.getRoleId(), "负责部门FTE报表");
+        List<SysRichFunction> functionProjectList = sysFunctionMapper.getRoleFunctions(targetUser.getRoleId(), "负责项目FTE报表");
+
         List<Integer> deptIds=null;
         List<Department> allDepartmentList=departmentMapper.selectList(new QueryWrapper<Department>().eq("company_id",targetUser.getCompanyId()));
         List<Department> userDepartmentList = departmentMapper.selectList(new QueryWrapper<Department>().eq("manager_id", targetUser.getId()).eq("company_id",targetUser.getCompanyId()));
         List<DepartmentOtherManager> departmentOtherManagerList = departmentOtherManagerMapper.selectList(new QueryWrapper<DepartmentOtherManager>().eq("other_manager_id", targetUser.getId()));
+        boolean onlyInchargeProject = false;
         //判断查看权限
         if(functionAllList.size()==0){
             deptIds=new ArrayList<>();
@@ -13554,9 +13557,14 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                     List<Integer> branchDepartment = getBranchDepartment(integer, allDepartmentList);
                     deptIds.addAll(branchDepartment);
                 }
+            } else if (functionProjectList.size() > 0){
+                //无任何权限,按项目经理的权限查看
+                onlyInchargeProject = true;
+                deptIds = null;
+                System.out.println("无任何权限,按项目经理的权限查看");
             }
         }
-        if(departmentId!=null){
+        if(departmentId!=null && !onlyInchargeProject){
             deptIds=new ArrayList<>();
             List<Integer> branchDepartment = getBranchDepartment(departmentId, allDepartmentList);
             deptIds.addAll(branchDepartment);
@@ -13575,10 +13583,11 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
         if(pageIndex!=null&&pageSize!=null){
             Integer size=pageSize;
             Integer start=(pageIndex-1)*size;
-            resultList=projectMapper.getFTEData(targetUser.getCompanyId(),startDate,endDate,start,size,area,userId,sortProp,sortOrder,null,deptIds,departmentId);
-            total=projectMapper.getFTEData(targetUser.getCompanyId(),startDate,endDate,null,null,area,userId,sortProp,sortOrder,null,deptIds,departmentId).size();
+            System.out.println("开始查询数据======"+start);
+            resultList=projectMapper.getFTEData(targetUser.getCompanyId(),startDate,endDate,start,size,area,userId,sortProp,sortOrder,null,deptIds,departmentId, onlyInchargeProject, targetUser.getId());
+            total=projectMapper.getFTEDataCount(targetUser.getCompanyId(),startDate,endDate,null,null,area,userId,sortProp,sortOrder,null,deptIds,departmentId, onlyInchargeProject, targetUser.getId());
         }else{
-            resultList=projectMapper.getFTEData(targetUser.getCompanyId(),startDate,endDate,null,null,area,userId,sortProp,sortOrder,null,deptIds,departmentId);
+            resultList=projectMapper.getFTEData(targetUser.getCompanyId(),startDate,endDate,null,null,area,userId,sortProp,sortOrder,null,deptIds,departmentId, onlyInchargeProject, targetUser.getId());
         }
         //固定月工时数 163.125
         double regularMonthTime=163.125;
@@ -13760,12 +13769,19 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
         titleList.add("审核状态");
         dataList.add(titleList);
 
+        String lastUserId = null;
+        int lastUserDays = 0;
+        String lastUserName = null;
+        String lastUserDeptName = null;
+        String lastUserPosition = null;
+        double planHoursSum = 0.0;
         for (int i = 0; i < list.size(); i++) {
             List<String> item=new ArrayList<>();
             item.add((i+1)+"");
             Map<String, Object> map = list.get(i);
-            Integer taskId= (Integer) map.get("id");
+            Integer taskId = (Integer) map.get("id");
             String userIdStr= (String) map.get("userId");
+
             Integer departmentIdStr= Math.toIntExact((Long) map.get("departmentId"));
             Optional<Department> deptFirst = allDepartmentList.stream().filter(f -> f.getDepartmentId().equals(departmentIdStr)).findFirst();
             String dateStr=map.get("startDate")+"-"+map.get("endDate");
@@ -13773,37 +13789,15 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
             double planHours = (double)map.get("planHours");
             int dayCount = (int)(planHours/7.5) + (planHours%7.5>0?1:0);
             item.add(dayCount+"天");//天数
-
             //所属部门
-            if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
-                if(!deptFirst.isPresent()){
-                    item.add("未分配");
-                }else {
-                    item.add(departmentService.exportWxDepartment(deptFirst.get(),allDepartmentList));
-                }
-            }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
-                if(!deptFirst.isPresent()){
-                    item.add("未分配");
-                }else {
-                    item.add(departmentService.exportDdDepartment(deptFirst.get(),allDepartmentList));
-                }
-            }else {
-                item.add(departmentService.getSupDepartment(deptFirst.get(),allDepartmentList));
-            }
-
+            String departmentName = departmentService.getSupDepartment(deptFirst.get(),allDepartmentList);
+            item.add(departmentName);
             //岗位
-            item.add(list.get(i).get("roleName")==null?"":list.get(i).get("roleName").toString());
+            String position = list.get(i).get("roleName")==null?"":list.get(i).get("roleName").toString();
+            item.add(position);
             //姓名
-            if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
-                item.add((String)("$userName="+map.get("corpwxUserId")+"$"));
-
-            }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
-                item.add((String)("$userName="+map.get("userName")+"$"));
-
-            }else {
-                item.add((String)map.get("userName"));
-            }
-
+            String userName = (String)map.get("userName");
+            item.add(userName);
             Integer taskPlanType= (Integer) map.get("taskPlanType");
             Optional<TaskType> typeOptional = taskTypeList.stream().filter(t -> t.getId().equals(taskPlanType)).findFirst();
             item.add(typeOptional.isPresent()?typeOptional.get().getName():""); //工作计划类型
@@ -13891,6 +13885,50 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                 }
             }
             dataList.add(item);
+            if (i == 0 || !userIdStr.equals(lastUserId)) {
+                //重置数据
+                lastUserId = userIdStr;
+                lastUserDays = dayCount;
+                planHoursSum = planHours;
+                lastUserName = userName;
+                lastUserDeptName = departmentName;
+                lastUserPosition = position;
+            } else {
+                //累加
+                lastUserDays += dayCount;
+                planHoursSum += planHours;
+            }
+            //判断是否要加上合计的数据
+            if (i == list.size() - 1 || !((String)list.get(i + 1).get("userId")).equals(userIdStr)) {
+                List<String> itemSum = new ArrayList<>();
+                itemSum.add("合计");
+                itemSum.add("");
+                itemSum.add(lastUserDays + "天");
+                itemSum.add(lastUserDeptName);
+                itemSum.add(lastUserPosition);
+                itemSum.add(lastUserName);
+                itemSum.add("");
+                itemSum.add("");
+                itemSum.add("");
+                itemSum.add("");
+                itemSum.add("");
+                itemSum.add("");
+                itemSum.add("");
+                itemSum.add("");
+                itemSum.add("");
+                itemSum.add("");
+                itemSum.add("");
+                itemSum.add(new BigDecimal(planHoursSum).setScale(2,BigDecimal.ROUND_HALF_UP).toString());
+                itemSum.add(new BigDecimal(planHoursSum)
+                        .divide(
+                                BigDecimal.valueOf(monthTime),
+                                2,                  // 保留2位小数
+                                RoundingMode.HALF_UP // 四舍五入
+                        )
+                        .toString());
+                itemSum.add("");
+                dataList.add(itemSum);
+            }
         }
         String fileName = "FTE计划报表"+System.currentTimeMillis();
         try {

+ 109 - 3
fhKeeper/formulahousekeeper/management-platform-mld/src/main/resources/mapper/ProjectMapper.xml

@@ -1766,6 +1766,9 @@
         ON user.id = report.creator_id
         WHERE report.state = 1
         AND p.is_public = 0
+          <if test="onlyInchargeProject" >
+            AND p.`incharger_id`=#{leaderId}
+          </if>
         AND report.company_id=#{companyId}
         AND
         (
@@ -1793,6 +1796,9 @@
         ON user.id = report.creator_id
         WHERE report.state = 1
         AND p.is_public = 1
+        <if test="onlyInchargeProject" >
+            AND p.`incharger_id`=#{leaderId}
+        </if>
         AND report.company_id=#{companyId}
         AND
         (
@@ -1813,6 +1819,9 @@
         ON u.id = t.creator_id
         WHERE u.company_id=#{companyId}
         AND (workTime>0 OR u.`is_active`=1)
+        <if test="onlyInchargeProject" >
+            AND r.projectName is not null
+        </if>
         <if test="area!=null and area != '' ">
             and u.plate1 = #{area}
         </if>
@@ -1831,9 +1840,6 @@
                 #{item}
             </foreach>
         </if>
-<!--        <if test="departmentId!=null">-->
-<!--            and u.department_id=#{departmentId}-->
-<!--        </if>-->
         <if test="sortProp!=null and sortProp!=''">
             <choose>
                 <when test="sortOrder==0">
@@ -1848,6 +1854,106 @@
             limit #{start},#{size}
         </if>
     </select>
+
+    <select id="getFTEDataCount" resultType="java.lang.Integer">
+        SELECT count(1) as total
+        FROM `user` u
+        LEFT JOIN department d on u.department_id=d.department_id
+        LEFT JOIN (
+        SELECT p.project_name projectName,p.project_code projectCode,report.working_time,report.creator_id,SUM(working_time) workTime
+        FROM report
+        LEFT JOIN project p
+        ON report.project_id = p.id
+        LEFT JOIN `user`
+        ON user.id = report.creator_id
+        WHERE report.state = 1
+        AND p.is_public = 0
+        <if test="onlyInchargeProject" >
+            AND p.`incharger_id`=#{leaderId}
+        </if>
+        AND report.company_id=#{companyId}
+        AND
+        (
+        IF(user.induction_date is not null AND user.induction_date &gt; #{startDate} and user.inactive_date is not null AND user.`is_active`=0 AND user.inactive_date &lt; #{endDate}
+        ,report.create_date BETWEEN user.induction_date AND user.inactive_date
+        ,report.create_date BETWEEN #{startDate} AND #{endDate} )
+        and
+        IF(user.induction_date is not null AND user.induction_date &gt; #{startDate}
+        ,report.create_date BETWEEN user.induction_date AND #{endDate}
+        ,report.create_date BETWEEN #{startDate} AND #{endDate} )
+        and
+        IF(user.inactive_date is not null AND user.`is_active`=0 AND user.inactive_date &lt; #{endDate}
+        ,report.create_date BETWEEN #{startDate} AND user.inactive_date
+        ,report.create_date BETWEEN #{startDate} AND #{endDate} )
+        )
+        GROUP BY user.id,p.id,user.plate1,p.project_name,p.project_code
+        ) r
+        ON u.id = r.creator_id
+        LEFT JOIN (
+        SELECT report.creator_id,SUM(working_time) nonProjectWorkingTime
+        FROM report
+        LEFT JOIN project p
+        ON report.project_id = p.id
+        LEFT JOIN `user`
+        ON user.id = report.creator_id
+        WHERE report.state = 1
+        AND p.is_public = 1
+        <if test="onlyInchargeProject" >
+            AND p.`incharger_id`=#{leaderId}
+        </if>
+        AND report.company_id=#{companyId}
+        AND
+        (
+        IF(user.induction_date is not null AND user.induction_date &gt; #{startDate} and user.inactive_date is not null AND user.`is_active`=0 AND user.inactive_date &lt; #{endDate}
+        ,report.create_date BETWEEN user.induction_date AND user.inactive_date
+        ,report.create_date BETWEEN #{startDate} AND #{endDate} )
+        and
+        IF(user.induction_date is not null AND user.induction_date &gt; #{startDate}
+        ,report.create_date BETWEEN user.induction_date AND #{endDate}
+        ,report.create_date BETWEEN #{startDate} AND #{endDate} )
+        and
+        IF(user.inactive_date is not null AND user.`is_active`=0 AND user.inactive_date &lt; #{endDate}
+        ,report.create_date BETWEEN #{startDate} AND user.inactive_date
+        ,report.create_date BETWEEN #{startDate} AND #{endDate} )
+        )
+        GROUP BY user.id
+        ) t
+        ON u.id = t.creator_id
+        WHERE u.company_id=#{companyId}
+        AND (workTime>0 OR u.`is_active`=1)
+        <if test="onlyInchargeProject" >
+            AND r.projectName is not null
+        </if>
+        <if test="area!=null and area != '' ">
+            and u.plate1 = #{area}
+        </if>
+        <if test="userId!=null and userId != '' ">
+            and u.id = #{userId}
+        </if>
+        <if test="branchDepartment!=null and branchDepartment.size()>0">
+            and u.department_id in
+            <foreach collection="branchDepartment" open="(" close=")" separator="," item="item">
+                #{item}
+            </foreach>
+        </if>
+        <if test="deptIds!=null and deptIds.size()>0">
+            and u.department_id in
+            <foreach collection="deptIds" open="(" item="item" close=")" separator=",">
+                #{item}
+            </foreach>
+        </if>
+        <if test="sortProp!=null and sortProp!=''">
+            <choose>
+                <when test="sortOrder==0">
+                    order by id desc
+                </when>
+                <otherwise>
+                    order by id
+                </otherwise>
+            </choose>
+        </if>
+    </select>
+
     <!--获取查询者所在公司每个项目的工时成本-->
     <select id="getTimeCostByToken" resultType="java.util.Map">
         SELECT a.id, a.project_code as projectCode, a.project_name AS project, SUM(b.working_time) AS cost, SUM(b.cost) AS costMoney,a.category_name as categoryName,c.job_number as userJobNumber,d.department_name as departmentName

+ 107 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/MaskWordController.java

@@ -0,0 +1,107 @@
+package com.management.platform.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.management.platform.entity.Department;
+import com.management.platform.entity.MaskWord;
+import com.management.platform.mapper.DepartmentMapper;
+import com.management.platform.mapper.UserMapper;
+import com.management.platform.service.MaskWordService;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * <p>
+ *  前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-09-10
+ */
+@RestController
+@RequestMapping("/mask-word")
+public class MaskWordController {
+
+    @Resource
+    private MaskWordService maskWordService;
+    @Resource
+    private UserMapper userMapper;
+    @Resource
+    private HttpServletRequest request;
+    @Resource
+    private DepartmentMapper departmentMapper;
+
+    @RequestMapping("/maskWordSetting")
+    public HttpRespMsg maskWordSetting(MaskWord maskWord){
+        HttpRespMsg msg = new HttpRespMsg();
+        Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
+        int count;
+        if(maskWord.getId() == null){
+            count = maskWordService.count(new LambdaQueryWrapper<MaskWord>().eq(MaskWord::getCompanyId, companyId).eq(MaskWord::getWordName, maskWord.getWordName()));
+        } else {
+            count = maskWordService.count(new LambdaQueryWrapper<MaskWord>().eq(MaskWord::getCompanyId, companyId).ne(MaskWord::getId, maskWord.getId()).eq(MaskWord::getWordName, maskWord.getWordName()));
+        }
+        if(count > 0){
+            msg.setError("当前过滤词已存在,请重新输入");
+            return msg;
+        }
+        maskWord.setCompanyId(companyId);
+        if(maskWord.getRangeType() == 0){
+            maskWord.setTargetDepts("");
+        }
+        if(maskWord.getRangeType() == 1){
+            if (StringUtils.isEmpty(maskWord.getTargetDepts())) {
+                msg.setError("部门不能为空");
+                return msg;
+            }
+        }
+        if(!maskWordService.saveOrUpdate(maskWord)){
+            msg.setError("验证失败");
+        }
+        return msg;
+    }
+
+    @RequestMapping("/list")
+    public HttpRespMsg list(){
+        HttpRespMsg msg = new HttpRespMsg();
+        Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
+        List<Department> departmentList = departmentMapper.selectList(new LambdaQueryWrapper<Department>().eq(Department::getCompanyId, companyId));
+        List<MaskWord> maskWords = maskWordService.list(new LambdaQueryWrapper<MaskWord>().eq(MaskWord::getCompanyId, companyId));
+        maskWords.forEach(m -> {
+            if(!StringUtils.isEmpty(m.getTargetDepts())){
+                String[] deptSplit = m.getTargetDepts().split(",");
+                List<String> deptNames = new ArrayList<>();
+                for (String deptId : deptSplit) {
+                    Optional<Department> first = departmentList.stream().filter(d -> d.getDepartmentId().equals(Integer.valueOf(deptId))).findFirst();
+                    if(first.isPresent()){
+                        deptNames.add(first.get().getDepartmentName());
+                    }
+                }
+                if(deptNames.size() > 0){
+                    String collect = deptNames.stream().collect(Collectors.joining(","));
+                    m.setTargetDeptsNames(collect);
+                }
+            }
+        });
+        msg.setData(maskWords);
+        return msg;
+    }
+
+    @RequestMapping("/delete")
+    public HttpRespMsg delete(Integer id){
+        HttpRespMsg msg = new HttpRespMsg();
+        if(!maskWordService.removeById(id)){
+            msg.setError("验证失败");
+        }
+        return msg;
+    }
+
+}

文件差异内容过多而无法显示
+ 3436 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java~


+ 11 - 5
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/U8Controller.java

@@ -32,22 +32,28 @@ public class U8Controller {
     private ProjectService projectService;
 
     @RequestMapping("/syncOATime")
-    public HttpRespMsg syncOATime(Boolean syncOrder, Boolean syncCard, Boolean syncLeave, Boolean syncBusinessTrip, Boolean syncProject) {
+    public HttpRespMsg syncOATime(Boolean syncOrder, Boolean syncCard, Boolean syncLeave, Boolean syncBusinessTrip, Boolean syncProject, String startDate, String endDate) {
         //自动同步过去7天内的数据(含今天)
         LocalDate now = LocalDate.now();
-        LocalDate endDate = now;
-        LocalDate startDate = now.minusDays(7);
+        LocalDate endDateLocal = now;
+        LocalDate startDateLocal = now.minusDays(7);
         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        if (startDate == null) {
+            startDate = formatter.format(startDateLocal);
+        }
+        if (endDate == null) {
+            endDate = formatter.format(endDateLocal);
+        }
         if (syncOrder != null && syncOrder) {
             //同步订单数据
             erpOrderInfoService.syncData();
         }
 
         if (syncCard != null && syncCard) {
-            userFvTimeService.syncUserFvTime(HONGHU_COMP_ID, null, formatter.format(startDate), formatter.format(endDate));
+            userFvTimeService.syncUserFvTime(HONGHU_COMP_ID, null, startDate, endDate);
         }
         if (syncLeave != null && syncLeave) {
-            leaveSheetService.syncHongHuLeaveSheet(formatter.format(startDate), formatter.format(endDate), HONGHU_COMP_ID);
+            leaveSheetService.syncHongHuLeaveSheet(startDate, endDate, HONGHU_COMP_ID);
         }
         if (syncBusinessTrip != null && syncBusinessTrip) {
             businessTripService.syncHongHuData(HONGHU_COMP_ID);

+ 65 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/MaskWord.java

@@ -0,0 +1,65 @@
+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 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-09-10
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class MaskWord extends Model<MaskWord> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @TableField("company_id")
+    private Integer companyId;
+
+    /**
+     * 过滤词名称
+     */
+    @TableField("word_name")
+    private String wordName;
+
+    /**
+     * 适用部门
+     */
+    @TableField("target_depts")
+    private String targetDepts;
+
+    /**
+     * 报错提示语
+     */
+    @TableField("alert_msg")
+    private String alertMsg;
+
+    /**
+     * 0-全公司 1-部分成员 有效范围
+     */
+    @TableField("range_type")
+    private Integer rangeType;
+
+    @TableField(exist = false)
+    private String targetDeptsNames;
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 16 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/MaskWordMapper.java

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

+ 7 - 4
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ExcelParserService.java

@@ -116,7 +116,7 @@ public class ExcelParserService {
                     // 计算工作时长(这里简化处理,实际需要更精确的计算)
                     if (startTime != null && endTime != null) {
                         try {
-                            double workHours = calculateZhengBeiWorkHours(startTime, endTime, false);
+                            double workHours = calculateZhengBeiWorkHours(startTime, endTime, onlyReduceRestTime);
                             attendance.setWorkHours(workHours);
                         } catch (Exception e) {
                             // 时间格式不正确时忽略
@@ -209,10 +209,13 @@ public class ExcelParserService {
         if (startHour <= 12 && endHour >= 13) {
             hours -= 1;
         }
-        //减去晚休时间,17:30-18:00
-        if (startHour <= 17 && endHour >= 18) {
-            hours -= 0.5;
+        if (!onlyReduceRestTime) {
+            //减去晚休时间,17:30-18:00
+            if (startHour <= 17 && endHour >= 18) {
+                hours -= 0.5;
+            }
         }
+
         //夜班减去休息1.5小时
         if (startHour >= 20 || endHour >= 30) {
             hours -= 1.5;

+ 16 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/MaskWordService.java

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

+ 1 - 3
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/LeaveSheetServiceImpl.java

@@ -965,15 +965,13 @@ public class LeaveSheetServiceImpl extends ServiceImpl<LeaveSheetMapper, LeaveSh
                                 if(null != ownerTmp && null != opTmp){
                                     leaveSheet.setOperatorId(ownerTmp.getId());
                                 }
-
                             }
                             List<LeaveSheet> realDataList = dataList.stream().filter(t -> org.apache.commons.lang3.StringUtils.isNotBlank(t.getOwnerId())).collect(Collectors.toList());
                             if(!CollectionUtils.isEmpty(realDataList)){
                                 List<String> collect = realDataList.stream().map(LeaveSheet::getProcinstId).distinct().collect(Collectors.toList());
                                 List<LeaveSheet> existLeaveSheetList = leaveSheetMapper.selectList(new LambdaQueryWrapper<LeaveSheet>().in(LeaveSheet::getProcinstId, collect).eq(LeaveSheet::getCompanyId, specialCompanyId));
-//                                List<String> existIds = existLeaveSheetList.stream().map(LeaveSheet::getProcinstId).collect(Collectors.toList());
-//                                List<String> existIds = leaveSheetMapper.getExistIds(collect,specialCompanyId);
                                 for (LeaveSheet tmp : realDataList) {
+                                    System.out.println("请假人:"+tmp.getOwnerId()+","+tmp.getOwnerName()+", "+tmp.getStartDate().toString());
                                     Optional<LeaveSheet> first = existLeaveSheetList.stream().filter(t -> t.getProcinstId().equals(tmp.getProcinstId())).findFirst();
                                     if (first.isPresent()) {
                                         tmp.setId(first.get().getId());

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

@@ -0,0 +1,20 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.entity.MaskWord;
+import com.management.platform.mapper.MaskWordMapper;
+import com.management.platform.service.MaskWordService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2025-09-10
+ */
+@Service
+public class MaskWordServiceImpl extends ServiceImpl<MaskWordMapper, MaskWord> implements MaskWordService {
+
+}

+ 2 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java

@@ -1403,7 +1403,7 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                             JSONArray data = spTitle.getJSONArray("data");
                             for (int m = 0; m < data.size(); m++) {
                                 String leaveText = data.getJSONObject(m).getString("text");
-                                if (leaveText.contains("假") || leaveText.contains("休") || leaveText.contains("其他")) {
+                                if (leaveText.contains("假") || leaveText.contains("休") || leaveText.contains("其他") || leaveText.contains("考勤异常特批")) {
                                     //只要有请假,就重新计算
                                     needRecaculate = true;
                                     //获取对应位置的请假时间段
@@ -1686,7 +1686,7 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                             JSONArray data = spTitle.getJSONArray("data");
                             for (int m = 0; m < data.size(); m++) {
                                 String leaveText = data.getJSONObject(m).getString("text");
-                                if (leaveText.contains("假") || leaveText.contains("休") || leaveText.contains("其他")) {
+                                if (leaveText.contains("假") || leaveText.contains("休") || leaveText.contains("其他") || leaveText.contains("考勤异常特批")) {
                                     //获取对应位置的请假时间段
                                     String string = holiday.getJSONObject("sp_description").getJSONArray("data").getJSONObject(m).getString("text");
                                     String[] s = string.split(" |\\~");

+ 4 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java

@@ -822,10 +822,12 @@ public class TimingTask {
     }
 
     //每天1:00 同步昨天的微信请假信息
-    @Scheduled(cron = "0 0 1 ? * *")
+    @Scheduled(cron = "0 04 11 ? * *")
     public void synWxLeave() throws Exception {
-        if (isDev) return;
+//        if (isDev) return;
         System.out.println("+++++++++++++++++++微信请假同步开始+++++++++++++++++++");
+        LocalDateTime start = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0);
+
         String startTime = Long.toString(System.currentTimeMillis()/1000L-86400);
         String endTime = Long.toString(System.currentTimeMillis()/1000L);
         //查询开启了微信请假同步的公司

+ 20 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/MaskWordMapper.xml

@@ -0,0 +1,20 @@
+<?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.MaskWordMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.MaskWord">
+        <id column="id" property="id" />
+        <result column="company_id" property="companyId" />
+        <result column="word_name" property="wordName" />
+        <result column="target_depts" property="targetDepts" />
+        <result column="alert_msg" property="alertMsg" />
+        <result column="range_type" property="rangeType" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, company_id, word_name, target_depts, alert_msg, range_type
+    </sql>
+
+</mapper>

+ 40 - 70
fhKeeper/formulahousekeeper/management-workshop/src/main/java/com/management/platform/service/impl/AttendanceStaffServiceImpl.java

@@ -171,8 +171,6 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
                                         staff.setClockEndTime(endClockTime);
                                         AttendanceStaff workType = getWorkTimeType(ruleList, dayAttendance);
                                         if (workType == null) {
-                                            //打印dayAttendance
-                                            System.out.println("上班时间为:" + startClockTime + "下班时间为:" + endClockTime);
                                             //算异常了
                                             staff.setWorkHour(new BigDecimal(0));
                                             staff.setAttendanceType(YI_CHANG);
@@ -286,77 +284,49 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
                     List<Attendance> curDateAttendances = attendances.stream().filter(a->a.getClockTime().toLocalDate().equals(date)).collect(Collectors.toList());
                     //夜班的话,下班时间是第二天
                     List<Attendance> nextDateAttendances = attendances.stream().filter(a->a.getClockTime().toLocalDate().equals(date.plusDays(1))).collect(Collectors.toList());
-//                    if (!WorkDayCalculateUtils.isWorkDay(date)) {
-//                        if (curDateAttendances.size() > 0) {
-//                            //全天加班的情况,只算加班
-//                            staff.setAttendanceType(JIA_BAN);
-//                            staff.setAttendanceTypeName("加班");
-//
-//                            //7点以后,最早的时间算是上班时间
-//                            curDateAttendances.stream().filter(a->a.getClockTime().toLocalTime().isAfter(LocalTime.of(7, 0))).findFirst()
-//                                    .ifPresent(a->staff.setClockStartTime(a.getClockTime()));
-//                            if (staff.getClockStartTime() != null) {
-//                                staff.setClockEndTime(curDateAttendances.stream().max(Comparator.comparing(Attendance::getClockTime)).get().getClockTime());
-//                                //计算加班时长
-//                                BigDecimal bigDecimal = calculateWorkHours(staff.getClockStartTime(), staff.getClockEndTime(), true);//周末以半小时为一个单位,不足半小时舍去
-//                                staff.setWorkHour(bigDecimal);
-//                            }
-//                            if (jobNumber.equals("LEW3055") && date.equals(LocalDate.of(2025, 8, 2))) {
-//                                System.out.println("刘友飞, 8-2日:"+staff.getWorkHour());
-//                            }
-//                        }
-//                    } else
-                    {
-                        AttendanceStaff workTimeType = getWorkTimeType(ruleList, curDateAttendances);
-                        if (workTimeType != null) {
-                            staff.setClockStartTime(workTimeType.getClockStartTime());
-                            staff.setAttendanceType(workTimeType.getAttendanceType());
-                            ruleList.stream().filter(r -> r.getId().intValue() == workTimeType.getAttendanceType().intValue()).findFirst().ifPresent(r -> staff.setAttendanceTypeName(r.getName()));
-                            //白班或者异常小夜班,下班时间是当天
-                            LocalDateTime endClockTime = null;
-                            if (workTimeType.getAttendanceType() == JIA_BAN) {
-                                curDateAttendances.stream().filter(a->a.getClockTime().toLocalTime().isAfter(LocalTime.of(7, 0))).findFirst()
-                                    .ifPresent(a->staff.setClockStartTime(a.getClockTime()));
-                                if (staff.getClockStartTime() != null) {
-                                    endClockTime = curDateAttendances.stream().max(Comparator.comparing(Attendance::getClockTime)).get().getClockTime();
-                                }
-                            }
-                            else if (workTimeType.getAttendanceType().intValue() == BAI_BAN || workTimeType.getAttendanceType().intValue() == BAI_BAN_YI_CHANG_1 || workTimeType.getAttendanceType().intValue() == BAI_BAN_YI_CHANG_2 ||
-                                    workTimeType.getAttendanceType().intValue() == ZHONG_BAN || workTimeType.getAttendanceType().intValue() == XIAO_YE_BAN_YI_CHANG_1 || workTimeType.getAttendanceType().intValue() == XIAO_YE_BAN_YI_CHANG_2) {
+
+                    AttendanceStaff workTimeType = getWorkTimeType(ruleList, curDateAttendances);
+                    if (workTimeType != null) {
+                        staff.setClockStartTime(workTimeType.getClockStartTime());
+                        staff.setAttendanceType(workTimeType.getAttendanceType());
+                        staff.setAttendanceTypeName(workTimeType.getAttendanceTypeName());
+//                        ruleList.stream().filter(r -> r.getId().intValue() == workTimeType.getAttendanceType().intValue()).findFirst().ifPresent(r -> staff.setAttendanceTypeName(r.getName()));
+                        //白班或者异常小夜班,下班时间是当天
+                        LocalDateTime endClockTime = null;
+                        if (workTimeType.getAttendanceType() == JIA_BAN) {
+                            curDateAttendances.stream().filter(a->a.getClockTime().toLocalTime().isAfter(LocalTime.of(7, 0))).findFirst()
+                                .ifPresent(a->staff.setClockStartTime(a.getClockTime()));
+                            if (staff.getClockStartTime() != null) {
                                 endClockTime = curDateAttendances.stream().max(Comparator.comparing(Attendance::getClockTime)).get().getClockTime();
-                            } else {
-                                //夜班,取第二天9:59点前的时间作为下班时间
-
-                                //第二日的加班申请单,看看有没有
-//                            List<ApplyForm> nextDayApplyFormList = applyFormList.stream().filter(a->a.getApplyId().equals(jobNumber) && a.getType() == 4
-//                                    && a.getStartTime().toLocalDate().equals(date) && a.getEndTime().toLocalTime().getHour() <= 9).collect(Collectors.toList());
-//                            if (nextDayApplyFormList.size() > 0) {
-//                                LocalDateTime formEndTime = nextDayApplyFormList.stream().max(Comparator.comparing(ApplyForm::getEndTime)).get().getEndTime();
-//                                //打卡时间结束时间以加班后1小时内为范围
-//                            }
-                                if (nextDateAttendances.size() > 0) {
-                                    List<Attendance> nextDateAttendances1 = nextDateAttendances.stream().filter(a->a.getClockTime().getHour() <= 9).collect(Collectors.toList());
-                                    if (nextDateAttendances1.size() > 0) {
-                                        endClockTime = nextDateAttendances1.stream().max(Comparator.comparing(Attendance::getClockTime)).get().getClockTime();
-                                    }
-                                }
-                            }
-                            staff.setClockEndTime(endClockTime);
-                            if (staff.getClockEndTime() == null) {
-                                staff.setClockEndTime(staff.getClockStartTime());
                             }
-                            //仅仅一次打卡,为异常班次
-                            if (staff.getClockEndTime().equals(staff.getClockStartTime())) {
-                                staff.setClockStartTime(staff.getClockStartTime());
-                                staff.setAttendanceType(YI_CHANG);
-                                staff.setAttendanceTypeName("班次异常");
-                                staff.setWorkHour(new BigDecimal(0));
-                            } else {
-                                //有上下班打卡时间,计算时长
-                                BigDecimal bigDecimal = calculateWorkHours(staff.getClockStartTime(), endClockTime, workTimeType.getAttendanceType());
-                                staff.setWorkHour(bigDecimal);
+                        }
+                        else if (workTimeType.getAttendanceType().intValue() == BAI_BAN || workTimeType.getAttendanceType().intValue() == BAI_BAN_YI_CHANG_1 || workTimeType.getAttendanceType().intValue() == BAI_BAN_YI_CHANG_2 ||
+                                workTimeType.getAttendanceType().intValue() == ZHONG_BAN || workTimeType.getAttendanceType().intValue() == XIAO_YE_BAN_YI_CHANG_1 || workTimeType.getAttendanceType().intValue() == XIAO_YE_BAN_YI_CHANG_2) {
+                            endClockTime = curDateAttendances.stream().max(Comparator.comparing(Attendance::getClockTime)).get().getClockTime();
+                        } else {
+                            //夜班,取第二天9:59点前的时间作为下班时间
+                            if (nextDateAttendances.size() > 0) {
+                                List<Attendance> nextDateAttendances1 = nextDateAttendances.stream().filter(a->a.getClockTime().getHour() <= 9).collect(Collectors.toList());
+                                if (nextDateAttendances1.size() > 0) {
+                                    endClockTime = nextDateAttendances1.stream().max(Comparator.comparing(Attendance::getClockTime)).get().getClockTime();
+                                }
                             }
                         }
+                        staff.setClockEndTime(endClockTime);
+                        if (staff.getClockEndTime() == null) {
+                            staff.setClockEndTime(staff.getClockStartTime());
+                        }
+                        //仅仅一次打卡,为异常班次
+                        if (staff.getClockEndTime().equals(staff.getClockStartTime())) {
+                            staff.setClockStartTime(staff.getClockStartTime());
+                            staff.setAttendanceType(YI_CHANG);
+                            staff.setAttendanceTypeName("班次异常");
+                            staff.setWorkHour(new BigDecimal(0));
+                        } else {
+                            //有上下班打卡时间,计算时长
+                            BigDecimal bigDecimal = calculateWorkHours(staff.getClockStartTime(), endClockTime, workTimeType.getAttendanceType());
+                            staff.setWorkHour(bigDecimal);
+                        }
                     }
 
                     //过滤掉空数据
@@ -414,7 +384,7 @@ public class AttendanceStaffServiceImpl extends ServiceImpl<AttendanceStaffMappe
                 staff.setAttendanceType(XIAO_YE_BAN_2);
                 staff.setAttendanceTypeName("小夜班2");
                 return staff;
-            } else if (!workTime.isBefore(dayeBanRule.getBeginWorkStartTime()) && !workTime.isAfter(dayeBanRule.getBeginWorkEndTime())) {
+            } else if (!workTime.isBefore(dayeBanRule.getBeginWorkStartTime())) {
                 staff.setAttendanceType(DA_YE_BAN);
                 staff.setAttendanceTypeName("大夜班");
                 return staff;

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

@@ -1791,6 +1791,8 @@
         </el-dialog>
         <el-dialog title="U8数据同步" show-header="false" :visible.sync="syncU8DialogVisable" top="60px" :close-on-click-modal="false" width="600px">
             <el-form label-width="20%">
+                <el-form-item >日期范围 <el-date-picker v-model="syncItem.dateRange" type="daterange" :range-separator="$t('other.to')" :start-placeholder="$t('time.startDate')" :end-placeholder="$t('time.endDate')" 
+                    value-format="yyyy-MM-dd" :placeholder="$t('optiondate')" size="small" clearable style="width:280px;margin-left:10px"></el-date-picker> </el-form-item>
                 <el-form-item ><el-checkbox v-model="syncItem.syncOrder">同步工单</el-checkbox></el-form-item>
                 <el-form-item ><el-checkbox v-model="syncItem.syncCard">同步考勤打卡</el-checkbox></el-form-item>
                 <el-form-item ><el-checkbox v-model="syncItem.syncLeave">同步请假单</el-checkbox></el-form-item>
@@ -1865,6 +1867,7 @@ a {
                     syncCard: true,
                     syncLeave: true,
                     syncBusinessTrip: true,
+                    dateRange: [],
                 },
                 syncU8DialogVisable: false,
                 startSyncingU8: false,
@@ -3654,6 +3657,10 @@ a {
             },
             syncU8Start() {
                 this.startSyncingU8 = true;
+                if (this.syncItem.dateRange && this.syncItem.dateRange.length > 1) {
+                    this.syncItem.startDate = this.syncItem.dateRange[0];
+                    this.syncItem.endDate = this.syncItem.dateRange[1];
+                }
                 this.http.post('/u8portal/syncOATime',this.syncItem,
                 res => {
                     if(res.code == 'ok'){
@@ -6356,6 +6363,11 @@ a {
             };
         },
         mounted() {
+            var now = new Date();
+            var t = util.formatDate.format(now, 'yyyy-MM-dd');
+            this.today = t;
+            var startStr = util.formatDate.format(new Date(), 'yyyy-MM') + "-01";
+            this.syncItem.dateRange = [startStr,t];
             this.userssHu()
             this.getDepartment();
             this.getDepartment2();

+ 2 - 2
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/weeklyCustomization.vue

@@ -220,7 +220,7 @@ export default {
             const { allday } = this.user.timeType // 系统设置的每日工作时间
             if (strArr.length > 0) {
                 this.$message({
-                    message: `【${strArr.join('、')}】`+this.$t('tianXieGongShiHeJiFei')+` ${allday}`+ this.$t('time.hour'),
+                    message: `【${strArr.join('、')}】`+'每日填写工时合计不可超过'+` ${allday}`+ this.$t('time.hour'),
                     type: "error"
                 });
                 return
@@ -337,7 +337,7 @@ export default {
             const strArr = result
                 .filter((arr) => arr[0].projectId)
                 .filter(
-                    (arr) => arr.reduce((sum, item) => sum + (+item.workingTime || 0), 0) != allday
+                    (arr) => arr.reduce((sum, item) => sum + (+item.workingTime || 0), 0) > allday
                 )
                 .map((arr) => arr[0].dateTime);
 

+ 2 - 0
fhKeeper/formulahousekeeper/timesheet_mld/src/permissions.js

@@ -103,6 +103,7 @@ const StringUtil = {
         reportResponsibleManhourCost: false, // 负责项目子项目工时成本 //
         reportFTEAll: false, // 全部部门FTE报表 //
         reportFTEPart: false, // 负责部门FTE报表 // 
+        reportFTEProject: false, // 负责部门FTE报表 // 
         reportFTEPlanAll: false, // 全部部门FTE计划报表 // 
         reportFTEPlanPart: false, // 负责部门FTE计划报表 // 
         reportEfficent: false, // 有效工时率 // 
@@ -261,6 +262,7 @@ const StringUtil = {
         arr[i] == '负责项目分组阶段工时' ? obj.reportStageWorkingTime = true : ''
         arr[i] == '全部部门FTE报表' ? obj.reportFTEAll = true : ''
         arr[i] == '负责部门FTE报表' ? obj.reportFTEPart = true : ''
+        arr[i] == '负责项目FTE报表' ? obj.reportFTEProject = true : ''
         arr[i] == '全部部门FTE计划报表' ? obj.reportFTEPlanAll = true : ''
         arr[i] == '负责部门FTE计划报表' ? obj.reportFTEPlanPart = true : ''
         arr[i] == '有效工时率表' ? obj.reportEfficent = true : ''

+ 1 - 1
fhKeeper/formulahousekeeper/timesheet_mld/src/views/corpreport/list.vue

@@ -39,7 +39,7 @@
                   <el-menu-item index="1-17" v-if="permissions.reportPhaseHours || permissions.reportStageWorkingTime" @click="ssl(16)"><p>{{ $t('xiang-mu-ge-fen-zu-yu-jie-duan-gong-shi-biao') }}</p></el-menu-item>
                   <el-menu-item index="1-18" v-if="permissions.reportAllManhourCost || permissions.reportResponsibleManhourCost" @click="ssl(17)"><p>{{ $t('ziXiangMuGongShiChengBenBiao') }}</p></el-menu-item>
                   <el-menu-item index="1-19" v-if="user.timeType.restartTaskNeedReason == 1" @click="ssl(18)"><p>{{ $t('renWuZhongQiBiao') }}</p></el-menu-item>
-                  <el-menu-item index="1-20" v-if="permissions.reportFTEAll || permissions.reportFTEPart" @click="ssl(19)"><p>{{ $t('fteBaoBiao') }}</p></el-menu-item>
+                  <el-menu-item index="1-20" v-if="permissions.reportFTEAll || permissions.reportFTEPart || permissions.reportFTEProject" @click="ssl(19)"><p>{{ $t('fteBaoBiao') }}</p></el-menu-item>
                   <el-menu-item index="1-21" v-if="permissions.reportEfficent"  @click="ssl(20)"><p>{{ $t('youXiaoGongShiShuaiBiao') }}</p></el-menu-item>
                   <el-menu-item index="1-22" v-if="permissions.reportSortScaleTable" @click="ssl(21)"><p>{{ $t('xiangMuFenLeiGongShiZhanBiBiao') }}</p></el-menu-item>
                   <el-menu-item index="1-23" v-if="permissions.reportSortDetailTable || permissions.reportSortSectionDetailTable" @click="ssl(22)"><p>{{ $t('fenLeiGongShiMingXiBiao') }}</p></el-menu-item>

+ 137 - 10
fhKeeper/formulahousekeeper/webttkuaiban/src/main/resources/static/css/personal.css

@@ -192,6 +192,11 @@ body {
     margin-top: 40px;
 }
 
+.download-btn-container {
+    position: relative;
+    display: inline-block;
+}
+
 .download-btn {
     display: inline-block;
     background: #3396FB;
@@ -213,9 +218,53 @@ body {
     text-decoration: none;
 }
 
+.download-qr {
+    position: absolute;
+    top: 50%;
+    left: 100%;
+    transform: translateY(-50%);
+    background: #fff;
+    padding: 20px;
+    border-radius: 12px;
+    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
+    text-align: center;
+    z-index: 1000;
+    margin-left: 15px;
+    min-width: 240px;
+}
+
+.download-qr::before {
+    content: '';
+    position: absolute;
+    top: 50%;
+    left: -8px;
+    transform: translateY(-50%);
+    width: 0;
+    height: 0;
+    border-top: 8px solid transparent;
+    border-bottom: 8px solid transparent;
+    border-right: 8px solid #fff;
+}
+
+.download-qr p {
+    margin: 0 0 15px 0;
+    font-size: 14px;
+    color: #333;
+    font-weight: 500;
+}
+
+.download-qr img {
+    width: 200px;
+    height: 200px;
+    border-radius: 8px;
+}
+
 .personal-banner-right {
     flex: 1;
     text-align: center;
+    display: flex;
+    align-items: center;
+    justify-content: center;
 }
 
 .dashboard-img {
@@ -225,6 +274,70 @@ body {
     box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
 }
 
+/* 轮播图样式 */
+.dashboard-carousel {
+    position: relative;
+    max-width: 100%;
+    margin: 0 auto;
+}
+
+.carousel-container {
+    position: relative;
+    overflow: hidden;
+    border-radius: 12px;
+    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
+    height: 500px; /* 限制容器高度 */
+}
+
+.carousel-list {
+    list-style: none;
+    margin: 0;
+    padding: 0;
+    display: flex;
+    transition: transform 0.5s ease-in-out;
+    height: 100%;
+}
+
+.carousel-list li {
+    min-width: 100%;
+    flex-shrink: 0;
+    height: 100%;
+}
+
+.carousel-list li img {
+    width: 100%;
+    height: 100%;
+    object-fit: contain; /* 保持图片比例并完整显示 */
+    display: block;
+    border-radius: 12px;
+}
+
+.carousel-dots {
+    display: flex;
+    justify-content: center;
+    gap: 12px;
+    margin-top: 20px;
+}
+
+.dot {
+    width: 12px;
+    height: 12px;
+    border-radius: 50%;
+    background: rgba(51, 150, 251, 0.3);
+    cursor: pointer;
+    transition: all 0.3s ease;
+}
+
+.dot.active {
+    background: #3396FB;
+    transform: scale(1.2);
+}
+
+.dot:hover {
+    background: #3396FB;
+    transform: scale(1.1);
+}
+
 /* 为什么选择工时管家个人版 */
 .why-choose-section {
     padding: 100px 0;
@@ -264,21 +377,24 @@ body {
 }
 
 .features-grid {
-    display: grid;
-    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
-    gap: 40px;
-    max-width: 1000px;
+    display: flex;
+    justify-content: space-between;
+    gap: 20px;
+    max-width: 1200px;
     margin: 0 auto;
+    flex-wrap: nowrap;
 }
 
 .feature-card {
     background: #fff;
-    padding: 40px 30px;
+    padding: 30px 20px;
     border-radius: 12px;
     text-align: center;
     box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08);
     transition: all 0.3s ease;
     border: 1px solid #f0f0f0;
+    flex: 1;
+    min-width: 0;
 }
 
 .feature-card:hover {
@@ -392,13 +508,14 @@ body {
 
 .feature-img {
     max-width: 100%;
-    height: auto;
+    height: 500px;
+    object-fit: contain;
     border-radius: 12px;
     box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
 }
 
 /* 响应式设计 */
-@media (max-width: 1024px) {
+@media (max-width: 800px) {
     .personal-banner-content {
         flex-direction: column;
         gap: 60px;
@@ -409,6 +526,11 @@ body {
         font-size: 40px;
     }
     
+    .dashboard-carousel {
+        max-width: 500px;
+        margin: 0 auto;
+    }
+    
     .feature-detail-row,
     .feature-detail-reverse .feature-detail-row {
         flex-direction: column;
@@ -416,8 +538,12 @@ body {
     }
     
     .features-grid {
-        grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
-        gap: 30px;
+        flex-wrap: wrap;
+        gap: 20px;
+    }
+    
+    .feature-card {
+        flex: 0 0 calc(50% - 10px);
     }
 }
 
@@ -449,12 +575,13 @@ body {
     }
     
     .features-grid {
-        grid-template-columns: 1fr;
+        flex-direction: column;
         gap: 30px;
     }
     
     .feature-card {
         padding: 30px 20px;
+        flex: none;
     }
 }
 

二进制
fhKeeper/formulahousekeeper/webttkuaiban/src/main/resources/static/image/my.jpg


二进制
fhKeeper/formulahousekeeper/webttkuaiban/src/main/resources/static/image/record.jpg


二进制
fhKeeper/formulahousekeeper/webttkuaiban/src/main/resources/static/image/statistic.jpg


+ 89 - 5
fhKeeper/formulahousekeeper/webttkuaiban/src/main/resources/static/personal.html

@@ -56,11 +56,30 @@
                     完全免费的工时管家个人版,专为兼职、小时工和车间人员设计,轻松记录工时,自动计算收入,让每一分钟都有价值。
                 </p>
                 <div class="personal-download">
-                    <a href="#" class="download-btn">下载手机版</a>
+                    <div class="download-btn-container">
+                        <a href="#" class="download-btn" id="downloadBtn">下载手机版</a>
+                        <div class="download-qr" id="downloadQr" style="display: none">
+                            <p>请使用手机浏览器扫码下载</p>
+                            <img src="./image/timesheet_qr.png" alt="下载二维码">
+                        </div>
+                    </div>
                 </div>
             </div>
             <div class="personal-banner-right">
-                <img src="./image/gs1.jpg" alt="工时管家个人版界面" class="dashboard-img">
+                <div class="dashboard-carousel" id="dashboardCarousel">
+                    <div class="carousel-container">
+                        <ul class="carousel-list">
+                            <li><img src="./image/record.jpg" alt="工时记录界面" class="dashboard-img"></li>
+                            <li><img src="./image/statistic.jpg" alt="统计分析界面" class="dashboard-img"></li>
+                            <li><img src="./image/my.jpg" alt="我的设置界面" class="dashboard-img"></li>
+                        </ul>
+                    </div>
+                    <div class="carousel-dots">
+                        <span class="dot active" data-index="0"></span>
+                        <span class="dot" data-index="1"></span>
+                        <span class="dot" data-index="2"></span>
+                    </div>
+                </div>
             </div>
         </div>
     </div>
@@ -132,7 +151,7 @@
                     </ul>
                 </div>
                 <div class="feature-detail-right">
-                    <img src="./image/gs2.jpg" alt="工时记录界面" class="feature-img">
+                    <img src="./image/record.jpg" alt="工时记录界面" class="feature-img">
                 </div>
             </div>
         </div>
@@ -143,7 +162,7 @@
         <div class="container">
             <div class="feature-detail-row">
                 <div class="feature-detail-left">
-                    <img src="./image/gs3.jpg" alt="统计分析界面" class="feature-img">
+                    <img src="./image/statistic.jpg" alt="统计分析界面" class="feature-img">
                 </div>
                 <div class="feature-detail-right">
                     <h3>统计分析</h3>
@@ -174,7 +193,7 @@
                     </ul>
                 </div>
                 <div class="feature-detail-right">
-                    <img src="./image/gs4.jpg" alt="我的设置界面" class="feature-img">
+                    <img src="./image/my.jpg" alt="我的设置界面" class="feature-img">
                 </div>
             </div>
         </div>
@@ -213,6 +232,71 @@
                 $('#zhixun').hide();
             }
         });
+        
+        // 下载按钮悬浮显示二维码
+        $('.download-btn-container').hover(
+            function() {
+                $('#downloadQr').show();
+            },
+            function() {
+                $('#downloadQr').hide();
+            }
+        );
+        
+        // 点击其他地方隐藏二维码
+        $(document).click(function(e) {
+            if(!$(e.target).closest('.download-btn-container').length) {
+                $('#downloadQr').hide();
+            }
+        });
+    </script>
+    
+    <script>
+        // 轮播图功能
+        $(document).ready(function() {
+            let currentIndex = 0;
+            const totalSlides = $('.carousel-list li').length;
+            const slideWidth = 100; // 每张图片占100%宽度
+            
+            // 自动轮播
+            function autoSlide() {
+                currentIndex = (currentIndex + 1) % totalSlides;
+                updateCarousel();
+            }
+            
+            // 更新轮播图位置和指示器
+            function updateCarousel() {
+                const translateX = -currentIndex * slideWidth;
+                $('.carousel-list').css('transform', `translateX(${translateX}%)`);
+                
+                // 更新指示器
+                $('.dot').removeClass('active');
+                $(`.dot[data-index="${currentIndex}"]`).addClass('active');
+            }
+            
+            // 点击指示器切换
+            $('.dot').click(function() {
+                currentIndex = parseInt($(this).data('index'));
+                updateCarousel();
+                
+                // 重置自动轮播定时器
+                clearInterval(carouselInterval);
+                carouselInterval = setInterval(autoSlide, 4000);
+            });
+            
+            // 启动自动轮播,每4秒切换一次
+            let carouselInterval = setInterval(autoSlide, 4000);
+            
+            // 鼠标悬停时暂停自动轮播
+            $('.dashboard-carousel').hover(
+                function() {
+                    clearInterval(carouselInterval);
+                },
+                function() {
+                    carouselInterval = setInterval(autoSlide, 4000);
+                }
+            );
+        });
     </script>
 </body>
 </html>