|
|
@@ -0,0 +1,2105 @@
|
|
|
+<template>
|
|
|
+ <ContentWrap v-loading="formLoading">
|
|
|
+ <!-- 第一部分:日报标题 -->
|
|
|
+ <div class="daily-report-title">
|
|
|
+ <h2>{{ pageTitle }}</h2>
|
|
|
+ <!-- 在审批模式下显示审批状态提示 -->
|
|
|
+ <div v-if="isReadonlyMode" class="approval-notice">
|
|
|
+ <el-alert :title="modeNotice" type="info" :closable="false" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 第二部分:项目/任务信息 -->
|
|
|
+ <ContentWrap>
|
|
|
+ <div class="info-table" style="margin-top: 1em">
|
|
|
+ <!-- 表格行 -->
|
|
|
+ <div class="table-row">
|
|
|
+ <div class="table-cell">
|
|
|
+ <div class="cell-content">
|
|
|
+ <span class="cell-label">甲方:</span>
|
|
|
+ <!-- 甲方字段:添加 single-line-ellipsis 类 + title 绑定完整内容 -->
|
|
|
+ <span
|
|
|
+ class="cell-value single-line-ellipsis"
|
|
|
+ :title="dailyReportData.manufactureName || '-'"
|
|
|
+ >
|
|
|
+ {{ dailyReportData.manufactureName || '-' }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="table-cell">
|
|
|
+ <div class="cell-content">
|
|
|
+ <span class="cell-label">合同号:</span>
|
|
|
+ <span class="cell-value">{{ dailyReportData.contractName || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="table-cell">
|
|
|
+ <div class="cell-content">
|
|
|
+ <span class="cell-label">井号:</span>
|
|
|
+ <span class="cell-value">{{ dailyReportData.wellName || dailyReportData.taskName || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="table-row">
|
|
|
+ <div class="table-cell">
|
|
|
+ <div class="cell-content">
|
|
|
+ <span class="cell-label">施工队伍:</span>
|
|
|
+ <span class="cell-value">{{ dailyReportData.deptName || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="table-cell">
|
|
|
+ <div class="cell-content">
|
|
|
+ <span class="cell-label">施工地点:</span>
|
|
|
+ <span class="cell-value">{{ dailyReportData.location || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="table-cell">
|
|
|
+ <div class="cell-content">
|
|
|
+ <span class="cell-label">工艺:</span>
|
|
|
+ <span class="cell-value">{{ dailyReportData.techniqueNames || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="table-row">
|
|
|
+ <div class="table-cell">
|
|
|
+ <div class="cell-content">
|
|
|
+ <span class="cell-label">搬迁日期:</span>
|
|
|
+ <span class="cell-value">{{ formatDate(dailyReportData.dpDate) || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="table-cell">
|
|
|
+ <div class="cell-content">
|
|
|
+ <span class="cell-label">开工日期:</span>
|
|
|
+ <span class="cell-value">{{ formatDate(dailyReportData.sgDate) || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="table-cell">
|
|
|
+ <div class="cell-content">
|
|
|
+ <span class="cell-label">完工日期:</span>
|
|
|
+ <span class="cell-value">{{ formatDate(dailyReportData.wgDate) || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="table-row">
|
|
|
+ <div class="table-cell">
|
|
|
+ <div class="cell-content">
|
|
|
+ <span class="cell-label">施工周期D:</span>
|
|
|
+ <span class="cell-value">{{ constructionPeriod || 0 }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="table-cell">
|
|
|
+ <div class="cell-content">
|
|
|
+ <span class="cell-label">停待时间D:</span>
|
|
|
+ <span class="cell-value">{{ dailyReportData.faultDowntime || 0 }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="table-cell">
|
|
|
+ <div class="cell-content">
|
|
|
+ <span class="cell-label">带班干部:</span>
|
|
|
+ <span class="cell-value">{{ dailyReportData.responsiblePersonNames || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 第五行:设备配置(单独一行) -->
|
|
|
+ <div class="table-row">
|
|
|
+ <div class="table-cell full-width">
|
|
|
+ <div class="cell-content">
|
|
|
+ <span class="cell-label">设备配置:</span>
|
|
|
+ <span class="cell-value indent-multiline">{{ dailyReportData.deviceNames || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </ContentWrap>
|
|
|
+
|
|
|
+ <!-- 第三部分:日报填报表单 -->
|
|
|
+ <ContentWrap class="section-padding">
|
|
|
+ <el-form
|
|
|
+ ref="formRef"
|
|
|
+ :model="formData"
|
|
|
+ :rules="isReadonlyMode ? {} : formRules"
|
|
|
+ v-loading="formLoading"
|
|
|
+ style="margin-top: 1em"
|
|
|
+ label-width="200px"
|
|
|
+ :disabled="isReadonlyMode"
|
|
|
+ >
|
|
|
+ <!-- 第一行:时间节点、施工状态 -->
|
|
|
+ <el-row :gutter="30">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="时间节点" prop="timeRange">
|
|
|
+ <el-time-picker
|
|
|
+ is-range
|
|
|
+ v-model="formData.timeRange"
|
|
|
+ range-separator="至"
|
|
|
+ start-placeholder="开始时间"
|
|
|
+ end-placeholder="结束时间"
|
|
|
+ placeholder="选择时间范围"
|
|
|
+ style="width: 100%"
|
|
|
+ :readonly="isReadonlyMode"
|
|
|
+ :disabled="isReadonlyMode"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="施工状态" prop="rdStatus">
|
|
|
+ <el-select v-model="formData.rdStatus" placeholder="请选择施工状态"
|
|
|
+ style="width: 100%" :disabled="isReadonlyMode">
|
|
|
+ <el-option
|
|
|
+ v-for="dict in rdStatusOptions"
|
|
|
+ :key="dict.value"
|
|
|
+ :label="dict.label"
|
|
|
+ :value="dict.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 平台井 -->
|
|
|
+ <el-row v-if="showPlatformField">
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="平台井" prop="platformId">
|
|
|
+ <el-select
|
|
|
+ v-model="formData.platformId"
|
|
|
+ placeholder="请选择平台井"
|
|
|
+ style="width: 100%"
|
|
|
+ :disabled="isReadonlyMode"
|
|
|
+ >
|
|
|
+ <el-option
|
|
|
+ v-for="platform in platformOptions"
|
|
|
+ :key="platform.id"
|
|
|
+ :label="platform.wellName"
|
|
|
+ :value="platform.id"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 施工设备字段 -->
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="施工设备" prop="deviceIds">
|
|
|
+ <!-- 编辑模式:显示选择按钮 -->
|
|
|
+ <template v-if="isEditMode">
|
|
|
+ <el-button
|
|
|
+ @click="openDeviceDialog"
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ :disabled="formLoading"
|
|
|
+ >
|
|
|
+ 选择设备
|
|
|
+ </el-button>
|
|
|
+ <el-tooltip
|
|
|
+ v-if="formData.deviceIds && formData.deviceIds.length > 0"
|
|
|
+ :content="getAllDeviceNamesForDisplay"
|
|
|
+ placement="top"
|
|
|
+ >
|
|
|
+ <span class="device-display-container">
|
|
|
+ {{ formatDevicesForDisplay }}
|
|
|
+ </span>
|
|
|
+ </el-tooltip>
|
|
|
+ <span v-else class="no-device">
|
|
|
+ 未选择设备
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 只读模式:只显示设备信息 -->
|
|
|
+ <template v-else>
|
|
|
+ <el-tooltip
|
|
|
+ v-if="formData.deviceIds && formData.deviceIds.length > 0"
|
|
|
+ :content="getAllDeviceNamesForDisplay"
|
|
|
+ placement="top"
|
|
|
+ >
|
|
|
+ <span class="device-display-container">
|
|
|
+ {{ formatDevicesForDisplay }}
|
|
|
+ </span>
|
|
|
+ </el-tooltip>
|
|
|
+ <span v-else class="no-device">-</span>
|
|
|
+ </template>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 未施工设备 -->
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="未施工设备" prop="unSelectedDeviceNames">
|
|
|
+ <el-input
|
|
|
+ v-model="unSelectedDeviceNames"
|
|
|
+ type="textarea"
|
|
|
+ :rows="2"
|
|
|
+ placeholder="未施工的设备将显示在这里"
|
|
|
+ :readonly="true"
|
|
|
+ class="unselected-device"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 第二行:施工工艺 -->
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="施工工艺" prop="techniqueIds">
|
|
|
+ <el-select v-model="formData.techniqueIds" placeholder="请选择施工工艺"
|
|
|
+ style="width: 100%" multiple :disabled="isReadonlyMode">
|
|
|
+ <el-option
|
|
|
+ v-for="dict in techniqueOptions"
|
|
|
+ :key="dict.value"
|
|
|
+ :label="dict.label"
|
|
|
+ :value="dict.value"
|
|
|
+ />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 动态属性区域:施工工艺与当日生产动态之间 -->
|
|
|
+ <el-row v-if="dynamicAttrs.length > 0" :gutter="30">
|
|
|
+ <el-col
|
|
|
+ v-for="attr in dynamicAttrs"
|
|
|
+ :key="attr.id"
|
|
|
+ :span="attr.dataType === 'textarea' ? 24 : 12"
|
|
|
+ >
|
|
|
+ <el-form-item
|
|
|
+ :label="attr.name + (attr.unit ? `(${attr.unit})` : '')"
|
|
|
+ :prop="'dynamicFields.' + attr.identifier"
|
|
|
+ :rules="isReadonlyMode ? [] : getDynamicAttrRules(attr)"
|
|
|
+ >
|
|
|
+ <!-- 文本类型 -->
|
|
|
+ <el-input
|
|
|
+ v-if="attr.dataType === 'text'"
|
|
|
+ v-model="formData.dynamicFields[attr.identifier]"
|
|
|
+ :placeholder="`请输入${attr.name}`"
|
|
|
+ :readonly="isReadonlyMode"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 文本域类型 -->
|
|
|
+ <el-input
|
|
|
+ v-else-if="attr.dataType === 'textarea'"
|
|
|
+ v-model="formData.dynamicFields[attr.identifier]"
|
|
|
+ :placeholder="`请输入${attr.name}`"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ :readonly="isReadonlyMode"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 数字类型 -->
|
|
|
+ <el-input
|
|
|
+ v-else-if="attr.dataType === 'double'"
|
|
|
+ v-model="formData.dynamicFields[attr.identifier]"
|
|
|
+ :placeholder="`请输入${attr.name}`"
|
|
|
+ type="number"
|
|
|
+ :min="attr.minValue || undefined"
|
|
|
+ :max="attr.maxValue || undefined"
|
|
|
+ :readonly="isReadonlyMode"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 日期类型 -->
|
|
|
+ <el-date-picker
|
|
|
+ v-else-if="attr.dataType === 'date'"
|
|
|
+ v-model="formData.dynamicFields[attr.identifier]"
|
|
|
+ type="date"
|
|
|
+ value-format="x"
|
|
|
+ :placeholder="`选择${attr.name}`"
|
|
|
+ style="width: 100%"
|
|
|
+ :readonly="isReadonlyMode"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 默认文本输入 -->
|
|
|
+ <el-input
|
|
|
+ v-else
|
|
|
+ v-model="formData.dynamicFields[attr.identifier]"
|
|
|
+ :placeholder="`请输入${attr.name}`"
|
|
|
+ :readonly="isReadonlyMode"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 第三行:当日生产动态 -->
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="当日生产动态" prop="productionStatus">
|
|
|
+ <el-input
|
|
|
+ v-model="formData.productionStatus"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ placeholder="请输入当日生产动态"
|
|
|
+ :readonly="isReadonlyMode"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 第四行:下步工作计划 -->
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="下步工作计划" prop="nextPlan">
|
|
|
+ <el-input
|
|
|
+ v-model="formData.nextPlan"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ placeholder="请输入下步工作计划"
|
|
|
+ :readonly="isReadonlyMode"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 第五行:外租设备 -->
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="外租设备" prop="externalRental">
|
|
|
+ <el-input
|
|
|
+ v-model="formData.externalRental"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ placeholder="请输入外租设备信息"
|
|
|
+ :readonly="isReadonlyMode"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 故障情况 -->
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="故障情况" prop="malfunction">
|
|
|
+ <el-input
|
|
|
+ v-model="formData.malfunction"
|
|
|
+ type="textarea"
|
|
|
+ :rows="3"
|
|
|
+ placeholder="请输入故障情况"
|
|
|
+ :readonly="isReadonlyMode"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 故障误工H -->
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="故障误工H" prop="faultDowntime">
|
|
|
+ <el-input
|
|
|
+ v-model="formData.faultDowntime"
|
|
|
+ type="number"
|
|
|
+ :rows="3"
|
|
|
+ placeholder="请输入故障误工H"
|
|
|
+ :readonly="isReadonlyMode"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 第六行:上传附件 -->
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="附件">
|
|
|
+ <!-- 文件上传组件 -->
|
|
|
+ <FileUpload
|
|
|
+ v-if="!isReadonlyMode"
|
|
|
+ ref="fileUploadRef"
|
|
|
+ :device-id="deviceId"
|
|
|
+ :show-folder-button="false"
|
|
|
+ @upload-success="handleUploadSuccess"
|
|
|
+ />
|
|
|
+
|
|
|
+ <!-- 已上传附件列表显示 -->
|
|
|
+ <div v-if="formData.attachments && formData.attachments.length > 0" class="attachment-container">
|
|
|
+ <div class="attachment-list">
|
|
|
+ <div
|
|
|
+ v-for="(attachment, index) in formData.attachments"
|
|
|
+ :key="attachment.id || index"
|
|
|
+ class="attachment-item"
|
|
|
+ >
|
|
|
+ <!-- 为附件名称添加点击事件,传递整个附件对象 -->
|
|
|
+ <a class="attachment-name" @click="inContent(attachment)">
|
|
|
+ {{ attachment.filename }}
|
|
|
+ </a>
|
|
|
+ <el-button
|
|
|
+ v-if="!isReadonlyMode"
|
|
|
+ type="danger"
|
|
|
+ link
|
|
|
+ size="small"
|
|
|
+ @click="removeAttachment(index)"
|
|
|
+ >
|
|
|
+ 删除
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 审批模式下只显示附件列表 -->
|
|
|
+ <div v-else-if="isApprovalMode && (!formData.attachments || formData.attachments.length === 0)" class="no-attachment">
|
|
|
+ 无附件
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ </el-form>
|
|
|
+ </ContentWrap>
|
|
|
+
|
|
|
+ <!-- 平台井工作量区域 - 只在平台井的详情或审批模式下显示 -->
|
|
|
+ <ContentWrap class="platform-workload-section" v-if="(isDetailMode || isApprovalMode) && dailyReportData?.platformWell === 1">
|
|
|
+ <h2 class="text-lg font-semibold mb-4">平台井工作量</h2>
|
|
|
+
|
|
|
+ <div class="platform-workload-table" v-if="dailyReportData?.platforms && dailyReportData.platforms.length > 0">
|
|
|
+ <el-table :data="dailyReportData.platforms" border style="width: 100%" class="platform-workload-el-table" table-layout="fixed">
|
|
|
+ <!-- 固定列 -->
|
|
|
+ <el-table-column prop="wellName" label="井号" align="center" :show-overflow-tooltip="true"/>
|
|
|
+ <el-table-column label="施工状态" align="center" :show-overflow-tooltip="true">
|
|
|
+ <template #default="scope">
|
|
|
+ {{ scope.row.rdStatusLabel || '' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="施工工艺" align="center" :show-overflow-tooltip="true">
|
|
|
+ <template #default="scope">
|
|
|
+ {{ scope.row.techniqueNames || '' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
+ <!-- 动态工作量列 -->
|
|
|
+ <el-table-column
|
|
|
+ v-for="workloadColumn in getWorkloadColumns()"
|
|
|
+ :key="workloadColumn.key"
|
|
|
+ :label="workloadColumn.label"
|
|
|
+ align="center"
|
|
|
+ :show-overflow-tooltip="true" >
|
|
|
+ <template #default="scope">
|
|
|
+ {{ getWorkloadValue(scope.row, workloadColumn.identifier) }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-else class="text-center text-gray-500 py-4">
|
|
|
+ 暂无平台井工作量数据
|
|
|
+ </div>
|
|
|
+ </ContentWrap>
|
|
|
+
|
|
|
+ <!-- 第四部分:审批意见 - 只在审批模式下显示 -->
|
|
|
+ <ContentWrap class="section-padding" v-if="isApprovalMode">
|
|
|
+ <el-form
|
|
|
+ ref="approvalFormRef"
|
|
|
+ :model="approvalForm"
|
|
|
+ style="margin-top: 1em"
|
|
|
+ label-width="200px"
|
|
|
+ >
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="审批意见" prop="opinion">
|
|
|
+ <el-input
|
|
|
+ v-model="approvalForm.opinion"
|
|
|
+ type="textarea"
|
|
|
+ :rows="4"
|
|
|
+ placeholder="请输入审批意见"
|
|
|
+ maxlength="500"
|
|
|
+ show-word-limit
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </el-form>
|
|
|
+ </ContentWrap>
|
|
|
+
|
|
|
+ <!-- 操作按钮 -->
|
|
|
+ <ContentWrap class="section-padding" v-if="isEditMode">
|
|
|
+ <el-form>
|
|
|
+ <el-form-item style="float: right">
|
|
|
+ <el-button @click="submitForm" type="primary" :disabled="formLoading">
|
|
|
+ {{ t('common.save') }}
|
|
|
+ </el-button>
|
|
|
+ <el-button @click="close">{{ t('common.cancel') }}</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </ContentWrap>
|
|
|
+
|
|
|
+ <!-- 审批模式下的操作按钮 -->
|
|
|
+ <ContentWrap class="section-padding" v-if="isApprovalMode">
|
|
|
+ <el-form>
|
|
|
+ <el-form-item style="float: right">
|
|
|
+ <el-button @click="handleApprove('pass')" type="success">
|
|
|
+ 审批通过
|
|
|
+ </el-button>
|
|
|
+ <el-button @click="handleApprove('reject')" type="danger">
|
|
|
+ 审批驳回
|
|
|
+ </el-button>
|
|
|
+ <el-button @click="close">{{ t('common.close') }}</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </ContentWrap>
|
|
|
+
|
|
|
+ <!-- 详情模式下的操作按钮 - 只有关闭按钮 -->
|
|
|
+ <ContentWrap class="section-padding" v-if="isDetailMode">
|
|
|
+ <el-form>
|
|
|
+ <el-form-item style="float: right">
|
|
|
+ <el-button @click="close">{{ t('common.close') }}</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </ContentWrap>
|
|
|
+
|
|
|
+ </ContentWrap>
|
|
|
+
|
|
|
+ <!-- 设备选择对话框 -->
|
|
|
+ <el-dialog
|
|
|
+ v-model="deviceDialogVisible"
|
|
|
+ title="选择施工设备"
|
|
|
+ width="1000px"
|
|
|
+ :before-close="handleDeviceDialogClose"
|
|
|
+ class="device-select-dialog"
|
|
|
+ >
|
|
|
+ <div class="transfer-container">
|
|
|
+ <el-transfer
|
|
|
+ v-model="selectedDeviceIds"
|
|
|
+ :data="filteredDeviceList"
|
|
|
+ :titles="['可选设备', '已选设备']"
|
|
|
+ :props="{ key: 'id', label: 'deviceCode' }"
|
|
|
+ filterable
|
|
|
+ class="transfer-component"
|
|
|
+ @change="handleTransferChange"
|
|
|
+ >
|
|
|
+ <template #default="{ option }">
|
|
|
+ <el-tooltip
|
|
|
+ effect="dark"
|
|
|
+ placement="top"
|
|
|
+ :content="`${option.deviceCode || ''} - ${option.deviceName || ''}`"
|
|
|
+ :disabled="!option.deviceCode && !option.deviceName"
|
|
|
+ transition="fade-in-linear"
|
|
|
+ >
|
|
|
+ <span class="transfer-option-text">
|
|
|
+ {{ option.deviceCode }} - {{ option.deviceName }}
|
|
|
+ </span>
|
|
|
+ </el-tooltip>
|
|
|
+ </template>
|
|
|
+ </el-transfer>
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <span class="dialog-footer">
|
|
|
+ <el-button @click="handleDeviceDialogClose">取消</el-button>
|
|
|
+ <el-button type="primary" @click="confirmDeviceSelection">确定</el-button>
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import { ref, reactive, computed, onMounted, nextTick, watch } from 'vue'
|
|
|
+import { useI18n } from '@/hooks/web/useI18n'
|
|
|
+import { useMessage } from '@/hooks/web/useMessage'
|
|
|
+import { useTagsViewStore } from '@/store/modules/tagsView'
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
+import {DICT_TYPE, getDictLabel, getStrDictOptions} from '@/utils/dict'
|
|
|
+import { IotRdDailyReportApi } from '@/api/pms/iotrddailyreport'
|
|
|
+import { IotDailyReportAttrsApi } from '@/api/pms/iotdailyreportattrs'
|
|
|
+import * as DeptApi from '@/api/system/dept'
|
|
|
+import { useUserStore } from '@/store/modules/user'
|
|
|
+import dayjs from 'dayjs'
|
|
|
+import FileUpload from "@/components/UploadFile/src/FileUpload.vue";
|
|
|
+
|
|
|
+const { t } = useI18n()
|
|
|
+const message = useMessage()
|
|
|
+const { delView } = useTagsViewStore()
|
|
|
+const { push, currentRoute } = useRouter()
|
|
|
+const { params } = useRoute()
|
|
|
+const userStore = useUserStore()
|
|
|
+
|
|
|
+/** 填报日报 表单 */
|
|
|
+defineOptions({ name: 'DailyReportApprovalForm' })
|
|
|
+
|
|
|
+const formLoading = ref(false)
|
|
|
+const formRef = ref()
|
|
|
+const id = params.id // 瑞都日报id
|
|
|
+const mode = 'approval'
|
|
|
+// 日报数据
|
|
|
+const dailyReportData = ref<any>({})
|
|
|
+
|
|
|
+// 添加模式判断计算属性 此处 固定为 审批页面
|
|
|
+const isApprovalMode = computed(() => mode === 'approval')
|
|
|
+const isDetailMode = computed(() => mode === 'edit')
|
|
|
+const isEditMode = computed(() => mode === 'fill') // 默认为编辑模式
|
|
|
+
|
|
|
+// 只读模式判断:审批模式或详情模式都为只读
|
|
|
+const isReadonlyMode = computed(() => isApprovalMode.value || isDetailMode.value)
|
|
|
+
|
|
|
+// 在表单数据定义附近添加
|
|
|
+const platformWellPairs = ref<any[]>([]) // 存储各平台井的工作量数据
|
|
|
+const currentPlatformId = ref<number>() // 当前选中的平台井ID
|
|
|
+
|
|
|
+// 页面标题计算
|
|
|
+const pageTitle = computed(() => {
|
|
|
+ if (isApprovalMode.value) {
|
|
|
+ return dailyReportData.value.wellName && dailyReportData.value.constructionStartDate
|
|
|
+ ? `${dailyReportData.value.wellName} - ${formatDate(dailyReportData.value.constructionStartDate)} 日报审批`
|
|
|
+ : '日报审批'
|
|
|
+ } else if (isDetailMode.value) {
|
|
|
+ return dailyReportData.value.wellName && dailyReportData.value.constructionStartDate
|
|
|
+ ? `${dailyReportData.value.wellName} - ${formatDate(dailyReportData.value.constructionStartDate)} 日报详情`
|
|
|
+ : '日报详情'
|
|
|
+ } else {
|
|
|
+ return dailyReportData.value.wellName && dailyReportData.value.constructionStartDate
|
|
|
+ ? `${dailyReportData.value.wellName} - ${formatDate(dailyReportData.value.constructionStartDate)} 生产日报`
|
|
|
+ : '日报填报'
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// 模式提示信息
|
|
|
+const modeNotice = computed(() => {
|
|
|
+ if (isApprovalMode.value) {
|
|
|
+ return '审批模式:所有字段均为只读'
|
|
|
+ } else if (isDetailMode.value) {
|
|
|
+ return '详情模式:所有字段均为只读'
|
|
|
+ }
|
|
|
+ return ''
|
|
|
+})
|
|
|
+
|
|
|
+// 动态属性相关变量
|
|
|
+const dynamicAttrs = ref<any[]>([]) // 存储动态属性列表
|
|
|
+
|
|
|
+// 添加设备选择相关变量
|
|
|
+const deviceDialogVisible = ref(false)
|
|
|
+const filteredDeviceList = ref<any[]>([])
|
|
|
+const selectedDeviceIds = ref<number[]>([])
|
|
|
+const deviceMap = ref<Record<number, any>>({})
|
|
|
+
|
|
|
+// 添加平台井相关响应式数据
|
|
|
+const platformOptions = ref<any[]>([]) // 平台井下拉选项
|
|
|
+const showPlatformField = ref(false) // 是否显示平台井字段
|
|
|
+
|
|
|
+// 计算属性:是否显示平台井字段
|
|
|
+const shouldShowPlatformField = computed(() => {
|
|
|
+ return dailyReportData.value.platformWell === 1
|
|
|
+})
|
|
|
+
|
|
|
+// 初始化平台井数据
|
|
|
+const initPlatformData = (reportData: any) => {
|
|
|
+ // 设置是否显示平台井字段
|
|
|
+ showPlatformField.value = reportData.platformWell === 1
|
|
|
+
|
|
|
+ // 设置平台井下拉选项
|
|
|
+ if (reportData.platforms && Array.isArray(reportData.platforms)) {
|
|
|
+ platformOptions.value = reportData.platforms
|
|
|
+
|
|
|
+ // 初始化 platformWellPairs,确保包含所有平台井的完整数据
|
|
|
+ if (reportData.platformWell === 1) {
|
|
|
+ // 初始化 platformWellPairs,包含所有平台井的完整数据
|
|
|
+ platformWellPairs.value = reportData.platforms.map((platform: any) => {
|
|
|
+ // 查找是否已有该平台井的数据
|
|
|
+ const existingData = reportData.platformWellPairs?.find((p: any) => p.taskId === platform.id)
|
|
|
+
|
|
|
+ // 确保 techniqueIds 是字符串数组格式
|
|
|
+ let techniqueIds = []
|
|
|
+ if (existingData && existingData.techniqueIds) {
|
|
|
+ techniqueIds = existingData.techniqueIds.map((id: any) => id.toString())
|
|
|
+ } else if (platform.techniqueIds) {
|
|
|
+ techniqueIds = platform.techniqueIds.map((id: any) => id.toString())
|
|
|
+ }
|
|
|
+
|
|
|
+ return existingData || {
|
|
|
+ taskId: platform.id,
|
|
|
+ reportId: platform.reportId, // 使用接口返回的 reportId
|
|
|
+ wellName: platform.wellName,
|
|
|
+ rdStatus: platform.rdStatus || '', // 初始为空
|
|
|
+ techniqueIds: techniqueIds, // 初始为空数组
|
|
|
+ extProperty: platform.extProperty || [] // 初始为空数组
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ platformOptions.value = []
|
|
|
+ platformWellPairs.value = []
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置默认选中的平台井
|
|
|
+ if (reportData.taskId && platformOptions.value.length > 0) {
|
|
|
+ // 查找与 taskId 匹配的平台井
|
|
|
+ const defaultPlatform = platformOptions.value.find(
|
|
|
+ (platform: any) => platform.id === reportData.taskId
|
|
|
+ )
|
|
|
+ if (defaultPlatform) {
|
|
|
+ formData.value.platformId = defaultPlatform.id
|
|
|
+ currentPlatformId.value = defaultPlatform.id
|
|
|
+
|
|
|
+ // 加载默认平台井的数据到表单
|
|
|
+ loadPlatformData(defaultPlatform.id)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 添加审批表单相关变量
|
|
|
+const approvalFormRef = ref()
|
|
|
+const approvalForm = reactive({
|
|
|
+ opinion: '' // 审批意见
|
|
|
+})
|
|
|
+
|
|
|
+// 审批表单验证规则(可选,根据需求添加)
|
|
|
+const approvalFormRules = reactive({
|
|
|
+ opinion: [
|
|
|
+ { required: false, message: '请输入审批意见', trigger: 'blur' },
|
|
|
+ { min: 0, max: 500, message: '审批意见长度不能超过500个字符', trigger: 'blur' }
|
|
|
+ ]
|
|
|
+})
|
|
|
+
|
|
|
+// 添加文件上传组件的引用
|
|
|
+const fileUploadRef = ref()
|
|
|
+
|
|
|
+// 表单数据
|
|
|
+const formData = ref({
|
|
|
+ id: undefined,
|
|
|
+ deptId: undefined,
|
|
|
+ taskId: undefined,
|
|
|
+ platformWell: undefined,
|
|
|
+ companyId: undefined,
|
|
|
+ deptName: undefined,
|
|
|
+ constructionStartDate: undefined,
|
|
|
+ contractName: undefined,
|
|
|
+ projectDepartment: '',
|
|
|
+ costCenterId: undefined,
|
|
|
+ costCenter: '',
|
|
|
+ platformId: undefined, // 平台井ID
|
|
|
+ // 新增日报填报字段
|
|
|
+ timeRange: [ // 设置默认时间范围 8:00 - 8:00
|
|
|
+ dayjs().hour(8).minute(0).second(0).toDate(),
|
|
|
+ dayjs().hour(8).minute(0).second(0).toDate()
|
|
|
+ ],
|
|
|
+ startTime: undefined, // 开始时间
|
|
|
+ endTime: undefined, // 结束时间
|
|
|
+ rdStatus: '', // 施工状态
|
|
|
+ deviceIds: [] as number[], // 设备ID数组
|
|
|
+ techniqueIds: [], // 施工工艺
|
|
|
+ productionStatus: '', // 当日生产动态
|
|
|
+ nextPlan: '', // 下步工作计划
|
|
|
+ externalRental: '', // 外租设备
|
|
|
+ malfunction: '', // 故障情况
|
|
|
+ faultDowntime: '', // 故障误工
|
|
|
+ // 添加动态字段对象
|
|
|
+ dynamicFields: {} as Record<string, any>,
|
|
|
+ // 附件列表
|
|
|
+ attachments: [] as any[]
|
|
|
+})
|
|
|
+
|
|
|
+// 添加上传成功处理函数
|
|
|
+const handleUploadSuccess = (result: any) => {
|
|
|
+ console.log('上传成功:', result)
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 检查响应是否成功
|
|
|
+ if (!result.response) {
|
|
|
+ message.error('上传响应数据异常')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (result.response.code !== 0) {
|
|
|
+ message.error(result.response.msg || '文件上传失败')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const responseData = result.response.data
|
|
|
+
|
|
|
+ if (!responseData) {
|
|
|
+ message.error('上传数据为空')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理返回的文件列表
|
|
|
+ if (responseData.files && Array.isArray(responseData.files) && responseData.files.length > 0) {
|
|
|
+ responseData.files.forEach((file: any) => {
|
|
|
+ if (!file.filePath) {
|
|
|
+ console.warn('文件缺少 filePath:', file)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据后端返回的数据结构构建附件对象
|
|
|
+ const attachment = {
|
|
|
+ id: undefined,
|
|
|
+ category: 'daily_report',
|
|
|
+ bizId: formData.value.id,
|
|
|
+ type: 'attachment',
|
|
|
+ filename: file.name || '未知文件',
|
|
|
+ fileType: getFileType(file.name),
|
|
|
+ filePath: file.filePath, //使用正确的 filePath
|
|
|
+ fileSize: formatFileSize(file.size || 0),
|
|
|
+ remark: ''
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加到附件列表
|
|
|
+ if (!formData.value.attachments) {
|
|
|
+ formData.value.attachments = []
|
|
|
+ }
|
|
|
+ formData.value.attachments.push(attachment)
|
|
|
+ })
|
|
|
+
|
|
|
+ message.success(`成功上传 ${responseData.files.length} 个文件`)
|
|
|
+ } else {
|
|
|
+ console.warn('上传成功但没有返回文件信息')
|
|
|
+ message.warning('上传成功但未获取到文件信息')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('处理上传结果时发生错误:', error)
|
|
|
+ message.error('处理上传结果失败')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 删除附件
|
|
|
+const removeAttachment = (index: number) => {
|
|
|
+ if (formData.value.attachments && formData.value.attachments.length > index) {
|
|
|
+ formData.value.attachments.splice(index, 1)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 计算属性:未施工设备名称
|
|
|
+const unSelectedDeviceNames = computed(() => {
|
|
|
+ const selectedDevices = dailyReportData.value.selectedDevices || []
|
|
|
+ const selectedDeviceIds = formData.value.deviceIds || []
|
|
|
+
|
|
|
+ if (selectedDevices.length === 0) {
|
|
|
+ return '无可用设备'
|
|
|
+ }
|
|
|
+
|
|
|
+ // 筛选出未选择的设备
|
|
|
+ const unselectedDevices = selectedDevices.filter((device: any) =>
|
|
|
+ !selectedDeviceIds.includes(device.id)
|
|
|
+ )
|
|
|
+
|
|
|
+ if (unselectedDevices.length === 0) {
|
|
|
+ return '所有设备都已施工'
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提取设备名称并用逗号分隔
|
|
|
+ const deviceNames = unselectedDevices
|
|
|
+ .map((device: any) => device.deviceName || device.deviceCode || '未知设备')
|
|
|
+ .filter((name: string) => name !== '未知设备')
|
|
|
+
|
|
|
+ return deviceNames.join(', ') || '无未选择设备'
|
|
|
+})
|
|
|
+
|
|
|
+// 附件名称点击事件
|
|
|
+const inContent = async (attachment) => {
|
|
|
+ if (!attachment || !attachment.filePath) {
|
|
|
+ message.error('附件路径不存在')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 直接使用 attachment.filePath
|
|
|
+ const filePath = attachment.filePath
|
|
|
+ // 确保 filePath 是编码后的格式
|
|
|
+ const encodedPath = encodeURIComponent(Base64.encode(filePath))
|
|
|
+
|
|
|
+ // 打开预览窗口
|
|
|
+ window.open(`http://doc.deepoil.cc:8012/onlinePreview?url=${encodedPath}`)
|
|
|
+ } catch (error) {
|
|
|
+ console.error('预览附件失败:', error)
|
|
|
+ message.error('预览附件失败')
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 获取文件类型辅助函数
|
|
|
+const getFileType = (filename: string) => {
|
|
|
+ const ext = filename.split('.').pop()?.toLowerCase()
|
|
|
+ if (['jpg', 'jpeg', 'png', 'gif', 'bmp'].includes(ext || '')) {
|
|
|
+ return 'image'
|
|
|
+ } else if (['pdf'].includes(ext || '')) {
|
|
|
+ return 'pdf'
|
|
|
+ } else if (['doc', 'docx'].includes(ext || '')) {
|
|
|
+ return 'word'
|
|
|
+ } else if (['xls', 'xlsx'].includes(ext || '')) {
|
|
|
+ return 'excel'
|
|
|
+ } else {
|
|
|
+ return 'other'
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 格式化文件大小辅助函数
|
|
|
+const formatFileSize = (bytes: number) => {
|
|
|
+ if (bytes === 0) return '0 Bytes'
|
|
|
+ const k = 1024
|
|
|
+ const sizes = ['Bytes', 'KB', 'MB', 'GB']
|
|
|
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
|
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
|
|
+}
|
|
|
+
|
|
|
+// 计算属性:格式化设备显示
|
|
|
+const formatDevicesForDisplay = computed(() => {
|
|
|
+ const deviceIds = formData.value.deviceIds
|
|
|
+ if (!deviceIds || deviceIds.length === 0) {
|
|
|
+ return '无设备'
|
|
|
+ }
|
|
|
+
|
|
|
+ const deviceNames = deviceIds
|
|
|
+ .map(id => deviceMap.value[id]?.deviceName)
|
|
|
+ .filter(name => name !== undefined && name !== '')
|
|
|
+
|
|
|
+ if (deviceNames.length === 0) return '无设备'
|
|
|
+
|
|
|
+ // 如果设备数量超过2个,显示前两个加省略号
|
|
|
+ /* if (deviceNames.length > 2) {
|
|
|
+ return `${deviceNames[0]}, ${deviceNames[1]}...`
|
|
|
+ } */
|
|
|
+
|
|
|
+ return deviceNames.join(', ')
|
|
|
+})
|
|
|
+
|
|
|
+// 计算属性:获取所有设备名称(用于tooltip)
|
|
|
+const getAllDeviceNamesForDisplay = computed(() => {
|
|
|
+ const deviceIds = formData.value.deviceIds
|
|
|
+ if (!deviceIds || deviceIds.length === 0) {
|
|
|
+ return '无设备'
|
|
|
+ }
|
|
|
+
|
|
|
+ const deviceNames = deviceIds
|
|
|
+ .map(id => deviceMap.value[id]?.deviceCode || '未知设备')
|
|
|
+ .filter(name => name !== '未知设备')
|
|
|
+
|
|
|
+ return deviceNames.join(', ') || '无有效设备'
|
|
|
+})
|
|
|
+
|
|
|
+// 打开设备选择对话框
|
|
|
+const openDeviceDialog = async () => {
|
|
|
+ if (!dailyReportData.value.deptId) {
|
|
|
+ message.error('请先加载项目信息')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ formLoading.value = true
|
|
|
+ selectedDeviceIds.value = [...(formData.value.deviceIds || [])]
|
|
|
+
|
|
|
+ // 直接从日报数据的 selectedDevices 中获取设备列表
|
|
|
+ const selectedDevices = dailyReportData.value.selectedDevices || []
|
|
|
+
|
|
|
+ // 更新设备映射表
|
|
|
+ const newDeviceMap = { ...deviceMap.value }
|
|
|
+ selectedDevices.forEach((device: any) => {
|
|
|
+ if (device.id) {
|
|
|
+ newDeviceMap[device.id] = device
|
|
|
+ }
|
|
|
+ })
|
|
|
+ deviceMap.value = newDeviceMap
|
|
|
+
|
|
|
+ filteredDeviceList.value = selectedDevices
|
|
|
+ deviceDialogVisible.value = true
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取设备列表失败:', error)
|
|
|
+ message.error('获取设备列表失败')
|
|
|
+ } finally {
|
|
|
+ formLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 处理穿梭框变化
|
|
|
+const handleTransferChange = (value: number[], direction: string, movedKeys: number[]) => {
|
|
|
+ // 可以添加额外的处理逻辑
|
|
|
+}
|
|
|
+
|
|
|
+// 确认设备选择
|
|
|
+const confirmDeviceSelection = () => {
|
|
|
+ formData.value.deviceIds = [...selectedDeviceIds.value]
|
|
|
+ deviceDialogVisible.value = false
|
|
|
+ message.success(`已选择 ${selectedDeviceIds.value.length} 台设备`)
|
|
|
+}
|
|
|
+
|
|
|
+// 关闭设备选择对话框
|
|
|
+const handleDeviceDialogClose = () => {
|
|
|
+ deviceDialogVisible.value = false
|
|
|
+}
|
|
|
+
|
|
|
+// 初始化设备数据
|
|
|
+const initDeviceData = (reportData: any) => {
|
|
|
+ // 初始化设备ID
|
|
|
+ if (reportData.deviceIds && Array.isArray(reportData.deviceIds)) {
|
|
|
+ formData.value.deviceIds = [...reportData.deviceIds]
|
|
|
+ } else {
|
|
|
+ formData.value.deviceIds = []
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化设备映射表(用于显示设备名称)
|
|
|
+ if (reportData.selectedDevices && Array.isArray(reportData.selectedDevices)) {
|
|
|
+ const newDeviceMap = { ...deviceMap.value }
|
|
|
+ reportData.selectedDevices.forEach((device: any) => {
|
|
|
+ if (device.id) {
|
|
|
+ newDeviceMap[device.id] = device
|
|
|
+ }
|
|
|
+ })
|
|
|
+ deviceMap.value = newDeviceMap
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 表单验证规则
|
|
|
+/* const formRules = reactive({
|
|
|
+ timeRange: [{ required: true, message: '时间节点不能为空', trigger: 'change' }],
|
|
|
+ rdStatus: [{ required: true, message: '施工状态不能为空', trigger: 'change' }],
|
|
|
+ techniqueIds: [{ required: true, message: '施工工艺不能为空', trigger: 'change' }],
|
|
|
+ productionStatus: [{ required: true, message: '当日生产动态不能为空', trigger: 'blur' }]
|
|
|
+}) */
|
|
|
+
|
|
|
+const formRules = computed(() => {
|
|
|
+ // 判断是否为虚拟项目
|
|
|
+ const isVirtualProject = dailyReportData.value.virtualProject === 'Y'
|
|
|
+
|
|
|
+ // 基础校验规则(时间节点、当日生产动态始终必填)
|
|
|
+ const rules = {
|
|
|
+ timeRange: [{ required: true, message: '时间节点不能为空', trigger: 'change' }],
|
|
|
+ productionStatus: [{ required: true, message: '当日生产动态不能为空', trigger: 'blur' }]
|
|
|
+ }
|
|
|
+
|
|
|
+ // 非虚拟项目时,添加施工状态、施工工艺的必填校验
|
|
|
+ if (!isVirtualProject) {
|
|
|
+ rules.rdStatus = [{ required: true, message: '施工状态不能为空', trigger: 'change' }]
|
|
|
+ rules.techniqueIds = [{ required: true, message: '施工工艺不能为空', trigger: 'change' }]
|
|
|
+ }
|
|
|
+
|
|
|
+ return rules
|
|
|
+})
|
|
|
+
|
|
|
+const queryParams = reactive({
|
|
|
+ deptId: undefined,
|
|
|
+ techniqueIds: [],
|
|
|
+})
|
|
|
+
|
|
|
+// 下拉选项
|
|
|
+const rdStatusOptions = getStrDictOptions(DICT_TYPE.PMS_PROJECT_RD_STATUS) // 施工状态
|
|
|
+const techniqueOptions = getStrDictOptions(DICT_TYPE.PMS_PROJECT_RD_TECHNOLOGY) // 瑞都施工工艺
|
|
|
+
|
|
|
+// 计算属性:日报标题
|
|
|
+const dailyReportTitle = computed(() => {
|
|
|
+ if (!dailyReportData.value.wellName || !dailyReportData.value.constructionStartDate) {
|
|
|
+ return '日报填报'
|
|
|
+ }
|
|
|
+ const dateStr = formatDate(dailyReportData.value.constructionStartDate)
|
|
|
+ return `${dailyReportData.value.wellName} - ${dateStr} 生产日报`
|
|
|
+})
|
|
|
+
|
|
|
+// 日报审批:日报标题
|
|
|
+const dailyReportApprovalTitle = computed(() => {
|
|
|
+ if (!dailyReportData.value.wellName || !dailyReportData.value.constructionStartDate) {
|
|
|
+ return '日报审批'
|
|
|
+ }
|
|
|
+ const dateStr = formatDate(dailyReportData.value.constructionStartDate)
|
|
|
+ return `${dailyReportData.value.wellName} - ${dateStr} 日报审批`
|
|
|
+})
|
|
|
+
|
|
|
+// 计算属性:施工周期
|
|
|
+const constructionPeriod = computed(() => {
|
|
|
+ const start = dailyReportData.value.constructionStartDate
|
|
|
+ const end = dailyReportData.value.constructionEndDate
|
|
|
+ if (!start || !end) return 0
|
|
|
+
|
|
|
+ const startDate = dayjs(start)
|
|
|
+ const endDate = dayjs(end)
|
|
|
+ return endDate.diff(startDate, 'day')
|
|
|
+})
|
|
|
+
|
|
|
+// 日期格式化函数
|
|
|
+const formatDate = (timestamp: number) => {
|
|
|
+ if (!timestamp) return ''
|
|
|
+ return dayjs(timestamp).format('YYYY-MM-DD')
|
|
|
+}
|
|
|
+
|
|
|
+const close = () => {
|
|
|
+ delView(unref(currentRoute))
|
|
|
+ push({ name: 'FillDailyReport', params: {} })
|
|
|
+}
|
|
|
+
|
|
|
+/** 提交表单 */
|
|
|
+const emit = defineEmits(['success'])
|
|
|
+const submitForm = async () => {
|
|
|
+ // 验证表单
|
|
|
+ try {
|
|
|
+ await formRef.value.validate()
|
|
|
+ } catch (error) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存当前平台井的数据
|
|
|
+ if (currentPlatformId.value) {
|
|
|
+ saveCurrentPlatformData(currentPlatformId.value)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 打印 platformWellPairs
|
|
|
+ console.log('platformWellPairs:', JSON.stringify(platformWellPairs.value, null, 2))
|
|
|
+
|
|
|
+ // 处理时间范围数据
|
|
|
+ if (formData.value.timeRange && formData.value.timeRange.length === 2) {
|
|
|
+ // 将时间范围转换为 LocalTime 格式的字符串
|
|
|
+ const startDate = dayjs(formData.value.timeRange[0])
|
|
|
+ const endDate = dayjs(formData.value.timeRange[1])
|
|
|
+
|
|
|
+ // 格式化为 HH:mm:ss 字符串,后端会自动转换为 LocalTime
|
|
|
+ formData.value.startTime = startDate.format('HH:mm:ss')
|
|
|
+ formData.value.endTime = endDate.format('HH:mm:ss')
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建动态属性 extProperty 数组
|
|
|
+ const extProperties = dynamicAttrs.value.map(attr => {
|
|
|
+ return {
|
|
|
+ name: attr.name,
|
|
|
+ sort: attr.sort,
|
|
|
+ unit: attr.unit,
|
|
|
+ actualValue: formData.value.dynamicFields[attr.identifier] || '', // 从 dynamicFields 中获取用户填写的值
|
|
|
+ dataType: attr.dataType,
|
|
|
+ maxValue: attr.maxValue,
|
|
|
+ minValue: attr.minValue,
|
|
|
+ required: attr.required,
|
|
|
+ accessMode: attr.accessMode,
|
|
|
+ identifier: attr.identifier,
|
|
|
+ defaultValue: attr.defaultValue
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 准备提交数据,包含动态字段
|
|
|
+ const baseSubmitData = {
|
|
|
+ ...formData.value,
|
|
|
+ // 将动态字段组装成 extProperty 数组
|
|
|
+ extProperty: extProperties,
|
|
|
+ deviceIds: formData.value.deviceIds, // 设备ID集合
|
|
|
+ }
|
|
|
+
|
|
|
+ // 删除不需要复制的字段
|
|
|
+ const { id: currentId, platformId, platformWell, taskId, rdStatus, techniqueIds, extProperty, ...baseData } = baseSubmitData
|
|
|
+
|
|
|
+ const submitDatas = []
|
|
|
+
|
|
|
+ if (dailyReportData.value.platformWell === 1 && platformWellPairs.value.length > 0) {
|
|
|
+ // 平台井模式:处理所有平台井数据
|
|
|
+ platformWellPairs.value.forEach(pair => {
|
|
|
+ // 复制基础数据
|
|
|
+ const platformData = { ...baseData }
|
|
|
+
|
|
|
+ // 设置平台井特定字段
|
|
|
+ platformData.id = pair.reportId // 使用 platformWellPairs 中的 reportId
|
|
|
+ platformData.platformId = pair.taskId // 使用 platformWellPairs 中的 taskId
|
|
|
+ platformData.rdStatus = pair.rdStatus || ''
|
|
|
+ platformData.techniqueIds = pair.techniqueIds || []
|
|
|
+ platformData.extProperty = pair.extProperty || []
|
|
|
+
|
|
|
+ // 处理附件:复制并修改 bizId 为当前 pair 的 reportId
|
|
|
+ platformData.attachments = (baseData.attachments || []).map(attachment => ({
|
|
|
+ ...attachment, // 深拷贝单个附件
|
|
|
+ bizId: pair.reportId // 替换 bizId 为当前平台井的 reportId
|
|
|
+ }))
|
|
|
+
|
|
|
+ // 重新构建 dynamicFields(如果需要)
|
|
|
+ const dynamicFields = {}
|
|
|
+ if (platformData.extProperty && platformData.extProperty.length > 0) {
|
|
|
+ platformData.extProperty.forEach(prop => {
|
|
|
+ if (prop.identifier) {
|
|
|
+ dynamicFields[prop.identifier] = prop.actualValue || ''
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ platformData.dynamicFields = dynamicFields
|
|
|
+
|
|
|
+ submitDatas.push(platformData)
|
|
|
+ })
|
|
|
+
|
|
|
+ console.log('平台井模式提交数据:', JSON.stringify(submitDatas, null, 2))
|
|
|
+ } else {
|
|
|
+ // 非平台井模式:只提交当前数据
|
|
|
+ submitDatas.push(baseSubmitData)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提交请求
|
|
|
+ formLoading.value = true
|
|
|
+ try {
|
|
|
+ // 调用更新接口
|
|
|
+ await IotRdDailyReportApi.saveBatch(submitDatas)
|
|
|
+ message.success(t('common.updateSuccess'))
|
|
|
+ close()
|
|
|
+ // 发送操作成功的事件
|
|
|
+ emit('success')
|
|
|
+ } catch (error) {
|
|
|
+ console.error('提交失败:', error)
|
|
|
+ } finally {
|
|
|
+ formLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/** 重置表单 */
|
|
|
+const resetForm = () => {
|
|
|
+ formRef.value?.resetFields()
|
|
|
+}
|
|
|
+
|
|
|
+// 初始化动态属性
|
|
|
+const initDynamicAttrs = (reportData: any) => {
|
|
|
+ if (reportData.dailyReportAttrs && reportData.dailyReportAttrs.length > 0) {
|
|
|
+ dynamicAttrs.value = reportData.dailyReportAttrs
|
|
|
+
|
|
|
+ // 初始化动态字段的值
|
|
|
+ const initialDynamicFields: Record<string, any> = {}
|
|
|
+
|
|
|
+ // 优先从 extProperty 中获取实际值(编辑时)
|
|
|
+ if (reportData.extProperty && reportData.extProperty.length > 0) {
|
|
|
+ reportData.extProperty.forEach((extProp: any) => {
|
|
|
+ if (extProp.identifier) {
|
|
|
+ initialDynamicFields[extProp.identifier] = extProp.actualValue || ''
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ reportData.dailyReportAttrs.forEach((attr: any) => {
|
|
|
+ if (!initialDynamicFields.hasOwnProperty(attr.identifier)) {
|
|
|
+ // 优先使用实际值,如果没有则使用默认值
|
|
|
+ const value = (attr.extProperty && attr.extProperty.actualValue !== undefined &&
|
|
|
+ attr.extProperty.actualValue !== null && attr.extProperty.actualValue !== '')
|
|
|
+ ? attr.extProperty.actualValue
|
|
|
+ : (attr.defaultValue || (attr.extProperty?.defaultValue || ''))
|
|
|
+
|
|
|
+ initialDynamicFields[attr.identifier] = value
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ formData.value.dynamicFields = initialDynamicFields
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 获取动态字段的验证规则
|
|
|
+const getDynamicAttrRules = (attr: any) => {
|
|
|
+ const rules = []
|
|
|
+ if (attr.required === 1) {
|
|
|
+ rules.push({
|
|
|
+ required: true,
|
|
|
+ message: `${attr.name}不能为空`,
|
|
|
+ trigger: 'blur'
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 数字类型验证
|
|
|
+ if (attr.dataType === 'double') {
|
|
|
+ rules.push({
|
|
|
+ validator: (rule: any, value: any, callback: any) => {
|
|
|
+ if (value === '' || value === null || value === undefined) {
|
|
|
+ callback()
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const numValue = Number(value)
|
|
|
+ if (isNaN(numValue)) {
|
|
|
+ callback(new Error(`${attr.name}必须是数字`))
|
|
|
+ } else if (attr.minValue && numValue < Number(attr.minValue)) {
|
|
|
+ callback(new Error(`${attr.name}不能小于${attr.minValue}`))
|
|
|
+ } else if (attr.maxValue && numValue > Number(attr.maxValue)) {
|
|
|
+ callback(new Error(`${attr.name}不能大于${attr.maxValue}`))
|
|
|
+ } else {
|
|
|
+ callback()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ trigger: 'blur'
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ return rules
|
|
|
+}
|
|
|
+
|
|
|
+// 更新动态属性(处理交集、新增和删除)
|
|
|
+const updateDynamicAttrs = async (newAttrs: any[], newTechniqueIds: string[], oldTechniqueIds?: string[]) => {
|
|
|
+ const oldAttrs = [...dynamicAttrs.value]
|
|
|
+ const oldDynamicFields = { ...formData.value.dynamicFields }
|
|
|
+
|
|
|
+ // 计算需要保留的字段(交集)
|
|
|
+ const commonAttrs = oldAttrs.filter(oldAttr =>
|
|
|
+ newAttrs.some(newAttr => newAttr.identifier === oldAttr.identifier)
|
|
|
+ )
|
|
|
+
|
|
|
+ // 计算需要新增的字段
|
|
|
+ const addedAttrs = newAttrs.filter(newAttr =>
|
|
|
+ !oldAttrs.some(oldAttr => oldAttr.identifier === newAttr.identifier)
|
|
|
+ )
|
|
|
+
|
|
|
+ // 计算需要删除的字段
|
|
|
+ const removedAttrs = oldAttrs.filter(oldAttr =>
|
|
|
+ !newAttrs.some(newAttr => newAttr.identifier === oldAttr.identifier)
|
|
|
+ )
|
|
|
+
|
|
|
+ // 构建新的动态属性数组
|
|
|
+ const updatedAttrs = [...commonAttrs, ...addedAttrs]
|
|
|
+
|
|
|
+ // 构建新的动态字段对象
|
|
|
+ const updatedDynamicFields = { ...oldDynamicFields }
|
|
|
+
|
|
|
+ // 移除已删除的字段
|
|
|
+ removedAttrs.forEach(attr => {
|
|
|
+ delete updatedDynamicFields[attr.identifier]
|
|
|
+ })
|
|
|
+
|
|
|
+ // 初始化新增字段的值
|
|
|
+ addedAttrs.forEach(attr => {
|
|
|
+ if (!updatedDynamicFields[attr.identifier]) {
|
|
|
+ // 如果有默认值使用默认值,否则为空
|
|
|
+ updatedDynamicFields[attr.identifier] = attr.defaultValue ||
|
|
|
+ (attr.extProperty?.defaultValue || '')
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ // 更新响应式数据
|
|
|
+ dynamicAttrs.value = updatedAttrs
|
|
|
+ formData.value.dynamicFields = updatedDynamicFields
|
|
|
+}
|
|
|
+
|
|
|
+// 加载动态属性
|
|
|
+const loadDynamicAttrs = async (newTechniqueIds: string[], oldTechniqueIds?: string[]) => {
|
|
|
+ try {
|
|
|
+ formLoading.value = true
|
|
|
+
|
|
|
+ const queryParams = {
|
|
|
+ techniqueIds: newTechniqueIds.join(',')
|
|
|
+ }
|
|
|
+
|
|
|
+ const response = await IotDailyReportAttrsApi.dailyReportAttrs(queryParams)
|
|
|
+ const newAttrs = response || []
|
|
|
+
|
|
|
+ // 处理动态属性更新
|
|
|
+ await updateDynamicAttrs(newAttrs, newTechniqueIds, oldTechniqueIds)
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载动态属性失败:', error)
|
|
|
+ message.error('加载动态属性失败')
|
|
|
+ } finally {
|
|
|
+ formLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 在 watch 监听平台井选择变化的部分附近,添加施工工艺转换函数
|
|
|
+// 添加施工工艺数值到标签的转换函数
|
|
|
+const convertTechniqueIdsToLabels = (techniqueIds: number[]): string[] => {
|
|
|
+ if (!techniqueIds || !Array.isArray(techniqueIds)) {
|
|
|
+ return []
|
|
|
+ }
|
|
|
+
|
|
|
+ return techniqueIds.map(id => {
|
|
|
+ const dict = techniqueOptions.value.find(option => option.value === id.toString())
|
|
|
+ return dict ? dict.label : id.toString()
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 监听施工工艺变化
|
|
|
+watch(() => formData.value.techniqueIds, async (newTechniqueIds, oldTechniqueIds) => {
|
|
|
+ if (newTechniqueIds && newTechniqueIds.length > 0) {
|
|
|
+ await loadDynamicAttrs(newTechniqueIds, oldTechniqueIds)
|
|
|
+
|
|
|
+ // 动态属性加载完成后,更新当前平台井的 extProperty
|
|
|
+ if (currentPlatformId.value) {
|
|
|
+ updateCurrentPlatformExtProperty()
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ dynamicAttrs.value = []
|
|
|
+ formData.value.dynamicFields = {}
|
|
|
+
|
|
|
+ // 清空当前平台井的 extProperty
|
|
|
+ if (currentPlatformId.value) {
|
|
|
+ updateCurrentPlatformExtProperty()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}, { deep: true })
|
|
|
+
|
|
|
+// 更新当前平台井的 extProperty
|
|
|
+const updateCurrentPlatformExtProperty = () => {
|
|
|
+ if (!currentPlatformId.value) return
|
|
|
+
|
|
|
+ const index = platformWellPairs.value.findIndex(item => item.taskId === currentPlatformId.value)
|
|
|
+ if (index !== -1) {
|
|
|
+ platformWellPairs.value[index].extProperty = getCurrentExtProperties()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 监听平台井选择变化
|
|
|
+watch(() => formData.value.platformId, (newPlatformId, oldPlatformId) => {
|
|
|
+ if (newPlatformId && newPlatformId !== oldPlatformId) {
|
|
|
+ // 保存当前平台井的数据到 platformWellPairs
|
|
|
+ if (oldPlatformId) {
|
|
|
+ saveCurrentPlatformData(oldPlatformId)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 加载新平台井的数据到表单
|
|
|
+ loadPlatformData(newPlatformId)
|
|
|
+ currentPlatformId.value = newPlatformId
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// 监听动态字段变化,实时更新到 platformWellPairs
|
|
|
+watch(() => formData.value.dynamicFields, (newFields) => {
|
|
|
+ if (currentPlatformId.value) {
|
|
|
+ updateCurrentPlatformExtProperty()
|
|
|
+ }
|
|
|
+}, { deep: true })
|
|
|
+
|
|
|
+// 监听施工状态变化
|
|
|
+watch(() => formData.value.rdStatus, (newStatus) => {
|
|
|
+ if (currentPlatformId.value) {
|
|
|
+ const index = platformWellPairs.value.findIndex(item => item.taskId === currentPlatformId.value)
|
|
|
+ if (index !== -1) {
|
|
|
+ platformWellPairs.value[index].rdStatus = newStatus
|
|
|
+ }
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// 保存当前平台井数据到 platformWellPairs
|
|
|
+const saveCurrentPlatformData = (platformId: number) => {
|
|
|
+ const index = platformWellPairs.value.findIndex(item => item.taskId === platformId)
|
|
|
+ if (index !== -1) {
|
|
|
+ platformWellPairs.value[index] = {
|
|
|
+ ...platformWellPairs.value[index],
|
|
|
+ rdStatus: formData.value.rdStatus,
|
|
|
+ techniqueIds: [...formData.value.techniqueIds],
|
|
|
+ extProperty: getCurrentExtProperties()
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 如果找不到对应的平台井,添加新的记录
|
|
|
+ const platform = platformOptions.value.find(p => p.id === platformId)
|
|
|
+ if (platform) {
|
|
|
+ platformWellPairs.value.push({
|
|
|
+ taskId: platformId,
|
|
|
+ reportId: undefined, // 新记录没有 reportId
|
|
|
+ wellName: platform.wellName,
|
|
|
+ rdStatus: formData.value.rdStatus,
|
|
|
+ techniqueIds: [...formData.value.techniqueIds],
|
|
|
+ extProperty: getCurrentExtProperties()
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 从 platformWellPairs 加载平台井数据到表单
|
|
|
+const loadPlatformData = (platformId: number) => {
|
|
|
+ const platformData = platformWellPairs.value.find(item => item.taskId === platformId)
|
|
|
+ if (platformData) {
|
|
|
+ // 更新表单字段
|
|
|
+ formData.value.rdStatus = platformData.rdStatus || ''
|
|
|
+ // formData.value.techniqueIds = platformData.techniqueIds ? [...platformData.techniqueIds] : []
|
|
|
+ // 将施工工艺数值转换为对应的标签
|
|
|
+ if (platformData.techniqueIds && Array.isArray(platformData.techniqueIds)) {
|
|
|
+ // 如果是数字数组,转换为字符串数组(与数据字典格式匹配)
|
|
|
+ formData.value.techniqueIds = platformData.techniqueIds.map(id => id.toString())
|
|
|
+ } else {
|
|
|
+ formData.value.techniqueIds = platformData.techniqueIds ? [...platformData.techniqueIds] : []
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新动态属性
|
|
|
+ if (platformData.extProperty && platformData.extProperty.length > 0) {
|
|
|
+ const dynamicFields: Record<string, any> = {}
|
|
|
+ platformData.extProperty.forEach((prop: any) => {
|
|
|
+ if (prop.identifier) {
|
|
|
+ dynamicFields[prop.identifier] = prop.actualValue || ''
|
|
|
+ }
|
|
|
+ })
|
|
|
+ formData.value.dynamicFields = dynamicFields
|
|
|
+ } else {
|
|
|
+ formData.value.dynamicFields = {}
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 如果没有找到数据,初始化默认值
|
|
|
+ formData.value.rdStatus = ''
|
|
|
+ formData.value.techniqueIds = []
|
|
|
+ formData.value.dynamicFields = {}
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 获取当前动态属性数据
|
|
|
+const getCurrentExtProperties = () => {
|
|
|
+ return dynamicAttrs.value.map(attr => {
|
|
|
+ return {
|
|
|
+ name: attr.name,
|
|
|
+ sort: attr.sort,
|
|
|
+ unit: attr.unit,
|
|
|
+ actualValue: formData.value.dynamicFields[attr.identifier] || '',
|
|
|
+ dataType: attr.dataType,
|
|
|
+ maxValue: attr.maxValue,
|
|
|
+ minValue: attr.minValue,
|
|
|
+ required: attr.required,
|
|
|
+ accessMode: attr.accessMode,
|
|
|
+ identifier: attr.identifier,
|
|
|
+ defaultValue: attr.defaultValue
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// 初始化表单数据
|
|
|
+const initFormData = (reportData: any) => {
|
|
|
+ // 处理附件数据格式转换
|
|
|
+ const formattedAttachments = (reportData.attachments || []).map((attachment: any) => ({
|
|
|
+ id: attachment.id,
|
|
|
+ category: attachment.category?.toLowerCase() || 'daily_report',
|
|
|
+ bizId: attachment.bizId,
|
|
|
+ type: attachment.type?.toLowerCase() || 'attachment',
|
|
|
+ filename: attachment.filename,
|
|
|
+ fileType: attachment.fileType, // 使用辅助函数获取文件类型
|
|
|
+ filePath: attachment.filePath,
|
|
|
+ fileSize: attachment.fileSize,
|
|
|
+ remark: attachment.remark || ''
|
|
|
+ }))
|
|
|
+
|
|
|
+ // 确保 techniqueIds 是字符串数组格式
|
|
|
+ let techniqueIds = []
|
|
|
+ if (reportData.techniqueIds && Array.isArray(reportData.techniqueIds)) {
|
|
|
+ techniqueIds = reportData.techniqueIds.map((id: number) => id.toString())
|
|
|
+ }
|
|
|
+
|
|
|
+ formData.value = {
|
|
|
+ ...formData.value,
|
|
|
+ id: reportData.id,
|
|
|
+ deptId: reportData.deptId,
|
|
|
+ taskId: reportData.taskId,
|
|
|
+ platformWell: reportData.platformWell,
|
|
|
+ rdStatus: reportData.rdStatus || '',
|
|
|
+ techniqueIds: techniqueIds,
|
|
|
+ productionStatus: reportData.productionStatus || '',
|
|
|
+ nextPlan: reportData.nextPlan || '',
|
|
|
+ externalRental: reportData.externalRental || '',
|
|
|
+ malfunction: reportData.malfunction || '',
|
|
|
+ faultDowntime: reportData.faultDowntime || '',
|
|
|
+ startTime: reportData.startTime || undefined,
|
|
|
+ endTime: reportData.endTime || undefined,
|
|
|
+ companyId: reportData.companyId || '',
|
|
|
+ dynamicFields: {}, // 确保有初始值
|
|
|
+ // 初始化附件数据
|
|
|
+ attachments: formattedAttachments
|
|
|
+ }
|
|
|
+ queryParams.deptId = reportData.companyId
|
|
|
+ // 设置时间范围选择器
|
|
|
+ if (reportData.startTime && reportData.startTime[0] && reportData.endTime && reportData.endTime[0]) {
|
|
|
+ formData.value.timeRange = [
|
|
|
+ new Date(reportData.startTime[0]),
|
|
|
+ new Date(reportData.endTime[0])
|
|
|
+ ]
|
|
|
+ }
|
|
|
+
|
|
|
+ // 初始化平台井数据
|
|
|
+ initPlatformData(reportData)
|
|
|
+
|
|
|
+ // 初始化动态属性
|
|
|
+ initDynamicAttrs(reportData)
|
|
|
+
|
|
|
+ // 初始化设备数据
|
|
|
+ initDeviceData(reportData)
|
|
|
+
|
|
|
+ // 如果是平台井模式且有数据,初始化 platformWellPairs 中的第一个平台井数据
|
|
|
+ /* if (reportData.platformWell === 1 && formData.value.platformId) {
|
|
|
+ loadPlatformData(formData.value.platformId)
|
|
|
+ } */
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+onMounted(async () => {
|
|
|
+ formLoading.value = true
|
|
|
+ try {
|
|
|
+ // 加载当前登录人所属部门
|
|
|
+ const deptId = userStore.getUser.deptId
|
|
|
+ const dept = await DeptApi.getDept(deptId)
|
|
|
+
|
|
|
+ // 查询瑞都日报详情
|
|
|
+ if (id) {
|
|
|
+ const response = await IotRdDailyReportApi.getIotRdDailyReport(id)
|
|
|
+ dailyReportData.value = response || {}
|
|
|
+ initFormData(dailyReportData.value)
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('初始化数据失败:', error)
|
|
|
+ message.error('数据加载失败')
|
|
|
+ } finally {
|
|
|
+ formLoading.value = false
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+// 详细 审批 平台井 获取工作量列配置
|
|
|
+const getWorkloadColumns = () => {
|
|
|
+ if (!dailyReportData.value.platforms) return [];
|
|
|
+
|
|
|
+ const columns = [];
|
|
|
+ const addedIdentifiers = new Set();
|
|
|
+
|
|
|
+ dailyReportData.value.platforms.forEach(platform => {
|
|
|
+ if (platform.extProperty && Array.isArray(platform.extProperty)) {
|
|
|
+ platform.extProperty.forEach(extProp => {
|
|
|
+ if (!addedIdentifiers.has(extProp.identifier)) {
|
|
|
+ columns.push({
|
|
|
+ key: extProp.identifier,
|
|
|
+ identifier: extProp.identifier,
|
|
|
+ label: `${extProp.name}(${extProp.unit})`
|
|
|
+ });
|
|
|
+ addedIdentifiers.add(extProp.identifier);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return columns;
|
|
|
+};
|
|
|
+
|
|
|
+// 详情 审批 平台井 获取工作量值
|
|
|
+const getWorkloadValue = (platform, identifier) => {
|
|
|
+ if (!platform.extProperty) return '';
|
|
|
+ const prop = platform.extProperty.find(item => item.identifier === identifier);
|
|
|
+ return prop ? prop.actualValue || '' : '';
|
|
|
+};
|
|
|
+
|
|
|
+/** 审批操作 */
|
|
|
+const handleApprove = async (action: 'pass' | 'reject') => {
|
|
|
+ // 只有在审批模式下才执行审批操作
|
|
|
+ if (!isApprovalMode.value) {
|
|
|
+ message.warning('当前不是审批模式')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ // 验证审批表单(如果需要)
|
|
|
+ // await approvalFormRef.value.validate()
|
|
|
+
|
|
|
+ formLoading.value = true
|
|
|
+
|
|
|
+ // 处理时间范围数据
|
|
|
+ if (formData.value.timeRange && formData.value.timeRange.length === 2) {
|
|
|
+ // 将时间范围转换为 LocalTime 格式的字符串
|
|
|
+ const startDate = dayjs(formData.value.timeRange[0])
|
|
|
+ const endDate = dayjs(formData.value.timeRange[1])
|
|
|
+
|
|
|
+ // 格式化为 HH:mm:ss 字符串,后端会自动转换为 LocalTime
|
|
|
+ formData.value.startTime = startDate.format('HH:mm:ss')
|
|
|
+ formData.value.endTime = endDate.format('HH:mm:ss')
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建审批数据,包含审批意见
|
|
|
+ const approveData = {
|
|
|
+ ...formData.value,
|
|
|
+ id: Number(id),
|
|
|
+ opinion: approvalForm.opinion,
|
|
|
+ auditStatus: action === 'pass' ? 20 : 30
|
|
|
+ }
|
|
|
+
|
|
|
+ // 这里可以调用审批API
|
|
|
+ if (action === 'pass') {
|
|
|
+ // 审批通过逻辑
|
|
|
+ await IotRdDailyReportApi.approveRdDailyReport(approveData)
|
|
|
+ message.success('审批通过')
|
|
|
+ } else {
|
|
|
+ // 审批驳回逻辑
|
|
|
+ await IotRdDailyReportApi.approveRdDailyReport(approveData)
|
|
|
+ message.success('审批驳回')
|
|
|
+ }
|
|
|
+ close()
|
|
|
+ } catch (error) {
|
|
|
+ console.error('审批操作失败:', error)
|
|
|
+ message.error('审批操作失败')
|
|
|
+ } finally {
|
|
|
+ formLoading.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+
|
|
|
+.info-table {
|
|
|
+ border: 1px solid #e0e0e0;
|
|
|
+ border-radius: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.table-row {
|
|
|
+ display: flex;
|
|
|
+ border-bottom: 1px solid #e0e0e0;
|
|
|
+}
|
|
|
+
|
|
|
+.table-row:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+}
|
|
|
+
|
|
|
+.table-cell {
|
|
|
+ flex: 1;
|
|
|
+ border-right: 1px solid #e0e0e0;
|
|
|
+ padding: 12px 8px;
|
|
|
+ min-height: 44px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.table-cell:last-child {
|
|
|
+ border-right: none;
|
|
|
+}
|
|
|
+
|
|
|
+.table-cell.full-width {
|
|
|
+ flex: 1;
|
|
|
+ border-right: none;
|
|
|
+}
|
|
|
+
|
|
|
+.cell-content {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.cell-label {
|
|
|
+ font-weight: 500;
|
|
|
+ /* 统一字体大小为 14px(Element 表单默认) */
|
|
|
+ font-size: 14px;
|
|
|
+ color: #606266;
|
|
|
+ min-width: 80px;
|
|
|
+ margin-right: 8px;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.cell-value {
|
|
|
+ /* 统一字体大小为 14px(Element 输入框默认) */
|
|
|
+ font-size: 14px;
|
|
|
+ color: #303133;
|
|
|
+ /* 统一行高为 1.5(Element 组件默认行高) */
|
|
|
+ line-height: 1.5;
|
|
|
+ flex: 1;
|
|
|
+ word-break: break-all;
|
|
|
+}
|
|
|
+
|
|
|
+/* 响应式设计 */
|
|
|
+@media (max-width: 768px) {
|
|
|
+ .table-row {
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+
|
|
|
+ .table-cell {
|
|
|
+ border-right: none;
|
|
|
+ border-bottom: 1px solid #e0e0e0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .table-cell:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.daily-report-title {
|
|
|
+ text-align: center;
|
|
|
+ margin: 20px 0;
|
|
|
+ padding: 10px;
|
|
|
+ border-bottom: 2px solid #409eff;
|
|
|
+}
|
|
|
+
|
|
|
+.daily-report-title h2 {
|
|
|
+ margin: 0;
|
|
|
+ color: #303133;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+
|
|
|
+/* 为第二、三部分增加左右留白 */
|
|
|
+.section-padding {
|
|
|
+ padding-left: 0px;
|
|
|
+ padding-right: 40px;
|
|
|
+}
|
|
|
+
|
|
|
+.project-info-section {
|
|
|
+ margin: 20px 0;
|
|
|
+ padding: 20px;
|
|
|
+ background-color: #f8f9fa;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid #e9ecef;
|
|
|
+}
|
|
|
+
|
|
|
+.info-row {
|
|
|
+ padding: 12px 0;
|
|
|
+ border-bottom: 1px solid #e9ecef;
|
|
|
+}
|
|
|
+
|
|
|
+.info-row:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+}
|
|
|
+
|
|
|
+.info-label {
|
|
|
+ font-weight: bold;
|
|
|
+ color: #495057;
|
|
|
+ margin-right: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.info-value {
|
|
|
+ color: #212529;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-textarea .el-textarea__inner) {
|
|
|
+ min-height: 80px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 确保表单label不换行 */
|
|
|
+:deep(.el-form-item__label) {
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+/* 甲方字段:单行显示+超出省略 */
|
|
|
+.single-line-ellipsis {
|
|
|
+ /* 强制文本单行显示 */
|
|
|
+ white-space: nowrap;
|
|
|
+ /* 超出容器部分隐藏 */
|
|
|
+ overflow: hidden;
|
|
|
+ /* 超出部分显示省略号 */
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ /* 避免文本被截断(可选,根据需求调整) */
|
|
|
+ word-break: normal;
|
|
|
+}
|
|
|
+
|
|
|
+/* 设备配置字段:换行缩进(与首行对齐) */
|
|
|
+.indent-multiline {
|
|
|
+ /* 首行及换行后缩进 2em(与 label 宽度匹配,可根据需求调整) */
|
|
|
+ text-indent: 0em;
|
|
|
+ /* 允许长文本换行(覆盖原有 cell-value 的 break-all,确保中文换行正常) */
|
|
|
+ word-break: break-word;
|
|
|
+ /* 保证换行后文本正常显示(可选,清除可能的 nowrap 影响) */
|
|
|
+ white-space: normal;
|
|
|
+}
|
|
|
+
|
|
|
+/* 添加审批模式下的样式 */
|
|
|
+.approval-notice {
|
|
|
+ margin-top: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 审批模式下表单字段的只读样式 */
|
|
|
+:deep(.el-form-item.is-disabled .el-input__inner),
|
|
|
+:deep(.el-form-item.is-disabled .el-textarea__inner) {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ border-color: #e4e7ed;
|
|
|
+ color: #c0c4cc;
|
|
|
+ cursor: not-allowed;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-form-item.is-disabled .el-select .el-input__inner) {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ border-color: #e4e7ed;
|
|
|
+ color: #c0c4cc;
|
|
|
+ cursor: not-allowed;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-form-item.is-disabled .el-date-editor .el-input__inner) {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ border-color: #e4e7ed;
|
|
|
+ color: #c0c4cc;
|
|
|
+ cursor: not-allowed;
|
|
|
+}
|
|
|
+
|
|
|
+/* 只读模式下表单字段的样式 */
|
|
|
+:deep(.el-form-item.is-disabled .el-input__inner),
|
|
|
+:deep(.el-form-item.is-disabled .el-textarea__inner),
|
|
|
+:deep(.el-form-item.is-disabled .el-select .el-input__inner),
|
|
|
+:deep(.el-form-item.is-disabled .el-date-editor .el-input__inner) {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ border-color: #e4e7ed;
|
|
|
+ color: #606266; /* 保持文字可读性 */
|
|
|
+ cursor: not-allowed;
|
|
|
+}
|
|
|
+
|
|
|
+/* 详情模式下的特殊样式 */
|
|
|
+.detail-mode .cell-value {
|
|
|
+ color: #303133;
|
|
|
+ font-weight: normal;
|
|
|
+}
|
|
|
+
|
|
|
+/* 添加审批意见区域的样式 */
|
|
|
+.approval-opinion-section {
|
|
|
+ margin-top: 20px;
|
|
|
+ border-top: 2px solid #f0f0f0;
|
|
|
+ padding-top: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 审批意见文本域样式 */
|
|
|
+:deep(.approval-opinion .el-textarea__inner) {
|
|
|
+ min-height: 100px;
|
|
|
+ resize: vertical;
|
|
|
+}
|
|
|
+
|
|
|
+/* 审批意见标签样式 */
|
|
|
+:deep(.approval-opinion .el-form-item__label) {
|
|
|
+ font-weight: bold;
|
|
|
+ color: #606266;
|
|
|
+}
|
|
|
+
|
|
|
+/* 附件列表样式 */
|
|
|
+.attachment-list {
|
|
|
+ width: 100%;
|
|
|
+ margin-top: 5px;
|
|
|
+ border: 1px solid #e0e0e0;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 10px;
|
|
|
+ background-color: #fafafa;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.attachment-item {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 8px 12px;
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
+}
|
|
|
+
|
|
|
+.attachment-item:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+}
|
|
|
+
|
|
|
+.attachment-name {
|
|
|
+ flex: 1;
|
|
|
+ color: #606266;
|
|
|
+ font-size: 11px;
|
|
|
+}
|
|
|
+
|
|
|
+.no-attachment {
|
|
|
+ color: #909399;
|
|
|
+ font-style: italic;
|
|
|
+ margin-top: 5px;
|
|
|
+ padding: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 附件名称链接样式 */
|
|
|
+.attachment-name {
|
|
|
+ color: #409eff;
|
|
|
+ text-decoration: underline;
|
|
|
+ cursor: pointer;
|
|
|
+}
|
|
|
+
|
|
|
+.attachment-name:hover {
|
|
|
+ color: #66b1ff;
|
|
|
+}
|
|
|
+
|
|
|
+/* 只读模式下的设备显示样式 */
|
|
|
+.device-display-readonly {
|
|
|
+ color: #606266;
|
|
|
+ font-size: 11px;
|
|
|
+ line-height: 1.5;
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ padding: 8px 12px;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid #e4e7ed;
|
|
|
+ display: inline-block;
|
|
|
+ min-width: 200px;
|
|
|
+}
|
|
|
+
|
|
|
+.no-device {
|
|
|
+ margin-left: 10px;
|
|
|
+ color: #909399;
|
|
|
+ font-style: italic;
|
|
|
+}
|
|
|
+
|
|
|
+/* 设备选择对话框样式 */
|
|
|
+.transfer-container {
|
|
|
+ text-align: center;
|
|
|
+ padding: 0px;
|
|
|
+}
|
|
|
+
|
|
|
+.transfer-component {
|
|
|
+ width: 100%;
|
|
|
+ min-width: 600px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-transfer-panel) {
|
|
|
+ width: 40% !important;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-transfer-panel__item) {
|
|
|
+ display: flex !important;
|
|
|
+ align-items: center !important;
|
|
|
+ height: 32px !important;
|
|
|
+ line-height: 32px !important;
|
|
|
+ padding: 0 8px !important;
|
|
|
+ margin: 0 !important;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+}
|
|
|
+
|
|
|
+.transfer-option-text {
|
|
|
+ display: inline-block;
|
|
|
+ max-width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.el-transfer-panel__list) {
|
|
|
+ width: 100% !important;
|
|
|
+}
|
|
|
+
|
|
|
+.device-display-container {
|
|
|
+ /* 与其他文本域保持相同的宽度和样式 */
|
|
|
+ display: inline-block;
|
|
|
+ width: 100%; /* 减去按钮宽度 */
|
|
|
+ min-height: 32px;
|
|
|
+ line-height: 32px;
|
|
|
+ padding: 0 12px;
|
|
|
+ margin-left: 0px;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid #e4e7ed;
|
|
|
+ background-color: #fff;
|
|
|
+ font-size: 11px;
|
|
|
+ /* 文本溢出处理 */
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+}
|
|
|
+
|
|
|
+/* 只读模式下的设备显示样式 */
|
|
|
+:deep(.is-disabled) .device-display-container {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ color: #606266;
|
|
|
+ cursor: not-allowed;
|
|
|
+}
|
|
|
+
|
|
|
+/* 附件容器样式调整 */
|
|
|
+.attachment-container {
|
|
|
+ /* 与其他文本域保持相同的宽度和边距 */
|
|
|
+ width: 100%;
|
|
|
+ margin-top: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 未选择设备字段的只读样式 */
|
|
|
+:deep(.unselected-device .el-textarea__inner) {
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ border-color: #e4e7ed;
|
|
|
+ color: #909399;
|
|
|
+ cursor: not-allowed;
|
|
|
+ resize: none;
|
|
|
+}
|
|
|
+
|
|
|
+/* 平台井工作量区域专用样式 */
|
|
|
+.platform-workload-section {
|
|
|
+ padding-left: 0px;
|
|
|
+ padding-right: 0px; /* 去掉右侧间距 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 表格样式优化 */
|
|
|
+.platform-workload-el-table {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+/* 表头不换行 */
|
|
|
+:deep(.platform-workload-el-table .el-table__header-wrapper th) {
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+/* 单元格内容不换行 */
|
|
|
+:deep(.platform-workload-el-table .el-table__body-wrapper td) {
|
|
|
+ white-space: nowrap;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+/* 强制设置表头宽度为100% */
|
|
|
+:deep(.platform-workload-el-table .el-table__header) {
|
|
|
+ width: 100% !important;
|
|
|
+ min-width: 100% !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 强制设置表格主体宽度为100% */
|
|
|
+:deep(.platform-workload-el-table .el-table__body) {
|
|
|
+ width: 100% !important;
|
|
|
+ min-width: 100% !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 确保表格填满容器 */
|
|
|
+:deep(.platform-workload-el-table .el-table) {
|
|
|
+ width: 100% !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 表格容器填满父容器 */
|
|
|
+.platform-workload-table {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+</style>
|