|
|
@@ -0,0 +1,463 @@
|
|
|
+<template>
|
|
|
+ <ContentWrap>
|
|
|
+ <!-- 搜索工作栏 -->
|
|
|
+ <el-form
|
|
|
+ class="-mb-15px"
|
|
|
+ :model="queryParams"
|
|
|
+ ref="queryFormRef"
|
|
|
+ :inline="true"
|
|
|
+ label-width="68px"
|
|
|
+ >
|
|
|
+ <el-form-item label="项目" prop="projectId">
|
|
|
+ <el-input
|
|
|
+ v-model="queryParams.projectId"
|
|
|
+ placeholder="请输入项目"
|
|
|
+ clearable
|
|
|
+ @keyup.enter="handleQuery"
|
|
|
+ class="!w-240px"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="任务" prop="taskId">
|
|
|
+ <el-input
|
|
|
+ v-model="queryParams.taskId"
|
|
|
+ placeholder="请输入任务"
|
|
|
+ clearable
|
|
|
+ @keyup.enter="handleQuery"
|
|
|
+ class="!w-240px"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="创建时间" prop="createTime">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="queryParams.createTime"
|
|
|
+ value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
+ type="daterange"
|
|
|
+ start-placeholder="开始日期"
|
|
|
+ end-placeholder="结束日期"
|
|
|
+ :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
|
|
+ class="!w-220px"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
|
|
+ <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ plain
|
|
|
+ @click="openForm('create')"
|
|
|
+ v-hasPermi="['pms:iot-rd-daily-report:create']"
|
|
|
+ >
|
|
|
+ <Icon icon="ep:plus" class="mr-5px" /> 新增
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ type="success"
|
|
|
+ plain
|
|
|
+ @click="handleExport"
|
|
|
+ :loading="exportLoading"
|
|
|
+ v-hasPermi="['pms:iot-rd-daily-report:export']"
|
|
|
+ >
|
|
|
+ <Icon icon="ep:download" class="mr-5px" /> 导出
|
|
|
+ </el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </ContentWrap>
|
|
|
+
|
|
|
+ <!-- 列表 -->
|
|
|
+ <ContentWrap ref="tableContainerRef">
|
|
|
+ <el-table ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
|
|
+ <el-table-column label="项目部" align="center" prop="projectDeptName" :width="columnWidths.deptName"/>
|
|
|
+ <el-table-column label="队伍" align="center" prop="deptName" :width="columnWidths.contractName"/>
|
|
|
+ <el-table-column label="甲方" align="center" prop="manufactureName" :width="columnWidths.taskName"/>
|
|
|
+ <el-table-column label="井号" align="center" prop="wellName" :width="columnWidths.timeRange"/>
|
|
|
+ <el-table-column label="工艺" align="center" prop="techniques" :width="columnWidths.timeRange"/>
|
|
|
+ <el-table-column label="总工作量" align="center" prop="workloadDesign" :width="columnWidths.timeRange"/>
|
|
|
+ <!-- 已完成工作量分组列 -->
|
|
|
+ <el-table-column label="已完成工作量" align="center">
|
|
|
+ <!-- 动态生成列 -->
|
|
|
+ <el-table-column
|
|
|
+ v-for="column in dynamicColumns"
|
|
|
+ :key="column"
|
|
|
+ :label="column"
|
|
|
+ :prop="column"
|
|
|
+ align="center"
|
|
|
+ min-width="120"
|
|
|
+ >
|
|
|
+ <template #default="scope">
|
|
|
+ {{ getWorkloadByUnit(scope.row.items, column) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
+ <!--
|
|
|
+ <el-table-column label="操作" align="center" min-width="120px" fixed="right">
|
|
|
+ <template #default="scope">
|
|
|
+
|
|
|
+ <el-button
|
|
|
+ link
|
|
|
+ type="primary"
|
|
|
+ @click="openForm('update', scope.row.id)"
|
|
|
+ v-hasPermi="['pms:iot-rd-daily-report:update']"
|
|
|
+ >
|
|
|
+ 编辑
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ link
|
|
|
+ type="warning"
|
|
|
+ @click="handleApprove(scope.row.id)"
|
|
|
+ v-hasPermi="['pms:iot-rd-daily-report:update']"
|
|
|
+ >
|
|
|
+ 审批
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column> -->
|
|
|
+ </el-table>
|
|
|
+ <!-- 分页 -->
|
|
|
+ <Pagination
|
|
|
+ :total="total"
|
|
|
+ v-model:page="queryParams.pageNo"
|
|
|
+ v-model:limit="queryParams.pageSize"
|
|
|
+ @pagination="getList"
|
|
|
+ />
|
|
|
+ </ContentWrap>
|
|
|
+
|
|
|
+ <!-- 表单弹窗:添加/修改 -->
|
|
|
+ <IotRdDailyReportForm ref="formRef" @success="getList" />
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { dateFormatter } from '@/utils/formatTime'
|
|
|
+import download from '@/utils/download'
|
|
|
+import { IotRdDailyReportApi, IotRdDailyReportVO } from '@/api/pms/iotrddailyreport'
|
|
|
+import IotRdDailyReportForm from './IotRdDailyReportForm.vue'
|
|
|
+import {DICT_TYPE} from "@/utils/dict";
|
|
|
+import { ref, reactive, onMounted, computed } from 'vue'
|
|
|
+
|
|
|
+/** 瑞都日报 汇总统计 */
|
|
|
+defineOptions({ name: 'IotRdDailyReportStatistics' })
|
|
|
+
|
|
|
+const message = useMessage() // 消息弹窗
|
|
|
+const { t } = useI18n() // 国际化
|
|
|
+const { push } = useRouter() // 路由跳转
|
|
|
+const loading = ref(true) // 列表的加载中
|
|
|
+const list = ref<IotRdDailyReportVO[]>([]) // 列表的数据
|
|
|
+const total = ref(0) // 列表的总页数
|
|
|
+const queryParams = reactive({
|
|
|
+ pageNo: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ deptId: undefined,
|
|
|
+ projectId: undefined,
|
|
|
+ taskId: undefined,
|
|
|
+ projectClassification: undefined,
|
|
|
+ techniqueIds: undefined,
|
|
|
+ deviceIds: undefined,
|
|
|
+ startTime: [],
|
|
|
+ endTime: [],
|
|
|
+ cumulativeWorkingWell: undefined,
|
|
|
+ cumulativeWorkingLayers: undefined,
|
|
|
+ dailyPumpTrips: undefined,
|
|
|
+ dailyToolsSand: undefined,
|
|
|
+ runCount: undefined,
|
|
|
+ bridgePlug: undefined,
|
|
|
+ waterVolume: undefined,
|
|
|
+ hourCount: undefined,
|
|
|
+ dailyFuel: undefined,
|
|
|
+ dailyPowerUsage: undefined,
|
|
|
+ productionTime: [],
|
|
|
+ nonProductionTime: [],
|
|
|
+ rdNptReason: undefined,
|
|
|
+ constructionStartDate: [],
|
|
|
+ constructionEndDate: [],
|
|
|
+ productionStatus: undefined,
|
|
|
+ externalRental: undefined,
|
|
|
+ nextPlan: undefined,
|
|
|
+ rdStatus: undefined,
|
|
|
+ malfunction: undefined,
|
|
|
+ faultDowntime: [],
|
|
|
+ personnel: undefined,
|
|
|
+ totalStaffNum: undefined,
|
|
|
+ leaveStaffNum: undefined,
|
|
|
+ extProperty: undefined,
|
|
|
+ sort: undefined,
|
|
|
+ remark: undefined,
|
|
|
+ status: undefined,
|
|
|
+ processInstanceId: undefined,
|
|
|
+ auditStatus: undefined,
|
|
|
+ createTime: [],
|
|
|
+})
|
|
|
+const queryFormRef = ref() // 搜索的表单
|
|
|
+const exportLoading = ref(false) // 导出的加载中
|
|
|
+
|
|
|
+// 表格引用
|
|
|
+const tableRef = ref()
|
|
|
+// 表格容器引用
|
|
|
+const tableContainerRef = ref()
|
|
|
+
|
|
|
+// 计算属性:获取所有动态列(去重的unit)
|
|
|
+const dynamicColumns = computed(() => {
|
|
|
+ const units = new Set()
|
|
|
+ list.value.forEach(item => {
|
|
|
+ item.items.forEach(subItem => {
|
|
|
+ if (subItem.unit) {
|
|
|
+ units.add(subItem.unit)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ return Array.from(units)
|
|
|
+})
|
|
|
+
|
|
|
+// 根据unit获取对应workload
|
|
|
+const getWorkloadByUnit = (items, unit) => {
|
|
|
+ if (!items || !Array.isArray(items)) return ''
|
|
|
+ const targetItem = items.find(item => item.unit === unit)
|
|
|
+ return targetItem ? targetItem.workload : ''
|
|
|
+}
|
|
|
+
|
|
|
+// 列宽度配置
|
|
|
+const columnWidths = ref({
|
|
|
+ id: '80px',
|
|
|
+ deptName: '120px',
|
|
|
+ contractName: '120px',
|
|
|
+ taskName: '120px',
|
|
|
+ timeRange: '120px',
|
|
|
+ rdStatus: '120px',
|
|
|
+ cumulativeWorkingWell: '120px',
|
|
|
+ cumulativeWorkingLayers: '120px',
|
|
|
+ dailyPumpTrips: '120px',
|
|
|
+ dailyToolsSand: '120px',
|
|
|
+ runCount: '80px',
|
|
|
+ bridgePlug: '80px',
|
|
|
+ waterVolume: '100px',
|
|
|
+ hourCount: '80px',
|
|
|
+ constructionStartDate: '180px',
|
|
|
+ constructionEndDate: '180px',
|
|
|
+ productionStatus: '200px',
|
|
|
+ nextPlan: '200px',
|
|
|
+ externalRental: '120px',
|
|
|
+ malfunction: '150px',
|
|
|
+ faultDowntime: '120px',
|
|
|
+ createTime: '180px',
|
|
|
+ operation: '120px'
|
|
|
+})
|
|
|
+
|
|
|
+// 计算文本宽度
|
|
|
+const getTextWidth = (text: string, fontSize = 14) => {
|
|
|
+ const span = document.createElement('span');
|
|
|
+ span.style.visibility = 'hidden';
|
|
|
+ span.style.position = 'absolute';
|
|
|
+ span.style.whiteSpace = 'nowrap';
|
|
|
+ span.style.fontSize = `${fontSize}px`;
|
|
|
+ span.style.fontFamily = 'inherit';
|
|
|
+ span.innerText = text;
|
|
|
+
|
|
|
+ document.body.appendChild(span);
|
|
|
+ const width = span.offsetWidth;
|
|
|
+ document.body.removeChild(span);
|
|
|
+
|
|
|
+ return width;
|
|
|
+};
|
|
|
+
|
|
|
+// 计算列宽度
|
|
|
+const calculateColumnWidths = () => {
|
|
|
+ const MIN_WIDTH = 80; // 最小列宽
|
|
|
+ const PADDING = 25; // 列内边距
|
|
|
+
|
|
|
+ // 确保表格容器存在
|
|
|
+ if (!tableContainerRef.value?.$el) return;
|
|
|
+
|
|
|
+ const newWidths: Record<string, string> = {};
|
|
|
+
|
|
|
+ // 计算各列宽度的函数
|
|
|
+ const calculateColumnWidth = (key: string, label: string, getValue: Function) => {
|
|
|
+ const headerWidth = getTextWidth(label) + PADDING;
|
|
|
+ let contentMaxWidth = MIN_WIDTH;
|
|
|
+
|
|
|
+ // 计算内容最大宽度
|
|
|
+ list.value.forEach((row, index) => {
|
|
|
+ let text = '';
|
|
|
+ if (key === 'rdStatus') {
|
|
|
+ // 特殊处理字典列,这里简化处理,实际应该获取字典标签
|
|
|
+ text = String(row[key] || '');
|
|
|
+ } else if (key.includes('Date') || key === 'createTime') {
|
|
|
+ // 日期列使用格式化后的值
|
|
|
+ text = dateFormatter(null, null, row[key]) || '';
|
|
|
+ } else {
|
|
|
+ text = String(getValue ? getValue(row, index) : (row[key] || ''));
|
|
|
+ }
|
|
|
+
|
|
|
+ const textWidth = getTextWidth(text) + PADDING;
|
|
|
+ if (textWidth > contentMaxWidth) contentMaxWidth = textWidth;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 取标题宽度和内容最大宽度的较大值
|
|
|
+ const finalWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH);
|
|
|
+ newWidths[key] = `${finalWidth}px`;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 计算各列宽度
|
|
|
+ calculateColumnWidth('deptName', '施工队伍', (row: any) => row.deptName);
|
|
|
+ calculateColumnWidth('contractName', '项目', (row: any) => row.contractName);
|
|
|
+ calculateColumnWidth('taskName', '任务', (row: any) => row.taskName);
|
|
|
+ calculateColumnWidth('timeRange', '时间节点', (row: any) => row.timeRange);
|
|
|
+ calculateColumnWidth('rdStatus', t('project.status'), (row: any) => row.rdStatus);
|
|
|
+ calculateColumnWidth('cumulativeWorkingWell', '累计施工井', (row: any) => row.cumulativeWorkingWell);
|
|
|
+ calculateColumnWidth('cumulativeWorkingLayers', '累计施工层', (row: any) => row.cumulativeWorkingLayers);
|
|
|
+ calculateColumnWidth('dailyPumpTrips', '当日泵车台次', (row: any) => row.dailyPumpTrips);
|
|
|
+ calculateColumnWidth('dailyToolsSand', '当日仪表/混砂', (row: any) => row.dailyToolsSand);
|
|
|
+ calculateColumnWidth('runCount', '趟数', (row: any) => row.runCount);
|
|
|
+ calculateColumnWidth('bridgePlug', '桥塞', (row: any) => row.bridgePlug);
|
|
|
+ calculateColumnWidth('waterVolume', '水方量', (row: any) => row.waterVolume);
|
|
|
+ calculateColumnWidth('hourCount', '时间H', (row: any) => row.hourCount);
|
|
|
+ calculateColumnWidth('constructionStartDate', '施工开始日期', (row: any) => dateFormatter(null, null, row.constructionStartDate));
|
|
|
+ calculateColumnWidth('constructionEndDate', '施工结束日期', (row: any) => dateFormatter(null, null, row.constructionEndDate));
|
|
|
+ calculateColumnWidth('productionStatus', '当日生产动态', (row: any) => row.productionStatus);
|
|
|
+ calculateColumnWidth('nextPlan', '下步工作计划', (row: any) => row.nextPlan);
|
|
|
+ calculateColumnWidth('externalRental', '外租设备', (row: any) => row.externalRental);
|
|
|
+ calculateColumnWidth('malfunction', '故障情况', (row: any) => row.malfunction);
|
|
|
+ calculateColumnWidth('faultDowntime', '故障误工H', (row: any) => row.faultDowntime);
|
|
|
+ calculateColumnWidth('createTime', '创建时间', (row: any) => dateFormatter(null, null, row.createTime));
|
|
|
+
|
|
|
+ // 操作列固定宽度
|
|
|
+ newWidths.operation = '120px';
|
|
|
+ // id列固定宽度(虽然隐藏)
|
|
|
+ newWidths.id = '80px';
|
|
|
+
|
|
|
+ // 更新列宽配置
|
|
|
+ columnWidths.value = newWidths;
|
|
|
+
|
|
|
+ // 触发表格重新布局
|
|
|
+ nextTick(() => {
|
|
|
+ tableRef.value?.doLayout();
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+/** 查询列表 */
|
|
|
+const getList = async () => {
|
|
|
+ loading.value = true
|
|
|
+ try {
|
|
|
+ const data = await IotRdDailyReportApi.statistics(queryParams)
|
|
|
+ list.value = data
|
|
|
+ // total.value = data.total
|
|
|
+ // 获取数据后计算列宽
|
|
|
+ nextTick(() => {
|
|
|
+ calculateColumnWidths();
|
|
|
+ });
|
|
|
+ } finally {
|
|
|
+ loading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 搜索按钮操作 */
|
|
|
+const handleQuery = () => {
|
|
|
+ queryParams.pageNo = 1
|
|
|
+ getList()
|
|
|
+}
|
|
|
+
|
|
|
+/** 重置按钮操作 */
|
|
|
+const resetQuery = () => {
|
|
|
+ queryFormRef.value.resetFields()
|
|
|
+ handleQuery()
|
|
|
+}
|
|
|
+
|
|
|
+/** 添加/修改操作 */
|
|
|
+const formRef = ref()
|
|
|
+const openForm = (type: string, id?: number) => {
|
|
|
+ formRef.value.open(type, id)
|
|
|
+}
|
|
|
+
|
|
|
+/** 审批按钮操作 */
|
|
|
+const handleApprove = async (id: number) => {
|
|
|
+ try {
|
|
|
+ // 跳转到 FillDailyReportForm 页面,传递审批模式和ID
|
|
|
+ push({
|
|
|
+ name: 'FillDailyReportForm',
|
|
|
+ params: {
|
|
|
+ id: id.toString(),
|
|
|
+ mode: 'approval' // 添加审批模式标识
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } catch (error) {
|
|
|
+ console.error('跳转审批页面失败:', error)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 删除按钮操作 */
|
|
|
+const handleDelete = async (id: number) => {
|
|
|
+ try {
|
|
|
+ // 删除的二次确认
|
|
|
+ await message.delConfirm()
|
|
|
+ // 发起删除
|
|
|
+ await IotRdDailyReportApi.deleteIotRdDailyReport(id)
|
|
|
+ message.success(t('common.delSuccess'))
|
|
|
+ // 刷新列表
|
|
|
+ await getList()
|
|
|
+ } catch {}
|
|
|
+}
|
|
|
+
|
|
|
+/** 导出按钮操作 */
|
|
|
+const handleExport = async () => {
|
|
|
+ try {
|
|
|
+ // 导出的二次确认
|
|
|
+ await message.exportConfirm()
|
|
|
+ // 发起导出
|
|
|
+ exportLoading.value = true
|
|
|
+ const data = await IotRdDailyReportApi.exportIotRdDailyReport(queryParams)
|
|
|
+ download.excel(data, '瑞都日报.xls')
|
|
|
+ } catch {
|
|
|
+ } finally {
|
|
|
+ exportLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 声明 ResizeObserver 实例
|
|
|
+let resizeObserver: ResizeObserver | null = null;
|
|
|
+
|
|
|
+/** 初始化 **/
|
|
|
+onMounted(() => {
|
|
|
+ getList()
|
|
|
+ // 创建 ResizeObserver 监听表格容器尺寸变化
|
|
|
+ if (tableContainerRef.value?.$el) {
|
|
|
+ resizeObserver = new ResizeObserver(() => {
|
|
|
+ // 使用防抖避免频繁触发
|
|
|
+ clearTimeout((window as any).resizeTimer);
|
|
|
+ (window as any).resizeTimer = setTimeout(() => {
|
|
|
+ calculateColumnWidths();
|
|
|
+ }, 100);
|
|
|
+ });
|
|
|
+ resizeObserver.observe(tableContainerRef.value.$el);
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+onUnmounted(() => {
|
|
|
+ // 清除 ResizeObserver
|
|
|
+ if (resizeObserver && tableContainerRef.value?.$el) {
|
|
|
+ resizeObserver.unobserve(tableContainerRef.value.$el);
|
|
|
+ resizeObserver = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 清除定时器
|
|
|
+ if ((window as any).resizeTimer) {
|
|
|
+ clearTimeout((window as any).resizeTimer);
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// 监听列表数据变化重新计算列宽
|
|
|
+watch(list, () => {
|
|
|
+ nextTick(calculateColumnWidths)
|
|
|
+}, { deep: true })
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+/* 确保表格单元格内容不换行 */
|
|
|
+:deep(.el-table .cell) {
|
|
|
+white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+/* 确保表格列标题不换行 */
|
|
|
+:deep(.el-table th > .cell) {
|
|
|
+white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+/* 调整表格最小宽度,确保内容完全显示 */
|
|
|
+:deep(.el-table) {
|
|
|
+min-width: 100%;
|
|
|
+}
|
|
|
+</style>
|