Kaynağa Gözat

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	doc/待更新脚本.txt
liao-sea 5 ay önce
ebeveyn
işleme
09a92f20a6
37 değiştirilmiş dosya ile 1497 ekleme ve 136 silme
  1. 5 0
      doc/待更新脚本.txt
  2. 24 2
      h5app/src/App.vue
  3. 11 0
      h5app/src/api/position/index.ts
  4. 1 0
      h5app/src/assets/fonts/loading.svg
  5. 4 1
      h5app/src/components/bImage.vue
  6. 139 0
      h5app/src/components/companySelect.vue
  7. 2 1
      h5app/src/components/empty.vue
  8. 139 0
      h5app/src/components/jobUserSelect.vue
  9. 117 25
      h5app/src/views/pages/work/signin/index.vue
  10. 3 3
      h5app/src/views/sapp/tabMain.vue
  11. 2 2
      h5app/src/views/sapp/tabWork.vue
  12. 1 1
      h5app/vue.config.js
  13. 16 0
      src/main/java/com/hz/employmentsite/controller/LongitudeLatitudeController.java
  14. 12 3
      src/main/java/com/hz/employmentsite/controller/baseSettings/SiteUserController.java
  15. 12 0
      src/main/java/com/hz/employmentsite/controller/companyService/CompanyController.java
  16. 8 0
      src/main/java/com/hz/employmentsite/mapper/cquery/CompanyCQuery.java
  17. 21 0
      src/main/java/com/hz/employmentsite/mapper/cquery/UserInfoCQuery.java
  18. 39 0
      src/main/java/com/hz/employmentsite/services/impl/LongitudeLatitudeServiceImpl.java
  19. 34 0
      src/main/java/com/hz/employmentsite/services/impl/baseSettings/SiteUserImpl.java
  20. 12 0
      src/main/java/com/hz/employmentsite/services/impl/companyService/CompanyServiceImpl.java
  21. 3 0
      src/main/java/com/hz/employmentsite/services/service/LongitudeLatitudeService.java
  22. 8 0
      src/main/java/com/hz/employmentsite/services/service/baseSettings/SiteUserService.java
  23. 8 0
      src/main/java/com/hz/employmentsite/services/service/companyService/CompanyService.java
  24. 10 0
      src/main/java/com/hz/employmentsite/vo/dataMap/CompanyPostMapVo.java
  25. 5 0
      src/main/java/com/hz/employmentsite/vo/dataMap/SiteUserMapVo.java
  26. 34 0
      src/main/java/com/hz/employmentsite/vo/signin/SigninVo.java
  27. 24 1
      src/main/resources/mapping/cquery/CompanyCQuery.xml
  28. 60 0
      src/main/resources/mapping/cquery/SiteUserCQuery.xml
  29. 13 0
      vue/src/api/baseSettings/userInfo.ts
  30. 13 0
      vue/src/api/companyService/company.ts
  31. BIN
      vue/src/assets/images/greenTh.png
  32. 10 1
      vue/src/router/outsideLayout.ts
  33. 2 0
      vue/src/utils/imageUtils.ts
  34. 198 83
      vue/src/views/dataMap/companyDataMap.vue
  35. 10 8
      vue/src/views/dataMap/jobUserDataMap.vue
  36. 5 5
      vue/src/views/dataMap/siteDataMap.vue
  37. 492 0
      vue/src/views/dataMap/siteUserWorkDataMap.vue

+ 5 - 0
doc/待更新脚本.txt

@@ -35,6 +35,11 @@ alter table pc_signin add constraint FK_Pc_Signin_Ref_Site foreign key (SiteID)
  ('b100cbf2-538d-11ef-8d63-7085c2a9999e', '', 'SigninType', 2, '走访人员', 2, 1, 0),
  ('b4ec8c51-538d-11ef-8d63-7085c2a9999e', '', 'SigninType', 3, '其他外联事项', 3, 1, 0);
 
+ -- 2024-08-06 驿站工作人员外出地图菜单
+ INSERT INTO sys_function_code VALUES ('T011004', '驿站工作人员外出地图', 'T0110', 4);
+ INSERT INTO `sys_menu` VALUES ('T011004', 4, '驿站工作人员外出地图', NULL, 'views/dataMap/siteUserWorkDataMap', '/siteUserWorkDataMap', 'T0110', NULL, 0, 1, 1, 'T011004', 1, NULL, 1);
+ insert into sys_role_sys_function_code (`RoleID`, `FunctionCode`) values('20afde90-a81a-11ed-a6c5-7085c2a9999e','T011004');
+
 
 
 INSERT INTO  `sys_function_code` (`FunctionCode`, `FunctionName`, `ParentFunctionCode`, `OrderNo`) VALUES ('T010305', '外出服务管理', 'T0103', 5); INSERT INTO  `sys_function_code` (`FunctionCode`, `FunctionName`, `ParentFunctionCode`, `OrderNo`) VALUES ('T01030501', '查看', 'T010305', 1);

+ 24 - 2
h5app/src/App.vue

@@ -301,7 +301,7 @@ ion-grid {
       --padding-start: 0px;
     }
 
-    ion-textarea{
+    ion-textarea {
       border-bottom: 1px solid #f1f5f7;
       color: #8c8f93;
       --padding-start: 0px;
@@ -411,7 +411,7 @@ ion-grid {
 }
 
 
-.cascade-model{
+.cascade-model {
   --height: 50%;
   --border-radius: 16px;
   --box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
@@ -477,6 +477,7 @@ ion-grid {
   }
 
   /* 可以根据需要自定义滚动条的样式 */
+
   .custom-scroll::-webkit-scrollbar {
     width: 8px;
   }
@@ -492,4 +493,25 @@ ion-grid {
 
 }
 
+.b-select {
+  display: flex;
+  justify-content: space-between;
+  border-bottom: 1px solid #f1f5f7;
+  align-items: center;
+
+  .select-placeholder {
+    color: #8c8f93;
+  }
+
+  .select-text {
+    color: #8c8f93;
+  }
+
+  a {
+    color: #02a6f1;
+    font-size: 16px;
+    padding: 10px 0px 10px 16px;
+  }
+}
+
 </style>

+ 11 - 0
h5app/src/api/position/index.ts

@@ -26,4 +26,15 @@ export function getLongitudeLatitudeList(userId: string, startDate: any, endDate
             isNew: true,
         },
     );
+}
+
+export function saveSignin(data: any) {
+    return request(
+        {
+            url: 'longitudeLatitude/saveSignin',
+            method: 'post',
+            data: data,
+        },
+        {isNew: true},
+    );
 }

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
h5app/src/assets/fonts/loading.svg


+ 4 - 1
h5app/src/components/bImage.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="img-list" v-viewer>
     <div class="img-item" v-for="(it,key) in imageList" :key="key">
-      <div v-if="it.blobUrl">
+      <div v-if="it.blobUrl" style="height: 100%;">
         <img :src="it.blobUrl"/>
         <ion-icon v-if="!readonly" :icon="closeCircleOutline" color="danger" class="remove-icon"
                   @click="deleteFile(it)"></ion-icon>
@@ -173,6 +173,8 @@ export default defineComponent({
     width: 25%;
     position: relative;
     padding: 0 5px;
+    margin: 3px 0;
+    height: 73px;
 
     img {
       height: 100%;
@@ -192,6 +194,7 @@ export default defineComponent({
       --box-shadow: 0px;
       height: 73px;
       width: 75px;
+      margin: 0px;
     }
   }
 }

+ 139 - 0
h5app/src/components/companySelect.vue

@@ -0,0 +1,139 @@
+<template>
+  <div class="b-select" @click="onOpen()">
+    <div class="select-text select-placeholder">{{ resultInfo.text ? resultInfo.text : '请选择企业' }}</div>
+    <a @click="onOpen()">选择</a>
+  </div>
+  <ion-modal :is-open="isOpen" @willPresent="onRest()">
+    <ion-header class="header-theme2">
+      <ion-toolbar>
+        <ion-buttons slot="start">
+          <ion-icon :icon="arrowBackOutline" @click="onClose()"></ion-icon>
+        </ion-buttons>
+        <ion-title>选择企业</ion-title>
+        <ion-buttons slot="end">
+          <ion-button fill="clear" @click="onClose()">关闭</ion-button>
+        </ion-buttons>
+      </ion-toolbar>
+    </ion-header>
+    <ion-content class="ion-padding">
+      <ion-item class="search-item" style="--border-style: unset; --inner-padding-end: 0px;--padding-start: 0px;">
+        <ion-input placeholder="请输入企业名称" class="custom"
+                   v-model="searchParams.companyName"
+                   @input="reload()" :clear-on-edit="true"
+                   style="border: 1px solid #f2f2f5;border-radius: 14px;height: 35px;">
+          <ion-icon slot="start" :icon="searchOutline" aria-hidden="true" style="padding: 0 10px;"></ion-icon>
+        </ion-input>
+      </ion-item>
+      <div class="bw-vue-list">
+        <div class="list-content">
+          <ion-list>
+            <ion-item v-for="(record,key) in dataList" :key="key">
+              <ion-label @click="onSelect(record)">
+                <span v-html="formatStr(record.companyName)"></span>
+              </ion-label>
+            </ion-item>
+          </ion-list>
+          <b-empty v-if="dataList.length<=0" :loading="loading"/>
+        </div>
+      </div>
+    </ion-content>
+  </ion-modal>
+</template>
+
+<script lang="ts">
+import {computed, defineComponent, reactive, ref, watch} from 'vue';
+import {arrowBackOutline, searchOutline} from 'ionicons/icons';
+import {IonIcon, onIonViewDidEnter} from '@ionic/vue';
+import BEmpty from "@/components/empty.vue";
+import {getCompanyList} from "@/api/company";
+import {useUserStore} from "@/store/modules/user";
+
+export default defineComponent({
+  name: 'companySelect',
+  components: {IonIcon, BEmpty},
+  props: {},
+  setup(props, context) {
+    const total = ref(30);
+    const loading = ref(false);
+    const pagination = computed(() => ({
+      total: total,
+      current: searchParams.pageIndex,
+      pageSize: searchParams.pageSize
+    }));
+    const searchParams = reactive({
+      pageIndex: 1,
+      pageSize: 30,
+      companyName: '',
+      loginUserID: ''
+    });
+    const isOpen = ref(false);
+    const dataList = ref<any>([]);
+    const resultInfo = reactive({value: '', text: ''})
+
+    const reload = () => {
+      loading.value = true;
+      setTimeout(() => {
+        searchParams.pageIndex = 1;
+        const loginUserInfo = useUserStore().getUserInfo;
+        searchParams.loginUserID = loginUserInfo.userID || '';
+        getCompanyList(searchParams).then(data => {
+          dataList.value = data.list;
+          total.value = data.total;
+          loading.value = false;
+        })
+      }, 300)
+    }
+
+    const onSelect = (item: any) => {
+      resultInfo.text = item.companyName;
+      resultInfo.value = item.companyID;
+      context.emit("resultInfo", resultInfo);
+      onClose();
+    }
+
+    const onOpen = () => {
+      isOpen.value = true;
+    }
+
+    const onClose = () => {
+      isOpen.value = false;
+    };
+
+    const onRest = () => {
+      dataList.value = [];
+      searchParams.companyName = '';
+      searchParams.pageIndex = 1;
+      resultInfo.text = '';
+      resultInfo.value = '';
+
+      reload();
+    }
+
+    const formatStr = (str: any) => {
+      if (!str) {
+        return "";
+      }
+
+      return str.replace(searchParams.companyName, '<span style="color: coral">' + searchParams.companyName + '</span>');
+    }
+
+    return {
+      searchOutline,
+      arrowBackOutline,
+      total,
+      loading,
+      isOpen,
+      dataList,
+      pagination,
+      searchParams,
+      onSelect,
+      onOpen,
+      onClose,
+      reload,
+      onRest,
+      formatStr,
+      resultInfo
+    }
+  }
+});
+</script>

+ 2 - 1
h5app/src/components/empty.vue

@@ -1,6 +1,7 @@
 <template>
   <div class="b-empty">
-    <img class="empty-img" src="@/assets/fonts/empty.svg">
+    <img v-if="!loading" class="empty-img" src="@/assets/fonts/empty.svg">
+    <img v-if="loading" class="empty-img" src="@/assets/fonts/loading.svg">
     <ion-text class="empty-text">{{ loading ? "数据加载中......" : '暂无数据' }}</ion-text>
   </div>
 </template>

+ 139 - 0
h5app/src/components/jobUserSelect.vue

@@ -0,0 +1,139 @@
+<template>
+  <div class="b-select" @click="onOpen()">
+    <div class="select-text select-placeholder">{{ resultInfo.text ? resultInfo.text : '请选择求职人员' }}</div>
+    <a @click="onOpen()">选择</a>
+  </div>
+  <ion-modal :is-open="isOpen" @willPresent="onRest()">
+    <ion-header class="header-theme2">
+      <ion-toolbar>
+        <ion-buttons slot="start">
+          <ion-icon :icon="arrowBackOutline" @click="onClose()"></ion-icon>
+        </ion-buttons>
+        <ion-title>选择人员</ion-title>
+        <ion-buttons slot="end">
+          <ion-button fill="clear" @click="onClose()">关闭</ion-button>
+        </ion-buttons>
+      </ion-toolbar>
+    </ion-header>
+    <ion-content class="ion-padding">
+      <ion-item class="search-item" style="--border-style: unset; --inner-padding-end: 0px;--padding-start: 0px;">
+        <ion-input placeholder="请输入求职人员姓名" class="custom"
+                   v-model="searchParams.name"
+                   @input="reload()" :clear-on-edit="true"
+                   style="border: 1px solid #f2f2f5;border-radius: 14px;height: 35px;">
+          <ion-icon slot="start" :icon="searchOutline" aria-hidden="true" style="padding: 0 10px;"></ion-icon>
+        </ion-input>
+      </ion-item>
+      <div class="bw-vue-list">
+        <div class="list-content">
+          <ion-list>
+            <ion-item v-for="(record,key) in dataList" :key="key">
+              <ion-label @click="onSelect(record)">
+                <span v-html="formatStr(record.name)"></span>
+              </ion-label>
+            </ion-item>
+          </ion-list>
+          <b-empty v-if="dataList.length<=0" :loading="loading"/>
+        </div>
+      </div>
+    </ion-content>
+  </ion-modal>
+</template>
+
+<script lang="ts">
+import {computed, defineComponent, reactive, ref, watch} from 'vue';
+import {arrowBackOutline, searchOutline} from 'ionicons/icons';
+import {IonIcon, onIonViewDidEnter} from '@ionic/vue';
+import BEmpty from "@/components/empty.vue";
+import {getJobUserList} from "@/api/jobUserInfo";
+import {useUserStore} from "@/store/modules/user";
+
+export default defineComponent({
+  name: 'jobuserSelect',
+  components: {IonIcon, BEmpty},
+  props: {},
+  setup(props, context) {
+    const total = ref(30);
+    const loading = ref(false);
+    const pagination = computed(() => ({
+      total: total,
+      current: searchParams.pageIndex,
+      pageSize: searchParams.pageSize
+    }));
+    const searchParams = reactive({
+      pageIndex: 1,
+      pageSize: 30,
+      name: '',
+      loginUserID: ''
+    });
+    const isOpen = ref(false);
+    const dataList = ref<any>([]);
+    const resultInfo = reactive({value: '', text: ''});
+
+    const reload = () => {
+      setTimeout(() => {
+        searchParams.pageIndex = 1;
+
+        loading.value = true;
+        const loginUserInfo = useUserStore().getUserInfo;
+        searchParams.loginUserID = loginUserInfo.userID || '';
+        getJobUserList(searchParams).then(data => {
+          dataList.value = data.list;
+          total.value = data.total;
+          loading.value = false;
+        })
+      }, 300)
+    }
+
+    const onSelect = (item: any) => {
+      resultInfo.text = item.name;
+      resultInfo.value = item.jobUserID;
+      context.emit("resultInfo", resultInfo);
+      onClose();
+    }
+
+    const onOpen = () => {
+      isOpen.value = true;
+    }
+
+    const onClose = () => {
+      isOpen.value = false;
+    };
+
+    const onRest = () => {
+      dataList.value = [];
+      searchParams.name = '';
+      searchParams.pageIndex = 1;
+      resultInfo.text = '';
+      resultInfo.value = '';
+      reload();
+    }
+
+    const formatStr = (str: any) => {
+      if (!str) {
+        return "";
+      }
+
+      return str.replace(searchParams.name, '<span style="color: coral">' + searchParams.name + '</span>');
+    }
+
+    return {
+      searchOutline,
+      arrowBackOutline,
+      total,
+      loading,
+      isOpen,
+      dataList,
+      pagination,
+      searchParams,
+      onSelect,
+      onOpen,
+      onClose,
+      reload,
+      onRest,
+      formatStr,
+      resultInfo
+    }
+  }
+});
+</script>

+ 117 - 25
h5app/src/views/pages/work/signin/index.vue

@@ -5,33 +5,51 @@
         <ion-buttons slot="start">
           <ion-icon :icon="arrowBackOutline()" @click="onBack"></ion-icon>
         </ion-buttons>
-        <ion-title>打卡签到</ion-title>
+        <ion-title>外出走访签到</ion-title>
       </ion-toolbar>
     </ion-header>
     <ion-content>
       <ion-item class="search-item">
         当前时间:{{ dayjs(new Date()).format("YYYY-MM-DD HH:mm") }}
       </ion-item>
-      <div id='signInMap' style=' width: 100%; height: 65%; z-index: 100'></div>
+      <div id='signInMap' style=' width: 100%; height: 200px; z-index: 100'></div>
       <div class="bw-vue-form">
-        <div class="form-detail">
-          <ion-label>经度</ion-label>
-          <ion-text>{{ position.longitude }}</ion-text>
+        <div class="form-select">
+          <ion-label>外出类型<span class="danger">*</span></ion-label>
+          <ion-select interface="action-sheet" placeholder="请选择外出类型" cancel-text="取消"
+                      id="signinType" v-model="dataModel.signinType" @change="signinTypeChange()">
+            <ion-select-option v-for="(record,key) in signinTypeList" :key="key"
+                               v-model:value="record.value">
+              {{ record.name }}
+            </ion-select-option>
+          </ion-select>
         </div>
-        <div class="form-detail">
-          <ion-label>纬度</ion-label>
-          <ion-text>{{ position.latitude }}</ion-text>
+        <div class="form-select" v-if="dataModel.signinType===1">
+          <ion-label>走访企业<span class="danger">*</span></ion-label>
+          <company-select ref="refCompanySelect" @resultInfo="onCompanySelect"></company-select>
+        </div>
+        <div class="form-select" v-if="dataModel.signinType===2">
+          <ion-label>走访人员<span class="danger">*</span></ion-label>
+          <job-user-select ref="refJobuserSelect" @resultInfo="onJobUserSelect"></job-user-select>
+        </div>
+        <div class="form-input">
+          <ion-label>服务内容</ion-label>
+          <ion-textarea v-model="dataModel.content" rows="5" cols="20" placeholder="请填写服务内容"></ion-textarea>
+        </div>
+        <div class="form-input">
+          <ion-label>照片</ion-label>
+          <b-image :file-ref-id="dataModel.signinId" :readonly="false" :is-single="false"></b-image>
         </div>
       </div>
       <div class="btn-box">
-        <ion-button @click="onSave">确定打卡</ion-button>
+        <ion-button @click="onSave">确定签到</ion-button>
       </div>
     </ion-content>
 
   </ion-page>
 </template>
 <script>
-import {defineComponent, reactive, ref} from "vue";
+import {defineComponent, reactive, ref, toRefs} from "vue";
 import {alertController, onIonViewDidEnter} from "@ionic/vue";
 import {useUserStore} from "../../../../store/modules/user";
 import dayjs from 'dayjs';
@@ -39,7 +57,12 @@ import {getPosition} from "@/utils/position";
 import thIcon from "@/assets/icon/th.jpg"
 import {arrowBackOutline} from "ionicons/icons";
 import {useRouter} from "vue-router";
-import {saveLongitudeLatitude} from "@/api/position";
+import {saveSignin} from "@/api/position";
+import CompanySelect from "@/components/companySelect.vue";
+import JobUserSelect from "@/components/jobUserSelect.vue";
+import {getSysDictionaryList} from "../../../../api/system/dictionary";
+import {v4 as uuidv4} from 'uuid';
+import BImage from "@/components/bImage.vue";
 
 const presentAlert = async (header, message) => {
   const alert = await alertController.create({
@@ -55,6 +78,7 @@ const presentAlert = async (header, message) => {
 
 export default defineComponent({
   name: 'signInMap',
+  components: {CompanySelect, JobUserSelect, BImage},
   methods: {
     arrowBackOutline() {
       return arrowBackOutline
@@ -73,6 +97,27 @@ export default defineComponent({
     let map = null;
     let label = null;
     let marker = null;
+    const refCompanySelect = ref();
+    const refJobuserSelect = ref();
+    const formState = reactive({
+      dataModel: {
+        signinId: null,
+        signinType: null,
+        companyID: null,
+        companyName: '',
+        jobuserID: null,
+        longitude: null,
+        latitude: null,
+        content: '',
+        createUserID: userInfo.value.userID
+      }
+    });
+    const signinTypeList = ref([]);
+
+    const getSigninTypeList = async function () {
+      const signinTypeListResult = await getSysDictionaryList("SigninType");
+      signinTypeList.value = signinTypeListResult;
+    }
 
     const setMarker = () => {
       // 删除点
@@ -125,30 +170,77 @@ export default defineComponent({
 
     // 保存
     function onSave() {
-      if (position.latitude && position.longitude) {
-        const sendData = {
-          userId: userInfo.value.userID,
-          time: Date.now(),
-          longitude: position.longitude,
-          latitude: position.latitude,
-        }
-        // 发送请求
-        saveLongitudeLatitude(sendData).then(() => {
-          presentAlert('提示', '签到成功');
-        });
-      } else {
+      if(!(position.latitude && position.longitude)){
         presentAlert('提示', '获取位置信息失败');
+        return false;
       }
+
+      if (!formState.dataModel.signinType) {
+        presentAlert('提示', '请选择打卡类型');
+        return false;
+      }
+
+      if (formState.dataModel.signinType === 1 && !formState.dataModel.companyID) {
+        presentAlert('提示', '请选择走访企业');
+        return false;
+      }
+
+      if (formState.dataModel.signinType === 2 && !formState.dataModel.jobuserID) {
+        presentAlert('提示', '请选择走访人员');
+        return false;
+      }
+
+      formState.dataModel.latitude = position.latitude;
+      formState.dataModel.longitude = position.longitude;
+      // 发送请求
+      saveSignin(formState.dataModel).then(() => {
+        onBack();
+        presentAlert('提示', '签到成功');
+      });
     }
 
+    const onCompanySelect = (data) => {
+      formState.dataModel.companyID = data.value;
+    };
+
+    const onJobUserSelect = (data) => {
+      formState.dataModel.jobuserID = data.value;
+    };
+
+    const signinTypeChange = () => {
+      formState.dataModel.companyID = '';
+      formState.dataModel.jobuserID = '';
+    };
+
+    const resetData = function () {
+      formState.dataModel.signinId = uuidv4();
+      formState.dataModel.companyID = null;
+      formState.dataModel.jobuserID = '';
+      formState.dataModel.signinType = null;
+      formState.dataModel.longitude = null;
+      formState.dataModel.latitude = null;
+      formState.dataModel.content = '';
+    };
+
     onIonViewDidEnter(() => {
+      resetData();
+
       initMap();
+
+      getSigninTypeList();
     });
 
     return {
+      ...toRefs(formState),
+      refCompanySelect,
+      refJobuserSelect,
+      signinTypeList,
       onBack,
       onSave,
-      position
+      onCompanySelect,
+      onJobUserSelect,
+      getSigninTypeList,
+      signinTypeChange
     }
   }
 });
@@ -159,6 +251,6 @@ export default defineComponent({
   width: 100%;
   display: flex;
   justify-content: center;
-  padding-top: 5px;
+  padding: 15px;
 }
 </style>

+ 3 - 3
h5app/src/views/sapp/tabMain.vue

@@ -40,11 +40,11 @@
             <div class="tool-title">岗位信息</div>
           </a>
           <a class="tool-item box-line"
-             @click="saveSysLog('推荐库(APP)', '/tabs/tabMain/recommendMgt/list', '点击菜单', router.push('/tabs/tabMain/recommendMgt/list'))">
+             @click="saveSysLog('走访签到(APP)', '/tabs/tabWork/work/signin', '点击菜单', router.push('/tabs/tabWork/work/signin'))">
             <div class="tool-img">
-              <img src="@/assets/icon/gwtj.png">
+              <img src="@/assets/icon/map.png">
             </div>
-            <div class="tool-title">推荐库</div>
+            <div class="tool-title">走访签到</div>
           </a>
           <a class="tool-item box-line"
              @click="saveSysLog('工作任务(APP)', '/tabs/tabMain/workTask/list', '点击菜单', router.push('/tabs/tabMain/workTask/list'))">

+ 2 - 2
h5app/src/views/sapp/tabWork.vue

@@ -151,11 +151,11 @@
                <div class="tool-title">工作轨迹</div>
              </a>
              <a class="tool-item box-line"
-                @click="saveSysLog('打卡签到(APP)', '/tabs/tabWork/work/signin', '点击菜单', router.push('/tabs/tabWork/work/signin'))">
+                @click="saveSysLog('走访签到(APP)', '/tabs/tabWork/work/signin', '点击菜单', router.push('/tabs/tabWork/work/signin'))">
                <div class="tool-img">
                  <img src="@/assets/icon/map.png">
                </div>
-               <div class="tool-title">打卡签到</div>
+               <div class="tool-title">走访签到</div>
              </a>
            </div>
          </div>

+ 1 - 1
h5app/vue.config.js

@@ -3,7 +3,7 @@ module.exports = {
 
     devServer: {
         port: 8202,
-        https: false,
+        https: true,
         client: {
             progress: true,
         },

+ 16 - 0
src/main/java/com/hz/employmentsite/controller/LongitudeLatitudeController.java

@@ -3,8 +3,11 @@ package com.hz.employmentsite.controller;
 import com.hz.employmentsite.filter.exception.BaseResponse;
 import com.hz.employmentsite.filter.exception.RespGenerstor;
 import com.hz.employmentsite.model.PcLongitudeLatitude;
+import com.hz.employmentsite.model.PcSignin;
+import com.hz.employmentsite.services.service.AccountService;
 import com.hz.employmentsite.services.service.LongitudeLatitudeService;
 import com.hz.employmentsite.vo.taskAndLog.PcLongitudeLatitudeVo;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
@@ -17,6 +20,8 @@ public class LongitudeLatitudeController {
 
     @Resource
     private LongitudeLatitudeService longitudeLatitudeService;
+    @Autowired
+    private AccountService accountService;
 
     @PostMapping("/save")
     public BaseResponse save(@RequestBody PcLongitudeLatitude data) {
@@ -36,4 +41,15 @@ public class LongitudeLatitudeController {
         return RespGenerstor.success(longitudeLatitudeService.newAllList(userName, startDate, regionCode, siteID));
     }
 
+    @PostMapping("/saveSignin")
+    public BaseResponse saveSignin(@RequestBody PcSignin data) {
+        String loginUserID = data.getCreateUserID();
+        try {
+            loginUserID = accountService.getLoginUserID();
+        }
+        catch (Exception e){
+        }
+        return RespGenerstor.success(longitudeLatitudeService.saveSignin(data,loginUserID));
+    }
+
 }

+ 12 - 3
src/main/java/com/hz/employmentsite/controller/baseSettings/SiteUserController.java

@@ -148,14 +148,23 @@ public class SiteUserController {
 
     @GetMapping("/dataMap")
     public BaseResponse<PageInfo<SiteUserMapVo>> getDataMapList(@RequestParam int pageIndex, @RequestParam int pageSize,
-                                                             @RequestParam(required = false) String siteUserName, @RequestParam(required = false) String regionCode,
-                                                             @RequestParam(required = false) String streetCode, @RequestParam(required = false) String siteID){
+                                                                @RequestParam(required = false) String siteUserName, @RequestParam(required = false) String regionCode,
+                                                                @RequestParam(required = false) String streetCode, @RequestParam(required = false) String siteID){
         PageInfo<SiteUserMapVo> result = userInfoService.getDataMapList(pageIndex, pageSize, siteUserName, regionCode, streetCode, siteID);
         return RespGenerstor.success(result);
     }
 
+    @GetMapping("/siteUserWorkDataMap")
+    public BaseResponse<PageInfo<SiteUserMapVo>> getDataMapListBySiteUserWorkMap(@RequestParam int pageIndex, @RequestParam int pageSize,
+                                                                                 @RequestParam(required = false) String siteUserName, @RequestParam(required = false) String regionCode,
+                                                                                 @RequestParam(required = false) String siteID, @RequestParam(required = false) Date startDate,
+                                                                                 @RequestParam(required = false) Date endDate) {
+        PageInfo<SiteUserMapVo> result = userInfoService.getDataMapListBySiteUserWorkMap(pageIndex, pageSize, siteUserName, regionCode, siteID, startDate, endDate);
+        return RespGenerstor.success(result);
+    }
+
     @GetMapping("/dataCount")
-    public BaseResponse<Map> findUserDataCount(@RequestParam String userID){
+    public BaseResponse<Map> findUserDataCount(@RequestParam String userID) {
         Map<String, Object> result = userInfoService.findUserDataCount(userID);
         return RespGenerstor.success(result);
     }

+ 12 - 0
src/main/java/com/hz/employmentsite/controller/companyService/CompanyController.java

@@ -16,6 +16,7 @@ import com.hz.employmentsite.util.ExcelHelper;
 import com.hz.employmentsite.vo.companyService.AppCompanyPostVo;
 import com.hz.employmentsite.vo.companyService.CompanyVo;
 import com.hz.employmentsite.vo.dataMap.CompanyPostMapVo;
+import com.hz.employmentsite.vo.signin.SigninVo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
@@ -251,4 +252,15 @@ public class CompanyController {
         return RespGenerstor.success(result);
     }
 
+    /**
+     * 查询企业的打卡服务记录
+     * @param companyID 企业ID
+     * @return 企业的打卡服务信息
+     */
+    @GetMapping("/allSignin")
+    public BaseResponse getCompanySigninList(@RequestParam(required = false) String companyID) {
+        List<SigninVo> signinList = companyService.getCompanySigninList(companyID);
+        return RespGenerstor.success(signinList);
+    }
+
 }

+ 8 - 0
src/main/java/com/hz/employmentsite/mapper/cquery/CompanyCQuery.java

@@ -3,6 +3,7 @@ package com.hz.employmentsite.mapper.cquery;
 import com.hz.employmentsite.model.PcFirm;
 import com.hz.employmentsite.vo.companyService.CompanyVo;
 import com.hz.employmentsite.vo.dataMap.CompanyPostMapVo;
+import com.hz.employmentsite.vo.signin.SigninVo;
 import org.apache.ibatis.annotations.Param;
 
 import java.util.Date;
@@ -56,4 +57,11 @@ public interface CompanyCQuery {
                                                     @Param("regionCode") String regionCode, @Param("siteID") String siteID,
                                                     @Param("latitude") Double latitude, @Param("longitude") Double longitude,
                                                     @Param("createTimeBy") String createTimeBy);
+
+    /**
+     * 查询企业的打卡服务记录
+     * @param companyID 企业ID
+     * @return 打卡记录
+     */
+    List<SigninVo> getCompanySigninList(@Param("companyID") String companyID);
 }

+ 21 - 0
src/main/java/com/hz/employmentsite/mapper/cquery/UserInfoCQuery.java

@@ -2,8 +2,10 @@ package com.hz.employmentsite.mapper.cquery;
 
 import com.hz.employmentsite.vo.baseSettings.SiteUserVo;
 import com.hz.employmentsite.vo.dataMap.SiteUserMapVo;
+import com.hz.employmentsite.vo.signin.SigninVo;
 import org.apache.ibatis.annotations.Param;
 
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -34,6 +36,25 @@ public interface UserInfoCQuery {
     List<SiteUserMapVo> getDataMapList(@Param("siteUserName") String siteUserName, @Param("regionCode") String regionCode,
                                        @Param("streetCode") String streetCode, @Param("siteID") String siteID);
 
+    /**
+     * 驿站工作人员地图查询驿站人员
+     * @param siteUserName 人员名称
+     * @param regionCode 所属县区
+     * @param siteID 所属驿站
+     * @return 驿站人员列表
+     */
+    List<SiteUserMapVo> getDataMapListBySiteUserWorkMap(@Param("siteUserName") String siteUserName, @Param("regionCode") String regionCode, @Param("siteID") String siteID);
+
+    /**
+     * 获取驿站人员的外出工作记录
+     * @param siteUserIDList 驿站人员ID
+     * @param startDate 开始日期
+     * @param endDate 结束日期
+     */
+    List<SigninVo> getSiteUserSigninList(@Param("siteUserIDList") String siteUserIDList,
+                                       @Param("startDate") Date startDate,
+                                       @Param("endDate") Date endDate);
+
     /**
      * 查询驿站人员登记的业务数据数量
      */

+ 39 - 0
src/main/java/com/hz/employmentsite/services/impl/LongitudeLatitudeServiceImpl.java

@@ -1,9 +1,15 @@
 package com.hz.employmentsite.services.impl;
 
 import com.hz.employmentsite.mapper.PcLongitudeLatitudeMapper;
+import com.hz.employmentsite.mapper.PcSigninMapper;
+import com.hz.employmentsite.mapper.PcSiteUserMapper;
 import com.hz.employmentsite.mapper.cquery.LongitudeLatitudeCQuery;
 import com.hz.employmentsite.model.PcLongitudeLatitude;
+import com.hz.employmentsite.model.PcSignin;
+import com.hz.employmentsite.model.PcSiteUser;
+import com.hz.employmentsite.model.PcSiteUserExample;
 import com.hz.employmentsite.services.service.LongitudeLatitudeService;
+import com.hz.employmentsite.util.StringUtils;
 import com.hz.employmentsite.vo.taskAndLog.PcLongitudeLatitudeVo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -22,6 +28,15 @@ public class LongitudeLatitudeServiceImpl implements LongitudeLatitudeService {
     @Autowired
     private LongitudeLatitudeCQuery longitudeLatitudeCQuery;
 
+    @Autowired
+    private PcSigninMapper pcSigninMapper;
+
+    @Autowired
+    private PcSiteUserMapper pcSiteUserMapper;
+
+    @Autowired
+    private StringUtils stringUtils;
+
     @Override
     public Integer save(PcLongitudeLatitude data) {
         Integer result = 0;
@@ -40,4 +55,28 @@ public class LongitudeLatitudeServiceImpl implements LongitudeLatitudeService {
     public List<PcLongitudeLatitudeVo> newAllList(String userName, Date startDate, String regionCode, String siteID) {
         return longitudeLatitudeCQuery.selectNewAllList(userName, startDate, regionCode, siteID);
     }
+
+    @Override
+    public Integer saveSignin(PcSignin data, String userId) {
+        data.setSigninTime(new Date());
+        data.setCreateTime(new Date());
+        data.setCreateUserID(userId);
+
+        if (!stringUtils.IsNullOrEmpty(userId)) {
+            PcSiteUserExample siteUserExp = new PcSiteUserExample();
+            siteUserExp.or().andUserIDEqualTo(userId);
+            PcSiteUser pcSiteUser = pcSiteUserMapper.selectByExample(siteUserExp).stream().findFirst().orElse(null);
+
+            if (pcSiteUser != null)
+                data.setSiteID(pcSiteUser.getSiteID());
+        }
+
+        if (stringUtils.IsNullOrEmpty(data.getCompanyID()))
+            data.setCompanyID(null);
+
+        if (stringUtils.IsNullOrEmpty(data.getJobuserID()))
+            data.setJobuserID(null);
+
+        return pcSigninMapper.insert(data);
+    }
 }

+ 34 - 0
src/main/java/com/hz/employmentsite/services/impl/baseSettings/SiteUserImpl.java

@@ -14,6 +14,7 @@ import com.hz.employmentsite.util.DateUtils;
 import com.hz.employmentsite.util.StringUtils;
 import com.hz.employmentsite.vo.baseSettings.SiteUserVo;
 import com.hz.employmentsite.vo.dataMap.SiteUserMapVo;
+import com.hz.employmentsite.vo.signin.SigninVo;
 import net.sourceforge.pinyin4j.PinyinHelper;
 import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
 import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
@@ -525,4 +526,37 @@ public class SiteUserImpl implements SiteUserService {
     public Map<String, Object> findUserDataCount(String userID) {
         return userInfoCQuery.findUserDataCount(userID);
     }
+
+    /**
+     * 驿站工作人员外出数据地图查询驿站人员
+     */
+    @Override
+    public PageInfo<SiteUserMapVo> getDataMapListBySiteUserWorkMap(int pageIndex, int pageSize, String siteUserName, String regionCode, String siteID, Date startDate, Date endDate) {
+        PageHelper.startPage(pageIndex, pageSize);
+        List<SiteUserMapVo> dataMapList = userInfoCQuery.getDataMapListBySiteUserWorkMap(siteUserName, regionCode, siteID);
+        PageInfo<SiteUserMapVo> result = new PageInfo(dataMapList);
+
+        // 获取所有的企业ID
+        List<String> siteUserIDs = result.getList().stream()
+                .filter(Objects::nonNull)
+                .map(SiteUserMapVo::getSiteUserID)
+                .filter(Objects::nonNull).toList();
+
+        if (!siteUserIDs.isEmpty()){
+            List<SigninVo> siteUserSigninList = userInfoCQuery.getSiteUserSigninList(stringUtils.ListToInSql(siteUserIDs), startDate, endDate);
+            Map<String, List<SigninVo>> signinMap = (siteUserSigninList != null) ?
+                    siteUserSigninList.stream()
+                            .filter(Objects::nonNull)
+                            .collect(Collectors.groupingBy(SigninVo::getSiteUserID))
+                    : new HashMap<>();
+            // 填充岗位数据
+            result.getList().forEach(siteUser -> {
+                if (siteUser != null && siteUser.getSiteUserID() != null) {
+                    siteUser.setSigninList(signinMap.getOrDefault(siteUser.getSiteUserID(), new ArrayList<>()));
+                }
+            });
+        }
+
+        return result;
+    }
 }

+ 12 - 0
src/main/java/com/hz/employmentsite/services/impl/companyService/CompanyServiceImpl.java

@@ -21,6 +21,7 @@ import com.hz.employmentsite.vo.companyService.AppCompanyPostVo;
 import com.hz.employmentsite.vo.companyService.CompanyVo;
 import com.hz.employmentsite.vo.companyService.PostVo;
 import com.hz.employmentsite.vo.dataMap.CompanyPostMapVo;
+import com.hz.employmentsite.vo.signin.SigninVo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -857,4 +858,15 @@ public class CompanyServiceImpl implements CompanyService {
         DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
         return resultDate.format(formatter);
     }
+
+    /**
+     * 查询企业的打卡服务记录
+     *
+     * @param companyID 企业ID
+     * @return 打卡记录
+     */
+    @Override
+    public List<SigninVo> getCompanySigninList(String companyID) {
+        return companyCQuery.getCompanySigninList(companyID);
+    }
 }

+ 3 - 0
src/main/java/com/hz/employmentsite/services/service/LongitudeLatitudeService.java

@@ -1,6 +1,7 @@
 package com.hz.employmentsite.services.service;
 
 import com.hz.employmentsite.model.PcLongitudeLatitude;
+import com.hz.employmentsite.model.PcSignin;
 import com.hz.employmentsite.vo.taskAndLog.PcLongitudeLatitudeVo;
 
 import java.util.Date;
@@ -12,4 +13,6 @@ public interface LongitudeLatitudeService {
     List<PcLongitudeLatitudeVo> list(String userId, Date startDate, Date endDate);
 
     List<PcLongitudeLatitudeVo> newAllList(String userName, Date startDate, String regionCode, String siteID);
+
+    Integer saveSignin(PcSignin data,String userId);
 }

+ 8 - 0
src/main/java/com/hz/employmentsite/services/service/baseSettings/SiteUserService.java

@@ -5,6 +5,7 @@ import com.hz.employmentsite.model.SelectProps;
 import com.hz.employmentsite.vo.baseSettings.SiteUserVo;
 import com.hz.employmentsite.vo.dataMap.SiteUserMapVo;
 
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -42,6 +43,13 @@ public interface SiteUserService {
     PageInfo<SiteUserMapVo> getDataMapList(int pageIndex, int pageSize, String siteUserName,
                                            String regionCode, String streetCode, String siteID);
 
+    /**
+     * 驿站工作人员外出数据地图查询驿站人员
+     */
+    PageInfo<SiteUserMapVo> getDataMapListBySiteUserWorkMap(int pageIndex, int pageSize, String siteUserName,
+                                                            String regionCode, String siteID, Date startDate,
+                                                            Date endDate);
+
     /**
      * 查询驿站人员登记的业务数据数量
      */

+ 8 - 0
src/main/java/com/hz/employmentsite/services/service/companyService/CompanyService.java

@@ -7,6 +7,7 @@ import com.hz.employmentsite.model.PcSite;
 import com.hz.employmentsite.vo.companyService.AppCompanyPostVo;
 import com.hz.employmentsite.vo.companyService.CompanyVo;
 import com.hz.employmentsite.vo.dataMap.CompanyPostMapVo;
+import com.hz.employmentsite.vo.signin.SigninVo;
 
 import javax.servlet.http.HttpServletRequest;
 import java.util.Date;
@@ -77,4 +78,11 @@ public interface CompanyService {
                                                         String regionCode, String siteID,
                                                         Double latitude, Double longitude,
                                                         String createTimeBy);
+
+    /**
+     * 查询企业的打卡服务记录
+     * @param companyID 企业ID
+     * @return 打卡记录
+     */
+    List<SigninVo> getCompanySigninList(String companyID);
 }

+ 10 - 0
src/main/java/com/hz/employmentsite/vo/dataMap/CompanyPostMapVo.java

@@ -47,6 +47,10 @@ public class CompanyPostMapVo {
 
     private String Latitude;
 
+    private String userName;
+
+    private String userMobile;
+
     private Double distance;
 
     private List<LabelVo> companyLabelList;
@@ -61,4 +65,10 @@ public class CompanyPostMapVo {
 
     private String companyCreateUserName;
     private String postCreateUserName;
+
+    private String industryName;
+
+    private String siteName;
+
+    private Integer signinCount;
 }

+ 5 - 0
src/main/java/com/hz/employmentsite/vo/dataMap/SiteUserMapVo.java

@@ -1,7 +1,10 @@
 package com.hz.employmentsite.vo.dataMap;
 
+import com.hz.employmentsite.vo.signin.SigninVo;
 import lombok.Data;
 
+import java.util.List;
+
 @Data
 public class SiteUserMapVo {
     private String siteUserID;
@@ -25,4 +28,6 @@ public class SiteUserMapVo {
     private String longitude;
 
     private String latitude;
+
+    private List<SigninVo> signinList;
 }

+ 34 - 0
src/main/java/com/hz/employmentsite/vo/signin/SigninVo.java

@@ -0,0 +1,34 @@
+package com.hz.employmentsite.vo.signin;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class SigninVo {
+    private String siteUserID;
+
+    private String signinId;
+
+    private Date signinTime;
+
+    private Integer signinType;
+
+    private String content;
+
+    private String longitude;
+
+    private String latitude;
+
+    private String siteName;
+
+    private String signinUserName;
+
+    private String companyName;
+
+    private String jobUserName;
+}

+ 24 - 1
src/main/resources/mapping/cquery/CompanyCQuery.xml

@@ -127,6 +127,8 @@
             com_status.`Name` AS recordStatusName,
             company.Longitude,
             company.Latitude,
+            company.UserName,
+            company.UserMobile,
             (
             6371 * ACOS(
             COS(
@@ -137,7 +139,10 @@
             RADIANS( company.Latitude ))
             )) AS distance,
             postCounts.postCount,
-            creditRecordCounts.creditRecordCount
+            creditRecordCounts.creditRecordCount,
+            industry.industryName,
+            site.siteName,
+            signinCounts.signinCount
         FROM
             pc_company company
             LEFT JOIN sys_dictionary_item cmodel ON company.CompanyModel = cmodel.`Value`
@@ -147,6 +152,9 @@
             LEFT JOIN sys_user company_user ON company.CreateUserID = company_user.UserID
             LEFT JOIN ( SELECT CompanyID, COUNT( 1 ) AS postCount FROM pc_post GROUP BY CompanyID ) AS postCounts ON company.CompanyID = postCounts.CompanyID
             LEFT JOIN ( SELECT CompanyID, COUNT(1) AS creditRecordCount FROM pc_credit_record GROUP BY CompanyID) AS creditRecordCounts ON company.CompanyID = creditRecordCounts.CompanyID
+            LEFT JOIN ( SELECT CompanyID, COUNT( 1 ) AS signinCount FROM pc_signin GROUP BY CompanyID ) AS signinCounts ON company.CompanyID = signinCounts.CompanyID
+            LEFT JOIN pc_industry industry ON company.IndustryID = industry.industryId
+            LEFT JOIN pc_site site ON company.siteID = site.siteID
         WHERE 1=1
         <if test="companyName!='' and companyName!=null">
             and company.companyName like Concat('%',#{companyName},'%')
@@ -251,4 +259,19 @@
         ORDER BY
         post.CreateTime DESC, distance DESC, CompanyID
     </select>
+
+    <select id="getCompanySigninList" resultType="com.hz.employmentsite.vo.signin.SigninVo">
+        SELECT
+            signinTime,
+            site.SiteName,
+            createUser.`Name` AS signinUserName
+        FROM
+            `pc_signin` signin
+                LEFT JOIN pc_site site ON signin.SiteID = site.SiteID
+                LEFT JOIN sys_user createUser ON signin.CreateUserID = createUser.UserID
+        WHERE
+            signin.CompanyID = #{companyID}
+        ORDER BY
+            signin.SigninTime DESC
+    </select>
 </mapper>

+ 60 - 0
src/main/resources/mapping/cquery/SiteUserCQuery.xml

@@ -134,6 +134,66 @@
         siteUser.CreateTime DESC
     </select>
 
+    <select id="getDataMapListBySiteUserWorkMap" resultType="com.hz.employmentsite.vo.dataMap.SiteUserMapVo">
+        SELECT
+            siteUser.siteUserID,
+            siteUser.SiteUserName,
+            siteUser.userNo,
+            siteUser.Mobile,
+            site.siteName
+        FROM
+            pc_site_user siteUser
+                LEFT JOIN pc_site site ON siteUser.SiteID = site.SiteID
+        WHERE
+            1=1
+            <if test="siteUserName!='' and siteUserName!=null">
+                and siteUser.siteUserName like Concat('%', #{siteUserName}, '%')
+            </if>
+            <if test="siteID!='' and siteID!=null">
+                and siteUser.siteID = #{siteID}
+            </if>
+            <if test="regionCode!='' and regionCode!=null">
+                and site.regionCode = #{regionCode}
+            </if>
+        ORDER BY
+            siteUser.CreateTime DESC
+    </select>
+
+    <select id="getSiteUserSigninList" resultType="com.hz.employmentsite.vo.signin.SigninVo">
+        SELECT
+            siteUser.SiteUserID,
+            signin.SigninId,
+            signin.SigninTime,
+            signin.SigninType,
+            signin.Content,
+            signin.Longitude,
+            signin.Latitude,
+            company.CompanyName,
+            jobUser.`Name` AS jobUserName
+        FROM
+            pc_signin signin
+                LEFT JOIN pc_company company ON signin.CompanyID = company.CompanyID
+                LEFT JOIN pc_jobuser jobUser ON signin.JobuserID = jobUser.JobuserID
+                LEFT JOIN pc_site_user siteUser ON signin.CreateUserID = siteUser.UserID
+        WHERE
+            1=1
+            <if test="siteUserIDList!='' and siteUserIDList!=null">
+                and siteUser.siteUserID in (${siteUserIDList})
+            </if>
+            <if test="startDate != null and endDate == null">
+                and Date(signin.SigninTime) <![CDATA[ >= ]]> Date(#{startDate})
+            </if>
+            <if test="endDate != null and startDate == null">
+                and Date(signin.SigninTime) <![CDATA[ <= ]]> Date(#{endDate})
+            </if>
+            <if test="startDate != null and endDate != null">
+                and Date(signin.SigninTime) <![CDATA[ >= ]]> Date(#{startDate})
+                and Date(signin.SigninTime) <![CDATA[ <= ]]> Date(#{endDate})
+            </if>
+        ORDER BY
+            signin.SigninTime DESC
+    </select>
+
     <select id="findUserDataCount" resultType="Map">
         SELECT
             (SELECT COUNT(1) FROM pc_company WHERE CreateUserID = #{userID}) AS companyCount,

+ 13 - 0
vue/src/api/baseSettings/userInfo.ts

@@ -109,6 +109,19 @@ export function getSiteUserDataMapList(params: any) {
   );
 }
 
+export function getDataMapListBySiteUserWorkMap(params: any) {
+  return request<object>(
+    {
+      url: "userInfo/siteUserWorkDataMap",
+      method: 'get',
+      params: params,
+    },
+    {
+      isNew: true,
+    },
+  );
+}
+
 export function findUserDataCount(userID: any) {
   return request<object>(
     {

+ 13 - 0
vue/src/api/companyService/company.ts

@@ -108,3 +108,16 @@ export function getDataMapListByPostName(params: any) {
     },
   );
 }
+
+export function getCompanySigninList(companyID: any) {
+  return request<object>(
+    {
+      url: "companyService/company/allSignin",
+      method: 'get',
+      params: {companyID},
+    },
+    {
+      isNew: true,
+    },
+  );
+}

BIN
vue/src/assets/images/greenTh.png


+ 10 - 1
vue/src/router/outsideLayout.ts

@@ -40,6 +40,15 @@ export const jobUserDataMapRoute: RouteRecordRaw = {
   },
 };
 
+export const SiteUserWorkDataMapRoute: RouteRecordRaw = {
+  path: '/dataMap/siteUserWorkDataMap',
+  name: 'dataMap_siteUserWorkDataMap',
+  component: () => import('@/views/dataMap/siteUserWorkDataMap.vue'),
+  meta: {
+    title: '惠州市就业驿站管理系统',
+  },
+};
+
 export const dataScreenIndexRoute: RouteRecordRaw = {
   path: '/dataScreen/dataScreenIndex',
   name: 'dataScreenIndex',
@@ -76,4 +85,4 @@ export const dataScreenSysServiceRoute: RouteRecordRaw = {
   },
 };
 
-export default [LoginRoute, companyDataMapRoute, siteDataMapRoute, jobUserDataMapRoute, dataScreenIndexRoute, dataScreenCompanyRoute, dataScreenJobUserRoute, dataScreenSysServiceRoute];
+export default [LoginRoute, companyDataMapRoute, siteDataMapRoute, jobUserDataMapRoute, SiteUserWorkDataMapRoute, dataScreenIndexRoute, dataScreenCompanyRoute, dataScreenJobUserRoute, dataScreenSysServiceRoute];

Dosya farkı çok büyük olduğundan ihmal edildi
+ 2 - 0
vue/src/utils/imageUtils.ts


+ 198 - 83
vue/src/views/dataMap/companyDataMap.vue

@@ -65,7 +65,7 @@
               :options="siteList"
               :field-names="{ label: 'siteName', value: 'siteID' }"
               placeholder="所属驿站" size="small" style="width: 149px"
-              @change="onSearch"
+              @change="siteChange"
             >
             </a-select>
             <a-select
@@ -95,6 +95,7 @@
             <div class="company-data-box"
                  :class="{
                   'check-company': (nowCheckCompany.companyID == company.companyID || nowMouseenterCompany.companyID == company.companyID) && company.creditRecordCount == null,
+                  'check-company-green': (nowCheckCompany.companyID == company.companyID || nowMouseenterCompany.companyID == company.companyID) && company.signinCount >= 1 && !company.creditRecordCount,
                   'check-company-red': (nowCheckCompany.companyID == company.companyID || nowMouseenterCompany.companyID == company.companyID) && company.creditRecordCount >= 1,
                  }"
                  v-if="companyList.length > 0 && searchType == 'company'" v-for="(company,index) in companyList"
@@ -195,17 +196,25 @@
               {{ nowCheckCompany.companyName }}
             </p>
             <p class="label-text">
-              工作地点:{{ nowCheckCompany.companyAddress }}
-            </p>
-            <p class="label-text">
-              企业规模:{{ nowCheckCompany.companyModelType }}
+              所属行业:{{ nowCheckCompany.industryName }}
             </p>
-            <p class="label-text">
-              企业状态:{{ nowCheckCompany.recordStatusName }}
+            <p class="label-text flex-box justify-between">
+              <span style="width: 50%">
+                企业规模:{{ nowCheckCompany.companyModelType }}
+              </span>
+              <span style="width: 50%">
+                企业状态:{{ nowCheckCompany.recordStatusName }}
+              </span>
             </p>
-            <p class="label-text">
-              登记人:{{ nowCheckCompany.companyCreateUserName }}
+            <p class="label-text flex-box justify-between">
+              <span>
+                联系人:{{ nowCheckCompany.userName }}
+              </span>
+              <span>
+                联系电话:{{ nowCheckCompany.userMobile }}
+              </span>
             </p>
+            <p class="label-text">标签</p>
             <!-- 标签 -->
             <div class="company-label-box"
                  v-if="nowCheckCompany.companyLabelList && nowCheckCompany.companyLabelList.length > 0"
@@ -220,60 +229,84 @@
                 <span class="launch-btn" v-else @click.stop="postBoxCompanyLabelExpanded = true">收起</span>
               </div>
             </div>
+            <div v-else class="label-text">
+              暂无标签
+            </div>
           </div>
+          <a-radio-group v-model:value="dataType" button-style="solid" size="small"
+                         style="margin-bottom: 10px">
+            <a-radio-button value="post">岗位</a-radio-button>
+            <a-radio-button value="signin">服务记录</a-radio-button>
+          </a-radio-group>
           <!-- 岗位列表 -->
-          <div class="list-box">
-            <div class="list-post-box  margin-bottom-10" v-if="postList.length > 0"
-                 v-for="(post, postIndex) in postList"
-                 :key="postIndex">
-              <div class="post-title">
-                <span>{{ post.professionName }}</span>
-                <span class="post-salary">{{ showSalary(post.minSalary, post.maxSalary) }}</span>
-              </div>
-              <p class="label-text">
-                招聘人数:{{ post.recruitCount }}
-              </p>
-              <p class="label-text">
-                招聘日期:
-                {{ dayjs(post.startTime).format("YYYY-MM-DD") }}
-                至
-                {{ dayjs(post.endTime).format("YYYY-MM-DD") }}
-              </p>
-              <!-- 岗位要求 -->
-              <div class="post-desc-box">
-                <div class="label-text post-desc" :ref="el => postDescBoxRef[postIndex] = el"
-                     :class="{'post-desc-max-height': post.descExpanded}">
-                  岗位要求:{{ post.postDesc }}
+          <div v-if="dataType == 'post'" class="dataType-box">
+            <div class="list-box">
+              <div class="list-post-box  margin-bottom-10" v-if="postList.length > 0"
+                   v-for="(post, postIndex) in postList"
+                   :key="postIndex">
+                <div class="post-title">
+                  <span>{{ post.professionName }}</span>
+                  <span class="post-salary">{{ showSalary(post.minSalary, post.maxSalary) }}</span>
                 </div>
-                <div v-if="showLaunchBtnBox(postDescBoxRef,postIndex,36)">
+                <p class="label-text">
+                  招聘人数:{{ post.recruitCount }}
+                </p>
+                <p class="label-text">
+                  招聘日期:
+                  {{ dayjs(post.startTime).format("YYYY-MM-DD") }}
+                  至
+                  {{ dayjs(post.endTime).format("YYYY-MM-DD") }}
+                </p>
+                <!-- 岗位要求 -->
+                <div class="post-desc-box">
+                  <div class="label-text post-desc" :ref="el => postDescBoxRef[postIndex] = el"
+                       :class="{'post-desc-max-height': post.descExpanded}">
+                    岗位要求:{{ post.postDesc }}
+                  </div>
+                  <div v-if="showLaunchBtnBox(postDescBoxRef,postIndex,36)">
                 <span class="launch-btn" v-if="post.descExpanded"
                       @click.stop="post.descExpanded = false">展开</span>
-                  <span class="launch-btn " v-else @click.stop="post.descExpanded = true">收起</span>
+                    <span class="launch-btn " v-else @click.stop="post.descExpanded = true">收起</span>
+                  </div>
                 </div>
-              </div>
-              <!-- 标签 -->
-              <div class="company-label-box" v-if="post.labelList && post.labelList.length > 0"
-                   :ref="el => postLabelBoxRef[postIndex] = el" :class="{'label-box-max-height': post.labelExpanded}">
-                <a-tag v-for="(label, labelIndex) in post.labelList" :key="labelIndex">
-                  {{ label.labelName }}
-                </a-tag>
-                <div v-if="showLaunchBtnBox(postLabelBoxRef,postIndex,50)">
+                <!-- 标签 -->
+                <div class="company-label-box" v-if="post.labelList && post.labelList.length > 0"
+                     :ref="el => postLabelBoxRef[postIndex] = el" :class="{'label-box-max-height': post.labelExpanded}">
+                  <a-tag v-for="(label, labelIndex) in post.labelList" :key="labelIndex">
+                    {{ label.labelName }}
+                  </a-tag>
+                  <div v-if="showLaunchBtnBox(postLabelBoxRef,postIndex,50)">
                 <span class="launch-btn" v-if="post.labelExpanded"
                       @click.stop="post.labelExpanded = false">展开</span>
-                  <span class="launch-btn" v-else @click.stop="post.labelExpanded = true">收起</span>
+                    <span class="launch-btn" v-else @click.stop="post.labelExpanded = true">收起</span>
+                  </div>
                 </div>
               </div>
+              <div v-else class="empty-box">
+                <a-empty/>
+              </div>
             </div>
-            <div v-else class="empty-box">
-              <a-empty/>
+            <!-- 分页控件 -->
+            <div class="pagination-box">
+              <span>共{{ postTotal }}个</span>
+              <a-pagination v-model:current="postSearchParams.pageIndex" :total="postTotal"
+                            v-model:pageSize="postSearchParams.pageSize"
+                            show-less-items @change="postPaginationChange" simple :show-size-changer="false"/>
             </div>
           </div>
-          <!-- 分页控件 -->
-          <div class="pagination-box">
-            <span>共{{ postTotal }}个</span>
-            <a-pagination v-model:current="postSearchParams.pageIndex" :total="postTotal"
-                          v-model:pageSize="postSearchParams.pageSize"
-                          show-less-items @change="postPaginationChange" simple :show-size-changer="false"/>
+          <div v-if="dataType == 'signin'" class="dataType-box">
+            <!-- 服务记录时间轴 -->
+            <div v-if="signinList.length > 0" class="signin-list">
+              <a-timeline>
+                <a-timeline-item v-for="(signin, key) in signinList" :key="key" position="left">
+                  <p>{{ dayjs(signin.signinTime).format('YYYY-MM-DD') }}</p>
+                  <p>{{ signin.siteName }}{{ signin.signinUserName }}进行走访服务</p>
+                </a-timeline-item>
+              </a-timeline>
+            </div>
+            <div v-else class="empty-box">
+              <a-empty description="暂无服务记录"/>
+            </div>
           </div>
         </div>
         <div v-if="searchType == 'post'"
@@ -335,13 +368,14 @@ import {onMounted, reactive, ref} from "vue";
 import huiZhouGeoJSON from "./geo";
 import {getPosition, setBoundary} from "@/utils/position";
 import redThIcon from "@/assets/images/redTh1.png";
-import redThIcon3 from "@/assets/images/redTh4.png";
+import redThIcon4 from "@/assets/images/redTh4.png";
 import blueThIcon from "@/assets/images/blueTh1.png";
+import greenThIcon from "@/assets/images/greenTh.png";
 import {message, type SelectProps} from "ant-design-vue";
 import {getSysDictionaryList} from "@/api/system/dictionary";
-import {getSiteList} from "@/api/baseSettings/siteInfo";
+import {getSiteByID, getSiteList} from "@/api/baseSettings/siteInfo";
 import {getRegionCodeList} from "@/api/system/area/index";
-import {getDataMapList, getDataMapListByPostName} from "@/api/companyService/company";
+import {getCompanySigninList, getDataMapList, getDataMapListByPostName} from "@/api/companyService/company";
 import {getCompanyMapPostList} from "@/api/companyService/post";
 import dayjs from "dayjs";
 import avtO1 from "@/assets/images/avt01.png";
@@ -367,10 +401,15 @@ const companySearchParam = reactive({
   siteID: undefined,
   regionCode: undefined,
   createTimeBy: 'all',
-  longitude: 114.416110,
-  latitude: 23.111582,
+  longitude: 114.411771,
+  latitude: 23.113454,
   isPost: null
 })
+// 当前电脑的位置
+const defaultLonLat = reactive({
+  longitude: 114.411771,
+  latitude: 23.113454,
+})
 // 企业数据
 const companyList = ref<Array<any>>([])
 // 企业分页条数
@@ -388,6 +427,10 @@ const postSearchParams = reactive({
 const postList = ref<Array<any>>([]);
 // 岗位分页总条数
 const postTotal = ref(0);
+// 当前选中的企业的打卡服务记录
+const signinList = ref<Array<any>>([])
+// 企业详情显示内容tab
+const dataType = ref("post");
 
 // 范围列表
 const rangeList = [
@@ -507,33 +550,48 @@ const initMap = () => {
   setLoginLocation();
 };
 
+// 设置中心点图标
+function setCenterIcon() {
+  // 清除地图上的查询位置标记点
+  (map as any).removeOverLay(userMarker);
+
+  // 设置中心点
+  (map as any).centerAndZoom(new T.LngLat(companySearchParam.longitude, companySearchParam.latitude), zoom);
+  const icon = new T.Icon({
+    iconUrl: redThIcon,
+    iconSize: new T.Point(10, 10),
+    iconAnchor: new T.Point(0, 5)
+  })
+  const point = new T.LngLat(companySearchParam.longitude, companySearchParam.latitude)
+  // 创建标注
+  userMarker = new T.Marker(point, {
+    icon: icon
+  });
+  //向地图上标记
+  (map as any).addOverLay(userMarker);
+}
+
 // 获取当前登录用户定位
 function setLoginLocation() {
   getPosition().then((data: any) => {
     if (data.longitude && data.latitude) {
-      companySearchParam.longitude = data.longitude;
-      companySearchParam.latitude = data.latitude;
+      if (!companySearchParam.siteID) {
+        companySearchParam.longitude = data.longitude;
+        companySearchParam.latitude = data.latitude;
+      }
+      // 记录下来当前位置
+      defaultLonLat.longitude = data.longitude;
+      defaultLonLat.latitude = data.latitude;
     } else {
       // 如果没有获取到经纬度,设置默认为惠州市人民政府的位置
-      companySearchParam.longitude = 114.420244;
-      companySearchParam.latitude = 23.116236;
+      if (!companySearchParam.siteID) {
+        companySearchParam.longitude = 114.411771;
+        companySearchParam.latitude = 23.113454;
+      }
       message.info("获取定位失败,已使用默认定位");
     }
   }).finally(() => {
-    // 设置中心点
-    (map as any).centerAndZoom(new T.LngLat(companySearchParam.longitude, companySearchParam.latitude), zoom);
-    const icon = new T.Icon({
-      iconUrl: redThIcon,
-      iconSize: new T.Point(10, 10),
-      iconAnchor: new T.Point(5, 10)
-    })
-    const point = new T.LngLat(companySearchParam.longitude, companySearchParam.latitude)
-    // 创建标注
-    userMarker = new T.Marker(point, {
-      icon: icon
-    });
-    //向地图上标记
-    (map as any).addOverLay(userMarker);
+    setCenterIcon();
     onSearch();
   })
 }
@@ -593,7 +651,12 @@ function setCompanyMarker(setCenter: boolean) {
       iconAnchor: sizeData.iconAnchor
     })
     const icon_red = new T.Icon({
-      iconUrl: redThIcon3,
+      iconUrl: redThIcon4,
+      iconSize: new T.Point(sizeData.iconSize, sizeData.iconSize),
+      iconAnchor: sizeData.iconAnchor
+    })
+    const icon_green = new T.Icon({
+      iconUrl: greenThIcon,
       iconSize: new T.Point(sizeData.iconSize, sizeData.iconSize),
       iconAnchor: sizeData.iconAnchor
     })
@@ -607,6 +670,11 @@ function setCompanyMarker(setCenter: boolean) {
             icon: icon_blue
           }); // 创建标注
         }
+        if (item.signinCount >= 1) {
+          marker = new T.Marker(point, {
+            icon: icon_green
+          }); // 创建标注
+        }
         if (item.creditRecordCount >= 1) {
           marker = new T.Marker(point, {
             icon: icon_red
@@ -684,18 +752,18 @@ function setCompanyMarker(setCenter: boolean) {
 // 天地图按缩放基本计算图标与文本的大小与锚点偏移值
 function computeMarkerSize(zoomLevel: any) {
   // 计算新的icon大小
-  let newIconSize = Math.min(Math.max(zoomLevel * 3, 15), 45);
+  let newIconSize = Math.min(Math.max(zoomLevel * 2, 10), 15);
   // 计算新的icon锚点位置
-  let iconAnchor = new T.Point(10 * (newIconSize / 45), 20 * (newIconSize / 45));
+  let iconAnchor = new T.Point(10 * (newIconSize / 15), 20 * (newIconSize / 15));
   // 计算新的偏移量,保持 label 居中且不超过初始值
-  let offsetX = -20 + ((newIconSize - 45) / 3);
-  let offsetY = 28 + ((newIconSize - 45) / 3);
+  let offsetX = -15 + ((newIconSize - 15) / 2);
+  let offsetY = 15 + ((newIconSize - 15) / 2);
   // 确保偏移量不超过初始值
-  offsetX = Math.max(offsetX, -20);
-  offsetY = Math.min(offsetY, 28);
+  offsetX = Math.max(offsetX, -15);
+  offsetY = Math.min(offsetY, 15);
   // 更新 label 的偏移量
   let newOffset = new T.Point(offsetX, offsetY);
-  let fontSize = Math.min(12, Math.max(8, 12 - (45 - newIconSize) / 3))
+  let fontSize = Math.min(12, Math.max(8, 12 - (15 - newIconSize) / 2))
 
   return {
     iconSize: newIconSize,
@@ -750,10 +818,11 @@ const checkCompanyChange = async (company: any, funE: any) => {
       autoPan: true,
       maxHeight: 300,
       maxWidth: 400,
-      offset: new T.Point(10, 0)
+      offset: new T.Point(-5, -15)
     });
   }
   await findPostList();
+  await findCompanySigninList();
 }
 
 // 企业信息鼠标移入移出事件
@@ -775,6 +844,13 @@ async function findPostList() {
   })
 }
 
+// 查询企业的打卡服务记录
+async function findCompanySigninList() {
+  await getCompanySigninList(nowCheckCompany.value.companyID).then((result: any) => {
+    signinList.value = result;
+  })
+}
+
 // 企业分页器页码变更事件
 function postPaginationChange() {
   findPostList();
@@ -850,6 +926,27 @@ function regionCodeChange() {
   onSearch();
 }
 
+function siteChange() {
+  if (companySearchParam.siteID) {
+    getSiteByID(companySearchParam.siteID).then((result: any) => {
+      // 设置查询的中心点为驿站的经纬度
+      if (result.siteLongitude && result.siteLatitude) {
+        companySearchParam.longitude = result.siteLongitude;
+        companySearchParam.latitude = result.siteLatitude;
+        setCenterIcon();
+      }
+    }).finally(() => {
+      onSearch();
+    })
+  } else {
+    // 未选择驿站时恢复默认位置
+    companySearchParam.longitude = defaultLonLat.longitude;
+    companySearchParam.latitude = defaultLonLat.latitude;
+    setCenterIcon();
+    onSearch();
+  }
+}
+
 onMounted(() => {
   initMap();
   getCompanyModelList();
@@ -1038,6 +1135,10 @@ export default {
         .check-company-red {
           border: 1px solid red;
         }
+
+        .check-company-green {
+          border: 1px solid #00ff00;
+        }
       }
     }
 
@@ -1054,6 +1155,20 @@ export default {
     right: 0;
     border-radius: 10px;
 
+    .dataType-box {
+      height: 72%;
+
+      .signin-list {
+        width: 100%;
+        height: 100%;
+        overflow: hidden;
+        overflow-y: auto;
+        padding: 15px;
+        background-color: white;
+        border-radius: 10px;
+      }
+    }
+
     .company-info-post-list {
       width: 100%;
       height: 100%;
@@ -1123,7 +1238,7 @@ export default {
     }
 
     .list-box {
-      height: calc(100% - 260px);
+      height: calc(100% - 40px);
 
       .list-post-box {
         padding: 8px;

+ 10 - 8
vue/src/views/dataMap/jobUserDataMap.vue

@@ -438,18 +438,20 @@ function setMapMarker(setCenter: boolean) {
 // 天地图按缩放基本计算图标与文本的大小与锚点偏移值
 function computeMarkerSize(zoomLevel: any) {
   // 计算新的icon大小
-  let newIconSize = Math.min(Math.max(zoomLevel * 3, 15), 45);
+  let newIconSize = Math.min(Math.max(zoomLevel * 2, 10), 15);
   // 计算新的icon锚点位置
-  let iconAnchor = new T.Point(10 * (newIconSize / 45), 20 * (newIconSize / 45));
+  let iconAnchor = new T.Point(10 * (newIconSize / 15), 20 * (newIconSize / 15));
   // 计算新的偏移量,保持 label 居中且不超过初始值
-  let offsetX = -20 + ((newIconSize - 45) / 3);
-  let offsetY = 30 + ((newIconSize - 45) / 3);
+  let offsetX = -27 + ((newIconSize - 15) / 2);
+  let offsetY = 2 + ((newIconSize - 15) / 2);
   // 确保偏移量不超过初始值
-  offsetX = Math.max(offsetX, -20);
-  offsetY = Math.min(offsetY, 30);
+  offsetX = Math.max(offsetX, -30);
+  offsetY = Math.min(offsetY, 10);
+  console.log(offsetX);
+  console.log(offsetY);
   // 更新 label 的偏移量
   let newOffset = new T.Point(offsetX, offsetY);
-  let fontSize = Math.min(12, Math.max(8, 12 - (45 - newIconSize) / 3))
+  let fontSize = Math.min(8, Math.max(6, 10 - (15 - newIconSize) / 2))
 
   return {
     iconSize: newIconSize,
@@ -503,7 +505,7 @@ function checkJobUser(jobUser: any) {
       autoPan: true,
       maxHeight: 400,
       maxWidth: 500,
-      offset: new T.Point(10, 0)
+      offset: new T.Point(-5, -15)
     });
   }
 }

+ 5 - 5
vue/src/views/dataMap/siteDataMap.vue

@@ -522,18 +522,18 @@ function delMapInfo() {
 // 天地图按缩放基本计算图标与文本的大小与锚点偏移值
 function computeMarkerSize(zoomLevel: any) {
   // 计算新的icon大小
-  let newIconSize = Math.min(Math.max(zoomLevel * 3, 15), 45);
+  let newIconSize = Math.min(Math.max(zoomLevel * 2, 10), 15);
   // 计算新的icon锚点位置
-  let iconAnchor = new T.Point(10 * (newIconSize / 45), 20 * (newIconSize / 45));
+  let iconAnchor = new T.Point(10 * (newIconSize / 30), 20 * (newIconSize / 45));
   // 计算新的偏移量,保持 label 居中且不超过初始值
-  let offsetX = -20 + ((newIconSize - 45) / 3);
-  let offsetY = 28 + ((newIconSize - 45) / 3);
+  let offsetX = -20 + ((newIconSize - 15) / 2);
+  let offsetY = 28 + ((newIconSize - 15) / 2);
   // 确保偏移量不超过初始值
   offsetX = Math.max(offsetX, -20);
   offsetY = Math.min(offsetY, 28);
   // 更新 label 的偏移量
   let newOffset = new T.Point(offsetX, offsetY);
-  let fontSize = Math.min(12, Math.max(8, 12 - (45 - newIconSize) / 3))
+  let fontSize = Math.min(8, Math.max(6, 10 - (15 - newIconSize) / 2))
 
   return {
     iconSize: newIconSize,

+ 492 - 0
vue/src/views/dataMap/siteUserWorkDataMap.vue

@@ -0,0 +1,492 @@
+<template>
+  <div>
+    <div class="map-box">
+      <!-- 地图 -->
+      <div
+        id="mapDiv"
+      ></div>
+      <!-- 查询表单与列表展示 -->
+      <div class="site-search-data-box">
+        <!-- 查询表单区域 -->
+        <div class="search-input-box">
+          <!-- 查询表单区域 -->
+          <div class="search-input-box">
+            <a-input-search
+              v-model:value="siteUserSearchParam.siteUserName"
+              placeholder="请输入驿站工作人员姓名"
+              enter-button
+              :loading="searchLoading"
+              style="width: 100%"
+              @search="onSearch"
+            />
+          </div>
+        </div>
+        <!-- 数据列表 -->
+        <div class="select-data-box">
+          <!-- 下拉框查询部分 -->
+          <div class="select-input-box flex-box items-center" style="flex-wrap: wrap">
+            <a-date-picker style="width: 50%;" v-model:value="siteUserSearchParam.startDate" value-format="YYYY-MM-DD"
+                           placeholder="请选择开始日期" :allow-clear="false" @change="changeRegion"/>
+            <a-date-picker style="width: 50%;" v-model:value="siteUserSearchParam.endDate" value-format="YYYY-MM-DD"
+                           placeholder="请选择结束日期" :allow-clear="false" @change="changeRegion"/>
+            <a-select
+              style="width: 50%;"
+              v-model:value="siteUserSearchParam.regionCode"
+              :allow-clear="true"
+              :options="regionList"
+              :field-names="{ label: 'name', value: 'code' }"
+              :bordered="false"
+              placeholder="所属县区"
+              @change="changeRegion"
+            >
+            </a-select>
+            <a-select
+              style="width: 50%;"
+              v-model:value="siteUserSearchParam.siteID"
+              :options="siteDicList"
+              :field-names="{ label: 'siteName', value: 'siteID' }"
+              :allow-clear="true"
+              :bordered="false"
+              placeholder="所属驿站"
+              @change="onSearch"
+            >
+            </a-select>
+          </div>
+          <!-- 数据列表部分 -->
+          <div class="list-box">
+            <!-- 驿站人员数据 -->
+            <div v-if="siteUserList.length > 0" class="site-data"
+                 :class="{'check-site':nowCheckSiteUser.siteUserID == siteUser.siteUserID || nowMouseenterSiteUser.siteUserID == siteUser.siteUserID}"
+                 v-for="(siteUser,siteIndex) in siteUserList"
+                 :key="siteIndex"
+                 @click="checkSiteUser(siteUser)"
+                 @mouseenter="siteUserMouseenter(siteUser)"
+                 @mouseleave="siteUserMouseenter({siteUserID: -1})"
+            >
+              <p class="site-name">
+                {{ siteUser.siteUserName }}
+              </p>
+              <p class="label-text">
+                工号:{{ siteUser.userNo }}
+              </p>
+              <p class="label-text">
+                联系电话:{{ siteUser.mobile }}
+              </p>
+              <p class="label-text">
+                所属驿站:{{ siteUser.siteName }}
+              </p>
+            </div>
+            <div
+              v-if="siteUserList.length == 0"
+              class="empty-box">
+              <a-empty/>
+            </div>
+          </div>
+        </div>
+        <!-- 分页控件 -->
+        <div class="pagination-box">
+          <span>共{{ paginationTotal }}人</span>
+          <a-pagination v-model:current="siteUserSearchParam.pageIndex" :total="paginationTotal"
+                        v-model:pageSize="siteUserSearchParam.pageSize" :disabled="searchLoading"
+                        show-less-items @change="onSearch" simple :show-size-changer="false"/>
+        </div>
+      </div>
+      <!-- 驿站人员信息详情 -->
+      <div class="site-user-info-box" v-if="nowCheckSiteUser.siteUserID">
+        <div class="user-info-box margin-bottom-10">
+          <p class="font-size-18 margin-bottom-8" style="font-weight: 600; text-align: center">
+            {{ nowCheckSiteUser.siteUserName }}
+          </p>
+        </div>
+        <p class="margin-bottom-10">服务记录</p>
+        <!-- 服务记录时间轴 -->
+        <div v-if="nowCheckSiteUser.signinList && nowCheckSiteUser.signinList.length > 0" class="serviceRecords-list">
+          <a-timeline>
+            <a-timeline-item v-for="(service, key) in nowCheckSiteUser.signinList" :key="key" position="left">
+              <p>{{ dayjs(service.signinTime).format('YYYY-MM-DD HH:mm') }}</p>
+              <p v-if="service.signinType == 1">走访{{ service.companyName }}</p>
+              <p v-if="service.signinType == 2">走访{{ service.jobUserName }}人员信息</p>
+              <p v-if="service.signinType == 3">外出走访({{ service.content }})</p>
+              <div class="flex-box" style="flex-wrap: wrap">
+                <div v-for="(item, index) in service.fileList" :key="index"
+                     style="width: 60px; height: 60px; margin-right: 5px; margin-bottom: 5px">
+                  <a-image
+                    v-if="item.previewUrl"
+                    width="100%"
+                    height="100%"
+                    :src="item.previewUrl"
+                    :fallback="errorImage"
+                  ></a-image>
+                </div>
+              </div>
+            </a-timeline-item>
+          </a-timeline>
+        </div>
+        <div v-else class="empty-box">
+          <a-empty description="暂无服务记录"/>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import huiZhouGeoJSON from "./geo"
+import {onMounted, reactive, ref} from "vue";
+import {setBoundary} from "@/utils/position";
+import type {SelectProps} from "ant-design-vue";
+import {getSiteList} from "@/api/baseSettings/siteInfo";
+import {getRegionCodeList} from "@/api/system/area/index";
+import redTh from "@/assets/images/redTh.png"
+import dayjs from "dayjs";
+import {getDataMapListBySiteUserWorkMap} from "@/api/baseSettings/userInfo";
+import {getFileBase64, getList} from "@/api/system/file";
+import {base64ToBlob, errorImage} from "@/utils/imageUtils";
+
+const T = (window as any).T;
+const zoom = 9;
+let map = null;
+// 天地图默认中心点坐标
+const centerLngLat = new T.LngLat(114.416110, 23.111582);
+// 地图标记点
+let markerList = ref<Array<any>>([]);
+// 地图画线
+let lineList = ref<Array<any>>([]);
+
+// 驿站人员查询数据
+const siteUserSearchParam = reactive({
+  pageIndex: 1,
+  pageSize: 100,
+  siteUserName: "",
+  regionCode: null,
+  siteID: null,
+  startDate: dayjs(new Date()).format('YYYY-MM-DD'),
+  endDate: dayjs(new Date()).format('YYYY-MM-DD'),
+});
+// 驿站人员列表
+const siteUserList = ref<Array<any>>([]);
+// 查询加载动画
+const searchLoading = ref(false);
+// 县区数据
+const regionList = ref<SelectProps['options']>();
+// 驿站下拉框数据
+const siteDicList = ref<any>([]);
+// 选中的驿站人员
+const nowCheckSiteUser = ref<any>({siteUserID: null})
+// 鼠标经过的驿站
+const nowMouseenterSiteUser = ref<any>({siteUserID: null});
+// 驿站总条数
+const paginationTotal = ref(0)
+
+// 地图初始化
+const initMap = () => {
+  // 初始化容器
+  map = new T.Map('mapDiv');
+  if (map != null) {
+    // 设置地图显示中心点为惠州市人民政府
+    (map as any).centerAndZoom(centerLngLat, zoom);
+    (map as any).setMinZoom(5);
+  }
+  // 设置城市边界
+  setBoundary(map, T, huiZhouGeoJSON);
+};
+
+// 查询方法
+async function onSearch() {
+  // 清除地图标记点和查询数据
+  siteUserList.value = [];
+  nowCheckSiteUser.value = {siteUserID: null};
+  nowMouseenterSiteUser.value = {siteUserID: null};
+  delMapInfo();
+  searchLoading.value = true;
+  // 设置地图显示中心点为惠州市人民政府
+  (map as any).centerAndZoom(centerLngLat, zoom);
+  await getDataMapListBySiteUserWorkMap({...siteUserSearchParam}).then((result: any) => {
+    siteUserList.value = result.list;
+    paginationTotal.value = result.total;
+
+    // 初始化地图标点图标
+    const icon_red = new T.Icon({
+      iconUrl: redTh,
+      iconSize: new T.Point(20, 20)
+    })
+
+    // 绘制地图标点
+    result.list.forEach((siteUser: any) => {
+      if (siteUser.signinList.length > 0) {
+        const points = new Array<any>();
+
+        siteUser.signinList.forEach((signin: any) => {
+          if (signin.longitude && signin.latitude) {
+            // 保存坐标点
+            points.push(new T.LngLat(signin.longitude, signin.latitude))
+
+            const point = new T.LngLat(signin.longitude, signin.latitude)
+            const marker = new T.Marker(point, {
+              icon: icon_red
+            }); // 创建标注
+            marker.addEventListener('click', () => {
+              checkSiteUser(siteUser);
+
+            });
+            (map as any).addOverLay(marker);// 将标注添加到地图中
+            markerList.value.push(marker);
+          }
+        })
+
+        // 在地图上画线
+        const line = new T.Polyline(points, {
+          color: "#c00020"
+        });
+        (map as any).addOverLay(line); //向地图上添加线
+        lineList.value.push(line);
+      }
+    })
+  }).finally(() => {
+    searchLoading.value = false;
+  });
+}
+
+// 查询县区
+const getRegionList = async function () {
+  regionList.value = await getRegionCodeList();
+}
+// 县区变更事件
+const changeRegion = async function () {
+  if (siteUserSearchParam.regionCode) {
+    getSiteList({pageIndex: 1, pageSize: 9999, regionCode: siteUserSearchParam.regionCode}).then((result: any) => {
+      siteDicList.value = result.list;
+    })
+  } else {
+    siteDicList.value = [];
+  }
+  await onSearch();
+}
+
+// 选择站点人员
+async function checkSiteUser(siteUser: any) {
+  // 清除地图标其他记点
+  delMapInfo();
+  if (siteUser.signinList.length >= 0) {
+    if (siteUser.signinList[0] && siteUser.signinList[0].longitude && siteUser.signinList[0].latitude) {
+      // 设置中心点
+      (map as any).centerAndZoom(new T.LngLat(siteUser.signinList[0].longitude, siteUser.signinList[0].latitude), 16);
+    }
+    // 绘制当前选择驿站人员的画线
+    const icon_red = new T.Icon({
+      iconUrl: redTh,
+      iconSize: new T.Point(20, 20)
+    })
+    const points = new Array<any>();
+    siteUser.signinList.forEach((signin: any) => {
+      if (signin.longitude && signin.latitude) {
+        // 保存坐标点
+        points.push(new T.LngLat(signin.longitude, signin.latitude))
+
+        const point = new T.LngLat(signin.longitude, signin.latitude)
+        const marker = new T.Marker(point, {
+          icon: icon_red
+        }); // 创建标注
+        (map as any).addOverLay(marker);// 将标注添加到地图中
+        markerList.value.push(marker);
+      }
+    })
+    // 在地图上画线
+    const line = new T.Polyline(points, {
+      color: "#c00020"
+    });
+    (map as any).addOverLay(line); //向地图上添加线
+    lineList.value.push(line);
+  }
+  nowCheckSiteUser.value = JSON.parse(JSON.stringify(siteUser));
+  nowCheckSiteUser.value.signinList.forEach((signin: any) => {
+    // 查询服务记录的图片数据
+    getList({fileRefID: signin.signinId}).then((result: any) => {
+      if (result && result.length > 0) {
+        signin.fileList = result;
+        signin.fileList.forEach((image: any) => {
+          getFileBase64({fileId: image.fileId}).then((result: any) => {
+            image.previewUrl = URL.createObjectURL(base64ToBlob("data:image/png;base64," + result));
+            console.log(image.previewUrl);
+          })
+        })
+      }
+    })
+  })
+}
+
+// 站点人员鼠标经过事件
+function siteUserMouseenter(siteUser: any) {
+  nowMouseenterSiteUser.value = JSON.parse(JSON.stringify(siteUser));
+}
+
+// 清空地图标点
+function delMapInfo() {
+  // 删除已有标点
+  if (markerList.value.length > 0) {
+    for (let i = 0; i < markerList.value.length; i++) {
+      (map as any).removeOverLay(markerList.value[i]);
+    }
+    markerList.value = [];
+  }
+  if (lineList.value.length > 0) {
+    for (let i = 0; i < lineList.value.length; i++) {
+      (map as any).removeOverLay(lineList.value[i]);
+    }
+    lineList.value = [];
+  }
+  (map as any).closeInfoWindow();
+}
+
+onMounted(() => {
+  // 获取当前日期与7天前的日期
+  // 获取当天日期
+  const today = dayjs();
+  // 获取7天前的日期
+  const sevenDaysAgo = today.subtract(7, 'day');
+  siteUserSearchParam.startDate = sevenDaysAgo.format('YYYY-MM-DD');
+  siteUserSearchParam.endDate = today.format('YYYY-MM-DD');
+
+  initMap();
+  getRegionList();
+  onSearch();
+})
+</script>
+
+<script lang="ts">
+// 设置页面名称进行组件缓存
+export default {
+  name: "SiteUserWorkDataMap"
+}
+</script>
+
+<style lang="less">
+.map-box {
+  width: 100vw;
+  height: 100vh;
+  position: relative;
+  border: 1px rgba(173, 173, 173, 0.8) solid;
+  box-sizing: border-box;
+
+  #mapDiv {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    z-index: 100;
+  }
+
+  .site-search-data-box {
+    width: 360px;
+    position: absolute;
+    top: 15px;
+    bottom: 5px;
+    left: 15px;
+    right: 390px;
+    z-index: 110;
+
+    .search-input-box {
+      width: 100%;
+      margin-bottom: 10px;
+    }
+
+    .select-data-box {
+      height: calc(100% - 95px);
+      background-color: #F8F8F8;
+      border-radius: 10px 10px 0 0;
+      padding: 10px;
+
+      .select-input-box {
+        width: 100%;
+        display: flex;
+        align-items: center;
+        background-color: white;
+        padding: 5px;
+        margin-bottom: 10px;
+        border-radius: 10px;
+      }
+
+      .list-box {
+        max-height: calc(100% - 100px);
+        overflow: hidden;
+        overflow-y: auto;
+
+        .site-data {
+          background-color: white;
+          border-radius: 10px;
+          padding: 8px;
+          cursor: pointer;
+          margin-bottom: 10px;
+          box-shadow: 0 5px 5px -5px rgba(0, 0, 0, 0.3);
+          box-sizing: border-box;
+          border: 1px solid white;
+        }
+
+        .check-site {
+          border-color: #007EFF;
+        }
+      }
+    }
+  }
+
+  .site-user-info-box {
+    width: 300px;
+    position: absolute;
+    top: 15px;
+    left: calc(100% - 330px);
+    right: 15px;
+    bottom: 15px;
+    z-index: 110;
+    border-radius: 10px;
+    background-color: white;
+    padding: 15px;
+    overflow: hidden;
+
+    .serviceRecords-list {
+      width: 100%;
+      height: calc(100% - 80px);
+      overflow: hidden;
+      overflow-y: auto;
+      background-color: white;
+      padding-top: 10px;
+    }
+  }
+
+  .pagination-box {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    padding: 10px 10px;
+    background-color: #F8F8F8;
+    border-radius: 0 0 10px 10px;
+  }
+
+  .empty-box {
+    width: 100%;
+    height: 450px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+
+  .ant-btn, .ant-input, .ant-picker, .ant-picker-input, .ant-picker-focused {
+    border: none !important;
+    box-shadow: none !important;
+  }
+
+  input {
+    height: 32px;
+  }
+
+  .ant-select:not(.ant-select-customize-input) .ant-select-selector,
+  .ant-select:not(.ant-select-customize-input) .ant-select-selector,
+  .ant-input-affix-wrapper, .ant-select-selector {
+    border: none !important;
+    box-shadow: none !important;
+  }
+
+  .ant-select:not(.ant-select-disabled).ant-select:not(.ant-select-customize-input) > .ant-select-selector {
+    border-color: transparent;
+    box-shadow: none;
+  }
+}
+</style>