xjindex.vue 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157
  1. <template>
  2. <el-row :gutter="20">
  3. <el-col :span="4" :xs="24">
  4. <ContentWrap class="h-1/1">
  5. <DeptTree2 :deptId="rootDeptId" @node-click="handleDeptNodeClick" />
  6. </ContentWrap>
  7. </el-col>
  8. <el-col :span="20" :xs="24">
  9. <ContentWrap>
  10. <!-- 搜索工作栏 -->
  11. <el-form
  12. class="-mb-15px"
  13. :model="queryParams"
  14. ref="queryFormRef"
  15. :inline="true"
  16. label-width="68px"
  17. >
  18. <el-form-item label="项目" prop="contractName">
  19. <el-input
  20. v-model="queryParams.contractName"
  21. placeholder="请输入项目"
  22. clearable
  23. @keyup.enter="handleQuery"
  24. class="!w-240px"
  25. />
  26. </el-form-item>
  27. <el-form-item label="任务" prop="taskName">
  28. <el-input
  29. v-model="queryParams.taskName"
  30. placeholder="请输入任务"
  31. clearable
  32. @keyup.enter="handleQuery"
  33. class="!w-240px"
  34. />
  35. </el-form-item>
  36. <el-form-item label="创建时间" prop="createTime">
  37. <el-date-picker
  38. v-model="queryParams.createTime"
  39. value-format="YYYY-MM-DD HH:mm:ss"
  40. type="daterange"
  41. start-placeholder="开始日期"
  42. end-placeholder="结束日期"
  43. :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
  44. class="!w-220px"
  45. :shortcuts="rangeShortcuts"
  46. />
  47. </el-form-item>
  48. <el-form-item>
  49. <el-button @click="handleQuery"
  50. ><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button
  51. >
  52. <el-button @click="resetQuery"
  53. ><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button
  54. >
  55. <el-button
  56. type="primary"
  57. plain
  58. @click="openForm('create')"
  59. v-hasPermi="['pms:iot-rh-daily-report:create']"
  60. >
  61. <Icon icon="ep:plus" class="mr-5px" /> 新增
  62. </el-button>
  63. <el-button
  64. type="success"
  65. plain
  66. @click="handleExport"
  67. :loading="exportLoading"
  68. v-hasPermi="['pms:iot-rh-daily-report:export']"
  69. >
  70. <Icon icon="ep:download" class="mr-5px" /> 导出
  71. </el-button>
  72. </el-form-item>
  73. </el-form>
  74. </ContentWrap>
  75. <ContentWrap class="mb-15px">
  76. <div class="color-legend">
  77. <div class="legend-item">
  78. <span class="color-indicator red"></span>
  79. <span
  80. >运行时效=生产时间/额定生产时间&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;超过100%红色预警</span
  81. >
  82. </div>
  83. <div class="legend-item">
  84. <span class="color-indicator orange"></span>
  85. <span
  86. >生产时间+非生产时间=额定生产时间&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;否则橙色预警</span
  87. >
  88. </div>
  89. <div class="legend-item">
  90. <span class="color-indicator yellow"></span>
  91. <span>当日油耗大于5吨&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;蓝色预警</span>
  92. </div>
  93. </div>
  94. </ContentWrap>
  95. <!-- 列表 -->
  96. <ContentWrap ref="tableContainerRef">
  97. <div class="table-container">
  98. <el-table
  99. ref="tableRef"
  100. v-loading="loading"
  101. :data="list"
  102. :stripe="true"
  103. :style="{ width: '100%' }"
  104. max-height="600"
  105. :cell-style="cellStyle"
  106. show-overflow-tooltip
  107. border
  108. >
  109. <el-table-column :label="t('iotDevice.serial')" width="56px" align="center">
  110. <template #default="scope">
  111. {{ scope.$index + 1 }}
  112. </template>
  113. </el-table-column>
  114. <el-table-column
  115. label="日期"
  116. align="center"
  117. prop="createTime"
  118. :formatter="dateFormatter2"
  119. :min-width="columnWidths.createTime.width"
  120. resizable
  121. />
  122. <el-table-column
  123. label="施工队伍"
  124. align="center"
  125. prop="deptName"
  126. :min-width="columnWidths.deptName.width"
  127. resizable
  128. />
  129. <el-table-column
  130. label="项目"
  131. align="center"
  132. prop="contractName"
  133. :min-width="columnWidths.contractName.width"
  134. resizable
  135. />
  136. <el-table-column
  137. label="任务"
  138. align="center"
  139. prop="taskName"
  140. :min-width="columnWidths.taskName.width"
  141. resizable
  142. />
  143. <el-table-column
  144. :label="t('project.status')"
  145. align="center"
  146. prop="repairStatus"
  147. :min-width="columnWidths.repairStatus.width"
  148. resizable
  149. >
  150. <template #default="scope">
  151. <dict-tag
  152. :type="DICT_TYPE.PMS_PROJECT_TASK_RY_REPAIR_SCHEDULE"
  153. :value="scope.row.repairStatus"
  154. />
  155. </template>
  156. </el-table-column>
  157. <el-table-column
  158. label="总施工井数"
  159. align="center"
  160. prop="totalConstructionWells"
  161. :min-width="columnWidths.totalConstructionWells.width"
  162. resizable
  163. />
  164. <el-table-column
  165. label="完工井数"
  166. align="center"
  167. prop="completedWells"
  168. :min-width="columnWidths.completedWells.width"
  169. resizable
  170. />
  171. <el-table-column
  172. :label="t('project.technology')"
  173. align="center"
  174. prop="technique"
  175. :min-width="columnWidths.technique.width"
  176. resizable
  177. >
  178. <template #default="scope">
  179. <dict-tag
  180. :type="DICT_TYPE.PMS_PROJECT_RY_TECHNOLOGY"
  181. :value="scope.row.technique"
  182. />
  183. </template>
  184. </el-table-column>
  185. <el-table-column
  186. label="井别"
  187. align="center"
  188. prop="wellCategory"
  189. :min-width="columnWidths.wellCategory.width"
  190. resizable
  191. />
  192. <el-table-column
  193. label="井深(m)"
  194. align="center"
  195. prop="designWellDepth"
  196. :min-width="columnWidths.designWellDepth.width"
  197. resizable
  198. />
  199. <el-table-column
  200. label="套生段产管尺寸(mm)"
  201. align="center"
  202. prop="casingPipeSize"
  203. :min-width="columnWidths.casingPipeSize.width"
  204. resizable
  205. />
  206. <el-table-column
  207. label="井控级别"
  208. align="center"
  209. prop="wellControlLevel"
  210. :min-width="columnWidths.wellControlLevel.width"
  211. resizable
  212. />
  213. <el-table-column align="center" label="当日">
  214. <el-table-column
  215. label="用电量(kWh)"
  216. align="center"
  217. prop="dailyPowerUsage"
  218. :min-width="columnWidths.dailyPowerUsage.width"
  219. resizable
  220. />
  221. <el-table-column
  222. label="油耗(吨)"
  223. align="center"
  224. prop="dailyFuel"
  225. :min-width="columnWidths.dailyFuel.width"
  226. resizable
  227. />
  228. </el-table-column>
  229. <el-table-column
  230. label="施工开始日期"
  231. align="center"
  232. prop="constructionStartDate"
  233. :formatter="dateFormatter"
  234. :min-width="columnWidths.constructionStartDate.width"
  235. resizable
  236. />
  237. <el-table-column
  238. label="施工结束日期"
  239. align="center"
  240. prop="constructionEndDate"
  241. :formatter="dateFormatter"
  242. :min-width="columnWidths.constructionEndDate.width"
  243. resizable
  244. />
  245. <el-table-column
  246. :label="t('project.currentOperation')"
  247. align="center"
  248. prop="currentOperation"
  249. :min-width="columnWidths.currentOperation.width"
  250. resizable
  251. />
  252. <el-table-column
  253. :label="t('project.nextPlan')"
  254. align="center"
  255. prop="nextPlan"
  256. :min-width="columnWidths.nextPlan.width"
  257. resizable
  258. />
  259. <el-table-column
  260. :label="t('project.transitTime')"
  261. align="center"
  262. prop="transitTime"
  263. :min-width="columnWidths.transitTime.width"
  264. resizable
  265. :formatter="percentageFormatter"
  266. />
  267. <el-table-column
  268. label="额定生产时间(H)"
  269. align="center"
  270. prop="ratedProductionTime"
  271. :min-width="columnWidths.ratedProductionTime.width"
  272. resizable
  273. />
  274. <el-table-column
  275. label="生产时间(H)"
  276. align="center"
  277. prop="productionTime"
  278. :min-width="columnWidths.productionTime.width"
  279. resizable
  280. />
  281. <el-table-column
  282. label="非生产时间(H)"
  283. align="center"
  284. prop="nonProductionTime"
  285. :min-width="columnWidths.nonProductionTime.width"
  286. resizable
  287. />
  288. <el-table-column
  289. :label="t('project.nptReason')"
  290. align="center"
  291. prop="ryNptReason"
  292. :min-width="columnWidths.ryNptReason.width"
  293. resizable
  294. >
  295. <template #default="scope">
  296. <dict-tag
  297. :type="DICT_TYPE.PMS_PROJECT_RY_NPT_REASON"
  298. :value="scope.row.ryNptReason"
  299. />
  300. </template>
  301. </el-table-column>
  302. <el-table-column
  303. label="生产动态"
  304. align="center"
  305. prop="productionStatus"
  306. :min-width="columnWidths.productionStatus.width"
  307. resizable
  308. />
  309. <el-table-column
  310. label="全员数量"
  311. align="center"
  312. prop="totalStaffNum"
  313. :min-width="columnWidths.totalStaffNum.width"
  314. resizable
  315. />
  316. <!--
  317. <el-table-column label="在岗人数" align="center" prop="onDutyStaffNum" :min-width="columnWidths.onDutyStaffNum"/> -->
  318. <el-table-column
  319. label="在岗人数"
  320. align="center"
  321. prop="onDutyStaffNum"
  322. :min-width="columnWidths.onDutyStaffNum.width"
  323. resizable
  324. >
  325. <template #default="scope">
  326. <!-- 动态计算:在岗人数 = 全员数量 - 休假人员数量 -->
  327. {{
  328. (Number(scope.row.totalStaffNum) || 0) - (Number(scope.row.leaveStaffNum) || 0)
  329. }}
  330. </template>
  331. </el-table-column>
  332. <el-table-column
  333. label="休假人员数量"
  334. align="center"
  335. prop="leaveStaffNum"
  336. :min-width="columnWidths.leaveStaffNum.width"
  337. resizable
  338. />
  339. <el-table-column label="操作" align="center" fixed="right">
  340. <template #default="scope">
  341. <el-button
  342. link
  343. type="primary"
  344. @click="openForm('update', scope.row.id, scope.row)"
  345. v-hasPermi="['pms:iot-ry-daily-report:update']"
  346. >
  347. 编辑
  348. </el-button>
  349. <el-button
  350. link
  351. type="danger"
  352. @click="handleDelete(scope.row.id)"
  353. v-hasPermi="['pms:iot-ry-daily-report:delete']"
  354. >
  355. 删除
  356. </el-button>
  357. </template>
  358. </el-table-column>
  359. </el-table>
  360. </div>
  361. <!-- 分页 -->
  362. <Pagination
  363. :total="total"
  364. v-model:page="queryParams.pageNo"
  365. v-model:limit="queryParams.pageSize"
  366. @pagination="getList"
  367. />
  368. </ContentWrap>
  369. <!-- 表单弹窗:添加/修改 -->
  370. <IotRyXjDailyReportForm ref="formRef" @success="getList" :row-data="selectedRowData" />
  371. </el-col>
  372. </el-row>
  373. </template>
  374. <script setup lang="ts">
  375. import { dateFormatter, dateFormatter2 } from '@/utils/formatTime'
  376. import download from '@/utils/download'
  377. import { IotRyDailyReportApi, IotRyDailyReportVO } from '@/api/pms/iotrydailyreport'
  378. import IotRyXjDailyReportForm from './IotRyXjDailyReportForm.vue'
  379. import { DICT_TYPE } from '@/utils/dict'
  380. import { ref, reactive, onMounted, nextTick, watch, onUnmounted } from 'vue'
  381. import DeptTree2 from '@/views/pms/iotrhdailyreport/DeptTree2.vue'
  382. import { useDebounceFn } from '@vueuse/core'
  383. import dayjs from 'dayjs'
  384. import quarterOfYear from 'dayjs/plugin/quarterOfYear'
  385. dayjs.extend(quarterOfYear)
  386. const rangeShortcuts = [
  387. {
  388. text: '今天',
  389. value: () => {
  390. const today = dayjs()
  391. return [today.startOf('day').toDate(), today.endOf('day').toDate()]
  392. }
  393. },
  394. {
  395. text: '昨天',
  396. value: () => {
  397. const yesterday = dayjs().subtract(1, 'day')
  398. return [yesterday.startOf('day').toDate(), yesterday.endOf('day').toDate()]
  399. }
  400. },
  401. {
  402. text: '本周',
  403. value: () => {
  404. return [dayjs().startOf('week').toDate(), dayjs().endOf('week').toDate()]
  405. }
  406. },
  407. {
  408. text: '上周',
  409. value: () => {
  410. const lastWeek = dayjs().subtract(1, 'week')
  411. return [lastWeek.startOf('week').toDate(), lastWeek.endOf('week').toDate()]
  412. }
  413. },
  414. {
  415. text: '本月',
  416. value: () => {
  417. return [dayjs().startOf('month').toDate(), dayjs().endOf('month').toDate()]
  418. }
  419. },
  420. {
  421. text: '上月',
  422. value: () => {
  423. const lastMonth = dayjs().subtract(1, 'month')
  424. return [lastMonth.startOf('month').toDate(), lastMonth.endOf('month').toDate()]
  425. }
  426. },
  427. {
  428. text: '本季度',
  429. value: () => {
  430. return [dayjs().startOf('quarter').toDate(), dayjs().endOf('quarter').toDate()]
  431. }
  432. },
  433. {
  434. text: '上季度',
  435. value: () => {
  436. const lastQuarter = dayjs().subtract(1, 'quarter')
  437. return [lastQuarter.startOf('quarter').toDate(), lastQuarter.endOf('quarter').toDate()]
  438. }
  439. },
  440. {
  441. text: '今年',
  442. value: () => {
  443. return [dayjs().startOf('year').toDate(), dayjs().endOf('year').toDate()]
  444. }
  445. },
  446. {
  447. text: '去年',
  448. value: () => {
  449. const lastYear = dayjs().subtract(1, 'year')
  450. return [lastYear.startOf('year').toDate(), lastYear.endOf('year').toDate()]
  451. }
  452. },
  453. {
  454. text: '最近7天',
  455. value: () => {
  456. return [dayjs().subtract(6, 'day').toDate(), dayjs().toDate()]
  457. }
  458. },
  459. {
  460. text: '最近30天',
  461. value: () => {
  462. return [dayjs().subtract(29, 'day').toDate(), dayjs().toDate()]
  463. }
  464. },
  465. {
  466. text: '最近90天',
  467. value: () => {
  468. return [dayjs().subtract(89, 'day').toDate(), dayjs().toDate()]
  469. }
  470. },
  471. {
  472. text: '最近一年',
  473. value: () => {
  474. return [dayjs().subtract(1, 'year').toDate(), dayjs().toDate()]
  475. }
  476. }
  477. ]
  478. /** 瑞鹰日报 列表 */
  479. defineOptions({ name: 'IotRyXjDailyReport' })
  480. const message = useMessage() // 消息弹窗
  481. const { t } = useI18n() // 国际化
  482. // 添加 selectedRowData 响应式变量
  483. const selectedRowData = ref<Record<string, any> | null>(null)
  484. const loading = ref(true) // 列表的加载中
  485. const list = ref<IotRyDailyReportVO[]>([]) // 列表的数据
  486. const total = ref(0) // 列表的总页数
  487. let queryParams = reactive({
  488. pageNo: 1,
  489. pageSize: 10,
  490. deptId: undefined,
  491. contractName: undefined,
  492. projectId: undefined,
  493. taskName: undefined,
  494. taskId: undefined,
  495. projectClassification: '2',
  496. relocationDays: undefined,
  497. latestWellDoneTime: [],
  498. currentDepth: undefined,
  499. dailyFootage: undefined,
  500. monthlyFootage: undefined,
  501. annualFootage: undefined,
  502. dailyPowerUsage: undefined,
  503. monthlyPowerUsage: undefined,
  504. dailyFuel: undefined,
  505. monthlyFuel: undefined,
  506. nonProductionTime: [],
  507. nptReason: undefined,
  508. constructionStartDate: [],
  509. constructionEndDate: [],
  510. productionStatus: undefined,
  511. nextPlan: undefined,
  512. rigStatus: undefined,
  513. personnel: undefined,
  514. mudDensity: undefined,
  515. mudViscosity: undefined,
  516. lateralLength: undefined,
  517. wellInclination: undefined,
  518. azimuth: undefined,
  519. extProperty: undefined,
  520. sort: undefined,
  521. remark: undefined,
  522. status: undefined,
  523. processInstanceId: undefined,
  524. auditStatus: undefined,
  525. createTime: []
  526. })
  527. const queryFormRef = ref() // 搜索的表单
  528. const exportLoading = ref(false) // 导出的加载中
  529. const rootDeptId = ref(158)
  530. // 表格引用
  531. const tableRef = ref()
  532. // 表格容器引用
  533. const tableContainerRef = ref()
  534. // 列宽度配置
  535. const columnWidths = ref<
  536. Record<
  537. string,
  538. {
  539. label: string
  540. prop: string
  541. width: string
  542. fn?: (row: IotRyDailyReportVO) => string | number
  543. }
  544. >
  545. >({
  546. createTime: {
  547. label: '日期',
  548. prop: 'createTime',
  549. width: '120px',
  550. fn: (row: IotRyDailyReportVO) => dateFormatter2(null, null, row.createTime)
  551. },
  552. deptName: {
  553. label: '施工队伍',
  554. prop: 'deptName',
  555. width: '120px'
  556. },
  557. contractName: {
  558. label: '项目',
  559. prop: 'contractName',
  560. width: '120px'
  561. },
  562. taskName: {
  563. label: '任务',
  564. prop: 'taskName',
  565. width: '120px'
  566. },
  567. equipmentType: {
  568. label: '设备型号',
  569. prop: 'equipmentType',
  570. width: '120px'
  571. },
  572. repairStatus: {
  573. label: '施工状态',
  574. prop: 'repairStatus',
  575. width: '120px'
  576. },
  577. totalConstructionWells: {
  578. label: '总施工井数',
  579. prop: 'totalConstructionWells',
  580. width: '120px'
  581. },
  582. completedWells: {
  583. label: '完工井数',
  584. prop: 'completedWells',
  585. width: '120px'
  586. },
  587. technique: {
  588. label: '施工工艺',
  589. prop: 'technique',
  590. width: '120px'
  591. },
  592. wellCategory: {
  593. label: '井别',
  594. prop: 'wellCategory',
  595. width: '120px'
  596. },
  597. designWellDepth: {
  598. label: '井深(m)',
  599. prop: 'designWellDepth',
  600. width: '120px'
  601. },
  602. casingPipeSize: {
  603. label: '套生段产管尺寸(mm)',
  604. prop: 'casingPipeSize',
  605. width: '120px'
  606. },
  607. wellControlLevel: {
  608. label: '井控级别',
  609. prop: 'wellControlLevel',
  610. width: '120px'
  611. },
  612. dailyPowerUsage: {
  613. label: '用电量(kWh)',
  614. prop: 'dailyPowerUsage',
  615. width: '120px'
  616. },
  617. dailyFuel: {
  618. label: '油耗(吨)',
  619. prop: 'dailyFuel',
  620. width: '120px'
  621. },
  622. constructionStartDate: {
  623. label: '施工开始日期',
  624. prop: 'constructionStartDate',
  625. width: '120px',
  626. fn: (row: IotRyDailyReportVO) => dateFormatter(null, null, row.constructionStartDate)
  627. },
  628. constructionEndDate: {
  629. label: '施工结束日期',
  630. prop: 'constructionEndDate',
  631. width: '120px',
  632. fn: (row: IotRyDailyReportVO) => dateFormatter(null, null, row.constructionEndDate)
  633. },
  634. currentOperation: {
  635. label: '目前',
  636. prop: 'currentOperation',
  637. width: '120px'
  638. },
  639. nextPlan: {
  640. label: '下步',
  641. prop: 'nextPlan',
  642. width: '120px'
  643. },
  644. transitTime: {
  645. label: '运行时效',
  646. prop: 'transitTime',
  647. width: '120px',
  648. fn: (row: IotRyDailyReportVO) => percentageFormatter(null, null, row.transitTime, null)
  649. },
  650. ratedProductionTime: {
  651. label: '额定生产时间(H)',
  652. prop: 'ratedProductionTime',
  653. width: '120px'
  654. },
  655. productionTime: {
  656. label: '生产时间(H)',
  657. prop: 'productionTime',
  658. width: '120px'
  659. },
  660. nonProductionTime: {
  661. label: '非生产时间(H)',
  662. prop: 'nonProductionTime',
  663. width: '120px'
  664. },
  665. ryNptReason: {
  666. label: '非生产时间原因',
  667. prop: 'ryNptReason',
  668. width: '120px'
  669. },
  670. drillingWorkingTime: {
  671. label: '进尺工作时间(H)',
  672. prop: 'drillingWorkingTime',
  673. width: '120px'
  674. },
  675. otherProductionTime: {
  676. label: '其它生产时间(H)',
  677. prop: 'otherProductionTime',
  678. width: '120px'
  679. },
  680. productionStatus: {
  681. label: '生产动态',
  682. prop: 'productionStatus',
  683. width: '120px'
  684. },
  685. totalStaffNum: {
  686. label: '全员数量',
  687. prop: 'totalStaffNum',
  688. width: '120px'
  689. },
  690. onDutyStaffNum: {
  691. label: '在岗人数',
  692. prop: 'onDutyStaffNum',
  693. width: '120px',
  694. fn: (row: IotRyDailyReportVO) =>
  695. (Number(row.totalStaffNum) || 0) - (Number(row.offDutyStaffNum) || 0)
  696. },
  697. leaveStaffNum: {
  698. label: '休假人员数量',
  699. prop: 'leaveStaffNum',
  700. width: '120px'
  701. }
  702. })
  703. // 计算文本宽度
  704. const getTextWidth = (text: string, fontSize = 12) => {
  705. const span = document.createElement('span')
  706. span.style.visibility = 'hidden'
  707. span.style.position = 'absolute'
  708. span.style.whiteSpace = 'nowrap'
  709. span.style.fontSize = `${fontSize}px`
  710. span.style.fontFamily = 'PingFang SC'
  711. span.innerText = text
  712. document.body.appendChild(span)
  713. const width = span.offsetWidth
  714. document.body.removeChild(span)
  715. return width
  716. }
  717. const calculateColumnWidths = useDebounceFn(() => {
  718. if (!tableContainerRef.value?.$el) return
  719. Object.values(columnWidths.value).forEach(({ fn, prop, label, width }) => {
  720. width =
  721. Math.min(
  722. ...[
  723. Math.max(
  724. ...[
  725. getTextWidth(label),
  726. ...list.value.map((v) => {
  727. return getTextWidth(fn ? fn(v) : v[prop])
  728. })
  729. ]
  730. ) +
  731. (label === '施工状态' || label === '施工工艺' || label === '非生产时间原因' ? 35 : 20),
  732. 200
  733. ]
  734. ) + 'px'
  735. columnWidths.value[prop].width = width
  736. })
  737. }, 1000)
  738. // 检查10个时间字段之和是否为24H
  739. const checkTimeSumEquals24 = (row: any) => {
  740. // 获取三个字段的值,转换为数字,如果为空则视为0
  741. const drillingWorkingTime = parseFloat(row.drillingWorkingTime) || 0
  742. const otherProductionTime = parseFloat(row.otherProductionTime) || 0
  743. const accidentTime = parseFloat(row.accidentTime) || 0
  744. const repairTime = parseFloat(row.repairTime) || 0
  745. const selfStopTime = parseFloat(row.selfStopTime) || 0
  746. const complexityTime = parseFloat(row.complexityTime) || 0
  747. const relocationTime = parseFloat(row.relocationTime) || 0
  748. const rectificationTime = parseFloat(row.rectificationTime) || 0
  749. const waitingStopTime = parseFloat(row.waitingStopTime) || 0
  750. const winterBreakTime = parseFloat(row.winterBreakTime) || 0
  751. // 计算总和
  752. const sum =
  753. drillingWorkingTime +
  754. otherProductionTime +
  755. accidentTime +
  756. repairTime +
  757. selfStopTime +
  758. complexityTime +
  759. relocationTime +
  760. rectificationTime +
  761. waitingStopTime +
  762. winterBreakTime
  763. // 返回是否等于24(允许一定的浮点数误差)
  764. return Math.abs(sum - 24) < 0.01 // 使用0.01作为误差范围
  765. }
  766. // 单元格样式函数
  767. const cellStyle = ({
  768. row,
  769. column,
  770. rowIndex,
  771. columnIndex
  772. }: {
  773. row: any
  774. column: any
  775. rowIndex: number
  776. columnIndex: number
  777. }) => {
  778. // 当日油耗预警逻辑
  779. if (column.property === 'dailyFuel') {
  780. const dailyFuel = parseFloat(row.dailyFuel) || 0
  781. if (dailyFuel > 5) {
  782. return {
  783. backgroundColor: '#e6f8ff', // 浅黄色背景
  784. color: '#0a35c4', // 橙色文字
  785. fontWeight: 'bold',
  786. border: '1px solid #ffd591' // 可选:添加边框突出显示
  787. }
  788. }
  789. }
  790. // 1. 检查三个时间字段:额定生产时间、生产时间、非生产时间
  791. const timeFields = ['ratedProductionTime', 'productionTime', 'nonProductionTime']
  792. if (timeFields.includes(column.property)) {
  793. const ratedTime = parseFloat(row.ratedProductionTime) || 0
  794. const prodTime = parseFloat(row.productionTime) || 0
  795. const nonProdTime = parseFloat(row.nonProductionTime) || 0
  796. // 新增:检查三个字段是否有空值
  797. const hasEmptyField =
  798. row.ratedProductionTime === null ||
  799. row.ratedProductionTime === undefined ||
  800. row.ratedProductionTime === '' ||
  801. row.productionTime === null ||
  802. row.productionTime === undefined ||
  803. row.productionTime === '' ||
  804. row.nonProductionTime === null ||
  805. row.nonProductionTime === undefined ||
  806. row.nonProductionTime === ''
  807. // 如果有空字段,应用警告样式
  808. if (hasEmptyField) {
  809. return {
  810. backgroundColor: '#fff2f0', // 浅红色背景,表示数据不完整
  811. color: '#d46b08', // 深红色文字
  812. fontWeight: 'bold',
  813. border: '1px solid #ffa39e' // 可选:添加边框突出显示
  814. }
  815. }
  816. // 如果三个字段都有值,且不满足公式:额定生产时间 = 生产时间 + 非生产时间
  817. // 使用容差比较,避免浮点数精度问题
  818. if (Math.abs(ratedTime - (prodTime + nonProdTime)) > 0.01) {
  819. return {
  820. backgroundColor: '#fffbe6', // 浅黄色背景
  821. color: '#d46b08', // 橙色文字
  822. fontWeight: 'bold'
  823. }
  824. }
  825. }
  826. // 2. 处理运行时效字段的颜色
  827. if (column.property === 'transitTime') {
  828. const transitTime = row.transitTime
  829. // 将运行时效转为数字(处理原逻辑中 toFixed(4) 生成的字符串)
  830. const transitTimeNum = parseFloat(transitTime)
  831. if (
  832. transitTime === null ||
  833. transitTime === undefined ||
  834. isNaN(transitTimeNum) ||
  835. transitTimeNum === 0 ||
  836. transitTimeNum >= 1
  837. ) {
  838. return {
  839. backgroundColor: '#fff2f0', // 浅红色背景(与数据不完整警告样式统一)
  840. color: '#ff4d4f', // 红色文字
  841. fontWeight: 'bold'
  842. }
  843. }
  844. }
  845. // 3. 处理“在岗人数”列:动态计算 + 负数黄色背景
  846. if (column.label === '在岗人数') {
  847. // 按列名匹配(避免prop绑定问题)
  848. // 步骤1:计算在岗人数(处理空值/非数字情况,默认设为0)
  849. const totalStaff = Number(row.totalStaffNum) || 0
  850. const leaveStaff = Number(row.leaveStaffNum) || 0
  851. const onDutyStaff = totalStaff - leaveStaff
  852. // 步骤2:若计算值为负数,设置黄色提醒样式
  853. if (onDutyStaff < 0) {
  854. return {
  855. backgroundColor: '#fff9e6', // 浅黄色背景
  856. fontWeight: 'bold'
  857. }
  858. }
  859. }
  860. // 默认返回空对象,不应用特殊样式
  861. return {}
  862. }
  863. /** 查询列表 */
  864. const getList = async () => {
  865. loading.value = true
  866. try {
  867. const data = await IotRyDailyReportApi.getIotRyDailyReportPage(queryParams)
  868. // 计算运行时效
  869. data.list.forEach((item: any) => {
  870. const ratedTime = parseFloat(item.ratedProductionTime) || 0
  871. const productionTime = parseFloat(item.productionTime) || 0
  872. if (ratedTime > 0 && !isNaN(productionTime)) {
  873. // 计算运行时效并保留4位小数用于计算
  874. item.transitTime = (productionTime / ratedTime).toFixed(4)
  875. } else {
  876. item.transitTime = null
  877. }
  878. })
  879. list.value = data.list
  880. total.value = data.total
  881. // 获取数据后计算列宽
  882. nextTick(() => {
  883. calculateColumnWidths()
  884. })
  885. } finally {
  886. loading.value = false
  887. }
  888. }
  889. // 百分比格式化函数
  890. const percentageFormatter = (row: any, column: any, cellValue: any, index: number | null) => {
  891. if (cellValue === null || cellValue === undefined || cellValue === '') return ''
  892. const value = parseFloat(cellValue)
  893. if (isNaN(value)) return '-'
  894. // 将小数转换为百分比,保留两位小数
  895. return `${(value * 100).toFixed(2)}%`
  896. }
  897. /** 搜索按钮操作 */
  898. const handleQuery = () => {
  899. queryParams.pageNo = 1
  900. getList()
  901. }
  902. /** 重置按钮操作 */
  903. const resetQuery = () => {
  904. queryFormRef.value.resetFields()
  905. handleQuery()
  906. }
  907. /** 添加/修改操作 */
  908. const formRef = ref()
  909. const openForm = (type: string, id?: number, row?: any) => {
  910. // 保存当前行数据
  911. if (row) {
  912. selectedRowData.value = {
  913. deptName: row.deptName,
  914. contractName: row.contractName,
  915. taskName: row.taskName,
  916. designWellDepth: row.designWellDepth,
  917. technique: row.technique,
  918. wellCategory: row.wellCategory,
  919. wellControlLevel: row.wellControlLevel,
  920. casingPipeSize: row.casingPipeSize
  921. }
  922. } else {
  923. selectedRowData.value = null
  924. }
  925. formRef.value.open(type, id)
  926. }
  927. /** 删除按钮操作 */
  928. const handleDelete = async (id: number) => {
  929. try {
  930. // 删除的二次确认
  931. await message.delConfirm()
  932. // 发起删除
  933. await IotRyDailyReportApi.deleteIotRyDailyReport(id)
  934. message.success(t('common.delSuccess'))
  935. // 刷新列表
  936. await getList()
  937. } catch {}
  938. }
  939. // 响应式变量存储选中的部门
  940. const selectedDept = ref<{ id: number; name: string }>()
  941. /** 处理部门被点击 */
  942. const handleDeptNodeClick = async (row) => {
  943. // 记录选中的部门信息
  944. selectedDept.value = { id: row.id, name: row.name }
  945. queryParams.deptId = row.id
  946. await getList()
  947. }
  948. /** 导出按钮操作 */
  949. const handleExport = async () => {
  950. try {
  951. // 导出的二次确认
  952. await message.exportConfirm()
  953. // 发起导出
  954. exportLoading.value = true
  955. const data = await IotRyDailyReportApi.exportIotRyDailyReport(queryParams)
  956. download.excel(data, '瑞鹰日报.xls')
  957. } catch {
  958. } finally {
  959. exportLoading.value = false
  960. }
  961. }
  962. // 声明 ResizeObserver 实例
  963. let resizeObserver: ResizeObserver | null = null
  964. const route = useRoute()
  965. /** 初始化 **/
  966. onMounted(() => {
  967. if (Object.keys(route.query).length > 0) {
  968. queryParams = {
  969. ...queryParams,
  970. ...route.query,
  971. deptId: Number(route.query.deptId) as any
  972. }
  973. handleQuery()
  974. } else getList()
  975. // 创建 ResizeObserver 监听表格容器尺寸变化
  976. if (tableContainerRef.value?.$el) {
  977. resizeObserver = new ResizeObserver(() => {
  978. // 使用防抖避免频繁触发
  979. clearTimeout((window as any).resizeTimer)
  980. ;(window as any).resizeTimer = setTimeout(() => {
  981. calculateColumnWidths()
  982. }, 100)
  983. })
  984. resizeObserver.observe(tableContainerRef.value.$el)
  985. }
  986. })
  987. onUnmounted(() => {
  988. // 清除 ResizeObserver
  989. if (resizeObserver && tableContainerRef.value?.$el) {
  990. resizeObserver.unobserve(tableContainerRef.value.$el)
  991. resizeObserver = null
  992. }
  993. // 清除定时器
  994. if ((window as any).resizeTimer) {
  995. clearTimeout((window as any).resizeTimer)
  996. }
  997. })
  998. // 监听列表数据变化重新计算列宽
  999. watch(
  1000. list,
  1001. () => {
  1002. nextTick(calculateColumnWidths)
  1003. },
  1004. { deep: true }
  1005. )
  1006. </script>
  1007. <style scoped>
  1008. /* 表格容器样式,确保水平滚动 */
  1009. .table-container {
  1010. width: 100%;
  1011. overflow-x: auto;
  1012. }
  1013. /* 确保表格单元格内容不换行 */
  1014. /* :deep(.el-table .cell) {
  1015. white-space: nowrap;
  1016. } */
  1017. /* 确保表格列标题不换行 */
  1018. /* :deep(.el-table th > .cell) {
  1019. white-space: nowrap;
  1020. } */
  1021. /* 调整表格最小宽度,确保内容完全显示 */
  1022. :deep(.el-table) {
  1023. min-width: 100%;
  1024. }
  1025. /* 强制显示所有内容,防止省略号 */
  1026. /* :deep(.el-table td.el-table__cell),
  1027. :deep(.el-table th.el-table__cell) {
  1028. overflow: visible !important;
  1029. } */
  1030. /* :deep(.el-table .cell) {
  1031. overflow: visible !important;
  1032. text-overflow: clip !important;
  1033. } */
  1034. /* 设计井身结构文本样式 - 多行显示并添加省略号 */
  1035. .design-well-struct-text {
  1036. display: -webkit-box;
  1037. max-height: 3em; /* 两行文本的高度 */
  1038. overflow: hidden;
  1039. line-height: 1.5;
  1040. text-overflow: ellipsis;
  1041. -webkit-box-orient: vertical;
  1042. -webkit-line-clamp: 2;
  1043. }
  1044. /* 确保设计井身结构列不参与自动调整 */
  1045. :deep(.el-table__header-wrapper .el-table__cell.fixed-width),
  1046. :deep(.el-table__body-wrapper .el-table__cell.fixed-width) {
  1047. flex-shrink: 0;
  1048. flex-grow: 0;
  1049. }
  1050. /* 颜色说明区域样式 */
  1051. .color-legend {
  1052. display: flex;
  1053. padding: 12px 16px;
  1054. background-color: #f8f9fa;
  1055. border-left: 4px solid #e6f7ff;
  1056. border-radius: 4px;
  1057. flex-direction: column;
  1058. gap: 8px;
  1059. }
  1060. .legend-item {
  1061. display: flex;
  1062. align-items: center;
  1063. gap: 8px;
  1064. font-size: 14px;
  1065. }
  1066. .color-indicator {
  1067. display: inline-block;
  1068. width: 12px;
  1069. height: 12px;
  1070. border-radius: 50%;
  1071. }
  1072. .color-indicator.red {
  1073. background-color: red;
  1074. }
  1075. .color-indicator.orange {
  1076. background-color: orange;
  1077. }
  1078. /* 在原有样式基础上添加黄色指示器 */
  1079. .color-indicator.yellow {
  1080. background-color: #0a35c4; /* 黄色,与警告颜色一致 */
  1081. }
  1082. </style>
  1083. <style>
  1084. /* 设计井身结构 tooltip 样式 - 保留换行符 */
  1085. .design-well-struct-tooltip {
  1086. max-width: 500px;
  1087. line-height: 1.5;
  1088. white-space: pre-line;
  1089. }
  1090. </style>