소스 검색

Merge remote-tracking branch 'origin/master'

yusm 4 주 전
부모
커밋
73268207c2
33개의 변경된 파일1560개의 추가작업 그리고 196개의 파일을 삭제
  1. 98 31
      fhKeeper/formulahousekeeper/collectdata/src/main/java/com/management/collectdata/controller/DataCollectController.java
  2. 6 0
      fhKeeper/formulahousekeeper/collectdata/src/main/java/com/management/collectdata/entity/LeaveSheet.java
  3. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/businessNew.png
  4. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/contactsNew.png
  5. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/contractNew.png
  6. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/customerNew.png
  7. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/orderNew.png
  8. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/productNew.png
  9. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/tasksNew.png
  10. BIN
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/threadNew.png
  11. 6 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/hooks/useApi.js
  12. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/moduleList/moduleList.vue
  13. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/addEditor.vue
  14. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/businessOpportunityStage.vue
  15. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/order/addEditor.vue
  16. 132 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/commonFunctions.vue
  17. 3 3
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/dataAnalysis.vue
  18. 132 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/quickEntrance.vue
  19. 5 5
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/workbench.vue
  20. 236 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/index copy.vue
  21. 237 99
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/index.vue
  22. 2 2
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/my/index copy.vue
  23. 28 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/visitor/index.vue
  24. 632 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/visitor/workbench.vue
  25. 1 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/visitorProgram/visitorDetails.vue
  26. 5 0
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/router.js
  27. 15 1
      fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/utility/generalVariables.js
  28. 7 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TimeType.java
  29. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ExcelParserService.java
  30. 0 46
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/DataCollectTask.java
  31. 2 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/TimeTypeMapper.xml
  32. 4 1
      fhKeeper/formulahousekeeper/timesheet/src/views/settings/timetype.vue
  33. 1 1
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

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

@@ -24,6 +24,7 @@ import java.time.LocalTime;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 @RequestMapping("/dataCollect")
 @RestController
@@ -133,37 +134,6 @@ public class DataCollectController {
             }
             System.out.println("本批次总数量:"+tisList.size()+",成功插入条数:"+insertCount+",成功更新条数:"+updateCount);
         }
-//        List<TisTimeVO> tisTimeVOS = JSONArray.parseArray(jsonarray, TisTimeVO.class);
-//        if(!CollectionUtils.isEmpty(tisList)){
-//            int count = 0;
-//            for (TisTimeVO tisTimeVO : tisList) {
-//                String sqlQuery = "select top 1 iRealCOID from ca_batchmap where cMOCode = ? and iMOSubSN = ? ";
-//                try (Connection connection = sqlServerDataSource.getConnection()) {
-//                    PreparedStatement queryStmt = connection.prepareStatement(sqlQuery);
-//                    queryStmt.setString(1,tisTimeVO.getOrderId());
-//                    queryStmt.setInt(2,tisTimeVO.getLine());
-//                    ResultSet queryRs = queryStmt.executeQuery();
-//                    if (queryRs.next()) {
-//                        tisTimeVO.setCoId(queryRs.getString("iRealCOID"));
-//                        String sqlInsert = "insert into CA_DayTiS(cPPID,iRealWkt,dDate) values(?,?,?)";
-//                        PreparedStatement insertStmt = connection.prepareStatement(sqlInsert);
-//                        insertStmt.setString(1,tisTimeVO.getCoId());
-//                        insertStmt.setBigDecimal(2,tisTimeVO.getWorkTime());
-//                        insertStmt.setString(3,tisTimeVO.getDateStr());
-//                        int i = insertStmt.executeUpdate();
-//                        if(i>0){
-//                            count++;
-////                        System.out.println("执行成功");
-//                        }else{
-//                            System.out.println(tisTimeVO.getCoId()+"执行失败");
-//                        }
-//                    }
-//                } catch (SQLException e) {
-//                    System.err.println("数据库操作错误: " + e.getMessage());
-//                }
-//            }
-//            System.out.println("本批次总数量:"+tisList.size()+",成功插入条数:"+count);
-//        }
         return "ok";
     }
 
@@ -373,7 +343,21 @@ public class DataCollectController {
                     userFvTime.setProcinstId(resultSet.getString("id"));
                     userFvTime.setUserId(resultSet.getString("emp_no"));
                     userFvTime.setWorkDate(null==resultSet.getDate("attendance_date")?null:LocalDate.parse(sdfYmd.format(resultSet.getDate("attendance_date")), DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+                    if (pageBO.getJobNumber() != null) {
+                        //用户单独刷新某天的
+                        String date = sdfYmd.format(resultSet.getDate("attendance_date"));
+                        List<LeaveSheet> sheetList = getLeaveSheetInRange(date, date, pageBO.getJobNumber());
+                        if (sheetList != null && sheetList.size() > 0) {
+                            //获取请假的最晚时间点作为考勤开始时间
+                            LeaveSheet leaveSheet = sheetList.get(0);
+                            userFvTime.setStartTime(leaveSheet.getEndTime());
+                        }
+                    }
                     String startTime = resultSet.getString("come1");
+                    //如果有请假的结束时间,并且比打卡时间要晚,以请假结束时间作为上班时间
+                    if (userFvTime.getStartTime() != null && userFvTime.getStartTime().compareTo(startTime) > 0) {
+                        startTime = userFvTime.getStartTime();
+                    }
                     //从9:05开始计算,小于9:05的以9:00为准,向上取30分钟, 比如9:09,就转换为9:30
                     if (startTime.compareTo("09:05") <= 0) {
                         startTime = "09:00";
@@ -411,6 +395,24 @@ public class DataCollectController {
                     resList.add(userFvTime);
                 }
             }
+
+            if (pageBO.getJobNumber() == null) {
+                //取请假的记录,合并处理考勤开始时间
+                List<LeaveSheet> leaveSheetList = getLeaveSheetInRange(pageBO.getStartDate(), pageBO.getEndDate(), null);
+                if (leaveSheetList != null && leaveSheetList.size() > 0) {
+                    for (LeaveSheet leaveSheet : leaveSheetList) {
+                        List<UserFvTime> userFvTimeList = resList.stream().filter(userFvTime -> leaveSheet.getStartDate().isEqual(userFvTime.getWorkDate())).collect(Collectors.toList());
+                        if (userFvTimeList != null && userFvTimeList.size() > 0) {
+                            //有记录的话,更新考勤开始时间
+                            UserFvTime userFvTime = userFvTimeList.get(0);
+                            if (userFvTime.getStartTime() == null || userFvTime.getStartTime().compareTo(leaveSheet.getEndTime()) < 0) {
+                                userFvTime.setStartTime(leaveSheet.getEndTime());
+                                userFvTime.setWorkHours(calculateWorkHoursFromTime(userFvTime));
+                            }
+                        }
+                    }
+                }
+            }
         } catch (SQLException e) {
             System.err.println("数据库操作错误: " + e.getMessage());
         }
@@ -549,6 +551,71 @@ public class DataCollectController {
         return resList;
     }
 
+    private List<LeaveSheet> getLeaveSheetInRange(String startDate, String endDate, String empNo) {
+        String sqlQuery = "select distinct ahlo.id,ahlo.company_id,ahlo.emp_no,ahlo.emp_name " +
+                " ,ahlo.begin_date,ahlo.end_date,ahlo.begin_time,ahlo.end_time,ahlo.leave_category_name " +
+                " ,ahlo.audit_status,ahlo.reason, eic.emp_no as operator_id " +
+                " ,ahlo.total_hour,ahlo.total_day,eie.mobile " +
+                " 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 ?";
+        if (empNo != null) {
+            sqlQuery += "and ahlo.emp_no = ?";
+        }
+        sqlQuery += " order by ahlo.end_time desc";
+        SimpleDateFormat sdfYmd = new SimpleDateFormat("yyyy-MM-dd");
+        List<LeaveSheet> resList = new ArrayList<>();
+        try (Connection connection = mysqlDataSource.getConnection()) {
+            PreparedStatement queryStmt = connection.prepareStatement(sqlQuery);
+            queryStmt.setString(1, startDate);
+            queryStmt.setString(2, endDate);
+            if (empNo != null) {
+                queryStmt.setString(3, empNo);
+            }
+            try (ResultSet resultSet = queryStmt.executeQuery()) {
+                while (resultSet.next()) {
+                    LeaveSheet leaveSheet = new LeaveSheet();
+                    leaveSheet.setCompanyId(companyId);
+                    leaveSheet.setOwnerId(resultSet.getString("emp_no"));
+                    leaveSheet.setOwnerName(resultSet.getString("emp_name"));
+                    leaveSheet.setBeginTime(resultSet.getString("begin_time"));
+                    leaveSheet.setEndTime(resultSet.getString("end_time"));
+                    leaveSheet.setStartDate(null==resultSet.getDate("begin_date")?null:LocalDate.parse(sdfYmd.format(resultSet.getDate("begin_date")),DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+                    leaveSheet.setEndDate(null==resultSet.getDate("end_date")?null:LocalDate.parse(sdfYmd.format(resultSet.getDate("end_date")),DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+                    switch (resultSet.getString("leave_category_name")){
+                        case "调休假" : leaveSheet.setLeaveType(6); break;
+                        case "年假" : leaveSheet.setLeaveType(2); break;
+                        case "事假" : leaveSheet.setLeaveType(0); break;
+                        case "病假" : leaveSheet.setLeaveType(1);break;
+                        case "婚假" : leaveSheet.setLeaveType(4);break;
+                        case "产假" : leaveSheet.setLeaveType(3);break;
+                        case "哺乳假" : leaveSheet.setLeaveType(3);break;
+                        case "陪产假" : leaveSheet.setLeaveType(7);break;
+                        case "丧假" : leaveSheet.setLeaveType(5);break;
+                        case "工伤假" : leaveSheet.setLeaveType(1);break;
+                        case "育儿假" : leaveSheet.setLeaveType(3);break;
+                        case "福利病假" : leaveSheet.setLeaveType(1);break;
+                        default: leaveSheet.setLeaveType(8);break;
+                    }
+                    leaveSheet.setStatus(0);
+                    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.setIndate(LocalDateTime.now());
+                    leaveSheet.setTimeType(0);
+                    leaveSheet.setTel(resultSet.getString("mobile"));
+                    leaveSheet.setProcinstId(resultSet.getString("id"));
+                    resList.add(leaveSheet);
+                }
+            }
+        } catch (SQLException e) {
+            System.err.println("数据库操作错误: " + e.getMessage());
+        }
+        return resList;
+    }
+
 
     @RequestMapping("/getBusinessTripDataSum")
     public String getBusinessTripDataSum() {

+ 6 - 0
fhKeeper/formulahousekeeper/collectdata/src/main/java/com/management/collectdata/entity/LeaveSheet.java

@@ -157,6 +157,12 @@ public class LeaveSheet extends Model<LeaveSheet> {
     @TableField("file_urls")
     private String fileUrls;
 
+    @TableField("begin_time")
+    private String beginTime;
+
+    @TableField("end_time")
+    private String endTime;
+
     @Override
     protected Serializable pkVal() {
         return this.id;

BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/businessNew.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/contactsNew.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/contractNew.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/customerNew.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/orderNew.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/productNew.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/tasksNew.png


BIN
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/assets/image/module/threadNew.png


+ 6 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/hooks/useApi.js

@@ -120,3 +120,9 @@ export const NEW_SALES_ORDER_PAYMENT_COLLECTION = `/order/paymentCollection` //
 export const SALES_ORDER_COLLECTION_EDITING = `/order/editPayment` // 编辑销售订单收款
 export const DELETE_SALES_ORDER_PAYMENT_RECORDS = `/order/deletePayment` // 删除销售订单收款记录
 export const OBTAIN_THE_CONTRACT_REMITTANCE_LIST = `/contract-payment/getList` // 获取合同打款列表
+
+export const GET_ALL_ROUTING_PARAMETERS = `/sys-module/getAllSysModule` // 获取全部路由
+export const GET_FAST_ACCESS_LIST = `/sys-module/getFastAccessList` // 快捷新建列表
+export const COMMON_FUNCTIONS = `/sys-module/getCommonUseList` // 获取当前公司的常用功能列表
+export const SAVE_FAST_ACCESS_LIST = `/sys-module/saveFastAccessList` // 保存当前公司的快捷新建数据
+export const SAVE_COMMON_USE_LIST = `/sys-module/saveCommonUseList` // 保存当前公司的常用功能数据

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/moduleList/moduleList.vue

@@ -499,7 +499,7 @@ function getFormJson(info = {}) {
     fixedData.updateState({
       formJson: {
         ...fixedData.formJson,
-        [info.key]: JSON.parse(data[0].config)
+        [info.key]: JSON.parse(data[0]?.config)
       }
     })
   })

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/addEditor.vue

@@ -12,7 +12,7 @@
           <template #foldingRight>
             <div class="flex items-center">
               <van-button icon="plus" color="#FF8B32" size="mini" class="relatedAddButton" @click="addBusinessItemProductList()">添加</van-button>
-              <van-button icon="plus" color="#075985" size="mini" class="relatedAddButton" @click.stop="resetBusinessItemProductList(index)" v-if="businessItemProductList.length == 1">重置</van-button>
+              <van-button icon="plus" color="#0859d6" size="mini" class="relatedAddButton" @click.stop="resetBusinessItemProductList(index)" v-if="businessItemProductList.length == 1">重置</van-button>
               <van-button icon="plus" color="#EE0A24" size="mini" class="relatedAddButton" @click.stop="deleteBusinessItemProductList(index)" v-if="businessItemProductList.length > 1">删除</van-button>
             </div>
           </template>

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/business/businessOpportunityStage.vue

@@ -209,7 +209,7 @@ useLifecycle({
   color: #858585;
 }
 .haveInHand {
-  background: #075985;
+  background: #0859d6;
   color: #fff;
 }
 .notStartedYet {

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/pageComponents/order/addEditor.vue

@@ -13,7 +13,7 @@
             <div class="flex items-center">
               <van-button icon="plus" color="#FF8B32" size="mini" class="relatedAddButton"
                 @click="addBusinessItemProductList()">添加</van-button>
-              <van-button icon="plus" color="#075985" size="mini" class="relatedAddButton"
+              <van-button icon="plus" color="#0859d6" size="mini" class="relatedAddButton"
                 @click.stop="resetBusinessItemProductList(index)"
                 v-if="businessItemProductList.length == 1">重置</van-button>
               <van-button icon="plus" color="#EE0A24" size="mini" class="relatedAddButton"

+ 132 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/commonFunctions.vue

@@ -0,0 +1,132 @@
+<template>
+  <div class="w-full h-full flex flex-col">
+    <div class="bg-[#f4f5fa] text-center componentTitle">常用功能设置</div>
+    <div class="smallFontTitle">常用功能</div>
+    <div class="module">
+      <div class="module-item" v-for="(item, index) in selectList" :key="index">
+        <div class="module-img"><img :src="getRouterImg(item.path)" alt=""></div>
+        <div class="module-text">{{ item.name }}</div>
+        <div class="absolute top-0 right-0" @click="operationEntrance(index, 'delete')">
+          <van-icon name="clear" color="#aaaaaa" size="18" />
+        </div>
+      </div>
+    </div>
+    <div class="smallFontTitle">全部</div>
+    <div class="flex-1 overflow-y-auto">
+      <div class="module">
+        <div class="module-item" v-for="(item, index) in allList">
+          <div class="module-img"><img :src="getRouterImg(item.path)" alt=""></div>
+          <div class="module-text">{{ item.name }}</div>
+          <div class="absolute top-0 right-0" @click="operationEntrance(index, 'add')">
+            <van-icon name="add" color="#1a6afb" size="18" />
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 确定按钮 -->
+    <van-button type="primary" class="confirmToSave" :disabled="!selectList.length" @click="saveConfiguration" loading-text="保存中..." :loading="loading">保存</van-button>
+  </div>
+</template>
+<script setup>
+import { ref, defineProps, defineEmits, watch } from "vue";
+import requests from "@common/requests"
+import { SAVE_COMMON_USE_LIST } from "@hooks/useApi";
+import useToast from "@hooks/useToast"
+import { routingInfos, getRouterImg } from "@utility/generalVariables.js";
+
+const { toastText, toastSuccess, toastFail, toastLoading } = useToast()
+const emit = defineEmits();
+const props = defineProps({
+  allModulesList: Array,
+  selectModule: Array
+});
+const selectList = ref([])
+const allList = ref([])
+const loading = ref(false)
+
+watch(() => props.selectModule, (newValue, oldValue) => {
+  processingData()
+}, { immediate: true });
+
+function saveConfiguration() {
+  loading.value = true
+  const list = selectList.value.map(item => {
+    return {
+      id: item.id,
+      name: item.name,
+      router: item.path
+    }
+  })
+  requests.post(SAVE_COMMON_USE_LIST, { jsonstr: JSON.stringify(list) }).then(() => {
+    toastSuccess('保存成功')
+    emit('save-value')
+  }).finally(() => {
+    loading.value = false
+  })
+}
+
+function operationEntrance(index, type) {
+  if(type == 'add') {
+    selectList.value.push(allList.value[index]) 
+  } else {
+    selectList.value.splice(index, 1)
+  }
+
+  allList.value = props.allModulesList.filter(item => !selectList.value.some(arrItem => arrItem.path === item.path))
+}
+
+function processingData() {
+  allList.value = props.allModulesList.filter(item => !props.selectModule.some(arrItem => arrItem.path === item.path))
+  selectList.value = props.selectModule
+}
+
+</script>
+<style lang="scss" scoped>
+.componentTitle {
+  font-size: 20px;
+  line-height: 54px;
+}
+
+.smallFontTitle {
+  font-size: 16px;
+  font-weight: bold;
+  color: #202d45;
+  padding: 15px 20px;
+}
+
+.module {
+  padding: 0 20px;
+  display: flex;
+  flex-wrap: wrap;
+  position: relative;
+
+  .module-item {
+    width: 22.5%;
+    padding: 10px 0;
+    background: #fff;
+    box-shadow: 0px 2px 8px 0px #e4e4e4;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    border-radius: 6px;
+    margin-right: 3%;
+    margin-bottom: 8px;
+    position: relative;
+
+    &:nth-child(4n) {
+      margin-right: 0;
+    }
+
+    .module-img {
+      width: 30px;
+      height: 30px;
+      margin-bottom: 10px;
+    }
+  }
+}
+
+.confirmToSave {
+  margin: 10px 20px;
+}
+</style>

+ 3 - 3
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/dataAnalysis.vue

@@ -32,8 +32,8 @@ useLifecycle({
 <style lang='scss'>
 /* 样式代码 */
 .dataAnalysis {
-  margin: 20px 0 16px 0;
-  padding: 0 16px;
+  // margin: 20px 0 16px 0;
+  // padding: 0 16px;
 
   .transitionEffect {
     transition: all .5s ease-in-out;
@@ -106,7 +106,7 @@ useLifecycle({
           content: '';
           width: 2px;
           border-radius: 10px;
-          background: #075985;
+          background: #0859d6;
           height: 18px;
           position: absolute;
           left: 0;

+ 132 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/quickEntrance.vue

@@ -0,0 +1,132 @@
+<template>
+  <div class="w-full h-full flex flex-col">
+    <div class="bg-[#f4f5fa] text-center componentTitle">快捷新建设置</div>
+    <div class="smallFontTitle">快捷新建</div>
+    <div class="module">
+      <div class="module-item" v-for="(item, index) in selectList" :key="index">
+        <div class="module-img"><img :src="getRouterImg(item.path)" alt=""></div>
+        <div class="module-text">建{{ item.name }}</div>
+        <div class="absolute top-0 right-0" @click="operationEntrance(index, 'delete')">
+          <van-icon name="clear" color="#aaaaaa" size="18" />
+        </div>
+      </div>
+    </div>
+    <div class="smallFontTitle">全部</div>
+    <div class="flex-1 overflow-y-auto">
+      <div class="module">
+        <div class="module-item" v-for="(item, index) in allList">
+          <div class="module-img"><img :src="getRouterImg(item.path)" alt=""></div>
+          <div class="module-text">建{{ item.name }}</div>
+          <div class="absolute top-0 right-0" v-if="selectList.length < 4" @click="operationEntrance(index, 'add')">
+            <van-icon name="add" color="#1a6afb" size="18" />
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 确定按钮 -->
+    <van-button type="primary" class="confirmToSave" :disabled="!selectList.length" @click="saveConfiguration" loading-text="保存中..." :loading="loading">保存</van-button>
+  </div>
+</template>
+<script setup>
+import { ref, defineProps, defineEmits, watch } from "vue";
+import requests from "@common/requests"
+import { SAVE_FAST_ACCESS_LIST } from "@hooks/useApi";
+import useToast from "@hooks/useToast"
+import { routingInfos, getRouterImg } from "@utility/generalVariables.js";
+
+const { toastText, toastSuccess, toastFail, toastLoading } = useToast()
+const emit = defineEmits();
+const props = defineProps({
+  allModulesList: Array,
+  selectModule: Array
+});
+const selectList = ref([])
+const allList = ref([])
+const loading = ref(false)
+
+watch(() => props.selectModule, (newValue, oldValue) => {
+  processingData()
+}, { immediate: true });
+
+function saveConfiguration() {
+  loading.value = true
+  const list = selectList.value.map(item => {
+    return {
+      id: item.id,
+      name: item.name,
+      router: item.path
+    }
+  })
+  requests.post(SAVE_FAST_ACCESS_LIST, { jsonstr: JSON.stringify(list) }).then(() => {
+    toastSuccess('保存成功')
+    emit('save-value')
+  }).finally(() => {
+    loading.value = false
+  })
+}
+
+function operationEntrance(index, type) {
+  if(type == 'add') {
+    selectList.value.push(allList.value[index]) 
+  } else {
+    selectList.value.splice(index, 1)
+  }
+
+  allList.value = props.allModulesList.filter(item => !selectList.value.some(arrItem => arrItem.path === item.path))
+}
+
+function processingData() {
+  allList.value = props.allModulesList.filter(item => !props.selectModule.some(arrItem => arrItem.path === item.path))
+  selectList.value = props.selectModule
+}
+
+</script>
+<style lang="scss" scoped>
+.componentTitle {
+  font-size: 20px;
+  line-height: 54px;
+}
+
+.smallFontTitle {
+  font-size: 16px;
+  font-weight: bold;
+  color: #202d45;
+  padding: 15px 20px;
+}
+
+.module {
+  padding: 0 20px;
+  display: flex;
+  flex-wrap: wrap;
+  position: relative;
+
+  .module-item {
+    width: 22.5%;
+    padding: 10px 0;
+    background: #fff;
+    box-shadow: 0px 2px 8px 0px #e4e4e4;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    border-radius: 6px;
+    margin-right: 3%;
+    margin-bottom: 8px;
+    position: relative;
+
+    &:nth-child(4n) {
+      margin-right: 0;
+    }
+
+    .module-img {
+      width: 30px;
+      height: 30px;
+      margin-bottom: 10px;
+    }
+  }
+}
+
+.confirmToSave {
+  margin: 10px 20px;
+}
+</style>

+ 5 - 5
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/component/workbench.vue

@@ -30,7 +30,7 @@
             <van-swipe-cell>
               <!-- 拜访计划 -->
               <template v-if="item.calendarType == 1">
-                <div class="bg-[#075985] ra6 p-4 mb-4 text-[#fff]" @click="jumpToVisitorDetails(item)">
+                <div class="bg-[#0859d6] ra6 p-4 mb-4 text-[#fff]" @click="jumpToVisitorDetails(item)">
                   <div class="w-full flex items-center justify-between">
                     <div class="text-size-in font-bold text-[#fff]">{{ item.planName }}</div>
                     <div :class="`labelTag ${item.finishState == 0 ? 'toBeCompleted' : 'completed'}`">{{ ['未完成',
@@ -127,7 +127,7 @@
             </div>
           </template>
 
-          <div class="w80 bg-[#075985] h-28 rounded-md flex flex-col items-center justify-center"
+          <div class="w80 bg-[#0859d6] h-28 rounded-md flex flex-col items-center justify-center"
             @click="showCommonForms = true">
             <div class="formImage">
               <img class="w-full h-full" src="/src/assets/image/more.png">
@@ -561,9 +561,9 @@ onActivated(() => {
 }
 
 .primarys {
-  background: rgba($color: #075985, $alpha: .1);
-  border-color: #075985;
-  color: #075985;
+  background: rgba($color: #0859d6, $alpha: .1);
+  border-color: #0859d6;
+  color: #0859d6;
 }
 
 .dangers {

+ 236 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/index copy.vue

@@ -0,0 +1,236 @@
+<template>
+  <Page styleReset="headerClass">
+    <template v-slot:headerLeft>
+      <div class="homeheaderleft">
+        <img src="/src/assets/image/home_logo.png">
+        <div class="text-white">客户管家</div>
+      </div>
+    </template>
+    <template v-slot:headerRight>
+      <div class="newButton" @click="showModule = true">+ 新建</div>
+    </template>
+
+    <template v-slot:body>
+      <div class="w-full h-full flex flex-col overflow-hidden">
+        <!-- 头部 -->
+        <div class="custom-tabs rounded-b-lg z-10">
+          <!-- <div class="custom-tabs-box text-size-in custom-tabs-rad flex items-center overflow-hidden">
+            <div :class="`w-1/2 h-full rounded-r-lg items-justify-center ${homepageType == 'workbench' ? 'themeTextColor bg-white font-bold' : ''}`" @click="homepageType = 'workbench'">工作台</div>
+            <div :class="`w-1/2 h-full rounded-l-lg items-justify-center ${homepageType == 'dataAnalysis' ? 'themeTextColor bg-white font-bold' : ''}`" @click="homepageType = 'dataAnalysis'">数据分析</div>
+          </div> -->
+          <div class="flex items-center">
+            <div :class="`mr-6 pb-2 relative ${homepageType == 'workbench' ? 'tabAction' : ''}`" @click="homepageType = 'workbench'">工作台</div>
+            <div :class="`mr-6 pb-2 relative ${homepageType == 'dataAnalysis' ? 'tabAction' : ''}`" @click="homepageType = 'dataAnalysis'">数据分析</div>
+          </div>
+        </div>
+        <!-- 内容 -->
+        <div class="flex-1 overflow-y-auto relative aopiuyt">
+            <Workbench v-if="homepageType == 'workbench'" />
+            <DataAnalysis v-if="homepageType == 'dataAnalysis'" />
+        </div>
+      </div>
+      
+
+
+      <!-- 显示对应的模块 -->
+      <van-popup v-model:show="showModule" closeable position="bottom" round>
+        <div class="newModuleAdded relative">
+          <div class="text-size-large text-[#474A56] absolute topTitle">客户管家</div>
+          <div class="flex flex-wrap">
+            <template v-for="(item) in moduleList" :key="item.id">
+              <div class="w-16 flex flex-col items-center mrSpacing" @click.stop="toAddEditor(item)">
+                <div class="newModuleImage">
+                  <img class="w-full h-full" :src="returnImageAddress(item)" alt="">
+                </div>
+                <div class="mt-3 text-[#474A56]">{{ item.name }}</div>
+              </div>
+            </template>
+          </div>
+        </div>
+      </van-popup>
+    </template>
+
+    <template v-slot:footer>
+      <Footer />
+    </template>
+  </Page>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import { useLifecycle } from "@hooks/useCommon.js";
+import useInfoStore from "@store/useInfoStore"
+import useRouterStore from "@store/useRouterStore.js";
+import { routingInfos } from "@utility/generalVariables.js";
+import Footer from "@components/page/footer.vue";
+import Workbench from "./component/workbench.vue";
+import DataAnalysis from "./component/dataAnalysis.vue";
+import requests from "@common/requests"
+
+const userInfo = useInfoStore()
+const modulListss = userInfo.modularList.filter(item => item.path != '/biReport')
+const router = useRouterStore()
+const homepageType = ref('workbench')
+const showModule = ref(false)
+const moduleList = ref(modulListss)
+
+function toAddEditor(rows) {
+  const jumpTo = routingInfos[rows.path.replace('/', '')]
+  router.navigateTo({
+    pathName: 'addEditor',
+    success: () => {
+      router.emit('addEditorParameter', {
+        routerInfo: JSON.stringify(jumpTo)
+      })
+    }
+  })
+}
+
+function returnImageAddress(rows) {
+  const row = routingInfos[rows.path.replace('/', '')]
+  return row.homeImage
+}
+
+function obtainEnterpriseWeChatParameters(data = {}) {
+  const token = data.id
+  // const curUrl = window.location.href.split('home')[0]
+  const curUrl = window.location.href
+  requests.post('/wxcorp/getCorpWXConfig', { url: curUrl, token }).then((res) => {
+    wx.config({
+      beta: true,
+      debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
+      appId: res.data.appid, // 必填,公众号的唯一标识 
+      timestamp: res.data.timestamp, // 必填,生成签名的时间戳 
+      nonceStr: res.data.noncestr, // 必填,生成签名的随机串 
+      signature: res.data.sign, // 必填,签名,见附录1 
+      jsApiList: ['chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'previewFile', 'getLocation', 'agentConfig']
+    })
+
+    wx.ready(function () {
+      // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
+      requests.post('/wxcorp/getCorpWXAgentConfig', { url: curUrl, token }).then((res) => {
+        console.log(res, '<====== 返回的参数 /wxcorp/getCorpWXAgentConfig')
+        wx.agentConfig({
+          corpid: res.data.corpid, // 必填,企业微信的corpid,必须与当前登录的企业一致
+          agentid: res.data.agentid, // 必填,企业微信的应用id (e.g. 1000247)
+          timestamp: res.data.timestamp, // 必填,生成签名的时间戳
+          nonceStr: res.data.nonceStr, // 必填,生成签名的随机串
+          signature: res.data.signature, // 必填,签名,见附录-JS-SDK使用权限签名算法
+          jsApiList: ['selectExternalContact', 'openThirdAppServiceChat', 'openAppManage'], //必填,传入需要使用的接口名称
+          success: function (result) {
+            //  wx.agentConfig成功回调后,WWOpenData 才会注入到 window 对象上面
+            window.WWOpenData.bind(document.querySelector('ww-open-data'))
+          },
+          fail: function (res) {
+            if (res.errMsg.indexOf('function not exist') > -1) {
+              alert('版本过低请升级')
+            }
+          },
+        })
+      }).catch(err => {
+        if (err.errMsg.indexOf('function not exist') > -1) {
+          alert('版本过低请升级')
+        }
+      })
+    })
+    wx.error(function (res) {
+      // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
+      // alert('wxConfig发生异常:'+JSON.stringify(res));
+      // 企业第一次授权安装进入后会报not in reliable domain的错误,刷新后正常
+      // location.reload();
+    });
+  }).catch(err => {
+    alert(err);
+  })
+}
+
+useLifecycle({
+  load: () => {
+
+  },
+  init: () => {
+    const currentEnvironment = navigator.userAgent.toLowerCase();
+    const isCorpWX = currentEnvironment.indexOf("wxwork") > 0 ? true : false
+    if(isCorpWX) {
+      obtainEnterpriseWeChatParameters(userInfo.userInfo)
+    }
+  }
+});
+
+
+</script>
+
+<style lang="scss" scoped>
+:deep(.van-tabs__content) {
+  flex: 1;
+  overflow-y: auto;
+}
+:deep(.van-tabs__wrap) {
+  background-color: $themeColor;
+}
+.homeheaderleft {
+  font-size: 20px;
+  font-weight: bold;
+  display: flex;
+  align-items: center;
+  img {
+    width: 30px;
+  }
+  div {
+    margin-left: 10px;
+  }
+}
+.newButton {
+  width: 86px;
+  line-height: 30px;
+  background: #fff;
+  color: $themeColor;
+  border-radius: 75px;
+}
+ 
+.custom-tabs {
+  background: $themeColor;
+  padding: 22px 15px;
+  color: #fff;
+}
+
+.custom-tabs-box {
+  width: 100%;
+  border: 1.5px solid #fff; 
+  height: 50px;
+}
+.custom-tabs-rad {
+  border-radius: 6px;
+}
+.aopiuyt {
+  margin-top: -6px;
+}
+.newModuleAdded {
+  padding: 45px 25px 45px 25px;
+}
+
+.newModuleImage {
+  width: 50px;
+  height: 50px;
+  border-radius: 10px;
+}
+.mrSpacing {
+  margin-top: 16px;
+  margin-right: 33px;
+  &:nth-child(4n) {
+    margin-right: 0;
+  }
+}
+.topTitle {
+  top: 14px;
+}
+.tabAction::after {
+  content: '';
+  position: absolute;
+  width: 100%;
+  height: 2px;
+  background: #fff;
+  bottom: 0;
+  left: 0;
+}
+</style>

+ 237 - 99
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/home/index.vue

@@ -6,48 +6,78 @@
         <div class="text-white">客户管家</div>
       </div>
     </template>
-    <template v-slot:headerRight>
-      <div class="newButton" @click="showModule = true">+ 新建</div>
-    </template>
 
     <template v-slot:body>
-      <div class="w-full h-full flex flex-col overflow-hidden">
-        <!-- 头部 -->
-        <div class="custom-tabs rounded-b-lg z-10">
-          <!-- <div class="custom-tabs-box text-size-in custom-tabs-rad flex items-center overflow-hidden">
-            <div :class="`w-1/2 h-full rounded-r-lg items-justify-center ${homepageType == 'workbench' ? 'themeTextColor bg-white font-bold' : ''}`" @click="homepageType = 'workbench'">工作台</div>
-            <div :class="`w-1/2 h-full rounded-l-lg items-justify-center ${homepageType == 'dataAnalysis' ? 'themeTextColor bg-white font-bold' : ''}`" @click="homepageType = 'dataAnalysis'">数据分析</div>
-          </div> -->
-          <div class="flex items-center">
-            <div :class="`mr-6 pb-2 relative ${homepageType == 'workbench' ? 'tabAction' : ''}`" @click="homepageType = 'workbench'">工作台</div>
-            <div :class="`mr-6 pb-2 relative ${homepageType == 'dataAnalysis' ? 'tabAction' : ''}`" @click="homepageType = 'dataAnalysis'">数据分析</div>
+      <div class="home-theContent">
+        <!-- 固定头部 -->
+        <div class="home-theContent-title">
+          <div class="item" v-for="(item, index) in addQuickList" :key="index">
+            <div class="item-img" @click="jumpAdd(item)">
+              <img :src="getRouterImg(item.path)" alt="">
+            </div>
+            <div class="item-text">建{{ item.name }}</div>
           </div>
         </div>
-        <!-- 内容 -->
-        <div class="flex-1 overflow-y-auto relative aopiuyt">
-            <Workbench v-if="homepageType == 'workbench'" />
-            <DataAnalysis v-if="homepageType == 'dataAnalysis'" />
-        </div>
-      </div>
-      
-
-
-      <!-- 显示对应的模块 -->
-      <van-popup v-model:show="showModule" closeable position="bottom" round>
-        <div class="newModuleAdded relative">
-          <div class="text-size-large text-[#474A56] absolute topTitle">客户管家</div>
-          <div class="flex flex-wrap">
-            <template v-for="(item) in moduleList" :key="item.id">
-              <div class="w-16 flex flex-col items-center mrSpacing" @click.stop="toAddEditor(item)">
-                <div class="newModuleImage">
-                  <img class="w-full h-full" :src="returnImageAddress(item)" alt="">
-                </div>
-                <div class="mt-3 text-[#474A56]">{{ item.name }}</div>
-              </div>
-            </template>
+        <!-- 可以滚动 -->
+        <div class="rollingLayout">
+          <!-- 模块配置 -->
+          <div class="layoutOfEachModule">
+            <div class="item" v-for="(item, index) in addCommonlyUsedList" :key="index" @click="jumpList(item)">
+              <div class="item-img"><img :src="getRouterImg(item.path)" alt=""></div>
+              <div class="item-text">{{ item.name }}</div>
+            </div>
+            <div class="item" @click="showBottom = true">
+              <div class="item-img"></div>
+              <div class="item-text">配置</div>
+            </div>
+          </div>
+
+          <!-- 销售简报、数据汇总、商机阶段 -->
+          <DataAnalysis></DataAnalysis>
+
+          <!-- 常用联系人 -->
+          <div class="mt-3">
+            <div class="text-size-large text-[#000] pl16">常用联系人</div>
+            <div class="p16 pt-0 pb-0">
+              <template v-if="topContactsList && topContactsList.length > 0">
+                <template v-for="item in topContactsList">
+                  <div class="flex flex-row items-center rounded-md p-4 bg-white mb-5" @click="toContactDetails(item)">
+                    <div class="contactImage">
+                      <img class="w-full h-full" src="/src/assets/image/topContacts.png">
+                    </div>
+                    <div class="flex-1">{{ item.name }}</div>
+                    <div class="rightArrow">
+                      <van-icon name="arrow" />
+                    </div>
+                  </div>
+                </template>
+              </template>
+              <template v-else>
+                <van-empty description="暂无常用联系人" />
+              </template>
+            </div>
           </div>
         </div>
-      </van-popup>
+
+        <!-- 弹出层 -->
+        <van-popup v-model:show="showBottom" position="bottom">
+          <div class="bg-[#eceeef]">
+            <div class="w-full text-center bg-white leading-10" @click="quickListShow = true">快捷新建设置</div>
+            <div class="w-full text-center bg-white leading-10 biankuang" @click="modulesListShow = true">常用功能设置</div>
+            <div class="w-full text-center mt-2 text-[red] bg-white leading-10" @click="showBottom = false">取消</div>
+          </div>
+        </van-popup>
+
+        <!-- 快捷入口设置 -->
+        <van-popup v-model:show="quickListShow" position="bottom" :style="{ height: '100%' }" closeable>
+          <QuickEntrance :allModulesList="allModulesList" :selectModule="addQuickList" @save-value="refreshData" />
+        </van-popup>
+
+        <!-- 常用功能设置 -->
+        <van-popup v-model:show="modulesListShow" position="bottom" :style="{ height: '100%' }" closeable>
+          <CommonFunctions :allModulesList="allModulesList" :selectModule="addCommonlyUsedList" @save-value="refreshData" />
+        </van-popup>
+      </div>
     </template>
 
     <template v-slot:footer>
@@ -60,21 +90,65 @@
 import { ref } from "vue";
 import { useLifecycle } from "@hooks/useCommon.js";
 import useInfoStore from "@store/useInfoStore"
-import useRouterStore from "@store/useRouterStore.js";
-import { routingInfos } from "@utility/generalVariables.js";
 import Footer from "@components/page/footer.vue";
-import Workbench from "./component/workbench.vue";
-import DataAnalysis from "./component/dataAnalysis.vue";
 import requests from "@common/requests"
+import { GET_FREQUENTLY_USED_CONTACTS, GET_FAST_ACCESS_LIST, GET_ALL_ROUTING_PARAMETERS, COMMON_FUNCTIONS } from "@hooks/useApi";
+import useToast from "@hooks/useToast"
+import { routingInfos, getRouterImg } from "@utility/generalVariables.js";
+import useRouterStore from "@store/useRouterStore.js";
+
+// 相关页面
+import DataAnalysis from './component/dataAnalysis.vue';
+import QuickEntrance from './component/quickEntrance.vue'
+import CommonFunctions from './component/commonFunctions.vue'
 
-const userInfo = useInfoStore()
-const modulListss = userInfo.modularList.filter(item => item.path != '/biReport')
+const { toastText, toastSuccess, toastFail, toastLoading } = useToast()
 const router = useRouterStore()
-const homepageType = ref('workbench')
-const showModule = ref(false)
-const moduleList = ref(modulListss)
+const useInfo = useInfoStore()
+const user = useInfo.userInfo
 
-function toAddEditor(rows) {
+const showBottom = ref(false)
+const quickListShow = ref(false)
+const modulesListShow = ref(false)
+const topContactsList = ref([])
+const allModulesList = [...useInfo.modularList.filter(item => item.path !== '/biReport'), { id: -1, path: '/visitor', name: '访客计划' }]
+const addQuickList = ref([])
+const addCommonlyUsedList = ref([])
+
+function getAllData() {
+  getFrequentlyUsedContacts()
+  getQuickList()
+  getCommonUseList()
+}
+
+// 获取当前公司的常用功能列表
+function getCommonUseList() {
+  requests.post(COMMON_FUNCTIONS, {}).then((res) => {
+    addCommonlyUsedList.value = res.data.map(item => { return { ...item, path: item.router } })
+  })
+}
+
+// 获取快捷新建列表
+function getQuickList() {
+  requests.post(GET_FAST_ACCESS_LIST, {}).then((res) => {
+    addQuickList.value = res.data.map(item => { return { ...item, path: item.router } })
+  })
+}
+
+function getFrequentlyUsedContacts() {
+  requests.post(GET_FREQUENTLY_USED_CONTACTS, {}).then((res) => {
+    topContactsList.value = res.data
+  })
+}
+
+function jumpAdd(rows) {
+  if(rows.path == '/addEditorVisitor') {
+    router.navigateTo({
+      pathName: 'addEditorVisitor',
+      success: () => {}
+    })
+    return
+  }
   const jumpTo = routingInfos[rows.path.replace('/', '')]
   router.navigateTo({
     pathName: 'addEditor',
@@ -86,9 +160,31 @@ function toAddEditor(rows) {
   })
 }
 
-function returnImageAddress(rows) {
-  const row = routingInfos[rows.path.replace('/', '')]
-  return row.homeImage
+function jumpList(item) {
+  if(item.path == '/visitor') {
+    router.navigateTo({
+      pathName: 'visitor',
+      success: () => {}
+    })
+    return
+  }
+  const jumpTo = routingInfos[item.path.replace('/', '')]
+  router.navigateTo({
+    pathName: 'moduleList',
+    success: () => {
+      router.emit('moduleListDetailParameter', {
+        row: JSON.stringify(jumpTo)
+      })
+    }
+  })
+}
+
+function refreshData() {
+  getQuickList()
+  getCommonUseList()
+  showBottom.value = false
+  quickListShow.value = false
+  modulesListShow.value = false
 }
 
 function obtainEnterpriseWeChatParameters(data = {}) {
@@ -146,12 +242,12 @@ function obtainEnterpriseWeChatParameters(data = {}) {
 
 useLifecycle({
   load: () => {
-
+    getAllData()
   },
   init: () => {
     const currentEnvironment = navigator.userAgent.toLowerCase();
     const isCorpWX = currentEnvironment.indexOf("wxwork") > 0 ? true : false
-    if(isCorpWX) {
+    if (isCorpWX) {
       obtainEnterpriseWeChatParameters(userInfo.userInfo)
     }
   }
@@ -165,72 +261,114 @@ useLifecycle({
   flex: 1;
   overflow-y: auto;
 }
+
 :deep(.van-tabs__wrap) {
   background-color: $themeColor;
 }
+
 .homeheaderleft {
   font-size: 20px;
   font-weight: bold;
   display: flex;
   align-items: center;
+
   img {
     width: 30px;
   }
+
   div {
     margin-left: 10px;
   }
 }
-.newButton {
-  width: 86px;
-  line-height: 30px;
-  background: #fff;
-  color: $themeColor;
-  border-radius: 75px;
-}
- 
-.custom-tabs {
-  background: $themeColor;
-  padding: 22px 15px;
-  color: #fff;
-}
 
-.custom-tabs-box {
-  width: 100%;
-  border: 1.5px solid #fff; 
-  height: 50px;
-}
-.custom-tabs-rad {
-  border-radius: 6px;
-}
-.aopiuyt {
-  margin-top: -6px;
-}
-.newModuleAdded {
-  padding: 45px 25px 45px 25px;
-}
+.home-theContent {
+  display: flex;
+  height: 100%;
+  flex-direction: column;
 
-.newModuleImage {
-  width: 50px;
-  height: 50px;
-  border-radius: 10px;
-}
-.mrSpacing {
-  margin-top: 16px;
-  margin-right: 33px;
-  &:nth-child(4n) {
-    margin-right: 0;
+  .home-theContent-title {
+    padding: 25px;
+    background-color: $themeColor;
+    display: flex;
+
+    .item {
+      position: relative;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      width: 25%;
+
+      &::after {
+        content: '';
+        width: 1px;
+        height: 30px;
+        background-color: rgba($color: #fff, $alpha: 0.5);
+        position: absolute;
+        left: 0px;
+        top: 0%;
+      }
+
+      &:first-child::after {
+        width: 0;
+      }
+
+      .item-img {
+        width: 24px;
+        height: 24px;
+
+        img {
+          width: 100%;
+          height: 100%;
+        }
+      }
+
+      .item-text {
+        color: #fff;
+        font-size: 14px;
+        margin-top: 8px;
+      }
+    }
+  }
+
+  .rollingLayout {
+    flex: 1;
+    overflow-y: auto;
+    margin: 20px 0;
+    padding: 0 20px;
+    border-radius: 10px;
+
+    .layoutOfEachModule {
+      display: flex;
+      flex-wrap: wrap;
+
+      .item {
+        width: 20%;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        margin-bottom: 20px;
+
+        .item-img {
+          width: 36px;
+          height: 36px;
+
+          img {
+            width: 100%;
+            height: 100%;
+          }
+        }
+
+        .item-text {
+          color: #474A56;
+          font-size: 14px;
+          margin-top: 8px;
+        }
+      }
+    }
   }
 }
-.topTitle {
-  top: 14px;
-}
-.tabAction::after {
-  content: '';
-  position: absolute;
-  width: 100%;
-  height: 2px;
-  background: #fff;
-  bottom: 0;
-  left: 0;
+
+.biankuang {
+  border-top: 1px solid #eceeef;
 }
 </style>

+ 2 - 2
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/tabbar/my/index copy.vue

@@ -13,9 +13,9 @@
             <div class="text-center">公司: {{ userInfo.userInfo?.company?.companyName }}</div>
             <div class="text-center">角色: {{ userInfo.userInfo?.roleName }}</div>
 
-            <div class="text-center text-[#075985] mt-2" @click="applicationMarket()">应用市场</div>
+            <div class="text-center text-[#0859d6] mt-2" @click="applicationMarket()">应用市场</div>
 
-            <div class="text-center text-[#075985] mt-8"
+            <div class="text-center text-[#0859d6] mt-8"
               v-if="userInfo.userInfo.userNameNeedTranslate != '1' && (isCorpWX || isWX)" @click="bindWeiXin">
               {{ `绑定${isCorpWX ? '企业' : ''}微信` }}
               (

+ 28 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/visitor/index.vue

@@ -0,0 +1,28 @@
+<template>
+  <Page title="访客计划" styleReset="backNone">
+    <template v-slot:body>
+      <div class="h-full">
+        <Workbench />
+      </div>
+    </template>
+  </Page>
+</template>
+
+<script setup>
+import { ref } from "vue";
+import { useLifecycle } from "@hooks/useCommon.js";
+
+import Workbench from "./workbench.vue";
+
+useLifecycle({
+  load: () => {
+    console.log('load')
+  },
+  init: () => {
+    console.log('init')
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+</style>

+ 632 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/visitor/workbench.vue

@@ -0,0 +1,632 @@
+<template>
+  <div class="w-full h-full workbench">
+    <!-- 日历 -->
+    <div class="w-full p16 backgroundThemeColor rounded-b-lg setCaleStrle">
+      <van-calendar ref="calendarRef" :default-date="(new Date(dateConditions))" switch-mode="year-month"
+        :show-title="false" :show-mark="false" :poppable="false" :show-confirm="false" :row-height="'3rem'"
+        :min-date="minDate"
+        :style="{ borderRadius: '0.3rem', height: expandAndCollapse ? (calendarHeight ? calendarHeight + 'px' : 'auto') : usePxToVwView(140) }"
+        :formatter="formatter" @select="calendarSelect" @panel-change="calendarPanelChangeSet">
+        <template #month-title></template>
+        <template #bottom-info="day">
+          <div class="flex justify-items-center">
+            <div class="doT" v-if="day?.bottomInfo"></div>
+            <!-- <div class="taskDot" v-if="day?.taskInfo"></div> -->
+          </div>
+        </template>
+      </van-calendar>
+      <div class="flex justify-center" @click="expandAndCollapseClick">
+        <van-icon name="arrow-double-left" color="#fff" :size="usePxToVwView(20)" class="mt-3 expandAndCollapseIcon"
+          :style="expandAndCollapse ? 'transform: rotate(90deg);' : 'transform: rotate(-90deg);'" />
+      </div>
+    </div>
+
+    <!-- 日程安排 -->
+    <div class="min-h-52 overflow-y-auto mt-5">
+      <!-- 有数据的情况 -->
+      <div class="w-full h-full flex flex-col items-center" v-if="visitorProgramList.length">
+        <div class="w-full overflow-y-auto max-h-72 px-5 mb-5">
+          <template v-for="item in visitorProgramList">
+            <van-swipe-cell>
+              <!-- 拜访计划 -->
+              <template v-if="item.calendarType == 1">
+                <div class="bg-[#0859d6] ra6 p-4 mb-4 text-[#fff]" @click="jumpToVisitorDetails(item)">
+                  <div class="w-full flex items-center justify-between">
+                    <div class="text-size-in font-bold text-[#fff]">{{ item.planName }}</div>
+                    <div :class="`labelTag ${item.finishState == 0 ? 'toBeCompleted' : 'completed'}`">{{ ['未完成',
+                      '已完成'][item.finishState] }}</div>
+                  </div>
+                  <div class="w-full flex items-center justify-between mt-4">
+                    <div class="text-[#fff]" style="width: 62%;">拜访目的: {{ item.visitGoalName }}</div>
+                    <div class="w-1/3 text-[#fff] flex items-center" style="width: 38%;">
+                      <van-icon name="user-circle-o" class="text-size-in mr-2" />
+                      {{ item.customName }}
+                    </div>
+                  </div>
+                  <div class="w-full flex items-center justify-between mt-4">
+                    <div class="w-2/3 text-[#fff]" style="width: 62%;">拜访时间: {{ item.visitTime }}</div>
+                    <div class="w-1/3 text-[#fff] flex items-center" style="width: 38%;">
+                      <van-icon name="phone-o" class="text-size-in mr-2" />
+                      {{ item?.companyPhone }}
+                    </div>
+                  </div>
+                </div>
+              </template>
+
+              <!-- 任务 -->
+              <template v-if="item.calendarType == 2 && false">
+                <div class="bg-[#FFA35919] ra6 p-4 mb-4" @click="toEditTask(item)">
+                  <div class="w-full flex items-center justify-between">
+                    <div class="text-size-in font-bold text-[#474A56]">{{ item.taskName }}</div>
+                    <div :class="`labelTag ${taskStatus[item.status]?.type}`">
+                      {{ taskStatus[item.status]?.label }}
+                    </div>
+                  </div>
+                  <div class="w-full flex items-center justify-between mt-4" v-if="user.company.isSimple != 1">
+                    <div class="text-[#505050]" style="width: 100%;">优先级: {{ ['低','中','高'][item.priority] }}</div>
+                  </div>
+                  <div class="w-full flex items-center justify-between mt-4">
+                    <div class="text-[#505050]" style="width: 100%;">任务开始时间: {{ item.startDate }}</div>
+                  </div>
+                  <div class="w-full flex items-center justify-between mt-4">
+                    <div class="text-[#505050]" style="width: 100%;">任务截至时间: {{ item.endDate }}</div>
+                  </div>
+                </div>
+              </template>
+              
+
+              <template #right>
+                <div class="flex items-center flex-col justify-around h-full bg-[#F9F0E9] ra-l6 py-4">
+                  <div class="buttonCircle text-white" @click.stop="jumpToAddNewVisitors(item)" v-if="item.calendarType == 1">
+                    <img src="/src/assets/image/edit.png" class="w-full h-full">
+                  </div>
+                  <div class="buttonCircle text-white" @click.stop="toEditTask(item)" v-if="item.calendarType == 2">
+                    <img src="/src/assets/image/edit.png" class="w-full h-full">
+                  </div>
+                  <div class="buttonCircle text-white" @click.stop="deleteVisitor(item)">
+                    <img src="/src/assets/image/delete.png" class="w-full h-full">
+                  </div>
+                </div>
+              </template>
+            </van-swipe-cell>
+          </template>
+        </div>
+        <!-- <van-button type="primary" icon="add-o" class="m-auto w-3/5" @click="addTaskPlanPopup = true">新增</van-button> -->
+        <van-button type="primary" icon="add-o" class="m-auto w-3/5" @click="jumpToAddNewVisitors()">新增</van-button>
+      </div>
+      <!-- 没有数据的情况下 -->
+      <div class="w-full h-full flex flex-col items-center justify-center" v-if="!visitorProgramList.length">
+        <div class="schedulePicture mb-5">
+          <img class="w-full h-full" src="/src/assets/image/schedule.png">
+        </div>
+        <div class="text-center text-[#C4C4C4] mb-5">您今天还没安排日程哦!</div>
+        <van-button type="primary" class="m-auto w-3/5" @click="addTaskPlanPopup = true">马上安排</van-button>
+      </div>
+    </div>
+
+    <!-- 拜访计划和任务的添加按钮 -->
+    <van-popup v-model:show="addTaskPlanPopup" closeable destroy-on-close position="bottom">
+      <div class="flex w-full flex-col p-12">
+        <van-button type="primary" class="m-auto" @click="addTaskClick()">新增任务</van-button>
+        <div class="h-6"></div>
+        <van-button type="primary" class="m-auto" @click="jumpToAddNewVisitors()">新增计划</van-button>
+      </div>
+    </van-popup>
+
+    <!-- 常用表单 -->
+    <div class="mt-5" v-if="false">
+      <div class="text-size-large text-[#000] pl16">常用表单</div>
+      <div class="p16 pt-0 pb-0 flex justify-between overflow-x-auto">
+        <div class="flex">
+          <template v-for="item in commonModulesList">
+            <div class="w80 bg-[#FFA359] h-28 rounded-md flex flex-col items-center justify-center"
+              @click="jumpToAdd(item)">
+              <div class="formImage">
+                <img class="w-full h-full" src="/src/assets/image/form.png">
+              </div>
+              <div class="text-white">{{ item.moduleName }}</div>
+            </div>
+          </template>
+
+          <div class="w80 bg-[#0859d6] h-28 rounded-md flex flex-col items-center justify-center"
+            @click="showCommonForms = true">
+            <div class="formImage">
+              <img class="w-full h-full" src="/src/assets/image/more.png">
+            </div>
+            <div class="text-white">更多</div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- 常用联系人 -->
+    <div class="mt-3" v-if="false">
+      <div class="text-size-large text-[#000] pl16">常用联系人</div>
+      <div class="p16 pt-0 pb-0">
+        <template v-if="topContactsList && topContactsList.length > 0">
+          <template v-for="item in topContactsList">
+            <div class="flex flex-row items-center rounded-md p-4 bg-white mb-5" @click="toContactDetails(item)">
+              <div class="contactImage">
+                <img class="w-full h-full" src="/src/assets/image/topContacts.png">
+              </div>
+              <div class="flex-1">{{ item.name }}</div>
+              <div class="rightArrow">
+                <van-icon name="arrow" />
+              </div>
+            </div>
+          </template>
+        </template>
+        <template v-else>
+          <van-empty description="暂无常用联系人" />
+        </template>
+      </div>
+    </div>
+
+    <!-- 添加常用表单 -->
+    <van-popup v-model:show="showCommonForms" closeable destroy-on-close position="bottom" :style="{ height: '80%' }">
+      <div class="px-5 flex flex-col h-full py-8">
+        <div class="flex-1 overflow-y-auto">
+          <div class="text-size-large mb-5">已添加表单</div>
+          <div class="flex flex-wrap mb-2">
+            <template v-for="(item) in commonExpressionsHaveBeenAdded" :key="item.id">
+              <div class="w-1/4 flex flex-col items-center mb-4 relative">
+                <div class="newModuleImage">
+                  <img class="w-full h-full" :src="returnImageAddress(item)" alt="">
+                </div>
+                <div class="mt-3 text-[#474A56]">{{ item.name }}</div>
+                <div class="absolute -top-2 right-3" @click="operationForm('delete', item)">
+                  <van-icon name="clear" color="#EE0A24" :size="`${usePxToVwView(20)}`" />
+                </div>
+              </div>
+            </template>
+          </div>
+          <div class="text-size-large mb-5">未添加表单</div>
+          <div class="flex flex-wrap mb-2">
+            <template v-for="(item) in commonExpressionsHaveBeenNodded" :key="item.id">
+              <div class="w-1/4 flex flex-col items-center mb-4 relative">
+                <div class="newModuleImage">
+                  <img class="w-full h-full" :src="returnImageAddress(item)" alt="">
+                </div>
+                <div class="mt-3 text-[#474A56]">{{ item.name }}</div>
+                <div class="absolute -top-2 right-3" @click="operationForm('add', item)">
+                  <van-icon name="add" color="#07C160" :size="`${usePxToVwView(20)}`" />
+                </div>
+              </div>
+            </template>
+          </div>
+        </div>
+        <van-button type="primary" @click="saveCommonlyUsedForms()">保存</van-button>
+      </div>
+    </van-popup>
+  </div>
+</template>
+
+<script setup>
+import { ref, onActivated, nextTick } from 'vue';
+import { showConfirmDialog } from 'vant';
+import { useLifecycle } from '@hooks/useCommon.js';
+import { DELETE_VISITOR_PLAN, GET_VISITOR_PLAN, GET_FREQUENTLY_USED_CONTACTS, GET_COMMONLY_USED_MODULES, SAVE_COMMONLY_USED_FORMS, GET_PLAN_CALENDAR, DELETE_TASK } from "@hooks/useApi";
+import { routingInfos } from "@utility/generalVariables.js";
+import usePxToVwView from "@hooks/usePxTransform.js";
+import useToast from "@hooks/useToast"
+import dayjs from 'dayjs';
+import requests from "@common/requests";
+import useRouterStore from "@store/useRouterStore.js";
+import useInfoStore from "@store/useInfoStore.js";
+
+const taskStatus = [
+  { label: '未开始', type: 'infos' },
+  { label: '进行中', type: 'primarys' },
+  { label: '已完成', type: 'completed' },
+  { label: '已超时', type: 'dangers' },
+]
+
+const router = useRouterStore()
+const useInfo = useInfoStore()
+const user = useInfo.userInfo
+const { toastText, toastSuccess, toastFail, toastLoading } = useToast()
+const expandAndCollapse = ref(true)
+const calendarHeight = ref(0)
+const calendarRef = ref()
+const dateConditions = ref(dayjs().format('YYYY-MM-DD'))
+const minDate = ref(new Date('2024-01-01'))
+const visitorProgramList = ref([])
+const topContactsList = ref([])
+const commonModulesList = ref([])
+const planCalendarList = ref([])
+const showCommonForms = ref(false)
+const commonExpressionsHaveBeenAdded = ref([])
+const commonExpressionsHaveBeenNodded = ref([])
+const areYouRequesting = ref(false)
+const displayFrequentlyUsedContacts = ref(false)
+const addTaskPlanPopup = ref(false)
+
+function addTaskClick() {
+  addTaskPlanPopup.value = false
+  const jumpTo = routingInfos['tasks']
+  router.navigateTo({
+    pathName: 'addEditor',
+    success: () => {
+      router.emit('addEditorParameter', {
+        routerInfo: JSON.stringify(jumpTo)
+      })
+    }
+  })
+}
+
+function toEditTask(row) {
+  const jumpTo = routingInfos['tasks']
+  let newRow = row
+  delete newRow.taskLogs
+  delete newRow.createDate
+  router.navigateTo({
+    pathName: 'addEditor',
+    success: () => {
+      router.emit('addEditorParameter', {
+        routerInfo: JSON.stringify(jumpTo),
+        filedValue: JSON.stringify(row),
+      })
+    }
+  })
+}
+
+function calendarPanelChangeSet(data) {
+  dateConditions.value = dayjs(data.date).format('YYYY-MM-DD')
+  getVisitorPlan()
+  setTimeout(() => {
+    getPlanCalendarList()
+  }, 1)
+}
+
+function jumpToAdd(rows) {
+  const jumpTo = routingInfos[rows.path.replace('/', '')]
+  router.navigateTo({
+    pathName: 'addEditor',
+    success: () => {
+      router.emit('addEditorParameter', {
+        routerInfo: JSON.stringify(jumpTo)
+      })
+    }
+  })
+}
+
+function toContactDetails(item) {
+  router.navigateTo({
+    pathName: 'details',
+    success: () => {
+      router.emit('detailParameter', {
+        routerInfo: JSON.stringify(routingInfos['contacts']),
+        parameter: JSON.stringify(item)
+      })
+    }
+  })
+}
+
+function saveCommonlyUsedForms() {
+  if(commonExpressionsHaveBeenAdded.value.map(item => item.id).length <= 0) {
+    toastText(`最少请选择一个常用表单!`)
+    return
+  }
+  const moduleIds = commonExpressionsHaveBeenAdded.value.map(item => item.id).join(',')
+  requests.post(SAVE_COMMONLY_USED_FORMS, { moduleIds }).then((res) => {
+    toastSuccess('保存成功')
+    getCommonlyUsedModules()
+    showCommonForms.value = false
+  })
+}
+
+function operationForm(type, row) {
+  const itemIndex = commonExpressionsHaveBeenNodded.value.findIndex(item => item.path === row.path);
+  if (type === 'add' && itemIndex !== -1) {
+    const item = commonExpressionsHaveBeenNodded.value.splice(itemIndex, 1)[0];
+    commonExpressionsHaveBeenAdded.value.push(item);
+  }
+
+  if (type === 'delete') {
+    const itemIndex = commonExpressionsHaveBeenAdded.value.findIndex(item => item.path === row.path);
+    if (itemIndex !== -1) {
+      const item = commonExpressionsHaveBeenAdded.value.splice(itemIndex, 1)[0];
+      commonExpressionsHaveBeenNodded.value.push(item);
+    }
+  }
+}
+
+function deleteVisitor(row) {
+  const { id, planName, calendarType, taskName } = row
+  const text = calendarType == 1 ? '访客' : '任务'
+  const textMessage = calendarType == 1 ? '访客计划' : '任务'
+  let formVal = {}
+  calendarType == 1 ? formVal.planId = id : formVal.taskIds = id
+  showConfirmDialog({
+    title: `删除${text}`,
+    message: `确定删除【${calendarType == 1 ? planName : taskName}】${textMessage}计划吗?`,
+  }).then(() => {
+    requests.post(calendarType == 1 ? DELETE_VISITOR_PLAN : DELETE_TASK, { ...formVal }).then((res) => {
+      toastSuccess('删除成功')
+      getVisitorPlan()
+    }).catch((err) => {
+      toastFail(err.msg ? err.msg : '删除失败')
+    })
+  })
+}
+
+function formatter(day) {
+  const { date, bottomInfo } = day
+  const currentDate = dayjs(date).format("YYYY-MM-DD");
+  const rqiList = planCalendarList.value.filter(item => item.ymd === currentDate)
+  day.bottomInfo = (rqiList.length > 0 ? true : false)
+  day.taskInfo = (planCalendarList.value.find(item => item.currentDate === currentDate)?.taskDtoList.length > 0 ? true : false)
+  return day
+}
+
+function calendarSelect(data) {
+  dateConditions.value = dayjs(data).format("YYYY-MM-DD");
+  getVisitorPlan()
+}
+
+function jumpToVisitorDetails(row) {
+  router.navigateTo({
+    pathName: 'visitorDetails',
+    success: () => {
+      router.emit('visitorDetailsParameter', {
+        row: JSON.stringify(row || {})
+      })
+    }
+  })
+}
+
+function jumpToAddNewVisitors(row) {
+  addTaskPlanPopup.value = false
+  router.navigateTo({
+    pathName: 'addEditorVisitor',
+    success: () => {
+      router.emit('addEditorVisitorParameter', {
+        row: JSON.stringify(row || {}),
+        date: JSON.stringify(dateConditions.value || {})
+      })
+    }
+  })
+}
+
+function returnImageAddress(rows) {
+  const row = routingInfos[rows.path.replace('/', '')]
+  return row && row.homeImage
+}
+
+function expandAndCollapseClick() {
+  expandAndCollapse.value = !expandAndCollapse.value
+  const dates = calendarRef.value.getSelectedDate()
+  calendarRef.value.scrollToDate(new Date(dates))
+}
+
+function processForms() {
+  const selectedForm = commonModulesList.value
+  const allFormList = useInfo.modularList || []
+
+  commonExpressionsHaveBeenAdded.value = allFormList.filter(item =>
+    selectedForm.some(arrItem => arrItem.path === item.path)
+  )
+
+  commonExpressionsHaveBeenNodded.value = allFormList.filter(item =>
+    !selectedForm.some(arrItem => arrItem.path === item.path)
+  ).filter(item => item.path !== '/biReport')
+}
+
+function getVisitorPlan() {
+  requests.post(GET_VISITOR_PLAN, {
+    calenderDate: dayjs(dateConditions.value).format('YYYY-MM-DD')
+  }).then((res) => {
+    // visitorProgramList.value = res.data || []
+    visitorProgramList.value = [
+      ...(res.data.planList || []).map(item => {
+        return {
+          ...item,
+          calendarType: 1,
+        }
+      }),
+      ...(res.data.taskList || []).map(item => {
+        return {
+          ...item,
+          calendarType: 2,
+        }
+      }),
+    ]
+  })
+}
+
+function getFrequentlyUsedContacts() {
+  requests.post(GET_FREQUENTLY_USED_CONTACTS, {}).then((res) => {
+    topContactsList.value = res.data
+  })
+}
+
+function getCommonlyUsedModules() {
+  requests.post(GET_COMMONLY_USED_MODULES, {}).then((res) => {
+    commonModulesList.value = res.data
+    processForms()
+  })
+}
+
+function getPlanCalendarList() {
+  const months = calendarRef.value.getSelectedDate()
+  requests.post(GET_PLAN_CALENDAR, { ym: dayjs(months).format('YYYY-MM') }).then(({ data }) => {
+    // planCalendarList.value = data.planList || []
+    const newData = [
+      ...(data.planList || []).map(item => {
+        return {
+          ...item,
+          calendarType: 1,
+        }
+      }),
+      ...(data.taskList || []).map(item => {
+        return {
+          ...item,
+          calendarType: 2,
+        }
+      }),
+    ]
+    planCalendarList.value = newData
+  })
+}
+
+function getAllData() {
+  if (areYouRequesting.value) {
+    return
+  }
+  displayFrequentlyUsedContacts.value = useInfo.modularList.filter(item => item.path === '/contacts').length
+  areYouRequesting.value = true
+  Promise.all([
+    getPlanCalendarList(),
+    getVisitorPlan(),
+    getCommonlyUsedModules(),
+    getFrequentlyUsedContacts(),
+  ]).finally(() => {
+    areYouRequesting.value = false
+  })
+}
+
+useLifecycle({
+  load: () => {
+    getAllData()
+  },
+  init: () => {
+    getAllData()
+    setTimeout(() => {
+      if (expandAndCollapse.value) {
+        calendarHeight.value = calendarRef.value.$el.offsetHeight
+      }
+    }, 500)
+  }
+});
+
+onActivated(() => {
+
+})
+</script>
+
+<style lang='scss' scoped>
+.p16 {
+  padding: 16px;
+}
+
+.pl16 {
+  padding-left: 16px;
+}
+
+.ra-l6 {
+  border-top-left-radius: 6px;
+  border-top-right-radius: 6px;
+}
+
+.w80 {
+  width: 80px;
+  margin-right: 12px;
+}
+
+.doT {
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  background-color: $themeColor;
+  margin: auto;
+  position: relative;
+  top: 6px;
+}
+
+.taskDot {
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  background-color: #ff7300;
+  margin: auto;
+  position: relative;
+  top: 6px;
+}
+
+.toBeCompleted {
+  background: rgba($color: #F38B3C, $alpha: .1);
+  border-color: #F38B3C;
+  color: #F38B3C;
+}
+
+.completed {
+  background: rgba($color: #07C160, $alpha: .1);
+  border-color: #07C160;
+  color: #07C160;
+}
+
+.infos {
+  background: rgba($color: #909399, $alpha: .1);
+  border-color: #909399;
+  color: #909399;
+}
+
+.primarys {
+  background: rgba($color: #0859d6, $alpha: .1);
+  border-color: #0859d6;
+  color: #0859d6;
+}
+
+.dangers {
+  background: rgba($color: #F56C6C, $alpha: .1);
+  border-color: #F56C6C;
+  color: #F56C6C;
+}
+
+.labelTag {
+  font-size: 10px;
+  padding: 3px 8px;
+  border-radius: 4px;
+  border: 1px solid;
+}
+
+.formImage {
+  width: 24px;
+  height: 24px;
+  margin-bottom: 12px;
+}
+
+.contactImage {
+  width: 29px;
+  height: 29px;
+  border-radius: 50%;
+  margin-right: 12px;
+}
+
+.rightArrow {
+  font-size: 16px;
+}
+
+.schedulePicture {
+  width: 48px;
+  height: 51px;
+}
+
+.buttonCircle {
+  width: 37px;
+  height: 37px;
+  margin: 0 14px;
+}
+
+.newModuleImage {
+  width: 50px;
+  height: 50px;
+  border-radius: 10px;
+}
+
+.expandAndCollapseIcon {
+  transition: 0.5s ease-in-out;
+}
+
+.setCaleStrle :deep(.van-calendar__month-title) {
+  display: none;
+}
+
+.setCaleStrle :deep(.van-calendar__month) {
+  padding: 0.8rem 0 0.5rem 0;
+}
+
+.setCaleStrle :deep(.van-calendar) {
+  transition: 0.5s ease-in-out;
+}
+</style>

+ 1 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/pages/visitorProgram/visitorDetails.vue

@@ -49,7 +49,7 @@
                 <div class="bg-[#F8F8FA] flex justify-between items-center px-6 py-4 mb-3 rounded">
                   <div class="w-3/12">{{ item.name }}</div>
                   <div class="flex-1">{{ item.phone }}</div>
-                  <van-icon name="phone" color="#075985" size="1.4rem" />
+                  <van-icon name="phone" color="#0859d6" size="1.4rem" />
                 </div>
               </a>
             </template>

+ 5 - 0
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/router.js

@@ -47,6 +47,11 @@ const routes = [
         name: 'addEditor',
         meta: { title: '模块新增编辑' },
         component: () => import("@pages/addEditor/index.vue"),
+    },{
+        path: '/visitor',
+        name: 'visitor',
+        meta: { title: '访客计划' },
+        component: () => import("@pages/visitor/index.vue"),
     }, {
         path: '/addEditorVisitor',
         name: 'addEditorVisitor',

+ 15 - 1
fhKeeper/formulahousekeeper/customerBuler-crm-h5/src/utility/generalVariables.js

@@ -179,4 +179,18 @@ export const routingInfos = {
       delete: 'orderDelete'
     }
   }
-}
+}
+
+/**
+ * 获取路由图片
+ * @param {String} routerName 路由名称
+ * @param {String} type 图片类型
+ * @returns {String} 图片地址
+ */
+export const getRouterImg = (routerName, file = 'moduleImageNew') => {
+  if (typeof routerName !== 'string') {
+    return '';
+  }
+  const path = routerName.indexOf('/') !== -1 ? routerName.split('/')[1] : routerName;
+  return routingInfos[path]?.[file] || '';
+};

+ 7 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TimeType.java

@@ -17,7 +17,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2025-04-20
+ * @since 2025-06-04
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -680,6 +680,12 @@ public class TimeType extends Model<TimeType> {
     @TableField("cc_dept_manager")
     private Boolean ccDeptManager;
 
+    /**
+     * 代填不校验考勤
+     */
+    @TableField("not_check_cardtime")
+    private Boolean notCheckCardtime;
+
 
     @TableField(exist = false)
     private List<User> userList;

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

@@ -165,6 +165,10 @@ public class ExcelParserService {
         if (startHour <= 12 && endHour >= 13) {
             hours -= 1;
         }
+        //减去晚休时间,17:30-18:00
+        if (startHour <= 17 && endHour > 18) {
+            hours -= 0.5;
+        }
 
         return hours;
     }

+ 0 - 46
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/DataCollectTask.java

@@ -154,53 +154,7 @@ public class DataCollectTask {
 
                 }
             }
-
         }
-
-//        List<TisTimeVO> timeVOList = fmwDetailMapper.getTisTimeByDateYm(specialCompanyId,dateStr);
-//        List<TisTimeVO> timeVOList =  reportMapper.getTisTimeByDate(specialCompanyId,dateStr);
-
-//        for (TisTimeVO tisTimeVO : timeVOList) {
-//            String sqlQuery = "select top 1 iRealCOID from ca_batchmap where cMOCode = ? and iMOSubSN = ? ";
-//            try (Connection connection = sqlServerDataSource.getConnection()) {
-//                PreparedStatement queryStmt = connection.prepareStatement(sqlQuery);
-//                queryStmt.setString(1,tisTimeVO.getOrderId());
-//                queryStmt.setInt(2,tisTimeVO.getLine());
-//                ResultSet queryRs = queryStmt.executeQuery();
-//                if (queryRs.next()) {
-//                    tisTimeVO.setCoId(queryRs.getString("iRealCOID"));
-//
-//                }
-//            } catch (SQLException e) {
-//                System.err.println("数据库操作错误: " + e.getMessage());
-//            }
-//        }
-
-//        for (TisTimeVO tisTimeVO : timeVOList) {
-//            String sqlQuery = "select top 1 iRealCOID from ca_batchmap where cMOCode = ? and iMOSubSN = ? ";
-//            try (Connection connection = sqlServerDataSource.getConnection()) {
-//                PreparedStatement queryStmt = connection.prepareStatement(sqlQuery);
-//                queryStmt.setString(1,tisTimeVO.getOrderId());
-//                queryStmt.setInt(2,tisTimeVO.getLine());
-//                ResultSet queryRs = queryStmt.executeQuery();
-//                if (queryRs.next()) {
-//                    String iRealCOID = queryRs.getString("iRealCOID");
-//                    String sqlInsert = "insert into CA_DayTiS(cPPID,iRealWkt,dDate) values(?,?,?)";
-//                    PreparedStatement insertStmt = connection.prepareStatement(sqlInsert);
-//                    insertStmt.setString(1,iRealCOID);
-//                    insertStmt.setBigDecimal(2,tisTimeVO.getWorkTime());
-//                    insertStmt.setString(3,dateStr);
-//                    int i = insertStmt.executeUpdate();
-//                    if(i>0){
-//                        System.out.println("执行成功");
-//                    }else{
-//                        System.out.println(iRealCOID+"执行失败");
-//                    }
-//                }
-//            } catch (SQLException e) {
-//                System.err.println("数据库操作错误: " + e.getMessage());
-//            }
-//        }
     }
 
 

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 2 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/TimeTypeMapper.xml


+ 4 - 1
fhKeeper/formulahousekeeper/timesheet/src/views/settings/timetype.vue

@@ -331,7 +331,10 @@
                     </el-form-item>
                 </div>
                 <el-form-item>
-                    <el-checkbox v-model="timeType.stopReport" :label="$t('guanBiRiBaoTianBaoGongNeng')" />  <el-checkbox  v-model="timeType.notAllowedOnNonWorkday" :label="$t('feiGongZuoRiHanZhouMoWeiFaDingGongZuoRiJinZhiTianBao')" @change="onNonWorkDaySet()"/> <el-checkbox :disabled="timeType.notAllowedOnNonWorkday" v-model="timeType.includeWeekends" :label="$t('piLiangDaiTianHanZhouMo')" />
+                    <el-checkbox v-model="timeType.stopReport" :label="$t('guanBiRiBaoTianBaoGongNeng')" />  
+                    <el-checkbox  v-model="timeType.notAllowedOnNonWorkday" :label="$t('feiGongZuoRiHanZhouMoWeiFaDingGongZuoRiJinZhiTianBao')" @change="onNonWorkDaySet()"/> 
+                    <el-checkbox :disabled="timeType.notAllowedOnNonWorkday" v-model="timeType.includeWeekends" :label="$t('piLiangDaiTianHanZhouMo')" />
+                    <el-checkbox v-if="timeType.syncCorpwxTime || timeType.syncDingding || timeType.syncFanwei" v-model="timeType.notCheckCardtime" label="代填不校验考勤" />
                 </el-form-item><br/>
                 <el-form-item :label="$t('filltime')" prop="fillMonths">
                     <el-select v-model="timeType.fillMonths" >

+ 1 - 1
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

@@ -8605,7 +8605,7 @@
                         }
                         //批量填报不校验考勤时长,交给后台校验
                         if (!this.isBatch) {
-                            if(this.user.timeType.notAllowedNoAttendance == 1 && this.isDraft == 0){
+                            if(this.user.timeType.notAllowedNoAttendance == 1 && this.isDraft == 0 && !(this.isSubstitude && this.user.timeType.notCheckCardtime)){
                                 if (this.workForm.time){
                                     if (this.workForm.time.workHours == 0){
                                         this.$message({