|
@@ -1,575 +1,101 @@
|
|
|
-<template>
|
|
|
|
|
- <ContentWrap>
|
|
|
|
|
- <el-tabs type="border-card" tab-position="left" v-loading="loading" style="height: 84vh">
|
|
|
|
|
- <el-tab-pane
|
|
|
|
|
- style="height: 100%"
|
|
|
|
|
- v-for="(deviceItem, deviceIndex) in list"
|
|
|
|
|
- :key="deviceIndex">
|
|
|
|
|
- <template #label>
|
|
|
|
|
- <span
|
|
|
|
|
- :class="['custom-label', { 'has-border': deviceItem.deviceName === '生产日报' }]"
|
|
|
|
|
- v-if="deviceItem.isFill === 1"
|
|
|
|
|
- @click="
|
|
|
|
|
- openFill(
|
|
|
|
|
- deviceItem.deviceCategoryId,
|
|
|
|
|
- deviceItem.deviceId,
|
|
|
|
|
- deviceItem.deptId,
|
|
|
|
|
- deviceItem.deviceName,
|
|
|
|
|
- deviceItem.deviceCode
|
|
|
|
|
- )
|
|
|
|
|
- ">
|
|
|
|
|
- {{ deviceItem.deviceCode }} ({{ deviceItem.deviceName }})
|
|
|
|
|
- </span>
|
|
|
|
|
- <span
|
|
|
|
|
- :class="['custom-label1', { 'has-border': deviceItem.deviceName === '生产日报' }]"
|
|
|
|
|
- v-else
|
|
|
|
|
- @click="
|
|
|
|
|
- openFill(
|
|
|
|
|
- deviceItem.deviceCategoryId,
|
|
|
|
|
- deviceItem.deviceId,
|
|
|
|
|
- deviceItem.deptId,
|
|
|
|
|
- deviceItem.deviceName,
|
|
|
|
|
- deviceItem.deviceCode
|
|
|
|
|
- )
|
|
|
|
|
- ">
|
|
|
|
|
- {{ deviceItem.deviceCode }} ({{ deviceItem.deviceName }})
|
|
|
|
|
- </span>
|
|
|
|
|
- </template>
|
|
|
|
|
- <div class="form-wrapper h-full">
|
|
|
|
|
- <el-form
|
|
|
|
|
- ref="formRef"
|
|
|
|
|
- size="default"
|
|
|
|
|
- label-width="120px"
|
|
|
|
|
- class="scrollable-form"
|
|
|
|
|
- :model="{ attrList: attrList, reportDetails, taskId }">
|
|
|
|
|
- <div style="margin-left: 24px">
|
|
|
|
|
- <el-form class="demo-form-inline" :inline="true">
|
|
|
|
|
- <el-form-item :label="t('common.createTime')" class="custom-label1">
|
|
|
|
|
- <span style="text-decoration: underline">
|
|
|
|
|
- {{ createTime }}
|
|
|
|
|
- </span>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item :label="t('operationFillForm.team')" class="custom-label1">
|
|
|
|
|
- <span style="text-decoration: underline">
|
|
|
|
|
- {{ deviceItem.orgName }}
|
|
|
|
|
- </span>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item
|
|
|
|
|
- v-if="
|
|
|
|
|
- deviceItem.deviceName === '生产日报' &&
|
|
|
|
|
- companyName !== 'ry' &&
|
|
|
|
|
- companyName !== 'rh'
|
|
|
|
|
- "
|
|
|
|
|
- label="井号"
|
|
|
|
|
- class="custom-label1">
|
|
|
|
|
- <span style="text-decoration: underline">
|
|
|
|
|
- {{ deviceItem.wellName }}
|
|
|
|
|
- </span>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item
|
|
|
|
|
- v-else-if="
|
|
|
|
|
- deviceItem.deviceName === '生产日报' &&
|
|
|
|
|
- (companyName === 'ry' || companyName === 'rh') &&
|
|
|
|
|
- taskOptions.length > 0
|
|
|
|
|
- "
|
|
|
|
|
- label="井号"
|
|
|
|
|
- class="custom-label1"
|
|
|
|
|
- prop="taskId">
|
|
|
|
|
- <el-select
|
|
|
|
|
- v-model="taskId"
|
|
|
|
|
- placeholder="请选择井号"
|
|
|
|
|
- :options="taskOptions"
|
|
|
|
|
- class="w-40!" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item
|
|
|
|
|
- v-else-if="deviceItem.deviceName === '生产日报'"
|
|
|
|
|
- label="井号"
|
|
|
|
|
- class="custom-label1">
|
|
|
|
|
- <span style="text-decoration: underline">
|
|
|
|
|
- {{ deviceItem.wellName }}
|
|
|
|
|
- </span>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-row :gutter="20">
|
|
|
|
|
- <el-col
|
|
|
|
|
- v-for="(summaryItem, summaryIndex) in attrList1"
|
|
|
|
|
- :key="summaryIndex"
|
|
|
|
|
- :span="24">
|
|
|
|
|
- <el-form-item :label="summaryItem.name" class="custom-label1">
|
|
|
|
|
- <span style="text-decoration: underline">
|
|
|
|
|
- {{ summaryItem.totalRunTime }}
|
|
|
|
|
- </span>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
- <!-- <el-form-item :label="t('operationFillForm.sumTime')" class="custom-label1">
|
|
|
|
|
- <span style="text-decoration: underline;">
|
|
|
|
|
- {{totalRunTime1}}h
|
|
|
|
|
- </span>
|
|
|
|
|
- </el-form-item>-->
|
|
|
|
|
- </el-form>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div
|
|
|
|
|
- v-for="(attrItem, attrIndex) in attrList.filter(
|
|
|
|
|
- (item) => !keys.includes(item.description)
|
|
|
|
|
- )"
|
|
|
|
|
- :key="attrIndex"
|
|
|
|
|
- style="margin-left: 24px">
|
|
|
|
|
- <!-- 添加提示文字 -->
|
|
|
|
|
- <div v-if="attrItem.isCollection === 1" class="plc-tip">
|
|
|
|
|
- <el-alert
|
|
|
|
|
- :title="t('operationFillForm.alert')"
|
|
|
|
|
- type="warning"
|
|
|
|
|
- :closable="false"
|
|
|
|
|
- center
|
|
|
|
|
- show-icon
|
|
|
|
|
- style="width: 320px" />
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <el-form-item
|
|
|
|
|
- v-if="companyName === 'ry' ? attrItem.description !== 'productionStatus' : true"
|
|
|
|
|
- :label="attrItem.name"
|
|
|
|
|
- :prop="
|
|
|
|
|
- 'attrList.' +
|
|
|
|
|
- attrList.findIndex((i) => i.description === attrItem.description) +
|
|
|
|
|
- '.fillContent'
|
|
|
|
|
- "
|
|
|
|
|
- label-position="top"
|
|
|
|
|
- :rules="rules[attrItem.description]">
|
|
|
|
|
- <div v-if="fillStatus === '1'">
|
|
|
|
|
- <el-select
|
|
|
|
|
- disabled
|
|
|
|
|
- v-model="attrItem.fillContent"
|
|
|
|
|
- v-if="attrItem.type === 'enum' && attrItem.description !== null"
|
|
|
|
|
- style="width: 200px">
|
|
|
|
|
- <el-option
|
|
|
|
|
- v-for="dict in attrItem.name === '非生产原因'
|
|
|
|
|
- ? getIntDictOptions(attrItem.description)
|
|
|
|
|
- : getStrDictOptions(attrItem.description)"
|
|
|
|
|
- :key="dict.label"
|
|
|
|
|
- :label="dict.label"
|
|
|
|
|
- :value="
|
|
|
|
|
- attrItem.name === '非生产原因' ? Number(dict.value) : dict.value.toString()
|
|
|
|
|
- " />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-else
|
|
|
|
|
- v-model="attrItem.fillContent"
|
|
|
|
|
- clearable
|
|
|
|
|
- style="width: 200px; margin-right: 10px"
|
|
|
|
|
- disabled />
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-else-if="attrItem.type === 'textarea'"
|
|
|
|
|
- v-model="attrItem.fillContent"
|
|
|
|
|
- type="textarea"
|
|
|
|
|
- clearable
|
|
|
|
|
- style="width: 200px" />
|
|
|
|
|
- <el-select
|
|
|
|
|
- v-model="attrItem.fillContent"
|
|
|
|
|
- clearable
|
|
|
|
|
- v-else-if="attrItem.type === 'enum' && attrItem.description !== null"
|
|
|
|
|
- style="width: 200px"
|
|
|
|
|
- filterable>
|
|
|
|
|
- <el-option
|
|
|
|
|
- v-for="dict in attrItem.name === '非生产原因'
|
|
|
|
|
- ? getIntDictOptions(attrItem.description)
|
|
|
|
|
- : getStrDictOptions(attrItem.description)"
|
|
|
|
|
- :key="dict.label"
|
|
|
|
|
- :label="dict.label"
|
|
|
|
|
- :value="
|
|
|
|
|
- attrItem.name === '非生产原因' ? Number(dict.value) : dict.value.toString()
|
|
|
|
|
- " />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-else
|
|
|
|
|
- v-model="attrItem.fillContent"
|
|
|
|
|
- clearable
|
|
|
|
|
- style="width: 200px"
|
|
|
|
|
- :placeholder="
|
|
|
|
|
- attrItem.type === 'double'
|
|
|
|
|
- ? t('operationFillForm.enterNumber')
|
|
|
|
|
- : t('operationFillForm.enterContent')
|
|
|
|
|
- "
|
|
|
|
|
- @input="handleInput(attrItem)"
|
|
|
|
|
- :maxlength="
|
|
|
|
|
- attrItem.type === 'double' ? calculateMaxLength(attrItem) : undefined
|
|
|
|
|
- " />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div
|
|
|
|
|
- v-for="(attrItem, attrIndex) in attrList"
|
|
|
|
|
- :key="attrIndex"
|
|
|
|
|
- style="margin-left: 24px">
|
|
|
|
|
- <el-divider v-if="attrItem.description === 'repairTime'" content-position="left"
|
|
|
|
|
- >非生产时间</el-divider
|
|
|
|
|
- >
|
|
|
|
|
- <el-form-item
|
|
|
|
|
- v-if="
|
|
|
|
|
- keys.includes(attrItem.description) && attrItem.description !== 'otherNptReason'
|
|
|
|
|
- "
|
|
|
|
|
- label-position="top"
|
|
|
|
|
- :label="attrItem.name"
|
|
|
|
|
- :prop="'attrList.' + attrIndex + '.fillContent'"
|
|
|
|
|
- :rules="rules[attrItem.description]">
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- class="w-80!"
|
|
|
|
|
- :min="0"
|
|
|
|
|
- :max="24"
|
|
|
|
|
- v-model="attrItem.fillContent"
|
|
|
|
|
- :controls="false"
|
|
|
|
|
- align="left"
|
|
|
|
|
- placeholder="请输入数字"
|
|
|
|
|
- :disabled="fillStatus === '1'" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
-
|
|
|
|
|
- <el-form-item
|
|
|
|
|
- v-else-if="attrItem.description === 'otherNptReason'"
|
|
|
|
|
- label-position="top"
|
|
|
|
|
- :label="attrItem.name"
|
|
|
|
|
- :prop="'attrList.' + attrIndex + '.fillContent'"
|
|
|
|
|
- :rules="rules[attrItem.description]">
|
|
|
|
|
- <el-input
|
|
|
|
|
- class="w-80!"
|
|
|
|
|
- v-model="attrItem.fillContent"
|
|
|
|
|
- placeholder="请输入其他非生产原因"
|
|
|
|
|
- :disabled="fillStatus === '1'" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div v-if="companyName === 'ry' && deviceItem.deviceName === '生产日报'">
|
|
|
|
|
- <div class="flex items-center justify-between mb-6">
|
|
|
|
|
- <div class="flex items-center gap-2">
|
|
|
|
|
- <div class="bg-[var(--el-color-primary)] w-1 h-5 rounded-full"></div>
|
|
|
|
|
- <div class="text-lg font-medium text-[var(--el-text-color-primary)]"
|
|
|
|
|
- >生产动态</div
|
|
|
|
|
- >
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-button
|
|
|
|
|
- :disabled="fillStatus === '1'"
|
|
|
|
|
- type="primary"
|
|
|
|
|
- link
|
|
|
|
|
- :icon="Plus"
|
|
|
|
|
- @click="addProductionStatusRow">
|
|
|
|
|
- 添加一行
|
|
|
|
|
- </el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
- <el-form-item prop="reportDetails" class="table-form-item">
|
|
|
|
|
- <ZmTable :data="reportDetails" :loading="false" class="mb-4">
|
|
|
|
|
- <ZmTableColumn label="日期" :width="180" prop="reportDate">
|
|
|
|
|
- <template #default="{ row, $index }">
|
|
|
|
|
- <el-form-item
|
|
|
|
|
- v-if="$index >= 0"
|
|
|
|
|
- class="mb-0!"
|
|
|
|
|
- :prop="`reportDetails.${$index}.reportDate`"
|
|
|
|
|
- :rules="{
|
|
|
|
|
- required: true,
|
|
|
|
|
- message: '请选择日期',
|
|
|
|
|
- trigger: ['change', 'blur'],
|
|
|
|
|
- type: 'number'
|
|
|
|
|
- }">
|
|
|
|
|
- <el-date-picker
|
|
|
|
|
- v-model="row.reportDate"
|
|
|
|
|
- placeholder="选择日期"
|
|
|
|
|
- clearable
|
|
|
|
|
- class="w-full!"
|
|
|
|
|
- value-format="x"
|
|
|
|
|
- :disabled="fillStatus === '1'"
|
|
|
|
|
- @change="inputCurrentDepth()" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </template>
|
|
|
|
|
- </ZmTableColumn>
|
|
|
|
|
-
|
|
|
|
|
- <ZmTableColumn :width="130" label="开始时间" prop="startTime">
|
|
|
|
|
- <template #default="{ row, $index }">
|
|
|
|
|
- <el-form-item
|
|
|
|
|
- v-if="$index >= 0"
|
|
|
|
|
- class="mb-0!"
|
|
|
|
|
- :prop="`reportDetails.${$index}.startTime`"
|
|
|
|
|
- :rules="{
|
|
|
|
|
- required: true,
|
|
|
|
|
- message: '请选择开始时间',
|
|
|
|
|
- trigger: ['change', 'blur']
|
|
|
|
|
- }">
|
|
|
|
|
- <el-time-picker
|
|
|
|
|
- v-model="row.startTime"
|
|
|
|
|
- placeholder="选择开始时间"
|
|
|
|
|
- clearable
|
|
|
|
|
- format="HH:mm"
|
|
|
|
|
- value-format="HH:mm"
|
|
|
|
|
- class="w-full!"
|
|
|
|
|
- @change="acalculateDuration(row)"
|
|
|
|
|
- :disabled="fillStatus === '1'" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </template>
|
|
|
|
|
- </ZmTableColumn>
|
|
|
|
|
- <ZmTableColumn :width="130" label="结束时间" prop="endTime">
|
|
|
|
|
- <template #default="{ row, $index }">
|
|
|
|
|
- <el-form-item
|
|
|
|
|
- v-if="$index >= 0"
|
|
|
|
|
- class="mb-0!"
|
|
|
|
|
- :prop="`reportDetails.${$index}.endTime`"
|
|
|
|
|
- :rules="{
|
|
|
|
|
- required: true,
|
|
|
|
|
- message: '请选择结束时间',
|
|
|
|
|
- trigger: ['change', 'blur']
|
|
|
|
|
- }">
|
|
|
|
|
- <el-time-picker
|
|
|
|
|
- v-model="row.endTime"
|
|
|
|
|
- placeholder="选择结束时间"
|
|
|
|
|
- clearable
|
|
|
|
|
- format="HH:mm"
|
|
|
|
|
- value-format="HH:mm"
|
|
|
|
|
- class="w-full!"
|
|
|
|
|
- @change="acalculateDuration(row)"
|
|
|
|
|
- :disabled="fillStatus === '1'" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </template>
|
|
|
|
|
- </ZmTableColumn>
|
|
|
|
|
- <ZmTableColumn :width="80" label="时长(H)" prop="duration" />
|
|
|
|
|
-
|
|
|
|
|
- <ZmTableColumn label="工况" min-width="140">
|
|
|
|
|
- <template #default="{ row, $index }">
|
|
|
|
|
- <el-form-item
|
|
|
|
|
- v-if="$index >= 0"
|
|
|
|
|
- :prop="`reportDetails.${$index}.currentOperation`"
|
|
|
|
|
- :rules="{
|
|
|
|
|
- required: true,
|
|
|
|
|
- message: '请输入工况',
|
|
|
|
|
- trigger: ['change', 'blur']
|
|
|
|
|
- }"
|
|
|
|
|
- class="mb-0!">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="row.currentOperation"
|
|
|
|
|
- type="textarea"
|
|
|
|
|
- :autosize="{ minRows: 1 }"
|
|
|
|
|
- show-word-limit
|
|
|
|
|
- :maxlength="1000"
|
|
|
|
|
- placeholder="请输入工况"
|
|
|
|
|
- :disabled="fillStatus === '1'" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </template>
|
|
|
|
|
- </ZmTableColumn>
|
|
|
|
|
-
|
|
|
|
|
- <ZmTableColumn v-if="showDepth" label="结束井深(m)" min-width="80">
|
|
|
|
|
- <template #default="{ row, $index }">
|
|
|
|
|
- <el-form-item
|
|
|
|
|
- v-if="$index >= 0"
|
|
|
|
|
- :prop="`reportDetails.${$index}.currentDepth`"
|
|
|
|
|
- :rules="[
|
|
|
|
|
- {
|
|
|
|
|
- required: true,
|
|
|
|
|
- message: '请输入结束井深',
|
|
|
|
|
- trigger: ['blur']
|
|
|
|
|
- }
|
|
|
|
|
- // { validator: validateLastCurrentDepth, trigger: ['change', 'blur'] }
|
|
|
|
|
- ]"
|
|
|
|
|
- class="mb-0!">
|
|
|
|
|
- <el-input-number
|
|
|
|
|
- v-model="row.currentDepth"
|
|
|
|
|
- :min="0"
|
|
|
|
|
- :controls="false"
|
|
|
|
|
- class="!w-full"
|
|
|
|
|
- align="left"
|
|
|
|
|
- placeholder="请输入结束井深"
|
|
|
|
|
- @input="() => inputCurrentDepth()"
|
|
|
|
|
- :disabled="fillStatus === '1'">
|
|
|
|
|
- <template #suffix> m </template>
|
|
|
|
|
- </el-input-number>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </template>
|
|
|
|
|
- </ZmTableColumn>
|
|
|
|
|
-
|
|
|
|
|
- <ZmTableColumn label="详细描述" min-width="200" align="center">
|
|
|
|
|
- <template #default="{ row, $index }">
|
|
|
|
|
- <el-form-item
|
|
|
|
|
- v-if="$index >= 0"
|
|
|
|
|
- :prop="`reportDetails.${$index}.constructionDetail`"
|
|
|
|
|
- :rules="{ required: true, message: '请输入详细描述', trigger: 'blur' }"
|
|
|
|
|
- class="mb-0!">
|
|
|
|
|
- <el-input
|
|
|
|
|
- v-model="row.constructionDetail"
|
|
|
|
|
- type="textarea"
|
|
|
|
|
- :autosize="{ minRows: 1 }"
|
|
|
|
|
- show-word-limit
|
|
|
|
|
- :maxlength="1000"
|
|
|
|
|
- placeholder="请输入详细描述"
|
|
|
|
|
- :disabled="fillStatus === '1'" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </template>
|
|
|
|
|
- </ZmTableColumn>
|
|
|
|
|
-
|
|
|
|
|
- <ZmTableColumn label="操作" width="80" fixed="right" align="center">
|
|
|
|
|
- <template #default="{ $index }">
|
|
|
|
|
- <el-button
|
|
|
|
|
- link
|
|
|
|
|
- type="danger"
|
|
|
|
|
- :icon="Delete"
|
|
|
|
|
- @click="removeProductionStatusRow($index)"
|
|
|
|
|
- :disabled="fillStatus === '1'">
|
|
|
|
|
- 删除
|
|
|
|
|
- </el-button>
|
|
|
|
|
- </template>
|
|
|
|
|
- </ZmTableColumn>
|
|
|
|
|
- </ZmTable>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <el-form-item>
|
|
|
|
|
- <el-button
|
|
|
|
|
- :loading="submitLoading"
|
|
|
|
|
- type="primary"
|
|
|
|
|
- @click="getFillInfo"
|
|
|
|
|
- v-show="showStatus"
|
|
|
|
|
- >{{ t('operationFillForm.confirm') }}</el-button
|
|
|
|
|
- >
|
|
|
|
|
- <el-button type="info" @click="deleteFillInfo" v-show="showStatus">{{
|
|
|
|
|
- t('operationFill.clear')
|
|
|
|
|
- }}</el-button>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </el-form>
|
|
|
|
|
- </div>
|
|
|
|
|
- </el-tab-pane>
|
|
|
|
|
- </el-tabs>
|
|
|
|
|
- </ContentWrap>
|
|
|
|
|
-</template>
|
|
|
|
|
-
|
|
|
|
|
-<script setup lang="ts">
|
|
|
|
|
-import { IotOpeationFillApi, IotOpeationFillVO } from '@/api/pms/iotopeationfill'
|
|
|
|
|
-import { ElMessage, FormInstance, FormRules } from 'element-plus'
|
|
|
|
|
-import moment from 'moment'
|
|
|
|
|
-import { getIntDictOptions, getStrDictOptions } from '@/utils/dict'
|
|
|
|
|
-import { useRoute } from 'vue-router'
|
|
|
|
|
|
|
+<script lang="ts" setup>
|
|
|
|
|
+import { IotOpeationFillApi } from '@/api/pms/iotopeationfill'
|
|
|
import { calculateDuration, formatT } from '@/utils/formatTime'
|
|
import { calculateDuration, formatT } from '@/utils/formatTime'
|
|
|
-import { Delete, Plus } from '@element-plus/icons-vue'
|
|
|
|
|
-import { useDebounceFn } from '@vueuse/core'
|
|
|
|
|
|
|
+import { getDictOptions } from '@/utils/dict'
|
|
|
|
|
+import { ArrowRight, Delete, Monitor, Plus, Warning } from '@element-plus/icons-vue'
|
|
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
|
|
+import type { FormInstance, FormItemRule } from 'element-plus'
|
|
|
import dayjs from 'dayjs'
|
|
import dayjs from 'dayjs'
|
|
|
|
|
+import { useRoute } from 'vue-router'
|
|
|
|
|
|
|
|
-/** 运行记录填报 列表 */
|
|
|
|
|
defineOptions({ name: 'FillOrderInfo' })
|
|
defineOptions({ name: 'FillOrderInfo' })
|
|
|
|
|
|
|
|
-const route = useRoute()
|
|
|
|
|
-
|
|
|
|
|
-const message = useMessage() // 消息弹窗
|
|
|
|
|
-const { t } = useI18n() // 国际化
|
|
|
|
|
-/** 提交表单 */
|
|
|
|
|
-const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
|
|
|
|
-const loading = ref(true) // 列表的加载中
|
|
|
|
|
-const { params } = useRoute() // 查询参数
|
|
|
|
|
-const deptId = params.id as string
|
|
|
|
|
-const list = ref<IotOpeationFillVO[]>([]) // 列表的数据
|
|
|
|
|
-const attrList = ref<IotOpeationFillVO[]>([]) // 非累计属性集合
|
|
|
|
|
-const attrList1 = ref<IotOpeationFillVO[]>([]) // 累计属性集合
|
|
|
|
|
-const attrList2 = ref<IotOpeationFillVO[]>([]) // 属性集合
|
|
|
|
|
-let companyName = ref('')
|
|
|
|
|
-
|
|
|
|
|
-let fillStatus = deptId.split(',')[4]
|
|
|
|
|
-let createTime = formatTimestamp(JSON.parse(deptId.split(',')[2].substring(0, 10)))
|
|
|
|
|
-let showStatus = true
|
|
|
|
|
-const queryParams = reactive<any>({
|
|
|
|
|
- pageNo: 1,
|
|
|
|
|
- pageSize: 10,
|
|
|
|
|
- deviceCode: undefined,
|
|
|
|
|
- deviceName: undefined,
|
|
|
|
|
- fillContent: undefined,
|
|
|
|
|
- deviceType: undefined,
|
|
|
|
|
- deviceComponent: undefined,
|
|
|
|
|
- deptId: undefined,
|
|
|
|
|
- orgName: undefined,
|
|
|
|
|
- proId: undefined,
|
|
|
|
|
- proName: undefined,
|
|
|
|
|
- teamId: undefined,
|
|
|
|
|
- teamName: undefined,
|
|
|
|
|
- dutyName: undefined,
|
|
|
|
|
- creDate: [],
|
|
|
|
|
- createTime: [],
|
|
|
|
|
- deviceCategoryId: 1,
|
|
|
|
|
- deviceId: undefined,
|
|
|
|
|
- threshold: undefined,
|
|
|
|
|
- defaultValue: undefined,
|
|
|
|
|
- isSum: undefined
|
|
|
|
|
-})
|
|
|
|
|
-
|
|
|
|
|
-interface ReportDetail {
|
|
|
|
|
- reportDate: number
|
|
|
|
|
- startTime: string
|
|
|
|
|
- endTime: string
|
|
|
|
|
- duration: number
|
|
|
|
|
- currentDepth: number
|
|
|
|
|
- currentOperation: string
|
|
|
|
|
- constructionDetail: string
|
|
|
|
|
|
|
+interface OperationRouteInfo {
|
|
|
|
|
+ rawId: string
|
|
|
|
|
+ deptId: number
|
|
|
|
|
+ userId: number
|
|
|
|
|
+ createTime: number
|
|
|
|
|
+ orderId: number
|
|
|
|
|
+ orderStatus: string
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const taskId = ref<number | undefined>(undefined)
|
|
|
|
|
-const taskOptions = ref<{ label: string; value: number }[]>([])
|
|
|
|
|
-
|
|
|
|
|
-const reportDetails = ref<ReportDetail[]>([])
|
|
|
|
|
-
|
|
|
|
|
-const addProductionStatusRow = () => {
|
|
|
|
|
- if (!reportDetails.value) {
|
|
|
|
|
- reportDetails.value = []
|
|
|
|
|
- }
|
|
|
|
|
- reportDetails.value.push({
|
|
|
|
|
- reportDate: createTime ? dayjs(createTime).valueOf() : dayjs().valueOf(),
|
|
|
|
|
- startTime: '',
|
|
|
|
|
- endTime: '',
|
|
|
|
|
- duration: 0,
|
|
|
|
|
- currentDepth: 0,
|
|
|
|
|
- currentOperation: '',
|
|
|
|
|
- constructionDetail: ''
|
|
|
|
|
- })
|
|
|
|
|
|
|
+interface DeviceFillInfo {
|
|
|
|
|
+ deptId: number
|
|
|
|
|
+ userId: number
|
|
|
|
|
+ createTime: string | number
|
|
|
|
|
+ orderId: number
|
|
|
|
|
+ deviceCategoryId: number
|
|
|
|
|
+ deviceId: number
|
|
|
|
|
+ deviceCode: string
|
|
|
|
|
+ deviceName: string
|
|
|
|
|
+ isFill: string
|
|
|
|
|
+ orgName: string
|
|
|
|
|
+ wellName: string
|
|
|
|
|
+ wellNamePair: WellNameOption[] | null
|
|
|
|
|
+ noGasTasks: number[]
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const removeProductionStatusRow = (index: number) => {
|
|
|
|
|
- if (index === 0) {
|
|
|
|
|
- message.warning('至少填写一条生产动态')
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- reportDetails.value.splice(index, 1)
|
|
|
|
|
|
|
+interface WellNameOption {
|
|
|
|
|
+ label: string
|
|
|
|
|
+ value: number
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const acalculateDuration = (row: any) => {
|
|
|
|
|
- calculateDuration(row)
|
|
|
|
|
- inputCurrentDepth()
|
|
|
|
|
|
|
+interface FillAttribute {
|
|
|
|
|
+ defaultValue: string | number | null
|
|
|
|
|
+ name: string
|
|
|
|
|
+ fillContent: string | number | null
|
|
|
|
|
+ type: string
|
|
|
|
|
+ description: string
|
|
|
|
|
+ totalRunTime?: string | number | null
|
|
|
|
|
+ threshold?: string | number | null
|
|
|
|
|
+ isCollection?: string | number | null
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const inputCurrentDepth = useDebounceFn(function inputCurrentDepth() {
|
|
|
|
|
- const details = reportDetails.value
|
|
|
|
|
-
|
|
|
|
|
- if (Array.isArray(details) && details.length > 0) {
|
|
|
|
|
- const latestDetail = details.reduce((prev, current) => {
|
|
|
|
|
- const currentFullTime = dayjs(current.reportDate)
|
|
|
|
|
- .hour(parseInt(current.endTime.split(':')[0]))
|
|
|
|
|
- .minute(parseInt(current.endTime.split(':')[1]))
|
|
|
|
|
- .valueOf()
|
|
|
|
|
-
|
|
|
|
|
- const prevFullTime = dayjs(prev.reportDate)
|
|
|
|
|
- .hour(parseInt(prev.endTime.split(':')[0]))
|
|
|
|
|
- .minute(parseInt(prev.endTime.split(':')[1]))
|
|
|
|
|
- .valueOf()
|
|
|
|
|
-
|
|
|
|
|
- return currentFullTime >= prevFullTime ? current : prev
|
|
|
|
|
- })
|
|
|
|
|
|
|
+interface DeviceAttrsResponse {
|
|
|
|
|
+ nonSumList: FillAttribute[]
|
|
|
|
|
+ sumList: FillAttribute[]
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- const currentDepth = attrList.value.find((item) => item.description === 'currentDepth')
|
|
|
|
|
- if (currentDepth) currentDepth.fillContent = latestDetail.currentDepth
|
|
|
|
|
- }
|
|
|
|
|
-}, 300)
|
|
|
|
|
|
|
+interface ReportDetail {
|
|
|
|
|
+ reportDate: string | number | null
|
|
|
|
|
+ startTime: string
|
|
|
|
|
+ endTime: string
|
|
|
|
|
+ duration: number
|
|
|
|
|
+ currentDepth: number | null
|
|
|
|
|
+ currentOperation: string
|
|
|
|
|
+ constructionDetail: string
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-// const inputCurrentDepth = useDebounceFn(function inputCurrentDepth(val: any, index: number) {
|
|
|
|
|
-// if (reportDetails.value && index === reportDetails.value.length - 1) {
|
|
|
|
|
-// const currentDepth = attrList.value.find((item) => item.description === 'currentDepth')
|
|
|
|
|
-// if (currentDepth) currentDepth.fillContent = val
|
|
|
|
|
-// }
|
|
|
|
|
-// }, 300)
|
|
|
|
|
|
|
+const route = useRoute()
|
|
|
|
|
|
|
|
-const showDepth = computed(() => {
|
|
|
|
|
- return attrList.value.some((item) => item.description === 'currentDepth')
|
|
|
|
|
|
|
+/** 路由参数格式:deptId,userId,createTime,orderId,orderStatus */
|
|
|
|
|
+const routeInfo = reactive<OperationRouteInfo>({
|
|
|
|
|
+ rawId: '',
|
|
|
|
|
+ deptId: 0,
|
|
|
|
|
+ userId: 0,
|
|
|
|
|
+ createTime: 0,
|
|
|
|
|
+ orderId: 0,
|
|
|
|
|
+ orderStatus: ''
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-const NON_KEYS = [
|
|
|
|
|
|
|
+const deviceFillList = ref<DeviceFillInfo[]>([])
|
|
|
|
|
+const selectedDeviceIndex = ref(-1)
|
|
|
|
|
+const companyName = ref('')
|
|
|
|
|
+const taskId = ref<number>()
|
|
|
|
|
+const nonSumList = ref<FillAttribute[]>([])
|
|
|
|
|
+const sumList = ref<FillAttribute[]>([])
|
|
|
|
|
+const reportDetails = ref<ReportDetail[]>([])
|
|
|
|
|
+const reportDetailsLoading = ref(false)
|
|
|
|
|
+const deviceAttrsLoading = ref(false)
|
|
|
|
|
+const fillFormRef = ref<FormInstance>()
|
|
|
|
|
+const fillFormModel = computed(() => ({
|
|
|
|
|
+ nonSumList: nonSumList.value,
|
|
|
|
|
+ reportDetails: reportDetails.value
|
|
|
|
|
+}))
|
|
|
|
|
+const fillStatus = computed(() => routeInfo.orderStatus)
|
|
|
|
|
+const isFormDisabled = computed(() => fillStatus.value === '1')
|
|
|
|
|
+
|
|
|
|
|
+const keys = [
|
|
|
'repairTime',
|
|
'repairTime',
|
|
|
'selfStopTime',
|
|
'selfStopTime',
|
|
|
'accidentTime',
|
|
'accidentTime',
|
|
@@ -581,10 +107,16 @@ const NON_KEYS = [
|
|
|
'partyaResource',
|
|
'partyaResource',
|
|
|
'relocationTime',
|
|
'relocationTime',
|
|
|
'winterBreakTime',
|
|
'winterBreakTime',
|
|
|
- 'otherNptTime'
|
|
|
|
|
|
|
+ 'otherNptTime',
|
|
|
|
|
+ 'drillingWorkingTime',
|
|
|
|
|
+ 'otherProductionTime',
|
|
|
|
|
+ 'ratedProductionTime',
|
|
|
|
|
+ 'productionTime',
|
|
|
|
|
+ 'dailyInjectGasTime',
|
|
|
|
|
+ 'otherNptReason'
|
|
|
]
|
|
]
|
|
|
|
|
|
|
|
-const keys = [
|
|
|
|
|
|
|
+const NON_KEYS = [
|
|
|
'repairTime',
|
|
'repairTime',
|
|
|
'selfStopTime',
|
|
'selfStopTime',
|
|
|
'accidentTime',
|
|
'accidentTime',
|
|
@@ -596,706 +128,1706 @@ const keys = [
|
|
|
'partyaResource',
|
|
'partyaResource',
|
|
|
'relocationTime',
|
|
'relocationTime',
|
|
|
'winterBreakTime',
|
|
'winterBreakTime',
|
|
|
- 'otherNptTime',
|
|
|
|
|
|
|
+ 'otherNptTime'
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+const MAX_24_KEYS = [
|
|
|
|
|
+ ...NON_KEYS,
|
|
|
'drillingWorkingTime',
|
|
'drillingWorkingTime',
|
|
|
'otherProductionTime',
|
|
'otherProductionTime',
|
|
|
'ratedProductionTime',
|
|
'ratedProductionTime',
|
|
|
'productionTime',
|
|
'productionTime',
|
|
|
- 'dailyInjectGasTime',
|
|
|
|
|
- 'otherNptReason'
|
|
|
|
|
|
|
+ 'dailyInjectGasTime'
|
|
|
]
|
|
]
|
|
|
|
|
|
|
|
-const sumNonProdTimes = () => {
|
|
|
|
|
- let sum = 0
|
|
|
|
|
- NON_KEYS.forEach((field) => {
|
|
|
|
|
- sum += attrList.value.find((item) => item.description === field)?.fillContent || 0
|
|
|
|
|
- })
|
|
|
|
|
- return sum
|
|
|
|
|
-}
|
|
|
|
|
|
|
+const REQUIRED_KEYS = ['daySupervisors', 'nightSupervisors']
|
|
|
|
|
|
|
|
-const rhValidateTotalTime =
|
|
|
|
|
- (isNon: boolean = false) =>
|
|
|
|
|
- (_rule: any, _value: any, callback: any) => {
|
|
|
|
|
- const gasTime =
|
|
|
|
|
- attrList.value.find((item) => item.description === 'dailyInjectGasTime')?.fillContent || 0
|
|
|
|
|
- const nonProdSum = sumNonProdTimes()
|
|
|
|
|
|
|
+const selectedDevice = computed(() => deviceFillList.value[selectedDeviceIndex.value])
|
|
|
|
|
+const isDailyReport = computed(() => selectedDevice.value?.deviceName === '生产日报')
|
|
|
|
|
+const showProductionDetails = computed(() => companyName.value === 'ry' && isDailyReport.value)
|
|
|
|
|
+const showFormActions = computed(() => {
|
|
|
|
|
+ return !['1', '3'].includes(fillStatus.value) && selectedDevice.value?.isFill !== '1'
|
|
|
|
|
+})
|
|
|
|
|
+const showDepth = computed(() =>
|
|
|
|
|
+ nonSumList.value.some((attribute) => attribute.description === 'currentDepth')
|
|
|
|
|
+)
|
|
|
|
|
+const showWellSelector = computed(() => {
|
|
|
|
|
+ return Boolean(
|
|
|
|
|
+ isDailyReport.value &&
|
|
|
|
|
+ ['rh', 'ry'].includes(companyName.value) &&
|
|
|
|
|
+ selectedDevice.value?.wellNamePair?.length
|
|
|
|
|
+ )
|
|
|
|
|
+})
|
|
|
|
|
|
|
|
- let total = 0
|
|
|
|
|
- let msg = ''
|
|
|
|
|
|
|
+const toNumber = (value: string) => {
|
|
|
|
|
+ const numberValue = Number(value)
|
|
|
|
|
+ return Number.isFinite(numberValue) ? numberValue : 0
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- total = parseFloat((gasTime + nonProdSum).toFixed(2))
|
|
|
|
|
- msg = `运转(${gasTime})+非生产(${nonProdSum})=${total}H,必须为24H`
|
|
|
|
|
|
|
+const formatCreateTime = (value: string | number) => {
|
|
|
|
|
+ const stringValue = String(value)
|
|
|
|
|
|
|
|
- if (Math.abs(total - 24) > 0.01) {
|
|
|
|
|
- if (!isNon) callback(new Error(msg))
|
|
|
|
|
- else callback(new Error())
|
|
|
|
|
- } else {
|
|
|
|
|
- callback()
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (/^\d+$/.test(stringValue)) {
|
|
|
|
|
+ return dayjs.unix(Number(stringValue.slice(0, 10))).format('YYYY-MM-DD')
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// const ryXjValidateTotalTime =
|
|
|
|
|
-// (isNon: boolean = false) =>
|
|
|
|
|
-// (_rule: any, _value: any, callback: any) => {
|
|
|
|
|
-// const rateTime =
|
|
|
|
|
-// attrList.value.find((item) => item.description === 'ratedProductionTime')?.fillContent || 0
|
|
|
|
|
-// const time =
|
|
|
|
|
-// attrList.value.find((item) => item.description === 'productionTime')?.fillContent || 0
|
|
|
|
|
-
|
|
|
|
|
-// const nonProdSum = sumNonProdTimes()
|
|
|
|
|
-
|
|
|
|
|
-// let total = 0
|
|
|
|
|
-// let msg = ''
|
|
|
|
|
-
|
|
|
|
|
-// total = parseFloat((time + nonProdSum).toFixed(2))
|
|
|
|
|
-// msg = `生产(${time})+非生产(${nonProdSum})=${total}H,必须等于额定${rateTime}H`
|
|
|
|
|
-
|
|
|
|
|
-// if (Math.abs(total - rateTime) > 0.01) {
|
|
|
|
|
-// if (!isNon) callback(new Error(msg))
|
|
|
|
|
-// else callback(new Error())
|
|
|
|
|
-// } else {
|
|
|
|
|
-// callback()
|
|
|
|
|
-// }
|
|
|
|
|
-// }
|
|
|
|
|
-
|
|
|
|
|
-const ryValidateTotalTime =
|
|
|
|
|
- (isNon: boolean = false) =>
|
|
|
|
|
- (_rule: any, _value: any, callback: any) => {
|
|
|
|
|
- const drillingTime =
|
|
|
|
|
- attrList.value.find((item) => item.description === 'drillingWorkingTime')?.fillContent || 0
|
|
|
|
|
- const otherTime =
|
|
|
|
|
- attrList.value.find((item) => item.description === 'otherProductionTime')?.fillContent || 0
|
|
|
|
|
-
|
|
|
|
|
- const nonProdSum = sumNonProdTimes()
|
|
|
|
|
-
|
|
|
|
|
- let total = 0
|
|
|
|
|
- let msg = ''
|
|
|
|
|
-
|
|
|
|
|
- total = parseFloat((drillingTime + otherTime + nonProdSum).toFixed(2))
|
|
|
|
|
- msg = `进尺(${drillingTime})+其他(${otherTime})+非生产(${nonProdSum})=${total}H,必须为24H`
|
|
|
|
|
-
|
|
|
|
|
- if (Math.abs(total - 24) > 0.01) {
|
|
|
|
|
- if (!isNon) callback(new Error(msg))
|
|
|
|
|
- else callback(new Error())
|
|
|
|
|
- } else {
|
|
|
|
|
- callback()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ return dayjs(value).format('YYYY-MM-DD')
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const getDevicePrimaryText = (device: DeviceFillInfo) => {
|
|
|
|
|
+ return device.deviceCode || device.deviceName || '未命名填报项'
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-const validateOtherReason = (_rule: any, value: any, callback: any) => {
|
|
|
|
|
- const time = attrList.value.find((item) => item.description === 'otherNptTime')?.fillContent || 0
|
|
|
|
|
- if (time > 0 && !value) {
|
|
|
|
|
- callback(new Error('填写了其他时间,必须说明原因'))
|
|
|
|
|
- } else {
|
|
|
|
|
- callback()
|
|
|
|
|
|
|
+const getDeviceSecondaryText = (device: DeviceFillInfo) => {
|
|
|
|
|
+ if (device.deviceCode) {
|
|
|
|
|
+ return device.deviceName || '未命名设备'
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ return device.deviceName === '生产日报' ? '填写当日生产记录' : '独立填报项'
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const rules = reactive<FormRules>({
|
|
|
|
|
- dailyInjectGasTime: [
|
|
|
|
|
- { required: true, message: '请输入当日运转时间' },
|
|
|
|
|
- { validator: rhValidateTotalTime() }
|
|
|
|
|
- ],
|
|
|
|
|
- drillingWorkingTime: [
|
|
|
|
|
- { required: true, message: '请输入进尺工作时间' },
|
|
|
|
|
- { validator: ryValidateTotalTime() }
|
|
|
|
|
- ],
|
|
|
|
|
- nightSupervisors: { required: true, message: '请输入夜班跟班干部' },
|
|
|
|
|
- daySupervisors: { required: true, message: '请输入白班跟班干部' },
|
|
|
|
|
- dailyGasInjection: [
|
|
|
|
|
- {
|
|
|
|
|
- validator: (_rule, value, callback) => {
|
|
|
|
|
- if (companyName.value !== 'rh' || !taskId.value || noGasTasks.value.includes(taskId.value))
|
|
|
|
|
- return callback()
|
|
|
|
|
-
|
|
|
|
|
- const dailyInjectGasTime =
|
|
|
|
|
- attrList.value.find((item) => item.description === 'dailyInjectGasTime')?.fillContent || 0
|
|
|
|
|
-
|
|
|
|
|
- // if (dailyInjectGasTime > 0 && !(value > 0)) {
|
|
|
|
|
- // return callback(new Error('当日运转时间大于0,注气量也需要大于0'))
|
|
|
|
|
- // }
|
|
|
|
|
-
|
|
|
|
|
- const hasDailyGasInjection = value !== undefined && value !== null && value !== ''
|
|
|
|
|
-
|
|
|
|
|
- if (dailyInjectGasTime > 0 && !hasDailyGasInjection) {
|
|
|
|
|
- return callback(new Error('当日运转时间大于0,需要填写当日注气量'))
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- callback()
|
|
|
|
|
- },
|
|
|
|
|
- trigger: ['blur', 'change']
|
|
|
|
|
- }
|
|
|
|
|
- ],
|
|
|
|
|
- otherProductionTime: [{ validator: ryValidateTotalTime() }],
|
|
|
|
|
- // ratedProductionTime: [
|
|
|
|
|
- // { required: true, message: '请输入额定生产时间', trigger: 'blur' },
|
|
|
|
|
- // { validator: ryXjValidateTotalTime(), trigger: 'blur' }
|
|
|
|
|
- // ],
|
|
|
|
|
- // productionTime: [
|
|
|
|
|
- // { required: true, message: '请输入生产时间', trigger: 'blur' },
|
|
|
|
|
- // { validator: ryXjValidateTotalTime(), trigger: 'blur' }
|
|
|
|
|
- // ],
|
|
|
|
|
- otherNptReason: [{ validator: validateOtherReason }]
|
|
|
|
|
-})
|
|
|
|
|
|
|
+const hasValue = (value: string | number | null | undefined) => {
|
|
|
|
|
+ return value !== null && value !== undefined && value !== ''
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-const totalValidatorComputed = computed(() => {
|
|
|
|
|
- if (attrList.value.some((item) => item.description === 'dailyInjectGasTime')) {
|
|
|
|
|
- return rhValidateTotalTime
|
|
|
|
|
- }
|
|
|
|
|
- // else if (attrList.value.some((item) => item.description === 'ratedProductionTime')) {
|
|
|
|
|
- // return ryXjValidateTotalTime
|
|
|
|
|
- // }
|
|
|
|
|
- else if (attrList.value.some((item) => item.description === 'drillingWorkingTime')) {
|
|
|
|
|
- return ryValidateTotalTime
|
|
|
|
|
- }
|
|
|
|
|
-})
|
|
|
|
|
|
|
+const getSumDisplayValue = (attribute: FillAttribute) => {
|
|
|
|
|
+ return hasValue(attribute.totalRunTime) ? attribute.totalRunTime : '暂无数据'
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-nextTick(() => {
|
|
|
|
|
- const validator = totalValidatorComputed.value
|
|
|
|
|
- if (!validator) return
|
|
|
|
|
- NON_KEYS.forEach((field) => {
|
|
|
|
|
- rules[field] = [{ validator: validator(true) }]
|
|
|
|
|
- })
|
|
|
|
|
-})
|
|
|
|
|
|
|
+const normalizeAttributeName = (name: string) => {
|
|
|
|
|
+ return name.replace(/填报/g, '').replace(/\s+/g, '').toUpperCase()
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-let cxStatus = true
|
|
|
|
|
|
|
+const getRdAttributeLimit = (attribute: FillAttribute) => {
|
|
|
|
|
+ if (companyName.value !== 'rd' || attribute.type !== 'double') return undefined
|
|
|
|
|
|
|
|
-// 计算数字输入的最大长度(根据阈值动态计算)
|
|
|
|
|
-const calculateMaxLength = (item: any) => {
|
|
|
|
|
- if (item.type !== 'double' || !item.threshold) return undefined
|
|
|
|
|
|
|
+ const normalizedName = normalizeAttributeName(attribute.name)
|
|
|
|
|
+ const matchedSumAttribute = sumList.value.find(
|
|
|
|
|
+ (sumAttribute) => normalizeAttributeName(sumAttribute.name) === normalizedName
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
- const max = parseFloat(item.threshold)
|
|
|
|
|
- if (isNaN(max)) return undefined
|
|
|
|
|
|
|
+ if (!matchedSumAttribute || !hasValue(matchedSumAttribute.totalRunTime)) return undefined
|
|
|
|
|
|
|
|
- // 整数部分长度 + 可能的小数点 + 两位小数
|
|
|
|
|
- return max.toString().length + (max.toString().includes('.') ? 0 : 3)
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ const totalRunTime = Number(matchedSumAttribute.totalRunTime)
|
|
|
|
|
+ if (!Number.isFinite(totalRunTime)) return undefined
|
|
|
|
|
|
|
|
-// 简单的节流函数,避免提示信息过于频繁
|
|
|
|
|
-const throttle = <T extends (...args: any[]) => any>(fn: T, delay: number) => {
|
|
|
|
|
- let lastTime = 0
|
|
|
|
|
|
|
+ const increment = normalizedName.endsWith('KM')
|
|
|
|
|
+ ? 3000
|
|
|
|
|
+ : normalizedName.endsWith('H')
|
|
|
|
|
+ ? 100
|
|
|
|
|
+ : undefined
|
|
|
|
|
|
|
|
- return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
|
|
|
|
|
- const now = Date.now()
|
|
|
|
|
- if (now - lastTime >= delay) {
|
|
|
|
|
- fn.apply(this, args)
|
|
|
|
|
- lastTime = now
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (increment === undefined) return undefined
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ totalRunTime,
|
|
|
|
|
+ increment,
|
|
|
|
|
+ max: totalRunTime + increment
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const showComponent = () => {
|
|
|
|
|
- if (JSON.parse(fillStatus) === 1 || JSON.parse(fillStatus) === 3) {
|
|
|
|
|
- showStatus = false
|
|
|
|
|
|
|
+const getRdLimitExceeded = (attribute: FillAttribute) => {
|
|
|
|
|
+ const limit = getRdAttributeLimit(attribute)
|
|
|
|
|
+ if (!limit || !hasValue(attribute.fillContent)) return undefined
|
|
|
|
|
+
|
|
|
|
|
+ const value = Number(attribute.fillContent)
|
|
|
|
|
+ if (!Number.isFinite(value) || value <= limit.max) return undefined
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ attribute,
|
|
|
|
|
+ value,
|
|
|
|
|
+ ...limit
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const noGasTasks = ref<number[]>([])
|
|
|
|
|
|
|
+const getRdLimitWarning = (attribute: FillAttribute) => {
|
|
|
|
|
+ const exceeded = getRdLimitExceeded(attribute)
|
|
|
|
|
+ if (!exceeded) return ''
|
|
|
|
|
|
|
|
-/** 查询列表 */
|
|
|
|
|
-const getList = async () => {
|
|
|
|
|
- loading.value = true
|
|
|
|
|
- try {
|
|
|
|
|
- queryParams.deptId = deptId.split(',')[0]
|
|
|
|
|
- queryParams.userId = deptId.split(',')[1]
|
|
|
|
|
- queryParams.createTime = formatTimestamp(JSON.parse(deptId.split(',')[2].substring(0, 10)))
|
|
|
|
|
- queryParams.orderId = deptId.split(',')[3]
|
|
|
|
|
- const data = await IotOpeationFillApi.getIotOpeationFillPage(queryParams)
|
|
|
|
|
- list.value = data
|
|
|
|
|
- if (cxStatus) {
|
|
|
|
|
- queryParams.deviceCategoryId = list.value[0].deviceCategoryId
|
|
|
|
|
- queryParams.deptId = list.value[0].deptId
|
|
|
|
|
- queryParams.deviceCode = list.value[0].deviceCode
|
|
|
|
|
- queryParams.deviceName = list.value[0].deviceName
|
|
|
|
|
- queryParams.deviceId = list.value[0].deviceId
|
|
|
|
|
- }
|
|
|
|
|
- await getAttrList()
|
|
|
|
|
|
|
+ return `超过建议上限:${exceeded.max}(累计值${exceeded.totalRunTime} + ${exceeded.increment})`
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- const daily = list.value.find((item) => item.deviceName === '生产日报') ?? {
|
|
|
|
|
- wellNamePair: {},
|
|
|
|
|
- noGasTasks: []
|
|
|
|
|
- }
|
|
|
|
|
|
|
+const getExceededRdAttributes = () => {
|
|
|
|
|
+ return nonSumList.value
|
|
|
|
|
+ .map((attribute) => getRdLimitExceeded(attribute))
|
|
|
|
|
+ .filter((item): item is NonNullable<typeof item> => Boolean(item))
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- taskOptions.value = Object.keys(daily.wellNamePair ?? {}).map((key) => ({
|
|
|
|
|
- label: daily.wellNamePair[key],
|
|
|
|
|
- value: Number(key)
|
|
|
|
|
- }))
|
|
|
|
|
|
|
+type RdExceededAttribute = ReturnType<typeof getExceededRdAttributes>[number]
|
|
|
|
|
+
|
|
|
|
|
+const rdConfirmVisible = ref(false)
|
|
|
|
|
+const rdExceededAttributes = ref<RdExceededAttribute[]>([])
|
|
|
|
|
|
|
|
- noGasTasks.value = (daily.noGasTasks ?? []).map(Number)
|
|
|
|
|
|
|
+const getAttributeMax = (attribute: FillAttribute) => {
|
|
|
|
|
+ if (getRdAttributeLimit(attribute)) return undefined
|
|
|
|
|
+ if (MAX_24_KEYS.includes(attribute.description)) return 24
|
|
|
|
|
+ if (!hasValue(attribute.threshold)) return undefined
|
|
|
|
|
|
|
|
- taskId.value = taskOptions.value[0]?.value
|
|
|
|
|
|
|
+ const max = Number(attribute.threshold)
|
|
|
|
|
+ return Number.isFinite(max) ? max : undefined
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- IotOpeationFillApi.getReportDetails(deptId.split(',')[3]).then((res) => {
|
|
|
|
|
- reportDetails.value = (res ? (res as any[]) : []).map((item) => ({
|
|
|
|
|
- reportDate: item.reportDate ?? dayjs(createTime).valueOf(),
|
|
|
|
|
- startTime: formatT(item.startTime),
|
|
|
|
|
- endTime: formatT(item.endTime),
|
|
|
|
|
- duration: item.duration,
|
|
|
|
|
- currentDepth: item.currentDepth,
|
|
|
|
|
- currentOperation: item.currentOperation,
|
|
|
|
|
- constructionDetail: item.constructionDetail
|
|
|
|
|
- }))
|
|
|
|
|
|
|
+const getNumericValue = (value: string | number | null) => {
|
|
|
|
|
+ if (!hasValue(value)) return null
|
|
|
|
|
|
|
|
- if (!reportDetails.value.length) {
|
|
|
|
|
- addProductionStatusRow()
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- } finally {
|
|
|
|
|
- loading.value = false
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const numberValue = Number(value)
|
|
|
|
|
+ return Number.isFinite(numberValue) ? numberValue : null
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const getDictOptionValue = (value: unknown) => {
|
|
|
|
|
+ return typeof value === 'number' ? value : String(value)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const getFillNumber = (description: string) => {
|
|
|
|
|
+ const value = nonSumList.value.find(
|
|
|
|
|
+ (attribute) => attribute.description === description
|
|
|
|
|
+ )?.fillContent
|
|
|
|
|
+
|
|
|
|
|
+ if (!hasValue(value)) return 0
|
|
|
|
|
+
|
|
|
|
|
+ const numberValue = Number(value)
|
|
|
|
|
+ return Number.isFinite(numberValue) ? numberValue : 0
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const getNonProductionTotal = () => {
|
|
|
|
|
+ return NON_KEYS.reduce((total, description) => total + getFillNumber(description), 0)
|
|
|
}
|
|
}
|
|
|
-function formatTimestamp(timestamp) {
|
|
|
|
|
- // const date = new Date(timestamp * 1000)
|
|
|
|
|
- return moment.unix(timestamp).format('YYYY-MM-DD')
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-const open = async (_type: string, id?: number) => {
|
|
|
|
|
- alert(id)
|
|
|
|
|
-}
|
|
|
|
|
-defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
|
|
|
|
-let devName = ''
|
|
|
|
|
-const openFill = (
|
|
|
|
|
- deviceCategoryId?: number,
|
|
|
|
|
- deviceId?: number,
|
|
|
|
|
- deptId?: number,
|
|
|
|
|
- deviceName?: string,
|
|
|
|
|
- deviceCode?: string
|
|
|
|
|
-) => {
|
|
|
|
|
- queryParams.deviceCategoryId = deviceCategoryId
|
|
|
|
|
- queryParams.deptId = deptId
|
|
|
|
|
- queryParams.deviceCode = deviceCode
|
|
|
|
|
- queryParams.deviceName = deviceName
|
|
|
|
|
- if (queryParams.deviceName == '生产日报') {
|
|
|
|
|
- devName = '生产日报'
|
|
|
|
|
|
|
+
|
|
|
|
|
+const validateRhTotalTime: FormItemRule['validator'] = (_rule, _value, callback) => {
|
|
|
|
|
+ const dailyInjectGasTime = getFillNumber('dailyInjectGasTime')
|
|
|
|
|
+ const nonProductionTime = getNonProductionTotal()
|
|
|
|
|
+ const total = Number((dailyInjectGasTime + nonProductionTime).toFixed(2))
|
|
|
|
|
+
|
|
|
|
|
+ if (Math.abs(total - 24) > 0.01) {
|
|
|
|
|
+ callback(
|
|
|
|
|
+ new Error(`运转(${dailyInjectGasTime}) + 非生产(${nonProductionTime}) = ${total}H,必须为24H`)
|
|
|
|
|
+ )
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
- queryParams.deviceId = deviceId
|
|
|
|
|
- getAttrList()
|
|
|
|
|
-}
|
|
|
|
|
-// 处理输入事件,实时限制输入格式和最大值
|
|
|
|
|
-const handleInput = (item: any) => {
|
|
|
|
|
- if (item.type === 'double') {
|
|
|
|
|
- // 保存原始值用于后续比较
|
|
|
|
|
- const originalValue = item.fillContent
|
|
|
|
|
-
|
|
|
|
|
- // 1. 格式验证:只允许数字和小数点
|
|
|
|
|
- item.fillContent = item.fillContent.replace(/[^\d.]/g, '')
|
|
|
|
|
- // 确保只有一个小数点
|
|
|
|
|
- item.fillContent = item.fillContent.replace(/\.{2,}/g, '.')
|
|
|
|
|
- // 确保小数点不在开头
|
|
|
|
|
- item.fillContent = item.fillContent.replace(/^\./g, '')
|
|
|
|
|
- // 限制小数位数为两位
|
|
|
|
|
- item.fillContent = item.fillContent.replace(/(\d+)\.(\d{2}).*/, '$1.$2')
|
|
|
|
|
-
|
|
|
|
|
- // 2. 最大值验证
|
|
|
|
|
- if (item.threshold) {
|
|
|
|
|
- const value = parseFloat(item.fillContent)
|
|
|
|
|
- const max = parseFloat(item.threshold)
|
|
|
|
|
-
|
|
|
|
|
- if (!isNaN(value) && !isNaN(max) && value > max) {
|
|
|
|
|
- // 输入值超过阈值时,恢复到修改前的值
|
|
|
|
|
- item.fillContent = originalValue
|
|
|
|
|
- .replace(/[^\d.]/g, '')
|
|
|
|
|
- .replace(/\.{2,}/g, '.')
|
|
|
|
|
- .replace(/^\./g, '')
|
|
|
|
|
- .replace(/(\d+)\.(\d{2}).*/, '$1.$2')
|
|
|
|
|
-
|
|
|
|
|
- if (parseFloat(item.fillContent) > max) {
|
|
|
|
|
- item.fillContent = max.toString()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- throttle(() => {
|
|
|
|
|
- ElMessage.warning(t('operationFillForm.exceedMax', { max }))
|
|
|
|
|
- }, 1000)()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- if (companyName.value === 'rd') {
|
|
|
|
|
- // 3. 累计值限制验证(改为弹窗提示但允许继续)
|
|
|
|
|
- if (item.maxAllowedValue !== undefined) {
|
|
|
|
|
- const value = parseFloat(item.fillContent)
|
|
|
|
|
- if (!isNaN(value) && value > item.maxAllowedValue) {
|
|
|
|
|
- // 不自动修改值,而是显示警告弹窗
|
|
|
|
|
- let limitDescription = ''
|
|
|
|
|
- if (item.limitType === 'km') {
|
|
|
|
|
- limitDescription = `当前累计值${item.currentSumValue} + 3000`
|
|
|
|
|
- } else if (item.limitType === 'time') {
|
|
|
|
|
- limitDescription = `当前累计值${item.currentSumValue} + 100`
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- ElMessage.warning(
|
|
|
|
|
- `填报值 ${value} 超过限制 ${item.maxAllowedValue} (${limitDescription}),请确认是否正确!`
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ callback()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const validateRyTotalTime: FormItemRule['validator'] = (_rule, _value, callback) => {
|
|
|
|
|
+ const drillingWorkingTime = getFillNumber('drillingWorkingTime')
|
|
|
|
|
+ const otherProductionTime = getFillNumber('otherProductionTime')
|
|
|
|
|
+ const nonProductionTime = getNonProductionTotal()
|
|
|
|
|
+ const total = Number((drillingWorkingTime + otherProductionTime + nonProductionTime).toFixed(2))
|
|
|
|
|
+
|
|
|
|
|
+ if (Math.abs(total - 24) > 0.01) {
|
|
|
|
|
+ callback(
|
|
|
|
|
+ new Error(
|
|
|
|
|
+ `进尺(${drillingWorkingTime}) + 其他(${otherProductionTime}) + 非生产(${nonProductionTime}) = ${total}H,必须为24H`
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ callback()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const getAttrList = async () => {
|
|
|
|
|
- loading.value = true
|
|
|
|
|
- try {
|
|
|
|
|
- queryParams.createTime = formatTimestamp(JSON.parse(deptId.split(',')[2].substring(0, 10)))
|
|
|
|
|
- const data = await IotOpeationFillApi.getAttrs(queryParams)
|
|
|
|
|
|
|
+const validateOtherNptReason: FormItemRule['validator'] = (_rule, value, callback) => {
|
|
|
|
|
+ if (getFillNumber('otherNptTime') > 0 && !hasValue(value)) {
|
|
|
|
|
+ callback(new Error('填写了其他非生产时间,必须填写其他非生产原因'))
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- const timeKeys = keys.filter((k) => k !== 'otherNptReason')
|
|
|
|
|
|
|
+ callback()
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- attrList.value = data[0].nonSumList.map((item) => {
|
|
|
|
|
- if (timeKeys.includes(item.description)) {
|
|
|
|
|
- item.fillContent = item.fillContent ?? 0
|
|
|
|
|
- }
|
|
|
|
|
- return item
|
|
|
|
|
- })
|
|
|
|
|
- attrList1.value = data[0].sumList
|
|
|
|
|
-
|
|
|
|
|
- // 建立累计数据映射,用于后续验证
|
|
|
|
|
- const sumMap = new Map()
|
|
|
|
|
- attrList1.value.forEach((item) => {
|
|
|
|
|
- // 创建匹配规则:移除"填报"字样的差异,保留核心名称
|
|
|
|
|
- const coreName = item.name.replace(/填报/g, '')
|
|
|
|
|
- sumMap.set(coreName, item)
|
|
|
|
|
- })
|
|
|
|
|
|
|
+const isDailyGasInjectionRequired = () => {
|
|
|
|
|
+ const currentTaskId = taskId.value
|
|
|
|
|
|
|
|
- // 为非累计数据添加最大值限制
|
|
|
|
|
- attrList.value.forEach(function (item) {
|
|
|
|
|
- let strVal = String(item.fillContent || '').trim()
|
|
|
|
|
-
|
|
|
|
|
- if (strVal !== '') {
|
|
|
|
|
- const num = Number(strVal)
|
|
|
|
|
-
|
|
|
|
|
- if (!isNaN(num)) {
|
|
|
|
|
- if (strVal.includes('.')) {
|
|
|
|
|
- item.fillContent = Number(num.toFixed(2))
|
|
|
|
|
- } else {
|
|
|
|
|
- item.fillContent = num
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // if (item.fillContent !== '' && item.fillContent !== null) {
|
|
|
|
|
- // const num = Number(item.fillContent)
|
|
|
|
|
- // if (!isNaN(num)) {
|
|
|
|
|
- // if (item.fillContent.includes('.')) {
|
|
|
|
|
- // item.fillContent = Number(num.toFixed(2))
|
|
|
|
|
- // } else {
|
|
|
|
|
- // item.fillContent = Math.floor(num)
|
|
|
|
|
- // }
|
|
|
|
|
- // }
|
|
|
|
|
- // }
|
|
|
|
|
-
|
|
|
|
|
- if (companyName.value === 'rd') {
|
|
|
|
|
- // 添加最大值限制逻辑
|
|
|
|
|
- const coreName = item.name.replace(/填报/g, '')
|
|
|
|
|
- const sumItem = sumMap.get(coreName)
|
|
|
|
|
- if (sumItem) {
|
|
|
|
|
- // 根据字段名称判断使用哪种限制规则
|
|
|
|
|
- if (item.name.includes('公里数填报')) {
|
|
|
|
|
- // 公里数限制:当前累计值 + 3000
|
|
|
|
|
- item.maxAllowedValue = sumItem.totalRunTime + 3000
|
|
|
|
|
- item.currentSumValue = sumItem.totalRunTime // 保存当前累计值用于提示
|
|
|
|
|
- item.limitType = 'km' // 标记为公里数限制
|
|
|
|
|
- } else if (item.name.includes('运转时长填报')) {
|
|
|
|
|
- // 运转时长限制:当前累计值 + 100
|
|
|
|
|
- item.maxAllowedValue = sumItem.totalRunTime + 100
|
|
|
|
|
- item.currentSumValue = sumItem.totalRunTime // 保存当前累计值用于提示
|
|
|
|
|
- item.limitType = 'time' // 标记为时长限制
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- item.deviceCode = queryParams.deviceCode
|
|
|
|
|
- item.deptId = queryParams.deptId
|
|
|
|
|
- item.deviceId = queryParams.deviceId
|
|
|
|
|
- item.deviceCategoryId = queryParams.deviceCategoryId
|
|
|
|
|
- item.modelId = item.id
|
|
|
|
|
- console.log(item.fillContent)
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ return Boolean(
|
|
|
|
|
+ companyName.value === 'rh' &&
|
|
|
|
|
+ hasValue(currentTaskId) &&
|
|
|
|
|
+ !selectedDevice.value?.noGasTasks.includes(Number(currentTaskId)) &&
|
|
|
|
|
+ getFillNumber('dailyInjectGasTime') > 0
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- attrList1.value.forEach(function (item) {
|
|
|
|
|
- item.deviceCode = queryParams.deviceCode
|
|
|
|
|
- item.deptId = queryParams.deptId
|
|
|
|
|
- item.deviceId = queryParams.deviceId
|
|
|
|
|
- item.deviceCategoryId = queryParams.deviceCategoryId
|
|
|
|
|
- item.modelId = item.id
|
|
|
|
|
- })
|
|
|
|
|
- } finally {
|
|
|
|
|
- loading.value = false
|
|
|
|
|
|
|
+const validateDailyGasInjection: FormItemRule['validator'] = (_rule, value, callback) => {
|
|
|
|
|
+ if (isDailyGasInjectionRequired() && !hasValue(value)) {
|
|
|
|
|
+ callback(new Error('当日运转时间大于0,需要填写当日注气量'))
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ callback()
|
|
|
}
|
|
}
|
|
|
-const formRef = ref<FormInstance[] | null>(null)
|
|
|
|
|
|
|
|
|
|
-const submitLoading = ref(false)
|
|
|
|
|
|
|
+const isRhTotalTimeField = (description: string) => {
|
|
|
|
|
+ return description === 'dailyInjectGasTime' || NON_KEYS.includes(description)
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-/** 获取填写信息保存到后台*/
|
|
|
|
|
-const getFillInfo = async () => {
|
|
|
|
|
- if (!formRef.value) return
|
|
|
|
|
|
|
+const isRyTotalTimeField = (description: string) => {
|
|
|
|
|
+ return (
|
|
|
|
|
+ ['drillingWorkingTime', 'otherProductionTime'].includes(description) ||
|
|
|
|
|
+ NON_KEYS.includes(description)
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- submitLoading.value = true
|
|
|
|
|
|
|
+const isAttributeRequired = (attribute: FillAttribute) => {
|
|
|
|
|
+ return (
|
|
|
|
|
+ REQUIRED_KEYS.includes(attribute.description) ||
|
|
|
|
|
+ (!isDailyReport.value && Number(attribute.isCollection) !== 1)
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- try {
|
|
|
|
|
- const validations = formRef.value.map((form) => form.validate())
|
|
|
|
|
- await Promise.all(validations)
|
|
|
|
|
-
|
|
|
|
|
- const company = await IotOpeationFillApi.getOrgName(route.params.id.toString().split(',')[0])
|
|
|
|
|
-
|
|
|
|
|
- if (devName != '生产日报') {
|
|
|
|
|
- // 检查必填字段
|
|
|
|
|
- const emptyFields = attrList.value.filter((item) => {
|
|
|
|
|
- // 只检查非disabled的字段
|
|
|
|
|
- return (
|
|
|
|
|
- !(item.isCollection === 1 || fillStatus === '1') &&
|
|
|
|
|
- (item.fillContent === undefined || item.fillContent === '')
|
|
|
|
|
- )
|
|
|
|
|
- })
|
|
|
|
|
|
|
+const getAttributeRules = (attribute: FillAttribute): FormItemRule[] => {
|
|
|
|
|
+ const rules: FormItemRule[] = []
|
|
|
|
|
+ const isSelect = attribute.type === 'enum'
|
|
|
|
|
|
|
|
- if (emptyFields.length > 0) {
|
|
|
|
|
- ElMessage.error(t('operationFillForm.fill'))
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (isAttributeRequired(attribute)) {
|
|
|
|
|
+ rules.push({
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ message: `${isSelect ? '请选择' : '请输入'}${attribute.name}`,
|
|
|
|
|
+ trigger: isSelect ? 'change' : ['blur', 'change'],
|
|
|
|
|
+ type: attribute.type === 'double' ? 'number' : undefined
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (company === 'rd') {
|
|
|
|
|
- // 检查是否有超出累计值限制的字段
|
|
|
|
|
- const exceededFields = attrList.value.filter((item) => {
|
|
|
|
|
- if (
|
|
|
|
|
- item.type === 'double' &&
|
|
|
|
|
- item.maxAllowedValue !== undefined &&
|
|
|
|
|
- item.fillContent !== '' &&
|
|
|
|
|
- item.fillContent !== null
|
|
|
|
|
- ) {
|
|
|
|
|
- const value = parseFloat(item.fillContent)
|
|
|
|
|
- return !isNaN(value) && value > item.maxAllowedValue
|
|
|
|
|
- }
|
|
|
|
|
- return false
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ if (attribute.description === 'dailyGasInjection') {
|
|
|
|
|
+ rules.push({
|
|
|
|
|
+ required: isDailyGasInjectionRequired(),
|
|
|
|
|
+ validator: validateDailyGasInjection,
|
|
|
|
|
+ trigger: ['blur', 'change']
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 如果有超出限制的字段,提示用户确认
|
|
|
|
|
- if (exceededFields.length > 0) {
|
|
|
|
|
- let exceededMessage = ''
|
|
|
|
|
- exceededFields.forEach((field) => {
|
|
|
|
|
- let limitDescription = ''
|
|
|
|
|
- if (field.limitType === 'km') {
|
|
|
|
|
- limitDescription = `(${field.currentSumValue} + 3000)`
|
|
|
|
|
- } else if (field.limitType === 'time') {
|
|
|
|
|
- limitDescription = `(${field.currentSumValue} + 100)`
|
|
|
|
|
- }
|
|
|
|
|
- exceededMessage += `${field.name};\n`
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
- // exceededMessage += '\n是否继续保存?'
|
|
|
|
|
-
|
|
|
|
|
- const confirmResult = await message.confirm(
|
|
|
|
|
- exceededMessage,
|
|
|
|
|
- '以下填报项超出限制,是否继续保存?',
|
|
|
|
|
- '继续保存',
|
|
|
|
|
- '取消'
|
|
|
|
|
- )
|
|
|
|
|
- if (!confirmResult) {
|
|
|
|
|
- return // 用户取消保存
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (keys.includes(attribute.description)) {
|
|
|
|
|
+ if (companyName.value === 'rh' && isRhTotalTimeField(attribute.description)) {
|
|
|
|
|
+ rules.push({
|
|
|
|
|
+ validator: validateRhTotalTime,
|
|
|
|
|
+ trigger: ['blur', 'change']
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- attrList2.value = attrList.value.concat(attrList1.value)
|
|
|
|
|
-
|
|
|
|
|
- attrList2.value.forEach(function (item) {
|
|
|
|
|
- item.pointName = item.name
|
|
|
|
|
- item.createTime = formatTimestamp(JSON.parse(deptId.split(',')[2].substring(0, 10)))
|
|
|
|
|
- item.userId = deptId.split(',')[1]
|
|
|
|
|
- item.id = deptId.split(',')[3]
|
|
|
|
|
- })
|
|
|
|
|
- const data = attrList2.value as unknown as IotOpeationFillVO
|
|
|
|
|
-
|
|
|
|
|
- const reqData = {
|
|
|
|
|
- createReqVO: data,
|
|
|
|
|
- reportDetails: reportDetails.value.map((item) => ({
|
|
|
|
|
- ...item,
|
|
|
|
|
- taskId: taskId.value
|
|
|
|
|
- }))
|
|
|
|
|
|
|
+ if (companyName.value === 'ry' && isRyTotalTimeField(attribute.description)) {
|
|
|
|
|
+ rules.push({
|
|
|
|
|
+ validator: validateRyTotalTime,
|
|
|
|
|
+ trigger: ['blur', 'change']
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- await IotOpeationFillApi.insertLog(reqData)
|
|
|
|
|
- message.success(t('common.createSuccess'))
|
|
|
|
|
- // 发送操作成功的事件
|
|
|
|
|
- emit('success')
|
|
|
|
|
- cxStatus = false
|
|
|
|
|
- getList()
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error('保存失败:', error)
|
|
|
|
|
- } finally {
|
|
|
|
|
- submitLoading.value = false
|
|
|
|
|
|
|
+ if (attribute.description === 'otherNptReason') {
|
|
|
|
|
+ rules.push({
|
|
|
|
|
+ validator: validateOtherNptReason,
|
|
|
|
|
+ trigger: ['blur', 'change']
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-/**清空填写信息*/
|
|
|
|
|
-const deleteFillInfo = () => {
|
|
|
|
|
- attrList.value.forEach(function (item) {
|
|
|
|
|
- item.fillContent = ''
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ return rules
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/** 初始化 **/
|
|
|
|
|
-onMounted(async () => {
|
|
|
|
|
- const company = await IotOpeationFillApi.getOrgName(route.params.id.toString().split(',')[0])
|
|
|
|
|
- companyName.value = company
|
|
|
|
|
-
|
|
|
|
|
- getList()
|
|
|
|
|
- showComponent()
|
|
|
|
|
-})
|
|
|
|
|
-</script>
|
|
|
|
|
-<style scoped>
|
|
|
|
|
-.scrollable-form {
|
|
|
|
|
- /* 设置最大高度,超过这个高度会出现滚动条 */
|
|
|
|
|
- max-height: 100%; /* 根据你的需求调整 */
|
|
|
|
|
|
|
+const validateAttributeField = async (description: string) => {
|
|
|
|
|
+ const attributeIndex = nonSumList.value.findIndex(
|
|
|
|
|
+ (attribute) => attribute.description === description
|
|
|
|
|
+ )
|
|
|
|
|
|
|
|
- /* 可选:添加内边距和边框美化 */
|
|
|
|
|
- padding: 16px;
|
|
|
|
|
|
|
+ if (attributeIndex < 0) return
|
|
|
|
|
|
|
|
- /* 超出部分显示垂直滚动条 */
|
|
|
|
|
- overflow-y: auto;
|
|
|
|
|
- border: 1px solid #e5e7eb;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
|
|
+ await nextTick()
|
|
|
|
|
+ void fillFormRef.value
|
|
|
|
|
+ ?.validateField(`nonSumList.${attributeIndex}.fillContent`)
|
|
|
|
|
+ .catch(() => undefined)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* 可选:美化滚动条 */
|
|
|
|
|
-.scrollable-form::-webkit-scrollbar {
|
|
|
|
|
- width: 6px;
|
|
|
|
|
|
|
+const handleTaskIdChange = () => {
|
|
|
|
|
+ void validateAttributeField('dailyGasInjection')
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.scrollable-form::-webkit-scrollbar-track {
|
|
|
|
|
- background: #f1f1f1;
|
|
|
|
|
- border-radius: 3px;
|
|
|
|
|
|
|
+const handleDoubleChange = (attribute: FillAttribute) => {
|
|
|
|
|
+ if (attribute.description === 'dailyInjectGasTime') {
|
|
|
|
|
+ void validateAttributeField('dailyGasInjection')
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.scrollable-form::-webkit-scrollbar-thumb {
|
|
|
|
|
- background: #c1c1c1;
|
|
|
|
|
- border-radius: 3px;
|
|
|
|
|
-}
|
|
|
|
|
|
|
+const syncTaskId = (device?: DeviceFillInfo) => {
|
|
|
|
|
+ const firstWell = device?.wellNamePair?.[0]
|
|
|
|
|
+ const canSelectWell =
|
|
|
|
|
+ device?.deviceName === '生产日报' &&
|
|
|
|
|
+ ['rh', 'ry'].includes(companyName.value) &&
|
|
|
|
|
+ Boolean(firstWell)
|
|
|
|
|
|
|
|
-.scrollable-form::-webkit-scrollbar-thumb:hover {
|
|
|
|
|
- background: #a8a8a8;
|
|
|
|
|
|
|
+ taskId.value = canSelectWell ? firstWell?.value : undefined
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.back-red {
|
|
|
|
|
- /* 红色背景 */
|
|
|
|
|
- background-color: red;
|
|
|
|
|
|
|
+const getDefaultReportDate = (device?: DeviceFillInfo) => {
|
|
|
|
|
+ return dayjs(formatCreateTime(device?.createTime ?? routeInfo.createTime)).valueOf()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.back-blue {
|
|
|
|
|
- background-color: grey;
|
|
|
|
|
-}
|
|
|
|
|
|
|
+const formatReportTime = (value: unknown) => {
|
|
|
|
|
+ if (Array.isArray(value) && value.length >= 2) {
|
|
|
|
|
+ return formatT([Number(value[0]), Number(value[1])])
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
-.step-container {
|
|
|
|
|
- display: grid;
|
|
|
|
|
- grid-template-columns: 220px 1fr;
|
|
|
|
|
- gap: 10px;
|
|
|
|
|
- height: 100%;
|
|
|
|
|
- min-height: 600px;
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ if (typeof value === 'string') {
|
|
|
|
|
+ const matchedTime = value.match(/^(\d{1,2}):(\d{2})/)
|
|
|
|
|
+ return matchedTime ? `${matchedTime[1].padStart(2, '0')}:${matchedTime[2]}` : ''
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
-.steps-nav {
|
|
|
|
|
- padding-right: 15px;
|
|
|
|
|
- overflow-y: auto;
|
|
|
|
|
|
|
+ return ''
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.form-wrapper {
|
|
|
|
|
- padding: 30px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- box-shadow: 0 2px 12px rgb(0 0 0 / 10%);
|
|
|
|
|
-}
|
|
|
|
|
|
|
+const createReportDetail = (device?: DeviceFillInfo): ReportDetail => ({
|
|
|
|
|
+ reportDate: getDefaultReportDate(device),
|
|
|
|
|
+ startTime: '',
|
|
|
|
|
+ endTime: '',
|
|
|
|
|
+ duration: 0,
|
|
|
|
|
+ currentDepth: 0,
|
|
|
|
|
+ currentOperation: '',
|
|
|
|
|
+ constructionDetail: ''
|
|
|
|
|
+})
|
|
|
|
|
|
|
|
-.navigation-controls {
|
|
|
|
|
- margin-top: 40px;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
|
|
+const addProductionStatusRow = () => {
|
|
|
|
|
+ reportDetails.value.push(createReportDetail(selectedDevice.value))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.custom-label {
|
|
|
|
|
- padding: 0 10px;
|
|
|
|
|
- font-size: 17px;
|
|
|
|
|
- font-weight: 1000;
|
|
|
|
|
- color: forestgreen;
|
|
|
|
|
-}
|
|
|
|
|
|
|
+const removeProductionStatusRow = (index: number) => {
|
|
|
|
|
+ if (reportDetails.value.length <= 1) {
|
|
|
|
|
+ ElMessage.warning('至少填写一条生产动态')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
-.custom-label1 {
|
|
|
|
|
- padding: 0 10px;
|
|
|
|
|
- font-size: 17px;
|
|
|
|
|
- font-weight: 1000;
|
|
|
|
|
|
|
+ reportDetails.value.splice(index, 1)
|
|
|
|
|
+ syncCurrentDepth()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.has-border {
|
|
|
|
|
- padding: 3px 8px; /* 适当增加内边距,避免文字太贴近边框 */
|
|
|
|
|
- font-size: 20px;
|
|
|
|
|
- border: 2px solid #333; /* 加粗到2px,使用深灰色#333让边框更清晰 */
|
|
|
|
|
- border-radius: 2px; /* 轻微圆角,可选 */
|
|
|
|
|
|
|
+const getReportDetailTimestamp = (detail: ReportDetail) => {
|
|
|
|
|
+ if (!hasValue(detail.reportDate) || !detail.endTime) return Number.NEGATIVE_INFINITY
|
|
|
|
|
+
|
|
|
|
|
+ const dateValue = /^\d+$/.test(String(detail.reportDate))
|
|
|
|
|
+ ? Number(detail.reportDate)
|
|
|
|
|
+ : detail.reportDate
|
|
|
|
|
+ const [hour, minute] = detail.endTime.split(':').map(Number)
|
|
|
|
|
+
|
|
|
|
|
+ if (!Number.isFinite(hour) || !Number.isFinite(minute)) return Number.NEGATIVE_INFINITY
|
|
|
|
|
+
|
|
|
|
|
+ return dayjs(dateValue).hour(hour).minute(minute).second(0).millisecond(0).valueOf()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-::v-deep .el-step__icon {
|
|
|
|
|
- color: #fff;
|
|
|
|
|
- background-color: #409eff;
|
|
|
|
|
- border: 0;
|
|
|
|
|
|
|
+const syncCurrentDepth = () => {
|
|
|
|
|
+ const currentDepthAttribute = nonSumList.value.find(
|
|
|
|
|
+ (attribute) => attribute.description === 'currentDepth'
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ if (!currentDepthAttribute || !reportDetails.value.length) return
|
|
|
|
|
+
|
|
|
|
|
+ const latestDetail = reportDetails.value.reduce((latest, current) => {
|
|
|
|
|
+ return getReportDetailTimestamp(current) >= getReportDetailTimestamp(latest) ? current : latest
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ currentDepthAttribute.fillContent = latestDetail.currentDepth
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.step-title-wrapper {
|
|
|
|
|
- position: relative;
|
|
|
|
|
- display: inline-flex;
|
|
|
|
|
- padding-right: 25px;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
|
|
+const handleReportTimeChange = (detail: ReportDetail) => {
|
|
|
|
|
+ calculateDuration(detail)
|
|
|
|
|
+ syncCurrentDepth()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* 覆盖步骤条默认样式 */
|
|
|
|
|
-:deep(.custom-steps) {
|
|
|
|
|
- /* 调整头部位置 */
|
|
|
|
|
- .el-step__head {
|
|
|
|
|
- top: 3px;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+const loadReportDetails = async (device: DeviceFillInfo) => {
|
|
|
|
|
+ reportDetails.value = []
|
|
|
|
|
|
|
|
- /* 标题容器定位 */
|
|
|
|
|
- .el-step__title {
|
|
|
|
|
- display: inline-block;
|
|
|
|
|
- padding-right: 0;
|
|
|
|
|
- margin-left: 10px;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (companyName.value !== 'ry' || device.deviceName !== '生产日报') return
|
|
|
|
|
+
|
|
|
|
|
+ reportDetailsLoading.value = true
|
|
|
|
|
|
|
|
- /* 步骤连接线 */
|
|
|
|
|
- .el-step__line {
|
|
|
|
|
- left: 11px;
|
|
|
|
|
- background-color: #ebeef5;
|
|
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await IotOpeationFillApi.getReportDetails(device.orderId)
|
|
|
|
|
+ const details = Array.isArray(response) ? response : []
|
|
|
|
|
+
|
|
|
|
|
+ reportDetails.value = details.map((item) => ({
|
|
|
|
|
+ reportDate: item.reportDate ?? getDefaultReportDate(device),
|
|
|
|
|
+ startTime: formatReportTime(item.startTime),
|
|
|
|
|
+ endTime: formatReportTime(item.endTime),
|
|
|
|
|
+ duration: Number(item.duration) || 0,
|
|
|
|
|
+ currentDepth: getNumericValue(item.currentDepth),
|
|
|
|
|
+ currentOperation: String(item.currentOperation ?? ''),
|
|
|
|
|
+ constructionDetail: String(item.constructionDetail ?? '')
|
|
|
|
|
+ }))
|
|
|
|
|
+
|
|
|
|
|
+ if (!reportDetails.value.length) {
|
|
|
|
|
+ reportDetails.value.push(createReportDetail(device))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ syncCurrentDepth()
|
|
|
|
|
+ console.log('生产动态 reportDetails:', reportDetails.value)
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ reportDetails.value.push(createReportDetail(device))
|
|
|
|
|
+ console.error('获取生产动态失败:', error)
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ reportDetailsLoading.value = false
|
|
|
}
|
|
}
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
- /* 当前步骤样式 */
|
|
|
|
|
- .is-process .title-text {
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- color: #409eff;
|
|
|
|
|
|
|
+const handleConfirm = async () => {
|
|
|
|
|
+ if (!fillFormRef.value) return
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ await fillFormRef.value.validate()
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ ElMessage.warning('请检查并完善表单内容')
|
|
|
|
|
+ console.warn('表单校验未通过:', error)
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /* 完成状态图标 */
|
|
|
|
|
- .is-finish .tip-icon {
|
|
|
|
|
- color: #67c23a;
|
|
|
|
|
|
|
+ const exceededAttributes = getExceededRdAttributes()
|
|
|
|
|
+
|
|
|
|
|
+ if (exceededAttributes.length) {
|
|
|
|
|
+ rdExceededAttributes.value = exceededAttributes
|
|
|
|
|
+ rdConfirmVisible.value = true
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ previewSubmit()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.horizontal-container {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-wrap: wrap; /* 允许换行 */
|
|
|
|
|
- gap: 20px; /* 项目间距 */
|
|
|
|
|
|
|
+const previewSubmit = () => {
|
|
|
|
|
+ console.log('表单校验及超限确认通过,预览数据:', {
|
|
|
|
|
+ nonSumList: nonSumList.value,
|
|
|
|
|
+ reportDetails: reportDetails.value,
|
|
|
|
|
+ taskId: taskId.value
|
|
|
|
|
+ })
|
|
|
|
|
+ ElMessage.success('校验已通过,当前为预览效果,未调用提交接口')
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.form-item-container {
|
|
|
|
|
- flex: 1; /* 等宽分布 */
|
|
|
|
|
- min-width: 200px; /* 最小宽度防止挤压 */
|
|
|
|
|
|
|
+const confirmRdExceededSubmit = () => {
|
|
|
|
|
+ rdConfirmVisible.value = false
|
|
|
|
|
+ previewSubmit()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* 新增日报填报项的边框样式 */
|
|
|
|
|
-.report-border {
|
|
|
|
|
- padding: 2px 4px;
|
|
|
|
|
- border: 2px solid #42b983; /* 使用Vue标志性的绿色边框 */
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
|
|
+const clearFillForm = async () => {
|
|
|
|
|
+ nonSumList.value.forEach((attribute) => {
|
|
|
|
|
+ attribute.fillContent = null
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ reportDetails.value = showProductionDetails.value
|
|
|
|
|
+ ? [createReportDetail(selectedDevice.value)]
|
|
|
|
|
+ : []
|
|
|
|
|
+
|
|
|
|
|
+ await nextTick()
|
|
|
|
|
+ fillFormRef.value?.clearValidate()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-:deep(.table-form-item) {
|
|
|
|
|
- .el-form-item__content {
|
|
|
|
|
- margin-left: 0 !important;
|
|
|
|
|
|
|
+const getDeviceAttrs = async (device: DeviceFillInfo) => {
|
|
|
|
|
+ const deviceText = getDevicePrimaryText(device)
|
|
|
|
|
+
|
|
|
|
|
+ deviceAttrsLoading.value = true
|
|
|
|
|
+ nonSumList.value = []
|
|
|
|
|
+ sumList.value = []
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const attrsParams = {
|
|
|
|
|
+ deptId: device.deptId,
|
|
|
|
|
+ userId: device.userId,
|
|
|
|
|
+ createTime: formatCreateTime(device.createTime),
|
|
|
|
|
+ orderId: device.orderId,
|
|
|
|
|
+ deviceCategoryId: device.deviceCategoryId,
|
|
|
|
|
+ deviceId: device.deviceId,
|
|
|
|
|
+ deviceCode: device.deviceCode,
|
|
|
|
|
+ deviceName: device.deviceName
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.log(`${deviceText} 的属性请求参数:`, attrsParams)
|
|
|
|
|
+ const attrs = await IotOpeationFillApi.getAttrs(attrsParams)
|
|
|
|
|
+ const attrsData = (Array.isArray(attrs) ? attrs[0] : attrs) as DeviceAttrsResponse | undefined
|
|
|
|
|
+
|
|
|
|
|
+ nonSumList.value = (attrsData?.nonSumList ?? []).map((attribute) => ({
|
|
|
|
|
+ ...attribute,
|
|
|
|
|
+ fillContent:
|
|
|
|
|
+ attribute.type === 'double'
|
|
|
|
|
+ ? getNumericValue(attribute.fillContent)
|
|
|
|
|
+ : (attribute.fillContent ?? null)
|
|
|
|
|
+ }))
|
|
|
|
|
+ sumList.value = attrsData?.sumList ?? []
|
|
|
|
|
+ await loadReportDetails(device)
|
|
|
|
|
+
|
|
|
|
|
+ console.log(`${deviceText} 的属性和已有填报值:`, attrs)
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error(`获取 ${deviceText} 的属性和已有填报值失败:`, error)
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ deviceAttrsLoading.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const getInitialData = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const company = await IotOpeationFillApi.getOrgName(routeInfo.deptId)
|
|
|
|
|
+ companyName.value = String(company ?? '')
|
|
|
|
|
+ console.log('公司标识:', company)
|
|
|
|
|
+
|
|
|
|
|
+ const fillList = await IotOpeationFillApi.getIotOpeationFillPage({
|
|
|
|
|
+ pageNo: 1,
|
|
|
|
|
+ pageSize: 10,
|
|
|
|
|
+ deviceCategoryId: 1,
|
|
|
|
|
+ deptId: routeInfo.deptId,
|
|
|
|
|
+ userId: routeInfo.userId,
|
|
|
|
|
+ createTime: formatCreateTime(routeInfo.createTime),
|
|
|
|
|
+ orderId: routeInfo.orderId
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ console.log('object :>> ', fillList)
|
|
|
|
|
+
|
|
|
|
|
+ deviceFillList.value = (fillList ?? []).map((item) => ({
|
|
|
|
|
+ deptId: item.deptId,
|
|
|
|
|
+ userId: routeInfo.userId,
|
|
|
|
|
+ createTime: item.createTime,
|
|
|
|
|
+ orderId: item.orderId,
|
|
|
|
|
+ deviceCategoryId: item.deviceCategoryId,
|
|
|
|
|
+ deviceId: item.deviceId,
|
|
|
|
|
+ deviceCode: item.deviceCode,
|
|
|
|
|
+ deviceName: item.deviceName,
|
|
|
|
|
+ isFill: String(item.isFill ?? ''),
|
|
|
|
|
+ orgName: item.orgName ?? '',
|
|
|
|
|
+ wellName: item.wellName ?? '',
|
|
|
|
|
+ wellNamePair: item.wellNamePair
|
|
|
|
|
+ ? Object.entries(item.wellNamePair).map(([key, value]) => ({
|
|
|
|
|
+ label: String(value),
|
|
|
|
|
+ value: Number(key)
|
|
|
|
|
+ }))
|
|
|
|
|
+ : null,
|
|
|
|
|
+ noGasTasks: Array.isArray(item.noGasTasks) ? item.noGasTasks.map(Number) : []
|
|
|
|
|
+ }))
|
|
|
|
|
+
|
|
|
|
|
+ selectedDeviceIndex.value = deviceFillList.value.length > 0 ? 0 : -1
|
|
|
|
|
+
|
|
|
|
|
+ console.log('整理后的设备和填报列表:', deviceFillList.value)
|
|
|
|
|
+
|
|
|
|
|
+ const firstDevice = deviceFillList.value[0]
|
|
|
|
|
+ if (firstDevice) {
|
|
|
|
|
+ syncTaskId(firstDevice)
|
|
|
|
|
+ await getDeviceAttrs(firstDevice)
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('获取初始化数据失败:', error)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const selectDevice = async (index: number) => {
|
|
|
|
|
+ selectedDeviceIndex.value = index
|
|
|
|
|
+
|
|
|
|
|
+ const device = deviceFillList.value[index]
|
|
|
|
|
+ if (device) {
|
|
|
|
|
+ syncTaskId(device)
|
|
|
|
|
+ await getDeviceAttrs(device)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const storeRouteInfo = (id: string | string[] | undefined) => {
|
|
|
|
|
+ const rawId = Array.isArray(id) ? (id[0] ?? '') : (id ?? '')
|
|
|
|
|
+ const [deptId = '', userId = '', createTime = '', orderId = '', orderStatus = ''] =
|
|
|
|
|
+ rawId.split(',')
|
|
|
|
|
+
|
|
|
|
|
+ Object.assign(routeInfo, {
|
|
|
|
|
+ rawId,
|
|
|
|
|
+ deptId: toNumber(deptId),
|
|
|
|
|
+ userId: toNumber(userId),
|
|
|
|
|
+ createTime: toNumber(createTime),
|
|
|
|
|
+ orderId: toNumber(orderId),
|
|
|
|
|
+ orderStatus: orderStatus
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ console.log('运行记录填报路由信息:', { ...routeInfo })
|
|
|
|
|
+ getInitialData()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+watch(() => route.params.id, storeRouteInfo, { immediate: true })
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<template>
|
|
|
|
|
+ <ContentWrap class="fill-content-wrap">
|
|
|
|
|
+ <div class="fill-layout">
|
|
|
|
|
+ <aside class="device-panel">
|
|
|
|
|
+ <div class="device-panel__header">
|
|
|
|
|
+ <div class="device-panel__title">
|
|
|
|
|
+ <span class="device-panel__icon">
|
|
|
|
|
+ <el-icon><Monitor /></el-icon>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <h2>填报列表</h2>
|
|
|
|
|
+ <p>选择并填写运行记录</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <span class="device-count">{{ deviceFillList.length }} 项</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <el-scrollbar class="device-scrollbar">
|
|
|
|
|
+ <div v-if="deviceFillList.length" class="device-list">
|
|
|
|
|
+ <button
|
|
|
|
|
+ v-for="(device, index) in deviceFillList"
|
|
|
|
|
+ :key="`${device.deviceId ?? 'report'}-${device.deviceCode || device.deviceName}-${index}`"
|
|
|
|
|
+ type="button"
|
|
|
|
|
+ class="device-item"
|
|
|
|
|
+ :class="{
|
|
|
|
|
+ 'is-active': selectedDeviceIndex === index,
|
|
|
|
|
+ 'is-filled': device.isFill === '1'
|
|
|
|
|
+ }"
|
|
|
|
|
+ @click="selectDevice(index)">
|
|
|
|
|
+ <span class="device-item__marker"></span>
|
|
|
|
|
+ <span class="device-item__content">
|
|
|
|
|
+ <span class="device-item__code">{{ getDevicePrimaryText(device) }}</span>
|
|
|
|
|
+ <span class="device-item__name">{{ getDeviceSecondaryText(device) }}</span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ <span class="device-item__arrow">
|
|
|
|
|
+ <el-icon><ArrowRight /></el-icon>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <el-empty v-else description="暂无设备数据" :image-size="90" />
|
|
|
|
|
+ </el-scrollbar>
|
|
|
|
|
+ </aside>
|
|
|
|
|
+
|
|
|
|
|
+ <main class="form-panel">
|
|
|
|
|
+ <template v-if="selectedDevice">
|
|
|
|
|
+ <div class="form-panel__header">
|
|
|
|
|
+ <div class="form-panel__identity">
|
|
|
|
|
+ <div class="form-panel__eyebrow">
|
|
|
|
|
+ {{ selectedDevice.deviceCode ? '设备填报' : '日报填报' }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <h1>{{ selectedDevice.deviceName || '未命名填报项' }}</h1>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="basic-info__grid">
|
|
|
|
|
+ <div class="basic-info__item">
|
|
|
|
|
+ <span class="basic-info__label">创建时间</span>
|
|
|
|
|
+ <span class="basic-info__value">
|
|
|
|
|
+ {{ formatCreateTime(selectedDevice.createTime) }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="basic-info__item">
|
|
|
|
|
+ <span class="basic-info__label">所属队伍</span>
|
|
|
|
|
+ <span class="basic-info__value">
|
|
|
|
|
+ {{ selectedDevice.orgName || '暂无队伍信息' }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div v-if="isDailyReport" class="basic-info__item basic-info__item--well">
|
|
|
|
|
+ <span class="basic-info__label">井号</span>
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-if="showWellSelector"
|
|
|
|
|
+ v-model="taskId"
|
|
|
|
|
+ :disabled="isFormDisabled"
|
|
|
|
|
+ size="default"
|
|
|
|
|
+ placeholder="请选择井号"
|
|
|
|
|
+ class="basic-info__select"
|
|
|
|
|
+ @change="handleTaskIdChange">
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="option in selectedDevice.wellNamePair ?? []"
|
|
|
|
|
+ :key="option.value"
|
|
|
|
|
+ :label="option.label"
|
|
|
|
|
+ :value="option.value" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ <span v-else class="basic-info__value">
|
|
|
|
|
+ {{ selectedDevice.wellName || '暂无井号信息' }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <span v-if="selectedDevice.deviceCode" class="selected-device-code">
|
|
|
|
|
+ {{ selectedDevice.deviceCode }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div v-loading="deviceAttrsLoading" class="form-panel__body">
|
|
|
|
|
+ <section v-if="sumList.length" class="sum-section">
|
|
|
|
|
+ <h3 class="sum-section__title">累计信息</h3>
|
|
|
|
|
+ <div class="sum-list">
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-for="(attribute, index) in sumList"
|
|
|
|
|
+ :key="attribute.description || `${attribute.name}-${index}`"
|
|
|
|
|
+ class="sum-list__item">
|
|
|
|
|
+ <span class="sum-list__label">{{ attribute.name }}</span>
|
|
|
|
|
+ <span class="sum-list__value">{{ getSumDisplayValue(attribute) }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </section>
|
|
|
|
|
+
|
|
|
|
|
+ <section v-if="nonSumList.length" class="fill-section">
|
|
|
|
|
+ <h3 class="fill-section__title">填写信息</h3>
|
|
|
|
|
+ <el-form
|
|
|
|
|
+ ref="fillFormRef"
|
|
|
|
|
+ :model="fillFormModel"
|
|
|
|
|
+ :disabled="isFormDisabled"
|
|
|
|
|
+ label-position="top"
|
|
|
|
|
+ size="default"
|
|
|
|
|
+ scroll-to-error
|
|
|
|
|
+ class="fill-form">
|
|
|
|
|
+ <div class="fill-form__grid">
|
|
|
|
|
+ <el-form-item
|
|
|
|
|
+ v-for="(attribute, index) in nonSumList"
|
|
|
|
|
+ :key="attribute.description || `${attribute.name}-${index}`"
|
|
|
|
|
+ :prop="`nonSumList.${index}.fillContent`"
|
|
|
|
|
+ :rules="getAttributeRules(attribute)"
|
|
|
|
|
+ :class="{ 'fill-form__item--wide': attribute.type === 'textarea' }">
|
|
|
|
|
+ <template #label>
|
|
|
|
|
+ <span class="fill-form__label">
|
|
|
|
|
+ {{ attribute.name }}
|
|
|
|
|
+ <el-tooltip
|
|
|
|
|
+ v-if="Number(attribute.isCollection) === 1"
|
|
|
|
|
+ content="以下数值取自PLC,如有不符请修改"
|
|
|
|
|
+ placement="top">
|
|
|
|
|
+ <el-icon class="collection-warning-icon">
|
|
|
|
|
+ <Warning />
|
|
|
|
|
+ </el-icon>
|
|
|
|
|
+ </el-tooltip>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-if="attribute.type === 'textarea'"
|
|
|
|
|
+ v-model="attribute.fillContent"
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ :autosize="{ minRows: 3, maxRows: 6 }"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ placeholder="请输入内容" />
|
|
|
|
|
+
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-else-if="attribute.type === 'double'"
|
|
|
|
|
+ v-model="attribute.fillContent"
|
|
|
|
|
+ :min="0"
|
|
|
|
|
+ :max="getAttributeMax(attribute)"
|
|
|
|
|
+ :precision="2"
|
|
|
|
|
+ :step="0.01"
|
|
|
|
|
+ :controls="false"
|
|
|
|
|
+ class="fill-form__control"
|
|
|
|
|
+ placeholder="请输入数字"
|
|
|
|
|
+ @change="handleDoubleChange(attribute)" />
|
|
|
|
|
+
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-else-if="attribute.type === 'enum'"
|
|
|
|
|
+ v-model="attribute.fillContent"
|
|
|
|
|
+ class="fill-form__control"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ filterable
|
|
|
|
|
+ placeholder="请选择">
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="option in getDictOptions(attribute.description)"
|
|
|
|
|
+ :key="String(option.value)"
|
|
|
|
|
+ :label="option.label"
|
|
|
|
|
+ :value="getDictOptionValue(option.value)" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-else
|
|
|
|
|
+ v-model="attribute.fillContent"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ placeholder="请输入内容" />
|
|
|
|
|
+
|
|
|
|
|
+ <span v-if="getRdLimitWarning(attribute)" class="rd-limit-warning">
|
|
|
|
|
+ {{ getRdLimitWarning(attribute) }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <section v-if="showProductionDetails" class="production-section">
|
|
|
|
|
+ <div class="production-section__header">
|
|
|
|
|
+ <h3 class="production-section__title">生产动态</h3>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ link
|
|
|
|
|
+ :icon="Plus"
|
|
|
|
|
+ :disabled="isFormDisabled"
|
|
|
|
|
+ @click="addProductionStatusRow">
|
|
|
|
|
+ 添加一行
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <el-table
|
|
|
|
|
+ v-loading="reportDetailsLoading"
|
|
|
|
|
+ :data="reportDetails"
|
|
|
|
|
+ border
|
|
|
|
|
+ class="production-table">
|
|
|
|
|
+ <el-table-column label="日期" width="170">
|
|
|
|
|
+ <template #default="{ row, $index }">
|
|
|
|
|
+ <el-form-item
|
|
|
|
|
+ :prop="`reportDetails.${$index}.reportDate`"
|
|
|
|
|
+ :rules="{
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ message: '请选择日期',
|
|
|
|
|
+ trigger: ['change', 'blur']
|
|
|
|
|
+ }"
|
|
|
|
|
+ class="production-table__form-item">
|
|
|
|
|
+ <el-date-picker
|
|
|
|
|
+ v-model="row.reportDate"
|
|
|
|
|
+ type="date"
|
|
|
|
|
+ value-format="x"
|
|
|
|
|
+ placeholder="选择日期"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ class="production-table__control"
|
|
|
|
|
+ @change="syncCurrentDepth" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+
|
|
|
|
|
+ <el-table-column label="开始时间" width="140">
|
|
|
|
|
+ <template #default="{ row, $index }">
|
|
|
|
|
+ <el-form-item
|
|
|
|
|
+ :prop="`reportDetails.${$index}.startTime`"
|
|
|
|
|
+ :rules="{
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ message: '请选择开始时间',
|
|
|
|
|
+ trigger: ['change', 'blur']
|
|
|
|
|
+ }"
|
|
|
|
|
+ class="production-table__form-item">
|
|
|
|
|
+ <el-time-picker
|
|
|
|
|
+ v-model="row.startTime"
|
|
|
|
|
+ format="HH:mm"
|
|
|
|
|
+ value-format="HH:mm"
|
|
|
|
|
+ placeholder="开始时间"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ class="production-table__control"
|
|
|
|
|
+ @change="handleReportTimeChange(row)" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+
|
|
|
|
|
+ <el-table-column label="结束时间" width="140">
|
|
|
|
|
+ <template #default="{ row, $index }">
|
|
|
|
|
+ <el-form-item
|
|
|
|
|
+ :prop="`reportDetails.${$index}.endTime`"
|
|
|
|
|
+ :rules="{
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ message: '请选择结束时间',
|
|
|
|
|
+ trigger: ['change', 'blur']
|
|
|
|
|
+ }"
|
|
|
|
|
+ class="production-table__form-item">
|
|
|
|
|
+ <el-time-picker
|
|
|
|
|
+ v-model="row.endTime"
|
|
|
|
|
+ format="HH:mm"
|
|
|
|
|
+ value-format="HH:mm"
|
|
|
|
|
+ placeholder="结束时间"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ class="production-table__control"
|
|
|
|
|
+ @change="handleReportTimeChange(row)" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+
|
|
|
|
|
+ <el-table-column label="时长(H)" width="90" align="center">
|
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
|
+ <span class="production-table__duration">
|
|
|
|
|
+ {{ Number(row.duration || 0).toFixed(2) }}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+
|
|
|
|
|
+ <el-table-column label="工况" min-width="180">
|
|
|
|
|
+ <template #default="{ row, $index }">
|
|
|
|
|
+ <el-form-item
|
|
|
|
|
+ :prop="`reportDetails.${$index}.currentOperation`"
|
|
|
|
|
+ :rules="{
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ message: '请输入工况',
|
|
|
|
|
+ trigger: ['change', 'blur']
|
|
|
|
|
+ }"
|
|
|
|
|
+ class="production-table__form-item">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="row.currentOperation"
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ :autosize="{ minRows: 1, maxRows: 3 }"
|
|
|
|
|
+ maxlength="1000"
|
|
|
|
|
+ show-word-limit
|
|
|
|
|
+ placeholder="请输入工况" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+
|
|
|
|
|
+ <el-table-column v-if="showDepth" label="结束井深(m)" width="160">
|
|
|
|
|
+ <template #default="{ row, $index }">
|
|
|
|
|
+ <el-form-item
|
|
|
|
|
+ :prop="`reportDetails.${$index}.currentDepth`"
|
|
|
|
|
+ :rules="{
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ message: '请输入结束井深',
|
|
|
|
|
+ trigger: ['change', 'blur']
|
|
|
|
|
+ }"
|
|
|
|
|
+ class="production-table__form-item">
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="row.currentDepth"
|
|
|
|
|
+ :min="0"
|
|
|
|
|
+ :precision="2"
|
|
|
|
|
+ :controls="false"
|
|
|
|
|
+ placeholder="结束井深"
|
|
|
|
|
+ class="production-table__control"
|
|
|
|
|
+ @change="syncCurrentDepth" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+
|
|
|
|
|
+ <el-table-column label="详细描述" min-width="220">
|
|
|
|
|
+ <template #default="{ row, $index }">
|
|
|
|
|
+ <el-form-item
|
|
|
|
|
+ :prop="`reportDetails.${$index}.constructionDetail`"
|
|
|
|
|
+ :rules="{
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ message: '请输入详细描述',
|
|
|
|
|
+ trigger: ['change', 'blur']
|
|
|
|
|
+ }"
|
|
|
|
|
+ class="production-table__form-item">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="row.constructionDetail"
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ :autosize="{ minRows: 1, maxRows: 3 }"
|
|
|
|
|
+ maxlength="1000"
|
|
|
|
|
+ show-word-limit
|
|
|
|
|
+ placeholder="请输入详细描述" />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+
|
|
|
|
|
+ <el-table-column label="操作" width="80" fixed="right" align="center">
|
|
|
|
|
+ <template #default="{ $index }">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="danger"
|
|
|
|
|
+ link
|
|
|
|
|
+ :icon="Delete"
|
|
|
|
|
+ :disabled="isFormDisabled"
|
|
|
|
|
+ @click="removeProductionStatusRow($index)">
|
|
|
|
|
+ 删除
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ </section>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ </section>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div v-if="showFormActions" class="form-actions">
|
|
|
|
|
+ <el-button size="default" @click="clearFillForm">清空</el-button>
|
|
|
|
|
+ <el-button size="default" type="primary" @click="handleConfirm">确定</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+
|
|
|
|
|
+ <el-empty v-else class="form-panel__empty" description="请先选择设备" />
|
|
|
|
|
+ </main>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <el-dialog
|
|
|
|
|
+ v-model="rdConfirmVisible"
|
|
|
|
|
+ title="确认提交超限数据"
|
|
|
|
|
+ width="540px"
|
|
|
|
|
+ append-to-body
|
|
|
|
|
+ destroy-on-close
|
|
|
|
|
+ class="rd-limit-dialog">
|
|
|
|
|
+ <p class="rd-confirm-description">以下数据超过建议上限,请确认是否继续提交。</p>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="rd-confirm-list">
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-for="item in rdExceededAttributes"
|
|
|
|
|
+ :key="item.attribute.description || item.attribute.name"
|
|
|
|
|
+ class="rd-confirm-item">
|
|
|
|
|
+ <div class="rd-confirm-item__header">
|
|
|
|
|
+ <span class="rd-confirm-item__name">{{ item.attribute.name }}</span>
|
|
|
|
|
+ <span class="rd-confirm-item__tag">超限</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="rd-confirm-item__values">
|
|
|
|
|
+ <div class="rd-confirm-item__value">
|
|
|
|
|
+ <span>填写值</span>
|
|
|
|
|
+ <strong>{{ item.value }}</strong>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="rd-confirm-item__value">
|
|
|
|
|
+ <span>建议上限</span>
|
|
|
|
|
+ <strong>{{ item.max }}</strong>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="rd-confirm-item__formula">
|
|
|
|
|
+ 计算方式:累计值 {{ item.totalRunTime }} + {{ item.increment }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <el-button size="default" @click="rdConfirmVisible = false">返回修改</el-button>
|
|
|
|
|
+ <el-button size="default" type="primary" @click="confirmRdExceededSubmit">
|
|
|
|
|
+ 确认提交
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+ </ContentWrap>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped>
|
|
|
|
|
+.fill-content-wrap {
|
|
|
|
|
+ --fill-primary: var(--el-color-primary);
|
|
|
|
|
+ --fill-primary-light: var(--el-color-primary-light-9);
|
|
|
|
|
+
|
|
|
|
|
+ height: calc(
|
|
|
|
|
+ 100vh - 20px - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height)
|
|
|
|
|
+ );
|
|
|
|
|
+ margin-bottom: 0 !important;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.fill-content-wrap :deep(.el-card__body) {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.fill-layout {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ grid-template-columns: 286px minmax(0, 1fr);
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ background: var(--el-bg-color);
|
|
|
|
|
+ border: 1px solid var(--el-border-color-lighter);
|
|
|
|
|
+ border-radius: 16px;
|
|
|
|
|
+ box-shadow: 0 10px 32px rgb(31 45 61 / 8%);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-panel {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ background: var(--el-fill-color-extra-light);
|
|
|
|
|
+ border-right: 1px solid var(--el-border-color-light);
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-panel__header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ height: 88px;
|
|
|
|
|
+ min-height: 88px;
|
|
|
|
|
+ padding: 16px 18px;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ border-bottom: 1px solid var(--el-border-color-light);
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-panel__title {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-panel__icon {
|
|
|
|
|
+ display: inline-flex;
|
|
|
|
|
+ width: 38px;
|
|
|
|
|
+ height: 38px;
|
|
|
|
|
+ font-size: 18px;
|
|
|
|
|
+ color: var(--fill-primary);
|
|
|
|
|
+ background: var(--fill-primary-light);
|
|
|
|
|
+ border: 1px solid var(--el-color-primary-light-7);
|
|
|
|
|
+ border-radius: 10px;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-panel__title h2,
|
|
|
|
|
+.form-panel__header h1 {
|
|
|
|
|
+ padding: 0;
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-panel__title h2 {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: 650;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-panel__title p {
|
|
|
|
|
+ margin: 3px 0 0;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: var(--el-text-color-secondary);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-count {
|
|
|
|
|
+ min-width: 40px;
|
|
|
|
|
+ padding: 4px 9px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ color: var(--el-text-color-secondary);
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ background: var(--el-fill-color);
|
|
|
|
|
+ border-radius: 999px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-scrollbar {
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ padding: 12px;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-list {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ padding: 5px;
|
|
|
|
|
+ background: var(--el-bg-color);
|
|
|
|
|
+ border: 1px solid var(--el-border-color-extra-light);
|
|
|
|
|
+ border-radius: 12px;
|
|
|
|
|
+ box-shadow: 0 2px 8px rgb(31 45 61 / 4%);
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 2px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-item {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ padding: 12px 10px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ font-family: inherit;
|
|
|
|
|
+ text-align: left;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ background: transparent;
|
|
|
|
|
+ border: 0;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ transition:
|
|
|
|
|
+ color 0.18s ease,
|
|
|
|
|
+ background-color 0.18s ease,
|
|
|
|
|
+ box-shadow 0.18s ease;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-item:hover {
|
|
|
|
|
+ background: var(--el-fill-color-light);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-item.is-active {
|
|
|
|
|
+ background: var(--fill-primary-light);
|
|
|
|
|
+ box-shadow: inset 3px 0 0 var(--fill-primary);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-item.is-active.is-filled {
|
|
|
|
|
+ background: var(--el-color-success-light-9);
|
|
|
|
|
+ box-shadow: inset 3px 0 0 var(--el-color-success);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-item__marker {
|
|
|
|
|
+ width: 8px;
|
|
|
|
|
+ height: 8px;
|
|
|
|
|
+ background: var(--el-border-color-darker);
|
|
|
|
|
+ border-radius: 999px;
|
|
|
|
|
+ transition: background 0.2s ease;
|
|
|
|
|
+ flex: 0 0 auto;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-item.is-active .device-item__marker {
|
|
|
|
|
+ background: var(--fill-primary);
|
|
|
|
|
+ box-shadow: 0 0 0 4px var(--el-color-primary-light-8);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-item.is-filled .device-item__marker {
|
|
|
|
|
+ background: var(--el-color-success);
|
|
|
|
|
+ box-shadow: 0 0 0 4px var(--el-color-success-light-8);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-item__content {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 4px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-item__code {
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-item.is-active .device-item__code {
|
|
|
|
|
+ color: var(--fill-primary);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-item.is-filled .device-item__code {
|
|
|
|
|
+ color: var(--el-color-success);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-item__name {
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ color: var(--el-text-color-regular);
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-item__arrow {
|
|
|
|
|
+ display: inline-flex;
|
|
|
|
|
+ width: 26px;
|
|
|
|
|
+ height: 26px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: var(--el-text-color-placeholder);
|
|
|
|
|
+ border-radius: 7px;
|
|
|
|
|
+ transition:
|
|
|
|
|
+ color 0.2s ease,
|
|
|
|
|
+ background-color 0.2s ease,
|
|
|
|
|
+ transform 0.2s ease;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-item.is-active .device-item__arrow {
|
|
|
|
|
+ color: var(--fill-primary);
|
|
|
|
|
+ background: var(--el-bg-color);
|
|
|
|
|
+ transform: translateX(2px);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.device-item.is-active.is-filled .device-item__arrow {
|
|
|
|
|
+ color: var(--el-color-success);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.form-panel {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ background: var(--el-bg-color);
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.form-panel__header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ height: 88px;
|
|
|
|
|
+ min-height: 88px;
|
|
|
|
|
+ padding: 16px 28px;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 24px;
|
|
|
|
|
+ border-bottom: 1px solid var(--el-border-color-light);
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.form-panel__identity {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ min-width: 140px;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.form-panel__eyebrow {
|
|
|
|
|
+ margin-bottom: 5px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ letter-spacing: 0.12em;
|
|
|
|
|
+ color: var(--fill-primary);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.form-panel__header h1 {
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ font-size: 21px;
|
|
|
|
|
+ font-weight: 650;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.selected-device-code {
|
|
|
|
|
+ max-width: 22%;
|
|
|
|
|
+ padding: 7px 13px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: var(--fill-primary);
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ background: var(--fill-primary-light);
|
|
|
|
|
+ border: 1px solid var(--el-color-primary-light-7);
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.form-panel__body {
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ padding: 24px 28px 28px;
|
|
|
|
|
+ overflow-y: auto;
|
|
|
|
|
+ overscroll-behavior: contain;
|
|
|
|
|
+ scrollbar-gutter: stable;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.basic-info__grid {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: flex-end;
|
|
|
|
|
+ gap: 28px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.basic-info__item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ min-width: 110px;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.basic-info__item--well {
|
|
|
|
|
+ width: 210px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.basic-info__label {
|
|
|
|
|
+ margin-bottom: 5px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ line-height: 18px;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.basic-info__value {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ height: 32px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ line-height: 20px;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.basic-info__select {
|
|
|
|
|
+ width: 210px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.sum-section {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ padding: 2px 0 16px;
|
|
|
|
|
+ margin-bottom: 18px;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 24px;
|
|
|
|
|
+ border-bottom: 1px solid var(--el-border-color-extra-light);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.sum-section__title {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ padding-left: 9px;
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+ flex: 0 0 auto;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.sum-list {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ max-width: 100%;
|
|
|
|
|
+ overflow-x: auto;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.sum-list__item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ padding: 0 22px;
|
|
|
|
|
+ align-items: baseline;
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.sum-list__item:first-child {
|
|
|
|
|
+ padding-left: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.sum-list__item + .sum-list__item {
|
|
|
|
|
+ border-left: 1px solid var(--el-border-color-lighter);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.sum-list__label {
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.sum-list__value {
|
|
|
|
|
+ font-size: 17px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ line-height: 22px;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.fill-section__title {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ padding-left: 9px;
|
|
|
|
|
+ margin: 0 0 16px;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.sum-section__title::before,
|
|
|
|
|
+.fill-section__title::before,
|
|
|
|
|
+.production-section__title::before {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: 50%;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ width: 3px;
|
|
|
|
|
+ height: 20px;
|
|
|
|
|
+ background: var(--fill-primary);
|
|
|
|
|
+ border-radius: 999px;
|
|
|
|
|
+ content: '';
|
|
|
|
|
+ transform: translateY(-50%);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.fill-form__grid {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
|
|
|
|
+ column-gap: 22px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.fill-form :deep(.el-form-item) {
|
|
|
|
|
+ margin-bottom: 18px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.fill-form :deep(.el-form-item__label) {
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.fill-form__label {
|
|
|
|
|
+ display: inline-flex;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 5px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.collection-warning-icon {
|
|
|
|
|
+ font-size: 15px;
|
|
|
|
|
+ color: var(--el-color-warning);
|
|
|
|
|
+ cursor: help;
|
|
|
|
|
+ flex: 0 0 auto;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.rd-limit-warning {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ margin-top: 5px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ line-height: 18px;
|
|
|
|
|
+ color: var(--el-color-warning-dark-2);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-limit-dialog) {
|
|
|
|
|
+ max-width: calc(100vw - 32px);
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ border-radius: 12px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-limit-dialog .el-dialog__header) {
|
|
|
|
|
+ padding: 16px 16px 10px;
|
|
|
|
|
+ margin-right: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-limit-dialog .el-dialog__title) {
|
|
|
|
|
+ font-size: 17px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-limit-dialog .el-dialog__body) {
|
|
|
|
|
+ padding: 0 16px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-confirm-description) {
|
|
|
|
|
+ margin: 0 0 12px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ line-height: 20px;
|
|
|
|
|
+ color: var(--el-text-color-secondary);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-confirm-list) {
|
|
|
|
|
+ max-height: min(430px, calc(100vh - 190px));
|
|
|
|
|
+ overflow-y: auto;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-confirm-item) {
|
|
|
|
|
+ padding: 11px 12px;
|
|
|
|
|
+ margin-bottom: 8px;
|
|
|
|
|
+ background: var(--el-color-warning-light-9);
|
|
|
|
|
+ border: 1px solid var(--el-color-warning-light-7);
|
|
|
|
|
+ border-radius: 9px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-confirm-item:last-child) {
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-confirm-item__header) {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ margin-bottom: 9px;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-confirm-item__name) {
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-confirm-item__tag) {
|
|
|
|
|
+ padding: 1px 7px;
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+ line-height: 18px;
|
|
|
|
|
+ color: var(--el-color-warning-dark-2);
|
|
|
|
|
+ background: var(--el-bg-color);
|
|
|
|
|
+ border: 1px solid var(--el-color-warning-light-5);
|
|
|
|
|
+ border-radius: 999px;
|
|
|
|
|
+ flex: 0 0 auto;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-confirm-item__values) {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ margin-bottom: 7px;
|
|
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-confirm-item__value) {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ padding: 8px 10px;
|
|
|
|
|
+ background: var(--el-bg-color);
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 3px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-confirm-item__value span) {
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+ color: var(--el-text-color-secondary);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-confirm-item__value strong) {
|
|
|
|
|
+ font-size: 15px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-confirm-item__formula) {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ line-height: 18px;
|
|
|
|
|
+ color: var(--el-text-color-regular);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-limit-dialog .el-dialog__footer) {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ padding: 12px 16px 14px;
|
|
|
|
|
+ justify-content: flex-end;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:global(.rd-limit-dialog .el-dialog__footer .el-button + .el-button) {
|
|
|
|
|
+ margin-left: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.fill-form__item--wide {
|
|
|
|
|
+ grid-column: 1 / -1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.fill-form__control,
|
|
|
|
|
+.fill-form :deep(.el-input),
|
|
|
|
|
+.fill-form :deep(.el-textarea) {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.fill-form :deep(.el-input-number .el-input__inner) {
|
|
|
|
|
+ text-align: left;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.production-section {
|
|
|
|
|
+ padding-top: 20px;
|
|
|
|
|
+ margin-top: 8px;
|
|
|
|
|
+ border-top: 1px solid var(--el-border-color-lighter);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.production-section__header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ margin-bottom: 14px;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.production-section__title {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ padding-left: 9px;
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.production-table {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.production-table :deep(.el-table__cell) {
|
|
|
|
|
+ vertical-align: middle;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.production-table :deep(.el-table__header-wrapper th) {
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+ background: var(--el-fill-color-light);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.production-table__form-item {
|
|
|
|
|
+ margin-bottom: 0 !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.production-table__control {
|
|
|
|
|
+ width: 100% !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.production-table__duration {
|
|
|
|
|
+ display: inline-flex;
|
|
|
|
|
+ min-height: 32px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.form-actions {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ min-height: 56px;
|
|
|
|
|
+ padding: 10px 24px;
|
|
|
|
|
+ background: var(--el-bg-color);
|
|
|
|
|
+ border-top: 1px solid var(--el-border-color-lighter);
|
|
|
|
|
+ box-shadow: 0 -4px 14px rgb(31 45 61 / 4%);
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: flex-end;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+ flex: 0 0 auto;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.form-actions :deep(.el-button) {
|
|
|
|
|
+ min-width: 76px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.form-panel__empty {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+@media (width <= 900px) {
|
|
|
|
|
+ .fill-layout {
|
|
|
|
|
+ grid-template-columns: 1fr;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .device-panel {
|
|
|
|
|
+ max-height: 340px;
|
|
|
|
|
+ border-right: 0;
|
|
|
|
|
+ border-bottom: 1px solid var(--el-border-color-lighter);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .form-panel__header,
|
|
|
|
|
+ .form-panel__body {
|
|
|
|
|
+ padding-right: 20px;
|
|
|
|
|
+ padding-left: 20px;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|