| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301 |
- <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'
- import { calculateDuration, formatT } from '@/utils/formatTime'
- import { Delete, Plus } from '@element-plus/icons-vue'
- import { useDebounceFn } from '@vueuse/core'
- import dayjs from 'dayjs'
- /** 运行记录填报 列表 */
- 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
- }
- 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: ''
- })
- }
- const removeProductionStatusRow = (index: number) => {
- if (index === 0) {
- message.warning('至少填写一条生产动态')
- return
- }
- reportDetails.value.splice(index, 1)
- }
- const acalculateDuration = (row: any) => {
- calculateDuration(row)
- inputCurrentDepth()
- }
- 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
- })
- const currentDepth = attrList.value.find((item) => item.description === 'currentDepth')
- if (currentDepth) currentDepth.fillContent = latestDetail.currentDepth
- }
- }, 300)
- // 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 showDepth = computed(() => {
- return attrList.value.some((item) => item.description === 'currentDepth')
- })
- const NON_KEYS = [
- 'repairTime',
- 'selfStopTime',
- 'accidentTime',
- 'complexityTime',
- 'rectificationTime',
- 'waitingStopTime',
- 'partyaDesign',
- 'partyaPrepare',
- 'partyaResource',
- 'relocationTime',
- 'winterBreakTime',
- 'otherNptTime'
- ]
- const keys = [
- 'repairTime',
- 'selfStopTime',
- 'accidentTime',
- 'complexityTime',
- 'rectificationTime',
- 'waitingStopTime',
- 'partyaDesign',
- 'partyaPrepare',
- 'partyaResource',
- 'relocationTime',
- 'winterBreakTime',
- 'otherNptTime',
- 'drillingWorkingTime',
- 'otherProductionTime',
- 'ratedProductionTime',
- 'productionTime',
- 'dailyInjectGasTime',
- 'otherNptReason'
- ]
- const sumNonProdTimes = () => {
- let sum = 0
- NON_KEYS.forEach((field) => {
- sum += attrList.value.find((item) => item.description === field)?.fillContent || 0
- })
- return sum
- }
- 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()
- let total = 0
- let msg = ''
- total = parseFloat((gasTime + nonProdSum).toFixed(2))
- msg = `运转(${gasTime})+非生产(${nonProdSum})=${total}H,必须为24H`
- if (Math.abs(total - 24) > 0.01) {
- if (!isNon) callback(new Error(msg))
- else callback(new Error())
- } else {
- callback()
- }
- }
- // 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()
- }
- }
- 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 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 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
- }
- })
- nextTick(() => {
- const validator = totalValidatorComputed.value
- if (!validator) return
- NON_KEYS.forEach((field) => {
- rules[field] = [{ validator: validator(true) }]
- })
- })
- let cxStatus = true
- // 计算数字输入的最大长度(根据阈值动态计算)
- const calculateMaxLength = (item: any) => {
- if (item.type !== 'double' || !item.threshold) return undefined
- const max = parseFloat(item.threshold)
- if (isNaN(max)) return undefined
- // 整数部分长度 + 可能的小数点 + 两位小数
- return max.toString().length + (max.toString().includes('.') ? 0 : 3)
- }
- // 简单的节流函数,避免提示信息过于频繁
- const throttle = <T extends (...args: any[]) => any>(fn: T, delay: number) => {
- let lastTime = 0
- return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
- const now = Date.now()
- if (now - lastTime >= delay) {
- fn.apply(this, args)
- lastTime = now
- }
- }
- }
- const showComponent = () => {
- if (JSON.parse(fillStatus) === 1 || JSON.parse(fillStatus) === 3) {
- showStatus = false
- }
- }
- const noGasTasks = ref<number[]>([])
- /** 查询列表 */
- 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()
- const daily = list.value.find((item) => item.deviceName === '生产日报') ?? {
- wellNamePair: {},
- noGasTasks: []
- }
- taskOptions.value = Object.keys(daily.wellNamePair ?? {}).map((key) => ({
- label: daily.wellNamePair[key],
- value: Number(key)
- }))
- noGasTasks.value = (daily.noGasTasks ?? []).map(Number)
- taskId.value = taskOptions.value[0]?.value
- 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
- }))
- if (!reportDetails.value.length) {
- addProductionStatusRow()
- }
- })
- } finally {
- loading.value = false
- }
- }
- 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 = '生产日报'
- }
- 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}),请确认是否正确!`
- )
- }
- }
- }
- }
- }
- 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 timeKeys = keys.filter((k) => k !== 'otherNptReason')
- 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)
- })
- // 为非累计数据添加最大值限制
- 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)
- })
- 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 formRef = ref<FormInstance[] | null>(null)
- const submitLoading = ref(false)
- /** 获取填写信息保存到后台*/
- const getFillInfo = async () => {
- if (!formRef.value) return
- submitLoading.value = true
- 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 === '')
- )
- })
- if (emptyFields.length > 0) {
- ElMessage.error(t('operationFillForm.fill'))
- return
- }
- }
- 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 (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 // 用户取消保存
- }
- }
- }
- 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
- }))
- }
- await IotOpeationFillApi.insertLog(reqData)
- message.success(t('common.createSuccess'))
- // 发送操作成功的事件
- emit('success')
- cxStatus = false
- getList()
- } catch (error) {
- console.error('保存失败:', error)
- } finally {
- submitLoading.value = false
- }
- }
- /**清空填写信息*/
- const deleteFillInfo = () => {
- attrList.value.forEach(function (item) {
- item.fillContent = ''
- })
- }
- /** 初始化 **/
- 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%; /* 根据你的需求调整 */
- /* 可选:添加内边距和边框美化 */
- padding: 16px;
- /* 超出部分显示垂直滚动条 */
- overflow-y: auto;
- border: 1px solid #e5e7eb;
- border-radius: 4px;
- }
- /* 可选:美化滚动条 */
- .scrollable-form::-webkit-scrollbar {
- width: 6px;
- }
- .scrollable-form::-webkit-scrollbar-track {
- background: #f1f1f1;
- border-radius: 3px;
- }
- .scrollable-form::-webkit-scrollbar-thumb {
- background: #c1c1c1;
- border-radius: 3px;
- }
- .scrollable-form::-webkit-scrollbar-thumb:hover {
- background: #a8a8a8;
- }
- .back-red {
- /* 红色背景 */
- background-color: red;
- }
- .back-blue {
- background-color: grey;
- }
- .step-container {
- display: grid;
- grid-template-columns: 220px 1fr;
- gap: 10px;
- height: 100%;
- min-height: 600px;
- }
- .steps-nav {
- padding-right: 15px;
- overflow-y: auto;
- }
- .form-wrapper {
- padding: 30px;
- background: #fff;
- border-radius: 8px;
- box-shadow: 0 2px 12px rgb(0 0 0 / 10%);
- }
- .navigation-controls {
- margin-top: 40px;
- text-align: center;
- }
- .custom-label {
- padding: 0 10px;
- font-size: 17px;
- font-weight: 1000;
- color: forestgreen;
- }
- .custom-label1 {
- padding: 0 10px;
- font-size: 17px;
- font-weight: 1000;
- }
- .has-border {
- padding: 3px 8px; /* 适当增加内边距,避免文字太贴近边框 */
- font-size: 20px;
- border: 2px solid #333; /* 加粗到2px,使用深灰色#333让边框更清晰 */
- border-radius: 2px; /* 轻微圆角,可选 */
- }
- ::v-deep .el-step__icon {
- color: #fff;
- background-color: #409eff;
- border: 0;
- }
- .step-title-wrapper {
- position: relative;
- display: inline-flex;
- padding-right: 25px;
- align-items: center;
- gap: 8px;
- }
- /* 覆盖步骤条默认样式 */
- :deep(.custom-steps) {
- /* 调整头部位置 */
- .el-step__head {
- top: 3px;
- }
- /* 标题容器定位 */
- .el-step__title {
- display: inline-block;
- padding-right: 0;
- margin-left: 10px;
- }
- /* 步骤连接线 */
- .el-step__line {
- left: 11px;
- background-color: #ebeef5;
- }
- /* 当前步骤样式 */
- .is-process .title-text {
- font-weight: 600;
- color: #409eff;
- }
- /* 完成状态图标 */
- .is-finish .tip-icon {
- color: #67c23a;
- }
- }
- .horizontal-container {
- display: flex;
- flex-wrap: wrap; /* 允许换行 */
- gap: 20px; /* 项目间距 */
- }
- .form-item-container {
- flex: 1; /* 等宽分布 */
- min-width: 200px; /* 最小宽度防止挤压 */
- }
- /* 新增日报填报项的边框样式 */
- .report-border {
- padding: 2px 4px;
- border: 2px solid #42b983; /* 使用Vue标志性的绿色边框 */
- border-radius: 4px;
- }
- :deep(.table-form-item) {
- .el-form-item__content {
- margin-left: 0 !important;
- }
- }
- </style>
|