|
|
@@ -34,7 +34,7 @@
|
|
|
>
|
|
|
</view>
|
|
|
</view>
|
|
|
- <button class="check-fab press" :class="{ disabled: !canPunch }" @click.stop="onPunch">{{ canPunch ? '立即打卡' : '暂停打卡' }}</button>
|
|
|
+ <button class="check-fab press" :class="{ disabled: !canPunch }" @click.stop="onPunch">{{ punchButtonLabel }}</button>
|
|
|
</view>
|
|
|
|
|
|
<view class="quick card section">
|
|
|
@@ -107,7 +107,7 @@
|
|
|
import AppTabBar from "@/components/AppTabBar.vue";
|
|
|
import ModuleIcon from "@/components/ModuleIcon.vue";
|
|
|
import { computed, reactive, ref } from "vue";
|
|
|
-import { onShow } from "@dcloudio/uni-app";
|
|
|
+import { onShow, onHide } from "@dcloudio/uni-app";
|
|
|
import { quickActions, notices } from "@/data/mock";
|
|
|
import { useCurrentUser } from "@/hooks/useCurrentUser";
|
|
|
import { getCurrentUser } from "@/utils/storage";
|
|
|
@@ -183,6 +183,22 @@ function extractTime(value) {
|
|
|
return m ? m[1] : String(value);
|
|
|
}
|
|
|
|
|
|
+// 把 "18:00:00" / "2026-09-14 18:00:00" 这种班次时间解析为"今日该时刻"的 Date
|
|
|
+function parseShiftTime(value) {
|
|
|
+ if (value == null || value === "") return null;
|
|
|
+ const m = String(value).match(/(\d{1,2}):(\d{2})(?::(\d{2}))?/);
|
|
|
+ if (!m) return null;
|
|
|
+ const d = new Date();
|
|
|
+ d.setHours(Number(m[1]), Number(m[2]), Number(m[3] || 0), 0);
|
|
|
+ return d;
|
|
|
+}
|
|
|
+
|
|
|
+// 今日下班时刻,用于判断下班卡是否到点
|
|
|
+const shiftEndDate = computed(() => {
|
|
|
+ const raw = daily.scheduledEnd || shiftInfo.endTime;
|
|
|
+ return parseShiftTime(raw);
|
|
|
+});
|
|
|
+
|
|
|
const inText = computed(() => daily.actualIn || "--:--");
|
|
|
const outText = computed(() => daily.actualOut || "--:--");
|
|
|
|
|
|
@@ -207,8 +223,30 @@ const buttonState = computed(() => {
|
|
|
return { cls: "pending", mark: "", label: "打卡" };
|
|
|
});
|
|
|
|
|
|
-// 是否允许打卡:规则未配置 / 已停用 时禁用按钮
|
|
|
-const canPunch = computed(() => ruleStatus.value !== 'empty' && ruleStatus.value !== 'disabled');
|
|
|
+// 是否允许打卡:
|
|
|
+// - 规则未配置 / 已停用:禁用
|
|
|
+// - 已上班未下班,且当前时间未到下班点:禁用(下班卡不能提前打)
|
|
|
+const canPunch = computed(() => {
|
|
|
+ if (ruleStatus.value === 'empty' || ruleStatus.value === 'disabled') return false;
|
|
|
+ if (daily.actualIn && !daily.actualOut) {
|
|
|
+ const end = shiftEndDate.value;
|
|
|
+ if (end && now.value < end) return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+});
|
|
|
+
|
|
|
+// 打卡按钮文案:根据不同禁用原因给出明确提示,而不是统一"暂停打卡"
|
|
|
+const punchButtonLabel = computed(() => {
|
|
|
+ if (ruleStatus.value === 'empty' || ruleStatus.value === 'disabled') return "暂停打卡";
|
|
|
+ if (daily.actualIn && !daily.actualOut) {
|
|
|
+ const raw = daily.scheduledEnd || shiftInfo.endTime;
|
|
|
+ const end = shiftEndDate.value;
|
|
|
+ if (end && now.value < end) {
|
|
|
+ return `${extractTime(raw)} 后下班打卡`;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return "立即打卡";
|
|
|
+});
|
|
|
|
|
|
async function loadShiftInfo() {
|
|
|
const result = await queryAttendanceRule();
|
|
|
@@ -238,13 +276,31 @@ async function loadDaily() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// 每分钟刷新一次"当前时间",让 canPunch / 按钮文案能跨过下班点自动从禁用变可用
|
|
|
+let nowTimer = null;
|
|
|
+function startNowTimer() {
|
|
|
+ if (nowTimer) return;
|
|
|
+ nowTimer = setInterval(() => {
|
|
|
+ now.value = new Date();
|
|
|
+ }, 60 * 1000);
|
|
|
+}
|
|
|
+function stopNowTimer() {
|
|
|
+ if (nowTimer) {
|
|
|
+ clearInterval(nowTimer);
|
|
|
+ nowTimer = null;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
onShow(() => {
|
|
|
// 进入页面时刷新"当前时间",确保问候语与日期反映最新时段
|
|
|
now.value = new Date();
|
|
|
+ startNowTimer();
|
|
|
loadPending();
|
|
|
loadDaily();
|
|
|
});
|
|
|
|
|
|
+onHide(stopNowTimer);
|
|
|
+
|
|
|
// 待办角标:与工作台 / 底部导航(AppTabBar)同源,统一走审批中心
|
|
|
async function loadPending() {
|
|
|
try {
|
|
|
@@ -267,6 +323,13 @@ async function onPunch() {
|
|
|
const reason = ruleBlockedReason.value || '不可用';
|
|
|
return uni.showToast({ title: `考勤规则${reason},暂不能打卡`, icon: "none" });
|
|
|
}
|
|
|
+ // 兜底:已上班未下班、且当前时间未到下班点时禁用下班卡
|
|
|
+ if (daily.actualIn && !daily.actualOut) {
|
|
|
+ const end = shiftEndDate.value;
|
|
|
+ if (end && now.value < end) {
|
|
|
+ return uni.showToast({ title: `未到下班时间(${extractTime(daily.scheduledEnd || shiftInfo.endTime)}),暂不能打卡`, icon: "none" });
|
|
|
+ }
|
|
|
+ }
|
|
|
uni.showLoading({ title: "定位中..." });
|
|
|
try {
|
|
|
const loc = await getLocationWithFallback();
|