gaoluyang
7 天以前 c4d25912d11ab9059f8165c25a161634bb9b5e97
src/pages/humanResources/attendance/checkin.vue
@@ -14,7 +14,7 @@
          <u-icon name="calendar"
                  color="#348fe2"
                  size="16"></u-icon>
          <text class="shift-text">白班: {{ todayRecord.startAt }}-{{ todayRecord.endAt }}</text>
          <text class="shift-text">{{ todayRecord.shift || '-' }}: {{ todayRecord.startAt }}-{{ todayRecord.endAt }}</text>
        </view>
      </view>
      <!-- 打卡按钮 -->
@@ -106,10 +106,7 @@
  const todayRecord = ref({});
  // 班次信息
  const workTimeDict = ref({
    startAt: "09:00",
    endAt: "18:00",
  });
  const workTimeDict = ref();
  // 当前时间展示
  const nowTime = ref("");
@@ -129,11 +126,18 @@
    findTodayPersonalAttendanceRecord({}).then(res => {
      if (res.data) {
        todayRecord.value = res.data;
        noNeedCheckIn.value = false;
        // 检查startAt和endAt是否为空,为空则无需打卡
        if (!todayRecord.value.startAt || !todayRecord.value.endAt) {
          noNeedCheckIn.value = true;
        } else {
          noNeedCheckIn.value = false;
        }
        updateInCheckRange();
      } else {
        // 页面显示“无需打卡”
        todayRecord.value = {};
        noNeedCheckIn.value = true;
        updateInCheckRange();
      }
    });
  };
@@ -156,21 +160,104 @@
    const m = String(now.getMinutes()).padStart(2, "0");
    const s = String(now.getSeconds()).padStart(2, "0");
    nowTime.value = `${Y}-${M}-${D} ${h}:${m}:${s}`;
    updateInCheckRange();
  };
  // 今日日期
  const todayStr = computed(() => nowTime.value.slice(0, 10));
  const parseHmToMinutes = hm => {
    if (!hm || typeof hm !== "string") return null;
    const [hStr, mStr] = hm.split(":");
    const h = Number(hStr);
    const m = Number(mStr);
    if (!Number.isFinite(h) || !Number.isFinite(m)) return null;
    if (h < 0 || h > 23 || m < 0 || m > 59) return null;
    return h * 60 + m;
  };
  const parseYmdToDate = ymd => {
    if (!ymd || typeof ymd !== "string") return null;
    const [yStr, mStr, dStr] = ymd.slice(0, 10).split("-");
    const y = Number(yStr);
    const m = Number(mStr);
    const d = Number(dStr);
    if (!Number.isFinite(y) || !Number.isFinite(m) || !Number.isFinite(d)) {
      return null;
    }
    return new Date(y, m - 1, d, 0, 0, 0, 0);
  };
  const addMinutes = (date, minutes) => {
    return new Date(date.getTime() + minutes * 60 * 1000);
  };
  const buildShiftWindow = () => {
    const startAtMinutes = parseHmToMinutes(todayRecord.value?.startAt);
    const endAtMinutes = parseHmToMinutes(todayRecord.value?.endAt);
    if (startAtMinutes === null || endAtMinutes === null) return null;
    const baseYmd = todayRecord.value?.date
      ? String(todayRecord.value.date).slice(0, 10)
      : todayStr.value;
    const baseDate = parseYmdToDate(baseYmd);
    if (!baseDate) return null;
    const startDateTime = addMinutes(baseDate, startAtMinutes);
    const crossDay = startAtMinutes > endAtMinutes;
    const endBase = crossDay ? addMinutes(baseDate, 24 * 60) : baseDate;
    const endDateTime = addMinutes(endBase, endAtMinutes);
    return { startDateTime, endDateTime };
  };
  const updateInCheckRange = () => {
    if (noNeedCheckIn.value) {
      inCheckRange.value = true;
      return;
    }
    const window = buildShiftWindow();
    if (!window) {
      inCheckRange.value = true;
      return;
    }
    const now = new Date();
    const checkInEarlyMinutes = 120;
    const checkOutLateMinutes = 720;
    const needAction = (() => {
      if (todayRecord.value?.workEndAt) return "done";
      if (todayRecord.value?.workStartAt) return "checkOut";
      const distToStart = Math.abs(
        now.getTime() - window.startDateTime.getTime()
      );
      const distToEnd = Math.abs(now.getTime() - window.endDateTime.getTime());
      return distToEnd < distToStart ? "checkOut" : "checkIn";
    })();
    const start = addMinutes(window.startDateTime, -checkInEarlyMinutes);
    const end = addMinutes(
      window.endDateTime,
      needAction === "checkOut" ? checkOutLateMinutes : 0
    );
    inCheckRange.value = now >= start && now <= end;
  };
  // 打卡按钮文本
  const checkInOutText = computed(() => {
    if (noNeedCheckIn.value) {
      return "无需打卡";
    }
    if (!todayRecord.value || !todayRecord.value.workStartAt) {
      return "上班打卡";
    }
    if (!todayRecord.value.workEndAt) {
      return "下班打卡";
      if (todayRecord.value.workStartAt) {
        return "下班打卡";
      }
      const window = buildShiftWindow();
      if (!window) return "上班打卡";
      const now = new Date();
      const distToStart = Math.abs(
        now.getTime() - window.startDateTime.getTime()
      );
      const distToEnd = Math.abs(now.getTime() - window.endDateTime.getTime());
      return distToEnd < distToStart ? "下班打卡" : "上班打卡";
    }
    return "已打卡";
  });
@@ -236,26 +323,52 @@
      // #endif
    });
  };
  const form = ref({
    longitude: "",
    latitude: "",
  });
  // 获取当前位置
  const getCurrentLocation = () => {
    return new Promise((resolve, reject) => {
      uni.getLocation({
        type: "wgs84",
        success: res => {
          currentLocation.value = res;
          // 模拟检查是否在打卡范围内(实际项目中应根据公司位置和允许的半径进行计算)
          // 这里简单模拟为始终在范围内
          inCheckRange.value = true;
          resolve(res);
        },
        fail: err => {
          console.error("获取位置失败:", err);
          // 失败时默认允许打卡(实际项目中应根据业务需求处理)
          inCheckRange.value = true;
          reject(err);
        },
      });
    uni.showLoading({ title: "获取位置中..." });
    uni.getLocation({
      type: "gcj02",
      success: res => {
        uni.hideLoading();
        form.value.latitude = res.latitude;
        form.value.longitude = res.longitude;
      },
      fail: err => {
        uni.hideLoading();
        console.error("获取位置失败:", err);
        // 显示错误提示并引导用户检查权限
        showToast("获取位置失败,请检查定位权限");
        // 引导用户检查权限设置
        uni.showModal({
          title: "位置权限提示",
          content:
            "获取位置失败,可能是因为位置权限未开启,请在设备设置中检查并开启位置权限。",
          confirmText: "知道了",
          cancelText: "取消",
          success: res => {
            if (res.confirm) {
              // 可以尝试打开设置页面(如果支持)
              if (uni.openSetting) {
                uni.openSetting({
                  success: settingRes => {
                    console.log("设置结果:", settingRes);
                  },
                });
              }
            }
          },
        });
        // 失败时显示错误信息
        form.value.visitAddress = "位置获取失败";
      },
    });
  };
@@ -318,7 +431,9 @@
    }
    // 调用打卡API
    createPersonalAttendanceRecord({})
    createPersonalAttendanceRecord({
      ...form.value,
    })
      .then(res => {
        // 记录打卡时间
        lastCheckInTime.value = Date.now();
@@ -331,7 +446,6 @@
        fetchTodayData();
      })
      .catch(error => {
        console.error("打卡失败:", error);
        uni.showToast({
          title: error.msg || "打卡失败,请重试",
          icon: "none",
@@ -347,7 +461,7 @@
    // 获取位置权限并检查位置
    try {
      await getLocationPermission();
      // await getLocationPermission();
      await getCurrentLocation();
    } catch (error) {
      console.error("位置权限获取失败:", error);
@@ -770,4 +884,4 @@
  .attendance-records {
    animation-delay: 0.2s;
  }
</style>
</style>