补卡记录,请假记录,施工人员列表日历

This commit is contained in:
Teo
2025-04-09 18:07:43 +08:00
parent 8439370389
commit 2643f4abc8
15 changed files with 827 additions and 172 deletions

View File

@ -8,13 +8,15 @@
import useSettingsStore from '@/store/modules/settings';
import { handleThemeStyle } from '@/utils/theme';
import useAppStore from '@/store/modules/app';
import { getProjectTeam } from './utils/projectTeam';
import { useUserStore } from '@/store/modules/user';
const appStore = useAppStore();
onMounted(() => {
nextTick(() => {
// 初始化主题样式
handleThemeStyle(useSettingsStore().theme);
getProjectTeam();
});
});
</script>

View File

@ -54,7 +54,15 @@ export interface AttendanceMonthVO {
id: string | number;
clockDate: string;
status: string;
attendanceList: monthList[];
attendanceList?: monthList[];
clockList?: clockObject;
}
interface clockObject {
downClockTime?: string;
downClockPic?: string;
upClockTime?: string;
upClockPic?: string;
}
interface monthList {

View File

@ -10,8 +10,24 @@ import {
ConstructionUserSalaryForm,
ConstructionUserExitForm,
ConstructionUserTemplateForm,
ConstructionUserMembeForm
ConstructionUserMembeForm,
ConstructionMonthQuery
} from '@/api/project/constructionUser/types';
import { AttendanceMonthVO } from '../attendance/types';
/**
* 查询施工人员月份考勤列表
* @param query
* @returns {*}
*/
export const listConstructionMonth = (query?: ConstructionMonthQuery): AxiosPromise<AttendanceMonthVO[]> => {
return request({
url: '/project/constructionUser/list/attendance/month',
method: 'get',
params: query
});
};
/**
* 查询施工人员列表

View File

@ -197,6 +197,19 @@ export interface skipType {
id: string | number;
}
export interface ConstructionMonthQuery {
/**
* id
*/
userId: string | number;
/**
* 打卡月份
*/
clockMonth?: string | number;
}
export interface ConstructionUserMembeForm {
/**
* 用户id

View File

@ -0,0 +1,63 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { WorkerDailyReportVO, WorkerDailyReportForm, WorkerDailyReportQuery } from '@/api/project/workerDailyReport/types';
/**
* 查询施工人员日报列表
* @param query
* @returns {*}
*/
export const listWorkerDailyReport = (query?: WorkerDailyReportQuery): AxiosPromise<WorkerDailyReportVO[]> => {
return request({
url: '/project/workerDailyReport/list',
method: 'get',
params: query
});
};
/**
* 查询施工人员日报详细
* @param id
*/
export const getWorkerDailyReport = (id: string | number): AxiosPromise<WorkerDailyReportVO> => {
return request({
url: '/project/workerDailyReport/' + id,
method: 'get'
});
};
/**
* 新增施工人员日报
* @param data
*/
export const addWorkerDailyReport = (data: WorkerDailyReportForm) => {
return request({
url: '/project/workerDailyReport',
method: 'post',
data: data
});
};
/**
* 修改施工人员日报
* @param data
*/
export const updateWorkerDailyReport = (data: WorkerDailyReportForm) => {
return request({
url: '/project/workerDailyReport',
method: 'put',
data: data
});
};
/**
* 删除施工人员日报
* @param id
*/
export const delWorkerDailyReport = (id: string | number | Array<string | number>) => {
return request({
url: '/project/workerDailyReport/' + id,
method: 'delete'
});
};

View File

@ -0,0 +1,111 @@
export interface WorkerDailyReportVO {
/**
* 申请人名字
*/
userName: string;
/**
* 今日完成工作
*/
todayCompletedWork: string;
/**
* 未完成工作
*/
unfinishedWork: string;
/**
* 明日工作
*/
tomorrowWork: string;
/**
* 需协调与帮助
*/
coordinationHelp: string;
}
export interface WorkerDailyReportForm extends BaseEntity {
/**
* 主键id
*/
id?: string | number;
/**
* 项目id
*/
projectId?: string | number;
/**
* 班组id
*/
teamId?: string | number;
/**
* 申请人id
*/
userId?: string | number;
/**
* 申请人名字
*/
userName?: string;
/**
* 今日完成工作
*/
todayCompletedWork?: string;
/**
* 未完成工作
*/
unfinishedWork?: string;
/**
* 明日工作
*/
tomorrowWork?: string;
/**
* 需协调与帮助
*/
coordinationHelp?: string;
/**
* 附件
*/
file?: string;
}
export interface WorkerDailyReportQuery extends PageQuery {
/**
* 项目id
*/
projectId?: string | number;
/**
* 班组id
*/
teamId?: string | number;
/**
* 申请人id
*/
userId?: string | number;
/**
* 申请人名字
*/
userName?: string;
/**
* 日期范围参数
*/
params?: any;
}

View File

@ -18,6 +18,7 @@
<script lang="ts" setup>
import { ref, computed, watch } from 'vue';
import { useUserStore } from '@/store/modules/user';
import { getProjectTeam } from '@/utils/projectTeam';
const userStore = useUserStore();
const projects = computed(() => [
@ -40,7 +41,8 @@ const handleSelect = (projectId: string) => {
const selectedProject = projects.value.find((p) => p.id === projectId);
if (selectedProject) {
userStore.setSelectedProject(selectedProject);
location.reload()
// getProjectTeam();
location.reload();
}
};
</script>

View File

@ -5,17 +5,26 @@ import { LoginData, UserProject } from '@/api/types';
import defAva from '@/assets/images/profile.jpg';
import store from '@/store';
import { defineStore } from 'pinia';
import { SpecialType } from '@/api/project/workWage/types';
// 添加两个函数用于操作localStorage
const saveSelectedProjectToStorage = (project) => {
localStorage.setItem('selectedProject', JSON.stringify(project));
};
const saveProjectTeamToStorage = (project) => {
localStorage.setItem('ProjectTeamList', JSON.stringify(project));
};
const getSelectedProjectFromStorage = () => {
const stored = localStorage.getItem('selectedProject');
return stored ? JSON.parse(stored) : null;
};
const getProjectTeamListFromStorage = () => {
const stored = localStorage.getItem('ProjectTeamList');
return stored ? JSON.parse(stored) : null;
};
export const useUserStore = defineStore('user', () => {
const token = ref(getToken());
const name = ref('');
@ -29,6 +38,7 @@ export const useUserStore = defineStore('user', () => {
const projects = ref<Array<{ id: string; name: string }>>([]);
// 从localStorage获取缓存的项目如果没有则默认为null
const selectedProject = ref<{ id: string; name: string } | null>(getSelectedProjectFromStorage());
const ProjectTeamList = ref<SpecialType[] | null>(getProjectTeamListFromStorage());
/**
* 登录
@ -112,14 +122,12 @@ export const useUserStore = defineStore('user', () => {
const setSelectedProject = (project: { id: string; name: string }) => {
selectedProject.value = project;
// 将选中的项目保存到localStorage
saveSelectedProjectToStorage(project);
};
// ** 切换项目后,需要清空当前项目下的所有缓存数据 **
// 清空 pinia 缓存
// store.$reset();
// console.log("选择的新项目名称:" + selectedProject.value.name)
// console.log("选择的新项目id" + selectedProject.value.id)
const setProjectTeamList = (project: SpecialType[]) => {
ProjectTeamList.value = project;
saveProjectTeamToStorage(project);
};
return {
@ -136,8 +144,10 @@ export const useUserStore = defineStore('user', () => {
setAvatar,
setProjects,
setSelectedProject,
setProjectTeamList,
projects,
selectedProject
selectedProject,
ProjectTeamList
};
});

22
src/utils/projectTeam.ts Normal file
View File

@ -0,0 +1,22 @@
//获取班组列表
import { listProjectTeam } from '@/api/project/projectTeam';
import { ProjectTeamVO } from '@/api/project/projectTeam/types';
import store from '@/store';
import { useUserStore } from '@/store/modules/user';
const userStore = useUserStore(store);
export const getProjectTeam = async () => {
const { id } = userStore.selectedProject;
const res = await listProjectTeam({
pageNum: 1,
pageSize: 20,
orderByColumn: 'createTime',
isAsc: 'desc',
projectId: id
});
const list = res.rows.map((projectTeam: ProjectTeamVO) => ({
value: projectTeam.id,
label: projectTeam.teamName
}));
userStore.setProjectTeamList(list);
};

View File

@ -9,7 +9,7 @@
</el-form-item>
<el-form-item label="班组" prop="teamId">
<el-select v-model="queryParams.teamId" placeholder="请选择班组" clearable>
<el-option v-for="dict in projectTeamOpt" :key="dict.value" :label="dict.label" :value="dict.value" />
<el-option v-for="dict in ProjectTeam" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="工种" prop="typeOfWork">
@ -210,6 +210,7 @@ import type { CalendarDateType, CalendarInstance } from 'element-plus';
const userStore = useUserStoreHook();
// 从 store 中获取项目列表和当前选中的项目
const currentProject = computed(() => userStore.selectedProject);
const ProjectTeam = computed(() => userStore.ProjectTeamList);
const attendanceList = ref<AttendanceVO[]>([]);
const attendanceTwoWeekList = ref<AttendanceTwoWeekVO[]>([]);
const buttonLoading = ref(false);
@ -222,8 +223,6 @@ const total = ref(0);
const calendarList = ref<AttendanceMonthVO[]>();
const queryFormRef = ref<ElFormInstance>();
const attendanceFormRef = ref<ElFormInstance>();
//班组列表
const projectTeamOpt = ref([]);
const dialog = reactive<DialogOption>({
visible: false,
details: false,
@ -308,7 +307,7 @@ const selectDate = async (val: CalendarDateType, date) => {
if (!calendar.value) return;
calendar.value.selectDate(val);
const clockMonth = incrementMonth(date, val == 'prev-month' ? -1 : 1);
const res = await listAttendanceMonth({ id: dialog.id, clockMonth });
const res = await listAttendanceMonth({ userId: dialog.id, clockMonth });
calendarList.value = res.data;
};
@ -321,22 +320,6 @@ const getList = async () => {
loading.value = false;
};
const getProjectTeamList = async () => {
loading.value = true;
const res = await listProjectTeam({
pageNum: 1,
pageSize: 20,
orderByColumn: 'createTime',
isAsc: 'desc',
projectId: currentProject.value.id
});
projectTeamOpt.value = res.rows.map((projectTeam: ProjectTeamVO) => ({
value: projectTeam.id,
label: projectTeam.teamName
}));
loading.value = false;
};
/** 查询近两周考勤列表 */
const getListTwoWeek = async () => {
loading.value = true;
@ -450,7 +433,6 @@ const submitForm = () => {
onMounted(() => {
getList();
getListTwoWeek();
getProjectTeamList();
});
</script>

View File

@ -9,7 +9,7 @@
</el-form-item>
<el-form-item label="班组" prop="teamId">
<el-select v-model="queryParams.teamId" placeholder="请选择班组" clearable>
<el-option v-for="dict in projectTeamOpt" :key="dict.value" :label="dict.label" :value="dict.value" />
<el-option v-for="dict in ProjectTeam" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="打卡日期" prop="clockDate">
@ -56,7 +56,9 @@
</el-table-column>
<el-table-column label="打卡时间" align="center" prop="clockTime">
<template #default="scope">
<span>{{ parseTime(scope.row.clockTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
<span>{{
parseTime(scope.row.clockTime, '{y}-{m}-{d} {h}:{i}:{s}') ? parseTime(scope.row.clockTime, '{y}-{m}-{d} {h}:{i}:{s}') : '缺卡'
}}</span>
</template>
</el-table-column>
</el-table>
@ -83,7 +85,6 @@ const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const projectTeamOpt = ref<any[]>([]);
const queryFormRef = ref<ElFormInstance>();
const attendanceFormRef = ref<ElFormInstance>();
@ -95,11 +96,12 @@ const dialog = reactive<DialogOption>({
const userStore = useUserStoreHook();
// 从 store 中获取项目列表和当前选中的项目
const currentProject = computed(() => userStore.selectedProject);
const ProjectTeam = computed(() => userStore.ProjectTeamList);
const initFormData: AttendanceForm = {
id: undefined,
userId: undefined,
facePic: undefined,
projectId: undefined,
projectId: currentProject.value?.id,
onClockTime: undefined,
offClockTime: undefined,
clockDate: undefined,
@ -118,7 +120,7 @@ const data = reactive<PageData<AttendanceForm, AttendanceQuery>>({
pageNum: 1,
pageSize: 10,
userName: undefined,
projectId: undefined,
projectId: currentProject.value?.id,
clockDate: undefined,
clockStatus: undefined,
commuter: undefined,
@ -158,25 +160,7 @@ const resetQuery = () => {
handleQuery();
};
/** 搜索班组操作 */
const getProjectTeamList = async () => {
loading.value = true;
const res = await listProjectTeam({
pageNum: 1,
pageSize: 20,
orderByColumn: 'createTime',
isAsc: 'desc',
projectId: currentProject.value.id
});
projectTeamOpt.value = res.rows.map((projectTeam: ProjectTeamVO) => ({
value: projectTeam.id,
label: projectTeam.teamName
}));
loading.value = false;
};
onMounted(() => {
getList();
getProjectTeamList();
});
</script>

View File

@ -14,7 +14,7 @@
</el-form-item>
<el-form-item label="班组" prop="contractorId">
<el-select v-model="queryParams.teamId" clearable placeholder="请选择班组">
<el-option v-for="item in projectTeamOpt" :key="item.value" :label="item.label" :value="item.value" />
<el-option v-for="item in ProjectTeam" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="工种" prop="typeOfWork">
@ -166,6 +166,7 @@
<el-button link type="success" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['project:constructionUser:edit']">
修改
</el-button>
<el-button link type="warning" icon="Female" @click="handlePlayCard(scope.row)"> 打卡 </el-button>
<el-button link type="danger" icon="Avatar" @click="handleJoinBlacklist(scope.row)" v-hasPermi="['project:constructionBlacklist:add']">
黑名单
</el-button>
@ -429,6 +430,24 @@
</span>
</template>
</el-dialog>
<el-dialog :title="`打卡记录`" v-model="playCardCalendar" width="770px" :close-on-click-modal="false">
<el-calendar ref="calendar" v-model="calendarDay">
<template #header="{ date }">
<span>{{ date }}</span>
<el-date-picker v-model="monthValue" type="month" placeholder="请选择月份" @change="handleMonth" />
</template>
<template #date-cell="{ data }">
<div
class="w100% h100% position-relative m-0 monthDay"
:class="data.isSelected ? 'is-selected' : ''"
@click="handleViewPlayCard(playCardIdx(data))"
>
{{ data.day.split('-').slice(1).join('-') }}
<div :style="{ background: playCardColor(data) }" v-if="playCardIdx(data) != -1"></div>
</div>
</template>
</el-calendar>
</el-dialog>
</div>
</template>
@ -447,7 +466,8 @@ import {
updateConstructionUserSalary,
getConstructionUserExit,
dowloadConstructionUserTemplate,
importConstructionUserInfo
importConstructionUserInfo,
listConstructionMonth
} from '@/api/project/constructionUser';
import {
ConstructionUserForm,
@ -472,7 +492,10 @@ import {
ConstructionUserFileQuery
} from '@/api/project/constructionUserFile/types';
import { ElLoadingService } from 'element-plus';
const imgurl = 'http://zmkg.cqet.top:8899/wxfile/upload_file/2024-12-03/1.jpg';
import type { CalendarDateType, CalendarInstance } from 'element-plus';
import { AttendanceMonthVO } from '@/api/project/attendance/types';
const calendar = ref<CalendarInstance>();
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { type_of_work, user_sex_type, user_clock_type, user_file_type, user_status_type, wage_measure_unit_type } = toRefs<any>(
proxy?.useDict('type_of_work', 'user_sex_type', 'user_clock_type', 'user_file_type', 'user_status_type', 'wage_measure_unit_type')
@ -481,6 +504,7 @@ const { type_of_work, user_sex_type, user_clock_type, user_file_type, user_statu
const userStore = useUserStoreHook();
// 从 store 中获取项目列表和当前选中的项目
const currentProject = computed(() => userStore.selectedProject);
const ProjectTeam = computed(() => userStore.ProjectTeamList);
const constructionUserList = ref<ConstructionUserVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
@ -495,8 +519,11 @@ const showFaceDrawer = ref(false);
const statusDialog = ref(false);
const playCardStatus = ref(false);
const playCardLoding = ref(false);
const playCardCalendar = ref(false);
const salaryStatus = ref(false);
const exitStatus = ref(false);
const calendarDay = ref<Date | null>(null);
const monthValue = ref<Date | null>(null);
const informationStatus = ref(false);
const filePath = ref<string>('');
const exitList = ref<ConstructionUserExitVO[]>([]);
@ -506,9 +533,11 @@ const fileList = ref<ConstructionUserFileVO[]>([]);
const queryFormRef = ref<ElFormInstance>();
const constructionUserFormRef = ref<ElFormInstance>();
const skipName = ref('');
const calendarList = ref<Array<AttendanceMonthVO>>([]);
const dialog = reactive<DialogOption>({
visible: false,
title: ''
title: '',
id: undefined
});
const baseUrl = import.meta.env.VITE_APP_BASE_API;
//人员迁移条件
@ -649,6 +678,28 @@ const uploadStatusColor = computed(() => (str: string) => {
const { queryParams, form, rules } = toRefs(data);
//打卡时间下标
const playCardIdx = computed(() => (date) => {
return calendarList.value.findIndex((item) => item.clockDate == date.day);
});
//打卡状态颜色
const playCardColor = computed(() => (date) => {
const idx = calendarList.value[playCardIdx.value(date)]?.status;
switch (idx) {
case '1':
return 'green';
case '2':
return 'orange';
case '3':
return 'red';
case '4':
return 'gray';
default:
return '';
}
});
/** 查询施工人员列表 */
const getList = async () => {
loading.value = true;
@ -658,6 +709,30 @@ const getList = async () => {
loading.value = false;
};
/** 查看打卡记录详情 */
const handleViewPlayCard = async (idx: number) => {
const statusColor = calendarList.value[idx]?.status;
if (idx == -1 || statusColor == '4' || statusColor == '3') {
return proxy?.$modal.msgWarning('暂无打卡记录数据');
}
const { downClockTime, downClockPic, upClockTime, upClockPic } = calendarList.value[idx]?.clockList;
ElNotification({
title: '温馨提示',
dangerouslyUseHTMLString: true,
message: `<div style="display: flex;flex-direction: row;align-items: center;margin-top: 15px;height:60px">
<span>头像:</span>
<div style="width: 50px;height: 50px;border-radius:15px;">
<img src="${upClockPic}" style="width: 100%;height: 100%;border-radius:15px;">
</div>
</div><span>上班打卡时间:${upClockTime ? upClockTime : ''}</span><div style="display: flex;flex-direction: row;align-items: center;margin-top: 15px;height:60px">
<span>头像:</span>
<div style="width: 50px;height: 50px;border-radius:15px;">
<img src="${downClockPic}" style="width: 100%;height: 100%;border-radius:15px;">
</div>
</div><span>下班打卡时间:${downClockTime ? downClockTime : ''}</span>`
});
};
const selectProject = (e: any) => {
//选中项目筛选出项目下的分包单位并清空分包单位value
contractorList.value = skipOptions.value.filter((item) => item.id == e)[0].contractorList;
@ -690,23 +765,10 @@ const getContractorList = async () => {
loading.value = false;
};
const projectTeamOpt = ref([]);
/** 查询当前项目下的班组列表 */
const getProjectTeamList = async () => {
loading.value = true;
const res = await listProjectTeam({
pageNum: 1,
pageSize: 20,
orderByColumn: 'createTime',
isAsc: 'desc',
projectId: currentProject.value.id
});
projectTeamOpt.value = res.rows.map((projectTeam: ProjectTeamVO) => ({
value: projectTeam.id,
label: projectTeam.teamName
}));
loading.value = false;
const handleMonth = async (e: any) => {
calendarDay.value = e;
const res = await listConstructionMonth({ userId: dialog.id, clockMonth: e });
calendarList.value = res.data;
};
/** 上传安全协议书按钮操作 */
@ -732,7 +794,7 @@ const updateProjectFile = async () => {
};
const getTeamName = (teamId: string | number) => {
const team = projectTeamOpt.value.find((item: any) => item.value === teamId);
const team = ProjectTeam.value.find((item: any) => item.value === teamId);
return team ? team.label : teamId;
};
@ -792,6 +854,17 @@ const handleShowDrawer = (row?: ConstructionUserVO) => {
showDetailDrawer.value = true;
};
//打卡按钮
const handlePlayCard = async (row: ConstructionUserVO) => {
const _id = row?.id || ids.value[0];
skipName.value = row?.userName;
const res = await listConstructionMonth({ userId: _id });
calendarList.value = res.data;
dialog.id = _id;
playCardCalendar.value = true;
};
//下载模板
const downloadTemplate = async () => {
const loadingInstance = ElLoadingService({
@ -961,10 +1034,28 @@ const handleClockStatus = async (row: ConstructionUserVO) => {
onMounted(() => {
getList();
getContractorList();
getProjectTeamList();
});
</script>
<style scoped lang="scss">
.monthDay {
padding: 8px;
> div {
position: absolute;
width: 20px;
height: 20px;
border-radius: 50%;
bottom: 8px;
left: 0;
right: 0;
margin: auto;
}
.type2 {
background: rgb(255, 0, 0);
}
.type3 {
background: rgb(0, 128, 0);
}
}
.block_box {
border: 1px solid #9eccfa;
border-radius: 6px;
@ -996,4 +1087,9 @@ onMounted(() => {
.informationStatus:hover .information {
display: block;
}
::v-deep(.el-calendar) {
.el-calendar-day {
padding: 0;
}
}
</style>

View File

@ -9,7 +9,7 @@
</el-form-item>
<el-form-item label="所属班组" prop="leaveType">
<el-select v-model="queryParams.teamId" placeholder="全部" clearable>
<el-option v-for="dict in projectTeamOpt" :key="dict.value" :label="dict.label" :value="dict.value" />
<el-option v-for="dict in ProjectTeam" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<!-- <el-form-item label="请假类型" prop="leaveType">
@ -131,32 +131,69 @@
</template>
</el-dialog>
<!-- 详情对话框 -->
<el-dialog title="审核进度" v-model="dialog.details" width="500px" append-to-body>
<el-steps style="max-width: 600px" :active="auditProgress" align-center finish-status="success">
<el-step :title="teamStatus ? detailObj.gangerName : '班组长'" :status="teamStatus ? teamStatus : ''">
<template #description>
<div v-if="!teamStatus">审核中</div>
<div v-else>
<span>{{ detailObj.gangerOpinion == '2' ? '同意' : '拒绝' }}</span>
<p>{{ detailObj.gangerExplain }}</p>
</div>
</template>
</el-step>
<el-step :title="managerStatus ? detailObj.managerName : '管理员'" :status="managerStatus ? managerStatus : ''">
<template #description>
<div v-if="!managerStatus">审核中</div>
<div v-else>
<span>{{ detailObj.managerOpinion == '2' ? '同意' : '拒绝' }}</span>
<p>{{ detailObj.managerExplain }}</p>
</div>
</template>
</el-step>
<el-step title="结果" :status="resultsStatus">
<template #description>
<div>{{ user_review_status_type[parseInt(detailObj.status) - 1].label }}</div>
</template>
</el-step>
</el-steps>
<el-dialog :title="detailObj.userName + '补卡申请详情'" v-model="dialog.details" width="700px" append-to-body>
<div class="block_box">
<span>补卡申请</span>
<el-form label-width="130px">
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="补卡申请时间">
{{ detailObj.userTime }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="班组长说明">
{{ detailObj.gangerExplain ? detailObj.gangerExplain : '暂无' }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="班组长操作时间">
{{ detailObj.gangerTime ? detailObj.gangerTime : '暂无' }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="管理员说明">
{{ detailObj.managerExplain ? detailObj.managerExplain : '暂无' }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="管理员操作时间">
{{ detailObj.managerTime ? detailObj.managerTime : '暂无' }}
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<div class="block_box">
<span>审核进度</span>
<el-steps style="max-width: 700px" :active="auditProgress" align-center finish-status="success">
<el-step :title="teamStatus ? detailObj.gangerName : '班组长'" :status="teamStatus ? teamStatus : ''">
<template #description>
<div v-if="!teamStatus">审核中</div>
<div v-else>
<span>{{ detailObj.gangerOpinion == '2' ? '同意' : '拒绝' }}</span>
</div>
</template>
</el-step>
<el-step
:title="managerStatus ? detailObj.managerName : '管理员'"
:status="managerStatus ? managerStatus : ''"
v-if="detailObj.gangerOpinion != '3'"
>
<template #description>
<div v-if="!managerStatus">审核中</div>
<div v-else>
<span>{{ detailObj.managerOpinion == '2' ? '同意' : '拒绝' }}</span>
</div>
</template>
</el-step>
<el-step title="结果" :status="resultsStatus">
<template #description>
<div>{{ user_review_status_type[parseInt(detailObj.status) - 1].label }}</div>
</template>
</el-step>
</el-steps>
</div>
</el-dialog>
</div>
</template>
@ -200,7 +237,6 @@ const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const leaveFormRef = ref<ElFormInstance>();
const projectTeamOpt = ref<any[]>([]);
const dialog = reactive<DialogOption>({
visible: false,
title: '',
@ -210,6 +246,7 @@ const dialog = reactive<DialogOption>({
const userStore = useUserStoreHook();
// 从 store 中获取项目列表和当前选中的项目
const currentProject = computed(() => userStore.selectedProject);
const ProjectTeam = computed(() => userStore.ProjectTeamList);
const initFormData: LeaveForm = {
id: undefined,
userId: undefined,
@ -370,25 +407,20 @@ const handleExport = () => {
);
};
/** 搜索班组操作 */
const getProjectTeamList = async () => {
loading.value = true;
const res = await listProjectTeam({
pageNum: 1,
pageSize: 20,
orderByColumn: 'createTime',
isAsc: 'desc',
projectId: currentProject.value.id
});
projectTeamOpt.value = res.rows.map((projectTeam: ProjectTeamVO) => ({
value: projectTeam.id,
label: projectTeam.teamName
}));
loading.value = false;
};
onMounted(() => {
getList();
getProjectTeamList();
});
</script>
<style lang="scss" scoped>
.block_box {
border: 1px solid #9eccfa;
border-radius: 6px;
padding: 10px 20px 20px 10px;
margin: 15px;
> span {
color: #409eff;
font-weight: 700;
font-size: 14px;
}
}
</style>

View File

@ -14,7 +14,7 @@
</el-form-item>
<el-form-item label="所属班组" prop="reissueCardType">
<el-select v-model="queryParams.teamId" placeholder="全部" clearable>
<el-option v-for="dict in projectTeamOpt" :key="dict.value" :label="dict.label" :value="dict.value" />
<el-option v-for="dict in ProjectTeam" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="班组长意见" prop="gangerOpinion" label-width="100px">
@ -107,32 +107,67 @@
</span>
</template>
</el-dialog>
<el-dialog title="审核进度" v-model="dialog.details" width="500px" append-to-body>
<el-steps style="max-width: 600px" :active="auditProgress" align-center finish-status="success">
<el-step :title="teamStatus ? detailObj.gangerName : '班组长'" :status="teamStatus ? teamStatus : ''">
<template #description>
<div v-if="!teamStatus">审核中</div>
<div v-else>
<span>{{ detailObj.gangerOpinion == '2' ? '同意' : '拒绝' }}</span>
<p>{{ detailObj.gangerExplain }}</p>
</div>
</template>
</el-step>
<el-step :title="managerStatus ? detailObj.managerName : '管理员'" :status="managerStatus ? managerStatus : ''">
<template #description>
<div v-if="!managerStatus">审核中</div>
<div v-else>
<span>{{ detailObj.managerOpinion == '2' ? '同意' : '拒绝' }}</span>
<p>{{ detailObj.managerExplain }}</p>
</div>
</template>
</el-step>
<el-step title="结果" :status="resultsStatus">
<template #description>
<div>{{ user_review_status_type[parseInt(detailObj.status) - 1].label }}</div>
</template>
</el-step>
</el-steps>
<el-dialog :title="detailObj.userName + '补卡申请详情'" v-model="dialog.details" width="700px" append-to-body>
<div class="block_box">
<span>补卡申请</span>
<el-form label-width="130px">
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="补卡申请时间">
{{ detailObj.userTime }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="班组长说明">
{{ detailObj.gangerExplain ? detailObj.gangerExplain : '暂无' }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="班组长操作时间">
{{ detailObj.gangerTime ? detailObj.gangerTime : '暂无' }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="管理员说明">
{{ detailObj.managerExplain ? detailObj.managerExplain : '暂无' }}
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="管理员操作时间">
{{ detailObj.managerTime ? detailObj.managerTime : '暂无' }}
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<div class="block_box">
<span>审核进度</span>
<el-steps style="max-width: 700px" :active="auditProgress" align-center finish-status="success">
<el-step :title="teamStatus ? detailObj.gangerName : '班组长'" :status="teamStatus ? teamStatus : ''">
<template #description>
<div v-if="!teamStatus">审核中</div>
<div v-else>
<span>{{ detailObj.gangerOpinion == '2' ? '同意' : '拒绝' }}</span>
<p>{{ detailObj.gangerExplain }}</p>
</div>
</template>
</el-step>
<el-step :title="managerStatus ? detailObj.managerName : '管理员'" :status="managerStatus ? managerStatus : ''">
<template #description>
<div v-if="!managerStatus">审核中</div>
<div v-else>
<span>{{ detailObj.managerOpinion == '2' ? '同意' : '拒绝' }}</span>
<p>{{ detailObj.managerExplain }}</p>
</div>
</template>
</el-step>
<el-step title="结果" :status="resultsStatus">
<template #description>
<div>{{ user_review_status_type[parseInt(detailObj.status) - 1].label }}</div>
</template>
</el-step>
</el-steps>
</div>
</el-dialog>
</div>
</template>
@ -171,7 +206,6 @@ const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const projectTeamOpt = ref<any[]>([]);
const queryFormRef = ref<ElFormInstance>();
const reissueCardFormRef = ref<ElFormInstance>();
const auditForm = reactive<AuditReissueCardForm>({
@ -188,6 +222,7 @@ const dialog = reactive<DialogOption>({
const userStore = useUserStoreHook();
// 从 store 中获取项目列表和当前选中的项目
const currentProject = computed(() => userStore.selectedProject);
const ProjectTeam = computed(() => userStore.ProjectTeamList);
const initFormData: ReissueCardForm = {
id: undefined,
userId: undefined,
@ -197,7 +232,7 @@ const initFormData: ReissueCardForm = {
gangerExplain: undefined,
managerOpinion: undefined,
managerExplain: undefined,
projectId: undefined,
projectId: currentProject.value?.id,
attendanceId: undefined,
remark: undefined
};
@ -209,7 +244,7 @@ const data = reactive<PageData<ReissueCardForm, ReissueCardQuery>>({
userName: undefined,
gangerOpinion: undefined,
managerOpinion: undefined,
projectId: undefined,
projectId: currentProject.value?.id,
teamId: undefined,
reissueCardType: undefined,
params: {}
@ -311,23 +346,6 @@ const handleUpdate = async (row?: ReissueCardVO) => {
dialog.title = '修改施工人员补卡申请';
};
/** 搜索班组操作 */
const getProjectTeamList = async () => {
loading.value = true;
const res = await listProjectTeam({
pageNum: 1,
pageSize: 20,
orderByColumn: 'createTime',
isAsc: 'desc',
projectId: currentProject.value.id
});
projectTeamOpt.value = res.rows.map((projectTeam: ProjectTeamVO) => ({
value: projectTeam.id,
label: projectTeam.teamName
}));
loading.value = false;
};
const handleDetail = (row: ReissueCardVO) => {
detailObj.value = row;
dialog.details = true;
@ -345,6 +363,19 @@ const submitAudit = async () => {
onMounted(() => {
getList();
getProjectTeamList();
});
</script>
<style lang="scss" scoped>
.block_box {
border: 1px solid #9eccfa;
border-radius: 6px;
padding: 10px 20px 20px 10px;
margin: 15px;
> span {
color: #409eff;
font-weight: 700;
font-size: 14px;
}
}
</style>

View File

@ -0,0 +1,283 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="申请人id" prop="userId">
<el-input v-model="queryParams.userId" placeholder="请输入申请人id" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="申请人名字" prop="userName">
<el-input v-model="queryParams.userName" placeholder="请输入申请人名字" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['project:workerDailyReport:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['project:workerDailyReport:edit']"
>修改</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Delete"
:disabled="multiple"
@click="handleDelete()"
v-hasPermi="['project:workerDailyReport:remove']"
>删除</el-button
>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['project:workerDailyReport:export']">导出</el-button>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="workerDailyReportList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="主键id" align="center" prop="id" v-if="false" />
<el-table-column label="申请人名字" align="center" prop="userName" />
<el-table-column label="今日完成工作" align="center" prop="todayCompletedWork" />
<el-table-column label="未完成工作" align="center" prop="unfinishedWork" />
<el-table-column label="明日工作" align="center" prop="tomorrowWork" />
<el-table-column label="需协调与帮助" align="center" prop="coordinationHelp" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['project:workerDailyReport:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button
link
type="primary"
icon="Delete"
@click="handleDelete(scope.row)"
v-hasPermi="['project:workerDailyReport:remove']"
></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改施工人员日报对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="workerDailyReportFormRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="项目id" prop="projectId">
<el-input v-model="form.projectId" placeholder="请输入项目id" />
</el-form-item>
<el-form-item label="班组id" prop="teamId">
<el-input v-model="form.teamId" placeholder="请输入班组id" />
</el-form-item>
<el-form-item label="申请人id" prop="userId">
<el-input v-model="form.userId" placeholder="请输入申请人id" />
</el-form-item>
<el-form-item label="申请人名字" prop="userName">
<el-input v-model="form.userName" placeholder="请输入申请人名字" />
</el-form-item>
<el-form-item label="今日完成工作" prop="todayCompletedWork">
<el-input v-model="form.todayCompletedWork" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="未完成工作" prop="unfinishedWork">
<el-input v-model="form.unfinishedWork" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="明日工作" prop="tomorrowWork">
<el-input v-model="form.tomorrowWork" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="需协调与帮助" prop="coordinationHelp">
<el-input v-model="form.coordinationHelp" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="附件" prop="file">
<file-upload v-model="form.file" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="WorkerDailyReport" lang="ts">
import {
listWorkerDailyReport,
getWorkerDailyReport,
delWorkerDailyReport,
addWorkerDailyReport,
updateWorkerDailyReport
} from '@/api/project/workerDailyReport';
import { WorkerDailyReportVO, WorkerDailyReportQuery, WorkerDailyReportForm } from '@/api/project/workerDailyReport/types';
import { useUserStoreHook } from '@/store/modules/user';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// 获取用户 store
const userStore = useUserStoreHook();
// 从 store 中获取项目列表和当前选中的项目
const currentProject = computed(() => userStore.selectedProject);
const ProjectTeam = computed(() => userStore.ProjectTeamList);
const workerDailyReportList = ref<WorkerDailyReportVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const workerDailyReportFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: WorkerDailyReportForm = {
id: undefined,
projectId: currentProject.value?.id,
teamId: undefined,
userId: undefined,
userName: undefined,
todayCompletedWork: undefined,
unfinishedWork: undefined,
tomorrowWork: undefined,
coordinationHelp: undefined,
file: undefined
};
const data = reactive<PageData<WorkerDailyReportForm, WorkerDailyReportQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
projectId: currentProject.value?.id,
teamId: undefined,
userId: undefined,
userName: undefined,
params: {}
},
rules: {
id: [{ required: true, message: '主键id不能为空', trigger: 'blur' }],
projectId: [{ required: true, message: '项目id不能为空', trigger: 'blur' }],
userId: [{ required: true, message: '申请人id不能为空', trigger: 'blur' }]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询施工人员日报列表 */
const getList = async () => {
loading.value = true;
const res = await listWorkerDailyReport(queryParams.value);
workerDailyReportList.value = res.rows;
total.value = res.total;
loading.value = false;
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
workerDailyReportFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/** 多选框选中数据 */
const handleSelectionChange = (selection: WorkerDailyReportVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
};
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = '添加施工人员日报';
};
/** 修改按钮操作 */
const handleUpdate = async (row?: WorkerDailyReportVO) => {
reset();
const _id = row?.id || ids.value[0];
const res = await getWorkerDailyReport(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = '修改施工人员日报';
};
/** 提交按钮 */
const submitForm = () => {
workerDailyReportFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateWorkerDailyReport(form.value).finally(() => (buttonLoading.value = false));
} else {
await addWorkerDailyReport(form.value).finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
};
/** 删除按钮操作 */
const handleDelete = async (row?: WorkerDailyReportVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除施工人员日报编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delWorkerDailyReport(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'project/workerDailyReport/export',
{
...queryParams.value
},
`workerDailyReport_${new Date().getTime()}.xlsx`
);
};
onMounted(() => {
getList();
});
</script>