init:first commit of plus-ui
This commit is contained in:
		
							
								
								
									
										585
									
								
								src/views/project/attendance/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										585
									
								
								src/views/project/attendance/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,585 @@ | ||||
| <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="人员姓名" prop="userName"> | ||||
|               <el-input v-model="queryParams.userName" placeholder="请输入人员姓名" clearable @keyup.enter="handleQuery" /> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="班组" prop="teamId"> | ||||
|               <el-select v-model="queryParams.teamId" placeholder="全部" clearable> | ||||
|                 <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"> | ||||
|               <el-select v-model="queryParams.typeOfWork" placeholder="全部" clearable> | ||||
|                 <el-option v-for="dict in type_of_work" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="打卡日期" prop="clockDate"> | ||||
|               <el-date-picker | ||||
|                 clearable | ||||
|                 v-model="queryParams.clockDate" | ||||
|                 type="date" | ||||
|                 value-format="YYYY-MM-DD" | ||||
|                 placeholder="请选择打卡日期" | ||||
|                 @change="selectDate" | ||||
|               /> | ||||
|             </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-row :gutter="20"> | ||||
|       <el-col :span="24" :offset="0"> | ||||
|         <el-card shadow="hover"> | ||||
|           <!-- <template #header> | ||||
|             <PieChart style="width: 1em; height: 1em; vertical-align: middle" /> | ||||
|             <span style="vertical-align: middle">命令统计</span> | ||||
|           </template> --> | ||||
|           <div class="el-table el-table--enable-row-hover el-table--medium"> | ||||
|             <div ref="commandstats" style="height: 200px" /> | ||||
|           </div> | ||||
|         </el-card> | ||||
|       </el-col> | ||||
|     </el-row> | ||||
|  | ||||
|     <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:attendance:add']">新增</el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['project:attendance:edit']" | ||||
|               >修改</el-button | ||||
|             > | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['project:attendance:remove']" | ||||
|               >删除</el-button | ||||
|             > | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['project:attendance:export']">导出</el-button> | ||||
|           </el-col> | ||||
|           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> | ||||
|         </el-row> | ||||
|       </template> --> | ||||
|  | ||||
|       <el-table v-loading="loading" :data="attendanceList"> | ||||
|         <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="teamName" /> | ||||
|         <el-table-column label="工种" align="center" prop="typeOfWork"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="type_of_work" :value="scope.row.typeOfWork" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="出勤(天)" align="center" prop="attendanceDays" /> | ||||
|         <el-table-column label="迟到(次)" align="center" prop="lateDays" /> | ||||
|         <el-table-column label="早退(次)" align="center" prop="leaveEarlyDays" /> | ||||
|         <el-table-column label="缺卡(次)" align="center" prop="unClockDays" /> | ||||
|         <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | ||||
|           <template #default="scope"> | ||||
|             <el-button link type="primary" icon="View" @click="handleDetails(scope.row)" v-hasPermi="['project:attendance:edit']">详情</el-button> | ||||
|           </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="attendanceFormRef" :model="form" :rules="rules" label-width="80px"> | ||||
|         <el-form-item label="人员id" prop="userId"> | ||||
|           <el-input v-model="form.userId" placeholder="请输入人员id" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="人脸照" prop="facePic"> | ||||
|           <image-upload v-model="form.facePic" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="项目id" prop="projectId"> | ||||
|           <el-input v-model="form.projectId" placeholder="请输入项目id" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="上班打卡时间" prop="onClockTime"> | ||||
|           <el-date-picker clearable v-model="form.onClockTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择上班打卡时间"> | ||||
|           </el-date-picker> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="下班打卡时间" prop="offClockTime"> | ||||
|           <el-date-picker clearable v-model="form.offClockTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择下班打卡时间"> | ||||
|           </el-date-picker> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="打卡日期" prop="clockDate"> | ||||
|           <el-date-picker clearable v-model="form.clockDate" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="请选择打卡日期"> | ||||
|           </el-date-picker> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="1正常,2迟到,3早退,4缺勤,5补卡" prop="clockStatus"> | ||||
|           <el-radio-group v-model="form.clockStatus"> | ||||
|             <el-radio v-for="dict in clock_status_type" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio> | ||||
|           </el-radio-group> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="代打人员id" prop="pinchUserId"> | ||||
|           <el-input v-model="form.pinchUserId" placeholder="请输入代打人员id" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="多次打卡时间记录" prop="clockRecord"> | ||||
|           <el-input v-model="form.clockRecord" type="textarea" placeholder="请输入内容" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="上下班" prop="commuter"> | ||||
|           <el-input v-model="form.commuter" placeholder="请输入上下班" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="日薪" prop="dailyWage"> | ||||
|           <el-input v-model="form.dailyWage" placeholder="请输入日薪" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="经度" prop="lng"> | ||||
|           <el-input v-model="form.lng" placeholder="请输入经度" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="纬度" prop="lat"> | ||||
|           <el-input v-model="form.lat" placeholder="请输入纬度" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="备注" prop="remark"> | ||||
|           <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" /> | ||||
|         </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> --> | ||||
|     <!-- 考勤详情对话框 --> | ||||
|     <el-dialog v-model="dialog.details" width="1300px"> | ||||
|       <el-calendar ref="calendar" v-model="calendarDay" class="h170 pos-relative"> | ||||
|         <template #header="{ date }"> | ||||
|           <span> | ||||
|             <el-date-picker v-model="monthValue" type="month" placeholder="请选择月份" @change="handleMonth" /> | ||||
|           </span> | ||||
|           <span class="label">{{ date }} —{{ dialog.title }}出勤</span> | ||||
|           <div class="status-detail flex items-center justify-between"> | ||||
|             <div class="dot1">全天考勤正常</div> | ||||
|             <div class="dot2">当天存在异常迟到、早退、缺卡</div> | ||||
|             <div class="dot3">当天提交过补卡申请</div> | ||||
|           </div> | ||||
|         </template> | ||||
|         <template #date-cell="{ data }"> | ||||
|           <div class="flex-c" @click="handleViewPlayCard(data)"> | ||||
|             <p class="time">{{ day(data) }}</p> | ||||
|             <img v-if="!isplayCard(data)" src="@/assets/icons/svg/empty-CZvxqguX.png" /><span v-if="!isplayCard(data)">暂无打卡记录</span> | ||||
|             <div v-if="isplayCard(data)" class="flex-r"><div class="circle" :class="'status' + attendanceStatus(data)"></div></div> | ||||
|             <div v-if="isplayCard(data)" class="flex justify-center flex-col w100% items-center"> | ||||
|               <el-button type="primary" plain size="small" class="w70% my-2" v-if="workTime(data)">{{ workTime(data) }} 上班打卡</el-button> | ||||
|               <el-button type="danger" plain size="small" class="w50% my-2" v-else>上班缺卡</el-button> | ||||
|               <span></span> | ||||
|               <el-button type="warning" plain size="small" class="w70%" v-if="workFromTime(data)">{{ workFromTime(data) }} 下班打卡</el-button> | ||||
|               <el-button type="danger" plain size="small" class="w50%" v-else>下班缺卡</el-button> | ||||
|             </div> | ||||
|           </div> | ||||
|         </template> | ||||
|       </el-calendar> | ||||
|     </el-dialog> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup name="Attendance" lang="ts"> | ||||
| import { | ||||
|   listAttendance, | ||||
|   getAttendance, | ||||
|   delAttendance, | ||||
|   addAttendance, | ||||
|   updateAttendance, | ||||
|   listAttendanceTwoWeek, | ||||
|   listAttendanceMonth | ||||
| } from '@/api/project/attendance'; | ||||
| import { option } from '@/api/project/attendance/echarts'; | ||||
| import * as echarts from 'echarts'; | ||||
| import { AttendanceVO, AttendanceQuery, AttendanceForm, AttendanceTwoWeekVO, AttendanceMonthVO } from '@/api/project/attendance/types'; | ||||
| import { listProjectTeam } from '@/api/project/projectTeam'; | ||||
| import { ProjectTeamVO } from '@/api/project/projectTeam/types'; | ||||
| import { useUserStoreHook } from '@/store/modules/user'; | ||||
| import { parseTime } from '@/utils/ruoyi'; | ||||
| const commandstats = ref(); | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| const { clock_status_type, type_of_work } = toRefs<any>(proxy?.useDict('clock_status_type', 'type_of_work')); | ||||
| import type { CalendarDateType, CalendarInstance } from 'element-plus'; | ||||
| // 获取用户 store | ||||
| 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); | ||||
| 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 calendarDay = ref<Date | null>(null); | ||||
| const monthValue = ref<Date | null>(null); | ||||
| const calendarList = ref<AttendanceMonthVO[]>(); | ||||
| const queryFormRef = ref<ElFormInstance>(); | ||||
| const attendanceFormRef = ref<ElFormInstance>(); | ||||
| const commandstatsIntance = ref<any>(null); | ||||
| const dialog = reactive<DialogOption>({ | ||||
|   visible: false, | ||||
|   details: false, | ||||
|   title: '' | ||||
| }); | ||||
| const echartsOption = ref<any>({}); | ||||
| const initFormData: AttendanceForm = { | ||||
|   id: undefined, | ||||
|   userId: undefined, | ||||
|   facePic: undefined, | ||||
|   onClockTime: undefined, | ||||
|   offClockTime: undefined, | ||||
|   clockDate: undefined, | ||||
|   clockStatus: undefined, | ||||
|   pinchUserId: undefined, | ||||
|   clockRecord: undefined, | ||||
|   commuter: undefined, | ||||
|   dailyWage: undefined, | ||||
|   projectId: currentProject.value.id, | ||||
|   lng: undefined, | ||||
|   lat: undefined, | ||||
|   remark: undefined, | ||||
|   typeOfWork: undefined, | ||||
|   teamId: undefined | ||||
| }; | ||||
| const data = reactive<PageData<AttendanceForm, AttendanceQuery>>({ | ||||
|   form: { ...initFormData }, | ||||
|   queryParams: { | ||||
|     pageNum: 1, | ||||
|     pageSize: 10, | ||||
|     userName: undefined, | ||||
|     clockDate: undefined, | ||||
|     clockStatus: undefined, | ||||
|     commuter: undefined, | ||||
|     projectId: currentProject.value.id, | ||||
|     typeOfWork: undefined, | ||||
|     teamId: undefined, | ||||
|     params: {} | ||||
|   }, | ||||
|   rules: { | ||||
|     id: [{ required: true, message: '主键id不能为空', trigger: 'blur' }], | ||||
|     userId: [{ required: true, message: '人员id不能为空', trigger: 'blur' }], | ||||
|     facePic: [{ required: true, message: '人脸照不能为空', trigger: 'blur' }], | ||||
|     projectId: [{ required: true, message: '项目id不能为空', trigger: 'blur' }], | ||||
|     clockDate: [{ required: true, message: '打卡日期不能为空', trigger: 'blur' }], | ||||
|     clockStatus: [{ required: true, message: '1正常,2迟到,3早退,4缺勤,5补卡不能为空', trigger: 'change' }] | ||||
|   } | ||||
| }); | ||||
|  | ||||
| const day = computed(() => (date) => { | ||||
|   return date.day.split('-').slice(1).join('-'); | ||||
| }); | ||||
| //是否打卡 | ||||
| const isplayCard = computed(() => (date) => { | ||||
|   return calendarList.value.some((item) => item.clockDate == date.day); | ||||
| }); | ||||
| //打卡时间下标 | ||||
| const playCardIdx = computed(() => (date) => { | ||||
|   return calendarList.value.findIndex((item) => item.clockDate == date.day); | ||||
| }); | ||||
| //上班时间 | ||||
| const workTime = computed(() => (date) => { | ||||
|   return calendarList.value[playCardIdx.value(date)].attendanceList[0].clockTime?.slice(10); | ||||
| }); | ||||
|  | ||||
| //下班时间 | ||||
| const workFromTime = computed(() => (date) => { | ||||
|   return calendarList.value[playCardIdx.value(date)].attendanceList[1]?.clockTime?.slice(10); | ||||
| }); | ||||
|  | ||||
| //考勤状态 | ||||
| const attendanceStatus = computed(() => (date) => { | ||||
|   return calendarList.value[playCardIdx.value(date)].status; | ||||
| }); | ||||
|  | ||||
| const { queryParams, form, rules } = toRefs(data); | ||||
|  | ||||
| const calendar = ref<CalendarInstance>(); | ||||
|  | ||||
| const handleMonth = async (e: any) => { | ||||
|   calendarDay.value = e; | ||||
|   handleCalendarMonth(e); | ||||
| }; | ||||
|  | ||||
| const selectDate = (e: any) => { | ||||
|   handleQuery(); | ||||
| }; | ||||
|  | ||||
| const handleCalendarMonth = async (e?) => { | ||||
|   let clockMonth; | ||||
|   if (e) { | ||||
|     clockMonth = parseTime(e, '{y}-{m}'); | ||||
|   } | ||||
|  | ||||
|   const res = await listAttendanceMonth({ userId: dialog.id, clockMonth }); | ||||
|   calendarList.value = res.data; | ||||
| }; | ||||
|  | ||||
| /** 查看打卡记录详情 */ | ||||
| const handleViewPlayCard = async (data: any) => { | ||||
|   if (data.type == 'next-month' || data.type == 'prev-month') { | ||||
|     monthValue.value = data.date; | ||||
|     handleCalendarMonth(monthValue.value); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| /** 查询考勤列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await listAttendance(queryParams.value); | ||||
|   attendanceList.value = res.rows; | ||||
|   total.value = res.total; | ||||
|   loading.value = false; | ||||
| }; | ||||
|  | ||||
| /** 查询近两周考勤列表 */ | ||||
| const getListTwoWeek = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await listAttendanceTwoWeek(queryParams.value); | ||||
|   attendanceTwoWeekList.value = res.data; | ||||
|   echartsOption.value = { ...option(attendanceTwoWeekList.value) }; | ||||
|   commandstatsIntance.value.setOption(echartsOption.value); | ||||
| }; | ||||
|  | ||||
| /** 取消按钮 */ | ||||
| const cancel = () => { | ||||
|   reset(); | ||||
|   dialog.visible = false; | ||||
| }; | ||||
|  | ||||
| /** 表单重置 */ | ||||
| const reset = () => { | ||||
|   form.value = { ...initFormData }; | ||||
|   attendanceFormRef.value?.resetFields(); | ||||
| }; | ||||
|  | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.value.pageNum = 1; | ||||
|   getList(); | ||||
|   getListTwoWeek(); | ||||
| }; | ||||
|  | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   handleQuery(); | ||||
| }; | ||||
|  | ||||
| //处理获取到的月份 | ||||
| const incrementMonth = (dateStr: string, monthsToAdd: number) => { | ||||
|   const [yearPart, monthPart] = dateStr.replace(/\s/g, '').split('年'); | ||||
|   const year = parseInt(yearPart, 10); | ||||
|   const month = parseInt(monthPart.replace('月', ''), 10); | ||||
|   // 创建一个新的 Date 对象,设置为输入日期的第一天 | ||||
|   const date = new Date(year, month - 1, 1); | ||||
|  | ||||
|   // 增加一个月 | ||||
|   date.setMonth(date.getMonth() + monthsToAdd); | ||||
|  | ||||
|   // 提取增加一个月后的年份和月份 | ||||
|   const newYear = date.getFullYear(); | ||||
|   const newMonth = String(date.getMonth() + 1).padStart(2, '0'); | ||||
|  | ||||
|   // 返回格式化后的日期字符串 | ||||
|   return `${newYear}-${newMonth}`; | ||||
| }; | ||||
|  | ||||
| /** 详情按钮操作 */ | ||||
| const handleDetails = async (row?: AttendanceVO) => { | ||||
|   const res = await listAttendanceMonth({ userId: row?.id }); | ||||
|   calendarList.value = res.data; | ||||
|   dialog.details = true; | ||||
|   dialog.id = row?.id; | ||||
|   dialog.title = row?.userName || ''; | ||||
| }; | ||||
|  | ||||
| /** 提交按钮 */ | ||||
| const submitForm = () => { | ||||
|   attendanceFormRef.value?.validate(async (valid: boolean) => { | ||||
|     if (valid) { | ||||
|       buttonLoading.value = true; | ||||
|       if (form.value.id) { | ||||
|         await updateAttendance(form.value).finally(() => (buttonLoading.value = false)); | ||||
|       } else { | ||||
|         await addAttendance(form.value).finally(() => (buttonLoading.value = false)); | ||||
|       } | ||||
|       proxy?.$modal.msgSuccess('操作成功'); | ||||
|       dialog.visible = false; | ||||
|       await getList(); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| /** 删除按钮操作 */ | ||||
| // const handleDelete = async (row?: AttendanceVO) => { | ||||
| //   const _ids = row?.id || ids.value; | ||||
| //   await proxy?.$modal.confirm('是否确认删除考勤编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false)); | ||||
| //   await delAttendance(_ids); | ||||
| //   proxy?.$modal.msgSuccess('删除成功'); | ||||
| //   await getList(); | ||||
| // }; | ||||
|  | ||||
| /** 导出按钮操作 */ | ||||
| // const handleExport = () => { | ||||
| //   proxy?.download( | ||||
| //     'project/attendance/export', | ||||
| //     { | ||||
| //       ...queryParams.value | ||||
| //     }, | ||||
| //     `attendance_${new Date().getTime()}.xlsx` | ||||
| //   ); | ||||
| // }; | ||||
| //初始化图表 | ||||
| const init = () => { | ||||
|   commandstatsIntance.value = echarts.init(commandstats.value, 'macarons'); | ||||
|  | ||||
|   commandstatsIntance.value.on('click', function (params) { | ||||
|     queryParams.value.clockDate = params.name; | ||||
|     handleQuery(); | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| //监听项目id刷新数据 | ||||
| const listeningProject = watch( | ||||
|   () => currentProject.value.id, | ||||
|   (nid, oid) => { | ||||
|     queryParams.value.projectId = nid; | ||||
|     form.value.projectId = nid; | ||||
|     getList(); | ||||
|   } | ||||
| ); | ||||
|  | ||||
| onUnmounted(() => { | ||||
|   listeningProject(); | ||||
| }); | ||||
|  | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
|   getListTwoWeek(); | ||||
|   init(); | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .label { | ||||
|   font-size: 24px; | ||||
|   position: absolute; | ||||
|   left: 50%; | ||||
|   top: 0; | ||||
|   transform: translate(-50%); | ||||
|   color: #000; | ||||
|   font-weight: 500; | ||||
| } | ||||
| .status-detail { | ||||
|   margin: 0 15px; | ||||
|   position: relative; | ||||
|   font-size: 12px; | ||||
|   > div { | ||||
|     margin: 0 15px; | ||||
|     position: relative; | ||||
|     font-size: 12px; | ||||
|     &::before { | ||||
|       position: absolute; | ||||
|       content: ''; | ||||
|       display: inline-block; | ||||
|       left: -15px; | ||||
|       top: 30%; | ||||
|       width: 8px; | ||||
|       height: 8px; | ||||
|       border-radius: 50%; | ||||
|     } | ||||
|   } | ||||
|   .dot1 { | ||||
|     &::before { | ||||
|       background-color: #1d6fe9; | ||||
|     } | ||||
|   } | ||||
|   .dot2 { | ||||
|     &::before { | ||||
|       background-color: #f55f4e; | ||||
|     } | ||||
|   } | ||||
|   .dot3 { | ||||
|     &::before { | ||||
|       background-color: #ff8d1a; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| .flex-c { | ||||
|   height: 110px; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   justify-content: center; | ||||
|   align-items: center; | ||||
|   position: relative; | ||||
|   .time { | ||||
|     position: absolute; | ||||
|     z-index: 10; | ||||
|     right: 0; | ||||
|     top: 0; | ||||
|   } | ||||
|   img { | ||||
|     width: 50%; | ||||
|     height: 50%; | ||||
|   } | ||||
|   > span { | ||||
|     font-size: 12px; | ||||
|     color: #ccc; | ||||
|     padding-top: 5px; | ||||
|   } | ||||
| } | ||||
| .el-calendar-table__row { | ||||
|   height: 100px; | ||||
| } | ||||
| .flex-r { | ||||
|   width: 100%; | ||||
|   display: flex; | ||||
|   flex-direction: row; | ||||
|   justify-content: center; | ||||
|   padding-top: 5px; | ||||
|   height: 10px; | ||||
|   .circle { | ||||
|     width: 7px; | ||||
|     height: 7px; | ||||
|     border-radius: 50%; | ||||
|     margin: 0 2px; | ||||
|     position: absolute; | ||||
|     z-index: 10; | ||||
|     right: 12px; | ||||
|     top: 35px; | ||||
|   } | ||||
|   .status2 { | ||||
|     background: #f55f4e; | ||||
|   } | ||||
|   .status1 { | ||||
|     background: #1d6fe9; | ||||
|   } | ||||
|   .status3 { | ||||
|     background: #ff8d1a; | ||||
|   } | ||||
| } | ||||
| ::v-deep(.el-calendar) { | ||||
|   .el-calendar__body { | ||||
|     height: 600px; | ||||
|     overflow: auto; | ||||
|   } | ||||
|   td { | ||||
|     height: 110px; | ||||
|     .el-calendar-day { | ||||
|       height: 100%; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										180
									
								
								src/views/project/attendanceRecords/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								src/views/project/attendanceRecords/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,180 @@ | ||||
| <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="人员姓名" prop="userName"> | ||||
|               <el-input v-model="queryParams.userName" placeholder="请输入人员姓名" clearable @keyup.enter="handleQuery" /> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="班组" prop="teamId"> | ||||
|               <el-select v-model="queryParams.teamId" placeholder="全部" clearable> | ||||
|                 <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"> | ||||
|               <el-date-picker clearable v-model="queryParams.clockDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择打卡日期" /> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="考勤状态" prop="clockStatus"> | ||||
|               <el-select v-model="queryParams.clockStatus" placeholder="全部" clearable> | ||||
|                 <el-option v-for="dict in clock_status_type" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </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"> | ||||
|       <el-table v-loading="loading" :data="attendanceList"> | ||||
|         <el-table-column type="selection" width="55" align="center" /> | ||||
|         <el-table-column label="序号" type="index" width="60" align="center" /> | ||||
|         <el-table-column label="人员姓名" align="center" prop="userName" /> | ||||
|         <!-- <el-table-column label="上班打卡时间" align="center" prop="onClockTime" width="180"> | ||||
|           <template #default="scope"> | ||||
|             <span>{{ parseTime(scope.row.onClockTime, '{y}-{m}-{d}') }}</span> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="下班打卡时间" align="center" prop="offClockTime" width="180"> | ||||
|           <template #default="scope"> | ||||
|             <span>{{ parseTime(scope.row.offClockTime, '{y}-{m}-{d}') }}</span> | ||||
|           </template> | ||||
|         </el-table-column> --> | ||||
|         <el-table-column label="上下班" align="center" prop="commuter"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="commuter_type" :value="scope.row.commuter" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="打卡日期" align="center" prop="clockDate"> | ||||
|           <template #default="scope"> | ||||
|             <span>{{ parseTime(scope.row.clockDate, '{y}-{m}-{d}') }}</span> | ||||
|           </template> | ||||
|         </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}') ? parseTime(scope.row.clockTime, '{y}-{m}-{d} {h}:{i}:{s}') : '缺卡' | ||||
|             }}</span> | ||||
|           </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> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup name="Attendance" lang="ts"> | ||||
| import { listAttendance, getAttendance, delAttendance, addAttendance, updateAttendance } from '@/api/project/attendanceRecords'; | ||||
| import { AttendanceVO, AttendanceQuery, AttendanceForm } from '@/api/project/attendanceRecords/types'; | ||||
| import { listProjectTeam } from '@/api/project/projectTeam'; | ||||
| import { ProjectTeamVO } from '@/api/project/projectTeam/types'; | ||||
| import { useUserStoreHook } from '@/store/modules/user'; | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| const { clock_status_type, commuter_type } = toRefs<any>(proxy?.useDict('clock_status_type', 'commuter_type')); | ||||
|  | ||||
| const attendanceList = ref<AttendanceVO[]>([]); | ||||
| 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 attendanceFormRef = ref<ElFormInstance>(); | ||||
|  | ||||
| const dialog = reactive<DialogOption>({ | ||||
|   visible: false, | ||||
|   title: '' | ||||
| }); | ||||
| // 获取用户 store | ||||
| const userStore = useUserStoreHook(); | ||||
| // 从 store 中获取项目列表和当前选中的项目 | ||||
| const currentProject = computed(() => userStore.selectedProject); | ||||
| const ProjectTeam = computed(() => userStore.ProjectTeamList); | ||||
| const initFormData: AttendanceForm = { | ||||
|   id: undefined, | ||||
|   userId: undefined, | ||||
|   facePic: undefined, | ||||
|   projectId: currentProject.value?.id, | ||||
|   onClockTime: undefined, | ||||
|   offClockTime: undefined, | ||||
|   clockDate: undefined, | ||||
|   clockStatus: undefined, | ||||
|   pinchUserId: undefined, | ||||
|   clockRecord: undefined, | ||||
|   commuter: undefined, | ||||
|   dailyWage: undefined, | ||||
|   lng: undefined, | ||||
|   lat: undefined, | ||||
|   remark: undefined | ||||
| }; | ||||
| const data = reactive<PageData<AttendanceForm, AttendanceQuery>>({ | ||||
|   form: { ...initFormData }, | ||||
|   queryParams: { | ||||
|     pageNum: 1, | ||||
|     pageSize: 10, | ||||
|     userName: undefined, | ||||
|     projectId: currentProject.value?.id, | ||||
|     clockDate: undefined, | ||||
|     clockStatus: undefined, | ||||
|     commuter: undefined, | ||||
|     params: {}, | ||||
|     teamId: undefined | ||||
|   }, | ||||
|   rules: { | ||||
|     id: [{ required: true, message: '主键id不能为空', trigger: 'blur' }], | ||||
|     userId: [{ required: true, message: '人员id不能为空', trigger: 'blur' }], | ||||
|     facePic: [{ required: true, message: '人脸照不能为空', trigger: 'blur' }], | ||||
|     projectId: [{ required: true, message: '项目id不能为空', trigger: 'blur' }], | ||||
|     clockDate: [{ required: true, message: '打卡日期不能为空', trigger: 'blur' }], | ||||
|     clockStatus: [{ required: true, message: '1正常,2迟到,3早退,4缺勤,5补卡不能为空', trigger: 'change' }] | ||||
|   } | ||||
| }); | ||||
|  | ||||
| const { queryParams, form, rules } = toRefs(data); | ||||
|  | ||||
| /** 查询考勤列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await listAttendance(queryParams.value); | ||||
|   attendanceList.value = res.rows; | ||||
|   total.value = res.total; | ||||
|   loading.value = false; | ||||
| }; | ||||
|  | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.value.pageNum = 1; | ||||
|   getList(); | ||||
| }; | ||||
|  | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   handleQuery(); | ||||
| }; | ||||
|  | ||||
| //监听项目id刷新数据 | ||||
| const listeningProject = watch( | ||||
|   () => currentProject.value.id, | ||||
|   (nid, oid) => { | ||||
|     queryParams.value.projectId = nid; | ||||
|     form.value.projectId = nid; | ||||
|     getList(); | ||||
|   } | ||||
| ); | ||||
|  | ||||
| onUnmounted(() => { | ||||
|   listeningProject(); | ||||
| }); | ||||
|  | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
| }); | ||||
| </script> | ||||
							
								
								
									
										177
									
								
								src/views/project/constructionBlacklist/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								src/views/project/constructionBlacklist/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,177 @@ | ||||
| <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="名字" prop="userName"> | ||||
|               <el-input v-model="queryParams.userName" placeholder="请输入名字" clearable @keyup.enter="handleQuery" /> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="身份证号码" prop="sfzNumber" label-width="120"> | ||||
|               <el-input v-model="queryParams.sfzNumber" 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="danger" | ||||
|               plain | ||||
|               icon="Delete" | ||||
|               :disabled="multiple" | ||||
|               @click="handleDelete()" | ||||
|               v-hasPermi="['project:constructionBlacklist:remove']" | ||||
|               >删除 | ||||
|             </el-button> | ||||
|           </el-col> | ||||
|           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> | ||||
|         </el-row> | ||||
|       </template> | ||||
|  | ||||
|       <el-table v-loading="loading" :data="constructionBlacklistList" @selection-change="handleSelectionChange"> | ||||
|         <el-table-column type="selection" width="55" align="center" /> | ||||
|         <el-table-column label="序号" type="index" width="60" align="center" /> | ||||
|         <el-table-column label="名字" align="center" prop="userName" /> | ||||
|         <el-table-column label="身份证号码" align="center" prop="sfzNumber" /> | ||||
|         <el-table-column label="备注" align="center" prop="remark" /> | ||||
|         <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | ||||
|           <template #default="scope"> | ||||
|             <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['project:constructionBlacklist:remove']"> | ||||
|               删除 | ||||
|             </el-button> | ||||
|           </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> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup name="ConstructionBlacklist" lang="ts"> | ||||
| import { delConstructionBlacklist, listConstructionBlacklist } from '@/api/project/constructionBlacklist'; | ||||
| import { ConstructionBlacklistForm, ConstructionBlacklistQuery, ConstructionBlacklistVO } from '@/api/project/constructionBlacklist/types'; | ||||
| import { useUserStoreHook } from '@/store/modules/user'; | ||||
|  | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| // 获取用户 store | ||||
| const userStore = useUserStoreHook(); | ||||
| // 从 store 中获取项目列表和当前选中的项目 | ||||
| const currentProject = computed(() => userStore.selectedProject); | ||||
| const constructionBlacklistList = ref<ConstructionBlacklistVO[]>([]); | ||||
| 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 constructionBlacklistFormRef = ref<ElFormInstance>(); | ||||
|  | ||||
| const dialog = reactive<DialogOption>({ | ||||
|   visible: false, | ||||
|   title: '' | ||||
| }); | ||||
|  | ||||
| const initFormData: ConstructionBlacklistForm = { | ||||
|   projectId: currentProject.value.id, | ||||
|   userId: undefined, | ||||
|   remark: undefined | ||||
| }; | ||||
| const data = reactive<PageData<ConstructionBlacklistForm, ConstructionBlacklistQuery>>({ | ||||
|   form: { ...initFormData }, | ||||
|   queryParams: { | ||||
|     pageNum: 1, | ||||
|     pageSize: 10, | ||||
|     id: undefined, | ||||
|     projectId: currentProject.value.id, | ||||
|     userId: undefined, | ||||
|     userName: undefined, | ||||
|     sfzNumber: undefined, | ||||
|     params: {} | ||||
|   }, | ||||
|   rules: { | ||||
|     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 listConstructionBlacklist(queryParams.value); | ||||
|   constructionBlacklistList.value = res.rows; | ||||
|   total.value = res.total; | ||||
|   loading.value = false; | ||||
| }; | ||||
|  | ||||
| /** 取消按钮 */ | ||||
| const cancel = () => { | ||||
|   reset(); | ||||
|   dialog.visible = false; | ||||
| }; | ||||
|  | ||||
| /** 表单重置 */ | ||||
| const reset = () => { | ||||
|   form.value = { ...initFormData }; | ||||
|   constructionBlacklistFormRef.value?.resetFields(); | ||||
| }; | ||||
|  | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.value.pageNum = 1; | ||||
|   getList(); | ||||
| }; | ||||
|  | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   handleQuery(); | ||||
| }; | ||||
|  | ||||
| /** 多选框选中数据 */ | ||||
| const handleSelectionChange = (selection: ConstructionBlacklistVO[]) => { | ||||
|   ids.value = selection.map((item) => item.id); | ||||
|   single.value = selection.length != 1; | ||||
|   multiple.value = !selection.length; | ||||
| }; | ||||
|  | ||||
| /** 删除按钮操作 */ | ||||
| const handleDelete = async (row?: ConstructionBlacklistVO) => { | ||||
|   const _ids = row?.id || ids.value; | ||||
|   await proxy?.$modal.confirm('是否确认删除黑名单编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false)); | ||||
|   await delConstructionBlacklist(_ids); | ||||
|   proxy?.$modal.msgSuccess('删除成功'); | ||||
|   await getList(); | ||||
| }; | ||||
|  | ||||
| //监听项目id刷新数据 | ||||
| const listeningProject = watch( | ||||
|   () => currentProject.value.id, | ||||
|   (nid, oid) => { | ||||
|     queryParams.value.projectId = nid; | ||||
|     form.value.projectId = nid; | ||||
|     getList(); | ||||
|   } | ||||
| ); | ||||
|  | ||||
| onUnmounted(() => { | ||||
|   listeningProject(); | ||||
| }); | ||||
|  | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
| }); | ||||
| </script> | ||||
| @ -0,0 +1,188 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <div class="block_box"> | ||||
|       <span>用户信息</span> | ||||
|       <el-form label-width="130px"> | ||||
|         <el-row :gutter="20" justify="space-around"> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="人脸照"> | ||||
|               <el-image :src="userDetail?.facePicUrl" style="width: 150px; height: 150px" /> | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="姓名"> | ||||
|               {{ userDetail?.userName }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="联系电话"> | ||||
|               {{ userDetail?.phone }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="性别"> | ||||
|               <dict-tag :options="user_sex_type" :value="userDetail?.sex" /> | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="年龄"> | ||||
|               {{ dayjs().diff(dayjs(userDetail?.sfzBirth), 'year') }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="民族"> | ||||
|               {{ userDetail?.nation }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="籍贯"> | ||||
|               {{ userDetail?.nativePlace }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="身份证号码"> | ||||
|               {{ userDetail?.sfzNumber }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="身份证号码"> | ||||
|               {{ userDetail?.sfzNumber }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="身份证有效开始期"> | ||||
|               {{ dayjs(userDetail?.sfzStart).format('YYYY 年 MM 月 DD 日') }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="身份证有效结束期"> | ||||
|               {{ dayjs(userDetail?.sfzEnd).format('YYYY 年 MM 月 DD 日') }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="身份证地址"> | ||||
|               {{ userDetail?.sfzSite }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|         </el-row> | ||||
|       </el-form> | ||||
|     </div> | ||||
|     <div class="block_box"> | ||||
|       <span>银行卡</span> | ||||
|       <el-form label-width="130px"> | ||||
|         <el-row :gutter="20"> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="银行卡号"> | ||||
|               {{ userDetail?.yhkNumber }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="银行开户行"> | ||||
|               {{ userDetail?.yhkOpeningBank }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="持卡人"> | ||||
|               {{ userDetail?.yhkCardholder }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|         </el-row> | ||||
|       </el-form> | ||||
|     </div> | ||||
|     <div class="block_box"> | ||||
|       <span>单位信息</span> | ||||
|       <el-form label-width="130px"> | ||||
|         <el-row :gutter="20"> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="施工单位"> | ||||
|               {{ userDetail?.contractorVo?.name }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="工种"> | ||||
|               <dict-tag :options="type_of_work" :value="userDetail?.typeOfWork" /> | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|         </el-row> | ||||
|       </el-form> | ||||
|     </div> | ||||
|     <div class="block_box"> | ||||
|       <span>其他信息</span> | ||||
|       <el-form label-width="130px"> | ||||
|         <el-row :gutter="20"> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="班组"> | ||||
|               {{ userDetail?.teamVo?.teamName }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="打卡状态"> | ||||
|               <dict-tag :options="user_clock_type" :value="userDetail?.clock" /> | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="入场时间"> | ||||
|               {{ userDetail?.entryDate ? dayjs(userDetail?.entryDate).format('YYYY 年 MM 月 DD 日 HH:mm:ss') : '' }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|           <el-col :span="12"> | ||||
|             <el-form-item label="离场时间"> | ||||
|               {{ userDetail?.leaveDate ? dayjs(userDetail?.leaveDate).format('YYYY 年 MM 月 DD 日 HH:mm:ss') : '' }} | ||||
|             </el-form-item> | ||||
|           </el-col> | ||||
|         </el-row> | ||||
|       </el-form> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { getConstructionUser } from '@/api/project/constructionUser'; | ||||
| import { ConstructionUserVO } from '@/api/project/constructionUser/types'; | ||||
| import dayjs from 'dayjs'; | ||||
|  | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| const { type_of_work, user_sex_type, user_clock_type } = toRefs<any>(proxy?.useDict('type_of_work', 'user_sex_type', 'user_clock_type')); | ||||
|  | ||||
| interface Props { | ||||
|   userId?: string | number; | ||||
| } | ||||
|  | ||||
| const props = defineProps<Props>(); | ||||
| const loading = ref<boolean>(false); | ||||
| const userDetail = ref<ConstructionUserVO>(); | ||||
| const getUserDetail = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await getConstructionUser(props.userId); | ||||
|   if (res.data && res.code === 200) { | ||||
|     userDetail.value = res.data; | ||||
|   } | ||||
|   loading.value = false; | ||||
| }; | ||||
|  | ||||
| onMounted(() => { | ||||
|   getUserDetail(); | ||||
| }); | ||||
|  | ||||
| watch( | ||||
|   () => props.userId, | ||||
|   (newId, oldId) => { | ||||
|     if (newId !== oldId) { | ||||
|       getUserDetail(); | ||||
|     } | ||||
|   } | ||||
| ); | ||||
| </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> | ||||
							
								
								
									
										1175
									
								
								src/views/project/constructionUser/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1175
									
								
								src/views/project/constructionUser/index.vue
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -0,0 +1,93 @@ | ||||
| <template> | ||||
|   <el-button type="primary" @click="handleUpdateFile">保存文件</el-button> | ||||
|   <el-row :gutter="20"> | ||||
|     <el-col :span="8"> | ||||
|       <el-menu :default-active="activeMenu" @select="handleMenuClick"> | ||||
|         <el-menu-item :key="item.value" v-for="item in contractor_file_type" :index="item.value"> | ||||
|           <el-space> | ||||
|             <p>{{ item.label }}</p> | ||||
|             <el-tag type="success" v-if="ossIdMap[item.value]">已上传</el-tag> | ||||
|             <el-tag type="warning" v-else>未上传</el-tag> | ||||
|           </el-space> | ||||
|         </el-menu-item> | ||||
|       </el-menu> | ||||
|     </el-col> | ||||
|     <el-col :span="16"> | ||||
|       <file-upload | ||||
|         v-model="ossIdMap[activeMenu]" | ||||
|         :limit="20" | ||||
|         :file-size="50" | ||||
|         :file-type="['pdf']" | ||||
|         isDarg | ||||
|         @update:model-value=" | ||||
|           (args) => { | ||||
|             handleOssUpdate(args); | ||||
|           } | ||||
|         " | ||||
|       /> | ||||
|     </el-col> | ||||
|   </el-row> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { ref } from 'vue'; | ||||
| import { getContractor, updateContractor } from '@/api/project/contractor'; | ||||
|  | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| const { contractor_file_type } = toRefs<any>(proxy?.useDict('contractor_file_type')); | ||||
|  | ||||
| interface Props { | ||||
|   contractorId?: string | number; | ||||
| } | ||||
|  | ||||
| const props = defineProps<Props>(); | ||||
| const loading = ref<boolean>(false); | ||||
|  | ||||
| // 当前选中的菜单项 | ||||
| const activeMenu = ref('0'); // 默认选中第一个 | ||||
| const ossIdMap = ref<Record<string, string>>({}); | ||||
| // 处理菜单点击事件 | ||||
| const handleMenuClick = (index: string) => { | ||||
|   activeMenu.value = index; | ||||
| }; | ||||
| // 获取公司详情 | ||||
| const getComponentVO = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await getContractor(props.contractorId); | ||||
|   ossIdMap.value = res.data.fileMap ?? { '0': '' }; | ||||
|   loading.value = false; | ||||
| }; | ||||
|  | ||||
| const handleOssUpdate = (ossId: string) => { | ||||
|   // 判断 ossId 是否为空 | ||||
|   if (ossId === '' || ossId === null || ossId === undefined) { | ||||
|     delete ossIdMap.value[activeMenu.value]; // 删除 key | ||||
|   } else { | ||||
|     ossIdMap.value[activeMenu.value] = ossId; // 直接赋值 | ||||
|   } | ||||
|   console.log(ossIdMap.value); | ||||
| }; | ||||
|  | ||||
| const handleUpdateFile = async () => { | ||||
|   loading.value = true; | ||||
|   await updateContractor({ | ||||
|     id: props.contractorId, | ||||
|     fileMap: ossIdMap.value | ||||
|   }); | ||||
|   proxy?.$modal.msgSuccess('保存成功'); | ||||
|   loading.value = false; | ||||
| }; | ||||
|  | ||||
| watch( | ||||
|   () => props.contractorId, | ||||
|   (newId, oldId) => { | ||||
|     if (newId !== oldId) { | ||||
|       getComponentVO(); | ||||
|     } | ||||
|   } | ||||
| ); | ||||
|  | ||||
| onMounted(() => { | ||||
|   getComponentVO(); | ||||
| }); | ||||
| </script> | ||||
							
								
								
									
										283
									
								
								src/views/project/contractor/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								src/views/project/contractor/index.vue
									
									
									
									
									
										Normal 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="公司名称" prop="name"> | ||||
|               <el-input v-model="queryParams.name" 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:contractor:add']"> 新增 </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['project:contractor:edit']" | ||||
|               >修改 | ||||
|             </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['project:contractor:remove']" | ||||
|               >删除 | ||||
|             </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['project:contractor:export']">导出 </el-button> | ||||
|           </el-col> | ||||
|           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> | ||||
|         </el-row> | ||||
|       </template> | ||||
|  | ||||
|       <el-table v-loading="loading" :data="contractorList" @selection-change="handleSelectionChange"> | ||||
|         <el-table-column type="selection" width="55" align="center" /> | ||||
|         <el-table-column label="序号" type="index" width="60" align="center" /> | ||||
|         <el-table-column label="公司名称" align="center" prop="name" /> | ||||
|         <el-table-column label="负责人" align="center" prop="principal" /> | ||||
|         <el-table-column label="负责人联系电话" align="center" prop="principalPhone" /> | ||||
|         <el-table-column label="管理人" align="center" prop="custodian" /> | ||||
|         <el-table-column label="管理人联系电话" align="center" prop="custodianPhone" /> | ||||
|         <el-table-column label="备注" align="center" prop="remark" /> | ||||
|         <el-table-column label="创建时间" align="center" prop="createTime" width="180" /> | ||||
|         <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | ||||
|           <template #default="scope"> | ||||
|             <el-space wrap> | ||||
|               <el-button link type="primary" icon="View" @click="handleContractorFile(scope.row)">文件</el-button> | ||||
|               <el-button link type="success" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['project:contractor:edit']">修改 </el-button> | ||||
|               <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['project:contractor:remove']"> | ||||
|                 删除 | ||||
|               </el-button> | ||||
|             </el-space> | ||||
|           </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="contractorFormRef" :model="form" :rules="rules" label-width="120px"> | ||||
|         <el-form-item label="公司名称" prop="name"> | ||||
|           <el-input v-model="form.name" placeholder="请输入公司名称" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="负责人" prop="principal"> | ||||
|           <el-input v-model="form.principal" placeholder="请输入负责人" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="负责人联系电话" prop="principalPhone"> | ||||
|           <el-input v-model="form.principalPhone" placeholder="请输入负责人联系电话" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="管理人" prop="custodian"> | ||||
|           <el-input v-model="form.custodian" placeholder="请输入管理人" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="管理人联系电话" prop="custodianPhone"> | ||||
|           <el-input v-model="form.custodianPhone" placeholder="请输入管理人联系电话" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="备注" prop="remark"> | ||||
|           <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" /> | ||||
|         </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> | ||||
|     <el-dialog title="上传文件" v-model="visible" width="800px"> | ||||
|       <contractor-file-dialog :contractor-id="currentContractorId" /> | ||||
|     </el-dialog> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup name="Contractor" lang="ts"> | ||||
| import { addContractor, delContractor, getContractor, listContractor, updateContractor } from '@/api/project/contractor'; | ||||
| import { ContractorForm, ContractorQuery, ContractorVO } from '@/api/project/contractor/types'; | ||||
| import ContractorFileDialog from '@/views/project/contractor/component/ContractorFileDialog.vue'; | ||||
| import { useUserStoreHook } from '@/store/modules/user'; | ||||
|  | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
|  | ||||
| const contractorList = ref<ContractorVO[]>([]); | ||||
| 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 contractorFormRef = ref<ElFormInstance>(); | ||||
| // 获取用户 store | ||||
| const userStore = useUserStoreHook(); | ||||
| // 从 store 中获取项目列表和当前选中的项目 | ||||
| const currentProject = computed(() => userStore.selectedProject); | ||||
| const dialog = reactive<DialogOption>({ | ||||
|   visible: false, | ||||
|   title: '' | ||||
| }); | ||||
|  | ||||
| const initFormData: ContractorForm = { | ||||
|   id: undefined, | ||||
|   name: undefined, | ||||
|   principal: undefined, | ||||
|   principalPhone: undefined, | ||||
|   custodian: undefined, | ||||
|   custodianPhone: undefined, | ||||
|   fileMap: undefined, | ||||
|   remark: undefined, | ||||
|   projectId: currentProject.value.id | ||||
| }; | ||||
| const data = reactive<PageData<ContractorForm, ContractorQuery>>({ | ||||
|   form: { ...initFormData }, | ||||
|   queryParams: { | ||||
|     pageNum: 1, | ||||
|     pageSize: 10, | ||||
|     orderByColumn: 'createTime', | ||||
|     isAsc: 'desc', | ||||
|     name: undefined, | ||||
|     principal: undefined, | ||||
|     principalPhone: undefined, | ||||
|     custodian: undefined, | ||||
|     custodianPhone: undefined, | ||||
|     projectId: currentProject.value.id, | ||||
|     params: {} | ||||
|   }, | ||||
|   rules: { | ||||
|     id: [{ required: true, message: '主键id不能为空', trigger: 'blur' }] | ||||
|   } | ||||
| }); | ||||
|  | ||||
| const { queryParams, form, rules } = toRefs(data); | ||||
|  | ||||
| /** 查询分包单位列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await listContractor(queryParams.value); | ||||
|   contractorList.value = res.rows; | ||||
|   total.value = res.total; | ||||
|   loading.value = false; | ||||
| }; | ||||
|  | ||||
| /** 取消按钮 */ | ||||
| const cancel = () => { | ||||
|   reset(); | ||||
|   dialog.visible = false; | ||||
| }; | ||||
|  | ||||
| /** 表单重置 */ | ||||
| const reset = () => { | ||||
|   form.value = { ...initFormData }; | ||||
|   contractorFormRef.value?.resetFields(); | ||||
| }; | ||||
|  | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.value.pageNum = 1; | ||||
|   getList(); | ||||
| }; | ||||
|  | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   handleQuery(); | ||||
| }; | ||||
|  | ||||
| /** 多选框选中数据 */ | ||||
| const handleSelectionChange = (selection: ContractorVO[]) => { | ||||
|   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?: ContractorVO) => { | ||||
|   reset(); | ||||
|   const _id = row?.id || ids.value[0]; | ||||
|   const res = await getContractor(_id); | ||||
|   Object.assign(form.value, res.data); | ||||
|   dialog.visible = true; | ||||
|   dialog.title = '修改分包单位'; | ||||
| }; | ||||
|  | ||||
| /** 提交按钮 */ | ||||
| const submitForm = () => { | ||||
|   contractorFormRef.value?.validate(async (valid: boolean) => { | ||||
|     if (valid) { | ||||
|       form.value.projectId = currentProject.value.id; | ||||
|       buttonLoading.value = true; | ||||
|       if (form.value.id) { | ||||
|         await updateContractor(form.value).finally(() => (buttonLoading.value = false)); | ||||
|       } else { | ||||
|         await addContractor(form.value).finally(() => (buttonLoading.value = false)); | ||||
|       } | ||||
|       proxy?.$modal.msgSuccess('操作成功'); | ||||
|       dialog.visible = false; | ||||
|       await getList(); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| /** 删除按钮操作 */ | ||||
| const handleDelete = async (row?: ContractorVO) => { | ||||
|   const _ids = row?.id || ids.value; | ||||
|   await proxy?.$modal.confirm('是否确认删除分包单位编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false)); | ||||
|   await delContractor(_ids); | ||||
|   proxy?.$modal.msgSuccess('删除成功'); | ||||
|   await getList(); | ||||
| }; | ||||
|  | ||||
| /** 导出按钮操作 */ | ||||
| const handleExport = () => { | ||||
|   proxy?.download( | ||||
|     'project/contractor/export', | ||||
|     { | ||||
|       ...queryParams.value | ||||
|     }, | ||||
|     `contractor_${new Date().getTime()}.xlsx` | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| /** 文件操作 **/ | ||||
| const visible = ref(); | ||||
| const currentContractorId = ref<number | string>(0); | ||||
| const handleContractorFile = (row?: ContractorVO) => { | ||||
|   currentContractorId.value = row.id ?? 0; | ||||
|   console.log(currentContractorId.value); | ||||
|   visible.value = true; | ||||
| }; | ||||
| //监听项目id刷新数据 | ||||
| const listeningProject = watch( | ||||
|   () => currentProject.value.id, | ||||
|   (nid, oid) => { | ||||
|     queryParams.value.projectId = nid; | ||||
|     form.value.projectId = nid; | ||||
|     console.log('监听项目id', queryParams.value.projectId, form.value.projectId); | ||||
|     getList(); | ||||
|   } | ||||
| ); | ||||
|  | ||||
| onUnmounted(() => { | ||||
|   listeningProject(); | ||||
| }); | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
| }); | ||||
| </script> | ||||
							
								
								
									
										440
									
								
								src/views/project/leave/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										440
									
								
								src/views/project/leave/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,440 @@ | ||||
| <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" label-width="100px"> | ||||
|             <el-form-item label="申请人" prop="userName"> | ||||
|               <el-input v-model="queryParams.userName" placeholder="请输入申请人" clearable @keyup.enter="handleQuery" /> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="所属班组" prop="leaveType"> | ||||
|               <el-select v-model="queryParams.teamId" placeholder="全部" clearable> | ||||
|                 <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"> | ||||
|               <el-select v-model="queryParams.leaveType" placeholder="请选择请假类型" clearable> | ||||
|                 <el-option v-for="dict in user_leave_type" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </el-form-item> --> | ||||
|             <el-form-item label="班组长意见" prop="gangerOpinion"> | ||||
|               <el-select v-model="queryParams.gangerOpinion" placeholder="全部" clearable> | ||||
|                 <el-option v-for="dict in user_opinion_type" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="管理员意见" prop="managerOpinion"> | ||||
|               <el-select v-model="queryParams.managerOpinion" placeholder="全部" clearable> | ||||
|                 <el-option v-for="dict in user_opinion_type" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </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:leave:add']">新增</el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['project:leave:edit']" | ||||
|               >修改</el-button | ||||
|             > | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['project:leave:remove']" | ||||
|               >删除</el-button | ||||
|             > | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['project:leave:export']">导出</el-button> | ||||
|           </el-col> | ||||
|           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> | ||||
|         </el-row> | ||||
|       </template> --> | ||||
|  | ||||
|       <el-table v-loading="loading" :data="leaveList"> | ||||
|         <el-table-column type="selection" width="55" align="center" /> | ||||
|         <el-table-column label="序号" align="center" type="index" width="50" /> | ||||
|         <el-table-column label="申请人" align="center" prop="userName" /> | ||||
|         <el-table-column label="申请请假说明" align="center" prop="userExplain" /> | ||||
|         <!-- <el-table-column label="请假申请时间" align="center" prop="userTime" width="180"> | ||||
|           <template #default="scope"> | ||||
|             <span>{{ parseTime(scope.row.userTime, '{y}-{m}-{d}') }}</span> | ||||
|           </template> | ||||
|         </el-table-column> --> | ||||
|         <el-table-column label="所属班组" align="center" prop="teamName"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="user_leave_type" :value="scope.row.teamName" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="状态" align="center" prop="status"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="user_review_status_type" :value="scope.row.status" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <!-- <el-table-column label="班组长意见" align="center" prop="gangerOpinion"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="user_opinion_type" :value="scope.row.gangerOpinion" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="班组长说明" align="center" prop="gangerExplain" /> | ||||
|         <el-table-column label="班组长操作时间" align="center" prop="gangerTime" width="180"> | ||||
|           <template #default="scope"> | ||||
|             <span>{{ parseTime(scope.row.gangerTime, '{y}-{m}-{d}') }}</span> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="管理员意见" align="center" prop="managerOpinion"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="user_opinion_type" :value="scope.row.managerOpinion" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="管理员说明" align="center" prop="managerExplain" /> | ||||
|         <el-table-column label="请假申请时间" align="center" prop="userTime" width="180"> | ||||
|           <template #default="scope"> | ||||
|             <span>{{ parseTime(scope.row.userTime) }}</span> | ||||
|           </template> | ||||
|         </el-table-column> --> | ||||
|         <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | ||||
|           <template #default="scope"> | ||||
|             <el-button link type="success" icon="View" @click="handleDetail(scope.row)">详情</el-button> | ||||
|           </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> | ||||
|       <div class="pl-xl"> | ||||
|         <el-form-item label="是否同意"> | ||||
|           <el-radio-group v-model="auditForm.managerOpinion"> | ||||
|             <el-radio value="2" size="small">同意</el-radio> | ||||
|             <el-radio value="3" size="small">拒绝</el-radio> | ||||
|           </el-radio-group> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="拒绝说明" v-if="auditForm.managerOpinion == '3'"> | ||||
|           <el-input v-model="auditForm.managerExplain" placeholder="请输入拒绝说明" type="textarea" clearable></el-input> | ||||
|         </el-form-item> | ||||
|       </div> | ||||
|       <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> | ||||
|     <!-- 详情对话框 --> | ||||
|     <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> | ||||
|  | ||||
| <script setup name="Leave" lang="ts"> | ||||
| import { listLeave, getLeave, delLeave, addLeave, updateLeave, AuditReissueCard } from '@/api/project/leave'; | ||||
| import { LeaveVO, LeaveQuery, LeaveForm } from '@/api/project/leave/types'; | ||||
| import { listProjectTeam } from '@/api/project/projectTeam'; | ||||
| import { ProjectTeamVO } from '@/api/project/projectTeam/types'; | ||||
| import { AuditReissueCardForm, ReissueCardVO } from '@/api/project/reissueCard/types'; | ||||
| import { useUserStoreHook } from '@/store/modules/user'; | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| const { user_leave_type, user_opinion_type, user_review_status_type } = toRefs<any>( | ||||
|   proxy?.useDict('user_leave_type', 'user_opinion_type', 'user_review_status_type') | ||||
| ); | ||||
| const detailObj = ref<ReissueCardVO>({ | ||||
|   userName: undefined, | ||||
|   id: undefined, | ||||
|   userExplain: undefined, | ||||
|   userTime: undefined, | ||||
|   gangerName: undefined, | ||||
|   gangerOpinion: undefined, | ||||
|   gangerExplain: undefined, | ||||
|   gangerTime: undefined, | ||||
|   managerOpinion: undefined, | ||||
|   managerExplain: undefined, | ||||
|   managerTime: undefined, | ||||
|   remark: undefined, | ||||
|   status: undefined, | ||||
|   managerName: undefined | ||||
| }); | ||||
|  | ||||
| const leaveList = ref<LeaveVO[]>([]); | ||||
| 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 leaveFormRef = ref<ElFormInstance>(); | ||||
| const dialog = reactive<DialogOption>({ | ||||
|   visible: false, | ||||
|   title: '', | ||||
|   details: false | ||||
| }); | ||||
| // 获取用户 store | ||||
| const userStore = useUserStoreHook(); | ||||
| // 从 store 中获取项目列表和当前选中的项目 | ||||
| const currentProject = computed(() => userStore.selectedProject); | ||||
| const ProjectTeam = computed(() => userStore.ProjectTeamList); | ||||
| const initFormData: LeaveForm = { | ||||
|   id: undefined, | ||||
|   userId: undefined, | ||||
|   userName: undefined, | ||||
|   userExplain: undefined, | ||||
|   userTime: undefined, | ||||
|   leaveType: undefined, | ||||
|   startTime: undefined, | ||||
|   endTime: undefined, | ||||
|   gangerId: undefined, | ||||
|   gangerName: undefined, | ||||
|   gangerOpinion: undefined, | ||||
|   gangerExplain: undefined, | ||||
|   gangerTime: undefined, | ||||
|   managerOpinion: undefined, | ||||
|   managerExplain: undefined, | ||||
|   managerTime: undefined, | ||||
|   projectId: undefined, | ||||
|   teamId: undefined, | ||||
|   remark: undefined | ||||
| }; | ||||
| const data = reactive<PageData<LeaveForm, LeaveQuery>>({ | ||||
|   form: { ...initFormData }, | ||||
|   queryParams: { | ||||
|     pageNum: 1, | ||||
|     pageSize: 10, | ||||
|     userName: undefined, | ||||
|     leaveType: undefined, | ||||
|     gangerOpinion: undefined, | ||||
|     managerOpinion: undefined, | ||||
|     projectId: undefined, | ||||
|     teamId: undefined, | ||||
|     params: {} | ||||
|   }, | ||||
|   rules: { | ||||
|     id: [{ required: true, message: '主键id不能为空', trigger: 'blur' }], | ||||
|     userId: [{ required: true, message: '申请人id不能为空', trigger: 'blur' }], | ||||
|     userName: [{ required: true, message: '申请人名字不能为空', trigger: 'blur' }], | ||||
|     leaveType: [{ required: true, message: '请假类型不能为空', trigger: 'change' }], | ||||
|     startTime: [{ required: true, message: '请假开始时间不能为空', trigger: 'blur' }], | ||||
|     endTime: [{ required: true, message: '请假结束时间不能为空', trigger: 'blur' }], | ||||
|     gangerOpinion: [{ required: true, message: '班组长意见不能为空', trigger: 'change' }], | ||||
|     managerOpinion: [{ required: true, message: '管理员意见不能为空', trigger: 'change' }], | ||||
|     projectId: [{ required: true, message: '项目id不能为空', trigger: 'blur' }] | ||||
|   } | ||||
| }); | ||||
| const auditForm = reactive<AuditReissueCardForm>({ | ||||
|   id: undefined, | ||||
|   managerOpinion: '2', | ||||
|   managerExplain: undefined | ||||
| }); | ||||
| const { queryParams, form, rules } = toRefs(data); | ||||
|  | ||||
| //审核进度 | ||||
| const auditProgress = computed(() => { | ||||
|   if (auditStatus.value) return 3; | ||||
|   if (managerStatus.value) return 2; | ||||
|   if (teamStatus.value) return 1; | ||||
|   return 0; | ||||
| }); | ||||
| //审核状态 | ||||
| const auditStatus = computed(() => { | ||||
|   if (detailObj.value.status == '3' || detailObj.value.status == '4') return true; | ||||
|   return false; | ||||
| }); | ||||
| //管理员审核状态 | ||||
| const managerStatus = computed(() => { | ||||
|   switch (detailObj.value.managerOpinion) { | ||||
|     case '1': | ||||
|       return false; | ||||
|     case '2': | ||||
|       return 'success'; | ||||
|     case '3': | ||||
|       return 'error'; | ||||
|   } | ||||
| }); | ||||
| //班组审核状态 | ||||
| const teamStatus = computed(() => { | ||||
|   switch (detailObj.value.gangerOpinion) { | ||||
|     case '1': | ||||
|       return false; | ||||
|     case '2': | ||||
|       return 'success'; | ||||
|     case '3': | ||||
|       return 'error'; | ||||
|   } | ||||
| }); | ||||
| //结果状态 | ||||
| const resultsStatus = computed(() => { | ||||
|   if (detailObj.value.status == '3') return 'success'; | ||||
|   if (detailObj.value.status == '4') return 'error'; | ||||
|   return ''; | ||||
| }); | ||||
|  | ||||
| /** 查询施工人员请假申请列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await listLeave(queryParams.value); | ||||
|   leaveList.value = res.rows; | ||||
|   total.value = res.total; | ||||
|   loading.value = false; | ||||
| }; | ||||
|  | ||||
| /** 取消按钮 */ | ||||
| const cancel = () => { | ||||
|   reset(); | ||||
|   dialog.visible = false; | ||||
| }; | ||||
|  | ||||
| /** 表单重置 */ | ||||
| const reset = () => {}; | ||||
|  | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.value.pageNum = 1; | ||||
|   getList(); | ||||
| }; | ||||
|  | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   handleQuery(); | ||||
| }; | ||||
|  | ||||
| /** 提交按钮 */ | ||||
| const submitForm = async () => { | ||||
|   await AuditReissueCard(auditForm); | ||||
|   proxy?.$modal.msgSuccess('审批成功'); | ||||
|   dialog.visible = false; | ||||
|  | ||||
|   await getList(); | ||||
| }; | ||||
|  | ||||
| const handleDetail = (row: ReissueCardVO) => { | ||||
|   detailObj.value = row; | ||||
|   console.log('🚀 ~ handleDetail ~ row:', row); | ||||
|  | ||||
|   dialog.details = true; | ||||
| }; | ||||
|  | ||||
| /** 审批按钮操作 */ | ||||
| const handleUpdate = async (row?: LeaveVO) => { | ||||
|   reset(); | ||||
|   const _id = row?.id || ids.value[0]; | ||||
|   auditForm.id = _id; | ||||
|   dialog.visible = true; | ||||
|   dialog.title = '修改施工人员补卡申请'; | ||||
| }; | ||||
|  | ||||
| /** 导出按钮操作 */ | ||||
| const handleExport = () => { | ||||
|   proxy?.download( | ||||
|     'project/leave/export', | ||||
|     { | ||||
|       ...queryParams.value | ||||
|     }, | ||||
|     `leave_${new Date().getTime()}.xlsx` | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| //监听项目id刷新数据 | ||||
| const listeningProject = watch( | ||||
|   () => currentProject.value.id, | ||||
|   (nid, oid) => { | ||||
|     queryParams.value.projectId = nid; | ||||
|     form.value.projectId = nid; | ||||
|     getList(); | ||||
|   } | ||||
| ); | ||||
|  | ||||
| onUnmounted(() => { | ||||
|   listeningProject(); | ||||
| }); | ||||
|  | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
| }); | ||||
| </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> | ||||
							
								
								
									
										584
									
								
								src/views/project/project/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										584
									
								
								src/views/project/project/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,584 @@ | ||||
| <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="项目名称" prop="projectName"> | ||||
|               <el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter="handleQuery" /> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="项目简称" prop="shortName"> | ||||
|               <el-input v-model="queryParams.shortName" 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:project:add']">新增 </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['project:project:edit']" | ||||
|               >修改 | ||||
|             </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['project:project:remove']" | ||||
|               >删除 | ||||
|             </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['project:project:export']">导出 </el-button> | ||||
|           </el-col> | ||||
|           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> | ||||
|         </el-row> | ||||
|       </template> | ||||
|  | ||||
|       <el-table v-loading="loading" :data="projectList" @selection-change="handleSelectionChange"> | ||||
|         <el-table-column type="selection" width="55" align="center" /> | ||||
|         <el-table-column label="序号" type="index" width="60" align="center" /> | ||||
|         <el-table-column label="项目名称" align="center" prop="projectName" /> | ||||
|         <el-table-column label="项目简称" align="center" prop="shortName" /> | ||||
|         <el-table-column label="状态" align="center" prop="status"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="sys_normal_disable" :value="scope.row.status" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="项目类型" align="center" prop="projectType" width="120"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="project_type" :value="scope.row.projectType" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="项目类别" align="center" prop="projectCategory"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="project_category_type" :value="scope.row.projectCategory" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="负责人" align="center" prop="principal" /> | ||||
|         <el-table-column label="负责人电话" align="center" prop="principalPhone" width="120" /> | ||||
|         <el-table-column label="实际容量(M)" align="center" prop="actual" width="100" /> | ||||
|         <el-table-column label="计划容量(M)" align="center" prop="plan" width="100" /> | ||||
|         <el-table-column label="开工时间" align="center" prop="onStreamTime" width="120" /> | ||||
|         <el-table-column label="打卡范围" align="center" prop="punchRange" /> | ||||
|         <el-table-column label="设计总量" align="center" prop="designTotal" /> | ||||
|         <el-table-column label="是否上传DXF" align="center" prop="designId" width="140"> | ||||
|           <template #default="scope"> | ||||
|             <el-link | ||||
|               :type="scope.row.designId ? 'primary' : 'default'" | ||||
|               :disabled="!scope.row.designId" | ||||
|               @click="handleOpenLayer(scope.row)" | ||||
|               v-loading.fullscreen.lock="fullscreenLoading" | ||||
|               >{{ scope.row.designId ? '已上传' : '未上传' }}</el-link | ||||
|             > | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="备注" align="center" prop="remark" /> | ||||
|         <el-table-column label="创建时间" align="center" prop="createTime" width="180" /> | ||||
|         <el-table-column fixed="right" label="操作" align="center" class-name="small-padding fixed-width" width="400"> | ||||
|           <template #default="scope"> | ||||
|             <el-space> | ||||
|               <el-button link type="primary" icon="FolderOpened" @click="handleShowUpload(scope.row)">导入安全协议书 </el-button> | ||||
|               <file-upload | ||||
|                 :limit="1" | ||||
|                 :fileSize="200" | ||||
|                 :fileType="['dxf']" | ||||
|                 v-model:model-value="dxfFile" | ||||
|                 uploadUrl="/project/projectFile/upload/dxf" | ||||
|                 :data="{ projectId: scope.row.id }" | ||||
|               > | ||||
|                 <el-button link type="primary" icon="upload">上传DXF </el-button> | ||||
|               </file-upload> | ||||
|  | ||||
|               <el-button link type="success" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['project:project:edit']">修改 </el-button> | ||||
|               <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['project:project:remove']">删除 </el-button> | ||||
|             </el-space> | ||||
|           </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="770px" append-to-body> | ||||
|       <el-form ref="projectFormRef" :model="form" :rules="rules" label-width="100px"> | ||||
|         <div class="block-box"> | ||||
|           <div class="">基础信息</div> | ||||
|           <el-row :gutter="20"> | ||||
|             <el-col :span="12" :offset="0"> | ||||
|               <el-form-item label="项目名称" prop="projectName"> | ||||
|                 <el-input v-model="form.projectName" placeholder="请输入项目名称" /> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="12" :offset="0"> | ||||
|               <el-form-item label="项目简称" prop="shortName"> | ||||
|                 <el-input v-model="form.shortName" placeholder="请输入项目简称" /> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="12" :offset="0"> | ||||
|               <el-form-item label="负责人" prop="principal"> | ||||
|                 <el-input v-model="form.principal" placeholder="请输入负责人" /> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="12" :offset="0"> | ||||
|               <el-form-item label="负责人电话" prop="principalPhone"> | ||||
|                 <el-input v-model="form.principalPhone" placeholder="请输入负责人电话" type="number" /> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="12" :offset="0"> | ||||
|               <el-form-item label="项目类型" prop="projectType" label-width="100px"> | ||||
|                 <el-select v-model="form.projectType" placeholder="请选择项目类型" clearable> | ||||
|                   <el-option v-for="dict in project_type" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|                 </el-select> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="12" :offset="0"> | ||||
|               <el-form-item label="项目类别" prop="projectCategory" label-width="100px"> | ||||
|                 <el-select v-model="form.projectCategory" placeholder="请选择项目类别" clearable> | ||||
|                   <el-option v-for="dict in project_category_type" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|                 </el-select> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="12" :offset="0"> | ||||
|               <el-form-item label="开工时间" prop="onStreamTime"> | ||||
|                 <el-date-picker clearable v-model="form.onStreamTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择开工时间" /> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="12" :push="3"> | ||||
|               <el-button type="primary" size="default" @click="amapStatus = true">获取经纬度</el-button> | ||||
|             </el-col> | ||||
|             <el-col :span="12" :offset="0"> | ||||
|               <el-form-item label="经度" prop="lng"> | ||||
|                 <el-input v-model="form.lng" disabled placeholder="请输入经度" /> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="12" :offset="0"> | ||||
|               <el-form-item label="纬度" prop="lat"> | ||||
|                 <el-input v-model="form.lat" disabled placeholder="请输入纬度" /> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="24" :offset="0"> | ||||
|               <el-form-item label="项目地址" prop="projectSite"> | ||||
|                 <el-input v-model="form.projectSite" disabled placeholder="请输入项目地址" /> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="12" :offset="0"> | ||||
|               <el-form-item label="计划容量(M)" prop="plan"> | ||||
|                 <el-input v-model="form.plan" placeholder="请输入计划容量" /> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="12" :offset="0"> | ||||
|               <el-form-item label="实际容量(M)" prop="actual"> | ||||
|                 <el-input v-model="form.actual" placeholder="请输入实际容量" /> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="12" :offset="0"> | ||||
|               <el-form-item label="设计总量(M)" prop="designTotal"> | ||||
|                 <el-input v-model="form.designTotal" placeholder="请输入设计总量" /> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="24" :offset="0"> | ||||
|               <el-form-item label="备注" prop="remark"> | ||||
|                 <el-input v-model="form.remark" placeholder="请输入内容" /> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="12" :offset="0"> | ||||
|               <el-form-item label="项目排序" prop="remark"> | ||||
|                 <el-input-number v-model="form.sort" :min="0" :max="10000" /> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|           </el-row> | ||||
|         </div> | ||||
|         <div class="block-box"> | ||||
|           <div class="">打卡设置</div> | ||||
|           <el-row :gutter="20"> | ||||
|             <el-col :span="12" :offset="0"> | ||||
|               <el-form-item label="打卡开始时间" prop="playCardStart" label-width="110px"> | ||||
|                 <!-- <el-time-picker value-format="HH:mm" v-model="form.playCardStart" placeholder="请输入打卡开始时间" /> --> | ||||
|                 <el-time-select | ||||
|                   v-model="form.playCardStart" | ||||
|                   style="width: 100%" | ||||
|                   class="mr-4" | ||||
|                   placeholder="请输入打卡开始时间" | ||||
|                   value-format="HH:mm" | ||||
|                   start="00:00" | ||||
|                   step="00:15" | ||||
|                   end="23:59" | ||||
|                 /> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="12" :offset="0"> | ||||
|               <el-form-item label="打卡结束时间" prop="playCardEnd" label-width="110px"> | ||||
|                 <!-- <el-time-picker value-format="HH:mm" v-model="form.playCardEnd" placeholder="请输入打卡结束时间" /> --> | ||||
|                 <el-time-select | ||||
|                   v-model="form.playCardEnd" | ||||
|                   style="width: 100%" | ||||
|                   :min-time="form.playCardStart" | ||||
|                   class="mr-4" | ||||
|                   placeholder="请输入打卡结束时间" | ||||
|                   value-format="HH:mm" | ||||
|                   start="00:00" | ||||
|                   step="00:15" | ||||
|                   end="23:59" | ||||
|                 /> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="24" :offset="0"> | ||||
|               <el-form-item label="安全协议书" prop="securityAgreement"> | ||||
|                 <file-upload v-model="form.securityAgreement" :limit="1" :file-type="['pdf']" :file-size="50" /> | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|           </el-row> | ||||
|         </div> | ||||
|       </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> | ||||
|     <el-dialog v-model="uploadVisible" title="上传安全协议书" width="400"> | ||||
|       <file-upload v-model="fileUploadParam.securityAgreement" :limit="1" :file-type="['pdf']" :file-size="50" /> | ||||
|       <template #footer> | ||||
|         <div class="dialog-footer"> | ||||
|           <el-button v-loading="buttonLoading" type="primary" @click="updateProjectFile"> 提交</el-button> | ||||
|           <el-button @click="uploadVisible = false">取消</el-button> | ||||
|         </div> | ||||
|       </template> | ||||
|     </el-dialog> | ||||
|  | ||||
|     <!-- //选取项目地址弹窗 --> | ||||
|     <el-dialog v-model="amapStatus" :title="form.projectName + '-获取经纬度'" width="80%"> | ||||
|       <amap height="620px" @setLocation="setPoi"></amap> | ||||
|     </el-dialog> | ||||
|     <!-- 选取方阵地址 --> | ||||
|     <el-dialog title="设置方阵" v-model="polygonStatus" width="1400px" :close-on-click-modal="false"> | ||||
|       <open-layers-map | ||||
|         :project-id="projectId" | ||||
|         :design-id="designId" | ||||
|         @handleCheckChange="setCheckedNodes" | ||||
|         @close="polygonStatus = false" | ||||
|       ></open-layers-map> | ||||
|     </el-dialog> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup name="Project" lang="ts"> | ||||
| import { | ||||
|   addProject, | ||||
|   addProjectFacilities, | ||||
|   addProjectPilePoint, | ||||
|   addProjectSquare, | ||||
|   delProject, | ||||
|   getProject, | ||||
|   listProject, | ||||
|   updateProject | ||||
| } from '@/api/project/project'; | ||||
| import { ProjectForm, ProjectQuery, ProjectVO, locationType } from '@/api/project/project/types'; | ||||
| import amap from '@/components/amap/index.vue'; | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| const { sys_normal_disable, project_category_type, project_type } = toRefs<any>( | ||||
|   proxy?.useDict('sys_normal_disable', 'project_category_type', 'project_type') | ||||
| ); | ||||
| const projectList = ref<ProjectVO[]>([]); | ||||
| 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 amapStatus = ref(false); | ||||
| const queryFormRef = ref<ElFormInstance>(); | ||||
| const projectFormRef = ref<ElFormInstance>(); | ||||
| const polygonStatus = ref(false); | ||||
| const dxfFile = ref(null); | ||||
| const projectId = ref<string>(''); | ||||
| const designId = ref<string>(''); | ||||
| //被选中的节点 | ||||
| const nodes = ref<any>([]); | ||||
| const dialog = reactive<DialogOption>({ | ||||
|   visible: false, | ||||
|   title: '' | ||||
| }); | ||||
| const jsonData = ref(null); | ||||
| const fullscreenLoading = ref(false); | ||||
|  | ||||
| const initFormData: ProjectForm = { | ||||
|   id: undefined, | ||||
|   projectName: undefined, | ||||
|   shortName: undefined, | ||||
|   pId: undefined, | ||||
|   status: undefined, | ||||
|   picUrl: undefined, | ||||
|   remark: undefined, | ||||
|   projectType: undefined, | ||||
|   projectCategory: undefined, | ||||
|   deletedAt: undefined, | ||||
|   projectSite: undefined, | ||||
|   principal: undefined, | ||||
|   principalPhone: undefined, | ||||
|   actual: undefined, | ||||
|   lng: undefined, | ||||
|   lat: undefined, | ||||
|   plan: undefined, | ||||
|   onStreamTime: undefined, | ||||
|   playCardStart: undefined, | ||||
|   playCardEnd: undefined, | ||||
|   designTotal: undefined, | ||||
|   securityAgreement: undefined, | ||||
|   sort: 0, | ||||
|   showHidden: undefined, | ||||
|   isDelete: undefined | ||||
| }; | ||||
| const data = reactive<PageData<ProjectForm, ProjectQuery>>({ | ||||
|   form: { ...initFormData }, | ||||
|   queryParams: { | ||||
|     pageNum: 1, | ||||
|     pageSize: 10, | ||||
|     projectName: undefined, | ||||
|     shortName: undefined, | ||||
|     pId: undefined, | ||||
|     status: undefined, | ||||
|     picUrl: undefined, | ||||
|     projectType: undefined, | ||||
|     projectCategory: undefined, | ||||
|     deletedAt: undefined, | ||||
|     projectSite: undefined, | ||||
|     principal: undefined, | ||||
|     principalPhone: undefined, | ||||
|     actual: undefined, | ||||
|     lng: undefined, | ||||
|     lat: undefined, | ||||
|     plan: undefined, | ||||
|     onStreamTime: undefined, | ||||
|     playCardStart: undefined, | ||||
|     playCardEnd: undefined, | ||||
|     designTotal: undefined, | ||||
|     securityAgreement: undefined, | ||||
|     sort: undefined, | ||||
|     showHidden: undefined, | ||||
|     isDelete: undefined, | ||||
|     params: {} | ||||
|   }, | ||||
|   rules: { | ||||
|     playCardStart: [{ required: true, message: '打卡开始时间不能为空', trigger: 'blur' }], | ||||
|     playCardEnd: [{ required: true, message: '打卡结束时间不能为空', trigger: 'blur' }], | ||||
|     projectName: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }], | ||||
|     shortName: [{ required: true, message: '项目简称不能为空', trigger: 'blur' }], | ||||
|     principalPhone: [{ required: true, message: '负责人电话不能为空', trigger: 'blur' }], | ||||
|     principal: [{ required: true, message: '负责人不能为空', trigger: 'blur' }], | ||||
|     projectType: [{ required: true, message: '项目类型不能为空', trigger: 'blur' }], | ||||
|     projectCategory: [{ required: true, message: '项目类别不能为空', trigger: 'blur' }], | ||||
|     projectSite: [{ required: true, message: '项目地址不能为空', trigger: 'blur' }], | ||||
|     actual: [{ required: true, message: '实际容量不能为空', trigger: 'blur' }], | ||||
|     lng: [{ required: true, message: '经度不能为空', trigger: 'blur' }], | ||||
|     lat: [{ required: true, message: '纬度不能为空', trigger: 'blur' }], | ||||
|     plan: [{ required: true, message: '计划容量不能为空', trigger: 'blur' }] | ||||
|   } | ||||
| }); | ||||
|  | ||||
| const { queryParams, form, rules } = toRefs(data); | ||||
|  | ||||
| /** 查询项目列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await listProject(queryParams.value); | ||||
|   projectList.value = res.rows; | ||||
|   total.value = res.total; | ||||
|   loading.value = false; | ||||
| }; | ||||
| /** 取消按钮 */ | ||||
| const cancel = () => { | ||||
|   reset(); | ||||
|   dialog.visible = false; | ||||
| }; | ||||
| /** 表单重置 */ | ||||
| const reset = () => { | ||||
|   form.value = { ...initFormData }; | ||||
|   projectFormRef.value?.resetFields(); | ||||
| }; | ||||
|  | ||||
| // 设置位置信息 | ||||
| const setPoi = (location: locationType) => { | ||||
|   if (location) { | ||||
|     console.log('🚀 ~ setPoi ~ poi:', location); | ||||
|     form.value.lng = location.lng; | ||||
|     form.value.lat = location.lat; | ||||
|     form.value.projectSite = location.projectSite; | ||||
|   } | ||||
|  | ||||
|   amapStatus.value = false; | ||||
| }; | ||||
| //设置需要上传的节点 | ||||
| const setCheckedNodes = (nodeList: any) => { | ||||
|   nodes.value = nodeList; | ||||
| }; | ||||
| //上传节点 | ||||
| // const addFacilities = async () => { | ||||
| //   if (!layerType.value) { | ||||
| //     return proxy?.$modal.msgError('请选择图层类型'); | ||||
| //   } | ||||
| //   if (!nodes.value.length) { | ||||
| //     return proxy?.$modal.msgError('请选择需要上传的图层'); | ||||
| //   } | ||||
| //   const data = { | ||||
| //     projectId: projectId.value, | ||||
| //     nameGeoJson: null, | ||||
| //     locationGeoJson: null, | ||||
| //     pointGeoJson: null | ||||
| //   }; | ||||
| //   loading.value = true; | ||||
| //   if (layerType.value == 1) { | ||||
| //     if (nodes.value[0].option == '名称') { | ||||
| //       data.nameGeoJson = jsonData.value[nodes.value[0].location.index]; | ||||
| //       data.locationGeoJson = jsonData.value[nodes.value[1].location.index]; | ||||
| //     } else { | ||||
| //       data.nameGeoJson = jsonData.value[nodes.value[1].location.index]; | ||||
| //       data.locationGeoJson = jsonData.value[nodes.value[0].location.index]; | ||||
| //     } | ||||
| //     await addProjectFacilities(data); | ||||
| //     await proxy?.$modal.msgSuccess('添加成功'); | ||||
| //   } else if (layerType.value == 2) { | ||||
| //     data.pointGeoJson = jsonData.value[nodes.value[0].location.index]; | ||||
| //     await addProjectPilePoint(data); | ||||
| //     await proxy?.$modal.msgSuccess('添加成功'); | ||||
| //   } else if (layerType.value == 3) { | ||||
| //     if (nodes.value[0].option == '名称') { | ||||
| //       data.nameGeoJson = jsonData.value[nodes.value[0].location.index]; | ||||
| //       data.locationGeoJson = jsonData.value[nodes.value[1].location.index]; | ||||
| //     } else { | ||||
| //       data.nameGeoJson = jsonData.value[nodes.value[1].location.index]; | ||||
| //       data.locationGeoJson = jsonData.value[nodes.value[0].location.index]; | ||||
| //     } | ||||
| //     await addProjectSquare(data); | ||||
| //     await proxy?.$modal.msgSuccess('添加成功'); | ||||
| //   } | ||||
| //   loading.value = false; | ||||
| // }; | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.value.pageNum = 1; | ||||
|   getList(); | ||||
| }; | ||||
|  | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   handleQuery(); | ||||
| }; | ||||
|  | ||||
| /** 多选框选中数据 */ | ||||
| const handleSelectionChange = (selection: ProjectVO[]) => { | ||||
|   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?: ProjectVO) => { | ||||
|   reset(); | ||||
|   const _id = row?.id || ids.value[0]; | ||||
|   const res = await getProject(_id); | ||||
|   Object.assign(form.value, res.data); | ||||
|   dialog.visible = true; | ||||
|   dialog.title = '修改-' + form.value.projectName; | ||||
| }; | ||||
|  | ||||
| /** 上传安全协议书按钮操作 */ | ||||
| const uploadVisible = ref<boolean>(false); | ||||
| const fileUploadParam = ref<{ id: string | number; securityAgreement: string }>({ | ||||
|   id: undefined, | ||||
|   securityAgreement: undefined | ||||
| }); | ||||
| const handleShowUpload = (row?: ProjectVO) => { | ||||
|   fileUploadParam.value.securityAgreement = row.securityAgreement; | ||||
|   fileUploadParam.value.id = row.id; | ||||
|   uploadVisible.value = true; | ||||
| }; | ||||
|  | ||||
| const handleOpenLayer = async (row: ProjectVO) => { | ||||
|   polygonStatus.value = true; | ||||
|   projectId.value = row.id; | ||||
|   designId.value = row.designId; | ||||
| }; | ||||
|  | ||||
| const updateProjectFile = async () => { | ||||
|   buttonLoading.value = true; | ||||
|   await updateProject(fileUploadParam.value); | ||||
|   proxy?.$modal.msgSuccess('上传成功'); | ||||
|   buttonLoading.value = false; | ||||
|   uploadVisible.value = false; | ||||
|   await getList(); | ||||
| }; | ||||
|  | ||||
| /** 提交按钮 */ | ||||
| const submitForm = () => { | ||||
|   projectFormRef.value?.validate(async (valid: boolean) => { | ||||
|     if (valid) { | ||||
|       buttonLoading.value = true; | ||||
|       if (form.value.id) { | ||||
|         await updateProject(form.value).finally(() => (buttonLoading.value = false)); | ||||
|       } else { | ||||
|         await addProject(form.value).finally(() => (buttonLoading.value = false)); | ||||
|       } | ||||
|       proxy?.$modal.msgSuccess('操作成功'); | ||||
|       dialog.visible = false; | ||||
|       await getList(); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| /** 删除按钮操作 */ | ||||
| const handleDelete = async (row?: ProjectVO) => { | ||||
|   const _ids = row?.id || ids.value; | ||||
|   await proxy?.$modal.confirm('是否确认删除项目编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false)); | ||||
|   await delProject(_ids); | ||||
|   proxy?.$modal.msgSuccess('删除成功'); | ||||
|   await getList(); | ||||
| }; | ||||
|  | ||||
| /** 导出按钮操作 */ | ||||
| const handleExport = () => { | ||||
|   proxy?.download( | ||||
|     'project/project/export', | ||||
|     { | ||||
|       ...queryParams.value | ||||
|     }, | ||||
|     `project_${new Date().getTime()}.xlsx` | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
| }); | ||||
| </script> | ||||
| <style scoped> | ||||
| .block-box { | ||||
|   border: 1px solid #9eccfa; | ||||
|   border-radius: 6px; | ||||
|   padding: 10px 20px 0 10px; | ||||
|   margin-bottom: 20px; | ||||
| } | ||||
| .block-box > div { | ||||
|   color: #409eff; | ||||
|   font-weight: 700; | ||||
|   font-size: 14px; | ||||
|   margin-bottom: 10px; | ||||
| } | ||||
| </style> | ||||
| @ -0,0 +1,86 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <div> | ||||
|       <el-transfer v-model="selectedProjects" :data="allProjects" filterable :titles="['项目列表', '已关联项目']" @change="handleTransferChange" /> | ||||
|     </div> | ||||
|     <div style="text-align: right; margin-top: 20px"> | ||||
|       <el-button type="primary" @click="$emit('close')">关闭</el-button> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts" setup> | ||||
| import { defineProps, onMounted, ref, watch } from 'vue'; | ||||
| import { listProject } from '@/api/project/project'; | ||||
| import { addNewProjectRelevancy, listUserProjects, removeNewProjectRelevancy } from '@/api/project/projectRelevancy'; | ||||
| import { ProjectRelevancyVO } from '@/api/project/projectRelevancy/types'; | ||||
|  | ||||
| // **从父组件接收 `userId`** | ||||
| const props = defineProps<{ userId: number | string }>(); | ||||
|  | ||||
| // **所有项目列表** | ||||
| const allProjects = ref<{ key: number | string; label: string }[]>([]); | ||||
|  | ||||
| // **已关联的项目 ID 列表** | ||||
| const selectedProjects = ref<(number | string)[]>([]); | ||||
|  | ||||
| // **获取所有项目列表** | ||||
| const getProjectList = async () => { | ||||
|   try { | ||||
|     const res = await listProject(); | ||||
|     allProjects.value = res.rows.map((project) => ({ | ||||
|       key: project.id, | ||||
|       label: project.projectName | ||||
|     })); | ||||
|   } catch (error) { | ||||
|     console.error('获取项目列表失败:', error); | ||||
|   } | ||||
| }; | ||||
| const getUserProjects = async () => { | ||||
|   if (!props.userId) return; | ||||
|   try { | ||||
|     const res = await listUserProjects({ userId: props.userId }); | ||||
|     selectedProjects.value = Array.isArray(res.rows) ? res.rows.map((item: ProjectRelevancyVO) => item.projectId) : []; | ||||
|   } catch (error) { | ||||
|     console.error('获取用户关联的项目失败:', error); | ||||
|     selectedProjects.value = []; // 请求失败时清空列表 | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // **监听 `userId` 变化,重新加载关联数据** | ||||
| watch( | ||||
|   () => props.userId, | ||||
|   async (newUserId) => { | ||||
|     if (newUserId) { | ||||
|       await getUserProjects(); | ||||
|     } | ||||
|   }, | ||||
|   { immediate: true } | ||||
| ); | ||||
|  | ||||
| // **处理穿梭框变更** | ||||
| const handleTransferChange = async (_: number[], direction: 'left' | 'right', movedKeys: number[]) => { | ||||
|   try { | ||||
|     if (direction === 'right') { | ||||
|       // **添加关联** | ||||
|       await addNewProjectRelevancy({ | ||||
|         userId: props.userId, | ||||
|         projectIdList: movedKeys | ||||
|       }); | ||||
|     } else { | ||||
|       // **移除关联** | ||||
|       await removeNewProjectRelevancy({ | ||||
|         userId: props.userId, | ||||
|         projectIdList: movedKeys | ||||
|       }); | ||||
|     } | ||||
|   } catch (error) { | ||||
|     console.error('更新项目关联失败:', error); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // **初始化数据** | ||||
| onMounted(async () => { | ||||
|   await getProjectList(); | ||||
| }); | ||||
| </script> | ||||
							
								
								
									
										318
									
								
								src/views/project/projectRelevancy/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										318
									
								
								src/views/project/projectRelevancy/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,318 @@ | ||||
| <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="项目ID" prop="projectId"> | ||||
|               <el-input v-model="queryParams.projectId" placeholder="请输入项目ID" 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:projectRelevancy:add']">新增 </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['project:projectRelevancy:edit']" | ||||
|               >修改 | ||||
|             </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['project:projectRelevancy:remove']" | ||||
|               >删除 | ||||
|             </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['project:projectRelevancy:export']">导出 </el-button> | ||||
|           </el-col> | ||||
|           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> | ||||
|         </el-row> | ||||
|       </template> | ||||
|  | ||||
|       <el-table v-loading="loading" :data="projectRelevancyList" @selection-change="handleSelectionChange"> | ||||
|         <el-table-column prop="id" label="关联ID" width="180" /> | ||||
|         <el-table-column prop="userId" label="用户ID" width="100" /> | ||||
|         <el-table-column prop="projectId" label="项目ID" width="180" /> | ||||
|         <el-table-column prop="project.projectName" label="项目名称" width="200" /> | ||||
|         <el-table-column prop="project.type" label="项目类型" width="150" /> | ||||
|         <el-table-column prop="project.projectSite" label="项目地址" width="200" /> | ||||
|         <el-table-column prop="project.principal" label="负责人" width="120" /> | ||||
|         <el-table-column prop="project.principalPhone" label="联系电话" width="150" /> | ||||
|         <el-table-column prop="project.onStreamTime" label="上线时间" width="150" /> | ||||
|         <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:projectRelevancy:edit']"></el-button> | ||||
|             </el-tooltip> | ||||
|             <el-tooltip content="删除" placement="top"> | ||||
|               <el-button | ||||
|                 link | ||||
|                 type="primary" | ||||
|                 icon="Delete" | ||||
|                 @click="handleDelete(scope.row)" | ||||
|                 v-hasPermi="['project:projectRelevancy:remove']" | ||||
|               ></el-button> | ||||
|             </el-tooltip> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|       </el-table> | ||||
|       <!-- 添加 ShuttleFrame 弹窗 --> | ||||
|       <el-dialog v-model="shuttleVisible" title="编辑关联项目" width="auto" destroy-on-close> | ||||
|         <shuttle-frame :userId="selectedUserId" @close="shuttleVisible = false" /> | ||||
|       </el-dialog> | ||||
|  | ||||
|       <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="projectRelevancyFormRef" :model="form" :rules="rules" label-width="80px"> | ||||
|         <el-form-item label="用户ID" prop="userId"> | ||||
|           <el-select v-model="form.userId" placeholder="请选择用户"> | ||||
|             <el-option v-for="user in userList" :key="user.userId" :label="user.nickName" :value="user.userId" /> | ||||
|           </el-select> | ||||
|         </el-form-item> | ||||
|  | ||||
|         <el-form-item label="项目ID" prop="projectId"> | ||||
|           <!-- 项目ID下拉选择框 --> | ||||
|           <el-select v-model="form.projectId" placeholder="请选择项目ID" @change="onProjectChange"> | ||||
|             <el-option v-for="project in projectList" :key="project.id" :label="project.projectName" :value="project.id" /> | ||||
|           </el-select> | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
|       <div v-if="selectedProject" style="padding: 0px 100px"> | ||||
|         <p><strong>项目名称:</strong> {{ selectedProject.projectName }}</p> | ||||
|         <p><strong>项目类型:</strong> {{ selectedProject.type }}</p> | ||||
|         <p><strong>项目地址:</strong> {{ selectedProject.projectSite }}</p> | ||||
|         <p><strong>负责人:</strong> {{ selectedProject.principal }}</p> | ||||
|         <p><strong>联系电话:</strong> {{ selectedProject.principalPhone }}</p> | ||||
|       </div> | ||||
|       <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="ProjectRelevancy" lang="ts"> | ||||
| import { addProjectRelevancy, delProjectRelevancy, listProjectRelevancy, updateProjectRelevancy } from '@/api/project/projectRelevancy'; | ||||
| import { listProject } from '@/api/project/project'; | ||||
| import { listUser } from '@/api/system/user'; | ||||
| import { ProjectRelevancyForm, ProjectRelevancyQuery, ProjectRelevancyVO } from '@/api/project/projectRelevancy/types'; | ||||
| import ShuttleFrame from './component/ShuttleFrame.vue'; | ||||
| import { UserVO } from '@/api/system/user/types'; | ||||
| import { ProjectVO } from '@/api/project/project/types'; | ||||
|  | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
|  | ||||
| const projectRelevancyList = ref<ProjectRelevancyVO[]>([]); | ||||
| 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 projectRelevancyFormRef = ref<ElFormInstance>(); | ||||
|  | ||||
| const dialog = reactive<DialogOption>({ | ||||
|   visible: false, | ||||
|   title: '' | ||||
| }); | ||||
|  | ||||
| const initFormData: ProjectRelevancyForm = { | ||||
|   id: undefined, | ||||
|   userId: undefined, | ||||
|   projectId: undefined, | ||||
|   deletedAt: undefined | ||||
| }; | ||||
| const data = reactive<PageData<ProjectRelevancyForm, ProjectRelevancyQuery>>({ | ||||
|   form: { ...initFormData }, | ||||
|   queryParams: { | ||||
|     pageNum: 1, | ||||
|     pageSize: 10, | ||||
|     orderByColumn: 'createTime', | ||||
|     isAsc: 'desc', | ||||
|     userId: undefined, | ||||
|     projectId: undefined, | ||||
|     deletedAt: undefined, | ||||
|     params: {} | ||||
|   }, | ||||
|   rules: { | ||||
|     id: [{ required: true, message: '主键ID不能为空', trigger: 'blur' }], | ||||
|     userId: [{ required: true, message: '用户ID不能为空', trigger: 'blur' }], | ||||
|     projectId: [{ required: true, message: '项目ID不能为空', trigger: 'blur' }] | ||||
|   } | ||||
| }); | ||||
|  | ||||
| const { queryParams, form, rules } = toRefs(data); | ||||
|  | ||||
| /** 查询系统用户与项目关联列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await listProjectRelevancy(queryParams.value); | ||||
|   projectRelevancyList.value = res.rows; | ||||
|   total.value = res.total; | ||||
|   loading.value = false; | ||||
| }; | ||||
|  | ||||
| /** 取消按钮 */ | ||||
| const cancel = () => { | ||||
|   reset(); | ||||
|   dialog.visible = false; | ||||
| }; | ||||
|  | ||||
| /** 表单重置 */ | ||||
| const reset = () => { | ||||
|   form.value = { ...initFormData }; | ||||
|   projectRelevancyFormRef.value?.resetFields(); | ||||
| }; | ||||
|  | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.value.pageNum = 1; | ||||
|   getList(); | ||||
| }; | ||||
|  | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   handleQuery(); | ||||
| }; | ||||
|  | ||||
| /** 多选框选中数据 */ | ||||
| const handleSelectionChange = (selection: ProjectRelevancyVO[]) => { | ||||
|   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 shuttleVisible = ref(false); | ||||
| const selectedUserId = ref<string | number>(); | ||||
|  | ||||
| const handleUpdate = (row?: ProjectRelevancyVO) => { | ||||
|   const currentRow = row || projectRelevancyList.value.find((item) => item.id === ids.value[0]); | ||||
|   if (currentRow) { | ||||
|     selectedUserId.value = currentRow.userId; | ||||
|     shuttleVisible.value = true; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| /** 提交按钮 */ | ||||
| const submitForm = () => { | ||||
|   projectRelevancyFormRef.value?.validate(async (valid: boolean) => { | ||||
|     if (valid) { | ||||
|       buttonLoading.value = true; | ||||
|       if (form.value.id) { | ||||
|         await updateProjectRelevancy(form.value).finally(() => (buttonLoading.value = false)); | ||||
|       } else { | ||||
|         await addProjectRelevancy(form.value).finally(() => (buttonLoading.value = false)); | ||||
|       } | ||||
|       proxy?.$modal.msgSuccess('操作成功'); | ||||
|       dialog.visible = false; | ||||
|       await getList(); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| /** 删除按钮操作 */ | ||||
| const handleDelete = async (row?: ProjectRelevancyVO) => { | ||||
|   const _ids = row?.id || ids.value; | ||||
|   await proxy?.$modal.confirm('是否确认删除系统用户与项目关联编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false)); | ||||
|   await delProjectRelevancy(_ids); | ||||
|   proxy?.$modal.msgSuccess('删除成功'); | ||||
|   await getList(); | ||||
| }; | ||||
|  | ||||
| /** 导出按钮操作 */ | ||||
| const handleExport = () => { | ||||
|   proxy?.download( | ||||
|     'project/projectRelevancy/export', | ||||
|     { | ||||
|       ...queryParams.value | ||||
|     }, | ||||
|     `projectRelevancy_${new Date().getTime()}.xlsx` | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| const projectList = ref<any[]>([]); | ||||
| const projectItem = new Map(); | ||||
| const selectedProject = ref<any | null>(null); | ||||
|  | ||||
| // 获取项目列表的方法 | ||||
| const getProjectList = async () => { | ||||
|   try { | ||||
|     const res = await listProject(); | ||||
|     const projectListData = res.rows; | ||||
|     projectList.value = projectListData; | ||||
|  | ||||
|     // 将项目详情存入 Map | ||||
|     projectListData.forEach((project: ProjectVO) => { | ||||
|       projectItem.set(project.id, { | ||||
|         projectName: project.projectName, | ||||
|         type: project.type, | ||||
|         projectSite: project.projectSite, | ||||
|         principal: project.principal, | ||||
|         principalPhone: project.principalPhone | ||||
|       }); | ||||
|     }); | ||||
|   } catch (error) { | ||||
|     console.error('获取项目列表失败:', error); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // 当选择项目时触发 | ||||
| const onProjectChange = (projectId: string) => { | ||||
|   selectedProject.value = projectItem.get(projectId) || null; | ||||
| }; | ||||
|  | ||||
| //获取新增用户列表方法 | ||||
|  | ||||
| const userList = ref<{ userId: string | number; nickName: string }[]>([]); | ||||
| const getUserList = async () => { | ||||
|   try { | ||||
|     const response = await listUser({ | ||||
|       pageNum: 1, | ||||
|       pageSize: 100 | ||||
|     }); | ||||
|     userList.value = response.rows.map((user: UserVO) => ({ | ||||
|       userId: user.userId, | ||||
|       nickName: user.nickName | ||||
|     })); | ||||
|   } catch (error) { | ||||
|     console.error('获取用户列表失败:', error); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
|   getProjectList(); | ||||
|   getUserList(); | ||||
| }); | ||||
| </script> | ||||
| <style></style> | ||||
							
								
								
									
										368
									
								
								src/views/project/projectTeam/component/UserListDialog.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										368
									
								
								src/views/project/projectTeam/component/UserListDialog.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,368 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <el-form ref="queryFormRef" :model="queryParams" :inline="true"> | ||||
|       <el-form-item label="施工人员" prop="memberName"> | ||||
|         <el-input v-model="queryParams.memberName" placeholder="请输入施工人员" clearable @keyup.enter="handleQuery" /> | ||||
|       </el-form-item> | ||||
|       <el-form-item label="岗位" prop="postId"> | ||||
|         <el-select v-model="queryParams.postId" clearable placeholder="请选择岗位"> | ||||
|           <el-option v-for="item in user_post_type" :key="item.value" :label="item.label" :value="item.value" /> | ||||
|         </el-select> | ||||
|       </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-row :gutter="10" class="mb8"> | ||||
|       <el-col :span="1.5"> | ||||
|         <el-button size="small" type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['project:projectTeamMember:add']"> 新增 </el-button> | ||||
|       </el-col> | ||||
|       <el-col :span="1.5"> | ||||
|         <el-button | ||||
|           size="small" | ||||
|           type="success" | ||||
|           plain | ||||
|           icon="Edit" | ||||
|           :disabled="single" | ||||
|           @click="handleUpdate()" | ||||
|           v-hasPermi="['project:projectTeamMember:edit']" | ||||
|         > | ||||
|           修改 | ||||
|         </el-button> | ||||
|       </el-col> | ||||
|       <el-col :span="1.5"> | ||||
|         <el-button | ||||
|           size="small" | ||||
|           type="danger" | ||||
|           plain | ||||
|           icon="Delete" | ||||
|           :disabled="multiple" | ||||
|           @click="handleDelete()" | ||||
|           v-hasPermi="['project:projectTeamMember:remove']" | ||||
|         > | ||||
|           删除 | ||||
|         </el-button> | ||||
|       </el-col> | ||||
|       <el-col :span="1.5"> | ||||
|         <el-button size="small" type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['project:projectTeamMember:export']"> | ||||
|           导出 | ||||
|         </el-button> | ||||
|       </el-col> | ||||
|     </el-row> --> | ||||
|     <el-table size="small" v-loading="loading" :data="projectTeamMemberList" @selection-change="handleSelectionChange"> | ||||
|       <el-table-column type="selection" width="55" align="center" /> | ||||
|       <el-table-column label="序号" type="index" width="60" align="center" /> | ||||
|       <el-table-column label="施工人员" align="center" prop="memberName" /> | ||||
|       <el-table-column label="岗位" align="center" prop="postId"> | ||||
|         <template #default="scope"> | ||||
|           <dict-tag :options="user_post_type" :value="scope.row.postId" /> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column label="备注" align="center" prop="remark" /> | ||||
|       <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | ||||
|         <template #default="scope"> | ||||
|           <el-space wrap> | ||||
|             <el-button link type="success" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['project:projectTeamMember:edit']"> | ||||
|               修改 | ||||
|             </el-button> | ||||
|             <el-button link type="danger" icon="Position" @click="handleExit(scope.row)" v-hasPermi="['project:projectTeamMember:remove']"> | ||||
|               退场 | ||||
|             </el-button> | ||||
|           </el-space> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|     </el-table> | ||||
|     <pagination | ||||
|       size="small" | ||||
|       v-show="total > 0" | ||||
|       :total="total" | ||||
|       v-model:page="queryParams.pageNum" | ||||
|       v-model:limit="queryParams.pageSize" | ||||
|       @pagination="getList" | ||||
|     /> | ||||
|  | ||||
|     <!-- 添加或修改项目班组下的成员对话框 --> | ||||
|     <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> | ||||
|       <el-form ref="projectTeamMemberFormRef" :model="form" :rules="rules" label-width="80px"> | ||||
|         <el-form-item label="施工人员" prop="memberId" v-if="!form.id"> | ||||
|           <el-select v-model="form.memberId" clearable placeholder="请选择人员" filterable> | ||||
|             <el-option v-for="item in userNotInTeamOpt" :key="item.value" :label="item.label" :value="item.value" /> | ||||
|             <pagination | ||||
|               size="small" | ||||
|               v-show="userNotInTeamTotal > 0" | ||||
|               :total="userNotInTeamTotal" | ||||
|               v-model:page="userQueryParams.pageNum" | ||||
|               v-model:limit="userQueryParams.pageSize" | ||||
|               @pagination="getUserListNotInTeam" | ||||
|             /> | ||||
|           </el-select> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="岗位" prop="postId"> | ||||
|           <el-select v-model="form.postId" clearable placeholder="请选择岗位"> | ||||
|             <el-option v-for="item in user_post_type" :key="item.value" :label="item.label" :value="item.value" /> | ||||
|           </el-select> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="备注" prop="remark"> | ||||
|           <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" /> | ||||
|         </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> | ||||
|     <!-- 上传退场记录 --> | ||||
|     <el-dialog title="员工离场" v-model="memberStatus" width="30%"> | ||||
|       <el-form :model="memberForm" ref="memberFormRef" :rules="memberRules" label-width="100px" :inline="false"> | ||||
|         <el-form-item label="用户名"> | ||||
|           <el-input v-model="memberForm.userName" disabled></el-input> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="退场文件"> | ||||
|           <file-upload v-model="memberForm.filePath" :limit="10" :is-show-tip="false" :file-size="50" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="备注"> | ||||
|           <el-input v-model="memberForm.remark" placeholder="请输入备注" type="textarea"></el-input> | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
|  | ||||
|       <template #footer> | ||||
|         <span> | ||||
|           <el-button type="primary" @click="submitMemberForm" :loading="buttonLoading">确定</el-button> | ||||
|           <el-button @click="memberStatus = false">取消</el-button> | ||||
|         </span> | ||||
|       </template> | ||||
|     </el-dialog> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { ProjectTeamVO } from '@/api/project/projectTeam/types'; | ||||
| import { ProjectTeamMemberForm, ProjectTeamMemberQuery, ProjectTeamMemberVO } from '@/api/project/projectTeamMember/types'; | ||||
| import { | ||||
|   addProjectTeamMember, | ||||
|   delProjectTeamMember, | ||||
|   getProjectTeamMember, | ||||
|   listProjectTeamMember, | ||||
|   updateProjectTeamMember | ||||
| } from '@/api/project/projectTeamMember'; | ||||
| import { computed, reactive, ref } from 'vue'; | ||||
| import { useUserStoreHook } from '@/store/modules/user'; | ||||
| import { listConstructionUser, delConstructionUserMember } from '@/api/project/constructionUser'; | ||||
| import { ConstructionUserQuery, ConstructionUserVO, ConstructionUserMembeForm } from '@/api/project/constructionUser/types'; | ||||
|  | ||||
| // 获取用户 store | ||||
| const userStore = useUserStoreHook(); | ||||
| // 从 store 中获取项目列表和当前选中的项目 | ||||
| const currentProject = computed(() => userStore.selectedProject); | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| const { user_post_type } = toRefs<any>(proxy?.useDict('user_post_type')); | ||||
| const memberStatus = ref(false); | ||||
| interface Props { | ||||
|   projectTeamVo: ProjectTeamVO; | ||||
| } | ||||
| const memberForm = reactive<ConstructionUserMembeForm>({ | ||||
|   id: undefined, | ||||
|   filePath: undefined, | ||||
|   remark: undefined, | ||||
|   userName: undefined | ||||
| }); | ||||
|  | ||||
| const props = defineProps<Props>(); | ||||
| // 是否可见 | ||||
| const visible = ref(false); | ||||
| const initFormData: ProjectTeamMemberForm = { | ||||
|   projectId: currentProject.value.id | ||||
| }; | ||||
| const data = reactive<PageData<ProjectTeamMemberForm, ProjectTeamMemberQuery>>({ | ||||
|   form: { ...initFormData }, | ||||
|   queryParams: { | ||||
|     pageNum: 1, | ||||
|     pageSize: 10, | ||||
|     orderByColumn: 'createTime', | ||||
|     isAsc: 'desc', | ||||
|     projectId: currentProject.value.id | ||||
|   }, | ||||
|   rules: { | ||||
|     id: [{ required: true, message: '主键id不能为空', trigger: 'blur' }] | ||||
|   }, | ||||
|   memberRules: { | ||||
|     filePath: [{ required: true, message: '请上传退场文件', trigger: 'blur' }] | ||||
|   } | ||||
| }); | ||||
| const buttonLoading = ref(false); | ||||
| const loading = 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 projectTeamMemberFormRef = ref<ElFormInstance>(); | ||||
| const memberFormRef = ref<ElFormInstance>(); | ||||
|  | ||||
| const dialog = reactive<DialogOption>({ | ||||
|   visible: false, | ||||
|   title: '' | ||||
| }); | ||||
| const { queryParams, form, rules, memberRules } = toRefs(data); | ||||
| const projectTeamMemberList = ref<ProjectTeamMemberVO[]>([]); | ||||
| /** 查询项目班组下的成员列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await listProjectTeamMember({ ...queryParams.value, teamId: props.projectTeamVo.id }); | ||||
|   projectTeamMemberList.value = res.rows; | ||||
|   total.value = res.total; | ||||
|   loading.value = false; | ||||
| }; | ||||
|  | ||||
| /** 取消按钮 */ | ||||
| const cancel = () => { | ||||
|   reset(); | ||||
|   dialog.visible = false; | ||||
| }; | ||||
|  | ||||
| /** 表单重置 */ | ||||
| const reset = () => { | ||||
|   form.value = { ...initFormData }; | ||||
|   projectTeamMemberFormRef.value?.resetFields(); | ||||
| }; | ||||
|  | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.value.pageNum = 1; | ||||
|   getList(); | ||||
| }; | ||||
|  | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   handleQuery(); | ||||
| }; | ||||
|  | ||||
| /** 多选框选中数据 */ | ||||
| const handleSelectionChange = (selection: ProjectTeamMemberVO[]) => { | ||||
|   ids.value = selection.map((item) => item.id); | ||||
|   single.value = selection.length != 1; | ||||
|   multiple.value = !selection.length; | ||||
| }; | ||||
|  | ||||
| /** 新增按钮操作 */ | ||||
| const handleAdd = () => { | ||||
|   reset(); | ||||
|   dialog.visible = true; | ||||
|   dialog.title = '添加项目班组下的成员'; | ||||
|   getUserListNotInTeam(); | ||||
| }; | ||||
|  | ||||
| const userNotInTeamOpt = ref(); | ||||
| const userNotInTeamTotal = ref(); | ||||
| const userQueryParams = ref<ConstructionUserQuery>({ | ||||
|   pageNum: 1, | ||||
|   pageSize: 10, | ||||
|   orderByColumn: 'createTime', | ||||
|   isAsc: 'desc', | ||||
|   projectId: currentProject.value.id | ||||
| }); | ||||
|  | ||||
| // 获取不在当前班组的成员 | ||||
| const getUserListNotInTeam = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await listConstructionUser({ ...userQueryParams.value, notTeamId: props.projectTeamVo.id }); | ||||
|   userNotInTeamOpt.value = res.rows.map((user: ConstructionUserVO) => ({ | ||||
|     value: user.id, | ||||
|     label: user.userName | ||||
|   })); | ||||
|   userNotInTeamTotal.value = res.total; | ||||
|   loading.value = false; | ||||
| }; | ||||
|  | ||||
| /** 修改按钮操作 */ | ||||
| const handleUpdate = async (row?: ProjectTeamMemberVO) => { | ||||
|   reset(); | ||||
|   const _id = row?.id || ids.value[0]; | ||||
|   const res = await getProjectTeamMember(_id); | ||||
|   Object.assign(form.value, res.data); | ||||
|   dialog.visible = true; | ||||
|   dialog.title = '修改项目班组下的成员'; | ||||
| }; | ||||
|  | ||||
| /** 提交按钮 */ | ||||
| const submitForm = () => { | ||||
|   projectTeamMemberFormRef.value?.validate(async (valid: boolean) => { | ||||
|     if (valid) { | ||||
|       buttonLoading.value = true; | ||||
|       if (form.value.id) { | ||||
|         await updateProjectTeamMember(form.value).finally(() => (buttonLoading.value = false)); | ||||
|       } else { | ||||
|         await addProjectTeamMember({ | ||||
|           ...form.value, | ||||
|           teamId: props.projectTeamVo.id | ||||
|         }).finally(() => (buttonLoading.value = false)); | ||||
|       } | ||||
|       proxy?.$modal.msgSuccess('操作成功'); | ||||
|       dialog.visible = false; | ||||
|       await getList(); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| /** 确定退场按钮 */ | ||||
| const submitMemberForm = async () => { | ||||
|   memberFormRef.value?.validate(async (valid: boolean) => { | ||||
|     if (valid) { | ||||
|       buttonLoading.value = true; | ||||
|       await delConstructionUserMember(memberForm).finally(() => (buttonLoading.value = false)); | ||||
|       proxy?.$modal.msgSuccess('操作成功'); | ||||
|       dialog.visible = false; | ||||
|       await getList(); | ||||
|       memberForm.filePath = undefined; | ||||
|       memberForm.remark = undefined; | ||||
|     } | ||||
|   }); | ||||
|   memberStatus.value = false; | ||||
| }; | ||||
|  | ||||
| /** 退场按钮操作 */ | ||||
| const handleExit = async (row?: ProjectTeamMemberVO) => { | ||||
|   const _ids = row?.id || ids.value; | ||||
|   memberForm.userName = row?.memberName; | ||||
|   console.log('🚀 ~ handleDelete ~ row:', row); | ||||
|   memberForm.id = row?.id; | ||||
|   memberStatus.value = true; | ||||
| }; | ||||
|  | ||||
| /** 删除按钮操作 */ | ||||
| const handleDelete = async (row?: ProjectTeamMemberVO) => { | ||||
|   const _ids = row?.id || ids.value; | ||||
|   await proxy?.$modal.confirm('是否确认删除项目班组下的成员编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false)); | ||||
|   await delProjectTeamMember(_ids); | ||||
|   proxy?.$modal.msgSuccess('删除成功'); | ||||
|   await getList(); | ||||
| }; | ||||
|  | ||||
| /** 导出按钮操作 */ | ||||
| const handleExport = () => { | ||||
|   proxy?.download( | ||||
|     'project/projectTeamMember/export', | ||||
|     { | ||||
|       ...queryParams.value | ||||
|     }, | ||||
|     `projectTeamMember_${new Date().getTime()}.xlsx` | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| watch( | ||||
|   () => props.projectTeamVo, | ||||
|   (newId, oldId) => { | ||||
|     if (newId !== oldId) { | ||||
|       getList(); | ||||
|     } | ||||
|   } | ||||
| ); | ||||
|  | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
| }); | ||||
| </script> | ||||
							
								
								
									
										284
									
								
								src/views/project/projectTeam/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								src/views/project/projectTeam/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,284 @@ | ||||
| <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="班组名称" prop="teamName"> | ||||
|               <el-input v-model="queryParams.teamName" 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:projectTeam:add']"> 新增 </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['project:projectTeam:edit']"> | ||||
|               修改 | ||||
|             </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['project:projectTeam:remove']"> | ||||
|               删除 | ||||
|             </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['project:projectTeam:export']">导出 </el-button> | ||||
|           </el-col> | ||||
|           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> | ||||
|         </el-row> | ||||
|       </template> | ||||
|  | ||||
|       <el-table v-loading="loading" :data="projectTeamList" @selection-change="handleSelectionChange"> | ||||
|         <el-table-column type="selection" width="55" align="center" /> | ||||
|         <el-table-column label="序号" type="index" width="60" align="center" /> | ||||
|         <el-table-column label="班组名称" align="center" prop="teamName" /> | ||||
|         <el-table-column label="班组人数" align="center" prop="peopleNumber"> | ||||
|           <template #default="scope"> | ||||
|             <el-link type="primary" :underline="false" @click="handleUserList(scope.row)"> {{ scope.row.peopleNumber }}</el-link> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="打卡范围" align="center" prop="isClockIn"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="team_clock_type" :value="scope.row.isClockIn" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="备注" align="center" prop="remark" /> | ||||
|         <el-table-column label="创建时间" align="center" prop="createTime" width="180" /> | ||||
|         <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | ||||
|           <template #default="scope"> | ||||
|             <el-space wrap> | ||||
|               <el-button link type="primary" icon="User" @click="handleUserList(scope.row)">班组成员</el-button> | ||||
|               <el-button link type="success" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['project:projectTeam:edit']"> 修改 </el-button> | ||||
|               <el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['project:projectTeam:remove']"> | ||||
|                 删除 | ||||
|               </el-button> | ||||
|             </el-space> | ||||
|           </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="projectTeamFormRef" :model="form" :rules="rules" label-width="80px"> | ||||
|         <el-form-item label="班组名称" prop="teamName"> | ||||
|           <el-input v-model="form.teamName" placeholder="请输入班组名称" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="打卡范围" prop="isClockIn"> | ||||
|           <el-select v-model="form.isClockIn" clearable placeholder="请选择打卡范围"> | ||||
|             <el-option v-for="item in team_clock_type" :key="item.value" :label="item.label" :value="item.value" /> | ||||
|           </el-select> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="备注" prop="remark"> | ||||
|           <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" /> | ||||
|         </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> | ||||
|     <el-dialog :title="currentRow.teamName" v-model="visible" width="1000px"> | ||||
|       <user-list-dialog :projectTeamVo="currentRow" /> | ||||
|     </el-dialog> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup name="ProjectTeam" lang="ts"> | ||||
| import { addProjectTeam, delProjectTeam, getProjectTeam, listProjectTeam, updateProjectTeam } from '@/api/project/projectTeam'; | ||||
| import { ProjectTeamForm, ProjectTeamQuery, ProjectTeamVO } from '@/api/project/projectTeam/types'; | ||||
| import { useUserStoreHook } from '@/store/modules/user'; | ||||
| import UserListDialog from '@/views/project/projectTeam/component/UserListDialog.vue'; | ||||
|  | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| const { team_clock_type } = toRefs<any>(proxy?.useDict('team_clock_type')); | ||||
| // 获取用户 store | ||||
| const userStore = useUserStoreHook(); | ||||
| // 从 store 中获取项目列表和当前选中的项目 | ||||
| const currentProject = computed(() => userStore.selectedProject); | ||||
| const projectTeamList = ref<ProjectTeamVO[]>([]); | ||||
| 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 currentRow = ref<ProjectTeamVO>({ | ||||
|   id: undefined, | ||||
|   projectId: undefined, | ||||
|   teamName: undefined, | ||||
|   isClockIn: undefined, | ||||
|   remark: undefined, | ||||
|   createTime: undefined | ||||
| }); | ||||
|  | ||||
| const queryFormRef = ref<ElFormInstance>(); | ||||
| const projectTeamFormRef = ref<ElFormInstance>(); | ||||
|  | ||||
| const dialog = reactive<DialogOption>({ | ||||
|   visible: false, | ||||
|   title: '' | ||||
| }); | ||||
|  | ||||
| const initFormData: ProjectTeamForm = { | ||||
|   id: undefined, | ||||
|   projectId: currentProject.value.id, | ||||
|   teamName: undefined, | ||||
|   isClockIn: undefined, | ||||
|   remark: undefined, | ||||
|   peopleNumber: undefined | ||||
| }; | ||||
| const data = reactive<PageData<ProjectTeamForm, ProjectTeamQuery>>({ | ||||
|   form: { ...initFormData }, | ||||
|   queryParams: { | ||||
|     pageNum: 1, | ||||
|     pageSize: 10, | ||||
|     orderByColumn: 'createTime', | ||||
|     isAsc: 'desc', | ||||
|     projectId: currentProject.value.id, | ||||
|     teamName: undefined, | ||||
|     isClockIn: undefined, | ||||
|     params: {}, | ||||
|     peopleNumber: undefined | ||||
|   }, | ||||
|   rules: { | ||||
|     id: [{ required: true, message: '主键id不能为空', trigger: 'blur' }], | ||||
|     isClockIn: [{ required: true, message: '范围内打卡不能为空', trigger: 'blur' }] | ||||
|   } | ||||
| }); | ||||
|  | ||||
| const { queryParams, form, rules } = toRefs(data); | ||||
|  | ||||
| /** 查询项目班组列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await listProjectTeam(queryParams.value); | ||||
|   projectTeamList.value = res.rows; | ||||
|   total.value = res.total; | ||||
|   loading.value = false; | ||||
| }; | ||||
|  | ||||
| /** 取消按钮 */ | ||||
| const cancel = () => { | ||||
|   reset(); | ||||
|   dialog.visible = false; | ||||
| }; | ||||
|  | ||||
| /** 表单重置 */ | ||||
| const reset = () => { | ||||
|   form.value = { ...initFormData }; | ||||
|   projectTeamFormRef.value?.resetFields(); | ||||
| }; | ||||
|  | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.value.pageNum = 1; | ||||
|   getList(); | ||||
| }; | ||||
|  | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   handleQuery(); | ||||
| }; | ||||
|  | ||||
| /** 多选框选中数据 */ | ||||
| const handleSelectionChange = (selection: ProjectTeamVO[]) => { | ||||
|   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?: ProjectTeamVO) => { | ||||
|   reset(); | ||||
|   const _id = row?.id || ids.value[0]; | ||||
|   const res = await getProjectTeam(_id); | ||||
|   Object.assign(form.value, res.data); | ||||
|   dialog.visible = true; | ||||
|   dialog.title = '修改项目班组'; | ||||
| }; | ||||
|  | ||||
| const visible = ref<boolean>(false); | ||||
| /** 班组成员操作 */ | ||||
| const handleUserList = (row?: ProjectTeamVO) => { | ||||
|   currentRow.value = row; | ||||
|   visible.value = true; | ||||
| }; | ||||
|  | ||||
| /** 提交按钮 */ | ||||
| const submitForm = () => { | ||||
|   projectTeamFormRef.value?.validate(async (valid: boolean) => { | ||||
|     if (valid) { | ||||
|       buttonLoading.value = true; | ||||
|       form.value.projectId = currentProject.value.id; | ||||
|       if (form.value.id) { | ||||
|         await updateProjectTeam(form.value).finally(() => (buttonLoading.value = false)); | ||||
|       } else { | ||||
|         await addProjectTeam(form.value).finally(() => (buttonLoading.value = false)); | ||||
|       } | ||||
|       proxy?.$modal.msgSuccess('操作成功'); | ||||
|       dialog.visible = false; | ||||
|       await getList(); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| /** 删除按钮操作 */ | ||||
| const handleDelete = async (row?: ProjectTeamVO) => { | ||||
|   const _ids = row?.id || ids.value; | ||||
|   await proxy?.$modal.confirm('是否确认删除项目班组编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false)); | ||||
|   await delProjectTeam(_ids); | ||||
|   proxy?.$modal.msgSuccess('删除成功'); | ||||
|   await getList(); | ||||
| }; | ||||
|  | ||||
| /** 导出按钮操作 */ | ||||
| const handleExport = () => { | ||||
|   proxy?.download( | ||||
|     'project/projectTeam/export', | ||||
|     { | ||||
|       ...queryParams.value | ||||
|     }, | ||||
|     `projectTeam_${new Date().getTime()}.xlsx` | ||||
|   ); | ||||
| }; | ||||
| //监听项目id刷新数据 | ||||
| const listeningProject = watch( | ||||
|   () => currentProject.value.id, | ||||
|   (nid, oid) => { | ||||
|     queryParams.value.projectId = nid; | ||||
|     form.value.projectId = nid; | ||||
|     getList(); | ||||
|   } | ||||
| ); | ||||
|  | ||||
| onUnmounted(() => { | ||||
|   listeningProject(); | ||||
| }); | ||||
|  | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
| }); | ||||
| </script> | ||||
							
								
								
									
										395
									
								
								src/views/project/reissueCard/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										395
									
								
								src/views/project/reissueCard/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,395 @@ | ||||
| <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="申请人" prop="userName"> | ||||
|               <el-input v-model="queryParams.userName" placeholder="请输入申请人" clearable @keyup.enter="handleQuery" /> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="补卡类型" prop="reissueCardType"> | ||||
|               <el-select v-model="queryParams.reissueCardType" placeholder="全部" clearable> | ||||
|                 <el-option v-for="dict in commuter_type" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="所属班组" prop="reissueCardType"> | ||||
|               <el-select v-model="queryParams.teamId" placeholder="全部" clearable> | ||||
|                 <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"> | ||||
|               <el-select v-model="queryParams.gangerOpinion" placeholder="全部" clearable> | ||||
|                 <el-option v-for="dict in user_opinion_type" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="管理员意见" prop="managerOpinion" label-width="100px"> | ||||
|               <el-select v-model="queryParams.managerOpinion" placeholder="全部" clearable> | ||||
|                 <el-option v-for="dict in user_opinion_type" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </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"> | ||||
|       <el-table v-loading="loading" :data="reissueCardList"> | ||||
|         <el-table-column type="selection" width="55" align="center" /> | ||||
|         <el-table-column label="序号" align="center" width="55" type="index" /> | ||||
|         <el-table-column label="申请人" align="center" prop="userName" /> | ||||
|         <el-table-column label="申请补卡说明" align="center" prop="userExplain" /> | ||||
|         <el-table-column label="所属班组" align="center" prop="teamName" /> | ||||
|         <el-table-column label="状态" align="center" prop="status"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="user_review_status_type" :value="scope.row.status" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <!-- <el-table-column label="班组长意见" align="center" prop="gangerOpinion"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="user_opinion_type" :value="scope.row.gangerOpinion" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="班组长说明" align="center" prop="gangerExplain" /> | ||||
|         <el-table-column label="班组长操作时间" align="center" prop="gangerTime" width="180"> | ||||
|           <template #default="scope"> | ||||
|             <span>{{ parseTime(scope.row.gangerTime, '{y}-{m}-{d}') }}</span> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="管理员意见" align="center" prop="managerOpinion"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="user_opinion_type" :value="scope.row.managerOpinion" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="管理员说明" align="center" prop="managerExplain" /> | ||||
|         <el-table-column label="补卡申请时间" align="center" prop="userTime" width="180"> | ||||
|           <template #default="scope"> | ||||
|             <span>{{ parseTime(scope.row.userTime, '{y}-{m}-{d}') }}</span> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|  | ||||
|         <el-table-column label="管理员操作时间" align="center" prop="managerTime" width="180"> | ||||
|           <template #default="scope"> | ||||
|             <span>{{ parseTime(scope.row.managerTime, '{y}-{m}-{d}') }}</span> | ||||
|           </template> | ||||
|         </el-table-column> --> | ||||
|         <!-- <el-table-column label="备注" align="center" prop="remark" /> --> | ||||
|         <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | ||||
|           <template #default="scope"> | ||||
|             <el-button link type="success" icon="View" @click="handleDetail(scope.row)">详情</el-button> | ||||
|           </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="修改施工人员补卡申请" v-model="dialog.visible" width="400px"> | ||||
|       <div class="pl-xl"> | ||||
|         <el-form-item label="是否同意"> | ||||
|           <el-radio-group v-model="auditForm.managerOpinion"> | ||||
|             <el-radio value="2" size="small">同意</el-radio> | ||||
|             <el-radio value="3" size="small">拒绝</el-radio> | ||||
|           </el-radio-group> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="拒绝说明" v-if="auditForm.managerOpinion == '3'"> | ||||
|           <el-input v-model="auditForm.managerExplain" placeholder="请输入拒绝说明" type="textarea" clearable></el-input> | ||||
|         </el-form-item> | ||||
|       </div> | ||||
|  | ||||
|       <template #footer> | ||||
|         <span> | ||||
|           <el-button type="primary" @click="submitAudit">确定</el-button> | ||||
|           <el-button @click="dialog.visible = false">取消</el-button> | ||||
|         </span> | ||||
|       </template> | ||||
|     </el-dialog> | ||||
|     <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> | ||||
|  | ||||
| <script setup name="ReissueCard" lang="ts"> | ||||
| import { listProjectTeam } from '@/api/project/projectTeam'; | ||||
| import { ProjectTeamVO } from '@/api/project/projectTeam/types'; | ||||
| import { listReissueCard, getReissueCard, delReissueCard, addReissueCard, updateReissueCard, AuditReissueCard } from '@/api/project/reissueCard'; | ||||
| import { ReissueCardVO, ReissueCardQuery, ReissueCardForm, AuditReissueCardForm } from '@/api/project/reissueCard/types'; | ||||
| import { useUserStoreHook } from '@/store/modules/user'; | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| const { commuter_type, user_opinion_type, user_review_status_type } = toRefs<any>( | ||||
|   proxy?.useDict('commuter_type', 'user_opinion_type', 'user_review_status_type') | ||||
| ); | ||||
| const detailObj = ref<ReissueCardVO>({ | ||||
|   userName: undefined, | ||||
|   id: undefined, | ||||
|   userExplain: undefined, | ||||
|   userTime: undefined, | ||||
|   gangerName: undefined, | ||||
|   gangerOpinion: undefined, | ||||
|   gangerExplain: undefined, | ||||
|   gangerTime: undefined, | ||||
|   managerOpinion: undefined, | ||||
|   managerExplain: undefined, | ||||
|   managerTime: undefined, | ||||
|   remark: undefined, | ||||
|   status: undefined, | ||||
|   managerName: undefined | ||||
| }); | ||||
| const reissueCardList = ref<ReissueCardVO[]>([]); | ||||
| 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 reissueCardFormRef = ref<ElFormInstance>(); | ||||
| const auditForm = reactive<AuditReissueCardForm>({ | ||||
|   id: undefined, | ||||
|   managerOpinion: '2', | ||||
|   managerExplain: undefined | ||||
| }); | ||||
| const dialog = reactive<DialogOption>({ | ||||
|   visible: false, | ||||
|   title: '', | ||||
|   details: false | ||||
| }); | ||||
| // 获取用户 store | ||||
| const userStore = useUserStoreHook(); | ||||
| // 从 store 中获取项目列表和当前选中的项目 | ||||
| const currentProject = computed(() => userStore.selectedProject); | ||||
| const ProjectTeam = computed(() => userStore.ProjectTeamList); | ||||
| const initFormData: ReissueCardForm = { | ||||
|   id: undefined, | ||||
|   userId: undefined, | ||||
|   userExplain: undefined, | ||||
|   gangerId: undefined, | ||||
|   gangerOpinion: undefined, | ||||
|   gangerExplain: undefined, | ||||
|   managerOpinion: undefined, | ||||
|   managerExplain: undefined, | ||||
|   projectId: currentProject.value?.id, | ||||
|   attendanceId: undefined, | ||||
|   remark: undefined | ||||
| }; | ||||
| const data = reactive<PageData<ReissueCardForm, ReissueCardQuery>>({ | ||||
|   form: { ...initFormData }, | ||||
|   queryParams: { | ||||
|     pageNum: 1, | ||||
|     pageSize: 10, | ||||
|     userName: undefined, | ||||
|     gangerOpinion: undefined, | ||||
|     managerOpinion: undefined, | ||||
|     projectId: currentProject.value?.id, | ||||
|     teamId: undefined, | ||||
|     reissueCardType: undefined, | ||||
|     params: {} | ||||
|   }, | ||||
|   rules: { | ||||
|     id: [{ required: true, message: '主键id不能为空', trigger: 'blur' }], | ||||
|     userId: [{ required: true, message: '申请人id不能为空', trigger: 'blur' }], | ||||
|     gangerId: [{ required: true, message: '班组长不能为空', trigger: 'blur' }], | ||||
|     gangerOpinion: [{ required: true, message: '班组长意见不能为空', trigger: 'blur' }], | ||||
|     managerOpinion: [{ required: true, message: '管理员意见不能为空', trigger: 'blur' }], | ||||
|     projectId: [{ required: true, message: '项目id不能为空', trigger: 'blur' }], | ||||
|     attendanceId: [{ required: true, message: '考勤表主键id不能为空', trigger: 'blur' }] | ||||
|   } | ||||
| }); | ||||
|  | ||||
| const { queryParams, form, rules } = toRefs(data); | ||||
|  | ||||
| //审核进度 | ||||
| const auditProgress = computed(() => { | ||||
|   if (auditStatus.value) return 3; | ||||
|   if (managerStatus.value) return 2; | ||||
|   if (teamStatus.value) return 1; | ||||
|   return 0; | ||||
| }); | ||||
| //审核状态 | ||||
| const auditStatus = computed(() => { | ||||
|   if (detailObj.value.status == '3' || detailObj.value.status == '4') return true; | ||||
|   return false; | ||||
| }); | ||||
| //管理员审核状态 | ||||
| const managerStatus = computed(() => { | ||||
|   switch (detailObj.value.managerOpinion) { | ||||
|     case '1': | ||||
|       return false; | ||||
|     case '2': | ||||
|       return 'success'; | ||||
|     case '3': | ||||
|       return 'error'; | ||||
|   } | ||||
| }); | ||||
| //班组审核状态 | ||||
| const teamStatus = computed(() => { | ||||
|   switch (detailObj.value.gangerOpinion) { | ||||
|     case '1': | ||||
|       return false; | ||||
|     case '2': | ||||
|       return 'success'; | ||||
|     case '3': | ||||
|       return 'error'; | ||||
|   } | ||||
| }); | ||||
| //结果状态 | ||||
| const resultsStatus = computed(() => { | ||||
|   if (detailObj.value.status == '3') return 'success'; | ||||
|   if (detailObj.value.status == '4') return 'error'; | ||||
|   return ''; | ||||
| }); | ||||
|  | ||||
| /** 查询施工人员补卡申请列表 */ | ||||
| async function getList() { | ||||
|   loading.value = true; | ||||
|   const res = await listReissueCard(queryParams.value); | ||||
|   reissueCardList.value = res.rows; | ||||
|   total.value = res.total; | ||||
|   loading.value = false; | ||||
| } | ||||
|  | ||||
| /** 表单重置 */ | ||||
| const reset = () => { | ||||
|   auditForm.managerExplain = ''; | ||||
|   auditForm.managerOpinion = '2'; | ||||
| }; | ||||
|  | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.value.pageNum = 1; | ||||
|   getList(); | ||||
| }; | ||||
|  | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   handleQuery(); | ||||
| }; | ||||
|  | ||||
| /** 新增按钮操作 */ | ||||
| const handleAdd = () => { | ||||
|   reset(); | ||||
|   dialog.visible = true; | ||||
|   dialog.title = '添加施工人员补卡申请'; | ||||
| }; | ||||
|  | ||||
| /** 审批按钮操作 */ | ||||
| const handleUpdate = async (row?: ReissueCardVO) => { | ||||
|   reset(); | ||||
|   const _id = row?.id || ids.value[0]; | ||||
|   auditForm.id = _id; | ||||
|   dialog.visible = true; | ||||
|   dialog.title = '修改施工人员补卡申请'; | ||||
| }; | ||||
|  | ||||
| const handleDetail = (row: ReissueCardVO) => { | ||||
|   detailObj.value = row; | ||||
|   dialog.details = true; | ||||
| }; | ||||
|  | ||||
| /** 审批操作 */ | ||||
| const submitAudit = async () => { | ||||
|   await AuditReissueCard(auditForm); | ||||
|   proxy?.$modal.msgSuccess('审批成功'); | ||||
|   dialog.visible = false; | ||||
|   auditForm.managerExplain = ''; | ||||
|   auditForm.managerOpinion = '2'; | ||||
|   await getList(); | ||||
| }; | ||||
|  | ||||
| //监听项目id刷新数据 | ||||
| const listeningProject = watch( | ||||
|   () => currentProject.value.id, | ||||
|   (nid, oid) => { | ||||
|     queryParams.value.projectId = nid; | ||||
|     form.value.projectId = nid; | ||||
|     getList(); | ||||
|   } | ||||
| ); | ||||
|  | ||||
| onUnmounted(() => { | ||||
|   listeningProject(); | ||||
| }); | ||||
|  | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
| }); | ||||
| </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> | ||||
							
								
								
									
										319
									
								
								src/views/project/workWage/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										319
									
								
								src/views/project/workWage/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,319 @@ | ||||
| <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="工种" prop="workType"> | ||||
|               <el-select v-model="queryParams.workType" placeholder="全部" clearable> | ||||
|                 <el-option v-for="dict in type_of_work" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="工资计算方式" prop="wageCalculationType" label-width="100px"> | ||||
|               <el-select v-model="queryParams.wageCalculationType" placeholder="全部" clearable> | ||||
|                 <el-option v-for="dict in wageCalculationTypeList" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </el-form-item> | ||||
|             <!-- <el-form-item label="工资计量单位" prop="wageMeasureUnit" label-width="100px"> | ||||
|               <el-select v-model="queryParams.wageMeasureUnit" placeholder="请选择工资计量单位" clearable> | ||||
|                 <el-option v-for="dict in wage_measure_unit_type" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </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:workWage:add']">新增 </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['project:workWage:edit']" | ||||
|               >修改 | ||||
|             </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['project:workWage:remove']" | ||||
|               >删除 | ||||
|             </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['project:workWage:export']">导出 </el-button> | ||||
|           </el-col> | ||||
|           <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> | ||||
|         </el-row> | ||||
|       </template> | ||||
|  | ||||
|       <el-table v-loading="loading" :data="workWageList" @selection-change="handleSelectionChange"> | ||||
|         <el-table-column type="selection" width="55" align="center" /> | ||||
|         <el-table-column label="序号" align="center" type="index" width="60px" /> | ||||
|         <!-- <el-table-column label="主键id" align="center" prop="id" v-if="true" /> --> | ||||
|         <!-- <el-table-column label="项目id" align="center" prop="projectId" /> --> | ||||
|         <el-table-column label="工种" align="center" prop="workType"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="type_of_work" :value="scope.row.workType" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="是否是特种兵" align="center" prop="isSpecialType"> | ||||
|           <template #default="scope"> | ||||
|             {{ scope.row.isSpecialType == 1 ? '是' : '否' }} | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="工资计算方式" align="center" prop="wageCalculationType"> | ||||
|           <template #default="scope"> | ||||
|             {{ scope.row.wageCalculationType == 1 ? '计时' : '计件' }} | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="工资标准" align="center" prop="wage" /> | ||||
|         <el-table-column label="工资计量单位" align="center" prop="wageMeasureUnit"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="wage_measure_unit_type" :value="scope.row.wageMeasureUnit" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="创建时间" align="center" prop="createTime" /> | ||||
|         <el-table-column label="备注" align="center" prop="remark" /> | ||||
|         <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:workWage:edit']"></el-button> | ||||
|             </el-tooltip> | ||||
|             <el-tooltip content="删除" placement="top"> | ||||
|               <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['project:workWage: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="workWageFormRef" :model="form" :rules="rules" label-width="100px"> | ||||
|         <!-- <el-form-item label="项目id" prop="projectId"> | ||||
|           <el-input v-model="form.projectId" placeholder="请输入项目id" /> | ||||
|         </el-form-item> --> | ||||
|         <el-form-item label="工种" prop="workType"> | ||||
|           <el-select v-model="form.workType" placeholder="请选择工种" :disabled="!form.id ? false : true"> | ||||
|             <el-option v-for="dict in type_of_work" :key="dict.value" :label="dict.label" :value="dict.value"></el-option> | ||||
|           </el-select> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="是否是特种兵 " prop="isSpecialType"> | ||||
|           <el-select v-model="form.isSpecialType" placeholder="请选择是否是特种兵"> | ||||
|             <el-option v-for="dict in isSpecialTypeList" :key="dict.value" :label="dict.label" :value="dict.value"></el-option> | ||||
|           </el-select> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="工资计算方式" prop="wageCalculationType"> | ||||
|           <el-select v-model="form.wageCalculationType" placeholder="请选择工资计算方式" :disabled="!form.id ? false : true"> | ||||
|             <el-option v-for="dict in wageCalculationTypeList" :key="dict.value" :label="dict.label" :value="dict.value"></el-option> | ||||
|           </el-select> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="工资标准" prop="wage"> | ||||
|           <el-input v-model="form.wage" placeholder="请输入工资标准" /> | ||||
|         </el-form-item> | ||||
|         <!-- <el-form-item label="工资计量单位" prop="wageMeasureUnit"> | ||||
|           <el-select v-model="form.wageMeasureUnit" placeholder="请选择工资计量单位"> | ||||
|             <el-option v-for="dict in wage_measure_unit_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option> | ||||
|           </el-select> | ||||
|         </el-form-item> --> | ||||
|         <el-form-item label="备注" prop="remark"> | ||||
|           <el-input v-model="form.remark" type="textarea" placeholder="请输入内容" /> | ||||
|         </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="WorkWage" lang="ts"> | ||||
| import { listWorkWage, getWorkWage, delWorkWage, addWorkWage, updateWorkWage } from '@/api/project/workWage'; | ||||
| import { WorkWageVO, WorkWageQuery, WorkWageForm, SpecialType } from '@/api/project/workWage/types'; | ||||
| import { useUserStoreHook } from '@/store/modules/user'; | ||||
|  | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| const { type_of_work, wage_measure_unit_type } = toRefs<any>(proxy?.useDict('type_of_work', 'wage_measure_unit_type')); | ||||
| console.log(type_of_work); | ||||
|  | ||||
| // 获取用户 store | ||||
| const userStore = useUserStoreHook(); | ||||
| // 从 store 中获取项目列表和当前选中的项目 | ||||
| const currentProject = computed(() => userStore.selectedProject); | ||||
| const workWageList = ref<WorkWageVO[]>([]); | ||||
| 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 workWageFormRef = ref<ElFormInstance>(); | ||||
| const isSpecialTypeList = ref<Array<SpecialType>>([ | ||||
|   { label: '是', value: '1' }, | ||||
|   { label: '否', value: '2' } | ||||
| ]); | ||||
|  | ||||
| const wageCalculationTypeList = ref<Array<SpecialType>>([ | ||||
|   { label: '计时', value: '1' }, | ||||
|   { label: '计件', value: '2' } | ||||
| ]); | ||||
| const dialog = reactive<DialogOption>({ | ||||
|   visible: false, | ||||
|   title: '' | ||||
| }); | ||||
|  | ||||
| const initFormData: WorkWageForm = { | ||||
|   id: undefined, | ||||
|   projectId: currentProject.value.id, | ||||
|   workType: undefined, | ||||
|   isSpecialType: undefined, | ||||
|   wageCalculationType: undefined, | ||||
|   wage: undefined, | ||||
|   wageMeasureUnit: undefined, | ||||
|   remark: undefined | ||||
| }; | ||||
| const data = reactive<PageData<WorkWageForm, WorkWageQuery>>({ | ||||
|   form: { ...initFormData }, | ||||
|   queryParams: { | ||||
|     pageNum: 1, | ||||
|     pageSize: 10, | ||||
|     projectId: currentProject.value.id, | ||||
|     workType: undefined, | ||||
|     isSpecialType: undefined, | ||||
|     wageCalculationType: undefined, | ||||
|     wage: undefined, | ||||
|     wageMeasureUnit: undefined, | ||||
|     params: {} | ||||
|   }, | ||||
|   rules: { | ||||
|     id: [{ required: true, message: '主键id不能为空', trigger: 'blur' }] | ||||
|   } | ||||
| }); | ||||
|  | ||||
| const { queryParams, form, rules } = toRefs(data); | ||||
|  | ||||
| /** 查询工种薪水列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await listWorkWage(queryParams.value); | ||||
|   workWageList.value = res.rows; | ||||
|   total.value = res.total; | ||||
|   loading.value = false; | ||||
| }; | ||||
|  | ||||
| /** 取消按钮 */ | ||||
| const cancel = () => { | ||||
|   reset(); | ||||
|   dialog.visible = false; | ||||
| }; | ||||
|  | ||||
| /** 表单重置 */ | ||||
| const reset = () => { | ||||
|   form.value = { ...initFormData }; | ||||
|   workWageFormRef.value?.resetFields(); | ||||
| }; | ||||
|  | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.value.pageNum = 1; | ||||
|   getList(); | ||||
| }; | ||||
|  | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   handleQuery(); | ||||
| }; | ||||
|  | ||||
| /** 多选框选中数据 */ | ||||
| const handleSelectionChange = (selection: WorkWageVO[]) => { | ||||
|   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?: WorkWageVO) => { | ||||
|   reset(); | ||||
|   const _id = row?.id || ids.value[0]; | ||||
|   const res = await getWorkWage(_id); | ||||
|   Object.assign(form.value, res.data); | ||||
|   dialog.visible = true; | ||||
|   dialog.title = '修改工种薪水'; | ||||
| }; | ||||
|  | ||||
| /** 提交按钮 */ | ||||
| const submitForm = () => { | ||||
|   workWageFormRef.value?.validate(async (valid: boolean) => { | ||||
|     if (valid) { | ||||
|       buttonLoading.value = true; | ||||
|       form.value.projectId = currentProject.value.id; | ||||
|       if (form.value.id) { | ||||
|         await updateWorkWage(form.value).finally(() => (buttonLoading.value = false)); | ||||
|       } else { | ||||
|         await addWorkWage(form.value).finally(() => (buttonLoading.value = false)); | ||||
|       } | ||||
|       proxy?.$modal.msgSuccess('操作成功'); | ||||
|       dialog.visible = false; | ||||
|       await getList(); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| /** 删除按钮操作 */ | ||||
| const handleDelete = async (row?: WorkWageVO) => { | ||||
|   const _ids = row?.id || ids.value; | ||||
|   await proxy?.$modal.confirm('是否确认删除工种薪水编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false)); | ||||
|   await delWorkWage(_ids); | ||||
|   proxy?.$modal.msgSuccess('删除成功'); | ||||
|   await getList(); | ||||
| }; | ||||
|  | ||||
| /** 导出按钮操作 */ | ||||
| const handleExport = () => { | ||||
|   proxy?.download( | ||||
|     'project/workWage/export', | ||||
|     { | ||||
|       ...queryParams.value | ||||
|     }, | ||||
|     `workWage_${new Date().getTime()}.xlsx` | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| //监听项目id刷新数据 | ||||
| const listeningProject = watch( | ||||
|   () => currentProject.value.id, | ||||
|   (nid, oid) => { | ||||
|     queryParams.value.projectId = nid; | ||||
|     form.value.projectId = nid; | ||||
|     getList(); | ||||
|   } | ||||
| ); | ||||
|  | ||||
| onUnmounted(() => { | ||||
|   listeningProject(); | ||||
| }); | ||||
|  | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
| }); | ||||
| </script> | ||||
							
								
								
									
										247
									
								
								src/views/project/workerDailyReport/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								src/views/project/workerDailyReport/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,247 @@ | ||||
| <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="申请人" prop="userName"> | ||||
|               <el-input v-model="queryParams.userName" placeholder="请输入申请人" clearable @keyup.enter="handleQuery" /> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="日报类型" prop="isResubmit"> | ||||
|               <el-select v-model="queryParams.isResubmit" placeholder="全部" @change="handleQuery"> | ||||
|                 <el-option v-for="dict in reSubmitType" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="所属班组" prop="leaveType"> | ||||
|               <el-select v-model="queryParams.teamId" placeholder="全部" clearable> | ||||
|                 <el-option v-for="dict in ProjectTeam" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </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"> | ||||
|       <el-table v-loading="loading" :data="workerDailyReportList"> | ||||
|         <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" v-if="queryParams.isResubmit == '1'" /> | ||||
|         <el-table-column label="需协调与帮助" align="center" prop="coordinationHelp" /> | ||||
|         <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | ||||
|           <template #default="scope"> | ||||
|             <el-button link type="success" icon="View" @click="handleDetail(scope.row)">详情</el-button> | ||||
|           </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="日报打卡详情" v-model="dialog.visible" width="700"> | ||||
|       <div class="block_box"> | ||||
|         <span>基本信息</span> | ||||
|         <el-form label-width="130px"> | ||||
|           <el-row :gutter="20"> | ||||
|             <el-col :span="12"> | ||||
|               <el-form-item label="所在班组"> | ||||
|                 {{ form.teamName }} | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="12"> | ||||
|               <el-form-item label="日报日期"> | ||||
|                 {{ form.reportDate ? form.reportDate : '暂无' }} | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <el-col :span="12"> | ||||
|               <el-form-item label="补交理由"> | ||||
|                 {{ form.resubmitReason ? form.resubmitReason : '暂无' }} | ||||
|               </el-form-item> | ||||
|             </el-col> | ||||
|             <!-- <el-col :span="12"> | ||||
|               <el-form-item label="附件"> | ||||
|                 <span v-if="!form.fileList.length">暂无</span> | ||||
|                 <div v-else> | ||||
|                   <el-link target="_blank" :href="item" :underline="false" v-for="(item, index) in form.fileList" :key="index"> | ||||
|                     <img src="../../../../src/assets/icons/svg/PDF.png" alt="" class="w10 mr" /> | ||||
|                   </el-link> | ||||
|                 </div> | ||||
|               </el-form-item> | ||||
|             </el-col> --> | ||||
|           </el-row> | ||||
|         </el-form> | ||||
|       </div> | ||||
|       <div class="block_box"> | ||||
|         <span>日报明细</span> | ||||
|         <el-table :data="form.dailyPieceItemVoList" class="mt"> | ||||
|           <el-table-column type="index" width="50" /> | ||||
|           <el-table-column label="计件类型" align="center" prop="pieceType" /> | ||||
|           <el-table-column label="计件数量" align="center" prop="pieceCount" /> | ||||
|           <el-table-column label="计件单位" align="center" prop="pieceUnit" /> | ||||
|           <el-table-column label="备注" align="center" prop="remark" /> | ||||
|         </el-table> | ||||
|       </div> | ||||
|       <div class="block_box" v-if="form.fileList.length"> | ||||
|         <span>附件</span> | ||||
|         <div class="mt pl"> | ||||
|           <el-link target="_blank" :href="item" :underline="false" v-for="(item, index) in form.fileList" :key="index"> | ||||
|             <img v-if="getFileTypeFromUrl(item)" src="../../../../src/assets/icons/svg/PDF.png" alt="" class="w22 mr-xl" /> | ||||
|             <img v-else :src="item" class="w22 mr-xl" /> | ||||
|           </el-link> | ||||
|         </div> | ||||
|       </div> | ||||
|     </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 { SpecialType } from '@/api/project/workWage/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 reSubmitType = ref<SpecialType[]>([ | ||||
|   { | ||||
|     label: '打卡', | ||||
|     value: '1' | ||||
|   }, | ||||
|   { | ||||
|     label: '补卡', | ||||
|     value: '2' | ||||
|   } | ||||
| ]); | ||||
|  | ||||
| const initFormData: WorkerDailyReportForm = { | ||||
|   id: undefined, | ||||
|   projectId: currentProject.value?.id, | ||||
|   teamId: undefined, | ||||
|   userId: undefined, | ||||
|   userName: undefined, | ||||
|   todayCompletedWork: undefined, | ||||
|   unfinishedWork: undefined, | ||||
|   tomorrowWork: undefined, | ||||
|   coordinationHelp: undefined, | ||||
|   fileList: undefined, | ||||
|   isResubmit: 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: {}, | ||||
|     isResubmit: '1' | ||||
|   } | ||||
| }); | ||||
|  | ||||
| const { queryParams, form } = toRefs(data); | ||||
|  | ||||
| const getFileTypeFromUrl = computed(() => (url) => { | ||||
|   const regex = /\.([a-zA-Z0-9]+)(?:[?#]|$)/; | ||||
|   const match = url.match(regex); | ||||
|   if (match[1]?.toLowerCase() == 'pdf') { | ||||
|     return true; | ||||
|   } | ||||
|   return false; | ||||
| }); | ||||
|  | ||||
| /** 查询施工人员日报列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await listWorkerDailyReport(queryParams.value); | ||||
|   workerDailyReportList.value = res.rows; | ||||
|   total.value = res.total; | ||||
|   loading.value = false; | ||||
| }; | ||||
|  | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.value.pageNum = 1; | ||||
|   getList(); | ||||
| }; | ||||
|  | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   handleQuery(); | ||||
| }; | ||||
|  | ||||
| /** 详情按钮操作 */ | ||||
| const handleDetail = async (row?: WorkerDailyReportVO) => { | ||||
|   const _id = row?.id || ids.value[0]; | ||||
|   const res = await getWorkerDailyReport(_id); | ||||
|   Object.assign(form.value, res.data); | ||||
|   dialog.visible = true; | ||||
|   dialog.title = '修改施工人员日报'; | ||||
| }; | ||||
|  | ||||
| //监听项目id刷新数据 | ||||
| const listeningProject = watch( | ||||
|   () => currentProject.value.id, | ||||
|   (nid, oid) => { | ||||
|     queryParams.value.projectId = nid; | ||||
|     form.value.projectId = nid; | ||||
|     getList(); | ||||
|   } | ||||
| ); | ||||
|  | ||||
| onUnmounted(() => { | ||||
|   listeningProject(); | ||||
| }); | ||||
|  | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
| }); | ||||
| </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> | ||||
		Reference in New Issue
	
	Block a user