From 1ed366885433dfdec1241312356535b868c39eee Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期四, 26 二月 2026 16:21:04 +0800
Subject: [PATCH] 合同管理模块开发
---
src/pages/humanResources/attendance/checkin.vue | 351 +++++++++++++++++++++++++++++++++------------------------
1 files changed, 202 insertions(+), 149 deletions(-)
diff --git a/src/pages/humanResources/attendance/checkin.vue b/src/pages/humanResources/attendance/checkin.vue
index 970d35a..483b264 100644
--- a/src/pages/humanResources/attendance/checkin.vue
+++ b/src/pages/humanResources/attendance/checkin.vue
@@ -14,14 +14,14 @@
<u-icon name="calendar"
color="#348fe2"
size="16"></u-icon>
- <text class="shift-text">鐧界彮: 09:00-18:00</text>
+ <text class="shift-text">鐧界彮: {{ todayRecord.startAt }}-{{ todayRecord.endAt }}</text>
</view>
</view>
<!-- 鎵撳崱鎸夐挳 -->
<view class="checkin-button-container">
<view class="checkin-button-wrapper">
<view class="checkin-button"
- :class="{ 'disabled': checkInOutText === '宸叉墦鍗�' }"
+ :class="{ 'disabled': todayRecord.workEndAt || noNeedCheckIn }"
@click="handleCheckInOut">
<text class="checkin-button-text">{{ checkInOutText }}</text>
<text class="checkin-time">{{ nowTime.split(' ')[1] }}</text>
@@ -41,53 +41,53 @@
<view class="employee-info">
<view class="info-item">
<text class="info-label">閮ㄩ棬</text>
- <text class="info-value">{{ currentUser.dept }}</text>
+ <text class="info-value">{{ todayRecord.deptName || '-' }}</text>
</view>
<view class="info-item">
<text class="info-label">濮撳悕</text>
- <text class="info-value">{{ currentUser.name }}</text>
+ <text class="info-value">{{ todayRecord.staffName || '-' }}</text>
</view>
<view class="info-item">
<text class="info-label">宸ュ彿</text>
- <text class="info-value">{{ currentUser.no }}</text>
+ <text class="info-value">{{ todayRecord.staffNo || '-' }}</text>
</view>
</view>
<!-- 浠婃棩鑰冨嫟鐘舵�� -->
<view class="today-status">
- <u-icon :name="todayRecord ? 'checkmark-circle' : 'close-circle'"
- :color="todayRecord ? '#4cd964' : '#ff3b30'"
+ <u-icon :name="todayRecord.id ? 'checkmark-circle' : 'close-circle'"
+ :color="todayRecord.id ? '#4cd964' : '#ff3b30'"
size="16"></u-icon>
<text class="status-text">
- {{ todayRecord ? `浠婃棩鑰冨嫟: 涓婄彮 ${todayRecord.checkInTime}` : '浠婃棩鏈墦鍗�' }}
+ {{ todayRecord.id ? `浠婃棩鑰冨嫟: 涓婄彮 ${todayRecord.workStartAt || '-'}` : '浠婃棩鏈墦鍗�' }}
</text>
</view>
<!-- 涓嬬彮鑰冨嫟鐘舵�� -->
- <view v-if="todayRecord && todayRecord.checkOutTime"
+ <view v-if="todayRecord.id && todayRecord.workEndAt"
class="today-status">
- <u-icon :name="todayRecord ? 'checkmark-circle' : 'close-circle'"
- :color="todayRecord ? '#4cd964' : '#ff3b30'"
+ <u-icon name="checkmark-circle"
+ color="#4cd964"
size="16"></u-icon>
<text class="status-text">
- {{ `浠婃棩鑰冨嫟: 涓嬬彮 ${todayRecord.checkOutTime}` }}
+ {{ `浠婃棩鑰冨嫟: 涓嬬彮 ${todayRecord.workEndAt}` }}
</text>
</view>
<!-- 鎵撳崱鐘舵�� -->
- <view v-if="todayRecord"
- class="today-status">
- <u-icon :name="todayRecord.status === 'normal' ? 'checkmark-circle' : 'clock'"
- :color="todayRecord.status === 'normal' ? '#4cd964' : '#ff3b30'"
+ <view class="today-status">
+ <u-icon :name="todayRecord.id ? (todayRecord.status === 0 ? 'checkmark-circle' : 'clock') : 'clock'"
+ :color="todayRecord.id ? (todayRecord.status === 0 ? '#4cd964' : '#ff3b30') : '#ff3b30'"
size="16"></u-icon>
<text class="status-text">
- {{ `鎵撳崱鐘舵��: ${todayRecord.statusText}` }}
+ {{ `鎵撳崱鐘舵��: ${todayStatusText}` }}
</text>
</view>
- <view v-else
+ <!-- 宸ユ椂淇℃伅 -->
+ <view v-if="todayRecord.id && todayRecord.workHours"
class="today-status">
<u-icon name="clock"
- color="#ff3b30"
+ color="#348fe2"
size="16"></u-icon>
<text class="status-text">
- 鎵撳崱鐘舵��: 缂哄崱
+ {{ `宸ユ椂(灏忔椂): ${todayRecord.workHours}` }}
</text>
</view>
</view>
@@ -97,56 +97,57 @@
<script setup>
import { ref, reactive, computed, onMounted, onBeforeUnmount } from "vue";
import PageHeader from "@/components/PageHeader.vue";
- // 妯℃嫙褰撳墠鐧诲綍鍛樺伐
- const currentUser = reactive({
- id: 1,
- name: "寮犱笁",
- no: "E10001",
- dept: "鐢熶骇涓�閮�",
- });
+ import { getDicts } from "@/api/system/dict/data";
+ import {
+ createPersonalAttendanceRecord,
+ findTodayPersonalAttendanceRecord,
+ } from "@/api/personnelManagement/attendance.js";
+ // 浠婃棩鎵撳崱璁板綍
+ const todayRecord = ref({});
- // 妯℃嫙鑰冨嫟鍘熷鏁版嵁
- const rawAttendance = ref([
- {
- id: 2,
- date: "2026-02-08",
- userId: 1,
- name: "寮犱笁",
- no: "E10001",
- dept: "鐢熶骇涓�閮�",
- checkInTime: "09:15",
- checkOutTime: "18:05",
- workHours: 8.8,
- status: "late",
- statusText: "杩熷埌",
- remark: "鍥犱氦閫氭嫢鍫佃繜鍒�",
- },
- {
- id: 3,
- date: "2026-02-07",
- userId: 1,
- name: "寮犱笁",
- no: "E10001",
- dept: "鐢熶骇涓�閮�",
- checkInTime: "08:45",
- checkOutTime: "18:30",
- workHours: 9.7,
- status: "normal",
- statusText: "姝e父",
- remark: "鍔犵彮0.5灏忔椂",
- },
- ]);
+ // 鐝淇℃伅
+ const workTimeDict = ref({
+ startAt: "09:00",
+ endAt: "18:00",
+ });
// 褰撳墠鏃堕棿灞曠ず
const nowTime = ref("");
let timer = null;
+
+ // 涓婃鎵撳崱鏃堕棿
+ const lastCheckInTime = ref(null);
+
+ // 鎵撳崱鍐峰嵈鏃堕棿锛堟绉掞級
+ const CHECKIN_COOLDOWN = 5000;
// 杩斿洖涓婁竴椤�
const goBack = () => {
uni.navigateBack();
};
+ // 鏌ヨ浠婃棩鎵撳崱淇℃伅
+ const fetchTodayData = () => {
+ findTodayPersonalAttendanceRecord({}).then(res => {
+ if (res.data) {
+ todayRecord.value = res.data;
+ // 妫�鏌tartAt鍜宔ndAt鏄惁涓虹┖锛屼负绌哄垯鏃犻渶鎵撳崱
+ if (!todayRecord.value.startAt || !todayRecord.value.endAt) {
+ noNeedCheckIn.value = true;
+ } else {
+ noNeedCheckIn.value = false;
+ }
+ } else {
+ // 椤甸潰鏄剧ず鈥滄棤闇�鎵撳崱鈥�
+ todayRecord.value = {};
+ noNeedCheckIn.value = true;
+ }
+ });
+ };
// 鎵撳崱鑼冨洿鐘舵��
const inCheckRange = ref(true);
+
+ // 鏄惁鏃犻渶鎵撳崱
+ const noNeedCheckIn = ref(false);
// 褰撳墠浣嶇疆
const currentLocation = ref(null);
@@ -165,22 +166,42 @@
// 浠婃棩鏃ユ湡
const todayStr = computed(() => nowTime.value.slice(0, 10));
- // 褰撴棩褰撳墠鍛樺伐鑰冨嫟璁板綍
- const todayRecord = computed(() =>
- rawAttendance.value.find(
- item => item.userId === currentUser.id && item.date === todayStr.value
- )
- );
-
// 鎵撳崱鎸夐挳鏂囨湰
const checkInOutText = computed(() => {
- if (!todayRecord.value || !todayRecord.value.checkInTime) {
+ if (noNeedCheckIn.value) {
+ return "鏃犻渶鎵撳崱";
+ }
+ if (!todayRecord.value || !todayRecord.value.workStartAt) {
return "涓婄彮鎵撳崱";
}
- if (!todayRecord.value.checkOutTime) {
+ if (!todayRecord.value.workEndAt) {
return "涓嬬彮鎵撳崱";
}
return "宸叉墦鍗�";
+ });
+
+ // 浠婃棩鐘舵�佹爣绛剧被鍨�
+ const todayStatusTag = computed(() => {
+ if (!todayRecord.value.id) return "info";
+ if (todayRecord.value.status === 0) return "success";
+ return "danger";
+ });
+
+ // 浠婃棩鐘舵�佹枃鏈�
+ const todayStatusText = computed(() => {
+ if (!todayRecord.value.id) return "鏈墦鍗�";
+ switch (todayRecord.value.status) {
+ case 0:
+ return "姝e父";
+ case 1:
+ return "杩熷埌";
+ case 2:
+ return "鏃╅��";
+ case 3:
+ return "杩熷埌銆佹棭閫�";
+ case 4:
+ return "缂哄嫟";
+ }
});
// 瀵艰埅鍒拌缁嗘姤鍛婇〉闈�
@@ -220,31 +241,104 @@
// #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 = "浣嶇疆鑾峰彇澶辫触";
+ },
});
};
- // 鎵撳崱閫昏緫锛堜粎鍓嶇妯℃嫙锛�
+ // 鑾峰彇鐝瀛楀吀鏁版嵁
+ const getWorkTimeDict = () => {
+ getDicts("sys_work_time")
+ .then(res => {
+ if (res.data && res.data.length > 0) {
+ const dictData = res.data;
+ workTimeDict.value = {
+ startAt: dictData[0].dictValue || "-",
+ endAt: dictData[1].dictValue || "-",
+ };
+ }
+ })
+ .catch(error => {
+ console.error("鑾峰彇鐝瀛楀吀澶辫触:", error);
+ });
+ };
+
+ // 鎵撳崱閫昏緫
const handleCheckInOut = async () => {
+ if (noNeedCheckIn.value) {
+ uni.showToast({
+ title: "浠婃棩鏃犻渶鎵撳崱",
+ icon: "none",
+ });
+ return;
+ }
+
+ if (todayRecord.value.workEndAt) {
+ uni.showToast({
+ title: "鎮ㄥ凡缁忔墦杩囧崱浜�,鏃犻渶閲嶅鎵撳崱!!!",
+ icon: "none",
+ });
+ return;
+ }
+
+ // 妫�鏌ユ槸鍚﹀湪鎵撳崱鍐峰嵈鏃堕棿鍐�
+ if (lastCheckInTime.value) {
+ const currentTime = Date.now();
+ const timeDiff = currentTime - lastCheckInTime.value;
+ if (timeDiff < CHECKIN_COOLDOWN) {
+ const remainingTime = Math.ceil((CHECKIN_COOLDOWN - timeDiff) / 1000);
+ uni.showToast({
+ title: `璇�${remainingTime}绉掑悗鍐嶈瘯`,
+ icon: "none",
+ });
+ return;
+ }
+ }
+
// 妫�鏌ユ槸鍚﹀湪鎵撳崱鑼冨洿鍐�
if (!inCheckRange.value) {
uni.showToast({
@@ -254,79 +348,38 @@
return;
}
- const [dateStr, timeStr] = nowTime.value.split(" ");
- if (!dateStr || !timeStr) return;
+ // 璋冪敤鎵撳崱API
+ createPersonalAttendanceRecord({
+ ...form.value,
+ })
+ .then(res => {
+ // 璁板綍鎵撳崱鏃堕棿
+ lastCheckInTime.value = Date.now();
- // 涓婄彮鎵撳崱
- if (!todayRecord.value) {
- const newId = rawAttendance.value.length
- ? Math.max(...rawAttendance.value.map(i => i.id)) + 1
- : 1;
- const status = timeStr > "09:00:00" ? "late" : "normal";
- const statusText = status === "late" ? "杩熷埌" : "姝e父";
- rawAttendance.value.push({
- id: newId,
- date: dateStr,
- userId: currentUser.id,
- name: currentUser.name,
- no: currentUser.no,
- dept: currentUser.dept,
- checkInTime: timeStr.slice(0, 5),
- checkOutTime: "",
- workHours: null,
- status,
- statusText,
- remark: "",
+ uni.showToast({
+ title: "鎵撳崱鎴愬姛锛�",
+ icon: "success",
+ });
+ // 閲嶆柊鑾峰彇浠婃棩鎵撳崱淇℃伅
+ fetchTodayData();
+ })
+ .catch(error => {
+ uni.showToast({
+ title: error.msg || "鎵撳崱澶辫触锛岃閲嶈瘯",
+ icon: "none",
+ });
});
- uni.showToast({
- title: "涓婄彮鎵撳崱鎴愬姛",
- icon: "success",
- });
- } else if (!todayRecord.value.checkOutTime) {
- // 涓嬬彮鎵撳崱
- todayRecord.value.checkOutTime = timeStr.slice(0, 5);
- // 绠�鍗曟寜 9:00-18:00 璁$畻宸ユ椂
- const start = todayRecord.value.checkInTime || "09:00";
- const [sh, sm] = start.split(":").map(v => parseInt(v, 10));
- const [eh, em] = todayRecord.value.checkOutTime
- .split(":")
- .map(v => parseInt(v, 10));
- const diff = (eh * 60 + em - (sh * 60 + sm)) / 60;
- todayRecord.value.workHours = Number(Math.max(diff, 0).toFixed(1));
-
- // 鏃╅��鍒ゆ柇锛�18:00 鍓嶇寮�瑙嗕负鏃╅��锛堝彧绀烘剰锛�
- if (timeStr < "18:00:00") {
- if (todayRecord.value.status === "late") {
- // 鏃㈣繜鍒板張鏃╅��
- todayRecord.value.status = "late-early";
- todayRecord.value.statusText = "杩熷埌 + 鏃╅��";
- } else {
- // 浠呮棭閫�
- todayRecord.value.status = "early";
- todayRecord.value.statusText = "鏃╅��";
- }
- } else if (todayRecord.value.status === "normal") {
- todayRecord.value.statusText = "姝e父";
- }
- uni.showToast({
- title: "涓嬬彮鎵撳崱鎴愬姛",
- icon: "success",
- });
- } else {
- uni.showToast({
- title: "浠婃棩宸插畬鎴愪笂涓嬬彮鎵撳崱",
- icon: "none",
- });
- }
};
onMounted(async () => {
+ fetchTodayData();
updateNowTime();
timer = setInterval(updateNowTime, 1000);
+ getWorkTimeDict();
// 鑾峰彇浣嶇疆鏉冮檺骞舵鏌ヤ綅缃�
try {
- await getLocationPermission();
+ // await getLocationPermission();
await getCurrentLocation();
} catch (error) {
console.error("浣嶇疆鏉冮檺鑾峰彇澶辫触:", error);
--
Gitblit v1.9.3