diff --git a/pages/attendanceStatus/attendanceStatistics.vue b/pages/attendanceStatus/attendanceStatistics.vue index e31b584..1fa656f 100644 --- a/pages/attendanceStatus/attendanceStatistics.vue +++ b/pages/attendanceStatus/attendanceStatistics.vue @@ -32,16 +32,17 @@ - + + + {{ selectDate ? $util.cutDate(new Date(selectDate), 'YYYY-MM') : "" }} - - + + + @@ -172,10 +173,12 @@ - - + + + 📊 暂无考勤数据 请选择其他时间或服务区查看 + @@ -200,7 +203,9 @@ export default { attendanceStatisticsData: [], isFirst: true, seatInfo: {}, - loading: false + loading: false, + hasLoaded: false, // 是否已经完成过初始加载 + retryCount: 0 } }, onLoad() { @@ -224,6 +229,19 @@ export default { return name ? name.charAt(0).toUpperCase() : '?' }, + // 重试加载 + retryLoad() { + if (this.retryCount < 3) { + this.retryCount++ + this.handleGetData(this.serviceInfo.SERVERPART_NAME) + } else { + uni.showToast({ + title: '多次重试失败,请检查网络连接', + icon: 'none' + }) + } + }, + // 格式化时长(分钟转小时分钟) formatDuration(minutes) { if (!minutes || minutes <= 0) return '' @@ -286,7 +304,11 @@ export default { "content-type": "application/x-www-form-urlencoded", }, success(res) { - resolve(res.data.data) + if (res.data && res.data.data) { + resolve(res.data.data) + } else { + reject(new Error('数据格式错误')) + } }, fail(err) { reject(err) @@ -303,7 +325,7 @@ export default { }) } this.attendanceStatisticsData = list - console.log('最终数据:', this.attendanceStatisticsData) + this.hasLoaded = true // 标记已完成加载 } catch (error) { console.error('获取考勤数据失败:', error) uni.showToast({ @@ -359,7 +381,7 @@ export default { }, // 修改日期 async handleChangeDate(type) { - this.attendanceStatisticsData = [] + // 月份切换时不清空数据,保持当前显示直到新数据加载完成 // type 1 加一天 2 减一天 // 兼容 iOS,把 2025-08-15 转成 2025/08/15 const cur = new Date((this.selectDate || '').replace(/-/g, '/')); @@ -376,6 +398,7 @@ export default { const d = String(last.getDate()).padStart(2, '0'); this.selectDate = `${y}-${m}-${d}`; + this.retryCount = 0 // 重置重试次数 await this.handleGetData(this.serviceInfo.SERVERPART_NAME) }, } @@ -509,9 +532,19 @@ export default { // border: 2rpx solid #27B35F; // background: rgba(255, 255, 255, .15); - .arrowIcon { - width: 40rpx; - height: 40rpx; + .arrowBtn { + display: flex; + align-items: center; + justify-content: center; + width: 60rpx; + height: 60rpx; + border-radius: 50%; + // background: rgba(39, 178, 95, 0.1); + + .arrowIcon { + width: 40rpx; + height: 40rpx; + } } .center { @@ -740,7 +773,6 @@ export default { display: flex; justify-content: space-between; flex-wrap: wrap; - // gap: 24rpx 16rpx; .statItem { display: flex; @@ -870,26 +902,45 @@ export default { flex-direction: column; align-items: center; justify-content: center; - padding: 120rpx 40rpx; + padding: 80rpx 40rpx; + text-align: center; + background: #fff; + border-radius: 16rpx; + margin-bottom: 32rpx; + box-shadow: @shadow; .emptyIcon { - width: 120rpx; - height: 120rpx; - margin-bottom: 32rpx; + font-size: 80rpx; + margin-bottom: 24rpx; opacity: 0.6; } .emptyText { font-size: 32rpx; - color: @muted; + color: #2c3e50; + font-weight: 600; margin-bottom: 16rpx; } .emptyDesc { font-size: 24rpx; - color: #bbb; - text-align: center; + color: #666; line-height: 1.5; + margin-bottom: 32rpx; + } + + .retryBtn { + background: @primary; + color: #fff; + border: none; + border-radius: 24rpx; + padding: 16rpx 32rpx; + font-size: 24rpx; + font-weight: 600; + + &::after { + border: none; + } } } } diff --git a/pages/attendanceStatus/roster.vue b/pages/attendanceStatus/roster.vue index 40bd60a..63cf732 100644 --- a/pages/attendanceStatus/roster.vue +++ b/pages/attendanceStatus/roster.vue @@ -27,16 +27,17 @@ - + + + {{ selectDate ? $util.cutDate(new Date(selectDate), 'YYYY-MM') : "" - }} - + }} + + + - @@ -61,14 +62,41 @@ - {{ selectedEmployee.userName }} - {{ selectedEmployee.psnJob }} - - {{ selectedEmployee.phone }} + {{ getFirstChar(selectedEmployee.userName) }} + + + + {{ selectedEmployee.userName || '-' }} + + + + + {{ selectedEmployee.phone || '-' }} + + + + {{ selectedEmployee.psnJob }} + + + + + 📋 + 暂无员工数据 + 请检查服务区设置或联系管理员 + + + + + + 📅 + 暂无排班数据 + 该员工在当前月份暂无排班安排 + + @@ -126,7 +154,10 @@ export default { selectUser: "",//当前选择的员工 isFirst: true, monthDays: [], - firstDayOfWeek: "" + firstDayOfWeek: "", + loading: false, + retryCount: 0, + hasLoaded: false, // 是否已经完成过初始加载 } }, computed: { @@ -171,14 +202,28 @@ export default { } }, methods: { + // 获取姓名首字符 + getFirstChar(name) { + return name ? name.charAt(0).toUpperCase() : '?' + }, + + // 重试加载 + retryLoad() { + if (this.retryCount < 3) { + this.retryCount++ + this.loadRosterData(this.serviceInfo.SERVERPART_NAME) + } else { + uni.showToast({ + title: '多次重试失败,请检查网络连接', + icon: 'none' + }) + } + }, + handleGetNewMonthData() { const year = this.currentYear const month = this.currentMonth - console.log('yearyearyear', year); - console.log('monthmonthmonthmonth', month); - - const daysInMonth = new Date(year, month + 1, 0).getDate() const today = new Date() const todayStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}` @@ -233,8 +278,10 @@ export default { this.currentMonth = last.getMonth() // 使用数字类型的月份索引 this.handleGetNewMonthData() + this.retryCount = 0 // 重置重试次数 - this.handleGetCurrentUserScheduleInfo() + // 月份切换时,不清空selectedEmployee,保持显示直到新数据加载完成 + await this.handleGetCurrentUserScheduleInfo() }, // 返回上一页 handleBack() { @@ -242,75 +289,149 @@ export default { }, // 员工选择变化 handleEmployeeChange(e) { - console.log('eee', e); - - this.selectedEmployeeIndex = Number(e.detail.value) - this.selectUser = this.userList && this.userList.length > 0 ? this.userList[this.selectedEmployeeIndex] : "" - - this.handleGetCurrentUserScheduleInfo() + const selectedIndex = Number(e.detail.value) + if (selectedIndex >= 0 && selectedIndex < this.userList.length) { + this.selectedEmployeeIndex = selectedIndex + this.selectUser = this.userList[selectedIndex] + // 切换员工时,暂时保持当前状态,直到新数据加载完成 + this.handleGetCurrentUserScheduleInfo() + } }, // 加载排班数据 async loadRosterData(SERVERPART_NAME) { - // 先拿到人员列表 + if (!SERVERPART_NAME) { + uni.showToast({ + title: '请先选择服务区', + icon: 'none' + }) + return + } + + this.loading = true let req = { bsessionKey: "0B30475A94674D608022885F7763959B", workTime: new Date(this.selectDate).getTime(), - saName: SERVERPART_NAME || "",// 服务区名称 - phone: "",// 手机号码 + saName: SERVERPART_NAME, + phone: "", } + uni.showLoading({ title: "加载中..." }) - const data = await new Promise((resolve, reject) => { - uni.request({ - url: "https://fwqznxj.yciccloud.com:9081/ynjt/pushManage/queryUserSchedule", - method: "POST", - data: req, - header: { - "content-type": "application/x-www-form-urlencoded", - }, - success(res) { - resolve(res.data.data) - }, + try { + const data = await new Promise((resolve, reject) => { + uni.request({ + url: "https://fwqznxj.yciccloud.com:9081/ynjt/pushManage/queryUserSchedule", + method: "POST", + data: req, + header: { + "content-type": "application/x-www-form-urlencoded", + }, + success(res) { + if (res.data && res.data.data) { + resolve(res.data.data) + } else { + reject(new Error('数据格式错误')) + } + }, + fail(err) { + reject(err) + } + }); }); - }); - uni.hideLoading() - console.log('人员列表', data); + const userList = Array.isArray(data) ? data : [] + this.userList = userList + this.selectedEmployeeIndex = 0 + this.selectUser = userList.length > 0 ? userList[0] : null - this.userList = data - this.selectedEmployeeIndex = 0 - this.selectUser = data && data.length > 0 ? data[0] : "" - console.log('this.selectUserthis.selectUserthis.selectUser', this.selectUser); + if (userList.length > 0) { + await this.handleGetCurrentUserScheduleInfo() + } else { + this.selectedEmployee = null + uni.showToast({ + title: '当前服务区暂无员工', + icon: 'none' + }) + } - await this.handleGetCurrentUserScheduleInfo() + // 标记已完成初始加载 + this.hasLoaded = true + } catch (error) { + console.error('加载排班数据失败:', error) + this.userList = [] + this.selectedEmployee = null + uni.showToast({ + title: error.message || '加载失败,请重试', + icon: 'none' + }) + } finally { + this.loading = false + uni.hideLoading() + } }, // 拿到当前用户的排班信息 async handleGetCurrentUserScheduleInfo() { - this.selectedEmployee = null + if (!this.selectUser || !this.selectUser.phone) { + // 只有在已经完成过初始加载后才显示空状态 + if (this.hasLoaded) { + this.selectedEmployee = null + } + return + } let req = { bsessionKey: "20ED2B50179D4877853C4DED45D8179E", date: new Date(this.selectDate).getTime(), phone: this.selectUser.phone } - const data = await new Promise((resolve, reject) => { - uni.request({ - url: "https://fwqznxj.yciccloud.com:9081/ynjt/pushManage/queryWorkDetails", - method: "POST", - data: req, - header: { - "content-type": "application/x-www-form-urlencoded", - }, - success(res) { - resolve(res.data.data) - }, - }); - }); - console.log('datadatadatadatadata', data); - this.selectedEmployee = data[0] + try { + const data = await new Promise((resolve, reject) => { + uni.request({ + url: "https://fwqznxj.yciccloud.com:9081/ynjt/pushManage/queryWorkDetails", + method: "POST", + data: req, + header: { + "content-type": "application/x-www-form-urlencoded", + }, + success(res) { + if (res.data && res.data.data && Array.isArray(res.data.data) && res.data.data.length > 0) { + resolve(res.data.data) + } else { + resolve(null) + } + }, + fail(err) { + reject(err) + } + }); + }); + + if (data && data.length > 0) { + this.selectedEmployee = data[0] + } else { + this.selectedEmployee = null + // 只有在已经加载过的情况下才显示提示 + if (this.hasLoaded) { + uni.showToast({ + title: '该员工暂无排班信息', + icon: 'none' + }) + } + } + + // 标记已完成加载 + this.hasLoaded = true + } catch (error) { + console.error('获取排班信息失败:', error) + this.selectedEmployee = null + uni.showToast({ + title: '获取排班信息失败', + icon: 'none' + }) + } } } } @@ -443,9 +564,19 @@ export default { // border: 2rpx solid #27B35F; // background: rgba(255, 255, 255, .15); - .arrowIcon { - width: 40rpx; - height: 40rpx; + .arrowBtn { + display: flex; + align-items: center; + justify-content: center; + width: 60rpx; + height: 60rpx; + border-radius: 50%; + // background: rgba(39, 178, 95, 0.1); + + .arrowIcon { + width: 40rpx; + height: 40rpx; + } } .center { @@ -489,39 +620,87 @@ export default { .employeeCard { background: #fff; - border-radius: 12rpx; - padding: 16rpx 24rpx; + border-radius: 16rpx; + padding: 24rpx; box-shadow: @shadow; + border: 1px solid rgba(39, 178, 95, 0.08); .employeeInfo { display: flex; - align-items: center; + align-items: flex-start; + justify-content: space-between; - .employeeName { - font-size: 28rpx; - font-weight: 600; - color: #333; - margin-right: 24rpx; - } - - .employeeJob { + .avatar { + width: 72rpx; + height: 72rpx; + border-radius: 50%; + background: @primary; + color: #fff; font-size: 24rpx; - color: @primary; - background: rgba(39, 178, 95, 0.1); - padding: 0 16rpx; - border-radius: 20rpx; + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; margin-right: 24rpx; + flex-shrink: 0; } - .phoneIcon { - width: 24rpx; - height: 24rpx; - margin-right: 8rpx; + .basicInfo { + flex: 1; + display: flex; + flex-direction: column; + gap: 12rpx; + margin-top: 4rpx; + + .nameRow { + display: flex; + align-items: center; + + .personIcon { + width: 24rpx; + height: 24rpx; + margin-right: 8rpx; + } + + .employeeName { + font-size: 28rpx; + font-weight: 600; + color: #2c3e50; + line-height: 1.3; + } + } + + .phoneRow { + display: flex; + align-items: center; + + .phoneIcon { + width: 24rpx; + height: 24rpx; + margin-right: 8rpx; + } + + .employeePhone { + font-size: 24rpx; + color: #666; + line-height: 1.3; + } + } } - .employeePhone { - font-size: 24rpx; - color: #666; + .jobInfo { + display: flex; + align-items: flex-start; + margin-top: 4rpx; + + .employeeJob { + font-size: 22rpx; + color: @primary; + background: rgba(39, 178, 95, 0.1); + padding: 6rpx 12rpx; + border-radius: 16rpx; + font-weight: 500; + } } } } @@ -577,7 +756,7 @@ export default { } &.today { - background-color: rgba(39, 178, 95, 0.1); + background-color: rgba(39, 178, 95, 0.15); border: 2rpx solid @primary; } @@ -621,6 +800,55 @@ export default { } } + /* 空状态样式 */ + .emptyState, + .emptySchedule { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 80rpx 40rpx; + text-align: center; + background: #fff; + border-radius: 16rpx; + margin-bottom: 32rpx; + box-shadow: @shadow; + + .emptyIcon { + font-size: 80rpx; + margin-bottom: 24rpx; + opacity: 0.6; + } + + .emptyText { + font-size: 32rpx; + color: #2c3e50; + font-weight: 600; + margin-bottom: 16rpx; + } + + .emptyDesc { + font-size: 24rpx; + color: #666; + line-height: 1.5; + margin-bottom: 32rpx; + } + + .retryBtn { + background: @primary; + color: #fff; + border: none; + border-radius: 24rpx; + padding: 16rpx 32rpx; + font-size: 24rpx; + font-weight: 600; + + &::after { + border: none; + } + } + } + } } \ No newline at end of file