ylj20011123 ad1b7773d5 update
2025-08-27 09:06:31 +08:00

1819 lines
66 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<page-meta :page-style="'overflow:' + (showPopup ? 'hidden' : 'visible')"></page-meta>
<view class="main" :style="{ paddingTop: (menu.bottom + 14) + 'px' }">
<!-- 自定义的页面顶部内容 -->
<view class="summaryTab" :style="{ height: (menu.bottom + 14) + 'px' }">
<view class="leftArrow" :style="{ top: (menu.top + ((menu.height - 24) / 2)) + 'px' }">
<image class="img" src="https://eshangtech.com/ShopICO/ahyd-BID/commercial/navigation-left.svg"
@click="handleBack"></image>
<view class="picker" :style="{ top: (menu.bottom + 24) + 'px' }" @click="handleChangeService">
<view class="selectService">
<image class="img" src="https://eshangtech.com/ShopICO/ahyd-BID/commercial/fixed.svg"></image>
<view class="select">
<view class="content">
<view class="uni-input">{{ serviceInfo.SAName ? serviceInfo.SAName :
'' }}
</view>
<!-- <p class="area">{{ serviceInfo.SPREGIONTYPE_NAME ? serviceInfo.SPREGIONTYPE_NAME : '' }} -->
<!-- </p> -->
<image class="rightArrow"
src="https://eshangtech.com/ShopICO/ahyd-BID/commercial/rightArrow.svg"></image>
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 服务区的信息 -->
<view class="serviceDetail" style="margin-top: 16rpx;">
<view class="detailTop">
<view class="serviceImg" v-if="serviceInfo.fileId && serviceInfo.fileId.length > 0">
<swiper scroll-x="true" circular="true" class="banner" indicator-dots="true"
indicator-active-color="#272625" autoplay interval="3000" :current="pageMsg.current"
style="width: 100%; height: 100%">
<swiper-item v-for="(img, i) in serviceInfo.fileId" :key="i" style="width: 100%; height: 100%">
<image style="width: 100%; height: 100%" mode="aspectFill" lazy-load="true"
:src="img.fileID ? 'https://fwqznxj.yciccloud.com:9081/fileDownloadApi/bsys/document/docDownloadAction?fileId=' + img.fileID : 'https://eshangtech.com/ShopICO/ahyd-BID/service/default.png'">
</image>
</swiper-item>
</swiper>
</view>
<view v-else class="serviceImg">
<image style="width: 100%; height: 100%" mode="aspectFill" lazy-load="true"
:src="'https://eshangtech.com/ShopICO/ahyd-BID/service/default.png'">
</image>
</view>
</view>
<!-- <view class="detailBottom">
<view class="detailFirst">
<text class="serviceName">{{
serviceInfo.SERVERPART_NAME || ""
}}</text>
<text class="serviceStatus">营业中</text>
</view>
<view class="detailSecond">
<image class="detailFixedIcon" src="/static/images/home/address.png" />
<text class="address">
<text class="distance">{{
serviceInfo.SERVERPART_DISTANCE === 0
? 0
: serviceInfo.SERVERPART_DISTANCE || "-"
}}km</text>
<text class="addressText">{{ serviceInfo.ServerpartInfo.SERVERPART_ADDRESS || "-"
}}{{ serviceInfo.ServerpartInfo.EXPRESSWAY_NAME || "-" }}</text>
</text>
</view>
<view class="navigationBox" @click="handleGomap">
<image class="navigationIcon" src="/static/images/home/navigationIcon.svg" />
</view>
</view> -->
</view>
<!-- 插入日历 即悬浮框 -->
<uni-calendar ref="calendar" class="selectDateCalendar" :mask-closable="true" :clear-date="true"
:date="selectDate" :insert="false" :startDate="info.startDate" :range="info.range"
@confirm="handleChangeTime" />
<view class="thingBox">
<!-- 特情事件提醒横幅 -->
<view class="emergency-banner" @click="showEmergencyDetails(1)">
<view class="emergency-header">
<view class="emergency-title">
<text>🚨 应急事件</text>
<view class="emergency-count">{{ emergencyListLength }}</view>
</view>
<!-- <text>👉</text> -->
</view>
</view>
<!-- 一般警告提醒 -->
<view class="warning-banner" @click="showEmergencyDetails(2)">
<view class="emergency-header">
<view class="emergency-title">
<text> 日常问题</text>
<view class="emergency-count">{{ dailyListLength }}</view>
</view>
<!-- <text>👉</text> -->
</view>
</view>
</view>
<!-- 人员类型的tab -->
<view class="personTabBox">
<view :class="selectTab === 1 ? 'personItem personItemSelect' : 'personItem '" @click="handleChangeTab(1)">
<view class="personValue">{{ onDutyPersonList && onDutyPersonList.length > 0 ? onDutyPersonList.length :
0 }}
</view>
<view class="personLabel">在岗人员</view>
</view>
<view :class="selectTab === 2 ? 'personItem personItemSelect' : 'personItem '" @click="handleChangeTab(2)">
<view class="personValue">{{ EmployeesOnLeaveList && EmployeesOnLeaveList.length > 0 ?
EmployeesOnLeaveList.length : 0 }}</view>
<view class="personLabel">请假人员</view>
</view>
<!-- <view :class="selectTab === 3 ? 'personItem personItemSelect' : 'personItem '" @click="handleChangeTab(3)">
<view class="personValue">1</view>
<view class="personLabel">值班经理</view>
</view> -->
</view>
<!-- 在岗人员 -->
<view class="onDutyPersonBox">
<view class="onDutyPersonBoxHeader">
<text class="title">{{ selectTab === 1 ? "👥 当前在岗人员" : selectTab === 2 ? "📋 请假人员列表" : selectTab === 3
? "👑 值班经理信息" : ""
}}</text>
<!-- 居中的日历时间 -->
<view class="dateBox">
<view class="centerDateBox">
<image class="arrowIcon" src="https://eshangtech.com/cyy_DIB/leftArrowIcon.png"
@click="handleChangeDate(2)" />
<view class="center" @click="open">
<text class="date-text">{{ selectDate }}</text>
<!-- <text class="date-icon">📅</text> -->
</view>
<image class="arrowIcon" src="https://eshangtech.com/cyy_DIB/rightArrowIcon.png"
@click="handleChangeDate(1)" />
</view>
</view>
<!-- <view class="tab-indicators">
<view class="tab-dot" :class="{ active: selectTab === 1 }"></view>
<view class="tab-dot" :class="{ active: selectTab === 2 }"></view>
</view> -->
</view>
<!-- 当前在岗人员 -->
<view class="onDutyPersonList" v-if="selectTab === 1">
<view class="onDutyPersonItem" v-for="(item, index) in onDutyPersonList" :key="index"
@click="viewStaffDetails(item)">
<view class="avatar" v-if="!item.phone">{{ item.userName ? item.userName.slice(0, 1) : "" }}</view>
<image class="avatar" v-else
:src="`https://fwqznxj.yciccloud.com:9081/fileDownloadApi/bsys/file/thumbnail/download/${item.phone}`" />
<view class="staffInfo">
<view class="staffInfoLeft">
<view class="staffInfoTop">
<view class="staff-name">{{ item.userName || "" }}</view>
<view class="staff-role" v-if="item.userJob">{{ item.userJob }}</view>
</view>
<view class="staff-status">
<view class="status-badge status-online" v-if="item.workType">{{ item.workType }}
</view>
</view>
</view>
<view class="staffInfoRight" @click.stop="handleCallPhone(item.phone)">
<image class="phoneIcon" src="https://eshangtech.com/cyy_DIB/phoneIcon.png" />
</view>
</view>
</view>
</view>
<!-- 请假人员 -->
<view class="EmployeesOnLeaveList" v-if="selectTab === 2">
<view class="EmployeesOnLeaveItem" v-for="(item, index) in EmployeesOnLeaveList" :key="index"
@click="viewStaffDetails(item)">
<view class="avatar" v-if="!item.phone">{{ item.userName ? item.userName.slice(0, 1) : "" }}</view>
<image class="avatar" v-else
:src="`https://fwqznxj.yciccloud.com:9081/fileDownloadApi/bsys/file/thumbnail/download/${item.phone}`" />
<view class="staffInfo">
<view class="staffInfoLeft">
<view class="staffInfoTop">
<view class="staff-name">{{ item.userName || "" }}</view>
<view class="staff-role">{{ item.userJob || "" }}</view>
</view>
<view class="staff-status">
<view class="status-badge status-rest" v-if="item.workType">{{ item.workType }}</view>
</view>
</view>
<view class="staffInfoRight" @click.stop="handleCallPhone(item.phone)">
<image class="phoneIcon" src="https://eshangtech.com/cyy_DIB/phoneIcon.png" />
</view>
</view>
</view>
</view>
<!-- 值班经理 -->
<view class="dutyManagerList" v-if="selectTab === 3">
<view class="dutyManagerItem" v-for="(item, index) in dutyManagerList" :key="index"
@click="viewStaffDetails(item)">
<view class="avatar manager-avatar" v-if="!item.phone">{{ item.userName ? item.userName.slice(0, 1)
: "" }}</view>
<image class="avatar manager-avatar" v-else
:src="`https://fwqznxj.yciccloud.com:9081/fileDownloadApi/bsys/file/thumbnail/download/${item.phone}`" />
<view class="staffInfoBox">
<view class="staffInfo">
<view class="staffInfoLeft">
<view class="staffInfoTop">
<view class="staff-name manager-name">{{ item.name || "" }}</view>
<view class="staff-role manager-role">👑{{ item.post || "-" }}</view>
</view>
<view class="staff-status">
<view class="status-badge status-manager">值班中</view>
</view>
</view>
<view class="staffInfoRight" @click.stop="handleCallPhone(item.phone)">
<image class="phoneIcon" src="https://eshangtech.com/cyy_DIB/phoneIcon.png" />
</view>
</view>
<view class="staff-address">{{ item.address }}</view>
</view>
</view>
</view>
</view>
<!-- 其他跳转链接 -->
<view class="otherJumpUrl">
<view class="jumpItem" v-for="(item, index) in jumpItemList" :key="index"
@click="handleClickJump(item.value)">
<image class="jumpIcon" :src="item.url" />
<view class="jumpText">{{ item.label }}</view>
</view>
</view>
<!-- 会员详情 -->
<uni-popup ref="popup" border-radius="10px 10px 0 0" @change="handlePopupChange">
<view class="userPopupBox" v-if="userDetail">
<view class="userBoxTitle">人员详情</view>
<view class="userDetailBox">
<view class="userAvatar">
<image class="userAvatarIcon" v-if="userDetail.phone"
:src="`https://fwqznxj.yciccloud.com:9081/fileDownloadApi/bsys/file/thumbnail/download/${userDetail.phone}`" />
<view class="userAvatarBox" v-else>{{ userDetail.userName ? userDetail.userName.slice(0, 1) : ""
}}
</view>
</view>
<view class="userDetailRight">
<view class="userDetailTop">
<view class="userDetailName">{{ userDetail.userName || "" }}</view>
<view class="userDetailStatus" v-if="userDetail.workType" :style="{
background: userDetail.workType === '班' || userDetail.workType === '差' || userDetail.workType === '出' ? '#27B25F' : '#F6F6F6',
color: userDetail.workType === '班' || userDetail.workType === '差' || userDetail.workType === '出' ? '#fff' : '#9FA6A3',
}">{{ userDetail.workType }}</view>
</view>
<view class="userDetailPostBox">
<span class="userDetailLabel">岗位</span>
<span class="userDetailValue">{{ userDetail.userJob || "-" }}</span>
</view>
<view class="userDetailPhoneBox">
<span class="userDetailLabel">电话</span>
<span class="userDetailValue">{{ userDetail.phone || "" }}</span>
</view>
</view>
</view>
<view class="line"></view>
<view class="userBoxTitle">当前出勤情况</view>
<view class="attendanceStatusBox">
<view class="attendanceStatusItem">
<view class="attendanceStatusLabel">当前排班信息</view>
<view class="attendanceStatusValue" v-if="userDetail.workType">{{ userDetail.workType }}</view>
</view>
<view class="attendanceStatusItem">
<view class="attendanceStatusLabel">上班打卡地</view>
<view class="attendanceStatusValue">{{ userDetail.dutyClockInPlace || "" }}</view>
<!-- dutyClockInPlace -->
</view>
<view class="attendanceStatusItem">
<view class="attendanceStatusLabel">上班打卡时间</view>
<view class="attendanceStatusValue">{{ userDetail.dutyClockInTime || "" }}</view>
<!-- dutyClockInTime -->
</view>
<view class="attendanceStatusItem">
<view class="attendanceStatusLabel">迟到时长</view>
<view class="attendanceStatusValue">{{ userDetail.lateNum || "" }}</view>
<!-- lateNum -->
</view>
<view class="attendanceStatusItem">
<view class="attendanceStatusLabel">下班打卡地</view>
<view class="attendanceStatusValue">{{ userDetail.offDutyClockInPlace || "" }}</view>
<!-- offDutyClockInPlace -->
</view>
<view class="attendanceStatusItem">
<view class="attendanceStatusLabel">下班打卡时间</view>
<view class="attendanceStatusValue">{{ userDetail.offDutyClockInTime || "" }}</view>
<!-- offDutyClockInTime -->
</view>
<view class="attendanceStatusItem">
<view class="attendanceStatusLabel">早退时长</view>
<view class="attendanceStatusValue">{{ userDetail.earlyNum || "" }}</view>
<!-- earlyNum -->
</view>
</view>
<view class="line"></view>
<view class="userBoxTitle">考勤统计</view>
<view class="attendanceStatusBox">
<view class="attendanceStatusItem">
<view class="attendanceStatusLabel">当月排班天数</view>
<view class="attendanceStatusValue">{{ userDetail.scheduleTotal || "" }}</view>
<!-- scheduleTotal -->
</view>
<view class="attendanceStatusItem">
<view class="attendanceStatusLabel">出勤天数</view>
<view class="attendanceStatusValue">{{ userDetail.attendTotal || "" }}</view>
<!-- attendTotal -->
</view>
<view class="attendanceStatusItem">
<view class="attendanceStatusLabel">休息天数</view>
<view class="attendanceStatusValue">{{ userDetail.restTotal || "" }}</view>
<!-- restTotal -->
</view>
<view class="attendanceStatusItem">
<view class="attendanceStatusLabel">迟到天数</view>
<view class="attendanceStatusValue">{{ userDetail.lateTotal || "" }}</view>
<!-- lateTotal -->
</view>
<view class="attendanceStatusItem">
<view class="attendanceStatusLabel">早退天数</view>
<view class="attendanceStatusValue">{{ userDetail.earlyTotal || "" }}</view>
<!-- earlyTotal -->
</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import request from "@/util/index.js";
import { formatTime } from '@/util/dateTime/index.js'
export default {
data() {
const nowDay = this.$util.cutDate(new Date(), 'YYYY-MM-DD') // 有数据的最近日期
return {
menu: {},
serviceInfo: {},
selectDate: nowDay,// 选中的时间
info: {
startDate: "2025-01-01"
},
selectTab: 1,
onDutyPersonList: [
// {
// name: "曹鑫",
// post: "三级驻站经理",
// status: 1,
// phone: "13987822022",
// service: "达连坝停车区-下行",
// dutyPlace: "", // 上班打卡地
// dutyTime: "2025-08-14 07:26",// 上班打卡时间
// toLateTime: "",// 迟到时长
// offWorkPlace: "",// 下班打卡地
// offWorkTime: "",// 下班打卡时间
// earlyWorkTime: "",// 早退时长
// monthDay: "23",// 当月排班天数
// AttendanceDay: "10",// 出勤天数
// leaveDay: "8",// 休息天数
// beLateDay: "",// 迟到天数
// leaveEarlyDay: "1",// 早退天数
// },
// {
// name: "陈艳雄",
// post: "安全管理岗",
// status: 1,
// phone: "18787834836",
// service: "昆西片区管理部",
// dutyPlace: "", // 上班打卡地
// dutyTime: "",// 上班打卡时间
// toLateTime: "",// 迟到时长
// offWorkPlace: "",// 下班打卡地
// offWorkTime: "",// 下班打卡时间
// earlyWorkTime: "",// 早退时长
// monthDay: "23",// 当月排班天数
// AttendanceDay: "0",// 出勤天数
// leaveDay: "8",// 休息天数
// beLateDay: "",// 迟到天数
// leaveEarlyDay: "",// 早退天数
// },
// {
// name: "陈芝丽",
// post: "三级驻站经理",
// status: 1,
// phone: "15887779180",
// service: "猫街停车区-下行",
// dutyPlace: "", // 上班打卡地
// dutyTime: "2025-08-14 07:11",// 上班打卡时间
// toLateTime: "",// 迟到时长
// offWorkPlace: "",// 下班打卡地
// offWorkTime: "",// 下班打卡时间
// earlyWorkTime: "",// 早退时长
// monthDay: "23",// 当月排班天数
// AttendanceDay: "11",// 出勤天数
// leaveDay: "8",// 休息天数
// beLateDay: "",// 迟到天数
// leaveEarlyDay: "1",// 早退天数
// }
],// 在岗人员 1 在岗 2 休息 3 调休 4 请假 5 旷工 6 迟到
EmployeesOnLeaveList: [
// {
// name: "高金平",
// post: "三级驻站经理",
// status: 2,
// phone: "17787888023",
// service: "大姚服务区-下行",
// dutyPlace: "", // 上班打卡地
// dutyTime: "",// 上班打卡时间
// toLateTime: "",// 迟到时长
// offWorkPlace: "",// 下班打卡地
// offWorkTime: "",// 下班打卡时间
// earlyWorkTime: "",// 早退时长
// monthDay: "23",// 当月排班天数
// AttendanceDay: "9",// 出勤天数
// leaveDay: "8",// 休息天数
// beLateDay: "",// 迟到天数
// leaveEarlyDay: "",// 早退天数
// },
// {
// name: "雷自云",
// post: "三级驻站经理",
// status: 2,
// phone: "15825167023",
// service: "永仁停车区-下行",
// dutyPlace: "", // 上班打卡地
// dutyTime: "",// 上班打卡时间
// toLateTime: "",// 迟到时长
// offWorkPlace: "",// 下班打卡地
// offWorkTime: "",// 下班打卡时间
// earlyWorkTime: "",// 早退时长
// monthDay: "23",// 当月排班天数
// AttendanceDay: "8",// 出勤天数
// leaveDay: "8",// 休息天数
// beLateDay: "",// 迟到天数
// leaveEarlyDay: "1",// 早退天数
// },
// {
// name: "刘凯峰",
// post: "一级驻站副经理",
// status: 2,
// phone: "15198787593",
// service: "恐龙山服务区-上行",
// dutyPlace: "", // 上班打卡地
// dutyTime: "",// 上班打卡时间
// toLateTime: "",// 迟到时长
// offWorkPlace: "",// 下班打卡地
// offWorkTime: "",// 下班打卡时间
// earlyWorkTime: "",// 早退时长
// monthDay: "23",// 当月排班天数
// AttendanceDay: "9",// 出勤天数
// leaveDay: "8",// 休息天数
// beLateDay: "",// 迟到天数
// leaveEarlyDay: "",// 早退天数
// },
// {
// name: "宋永鑫",
// post: "一级驻站副经理",
// service: "读书铺服务区-上行",
// status: 2,
// phone: "13649668882",
// dutyPlace: "", // 上班打卡地
// dutyTime: "",// 上班打卡时间
// toLateTime: "",// 迟到时长
// offWorkPlace: "",// 下班打卡地
// offWorkTime: "",// 下班打卡时间
// earlyWorkTime: "",// 早退时长
// monthDay: "23",// 当月排班天数
// AttendanceDay: "13",// 出勤天数
// leaveDay: "8",// 休息天数
// beLateDay: "",// 迟到天数
// leaveEarlyDay: "",// 早退天数
// }
],// 请假人员列表
dutyManagerList: [
// { name: "王五", post: "经理", status: 1, phone: "6666", address: "G56杭瑞高速主干道昆明至滇西方向" },
],// 值班经理
jumpItemList: [
// { label: "应急事件", value: 1, url: "https://eshangtech.com/cyy_DIB/funIcon1.png" },
{ label: "设施状态", value: 2, url: "https://eshangtech.com/cyy_DIB/funIcon2.png" },
{ label: "考勤统计", value: 3, url: "https://eshangtech.com/cyy_DIB/funIcon3.png" },
{ label: "排班表", value: 5, url: "https://eshangtech.com/cyy_DIB/funIcon5.png" },
// { label: "请假管理", value: 4, url: "https://eshangtech.com/cyy_DIB/funIcon4.png" },
// { label: "通知公告", value: 6, url: "https://eshangtech.com/cyy_DIB/funIcon6.png" },
],
showPopup: false,
userDetail: null,
isFirst: true,
emergencyListLength: 0,
dailyListLength: 0,
};
},
onLoad() {
this.menu = uni.getMenuButtonBoundingClientRect()
let currentService = uni.getStorageSync('currentService')
console.log('currentServicecurrentServicecurrentService', currentService);
// 防护:确保 currentService 存在
if (currentService && (currentService.SAName || currentService.SACode)) {
this.serviceInfo = {
...currentService,
}
this.handleGetCurrentServiceAttendanceData(currentService.SAName)
this.handleGetEventsData(currentService.SAName)
} else {
console.warn('onLoad: currentService 为空或无效')
// 可以考虑跳转回服务区选择页面或显示错误提示
}
},
onShow() {
let currentService = uni.getStorageSync('currentService')
console.log('onShowonShowonShowonShow', currentService);
// 防护:检查 currentService 是否有效
if (!currentService || (!currentService.SACode)) {
console.warn('currentService 为空或无效,跳过更新');
this.isFirst = false;
return;
}
// 获取统一的服务区ID
const currentServiceId = currentService.SACode;
const currentServiceName = currentService.SAName;
// 只有当服务区真正发生变化且不是首次加载时才更新
if (currentServiceId && currentServiceId !== this.serviceInfo.SACode && !this.isFirst) {
console.log('111111', currentService);
this.emergencyListLength = 0
this.dailyListLength = 0
this.serviceInfo = {
...currentService,
}
// this.handleGetServerpartDetail(currentServiceId);
this.handleGetCurrentServiceAttendanceData(currentServiceName);
this.handleGetEventsData(currentServiceName)
} else {
this.isFirst = false;
}
},
methods: {
// 现在异步调用一个方法 拿到 该服务区应急事件、日常事件
async handleGetEventsData(saName) {
const req = {
bsessionKey: "EA65F66FA29B47FD8072A4AFC66967B3",
saName: saName || this.serviceInfo.SAName
}
console.log('reqreq', req);
const data = await new Promise((resolve, reject) => {
uni.request({
url: "https://fwqznxj.yciccloud.com:9081/ynjt/pushManage/queryEmergency",
method: "POST",
data: req,
header: {
"content-type": "application/x-www-form-urlencoded",
},
success(res) {
resolve(res.data.data)
},
});
});
let emergencyList = data
const req2 = {
bsessionKey: "0DAF3A5982D54A619D4B63A34CA20C55",
saName: saName || this.serviceInfo.SAName
}
const data2 = await new Promise((resolve, reject) => {
uni.request({
url: "https://fwqznxj.yciccloud.com:9081/ynjt/pushManage/queryQuestions",
method: "POST",
data: req2,
header: {
"content-type": "application/x-www-form-urlencoded",
},
success(res) {
resolve(res.data.data)
},
});
});
let dailyList = data2
console.log('emergencyListemergencyListemergencyList', emergencyList);
console.log('dailyListdailyListdailyListdailyListdailyList', dailyList);
this.emergencyListLength = emergencyList && emergencyList.length > 0 ? emergencyList.length : "0"
this.dailyListLength = dailyList && dailyList.length > 0 ? dailyList.length : "0"
},
// 拿到当前服务区的考勤数据
async handleGetCurrentServiceAttendanceData(SERVERPART_NAME) {
// 防护:如果服务区名称为空,则不执行请求
if (!SERVERPART_NAME) {
console.warn('handleGetCurrentServiceAttendanceData: SERVERPART_NAME 为空,跳过请求');
return;
}
let req = {
bsessionKey: "0B30475A94674D608022885F7763959B",
workTime: new Date(this.selectDate).getTime(),
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)
},
});
});
uni.hideLoading()
this.onDutyPersonList = []
this.EmployeesOnLeaveList = []
// 当前服务区的人员信息
let list = data
console.log("listlistlistlistlistlistlistlist", list);
// 上班人员信息
let workPersonList = []
// 请假人员信息
let leaveList = []
if (list && list.length > 0) {
list.forEach((item) => {
item.dutyClockInTime = item.dutyClockInTime ? formatTime(item.dutyClockInTime) : ""
item.offDutyClockInTime = item.offDutyClockInTime ? formatTime(item.offDutyClockInTime) : ""
if (item.workType === '班' || item.workType === '差' || item.workType === '出') {
workPersonList.push(item)
} else {
leaveList.push(item)
}
})
}
this.onDutyPersonList = workPersonList
this.EmployeesOnLeaveList = leaveList
},
// 修改日期
async handleChangeDate(type) {
// type 1 加一天 2 减一天
// 兼容 iOS把 2025-08-15 转成 2025/08/15
const date = new Date(this.selectDate.replace(/-/g, "/"));
if (type === 1) {
date.setDate(date.getDate() + 1); // 加一天
} else if (type === 2) {
date.setDate(date.getDate() - 1); // 减一天
}
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
this.selectDate = `${year}-${month}-${day}`;
console.log('serviceInfo', this.serviceInfo);
await this.handleGetCurrentServiceAttendanceData(this.serviceInfo.SAName)
},
// 查询服务区详情
async handleGetServerpartDetail(id) {
// 防护如果ID为空则不执行请求
if (!id) {
console.warn('handleGetServerpartDetail: id 为空,跳过请求');
return;
}
let currentService = uni.getStorageSync("currentService");
let seatInfo = uni.getStorageSync("seatInfo");
this.seatInfo = JSON.parse(seatInfo || '{}');
let req = {
ServerpartId: id,
latitude: this.seatInfo.latitude || 0,
longitude: this.seatInfo.longitude || 0,
};
console.log('req', req);
uni.showLoading({
title: "加载中...",
});
const data = await request.$webGet(
"/CommercialApi/BaseInfo/GetServerpartInfo",
req
);
uni.hideLoading();
let obj = data.Result_Data;
this.serviceInfo = obj;
this.$forceUpdate();
console.log('this.serviceInfo', this.serviceInfo);
},
// 打开导航
handleGomap() {
let obj = this.serviceInfo;
uni.openLocation({
latitude: obj.latitude ? obj.latitude * 1 : obj.SERVERPART_Y * 1,
longitude: obj.longitude ? obj.longitude * 1 : obj.SERVERPART_X * 1,
scale: 16, // 缩放比例
name: obj.SAName,
// address: "", // 这个可能会影响地图的定位,所以可以选择不填
success(data) {
},
fail(err) {
},
});
},
handleChangeService() {
this.$util.toNextRoute("navigateTo", "/pages/map/index?type=attendanceStatus");
},
open() {
this.$refs.calendar.open()
},
handlePopupChange(e) {
this.showPopup = e.show
console.log('e', e);
if (!e.show) {
this.userDetail = null
}
},
handleBack() {
uni.navigateBack({
delta: 1
});
},
// 改变查询时间
async handleChangeTime(e) {
console.log('e', e);
// this.selectDate = e.detail.value
this.selectDate = e.fulldate
await this.handleGetCurrentServiceAttendanceData(this.serviceInfo.SAName)
},
// 切换tab
handleChangeTab(e) {
this.selectTab = e
},
// 打电话
handleCallPhone(phone) {
uni.makePhoneCall({
phoneNumber: phone
});
},
// 跳转应急事件页面
showEmergencyDetails(type) {
// type 1 应急事件 2 日常问题
this.$util.toNextRoute("navigateTo", `/pages/attendanceStatus/emergencyEvents?type=${type}`);
},
// 跳转其他页面
handleClickJump(value) {
if (value === 1) {
this.showEmergencyDetails(1)
} else if (value === 2) {
// this.$util.toNextRoute("navigateTo", `/pages/attendanceStatus/serviceFacility`);
this.$util.toNextRoute('navigateTo', `/pages/summaryOfPortraits/index?index=0`)
} else if (value === 3) {
this.$util.toNextRoute('navigateTo', `/pages/attendanceStatus/attendanceStatistics`)
} else if (value === 5) {
this.$util.toNextRoute('navigateTo', `/pages/attendanceStatus/roster`)
}
},
// 会员详情信息
viewStaffDetails(obj) {
this.$refs.popup.open('center')
this.userDetail = obj
}
},
onUnload() {
}
};
</script>
<style lang="less" scoped>
@bg: #f8f9fa;
@muted: #666;
@card: #fff;
@shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
@primary: #27B25F;
@primary2: #4CCC7F;
@ok: #2ed573;
@warn: #ff9f43;
@danger: #ff4757;
.main {
width: 100vw;
min-height: 100vh;
// background-image: url("https://eshangtech.com/minTestImg/pageBg.png");
background-color: #F5F5F5;
box-sizing: border-box;
padding-bottom: env(safe-area-inset-bottom);
.summaryTab {
position: fixed;
left: 0;
top: 0;
width: 100vw;
// background: #fff;
background-image: url("https://eshangtech.com/minTestImg/pageBg.png");
z-index: 99;
display: flex;
align-items: center;
box-sizing: border-box;
padding-bottom: 16px;
.leftArrow {
width: 100%;
height: 24px;
position: absolute;
// left: 16px;
z-index: 99999999999;
box-sizing: border-box;
display: flex;
align-items: center;
padding: 0 32rpx;
.img {
width: 24px;
height: 24px;
margin-right: 8px;
z-index: 99;
}
.picker {
.selectService {
display: flex;
align-items: center;
.img {
width: 40px;
height: 40px;
z-index: 2;
}
.select {
height: 32px;
background: #fff;
border-radius: 0 16px 16px 0;
transform: translateX(-40px);
box-sizing: border-box;
padding-left: 35px;
padding-right: 8rpx;
display: flex;
align-items: center;
.content {
display: flex;
align-items: center;
.uni-input {
padding: 0;
background: transparent;
font-size: 14px;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
color: #160002;
line-height: 50rpx;
height: 50rpx;
}
.area {
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #786B6C;
line-height: 40px;
margin-left: 4px;
}
.rightArrow {
width: 12px;
height: 12px;
}
.noticeText {
font-size: 24rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
color: #B6BACB;
line-height: 40rpx;
white-space: nowrap;
}
}
}
}
}
}
}
.serviceDetail {
width: calc(100% - 64rpx);
background: #ffffff;
border-radius: 16rpx;
margin-left: 32rpx;
.detailTop {
width: 100%;
height: 320rpx;
border-radius: 16rpx;
overflow: hidden;
.serviceImg {
width: 100%;
height: 100%;
}
}
.detailBottom {
width: 100%;
box-sizing: border-box;
padding: 4rpx 24rpx 24rpx;
position: relative;
.detailFirst {
width: 100%;
.serviceName {
width: calc(100% - 190rpx);
font-family: PingFangSC, PingFang SC;
font-weight: 600;
font-size: 30rpx;
color: #130f05;
line-height: 52rpx;
text-align: left;
font-style: normal;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
margin-right: 16rpx;
}
.serviceStatus {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 22rpx;
color: #009f43;
line-height: 32rpx;
text-align: justify;
font-style: normal;
display: inline-block;
padding: 2rpx 6rpx;
background: #e9f8ee;
border-radius: 4rpx;
}
}
.detailSecond {
width: 100%;
display: flex;
align-items: center;
.detailFixedIcon {
width: 24rpx;
height: 24rpx;
margin-right: 4rpx;
}
.address {
width: calc(100% - 120rpx);
display: inline-block;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
.distance {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 24rpx;
color: #130f05;
line-height: 36rpx;
text-align: left;
font-style: normal;
margin-right: 8rpx;
}
.addressText {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 24rpx;
color: #716f69;
line-height: 36rpx;
text-align: left;
font-style: normal;
}
}
}
.navigationBox {
width: 64rpx;
height: 64rpx;
position: absolute;
right: 24rpx;
top: 32rpx;
.navigationIcon {
width: 100%;
height: 100%;
}
}
}
}
.selectDateCalendar {
/deep/ .uni-calendar-item__weeks-box-item {
width: calc(100% / 7);
}
/deep/ .uni-calendar-item--checked {
background-color: #3CC071;
}
/deep/ .uni-calendar-item--isDay {
background-color: #3CC071;
color: #fff !important;
}
/deep/ .uni-calendar-item--isDay-text {
white-space: nowrap;
color: #000;
}
/deep/ .uni-calendar__backtoday {
background-color: #3CC071;
color: #fff;
}
}
.personTabBox {
width: 100%;
box-sizing: border-box;
padding: 0 32rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16rpx;
margin-top: 24rpx;
.personItem {
background: #fff;
padding: 30rpx;
border-radius: 24rpx;
text-align: center;
box-shadow: @shadow;
.personValue {
font-size: 30rpx;
font-weight: 600;
color: @primary;
margin-bottom: 8rpx;
}
.personLabel {
font-size: 24rpx;
color: @muted;
}
}
.personItemSelect {
background: linear-gradient(135deg, @primary, @primary2);
color: #fff;
.personValue {
color: #fff;
}
.personLabel {
color: rgba(255, 255, 255, .9);
}
}
}
.thingBox {
width: 100%;
box-sizing: border-box;
padding: 0 32rpx;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16rpx;
margin-top: 24rpx;
.emergency-banner {
background: linear-gradient(135deg, @danger, #ff3838);
color: #fff;
padding: 20rpx 30rpx;
border-radius: 16rpx;
box-shadow: 0 4rpx 12rpx rgba(255, 71, 87, .3);
border-left: 8rpx solid #ff1744;
.emergency-header {
display: flex;
align-items: center;
justify-content: space-between;
// margin-bottom: 10rpx;
.emergency-title {
font-weight: 700;
font-size: 28rpx;
display: flex;
align-items: center;
gap: 16rpx;
}
.emergency-count {
background: rgba(255, 255, 255, .3);
padding: 4rpx 16rpx;
border-radius: 24rpx;
font-size: 24rpx;
font-weight: 700;
}
}
.emergency-preview {
font-size: 24rpx;
opacity: .9;
}
}
.warning-banner {
background: linear-gradient(135deg, @warn, #ff8c00);
color: #fff;
padding: 20rpx 30rpx;
border-radius: 16rpx;
box-shadow: 0 4rpx 12rpx rgba(255, 159, 67, .3);
border-left: 8rpx solid #ff8c00;
.emergency-header {
display: flex;
align-items: center;
justify-content: space-between;
// margin-bottom: 10rpx;
}
.emergency-title {
font-weight: 700;
font-size: 28rpx;
display: flex;
align-items: center;
gap: 16rpx;
}
.emergency-count {
background: rgba(255, 255, 255, .3);
padding: 4rpx 16rpx;
border-radius: 24rpx;
font-size: 24rpx;
font-weight: 700;
}
.emergency-preview {
font-size: 24rpx;
opacity: .9;
}
}
}
.onDutyPersonBox {
width: calc(100% - 64rpx);
margin: 32rpx;
background: #fff;
border-radius: 24rpx;
overflow: hidden;
box-shadow: @shadow;
.onDutyPersonBoxHeader {
background: @bg;
padding: 10rpx 30rpx;
border-bottom: 1rpx solid #e9ecef;
font-weight: 700;
font-size: 28rpx;
color: #2c3e50;
display: flex;
justify-content: space-between;
align-items: center;
.tab-indicators {
display: flex;
gap: 16rpx;
.tab-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
background: #ddd;
&.active {
background: @primary;
}
}
}
.dateBox {
box-sizing: border-box;
padding: 16rpx 0;
display: flex;
align-items: center;
justify-content: center;
.centerDateBox {
display: flex;
align-items: center;
gap: 10rpx;
padding: 10rpx 20rpx;
border-radius: 30rpx;
background-color: #fff;
// border: 2rpx solid #27B35F;
// background: rgba(255, 255, 255, .15);
.arrowIcon {
width: 40rpx;
height: 40rpx;
}
.center {
.date-text {
font-size: 28rpx;
font-weight: 700;
}
}
}
}
}
.onDutyPersonList {
.onDutyPersonItem {
display: flex;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f1f3f4;
&:last-child {
border-bottom: none;
}
.avatar {
width: 90rpx;
height: 90rpx;
border-radius: 50%;
background: linear-gradient(270deg, #27B25F 0%, #4CCC7F 100%);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: 700;
margin-right: 30rpx;
font-size: 28rpx;
}
.staffInfo {
flex: 1;
display: flex;
align-items: center;
.staffInfoLeft {
flex: 1;
.staffInfoTop {
display: flex;
align-items: center;
margin-bottom: 8rpx;
.staff-name {
font-weight: 600;
font-size: 26rpx;
}
.staff-role {
color: @muted;
font-size: 24rpx;
margin-left: 16rpx;
}
}
.staff-status {
font-size: 24rpx;
.status-badge {
display: inline-block;
padding: 6rpx 16rpx;
border-radius: 20rpx;
font-size: 22rpx;
font-weight: 700;
&.status-online {
background: #d4edda;
color: #155724;
}
&.status-absentee {
background: #ff0000;
color: #fff;
}
&.status-beLate {
background: #f8d7da;
color: #721c24;
}
}
}
}
.staffInfoRight {
width: 100rpx;
display: flex;
align-items: center;
justify-content: center;
.phoneIcon {
width: 40rpx;
height: 40rpx;
margin-left: 8rpx;
background-color: #fff;
border-radius: 50%;
}
}
}
}
}
.EmployeesOnLeaveList {
.EmployeesOnLeaveItem {
display: flex;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f1f3f4;
&:last-child {
border-bottom: none;
}
.avatar {
width: 90rpx;
height: 90rpx;
border-radius: 50%;
background: linear-gradient(270deg, #27B25F 0%, #4CCC7F 100%);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: 700;
margin-right: 30rpx;
font-size: 28rpx;
}
.staffInfo {
flex: 1;
display: flex;
align-items: center;
.staffInfoLeft {
flex: 1;
.staffInfoTop {
display: flex;
align-items: center;
margin-bottom: 8rpx;
.staff-name {
font-weight: 600;
font-size: 26rpx;
}
.staff-role {
color: @muted;
font-size: 24rpx;
margin-left: 16rpx;
}
}
.staff-status {
font-size: 24rpx;
.status-badge {
display: inline-block;
padding: 6rpx 16rpx;
border-radius: 20rpx;
font-size: 22rpx;
font-weight: 700;
&.status-rest {
background: #F6F6F6;
color: #9FA6A3;
}
&.status-adjust {
background: #fff3cd;
color: #856404;
}
&.status-leave {
background: #FF8B3F;
color: #fff;
}
}
}
}
.staffInfoRight {
width: 100rpx;
display: flex;
align-items: center;
justify-content: center;
.phoneIcon {
width: 40rpx;
height: 40rpx;
margin-left: 8rpx;
background-color: #fff;
border-radius: 50%;
}
}
}
}
}
.dutyManagerList {
.dutyManagerItem {
display: flex;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f1f3f4;
background: #E7F8EE;
border: 4rpx solid #27B25F;
border-radius: 24rpx;
margin: 20rpx;
box-shadow: 0 8rpx 24rpx #E7F8EE;
.avatar {
width: 90rpx;
height: 90rpx;
border-radius: 50%;
background: linear-gradient(270deg, #27B25F 0%, #4CCC7F 100%);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: 700;
margin-right: 30rpx;
font-size: 28rpx;
}
.manager-avatar {
background: linear-gradient(135deg, #27B25F, #4CCC7F);
border: 6rpx solid #27B25F;
box-shadow: 0 4rpx 16rpx #E7F8EE;
}
.manager-name {
color: #27B25F;
font-size: 36rpx;
}
.manager-role {
color: #ff9800;
font-weight: 700;
}
.manager-actions {
display: flex;
flex-direction: column;
gap: 16rpx;
.contact-btn,
.locate-btn {
background: linear-gradient(135deg, #ff9800, #f57c00);
border: none;
color: #fff;
width: 72rpx;
height: 72rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
}
}
.staffInfoBox {
flex: 1;
.staffInfo {
display: flex;
align-items: center;
margin-bottom: 6rpx;
.staffInfoLeft {
flex: 1;
.staffInfoTop {
display: flex;
align-items: center;
margin-bottom: 6rpx;
.staff-name {
font-weight: 700;
font-size: 32rpx;
}
.staff-role {
color: @muted;
font-size: 26rpx;
margin-left: 16rpx;
}
}
.staff-status {
font-size: 24rpx;
margin-bottom: 6rpx;
.status-badge {
display: inline-block;
padding: 6rpx 16rpx;
border-radius: 20rpx;
font-size: 22rpx;
font-weight: 700;
&.status-online {
background: #d4edda;
color: #155724;
}
&.status-break {
background: #fff3cd;
color: #856404;
}
&.status-offline {
background: #f8d7da;
color: #721c24;
}
&.status-manager {
background: linear-gradient(135deg, #27B25F, #4CCC7F);
color: #fff;
border: 1rpx solid #27B25F;
}
}
}
}
.staffInfoRight {
width: 100rpx;
display: flex;
align-items: center;
justify-content: center;
.phoneIcon {
width: 40rpx;
height: 40rpx;
margin-left: 8rpx;
background-color: #E7F8EE;
border-radius: 50%;
}
}
}
.staff-address {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 24rpx;
color: #716F69;
line-height: 36rpx;
text-align: left;
font-style: normal;
}
}
}
}
}
.otherJumpUrl {
width: calc(100% - 64rpx);
margin: 0 32rpx;
background-color: #fff;
box-shadow: @shadow;
box-sizing: border-box;
padding: 24rpx;
border-radius: 24rpx;
display: flex;
flex-wrap: wrap;
.jumpItem {
width: 25%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.jumpIcon {
width: 48rpx;
height: 48rpx;
margin-bottom: 16rpx;
}
.jumpText {
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 24rpx;
color: #716F69;
line-height: 36rpx;
text-align: left;
font-style: normal;
}
}
}
.userPopupBox {
width: 90vw;
height: 80vh;
background-color: #fff;
border-radius: 16rpx;
box-sizing: border-box;
padding: 24rpx;
overflow-y: auto;
.userBoxTitle {
width: 100%;
font-size: 28rpx;
font-weight: 600;
}
.userDetailBox {
width: 100%;
margin-top: 16rpx;
display: flex;
align-items: center;
.userAvatar {
width: 180rpx;
height: 180rpx;
margin-right: 20rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
.userAvatarIcon {
width: 100%;
height: 100%;
border-radius: 50%;
}
.userAvatarBox {
width: 70%;
height: 70%;
border-radius: 50%;
background: linear-gradient(270deg, #27B25F 0%, #4CCC7F 100%);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: 700;
margin-right: 30rpx;
font-size: 30rpx;
}
}
.userDetailRight {
flex: 1;
.userDetailTop {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.userDetailName {
display: inline-block;
max-width: 70%;
font-size: 26rpx;
font-weight: 600;
}
.userDetailStatus {
display: inline-block;
padding: 0 8rpx;
border-radius: 8rpx;
}
}
.userDetailPostBox,
.userDetailPhoneBox {
.userDetailLabel {
font-size: 26rpx;
font-weight: 600;
}
.userDetailValue {
font-size: 24rpx;
}
}
}
}
.line {
width: 100%;
height: 2rpx;
background-color: #e4e3e3;
margin: 16rpx 0;
}
.attendanceStatusBox {
width: 100%;
margin-top: 24rpx;
.attendanceStatusItem {
width: 100%;
margin-bottom: 16rpx;
display: flex;
align-items: center;
.attendanceStatusLabel {
width: 200rpx;
text-align: left;
font-size: 26rpx;
}
.attendanceStatusValue {
flex: 1;
font-size: 24rpx;
}
}
}
}
}
/* 通用 */
.muted {
color: @muted;
}
.ml8 {
margin-left: 8px;
}
.h1 {
font-size: 36rpx;
font-weight: 600;
}
.pad15 {
padding: 30rpx;
}
.m15 {
margin: 30rpx;
}
.p15 {
padding: 30rpx;
}
.noMargin {
margin: 0;
}
.noPad {
padding: 0;
}
</style>