| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516 |
- <template>
- <view class="workGroup-list">
- <view class="working-group" v-for="({ data, info }, index) of historyList" :key="index">
- <view>
- <view class="title" v-if="!info.isCheckIn">{{ '开始上通道/负责区域' }}</view>
- <view v-if="!info.isCheckIn" class="content">
- <view class="empty" style="margin: 20px auto 0;"></view>
- <view class="empty-text">当前未上通道/区域</view>
- </view>
- <view v-else class="content">
- <view class="content-cell" v-if="notkezhang">
- <text class="content-cell-label">通道</text>
- <text class="content-cell-value">{{ info.channelName || info.channelCode || '--' }}</text>
- </view>
- <view class="content-cell" v-if="notkezhang">
- <text class="content-cell-label">当班人员</text>
- <text></text>
- </view>
- <view class="content-workings" v-if="notkezhang">
- <view class="personnel-list" v-if="data.length">
- <view class="personnel-item" v-for="item of data" :key="item.id">
- <view class="personnel-img">
- <UserAvatar :userName="item.userName" :avatarLink="item.avatar" />
- </view>
- <view class="personnel-name">{{ item.userName }}</view>
- </view>
- </view>
- <view class="empty" v-else></view>
- </view>
- <view class="content-cell" style="height: auto;display: flex;flex-direction: column;align-items: flex-start;"
- v-if="!notkezhang">
- <text class="content-cell-label">负责区域</text>
- <!-- <text class="content-cell-value">{{ info.regionalName || info.regionalCode || '--' }}</text> -->
- <view class="selected-areas"
- v-if="getHandleAreaData(data) && getHandleAreaData(data).length && !notkezhang">
- <view class="area-item" v-for="area in getHandleAreaData(data)" :key="area.code">
- <view class="area-label">{{ area.label }}</view>
- <text-tag :showClose="false" :tags="area.children" :labelColumn="'regionalName'" />
- </view>
- </view>
- </view>
- <view class="content-cell">
- <text class="content-cell-label">上通道时间</text>
- <text class="content-cell-value">{{ info.checkInTimeFormat }}</text>
- </view>
- <view class="content-cell" v-if="info.isCheckIn">
- <text class="content-cell-label">下通道时间</text>
- <text class="content-cell-value">{{ info.checkOutTime || '-:-' }}</text>
- </view>
- </view>
- </view>
- <!-- {{ selectedMember.length === 0 }}{{ !userInfo.roles.includes('banzuzhang') }}{{ !attendanceInfo.checkInTime }} -->
- <!-- :disabled="!attendanceInfo.checkInTime || checkOutStatus" -->
- <AddAttendancePersonnelModal v-if="index === 0" :disabled="checkOutStatus" :notkezhang="notkezhang"
- :userInfo="userInfo" @updateRecord="invokerGetPostRecordList" :selectedMember="selectedMember"
- :attendanceInfo="attendanceInfo">
- <!-- :class="{ disabled: !attendanceInfo.checkInTime }" 没有选人不可以上通道 -->
- <view v-if="authority" class="custom-btn" @click="openModal"
- :class="{ disabled: selectedMember.length === 0 && !userInfo.roles.includes('banzuzhang') || (!attendanceInfo.checkInTime || (attendanceInfo.checkInTime && attendanceInfo.checkOutTime)) }">
- {{ checkOutStatus ? '下通道' : '上通道' }}
- </view>
- </AddAttendancePersonnelModal>
- </view>
- <u-popup :show="checkOutWork" mode="center" :round="8">
- <view class="modal-content">
- <view class="title">
- <text>员工下通道</text>
- <u-icon name="close" color="#666666" size="20" @click="close" />
- </view>
- <view class="title-cell">
- <view class="title-text">{{ '选择下通道时间' }}</view>
- <uni-datetime-picker type="datetime" v-model="currentTime" />
- </view>
- <view class="title-cell">
- <view class="custom-btn-normal" @click="invokerUpdatePostRecord" :class="{ disabled: !currentTime }">确认下通道
- </view>
- </view>
- </view>
- </u-popup>
- </view>
- </template>
- <script>
- import { listgroupbyTimeanduserid, updatePostRecord } from "@/api/attendance/attendance"
- import { formatTime, formatName } from '@/utils/formatUtils'
- import AddAttendancePersonnelModal from './AddAttendancePersonnelModal';
- import UserAvatar from './UserAvatar'
- export default {
- components: { AddAttendancePersonnelModal, UserAvatar },
- props: {
- userInfo: {
- type: Object,
- default: () => ({})
- },
- attendanceInfo: { // 考勤信息
- type: Object,
- default: () => ({})
- },
- selectedMember: {
- type: Array,
- default: () => []
- }
- },
- data() {
- return {
- curUserWorkingGroupData: {
- passage: '',
- checkInTime: '',
- checkOutTime: '',
- isCheckIn: false,
- },
- checkOutStatus: false,
- checkOutWork: false,
- currentTime: undefined,
- workingGroupId: '',
- historyList: [], //班组工作记录 todo 现在只能做科长
- }
- },
- computed: {
- notkezhang() {
- return !this.userInfo.roles || !this.userInfo.roles.includes('banzuzhang')
- },
- authority() { // 上通道权限
- return this.userInfo.roles && (
- this.userInfo.roles.includes('banzuzhang') ||
- this.userInfo.roles.includes('xiaozuzhang')
- )
- }
- },
- methods: {
- openModal() {
- if (!this.checkOutStatus) {
- return;
- }
- this.currentTime = formatTime(new Date())
- this.checkOutWork = true
- },
- close() {
- this.currentTime = undefined
- this.checkOutWork = false
- },
- isCheckOutJobs(curUserWorkingInfo = this.curUserWorkingGroupData) {
- return Boolean(curUserWorkingInfo.checkInTime) && (!curUserWorkingInfo.checkOutTime || curUserWorkingInfo.checkOutTime === '2000-01-01 00:00:00')
- },
- getHandleAreaData(data) {
- let louObj = {};
- let areaArr = [];
- data.forEach(element => {
- let name = `${element.terminlName}-${element.terminlCode}`
- if (!louObj[name]) {
- louObj[name] = [];
- }
- louObj[name].push(element);
- });
- Object.keys(louObj).forEach(key => {
- areaArr.push({
- label: key.split('-')[0],
- code: key.split('-')[1],
- children: louObj[key]
- });
- });
- return areaArr;
- },
- invokerGetPostRecordList(callback) {
- return listgroupbyTimeanduserid().then(res => {
- if (res.code === 200) {
- const resData = (res.data || [])
- this.historyList = resData.reduce((cur, item, index) => {
- let info = {}
- const workDataList = item.records.map((attr) => {
- const result = {
- ...attr,
- isCheckIn: Boolean(attr.checkInTime), // 是否上过通道
- checkInTimeFormat: formatTime(attr.checkInTime, 'hh:mm:ss'),
- checkOutTime: attr.checkOutTime === '2000-01-01 00:00:00' ? '' : formatTime(attr.checkOutTime, 'hh:mm:ss'),
- userName: formatName(attr.userName),
- }
- // if (attr.userId === this.userInfo.userId) {
- // info = result
- // }
- info = result
- if (index === 0) { // 取最新纪录
- this.curUserWorkingGroupData = result
- this.workingGroupId = item.shiftCode
- }
- return result
- })
- cur.push({
- info: info,
- data: workDataList,
- workingGroupId: item.shiftCode
- })
- return cur
- }, [])
- this.checkOutStatus = this.isCheckOutJobs(this.curUserWorkingGroupData)
- if (!this.checkOutStatus) {
- this.historyList = [{
- info: {},
- data: [],
- workingGroupId: undefined
- }, ...this.historyList]
- }
- }
- }).catch(() => {
- return Promise.resolve()
- }).finally(() => {
- callback && callback()
- })
- },
- createRecordData(curUserRecordInfo) {
- // 🔥 重要:计算工作时长
- const checkTime = new Date(this.currentTime);
- const workDurationMinutes = Math.floor((Date.now() - checkTime.getTime()) / 1000 / 60);
- // 🔥 重要:构建更新数据
- let res = [];
- //如果是科长走多选逻辑,如果是班组长走单选逻辑
- if (!this.notkezhang) {
- console.log(this.historyList)
- this.historyList.forEach(item => {
- item.data.forEach(element => {
- res.push(
- {
- // 基本字段
- ...curUserRecordInfo,
- isCheckIn: undefined,
- revision: (curUserRecordInfo.revision || 1) + 1,
- // 时间相关
- checkInTime: typeof curUserRecordInfo.checkInTime === 'string' ? curUserRecordInfo.checkInTime : formatTime(new Date(curUserRecordInfo.checkInTime)),
- attendanceDate: typeof curUserRecordInfo.attendanceDate === 'string' ? curUserRecordInfo.attendanceDate : formatTime(new Date(curUserRecordInfo.attendanceDate), 'YYYY-MM-DD'),
- workDuration: workDurationMinutes, // 重新计算工作时长
- overDuration: curUserRecordInfo.overDuration || 0,
- // 组织架构信息
- attendanceTeamId: curUserRecordInfo.attendanceTeamId || null,
- attendanceTeamName: curUserRecordInfo.attendanceTeamName || '',
- attendanceDepartmentId: curUserRecordInfo.attendanceDepartmentId || null,
- attendanceDepartmentName: curUserRecordInfo.attendanceDepartmentName || '',
- attendanceStationId: curUserRecordInfo.attendanceStationId || null,
- attendanceStationName: curUserRecordInfo.attendanceStationName || '',
- remark: curUserRecordInfo.remark || '手动添加上通道记录',
- // 审计字段
- createBy: curUserRecordInfo.createBy || (this.userInfo.userId + ''),
- createTime: typeof curUserRecordInfo.createTime === 'string' ? curUserRecordInfo.createTime : formatTime(new Date(curUserRecordInfo.createTime)),
- updateBy: (this.userInfo.userId + ''),
- updateTime: formatTime(new Date()),
- ...element,
- checkOutTime: formatTime(checkTime), // 设置实际下岗时间
- }
- )
- })
- })
- } else {
- res = [{
- // 基本字段
- ...curUserRecordInfo,
- userName: (curUserRecordInfo.userName || '').replace(/\s/g, ''),
- isCheckIn: undefined,
- revision: (curUserRecordInfo.revision || 1) + 1,
- // 时间相关
- checkInTime: typeof curUserRecordInfo.checkInTime === 'string' ? curUserRecordInfo.checkInTime : formatTime(new Date(curUserRecordInfo.checkInTime)),
- checkOutTime: formatTime(checkTime), // 设置实际下岗时间
- attendanceDate: typeof curUserRecordInfo.attendanceDate === 'string' ? curUserRecordInfo.attendanceDate : formatTime(new Date(curUserRecordInfo.attendanceDate), 'YYYY-MM-DD'),
- workDuration: workDurationMinutes, // 重新计算工作时长
- overDuration: curUserRecordInfo.overDuration || 0,
- // 组织架构信息
- attendanceTeamId: curUserRecordInfo.attendanceTeamId || null,
- attendanceTeamName: curUserRecordInfo.attendanceTeamName || '',
- attendanceDepartmentId: curUserRecordInfo.attendanceDepartmentId || null,
- attendanceDepartmentName: curUserRecordInfo.attendanceDepartmentName || '',
- attendanceStationId: curUserRecordInfo.attendanceStationId || null,
- attendanceStationName: curUserRecordInfo.attendanceStationName || '',
- remark: curUserRecordInfo.remark || '手动添加上通道记录',
- // 审计字段
- createBy: curUserRecordInfo.createBy || (this.userInfo.userId + ''),
- createTime: typeof curUserRecordInfo.createTime === 'string' ? curUserRecordInfo.createTime : formatTime(new Date(curUserRecordInfo.createTime)),
- updateBy: (this.userInfo.userId + ''),
- updateTime: formatTime(new Date())
- }]
- }
- console.log(res, "res")
- return res;
- },
- invokerUpdatePostRecord() {
- if (this.checkOutStatus) {
- // 科长下区域
- if (!this.notkezhang) {
- uni.showLoading({ title: '正在下通道...' });
- updatePostRecord(this.createRecordData(this.curUserWorkingGroupData)).then(res => {
- if (res && res.code === 200) {
- uni.showToast({
- title: '下通道完成!',
- icon: 'success',
- duration: 2000
- });
- } else {
- uni.showToast({
- title: `下通道失败!${res.msg || '未知错误'}`,
- icon: 'none',
- duration: 2000
- });
- }
- this.close()
- return this.invokerGetPostRecordList()
- }).catch(error => {
- uni.showToast({
- title: '下通道失败:' + (error.msg || '请重试'),
- icon: 'error',
- duration: 2000
- });
- }).finally(() => {
- uni.hideLoading();
- })
- } else {// 当班人员下通道
- uni.showLoading({ title: '正在批量下通道...' });
- // 获取当前最新的工作组数据
- const recordList = this.historyList.find(item => item.workingGroupId === this.workingGroupId) || { data: [] }
- const promises = recordList.data.map(item => {
- return updatePostRecord(this.createRecordData(item)).then(res => {
- if (res && res.code === 200) {
- return `${item.userName}: 下通道完成!`
- } else {
- return `${item.userName}下通道失败: ${res.msg || '未知错误'}`
- }
- }).catch((err) => {
- return Promise.resolve(`${item.userName}下通道失败: 网络错误!`)
- })
- })
- Promise.all(promises).then((res) => {
- uni.showModal({
- title: '批量下通道结果',
- content: `${res.join('\n')}`,
- showCancel: false,
- confirmText: '确定'
- });
- this.close()
- return this.invokerGetPostRecordList()
- }).finally(() => {
- uni.hideLoading();
- })
- }
- }
- }
- },
- }
- </script>
- <style lang="scss" scoped>
- .workGroup-list {
- display: flex;
- flex-direction: column;
- row-gap: 15px;
- }
- .working-group {
- width: 100%;
- background: #FFFFFF;
- box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.08);
- border-radius: 16px 16px 16px 16px;
- border: 1px solid #F0F8FF;
- padding: 15px;
- box-sizing: border-box;
- .empty {
- margin: 0 auto;
- width: 160px;
- height: 155px;
- background: url("../../../static/images/Empty.png") no-repeat;
- background-size: cover;
- }
- .empty-text {
- font-weight: 400;
- font-size: 14px;
- color: #3D3D3D;
- line-height: 16px;
- text-align: center;
- font-style: normal;
- text-transform: none;
- margin-bottom: 20px;
- }
- .title {
- font-weight: 400;
- font-size: 18px;
- color: #333333;
- line-height: 24px;
- text-align: left;
- font-style: normal;
- text-transform: none;
- padding: 5px 0;
- }
- .content-cell {
- display: flex;
- align-items: center;
- justify-content: space-between;
- font-weight: 400;
- font-size: 14px;
- font-style: normal;
- text-transform: none;
- width: 100%;
- height: 36px;
- .content-cell-label {
- color: #222222;
- }
- .content-cell-value {
- color: #999999;
- padding-right: 5px;
- }
- .selected-areas {
- .area-item {
- .area-label {
- height: 72rpx;
- line-height: 72rpx;
- }
- }
- }
- }
- .content-workings {
- display: flex;
- padding: 10px 0;
- .personnel-list {
- display: flex;
- flex-wrap: wrap;
- row-gap: 10px;
- column-gap: 10px;
- .personnel-item {
- width: fit-content;
- height: 34px;
- background: #F0F0F0;
- border-radius: 6px;
- display: flex;
- align-items: center;
- column-gap: 8px;
- padding: 0 10px;
- .personnel-img {
- width: 24px;
- height: 24px;
- border-radius: 6px;
- overflow: hidden;
- }
- .personnel-name {
- width: fit-content;
- font-weight: 400;
- font-size: 13px;
- color: #3D3D3D;
- text-align: left;
- font-style: normal;
- text-transform: none;
- }
- }
- }
- }
- }
- .modal-content {
- width: 90vw;
- max-height: 75vh;
- padding: 10px 0;
- .title {
- height: 24px;
- font-weight: 400;
- font-size: 18px;
- color: #333333;
- line-height: 21px;
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 15px;
- box-sizing: border-box;
- }
- .title-cell {
- padding: 10px 15px;
- box-sizing: border-box;
- .title-text {
- height: 16px;
- font-weight: 400;
- font-size: 14px;
- color: #333333;
- line-height: 16px;
- text-align: left;
- margin-bottom: 14px;
- text-indent: 5px;
- }
- }
- }
- </style>
|