FillDailyReportForm.vue 100 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271
  1. <template>
  2. <ContentWrap v-loading="formLoading">
  3. <!-- 第一部分:日报标题 -->
  4. <div class="daily-report-title">
  5. <h2>{{ pageTitle }}</h2>
  6. <!-- 在审批模式下显示审批状态提示 -->
  7. <div v-if="isReadonlyMode" class="approval-notice">
  8. <el-alert :title="modeNotice" type="info" :closable="false" />
  9. </div>
  10. </div>
  11. <!-- 第二部分:项目/任务信息 -->
  12. <ContentWrap>
  13. <div class="info-table" style="margin-top: 1em">
  14. <!-- 表格行 -->
  15. <div class="table-row">
  16. <div class="table-cell">
  17. <div class="cell-content">
  18. <span class="cell-label">甲方:</span>
  19. <!-- 甲方字段:添加 single-line-ellipsis 类 + title 绑定完整内容 -->
  20. <span
  21. class="cell-value single-line-ellipsis"
  22. :title="dailyReportData.manufactureName || '-'"
  23. >
  24. {{ dailyReportData.manufactureName || '-' }}
  25. </span>
  26. </div>
  27. </div>
  28. <div class="table-cell">
  29. <div class="cell-content">
  30. <span class="cell-label">合同号:</span>
  31. <span class="cell-value">{{ dailyReportData.contractName || '-' }}</span>
  32. </div>
  33. </div>
  34. <div class="table-cell">
  35. <div class="cell-content">
  36. <span class="cell-label">井号:</span>
  37. <span class="cell-value">{{
  38. displayWellName || dailyReportData.taskName || '-'
  39. }}</span>
  40. </div>
  41. </div>
  42. </div>
  43. <div class="table-row">
  44. <div class="table-cell">
  45. <div class="cell-content">
  46. <span class="cell-label">施工队伍:</span>
  47. <span class="cell-value">{{ dailyReportData.deptName || '-' }}</span>
  48. </div>
  49. </div>
  50. <div class="table-cell">
  51. <div class="cell-content">
  52. <span class="cell-label">施工地点:</span>
  53. <span class="cell-value">{{ dailyReportData.location || '-' }}</span>
  54. </div>
  55. </div>
  56. <div class="table-cell">
  57. <div class="cell-content">
  58. <span class="cell-label">工艺:</span>
  59. <span class="cell-value">{{ dailyReportData.techniqueNames || '-' }}</span>
  60. </div>
  61. </div>
  62. </div>
  63. <div class="table-row">
  64. <div class="table-cell">
  65. <div class="cell-content">
  66. <span class="cell-label">设计工作量:</span>
  67. <span class="cell-value">{{ dailyReportData.workloadDesign || '-' }}</span>
  68. </div>
  69. </div>
  70. <div class="table-cell">
  71. <div class="cell-content">
  72. <span class="cell-label">开工日期:</span>
  73. <span class="cell-value">{{ dailyReportData.commencementDate || '-' }}</span>
  74. </div>
  75. </div>
  76. <div class="table-cell">
  77. <div class="cell-content">
  78. <span class="cell-label">完工日期:</span>
  79. <span class="cell-value">{{ dailyReportData.completionDate || '-' }}</span>
  80. </div>
  81. </div>
  82. </div>
  83. <div class="table-row">
  84. <div class="table-cell">
  85. <div class="cell-content">
  86. <span class="cell-label">施工周期D:</span>
  87. <span class="cell-value">{{ dailyReportData.constructionPeriod || '' }}</span>
  88. </div>
  89. </div>
  90. <div class="table-cell">
  91. <div class="cell-content">
  92. <span class="cell-label">停待时间D:</span>
  93. <span class="cell-value">{{ dailyReportData.idleTime || '' }}</span>
  94. </div>
  95. </div>
  96. <div class="table-cell">
  97. <div class="cell-content">
  98. <span class="cell-label">带班干部:</span>
  99. <span class="cell-value">{{ dailyReportData.responsiblePersonNames || '-' }}</span>
  100. </div>
  101. </div>
  102. </div>
  103. <!-- 第五行:设备配置(单独一行) -->
  104. <div class="table-row">
  105. <div class="table-cell full-width">
  106. <div class="cell-content">
  107. <span class="cell-label">设备配置:</span>
  108. <span class="cell-value indent-multiline">{{
  109. dailyReportData.deviceNames || '-'
  110. }}</span>
  111. </div>
  112. </div>
  113. </div>
  114. </div>
  115. </ContentWrap>
  116. <!-- 实际进度显示区域(新增) -->
  117. <ContentWrap class="section-padding" v-if="showActualProgress">
  118. <h3 class="progress-title">任务进度</h3>
  119. <div class="actual-progress-container">
  120. <div v-if="actualProgressData.length > 0">
  121. <el-steps
  122. direction="horizontal"
  123. :active="actualProgressData.length - 1"
  124. finish-status="success"
  125. >
  126. <el-step
  127. v-for="(step, index) in actualProgressData"
  128. :key="index"
  129. :title="step.title"
  130. :description="step.description"
  131. :status="step.status"
  132. />
  133. </el-steps>
  134. </div>
  135. <div v-else class="no-progress-data"> 暂无实际进度数据 </div>
  136. </div>
  137. </ContentWrap>
  138. <!-- 第三部分:日报填报表单 -->
  139. <ContentWrap class="section-padding">
  140. <el-form
  141. ref="formRef"
  142. :model="formData"
  143. :rules="isReadonlyMode ? {} : formRules"
  144. v-loading="formLoading"
  145. style="margin-top: 1em"
  146. label-width="200px"
  147. :disabled="isReadonlyMode"
  148. >
  149. <!-- 第一行:时间节点、施工状态 -->
  150. <el-row :gutter="30">
  151. <el-col :span="12">
  152. <el-form-item label="时间节点" prop="timeRange">
  153. <el-time-picker
  154. is-range
  155. v-model="formData.timeRange"
  156. range-separator="至"
  157. start-placeholder="开始时间"
  158. end-placeholder="结束时间"
  159. placeholder="选择时间范围"
  160. style="width: 100%"
  161. :readonly="isReadonlyMode"
  162. :disabled="isReadonlyMode"
  163. />
  164. </el-form-item>
  165. </el-col>
  166. <el-col :span="12">
  167. <el-form-item label="施工状态" prop="rdStatus">
  168. <el-select
  169. v-model="formData.rdStatus"
  170. placeholder="请选择施工状态"
  171. style="width: 100%"
  172. :disabled="isReadonlyMode"
  173. >
  174. <el-option
  175. v-for="dict in rdStatusOptions"
  176. :key="dict.value"
  177. :label="dict.label"
  178. :value="dict.value"
  179. />
  180. </el-select>
  181. </el-form-item>
  182. </el-col>
  183. </el-row>
  184. <!-- 平台井 -->
  185. <el-row v-if="showPlatformField">
  186. <el-col :span="24">
  187. <el-form-item label="平台井" prop="platformId">
  188. <el-select
  189. v-model="formData.platformId"
  190. placeholder="请选择平台井"
  191. style="width: 100%"
  192. :disabled="isReadonlyMode && query.istime !== 'true'"
  193. >
  194. <el-option
  195. v-for="platform in platformOptions"
  196. :key="platform.id"
  197. :label="platform.wellName"
  198. :value="platform.id"
  199. />
  200. </el-select>
  201. </el-form-item>
  202. </el-col>
  203. </el-row>
  204. <!-- 施工设备字段 -->
  205. <el-row>
  206. <el-col :span="24">
  207. <el-form-item label="施工设备" prop="deviceIds">
  208. <!-- 编辑模式:显示选择按钮 -->
  209. <template v-if="isEditMode">
  210. <el-button
  211. @click="openDeviceDialog"
  212. type="primary"
  213. size="small"
  214. :disabled="formLoading || isReadonlyMode"
  215. >
  216. 选择设备
  217. </el-button>
  218. <el-tooltip
  219. v-if="formData.deviceIds && formData.deviceIds.length > 0"
  220. :content="getAllDeviceNamesForDisplay"
  221. placement="top"
  222. >
  223. <span class="device-display-container">
  224. {{ formatDevicesForDisplay }}
  225. </span>
  226. </el-tooltip>
  227. <span v-else class="no-device"> 未选择设备 </span>
  228. </template>
  229. <!-- 只读模式:只显示设备信息 -->
  230. <template v-else>
  231. <el-tooltip
  232. v-if="formData.deviceIds && formData.deviceIds.length > 0"
  233. :content="getAllDeviceNamesForDisplay"
  234. placement="top"
  235. >
  236. <span class="device-display-container">
  237. {{ formatDevicesForDisplay }}
  238. </span>
  239. </el-tooltip>
  240. <span v-else class="no-device">-</span>
  241. </template>
  242. </el-form-item>
  243. </el-col>
  244. </el-row>
  245. <!-- 未施工设备 -->
  246. <el-row>
  247. <el-col :span="24">
  248. <el-form-item label="未施工设备" prop="unSelectedDeviceNames">
  249. <el-input
  250. v-model="unSelectedDeviceNames"
  251. type="textarea"
  252. :rows="2"
  253. placeholder="未施工的设备将显示在这里"
  254. :readonly="true"
  255. class="unselected-device"
  256. />
  257. </el-form-item>
  258. </el-col>
  259. </el-row>
  260. <!-- 第二行:施工工艺 -->
  261. <el-row>
  262. <el-col :span="24">
  263. <el-form-item label="施工工艺" prop="techniqueIds">
  264. <el-select
  265. v-model="formData.techniqueIds"
  266. placeholder="请选择施工工艺"
  267. style="width: 100%"
  268. multiple
  269. :disabled="isReadonlyMode"
  270. >
  271. <el-option
  272. v-for="dict in techniqueOptions"
  273. :key="dict.value"
  274. :label="dict.label"
  275. :value="dict.value"
  276. />
  277. </el-select>
  278. </el-form-item>
  279. </el-col>
  280. </el-row>
  281. <!-- 动态属性区域:施工工艺与当日生产动态之间 -->
  282. <el-row v-if="dynamicAttrs.length > 0" :gutter="30">
  283. <el-col
  284. v-for="attr in dynamicAttrs"
  285. :key="attr.id"
  286. :span="attr.dataType === 'textarea' ? 24 : 12"
  287. >
  288. <el-form-item
  289. :label="attr.name + (attr.unit ? `(${attr.unit})` : '')"
  290. :prop="'dynamicFields.' + attr.identifier"
  291. :rules="isReadonlyMode ? [] : getDynamicAttrRules(attr)"
  292. >
  293. <!-- 文本类型 -->
  294. <el-input
  295. v-if="attr.dataType === 'text'"
  296. v-model="formData.dynamicFields[attr.identifier]"
  297. :placeholder="`请输入${attr.name}`"
  298. :readonly="isReadonlyMode"
  299. />
  300. <!-- 文本域类型 -->
  301. <el-input
  302. v-else-if="attr.dataType === 'textarea'"
  303. v-model="formData.dynamicFields[attr.identifier]"
  304. :placeholder="`请输入${attr.name}`"
  305. type="textarea"
  306. :rows="3"
  307. :readonly="isReadonlyMode"
  308. />
  309. <!-- 数字类型 -->
  310. <el-input
  311. v-else-if="attr.dataType === 'double'"
  312. v-model="formData.dynamicFields[attr.identifier]"
  313. :placeholder="`请输入${attr.name}`"
  314. type="number"
  315. :min="attr.minValue || undefined"
  316. :max="attr.maxValue || undefined"
  317. :readonly="isReadonlyMode"
  318. />
  319. <!-- 日期类型 -->
  320. <el-date-picker
  321. v-else-if="attr.dataType === 'date'"
  322. v-model="formData.dynamicFields[attr.identifier]"
  323. type="date"
  324. value-format="x"
  325. :placeholder="`选择${attr.name}`"
  326. style="width: 100%"
  327. :readonly="isReadonlyMode"
  328. />
  329. <!-- 默认文本输入 -->
  330. <el-input
  331. v-else
  332. v-model="formData.dynamicFields[attr.identifier]"
  333. :placeholder="`请输入${attr.name}`"
  334. :readonly="isReadonlyMode"
  335. />
  336. </el-form-item>
  337. </el-col>
  338. </el-row>
  339. <el-row>
  340. <el-col :span="24">
  341. <el-form-item label="当日油耗(L)" prop="dailyFuel">
  342. <el-input
  343. v-model="formData.dailyFuel"
  344. type="text"
  345. :min="0"
  346. placeholder="自动计算当日油耗"
  347. :readonly="isReadonlyMode"
  348. @blur="formatDailyFuel"
  349. />
  350. </el-form-item>
  351. </el-col>
  352. </el-row>
  353. <!-- 第三行:当日生产动态 -->
  354. <el-row>
  355. <el-col :span="24">
  356. <el-form-item label="当日生产动态" prop="productionStatus">
  357. <el-input
  358. v-model="formData.productionStatus"
  359. type="textarea"
  360. :rows="3"
  361. placeholder="请输入当日生产动态"
  362. :readonly="isReadonlyMode"
  363. />
  364. </el-form-item>
  365. </el-col>
  366. </el-row>
  367. <!-- 第四行:下步工作计划 -->
  368. <el-row>
  369. <el-col :span="24">
  370. <el-form-item label="下步工作计划" prop="nextPlan">
  371. <el-input
  372. v-model="formData.nextPlan"
  373. type="textarea"
  374. :rows="3"
  375. placeholder="请输入下步工作计划"
  376. :readonly="isReadonlyMode"
  377. />
  378. </el-form-item>
  379. </el-col>
  380. </el-row>
  381. <!-- 第五行:外租设备 -->
  382. <el-row>
  383. <el-col :span="24">
  384. <el-form-item label="外租设备" prop="externalRental">
  385. <el-input
  386. v-model="formData.externalRental"
  387. type="textarea"
  388. :rows="3"
  389. placeholder="请输入外租设备信息"
  390. :readonly="isReadonlyMode"
  391. />
  392. </el-form-item>
  393. </el-col>
  394. </el-row>
  395. <!-- 故障情况 -->
  396. <el-row>
  397. <el-col :span="24">
  398. <el-form-item label="故障情况" prop="malfunction">
  399. <el-input
  400. v-model="formData.malfunction"
  401. type="textarea"
  402. :rows="3"
  403. placeholder="请输入故障情况"
  404. :readonly="isReadonlyMode"
  405. />
  406. </el-form-item>
  407. </el-col>
  408. </el-row>
  409. <!-- 故障误工H -->
  410. <el-row>
  411. <el-col :span="24">
  412. <el-form-item label="故障误工H" prop="faultDowntime">
  413. <el-input
  414. v-model="formData.faultDowntime"
  415. type="number"
  416. :rows="3"
  417. placeholder="请输入故障误工H"
  418. :readonly="isReadonlyMode"
  419. />
  420. </el-form-item>
  421. </el-col>
  422. </el-row>
  423. <div class="grid grid-cols-3 gao-4">
  424. <el-form-item
  425. v-for="field in NON_PROD_FIELDS"
  426. :key="field.key"
  427. :label="field.label + '(H)'"
  428. :prop="field.key"
  429. >
  430. <el-input-number
  431. class="!w-full"
  432. :min="0"
  433. :max="24"
  434. v-model="formData[field.key]"
  435. :controls="false"
  436. align="left"
  437. :disabled="query.istime !== 'true'"
  438. />
  439. </el-form-item>
  440. </div>
  441. <el-row>
  442. <el-col :span="24">
  443. <el-form-item label="其他非生产原因" prop="otherNptReason">
  444. <el-input
  445. v-model="formData.otherNptReason"
  446. placeholder="请输入其他非生产原因"
  447. :disabled="query.istime !== 'true'"
  448. />
  449. </el-form-item>
  450. </el-col>
  451. </el-row>
  452. <!-- 第六行:上传附件 -->
  453. <el-row>
  454. <el-col :span="24">
  455. <el-form-item label="附件">
  456. <!-- 文件上传组件 -->
  457. <FileUpload
  458. v-if="!isReadonlyMode"
  459. ref="fileUploadRef"
  460. :device-id="deviceId"
  461. :show-folder-button="false"
  462. @upload-success="handleUploadSuccess"
  463. />
  464. <!-- 已上传附件列表显示 -->
  465. <div
  466. v-if="formData.attachments && formData.attachments.length > 0"
  467. class="attachment-container"
  468. >
  469. <div class="attachment-list">
  470. <div
  471. v-for="(attachment, index) in formData.attachments"
  472. :key="attachment.id || index"
  473. class="attachment-item"
  474. >
  475. <!-- 为附件名称添加点击事件,传递整个附件对象 -->
  476. <a class="attachment-name" @click="inContent(attachment)">
  477. {{ attachment.filename }}
  478. </a>
  479. <el-button
  480. v-if="!isReadonlyMode"
  481. type="danger"
  482. link
  483. size="small"
  484. @click="removeAttachment(index)"
  485. >
  486. 删除
  487. </el-button>
  488. </div>
  489. </div>
  490. </div>
  491. <!-- 审批模式下只显示附件列表 -->
  492. <div
  493. v-else-if="
  494. isApprovalMode && (!formData.attachments || formData.attachments.length === 0)
  495. "
  496. class="no-attachment"
  497. >
  498. 无附件
  499. </div>
  500. </el-form-item>
  501. </el-col>
  502. </el-row>
  503. </el-form>
  504. </ContentWrap>
  505. <!-- 油耗信息区域 - 当有油耗数据时显示 -->
  506. <ContentWrap class="fuel-consumption-section" v-if="showFuelConsumption">
  507. <h2 class="text-lg font-semibold mb-4">油耗信息</h2>
  508. <div class="fuel-consumption-table">
  509. <el-table
  510. :data="fuelConsumptionData"
  511. border
  512. style="width: 100%"
  513. class="fuel-consumption-el-table"
  514. table-layout="fixed"
  515. :key="fuelTableKey"
  516. row-key="deviceId"
  517. >
  518. <!-- 车辆编码 -->
  519. <el-table-column
  520. prop="deviceCode"
  521. label="车辆编码"
  522. align="center"
  523. :show-overflow-tooltip="false"
  524. width="120"
  525. />
  526. <!-- 车辆名称 -->
  527. <el-table-column
  528. prop="deviceName"
  529. label="车辆名称"
  530. align="center"
  531. :show-overflow-tooltip="false"
  532. width="150"
  533. />
  534. <!-- 发生日期 -->
  535. <el-table-column
  536. label="发生日期"
  537. align="center"
  538. :show-overflow-tooltip="false"
  539. width="120"
  540. >
  541. <template #default="scope">
  542. {{ formatDate(scope.row.queryDate) }}
  543. </template>
  544. </el-table-column>
  545. <!-- 中航北斗油耗 -->
  546. <el-table-column label="中航北斗油耗(L)" align="center" width="140">
  547. <template #default="scope">
  548. {{ formatNumber(scope.row.zhbdFuel, 2) }}
  549. </template>
  550. </el-table-column>
  551. <!-- 实际油耗 -->
  552. <el-table-column label="实际油耗(L)" align="center" width="140">
  553. <template #default="scope">
  554. <!-- 编辑模式下显示输入框 -->
  555. <el-input
  556. v-if="!isReadonlyMode"
  557. v-model="scope.row.customFuel"
  558. type="text"
  559. :min="0"
  560. placeholder="请输入实际油耗"
  561. @blur="handleCustomFuelChange(scope.row)"
  562. style="width: 100%"
  563. size="small"
  564. @focus="handleFuelInputFocus(scope.row)"
  565. :key="scope.row.deviceId"
  566. />
  567. <!-- 只读模式下显示数值 -->
  568. <span v-else>
  569. {{ formatNumber(scope.row.customFuel, 2) }}
  570. </span>
  571. </template>
  572. </el-table-column>
  573. </el-table>
  574. </div>
  575. </ContentWrap>
  576. <!-- 平台井工作量区域 - 只在平台井的详情或审批模式下显示 -->
  577. <ContentWrap
  578. class="platform-workload-section"
  579. v-if="(isDetailMode || isApprovalMode) && dailyReportData?.platformWell === 1"
  580. >
  581. <h2 class="text-lg font-semibold mb-4">平台井工作量</h2>
  582. <div
  583. class="platform-workload-table"
  584. v-if="platformWorkloadData && platformWorkloadData.length > 0"
  585. >
  586. <el-table
  587. :data="platformWorkloadData"
  588. border
  589. style="width: 100%"
  590. class="platform-workload-el-table"
  591. table-layout="fixed"
  592. >
  593. <!-- 固定列 -->
  594. <el-table-column
  595. prop="wellName"
  596. label="井号"
  597. align="center"
  598. :show-overflow-tooltip="true"
  599. />
  600. <el-table-column label="施工状态" align="center" :show-overflow-tooltip="true">
  601. <template #default="scope">
  602. {{ scope.row.rdStatusLabel || '' }}
  603. </template>
  604. </el-table-column>
  605. <el-table-column label="施工工艺" align="center" :show-overflow-tooltip="true">
  606. <template #default="scope">
  607. {{ scope.row.techniqueNames || '' }}
  608. </template>
  609. </el-table-column>
  610. <!-- 动态工作量列 -->
  611. <el-table-column
  612. v-for="workloadColumn in getWorkloadColumns()"
  613. :key="workloadColumn.key"
  614. :label="workloadColumn.label"
  615. align="center"
  616. :show-overflow-tooltip="true"
  617. >
  618. <template #default="scope">
  619. {{ getWorkloadValue(scope.row, workloadColumn.identifier) }}
  620. </template>
  621. </el-table-column>
  622. </el-table>
  623. </div>
  624. <div v-else class="text-center text-gray-500 py-4"> 暂无平台井工作量数据 </div>
  625. </ContentWrap>
  626. <!-- 第四部分:审批意见 - 只在审批模式下显示 -->
  627. <ContentWrap class="section-padding" v-if="isApprovalMode || isEditMode || isDetailMode">
  628. <el-form
  629. ref="approvalFormRef"
  630. :model="approvalForm"
  631. style="margin-top: 1em"
  632. label-width="200px"
  633. >
  634. <el-row>
  635. <el-col :span="24">
  636. <el-form-item label="审批意见" prop="opinion">
  637. <el-input
  638. v-model="approvalForm.opinion"
  639. type="textarea"
  640. :rows="4"
  641. placeholder="请输入审批意见"
  642. maxlength="500"
  643. show-word-limit
  644. :readonly="!isApprovalMode"
  645. :disabled="!isApprovalMode"
  646. />
  647. </el-form-item>
  648. </el-col>
  649. </el-row>
  650. </el-form>
  651. </ContentWrap>
  652. <!-- 操作按钮 -->
  653. <ContentWrap class="section-padding" v-if="isEditMode">
  654. <el-form>
  655. <el-form-item style="float: right">
  656. <el-button @click="submitForm" type="primary" :disabled="formLoading">
  657. {{ t('common.save') }}
  658. </el-button>
  659. <el-button @click="close">{{ t('common.cancel') }}</el-button>
  660. </el-form-item>
  661. </el-form>
  662. </ContentWrap>
  663. <!-- 审批模式下的操作按钮 -->
  664. <ContentWrap class="section-padding" v-if="isApprovalMode">
  665. <el-form>
  666. <el-form-item style="float: right">
  667. <el-button @click="handleApprove('pass')" type="success"> 审批通过 </el-button>
  668. <el-button @click="handleApprove('reject')" type="danger"> 审批驳回 </el-button>
  669. <el-button @click="close">{{ t('common.close') }}</el-button>
  670. </el-form-item>
  671. </el-form>
  672. </ContentWrap>
  673. <!-- 详情模式下的操作按钮 - 只有关闭按钮 -->
  674. <ContentWrap class="section-padding" v-if="isDetailMode">
  675. <el-form>
  676. <el-form-item style="float: right">
  677. <el-button @click="close">{{ t('common.close') }}</el-button>
  678. </el-form-item>
  679. </el-form>
  680. </ContentWrap>
  681. </ContentWrap>
  682. <!-- 设备选择对话框 -->
  683. <el-dialog
  684. v-model="deviceDialogVisible"
  685. title="选择施工设备"
  686. width="1000px"
  687. :before-close="handleDeviceDialogClose"
  688. class="device-select-dialog"
  689. >
  690. <div class="transfer-container">
  691. <el-transfer
  692. v-model="selectedDeviceIds"
  693. :data="filteredDeviceList"
  694. :titles="['可选设备', '已选设备']"
  695. :props="{ key: 'id', label: 'deviceCode' }"
  696. filterable
  697. class="transfer-component"
  698. @change="handleTransferChange"
  699. >
  700. <template #default="{ option }">
  701. <el-tooltip
  702. effect="dark"
  703. placement="top"
  704. :content="`${option.deviceCode || ''} - ${option.deviceName || ''}`"
  705. :disabled="!option.deviceCode && !option.deviceName"
  706. transition="fade-in-linear"
  707. >
  708. <span class="transfer-option-text">
  709. {{ option.deviceCode }} - {{ option.deviceName }}
  710. </span>
  711. </el-tooltip>
  712. </template>
  713. </el-transfer>
  714. </div>
  715. <template #footer>
  716. <span class="dialog-footer">
  717. <el-button @click="handleDeviceDialogClose">取消</el-button>
  718. <el-button type="primary" @click="confirmDeviceSelection">确定</el-button>
  719. </span>
  720. </template>
  721. </el-dialog>
  722. </template>
  723. <script setup lang="ts">
  724. import { ref, reactive, computed, onMounted, nextTick, watch } from 'vue'
  725. import { useI18n } from '@/hooks/web/useI18n'
  726. import { useMessage } from '@/hooks/web/useMessage'
  727. import { useTagsViewStore } from '@/store/modules/tagsView'
  728. import { useRouter } from 'vue-router'
  729. import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
  730. import { IotRdDailyReportApi } from '@/api/pms/iotrddailyreport'
  731. import { IotDailyReportAttrsApi } from '@/api/pms/iotdailyreportattrs'
  732. import * as DeptApi from '@/api/system/dept'
  733. import { useUserStore } from '@/store/modules/user'
  734. import dayjs from 'dayjs'
  735. import FileUpload from '@/components/UploadFile/src/FileUpload.vue'
  736. const NON_PROD_FIELDS = [
  737. { key: 'repairTime', label: '设备故障' },
  738. { key: 'selfStopTime', label: '设备保养' },
  739. { key: 'accidentTime', label: '工程质量' },
  740. { key: 'complexityTime', label: '技术受限' },
  741. { key: 'rectificationTime', label: '生产组织' },
  742. { key: 'waitingStopTime', label: '不可抗力' },
  743. { key: 'partyaDesign', label: '甲方设计' },
  744. { key: 'partyaPrepare', label: '甲方准备' },
  745. { key: 'partyaResource', label: '甲方资源' },
  746. { key: 'relocationTime', label: '生产配合' },
  747. { key: 'winterBreakTime', label: '待命' },
  748. { key: 'otherNptTime', label: '其他非生产时间' }
  749. ] as const
  750. const { t } = useI18n()
  751. const message = useMessage()
  752. const { delView } = useTagsViewStore()
  753. const { push, currentRoute } = useRouter()
  754. const { params, query } = useRoute()
  755. const userStore = useUserStore()
  756. /** 填报日报 表单 */
  757. defineOptions({ name: 'FillDailyReportForm' })
  758. const formLoading = ref(false)
  759. const formRef = ref()
  760. const id = params.id // 瑞都日报id
  761. const fuelTableKey = ref(0) // 用于强制重新渲染表格
  762. // 添加一个新的响应式变量用于输入
  763. const dailyFuelInput = ref('')
  764. // 日报数据
  765. const dailyReportData = ref<any>({})
  766. // 修改 reportFuels 的 watch,添加标志位避免自动覆盖
  767. const dailyFuelManuallyModified = ref(false)
  768. // 添加模式判断计算属性
  769. const isApprovalMode = computed(() => params.mode === 'approval')
  770. const isDetailMode = computed(() => params.mode === 'detail')
  771. const isEditMode = computed(() => params.mode === 'fill' || !params.mode) // 默认为编辑模式
  772. // 只读模式判断:审批模式或详情模式都为只读
  773. const isReadonlyMode = computed(
  774. () => isApprovalMode.value || isDetailMode.value || formData.value.auditStatus === 20
  775. )
  776. // 在表单数据定义附近添加
  777. const platformWellPairs = ref<any[]>([]) // 存储各平台井的工作量数据
  778. const currentPlatformId = ref<number>() // 当前选中的平台井ID
  779. // 计算属性:显示井名(统一处理主井和平台井逻辑)
  780. const displayWellName = computed(() => {
  781. // 如果是平台井模式
  782. if (dailyReportData.value.platformWell === 1) {
  783. // 确定平台井数据源:优先使用 platforms,不存在则使用 finishedPlatforms
  784. const platformSource =
  785. dailyReportData.value.platforms || dailyReportData.value.finishedPlatforms
  786. // 如果有平台井数据
  787. if (platformSource && platformSource.length > 0) {
  788. // 检查主井是否在平台井列表中
  789. const isMainWellInPlatforms = platformSource.some(
  790. (platform: any) => platform.id === dailyReportData.value.taskId
  791. )
  792. // 如果主井不在平台井列表中(说明主井已施工完成),使用第一个平台井的名称
  793. if (!isMainWellInPlatforms) {
  794. const firstPlatformWellName = platformSource[0].wellName
  795. console.log(`主井已施工完成,井号显示使用平台井名称: ${firstPlatformWellName}`)
  796. return firstPlatformWellName
  797. }
  798. }
  799. }
  800. // 其他情况使用原来的井名
  801. return dailyReportData.value.wellName
  802. })
  803. // 页面标题计算
  804. const pageTitle = computed(() => {
  805. const displayWellNameValue = displayWellName.value
  806. const constructionDate = dailyReportData.value.constructionStartDate
  807. if (isApprovalMode.value) {
  808. return displayWellNameValue && constructionDate
  809. ? `${displayWellNameValue} - ${formatDate(constructionDate)} 日报审批`
  810. : '日报审批'
  811. } else if (isDetailMode.value) {
  812. return displayWellNameValue && constructionDate
  813. ? `${displayWellNameValue} - ${formatDate(constructionDate)} 日报详情`
  814. : '日报详情'
  815. } else {
  816. return displayWellNameValue && constructionDate
  817. ? `${displayWellNameValue} - ${formatDate(constructionDate)} 生产日报`
  818. : '日报填报'
  819. }
  820. })
  821. // 处理输入框获取焦点事件
  822. const handleFuelInputFocus = (fuelItem: any) => {
  823. // 如果 customFuel 是空的,确保它显示默认值
  824. if (!fuelItem.customFuel || fuelItem.customFuel === '') {
  825. const zhbdValue = parseFloat(fuelItem.zhbdFuel)
  826. fuelItem.customFuel = !isNaN(zhbdValue) ? formatNumber(zhbdValue, 2) : '0.00'
  827. }
  828. }
  829. // 模式提示信息
  830. const modeNotice = computed(() => {
  831. if (isApprovalMode.value) {
  832. return '审批模式:所有字段均为只读'
  833. } else if (isDetailMode.value) {
  834. return '详情模式:所有字段均为只读'
  835. }
  836. return ''
  837. })
  838. // 动态属性相关变量
  839. const dynamicAttrs = ref<any[]>([]) // 存储动态属性列表
  840. // 添加设备选择相关变量
  841. const deviceDialogVisible = ref(false)
  842. const filteredDeviceList = ref<any[]>([])
  843. const selectedDeviceIds = ref<number[]>([])
  844. const deviceMap = ref<Record<number, any>>({})
  845. // 添加平台井相关响应式数据
  846. const platformOptions = ref<any[]>([]) // 平台井下拉选项
  847. const showPlatformField = ref(false) // 是否显示平台井字段
  848. // 计算属性:是否显示平台井字段
  849. const shouldShowPlatformField = computed(() => {
  850. return dailyReportData.value.platformWell === 1
  851. })
  852. // 初始化平台井数据
  853. const initPlatformData = (reportData: any) => {
  854. // 设置是否显示平台井字段
  855. showPlatformField.value = reportData.platformWell === 1
  856. // 设置平台井下拉选项 - 修改后的逻辑
  857. let platformSource = reportData.platforms
  858. // 在详情或审批模式下,如果 platforms 不存在,则使用 finishedPlatforms
  859. if (
  860. (isDetailMode.value || isApprovalMode.value) &&
  861. (!platformSource || platformSource.length === 0)
  862. ) {
  863. platformSource = reportData.finishedPlatforms
  864. }
  865. // 设置平台井下拉选项
  866. if (platformSource && Array.isArray(platformSource)) {
  867. platformOptions.value = platformSource
  868. // 初始化 platformWellPairs,确保包含所有平台井的完整数据
  869. if (reportData.platformWell === 1) {
  870. // 初始化 platformWellPairs,包含所有平台井的完整数据
  871. platformWellPairs.value = platformSource.map((platform: any) => {
  872. // 查找是否已有该平台井的数据
  873. const existingData = reportData.platformWellPairs?.find(
  874. (p: any) => p.taskId === platform.id
  875. )
  876. // 确保 techniqueIds 是字符串数组格式
  877. let techniqueIds = []
  878. if (existingData && existingData.techniqueIds) {
  879. techniqueIds = existingData.techniqueIds.map((id: any) => id.toString())
  880. } else if (platform.techniqueIds) {
  881. techniqueIds = platform.techniqueIds.map((id: any) => id.toString())
  882. }
  883. return (
  884. existingData || {
  885. taskId: platform.id,
  886. dailyFuel: platform.dailyFuel || '',
  887. reportId: platform.reportId, // 使用接口返回的 reportId
  888. wellName: platform.wellName,
  889. rdStatus: platform.rdStatus || '', // 初始为空
  890. techniqueIds: techniqueIds, // 初始为空数组
  891. extProperty: platform.extProperty || [], // 初始为空数组
  892. repairTime: platform.repairTime ?? 0,
  893. selfStopTime: platform.selfStopTime ?? 0,
  894. accidentTime: platform.accidentTime ?? 0,
  895. complexityTime: platform.complexityTime ?? 0,
  896. rectificationTime: platform.rectificationTime ?? 0,
  897. waitingStopTime: platform.waitingStopTime ?? 0,
  898. partyaDesign: platform.partyaDesign ?? 0,
  899. partyaPrepare: platform.partyaPrepare ?? 0,
  900. partyaResource: platform.partyaResource ?? 0,
  901. relocationTime: platform.relocationTime ?? 0,
  902. winterBreakTime: platform.winterBreakTime ?? 0,
  903. otherNptTime: platform.otherNptTime ?? 0,
  904. otherNptReason: platform.otherNptReason || ''
  905. }
  906. )
  907. })
  908. }
  909. } else {
  910. platformOptions.value = []
  911. platformWellPairs.value = []
  912. }
  913. // 设置默认选中的平台井 - 修改后的逻辑
  914. if (platformOptions.value.length > 0) {
  915. let selectedPlatform = null
  916. // 首先尝试查找与 taskId 匹配的平台井
  917. if (reportData.taskId) {
  918. selectedPlatform = platformOptions.value.find(
  919. (platform: any) => platform.id === reportData.taskId
  920. )
  921. }
  922. // 如果没有找到匹配的平台井,选择第一个平台井
  923. if (!selectedPlatform) {
  924. selectedPlatform = platformOptions.value[0]
  925. }
  926. // 设置选中的平台井
  927. if (selectedPlatform) {
  928. formData.value.platformId = selectedPlatform.id
  929. currentPlatformId.value = selectedPlatform.id
  930. // 加载平台井的数据到表单
  931. loadPlatformData(selectedPlatform.id)
  932. // 可选:在控制台输出提示信息
  933. if (reportData.taskId && selectedPlatform.id !== reportData.taskId) {
  934. console.log(`主井已施工完成,已自动选择第一个平台井: ${selectedPlatform.wellName}`)
  935. }
  936. }
  937. }
  938. }
  939. // 添加审批表单相关变量
  940. const approvalFormRef = ref()
  941. const approvalForm = reactive({
  942. opinion: '' // 审批意见
  943. })
  944. // 审批表单验证规则(可选,根据需求添加)
  945. const approvalFormRules = reactive({
  946. opinion: [
  947. { required: false, message: '请输入审批意见', trigger: 'blur' },
  948. { min: 0, max: 500, message: '审批意见长度不能超过500个字符', trigger: 'blur' }
  949. ]
  950. })
  951. // 将时分秒数组转换为Date对象(基于constructionStartDate的日期)
  952. const parseTimeArrayToDate = (timeArray: number[], baseDate: number) => {
  953. if (!Array.isArray(timeArray) || !baseDate) {
  954. return null
  955. }
  956. const hour = timeArray[0] || 0
  957. const minute = timeArray[1] || 0
  958. const second = timeArray[2] || 0
  959. // 基于日报日期(constructionStartDate)设置时分秒
  960. return dayjs(baseDate).hour(hour).minute(minute).second(second).toDate()
  961. }
  962. // 添加文件上传组件的引用
  963. const fileUploadRef = ref()
  964. // 表单数据
  965. const formData = ref<any>({
  966. id: undefined,
  967. deptId: undefined,
  968. taskId: undefined,
  969. platformWell: undefined,
  970. companyId: undefined,
  971. deptName: undefined,
  972. constructionStartDate: undefined,
  973. contractName: undefined,
  974. projectDepartment: '',
  975. costCenterId: undefined,
  976. costCenter: '',
  977. platformId: undefined, // 平台井ID
  978. // 日报填报字段
  979. timeRange: [
  980. // 设置默认时间范围 8:00 - 8:00
  981. dayjs().hour(8).minute(0).second(0).toDate(),
  982. dayjs().hour(8).minute(0).second(0).toDate()
  983. ],
  984. startTime: undefined, // 开始时间
  985. endTime: undefined, // 结束时间
  986. rdStatus: '', // 施工状态
  987. deviceIds: [] as number[], // 设备ID数组
  988. techniqueIds: [], // 施工工艺
  989. dailyFuel: '', // 当日油耗
  990. productionStatus: '', // 当日生产动态
  991. nextPlan: '', // 下步工作计划
  992. externalRental: '', // 外租设备
  993. malfunction: '', // 故障情况
  994. faultDowntime: '', // 故障误工
  995. // 添加动态字段对象
  996. dynamicFields: {} as Record<string, any>,
  997. // 附件列表
  998. attachments: [] as any[],
  999. reportFuels: [] as any[], // 油耗信息数组
  1000. repairTime: 0,
  1001. selfStopTime: 0,
  1002. accidentTime: 0,
  1003. complexityTime: 0,
  1004. rectificationTime: 0,
  1005. waitingStopTime: 0,
  1006. partyaDesign: 0,
  1007. partyaPrepare: 0,
  1008. partyaResource: 0,
  1009. relocationTime: 0,
  1010. winterBreakTime: 0,
  1011. otherNptTime: 0,
  1012. otherNptReason: ''
  1013. })
  1014. // 添加上传成功处理函数
  1015. const handleUploadSuccess = (result: any) => {
  1016. console.log('上传成功', result)
  1017. try {
  1018. // 检查响应是否成功
  1019. if (!result.response) {
  1020. message.error('上传响应数据异常')
  1021. return
  1022. }
  1023. if (result.response.code !== 0) {
  1024. message.error(result.response.msg || '文件上传失败')
  1025. return
  1026. }
  1027. const responseData = result.response.data
  1028. if (!responseData) {
  1029. message.error('上传数据为空')
  1030. return
  1031. }
  1032. // 处理返回的文件列表
  1033. if (responseData.files && Array.isArray(responseData.files) && responseData.files.length > 0) {
  1034. responseData.files.forEach((file: any) => {
  1035. if (!file.filePath) {
  1036. console.warn('文件缺少 filePath:', file)
  1037. return
  1038. }
  1039. // 根据后端返回的数据结构构建附件对象
  1040. const attachment = {
  1041. id: undefined,
  1042. category: 'daily_report',
  1043. bizId: formData.value.id,
  1044. type: 'attachment',
  1045. filename: file.name || '未知文件',
  1046. fileType: getFileType(file.name),
  1047. filePath: file.filePath, //使用正确的 filePath
  1048. fileSize: formatFileSize(file.size || 0),
  1049. remark: ''
  1050. }
  1051. // 添加到附件列表
  1052. if (!formData.value.attachments) {
  1053. formData.value.attachments = []
  1054. }
  1055. formData.value.attachments.push(attachment)
  1056. })
  1057. message.success(`成功上传 ${responseData.files.length} 个文件`)
  1058. } else {
  1059. console.warn('上传成功但没有返回文件信息')
  1060. message.warning('上传成功但未获取到文件信息')
  1061. }
  1062. } catch (error) {
  1063. console.error('处理上传结果时发生错误:', error)
  1064. message.error('处理上传结果失败')
  1065. }
  1066. }
  1067. // 删除附件
  1068. const removeAttachment = (index: number) => {
  1069. if (formData.value.attachments && formData.value.attachments.length > index) {
  1070. formData.value.attachments.splice(index, 1)
  1071. }
  1072. }
  1073. // 计算属性:未施工设备名称
  1074. const unSelectedDeviceNames = computed(() => {
  1075. const selectedDevices = dailyReportData.value.selectedDevices || []
  1076. const selectedDeviceIds = formData.value.deviceIds || []
  1077. if (selectedDevices.length === 0) {
  1078. return '无可用设备'
  1079. }
  1080. // 筛选出未选择的设备
  1081. const unselectedDevices = selectedDevices.filter(
  1082. (device: any) => !selectedDeviceIds.includes(device.id)
  1083. )
  1084. if (unselectedDevices.length === 0) {
  1085. return '所有设备都已施工'
  1086. }
  1087. // 提取设备名称并用逗号分隔
  1088. const deviceNames = unselectedDevices
  1089. .map((device: any) => device.deviceName || device.deviceCode || '未知设备')
  1090. .filter((name: string) => name !== '未知设备')
  1091. return deviceNames.join(', ') || '无未选择设备'
  1092. })
  1093. // 附件名称点击事件
  1094. const inContent = async (attachment) => {
  1095. if (!attachment || !attachment.filePath) {
  1096. message.error('附件路径不存在')
  1097. return
  1098. }
  1099. try {
  1100. // 直接使用 attachment.filePath
  1101. const filePath = attachment.filePath
  1102. // 确保 filePath 是编码后的格式
  1103. const encodedPath = encodeURIComponent(Base64.encode(filePath))
  1104. // 打开预览窗口
  1105. window.open(`http://doc.deepoil.cc:8012/onlinePreview?url=${encodedPath}`)
  1106. } catch (error) {
  1107. console.error('预览附件失败:', error)
  1108. message.error('预览附件失败')
  1109. }
  1110. }
  1111. // 获取文件类型辅助函数
  1112. const getFileType = (filename: string) => {
  1113. const ext = filename.split('.').pop()?.toLowerCase()
  1114. if (['jpg', 'jpeg', 'png', 'gif', 'bmp'].includes(ext || '')) {
  1115. return 'image'
  1116. } else if (['pdf'].includes(ext || '')) {
  1117. return 'pdf'
  1118. } else if (['doc', 'docx'].includes(ext || '')) {
  1119. return 'word'
  1120. } else if (['xls', 'xlsx'].includes(ext || '')) {
  1121. return 'excel'
  1122. } else {
  1123. return 'other'
  1124. }
  1125. }
  1126. // 格式化文件大小辅助函数
  1127. const formatFileSize = (bytes: number) => {
  1128. if (bytes === 0) return '0 Bytes'
  1129. const k = 1024
  1130. const sizes = ['Bytes', 'KB', 'MB', 'GB']
  1131. const i = Math.floor(Math.log(bytes) / Math.log(k))
  1132. return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
  1133. }
  1134. // 计算属性:格式化设备显示
  1135. const formatDevicesForDisplay = computed(() => {
  1136. const deviceIds = formData.value.deviceIds
  1137. if (!deviceIds || deviceIds.length === 0) {
  1138. return '无设备'
  1139. }
  1140. const deviceNames = deviceIds
  1141. .map((id) => deviceMap.value[id]?.deviceName)
  1142. .filter((name) => name !== undefined && name !== '')
  1143. if (deviceNames.length === 0) return '无设备'
  1144. // 如果设备数量超过2个,显示前两个加省略号
  1145. /* if (deviceNames.length > 2) {
  1146. return `${deviceNames[0]}, ${deviceNames[1]}...`
  1147. } */
  1148. return deviceNames.join(', ')
  1149. })
  1150. // 计算属性:获取所有设备名称(用于tooltip)
  1151. const getAllDeviceNamesForDisplay = computed(() => {
  1152. const deviceIds = formData.value.deviceIds
  1153. if (!deviceIds || deviceIds.length === 0) {
  1154. return '无设备'
  1155. }
  1156. const deviceNames = deviceIds
  1157. .map((id) => deviceMap.value[id]?.deviceCode || '未知设备')
  1158. .filter((name) => name !== '未知设备')
  1159. return deviceNames.join(', ') || '无有效设备'
  1160. })
  1161. // 打开设备选择对话框
  1162. const openDeviceDialog = async () => {
  1163. if (!dailyReportData.value.deptId) {
  1164. message.error('请先加载项目信息')
  1165. return
  1166. }
  1167. try {
  1168. formLoading.value = true
  1169. selectedDeviceIds.value = [...(formData.value.deviceIds || [])]
  1170. // 直接从日报数据的 selectedDevices 中获取设备列表
  1171. const selectedDevices = dailyReportData.value.selectedDevices || []
  1172. // 更新设备映射表
  1173. const newDeviceMap = { ...deviceMap.value }
  1174. selectedDevices.forEach((device: any) => {
  1175. if (device.id) {
  1176. newDeviceMap[device.id] = device
  1177. }
  1178. })
  1179. deviceMap.value = newDeviceMap
  1180. filteredDeviceList.value = selectedDevices
  1181. deviceDialogVisible.value = true
  1182. } catch (error) {
  1183. console.error('获取设备列表失败:', error)
  1184. message.error('获取设备列表失败')
  1185. } finally {
  1186. formLoading.value = false
  1187. }
  1188. }
  1189. // 修改格式化函数,改为失去焦点时触发
  1190. const formatDailyFuel = () => {
  1191. if (!dailyFuelInput.value || dailyFuelInput.value.trim() === '') {
  1192. formData.value.dailyFuel = ''
  1193. dailyFuelInput.value = ''
  1194. return
  1195. }
  1196. // 移除非数字字符(除了小数点)
  1197. const cleaned = dailyFuelInput.value.replace(/[^\d.]/g, '')
  1198. // 确保只有一个小数点
  1199. const parts = cleaned.split('.')
  1200. if (parts.length > 2) {
  1201. dailyFuelInput.value = parts[0] + '.' + parts.slice(1).join('')
  1202. }
  1203. const numValue = parseFloat(dailyFuelInput.value)
  1204. if (!isNaN(numValue)) {
  1205. // 限制到两位小数
  1206. formData.value.dailyFuel = formatNumber(numValue, 2)
  1207. dailyFuelInput.value = formData.value.dailyFuel
  1208. } else {
  1209. formData.value.dailyFuel = ''
  1210. dailyFuelInput.value = ''
  1211. }
  1212. }
  1213. // 处理穿梭框变化
  1214. const handleTransferChange = (value: number[], direction: string, movedKeys: number[]) => {
  1215. // 可以添加额外的处理逻辑
  1216. }
  1217. // 确认设备选择
  1218. const confirmDeviceSelection = () => {
  1219. formData.value.deviceIds = [...selectedDeviceIds.value]
  1220. deviceDialogVisible.value = false
  1221. message.success(`已选择 ${selectedDeviceIds.value.length} 台设备`)
  1222. }
  1223. // 关闭设备选择对话框
  1224. const handleDeviceDialogClose = () => {
  1225. deviceDialogVisible.value = false
  1226. }
  1227. // 初始化设备数据
  1228. const initDeviceData = (reportData: any) => {
  1229. // 初始化设备ID
  1230. if (reportData.deviceIds && Array.isArray(reportData.deviceIds)) {
  1231. formData.value.deviceIds = [...reportData.deviceIds]
  1232. } else {
  1233. formData.value.deviceIds = []
  1234. }
  1235. // 初始化设备映射表(用于显示设备名称)
  1236. if (reportData.selectedDevices && Array.isArray(reportData.selectedDevices)) {
  1237. const newDeviceMap = { ...deviceMap.value }
  1238. reportData.selectedDevices.forEach((device: any) => {
  1239. if (device.id) {
  1240. newDeviceMap[device.id] = device
  1241. }
  1242. })
  1243. deviceMap.value = newDeviceMap
  1244. }
  1245. }
  1246. // const validateOtherReason = (_rule: any, value: any, callback: any) => {
  1247. // const time = formData.value.otherNptTime || 0
  1248. // if (time > 0 && !value) {
  1249. // callback(new Error('填写了其他时间,必须说明原因'))
  1250. // } else {
  1251. // callback()
  1252. // }
  1253. // }
  1254. const formRules = computed(() => {
  1255. // 判断是否为虚拟项目
  1256. const isVirtualProject = dailyReportData.value.virtualProject === 'Y'
  1257. // 基础校验规则(时间节点、当日生产动态始终必填)
  1258. const rules = {
  1259. timeRange: [{ required: true, message: '时间节点不能为空', trigger: 'change' }],
  1260. productionStatus: [{ required: true, message: '当日生产动态不能为空', trigger: 'blur' }],
  1261. nextPlan: [{ required: true, message: '下步工作计划不能为空', trigger: 'blur' }],
  1262. // otherNptReason: [{ validator: validateOtherReason, trigger: ['blur', 'change'] }],
  1263. dailyFuel: [
  1264. {
  1265. required: true,
  1266. message: '当日油耗不能为空',
  1267. trigger: 'blur'
  1268. },
  1269. {
  1270. validator: (rule: any, value: any, callback: any) => {
  1271. if (value === '' || value === null || value === undefined) {
  1272. callback()
  1273. return
  1274. }
  1275. const numValue = Number(value)
  1276. if (isNaN(numValue)) {
  1277. callback(new Error('当日油耗必须是数字'))
  1278. } else if (numValue < 0) {
  1279. callback(new Error('当日油耗不能小于0'))
  1280. } else {
  1281. callback()
  1282. }
  1283. },
  1284. trigger: 'blur'
  1285. }
  1286. ]
  1287. }
  1288. // 非虚拟项目时,添加施工状态、施工工艺的必填校验
  1289. if (!isVirtualProject) {
  1290. rules.rdStatus = [{ required: true, message: '施工状态不能为空', trigger: 'change' }]
  1291. rules.techniqueIds = [{ required: true, message: '施工工艺不能为空', trigger: 'change' }]
  1292. }
  1293. return rules
  1294. })
  1295. const queryParams = reactive({
  1296. deptId: undefined,
  1297. techniqueIds: []
  1298. })
  1299. // 下拉选项
  1300. const rdStatusOptions = getStrDictOptions(DICT_TYPE.PMS_PROJECT_RD_STATUS) // 施工状态
  1301. const techniqueOptions = getStrDictOptions(DICT_TYPE.PMS_PROJECT_RD_TECHNOLOGY) // 瑞都施工工艺
  1302. // 计算属性:日报标题
  1303. const dailyReportTitle = computed(() => {
  1304. if (!dailyReportData.value.wellName || !dailyReportData.value.constructionStartDate) {
  1305. return '日报填报'
  1306. }
  1307. const dateStr = formatDate(dailyReportData.value.constructionStartDate)
  1308. return `${dailyReportData.value.wellName} - ${dateStr} 生产日报`
  1309. })
  1310. // 日报审批:日报标题
  1311. const dailyReportApprovalTitle = computed(() => {
  1312. if (!dailyReportData.value.wellName || !dailyReportData.value.constructionStartDate) {
  1313. return '日报审批'
  1314. }
  1315. const dateStr = formatDate(dailyReportData.value.constructionStartDate)
  1316. return `${dailyReportData.value.wellName} - ${dateStr} 日报审批`
  1317. })
  1318. // 计算属性:施工周期
  1319. const constructionPeriod = computed(() => {
  1320. const start = dailyReportData.value.constructionStartDate
  1321. const end = dailyReportData.value.constructionEndDate
  1322. if (!start || !end) return 0
  1323. const startDate = dayjs(start)
  1324. const endDate = dayjs(end)
  1325. return endDate.diff(startDate, 'day')
  1326. })
  1327. // 日期格式化函数
  1328. const formatDate = (timestamp: number) => {
  1329. if (!timestamp) return ''
  1330. return dayjs(timestamp).format('YYYY-MM-DD')
  1331. }
  1332. const close = () => {
  1333. delView(unref(currentRoute))
  1334. push({ name: 'FillDailyReport', params: {} })
  1335. }
  1336. /** 提交表单 */
  1337. const emit = defineEmits(['success'])
  1338. const submitForm = async () => {
  1339. // 验证表单
  1340. try {
  1341. await formRef.value.validate()
  1342. } catch (error) {
  1343. return
  1344. }
  1345. // 保存当前平台井的数据
  1346. if (currentPlatformId.value) {
  1347. saveCurrentPlatformData(currentPlatformId.value)
  1348. }
  1349. let validationPassed = true
  1350. const validationErrors: string[] = []
  1351. // 检查所有平台井数据
  1352. for (const pair of platformWellPairs.value) {
  1353. // 计算所有时间字段的总和
  1354. const totalTime =
  1355. (pair.repairTime || 0) +
  1356. (pair.selfStopTime || 0) +
  1357. (pair.accidentTime || 0) +
  1358. (pair.complexityTime || 0) +
  1359. (pair.rectificationTime || 0) +
  1360. (pair.waitingStopTime || 0) +
  1361. (pair.partyaDesign || 0) +
  1362. (pair.partyaPrepare || 0) +
  1363. (pair.partyaResource || 0) +
  1364. (pair.relocationTime || 0) +
  1365. (pair.winterBreakTime || 0) +
  1366. (pair.otherNptTime || 0)
  1367. // 检查总和是否超过24小时
  1368. if (totalTime > 24) {
  1369. validationPassed = false
  1370. validationErrors.push(`${pair.wellName || '平台井'}的时间总和不能超过24小时`)
  1371. }
  1372. // 检查otherNptTime>0时是否填写了otherNptReason
  1373. if ((pair.otherNptTime || 0) > 0 && !pair.otherNptReason) {
  1374. validationPassed = false
  1375. validationErrors.push(`${pair.wellName || '平台井'}的其他时间大于0时必须填写原因`)
  1376. }
  1377. }
  1378. // 检查非平台井模式
  1379. if (dailyReportData.value.platformWell !== 1) {
  1380. // 计算所有时间字段的总和
  1381. const totalTime =
  1382. (formData.value.repairTime || 0) +
  1383. (formData.value.selfStopTime || 0) +
  1384. (formData.value.accidentTime || 0) +
  1385. (formData.value.complexityTime || 0) +
  1386. (formData.value.rectificationTime || 0) +
  1387. (formData.value.waitingStopTime || 0) +
  1388. (formData.value.partyaDesign || 0) +
  1389. (formData.value.partyaPrepare || 0) +
  1390. (formData.value.partyaResource || 0) +
  1391. (formData.value.relocationTime || 0) +
  1392. (formData.value.winterBreakTime || 0) +
  1393. (formData.value.otherNptTime || 0)
  1394. // 检查总和是否超过24小时
  1395. if (totalTime > 24) {
  1396. validationPassed = false
  1397. validationErrors.push('时间总和不能超过24小时')
  1398. }
  1399. }
  1400. // 如果验证失败,显示错误信息
  1401. if (!validationPassed) {
  1402. validationErrors.forEach((error) => {
  1403. message.error(error)
  1404. })
  1405. return
  1406. }
  1407. // 打印 platformWellPairs
  1408. console.log('platformWellPairs:', JSON.stringify(platformWellPairs.value, null, 2))
  1409. // 处理时间范围数据
  1410. if (formData.value.timeRange && formData.value.timeRange.length === 2) {
  1411. // 将时间范围转换为 LocalTime 格式的字符串
  1412. const startDate = dayjs(formData.value.timeRange[0])
  1413. const endDate = dayjs(formData.value.timeRange[1])
  1414. // 格式化为 HH:mm:ss 字符串,后端会自动转换为 LocalTime
  1415. formData.value.startTime = startDate.format('HH:mm:ss')
  1416. formData.value.endTime = endDate.format('HH:mm:ss')
  1417. }
  1418. // 构建动态属性 extProperty 数组
  1419. const extProperties = dynamicAttrs.value.map((attr) => {
  1420. return {
  1421. name: attr.name,
  1422. sort: attr.sort,
  1423. unit: attr.unit,
  1424. actualValue: formData.value.dynamicFields[attr.identifier] || '', // 从 dynamicFields 中获取用户填写的值
  1425. dataType: attr.dataType,
  1426. maxValue: attr.maxValue,
  1427. minValue: attr.minValue,
  1428. required: attr.required,
  1429. accessMode: attr.accessMode,
  1430. identifier: attr.identifier,
  1431. defaultValue: attr.defaultValue
  1432. }
  1433. })
  1434. // 准备提交数据,包含动态字段
  1435. const baseSubmitData = {
  1436. ...formData.value,
  1437. // 将动态字段组装成 extProperty 数组
  1438. extProperty: extProperties,
  1439. deviceIds: formData.value.deviceIds, // 设备ID集合
  1440. // 在填报模式下也提交审批意见字段
  1441. opinion: isEditMode.value ? approvalForm.opinion : undefined,
  1442. // 将油耗数据格式化为后端需要的格式
  1443. /* reportFuels: formData.value.reportFuels.map(fuel => ({
  1444. ...fuel,
  1445. // 确保 customFuel 是数字格式
  1446. customFuel: fuel.customFuel ? parseFloat(fuel.customFuel) : null
  1447. })), */
  1448. // 确保当日油耗是数字格式
  1449. dailyFuel: formData.value.dailyFuel ? parseFloat(formData.value.dailyFuel) : 0,
  1450. nonProduct: query.istime === 'true' ? 'Y' : ''
  1451. }
  1452. console.log('baseSubmitData:', baseSubmitData)
  1453. // 删除不需要复制的字段
  1454. const {
  1455. id: currentId,
  1456. platformId,
  1457. platformWell,
  1458. taskId,
  1459. rdStatus,
  1460. techniqueIds,
  1461. extProperty,
  1462. ...baseData
  1463. } = baseSubmitData
  1464. const submitDatas: any[] = []
  1465. if (dailyReportData.value.platformWell === 1 && platformWellPairs.value.length > 0) {
  1466. // 平台井模式:处理所有平台井数据
  1467. platformWellPairs.value.forEach((pair) => {
  1468. console.log('pair:', pair)
  1469. // 复制基础数据
  1470. const platformData: any = { ...baseData }
  1471. // 设置平台井特定字段
  1472. platformData.id = pair.reportId // 使用 platformWellPairs 中的 reportId
  1473. platformData.platformId = pair.taskId // 使用 platformWellPairs 中的 taskId
  1474. platformData.rdStatus = pair.rdStatus || ''
  1475. platformData.techniqueIds = pair.techniqueIds || []
  1476. platformData.extProperty = pair.extProperty || []
  1477. platformData.repairTime = pair.repairTime || 0
  1478. platformData.selfStopTime = pair.selfStopTime || 0
  1479. platformData.accidentTime = pair.accidentTime || 0
  1480. platformData.complexityTime = pair.complexityTime || 0
  1481. platformData.rectificationTime = pair.rectificationTime || 0
  1482. platformData.waitingStopTime = pair.waitingStopTime || 0
  1483. platformData.partyaDesign = pair.partyaDesign || 0
  1484. platformData.partyaPrepare = pair.partyaPrepare || 0
  1485. platformData.partyaResource = pair.partyaResource || 0
  1486. platformData.relocationTime = pair.relocationTime || 0
  1487. platformData.winterBreakTime = pair.winterBreakTime || 0
  1488. platformData.otherNptTime = pair.otherNptTime || 0
  1489. platformData.otherNptReason = pair.otherNptReason || ''
  1490. // 处理附件:复制并修改 bizId 为当前 pair 的 reportId
  1491. platformData.attachments = (baseData.attachments || []).map((attachment) => ({
  1492. ...attachment, // 深拷贝单个附件
  1493. bizId: pair.reportId // 替换 bizId 为当前平台井的 reportId
  1494. }))
  1495. // 车辆油耗:复制并修改 reportId 为当前 pair 的 reportId
  1496. platformData.reportFuels = (baseData.reportFuels || []).map((reportFuel) => ({
  1497. ...reportFuel, // 深拷贝单个油耗
  1498. reportId: pair.reportId // 替换 reportId 为当前平台井的 reportId
  1499. }))
  1500. // 重新构建 dynamicFields(如果需要)
  1501. const dynamicFields = {}
  1502. if (platformData.extProperty && platformData.extProperty.length > 0) {
  1503. platformData.extProperty.forEach((prop) => {
  1504. if (prop.identifier) {
  1505. dynamicFields[prop.identifier] = prop.actualValue || ''
  1506. }
  1507. })
  1508. }
  1509. platformData.dynamicFields = dynamicFields
  1510. submitDatas.push(platformData)
  1511. })
  1512. console.log('平台井模式提交数据:', JSON.stringify(submitDatas, null, 2))
  1513. } else {
  1514. // 非平台井模式:只提交当前数据
  1515. submitDatas.push(baseSubmitData)
  1516. }
  1517. // 提交请求
  1518. formLoading.value = true
  1519. try {
  1520. // 调用更新接口
  1521. await IotRdDailyReportApi.saveBatch(submitDatas)
  1522. message.success(t('common.updateSuccess'))
  1523. close()
  1524. // 发送操作成功的事件
  1525. emit('success')
  1526. } catch (error) {
  1527. console.error('提交失败:', error)
  1528. } finally {
  1529. formLoading.value = false
  1530. }
  1531. }
  1532. /** 重置表单 */
  1533. const resetForm = () => {
  1534. formRef.value?.resetFields()
  1535. }
  1536. // 初始化动态属性
  1537. const initDynamicAttrs = (reportData: any) => {
  1538. if (reportData.dailyReportAttrs && reportData.dailyReportAttrs.length > 0) {
  1539. dynamicAttrs.value = reportData.dailyReportAttrs
  1540. // 初始化动态字段的值
  1541. const initialDynamicFields: Record<string, any> = {}
  1542. // 优先从 extProperty 中获取实际值(编辑时)
  1543. if (reportData.extProperty && reportData.extProperty.length > 0) {
  1544. reportData.extProperty.forEach((extProp: any) => {
  1545. if (extProp.identifier) {
  1546. initialDynamicFields[extProp.identifier] = extProp.actualValue || ''
  1547. }
  1548. })
  1549. }
  1550. reportData.dailyReportAttrs.forEach((attr: any) => {
  1551. if (!initialDynamicFields.hasOwnProperty(attr.identifier)) {
  1552. // 优先使用实际值,如果没有则使用默认值
  1553. const value =
  1554. attr.extProperty &&
  1555. attr.extProperty.actualValue !== undefined &&
  1556. attr.extProperty.actualValue !== null &&
  1557. attr.extProperty.actualValue !== ''
  1558. ? attr.extProperty.actualValue
  1559. : attr.defaultValue || attr.extProperty?.defaultValue || ''
  1560. initialDynamicFields[attr.identifier] = value
  1561. }
  1562. })
  1563. formData.value.dynamicFields = initialDynamicFields
  1564. }
  1565. }
  1566. // 获取动态字段的验证规则
  1567. const getDynamicAttrRules = (attr: any) => {
  1568. const rules = []
  1569. if (attr.required === 1) {
  1570. rules.push({
  1571. required: true,
  1572. message: `${attr.name}不能为空`,
  1573. trigger: 'blur'
  1574. })
  1575. }
  1576. // 数字类型验证
  1577. if (attr.dataType === 'double') {
  1578. rules.push({
  1579. validator: (rule: any, value: any, callback: any) => {
  1580. if (value === '' || value === null || value === undefined) {
  1581. callback()
  1582. return
  1583. }
  1584. const numValue = Number(value)
  1585. if (isNaN(numValue)) {
  1586. callback(new Error(`${attr.name}必须是数字`))
  1587. } else if (attr.minValue && numValue < Number(attr.minValue)) {
  1588. callback(new Error(`${attr.name}不能小于${attr.minValue}`))
  1589. } else if (attr.maxValue && numValue > Number(attr.maxValue)) {
  1590. callback(new Error(`${attr.name}不能大于${attr.maxValue}`))
  1591. } else {
  1592. callback()
  1593. }
  1594. },
  1595. trigger: 'blur'
  1596. })
  1597. }
  1598. return rules
  1599. }
  1600. // 更新动态属性(处理交集、新增和删除)
  1601. const updateDynamicAttrs = async (
  1602. newAttrs: any[],
  1603. newTechniqueIds: string[],
  1604. oldTechniqueIds?: string[]
  1605. ) => {
  1606. const oldAttrs = [...dynamicAttrs.value]
  1607. const oldDynamicFields = { ...formData.value.dynamicFields }
  1608. // 计算需要保留的字段(交集)
  1609. const commonAttrs = oldAttrs.filter((oldAttr) =>
  1610. newAttrs.some((newAttr) => newAttr.identifier === oldAttr.identifier)
  1611. )
  1612. // 计算需要新增的字段
  1613. const addedAttrs = newAttrs.filter(
  1614. (newAttr) => !oldAttrs.some((oldAttr) => oldAttr.identifier === newAttr.identifier)
  1615. )
  1616. // 计算需要删除的字段
  1617. const removedAttrs = oldAttrs.filter(
  1618. (oldAttr) => !newAttrs.some((newAttr) => newAttr.identifier === oldAttr.identifier)
  1619. )
  1620. // 构建新的动态属性数组
  1621. const updatedAttrs = [...commonAttrs, ...addedAttrs]
  1622. // 构建新的动态字段对象
  1623. const updatedDynamicFields = { ...oldDynamicFields }
  1624. // 移除已删除的字段
  1625. removedAttrs.forEach((attr) => {
  1626. delete updatedDynamicFields[attr.identifier]
  1627. })
  1628. // 初始化新增字段的值
  1629. addedAttrs.forEach((attr) => {
  1630. if (!updatedDynamicFields[attr.identifier]) {
  1631. // 如果有默认值使用默认值,否则为空
  1632. updatedDynamicFields[attr.identifier] =
  1633. attr.defaultValue || attr.extProperty?.defaultValue || ''
  1634. }
  1635. })
  1636. // 更新响应式数据
  1637. dynamicAttrs.value = updatedAttrs
  1638. formData.value.dynamicFields = updatedDynamicFields
  1639. }
  1640. // 加载动态属性
  1641. const loadDynamicAttrs = async (newTechniqueIds: string[], oldTechniqueIds?: string[]) => {
  1642. try {
  1643. formLoading.value = true
  1644. const queryParams = {
  1645. techniqueIds: newTechniqueIds.join(',')
  1646. }
  1647. const response = await IotDailyReportAttrsApi.dailyReportAttrs(queryParams)
  1648. const newAttrs = response || []
  1649. // 处理动态属性更新
  1650. await updateDynamicAttrs(newAttrs, newTechniqueIds, oldTechniqueIds)
  1651. } catch (error) {
  1652. console.error('加载动态属性失败:', error)
  1653. message.error('加载动态属性失败')
  1654. } finally {
  1655. formLoading.value = false
  1656. }
  1657. }
  1658. // 计算属性:获取平台井工作量数据(在详情/审批模式下优先使用 platforms,不存在则使用 finishedPlatforms)
  1659. const platformWorkloadData = computed(() => {
  1660. if (!dailyReportData.value) return []
  1661. // 在详情或审批模式下
  1662. if (isDetailMode.value || isApprovalMode.value) {
  1663. // 优先使用 platforms,如果不存在则使用 finishedPlatforms
  1664. return dailyReportData.value.platforms || dailyReportData.value.finishedPlatforms || []
  1665. }
  1666. // 其他模式只使用 platforms
  1667. return dailyReportData.value.platforms || []
  1668. })
  1669. // 在 watch 监听平台井选择变化的部分附近,添加施工工艺转换函数
  1670. // 添加施工工艺数值到标签的转换函数
  1671. const convertTechniqueIdsToLabels = (techniqueIds: number[]): string[] => {
  1672. if (!techniqueIds || !Array.isArray(techniqueIds)) {
  1673. return []
  1674. }
  1675. return techniqueIds.map((id) => {
  1676. const dict = techniqueOptions.value.find((option) => option.value === id.toString())
  1677. return dict ? dict.label : id.toString()
  1678. })
  1679. }
  1680. // 监听施工工艺变化
  1681. watch(
  1682. () => formData.value.techniqueIds,
  1683. async (newTechniqueIds, oldTechniqueIds) => {
  1684. if (newTechniqueIds && newTechniqueIds.length > 0) {
  1685. await loadDynamicAttrs(newTechniqueIds, oldTechniqueIds)
  1686. // 动态属性加载完成后,更新当前平台井的 extProperty
  1687. if (currentPlatformId.value) {
  1688. updateCurrentPlatformExtProperty()
  1689. }
  1690. } else {
  1691. dynamicAttrs.value = []
  1692. formData.value.dynamicFields = {}
  1693. // 清空当前平台井的 extProperty
  1694. if (currentPlatformId.value) {
  1695. updateCurrentPlatformExtProperty()
  1696. }
  1697. }
  1698. },
  1699. { deep: true }
  1700. )
  1701. // 监听 formData.dailyFuel 变化,同步到输入变量
  1702. watch(
  1703. () => formData.value.dailyFuel,
  1704. (newVal) => {
  1705. if (newVal !== null && newVal !== undefined && newVal !== '') {
  1706. // 将数字转换为字符串显示,但不干扰输入
  1707. dailyFuelInput.value = String(newVal)
  1708. } else {
  1709. dailyFuelInput.value = ''
  1710. }
  1711. dailyFuelManuallyModified.value = true
  1712. },
  1713. { immediate: true }
  1714. )
  1715. // 监听reportFuels的变化,自动更新当日油耗
  1716. watch(
  1717. () => formData.value.reportFuels,
  1718. (newFuels) => {
  1719. // 只有在编辑模式且用户没有手动修改过当日油耗时才自动计算
  1720. if (!isReadonlyMode.value) {
  1721. calculateAndUpdateDailyFuel()
  1722. }
  1723. },
  1724. { deep: true }
  1725. )
  1726. // 更新当前平台井的 extProperty
  1727. const updateCurrentPlatformExtProperty = () => {
  1728. if (!currentPlatformId.value) return
  1729. const index = platformWellPairs.value.findIndex((item) => item.taskId === currentPlatformId.value)
  1730. if (index !== -1) {
  1731. platformWellPairs.value[index].extProperty = getCurrentExtProperties()
  1732. }
  1733. }
  1734. // 监听平台井选择变化
  1735. watch(
  1736. () => formData.value.platformId,
  1737. (newPlatformId, oldPlatformId) => {
  1738. if (newPlatformId && newPlatformId !== oldPlatformId) {
  1739. // 保存当前平台井的数据到 platformWellPairs
  1740. if (oldPlatformId) {
  1741. saveCurrentPlatformData(oldPlatformId)
  1742. }
  1743. // 加载新平台井的数据到表单
  1744. loadPlatformData(newPlatformId)
  1745. currentPlatformId.value = newPlatformId
  1746. }
  1747. }
  1748. )
  1749. // 监听动态字段变化,实时更新到 platformWellPairs
  1750. watch(
  1751. () => formData.value.dynamicFields,
  1752. (newFields) => {
  1753. if (currentPlatformId.value) {
  1754. updateCurrentPlatformExtProperty()
  1755. }
  1756. },
  1757. { deep: true }
  1758. )
  1759. // 监听施工状态变化
  1760. watch(
  1761. () => formData.value.rdStatus,
  1762. (newStatus) => {
  1763. if (currentPlatformId.value) {
  1764. const index = platformWellPairs.value.findIndex(
  1765. (item) => item.taskId === currentPlatformId.value
  1766. )
  1767. if (index !== -1) {
  1768. platformWellPairs.value[index].rdStatus = newStatus
  1769. }
  1770. }
  1771. }
  1772. )
  1773. // 保存当前平台井数据到 platformWellPairs
  1774. const saveCurrentPlatformData = (platformId: number) => {
  1775. const index = platformWellPairs.value.findIndex((item) => item.taskId === platformId)
  1776. if (index !== -1) {
  1777. platformWellPairs.value[index] = {
  1778. ...platformWellPairs.value[index],
  1779. rdStatus: formData.value.rdStatus,
  1780. techniqueIds: [...formData.value.techniqueIds],
  1781. extProperty: getCurrentExtProperties(),
  1782. repairTime: formData.value.repairTime ?? 0,
  1783. selfStopTime: formData.value.selfStopTime ?? 0,
  1784. accidentTime: formData.value.accidentTime ?? 0,
  1785. complexityTime: formData.value.complexityTime ?? 0,
  1786. rectificationTime: formData.value.rectificationTime ?? 0,
  1787. waitingStopTime: formData.value.waitingStopTime ?? 0,
  1788. partyaDesign: formData.value.partyaDesign ?? 0,
  1789. partyaPrepare: formData.value.partyaPrepare ?? 0,
  1790. partyaResource: formData.value.partyaResource ?? 0,
  1791. relocationTime: formData.value.relocationTime ?? 0,
  1792. winterBreakTime: formData.value.winterBreakTime ?? 0,
  1793. otherNptTime: formData.value.otherNptTime ?? 0,
  1794. otherNptReason: formData.value.otherNptReason || ''
  1795. }
  1796. } else {
  1797. // 如果找不到对应的平台井,添加新的记录
  1798. const platform = platformOptions.value.find((p) => p.id === platformId)
  1799. if (platform) {
  1800. platformWellPairs.value.push({
  1801. taskId: platformId,
  1802. reportId: undefined, // 新记录没有 reportId
  1803. wellName: platform.wellName,
  1804. rdStatus: formData.value.rdStatus,
  1805. techniqueIds: [...formData.value.techniqueIds],
  1806. extProperty: getCurrentExtProperties(),
  1807. repairTime: formData.value.repairTime ?? 0,
  1808. selfStopTime: formData.value.selfStopTime ?? 0,
  1809. accidentTime: formData.value.accidentTime ?? 0,
  1810. complexityTime: formData.value.complexityTime ?? 0,
  1811. rectificationTime: formData.value.rectificationTime ?? 0,
  1812. waitingStopTime: formData.value.waitingStopTime ?? 0,
  1813. partyaDesign: formData.value.partyaDesign ?? 0,
  1814. partyaPrepare: formData.value.partyaPrepare ?? 0,
  1815. partyaResource: formData.value.partyaResource ?? 0,
  1816. relocationTime: formData.value.relocationTime ?? 0,
  1817. winterBreakTime: formData.value.winterBreakTime ?? 0,
  1818. otherNptTime: formData.value.otherNptTime ?? 0,
  1819. otherNptReason: formData.value.otherNptReason || ''
  1820. })
  1821. }
  1822. }
  1823. }
  1824. // 从 platformWellPairs 加载平台井数据到表单
  1825. const loadPlatformData = (platformId: number) => {
  1826. console.log('11 :>> ', 11)
  1827. const platformData = platformWellPairs.value.find((item) => item.taskId === platformId)
  1828. if (platformData) {
  1829. // 更新表单字段
  1830. formData.value.rdStatus = platformData.rdStatus || ''
  1831. // formData.value.dailyFuel = platformData.dailyFuel ? [...platformData.dailyFuel] : []
  1832. // 将施工工艺数值转换为对应的标签
  1833. if (platformData.techniqueIds && Array.isArray(platformData.techniqueIds)) {
  1834. // 如果是数字数组,转换为字符串数组(与数据字典格式匹配)
  1835. formData.value.techniqueIds = platformData.techniqueIds.map((id) => id.toString())
  1836. } else {
  1837. formData.value.techniqueIds = platformData.techniqueIds ? [...platformData.techniqueIds] : []
  1838. }
  1839. // 在详情或审批模式下,更新 dailyFuel 为当前平台井的值
  1840. if (isDetailMode.value || isApprovalMode.value) {
  1841. // 使用平台井的 dailyFuel 值
  1842. const platformDailyFuel = platformData.dailyFuel || ''
  1843. formData.value.dailyFuel = platformDailyFuel ? formatNumber(platformDailyFuel, 2) : ''
  1844. // 同步更新输入框
  1845. dailyFuelInput.value = formData.value.dailyFuel
  1846. }
  1847. // 更新动态属性
  1848. if (platformData.extProperty && platformData.extProperty.length > 0) {
  1849. const dynamicFields: Record<string, any> = {}
  1850. platformData.extProperty.forEach((prop: any) => {
  1851. if (prop.identifier) {
  1852. dynamicFields[prop.identifier] = prop.actualValue || ''
  1853. }
  1854. })
  1855. formData.value.dynamicFields = dynamicFields
  1856. } else {
  1857. formData.value.dynamicFields = {}
  1858. }
  1859. formData.value.repairTime = platformData.repairTime ?? 0
  1860. formData.value.selfStopTime = platformData.selfStopTime ?? 0
  1861. formData.value.accidentTime = platformData.accidentTime ?? 0
  1862. formData.value.complexityTime = platformData.complexityTime ?? 0
  1863. formData.value.rectificationTime = platformData.rectificationTime ?? 0
  1864. formData.value.waitingStopTime = platformData.waitingStopTime ?? 0
  1865. formData.value.partyaDesign = platformData.partyaDesign ?? 0
  1866. formData.value.partyaPrepare = platformData.partyaPrepare ?? 0
  1867. formData.value.partyaResource = platformData.partyaResource ?? 0
  1868. formData.value.relocationTime = platformData.relocationTime ?? 0
  1869. formData.value.winterBreakTime = platformData.winterBreakTime ?? 0
  1870. formData.value.otherNptTime = platformData.otherNptTime ?? 0
  1871. formData.value.otherNptReason = platformData.otherNptReason || ''
  1872. } else {
  1873. // 如果没有找到数据,初始化默认值
  1874. formData.value.rdStatus = ''
  1875. formData.value.techniqueIds = []
  1876. formData.value.dynamicFields = {}
  1877. // 初始化其他时间字段
  1878. formData.value.repairTime = 0
  1879. formData.value.selfStopTime = 0
  1880. formData.value.accidentTime = 0
  1881. formData.value.complexityTime = 0
  1882. formData.value.rectificationTime = 0
  1883. formData.value.waitingStopTime = 0
  1884. formData.value.partyaDesign = 0
  1885. formData.value.partyaPrepare = 0
  1886. formData.value.partyaResource = 0
  1887. formData.value.relocationTime = 0
  1888. formData.value.winterBreakTime = 0
  1889. formData.value.otherNptTime = 0
  1890. formData.value.otherNptReason = ''
  1891. // 在详情或审批模式下,清空 dailyFuel
  1892. if (isDetailMode.value || isApprovalMode.value) {
  1893. formData.value.dailyFuel = ''
  1894. dailyFuelInput.value = ''
  1895. }
  1896. }
  1897. }
  1898. // 获取当前动态属性数据
  1899. const getCurrentExtProperties = () => {
  1900. return dynamicAttrs.value.map((attr) => {
  1901. return {
  1902. name: attr.name,
  1903. sort: attr.sort,
  1904. unit: attr.unit,
  1905. actualValue: formData.value.dynamicFields[attr.identifier] || '',
  1906. dataType: attr.dataType,
  1907. maxValue: attr.maxValue,
  1908. minValue: attr.minValue,
  1909. required: attr.required,
  1910. accessMode: attr.accessMode,
  1911. identifier: attr.identifier,
  1912. defaultValue: attr.defaultValue
  1913. }
  1914. })
  1915. }
  1916. // 是否显示实际进度
  1917. const showActualProgress = computed(() => {
  1918. // 在所有模式下都显示,如果有数据就显示
  1919. return dailyReportData.value?.taskProgresses && dailyReportData.value.taskProgresses.length > 0
  1920. })
  1921. // 实际进度数据
  1922. const actualProgressData = computed(() => {
  1923. if (
  1924. !dailyReportData.value?.taskProgresses ||
  1925. !Array.isArray(dailyReportData.value.taskProgresses)
  1926. ) {
  1927. return []
  1928. }
  1929. // 将 taskProgresses 转换为 el-steps 需要的格式
  1930. return dailyReportData.value.taskProgresses.map((progress: any, index: number) => {
  1931. // 格式化日期:如果只有日期部分,使用日期格式;如果有时间,使用日期时间格式
  1932. let formattedDate = ''
  1933. if (progress.createTime) {
  1934. // 判断日期格式,如果包含时间则显示完整时间,否则只显示日期
  1935. if (progress.createTime.includes(' ')) {
  1936. // 已经是完整的日期时间格式
  1937. formattedDate = progress.createTime
  1938. } else {
  1939. // 只有日期部分
  1940. formattedDate = progress.createTime
  1941. }
  1942. }
  1943. // 构建标题:日期 + 状态
  1944. const title =
  1945. formattedDate && progress.rdStatusLabel
  1946. ? `${formattedDate} ${progress.rdStatusLabel}`
  1947. : progress.rdStatusLabel || '未知状态'
  1948. return {
  1949. title: title,
  1950. description: '', // 可以根据需要添加描述信息
  1951. status: undefined, // el-steps 会自动计算状态
  1952. // 保留原始数据,便于调试
  1953. rawData: progress
  1954. }
  1955. })
  1956. })
  1957. // 初始化表单数据
  1958. const initFormData = (reportData: any) => {
  1959. // 处理附件数据格式转换
  1960. const formattedAttachments = (reportData.attachments || []).map((attachment: any) => ({
  1961. id: attachment.id,
  1962. category: attachment.category?.toLowerCase() || 'daily_report',
  1963. bizId: attachment.bizId,
  1964. type: attachment.type?.toLowerCase() || 'attachment',
  1965. filename: attachment.filename,
  1966. fileType: attachment.fileType, // 使用辅助函数获取文件类型
  1967. filePath: attachment.filePath,
  1968. fileSize: attachment.fileSize,
  1969. remark: attachment.remark || ''
  1970. }))
  1971. // 确保 techniqueIds 是字符串数组格式
  1972. let techniqueIds = []
  1973. if (reportData.techniqueIds && Array.isArray(reportData.techniqueIds)) {
  1974. techniqueIds = reportData.techniqueIds.map((id: number) => id.toString())
  1975. }
  1976. formData.value = {
  1977. ...formData.value,
  1978. id: reportData.id,
  1979. deptId: reportData.deptId,
  1980. taskId: reportData.taskId,
  1981. platformWell: reportData.platformWell,
  1982. rdStatus: reportData.rdStatus || '',
  1983. techniqueIds: techniqueIds,
  1984. dailyFuel: reportData.dailyFuel, // 当日油耗默认值
  1985. productionStatus: reportData.productionStatus || '',
  1986. nextPlan: reportData.nextPlan || '',
  1987. externalRental: reportData.externalRental || '',
  1988. malfunction: reportData.malfunction || '',
  1989. faultDowntime: reportData.faultDowntime || '',
  1990. startTime: reportData.startTime || undefined,
  1991. endTime: reportData.endTime || undefined,
  1992. companyId: reportData.companyId || '',
  1993. dynamicFields: {}, // 确保有初始值
  1994. auditStatus: reportData.auditStatus,
  1995. // 初始化附件数据
  1996. attachments: formattedAttachments,
  1997. repairTime: reportData.repairTime ?? 0,
  1998. selfStopTime: reportData.selfStopTime ?? 0,
  1999. accidentTime: reportData.accidentTime ?? 0,
  2000. complexityTime: reportData.complexityTime ?? 0,
  2001. rectificationTime: reportData.rectificationTime ?? 0,
  2002. waitingStopTime: reportData.waitingStopTime ?? 0,
  2003. partyaDesign: reportData.partyaDesign ?? 0,
  2004. partyaPrepare: reportData.partyaPrepare ?? 0,
  2005. partyaResource: reportData.partyaResource ?? 0,
  2006. relocationTime: reportData.relocationTime ?? 0,
  2007. winterBreakTime: reportData.winterBreakTime ?? 0,
  2008. otherNptTime: reportData.otherNptTime ?? 0,
  2009. otherNptReason: reportData.otherNptReason || ''
  2010. }
  2011. // 初始化审批意见数据
  2012. approvalForm.opinion = reportData.auditOpinion || reportData.opinion || ''
  2013. queryParams.deptId = reportData.companyId
  2014. // 设置时间范围选择器
  2015. if (
  2016. reportData.startTime &&
  2017. Array.isArray(reportData.startTime) &&
  2018. reportData.endTime &&
  2019. Array.isArray(reportData.endTime)
  2020. ) {
  2021. // 基于日报的施工开始日期作为基准日期
  2022. const baseDate = reportData.constructionStartDate || Date.now()
  2023. const startTime = parseTimeArrayToDate(reportData.startTime, baseDate)
  2024. const endTime = parseTimeArrayToDate(reportData.endTime, baseDate)
  2025. if (startTime && endTime) {
  2026. formData.value.timeRange = [startTime, endTime]
  2027. }
  2028. }
  2029. // 初始化平台井数据
  2030. initPlatformData(reportData)
  2031. // 初始化动态属性
  2032. initDynamicAttrs(reportData)
  2033. // 初始化设备数据
  2034. initDeviceData(reportData)
  2035. // 初始化油耗数据 - 根据模式选择数据源
  2036. if (isDetailMode.value || isApprovalMode.value) {
  2037. // 详情或审批模式:优先使用 reportedFuels
  2038. let fuelSource = reportData.reportedFuels
  2039. if (fuelSource && Array.isArray(fuelSource) && fuelSource.length > 0) {
  2040. // 处理每个油耗数据项,设置 customFuel 的默认值并确保格式正确
  2041. const processedFuels = fuelSource.map((fuel: any) => {
  2042. // 创建全新的对象,避免引用共享
  2043. const newFuel = {
  2044. ...fuel, // 使用展开运算符创建浅拷贝
  2045. // 确保每个字段都有独立的值
  2046. createTime: fuel.createTime,
  2047. updateTime: fuel.updateTime,
  2048. creator: fuel.creator,
  2049. updater: fuel.updater,
  2050. deleted: fuel.deleted,
  2051. id: fuel.id,
  2052. type: fuel.type,
  2053. reportId: fuel.reportId,
  2054. deviceId: fuel.deviceId,
  2055. deviceCode: fuel.deviceCode,
  2056. yfDeviceCode: fuel.yfDeviceCode,
  2057. deviceName: fuel.deviceName,
  2058. carId: fuel.carId,
  2059. zhbdFuel: fuel.zhbdFuel,
  2060. customFuel: null, // 初始化为null
  2061. queryDate: fuel.queryDate,
  2062. remark: fuel.remark
  2063. }
  2064. let customFuelValue
  2065. // 如果 customFuel 不为空,确保它是字符串格式并已正确格式化
  2066. if (fuel.customFuel !== null && fuel.customFuel !== undefined) {
  2067. const numValue = parseFloat(fuel.customFuel)
  2068. customFuelValue = !isNaN(numValue) ? formatNumber(numValue, 2) : '0.00'
  2069. } else {
  2070. // 如果 customFuel 为空,则使用 zhbdFuel 的值
  2071. const zhbdValue = parseFloat(fuel.zhbdFuel)
  2072. customFuelValue = !isNaN(zhbdValue) ? formatNumber(zhbdValue, 2) : '0.00'
  2073. }
  2074. return {
  2075. ...newFuel,
  2076. customFuel: customFuelValue
  2077. }
  2078. })
  2079. formData.value.reportFuels = processedFuels
  2080. // 计算初始的当日油耗
  2081. calculateTotalDailyFuel()
  2082. } else {
  2083. // 如果 reportedFuels 不存在或为空,设置空数组
  2084. formData.value.reportFuels = []
  2085. }
  2086. } else {
  2087. // 编辑模式:优先使用 reportedFuels,不存在则使用 reportFuels
  2088. let fuelSource = reportData.reportedFuels || reportData.reportFuels || []
  2089. if (fuelSource && Array.isArray(fuelSource) && fuelSource.length > 0) {
  2090. // 处理每个油耗数据项,设置 customFuel 的默认值并确保格式正确
  2091. const processedFuels = fuelSource.map((fuel: any) => {
  2092. // 创建全新的对象,避免引用共享
  2093. const newFuel = {
  2094. ...fuel, // 使用展开运算符创建浅拷贝
  2095. // 确保每个字段都有独立的值
  2096. createTime: fuel.createTime,
  2097. updateTime: fuel.updateTime,
  2098. creator: fuel.creator,
  2099. updater: fuel.updater,
  2100. deleted: fuel.deleted,
  2101. id: fuel.id,
  2102. type: fuel.type,
  2103. reportId: fuel.reportId,
  2104. deviceId: fuel.deviceId,
  2105. deviceCode: fuel.deviceCode,
  2106. yfDeviceCode: fuel.yfDeviceCode,
  2107. deviceName: fuel.deviceName,
  2108. carId: fuel.carId,
  2109. zhbdFuel: fuel.zhbdFuel,
  2110. customFuel: null, // 初始化为null
  2111. queryDate: fuel.queryDate,
  2112. remark: fuel.remark
  2113. }
  2114. let customFuelValue
  2115. // 如果 customFuel 不为空,确保它是字符串格式并已正确格式化
  2116. if (fuel.customFuel !== null && fuel.customFuel !== undefined && fuel.customFuel !== '') {
  2117. const numValue = parseFloat(fuel.customFuel)
  2118. customFuelValue = !isNaN(numValue) ? formatNumber(numValue, 2) : '0.00'
  2119. } else {
  2120. // 如果 customFuel 为空,则使用 zhbdFuel 的值
  2121. const zhbdValue = parseFloat(fuel.zhbdFuel)
  2122. customFuelValue = !isNaN(zhbdValue) ? formatNumber(zhbdValue, 2) : '0.00'
  2123. }
  2124. return {
  2125. ...newFuel,
  2126. customFuel: customFuelValue
  2127. }
  2128. })
  2129. formData.value.reportFuels = processedFuels
  2130. // 计算初始的当日油耗
  2131. calculateTotalDailyFuel()
  2132. } else {
  2133. formData.value.reportFuels = []
  2134. }
  2135. }
  2136. }
  2137. onMounted(async () => {
  2138. formLoading.value = true
  2139. try {
  2140. // 加载当前登录人所属部门
  2141. const deptId = userStore.getUser.deptId
  2142. const dept = await DeptApi.getDept(deptId)
  2143. // 查询瑞都日报详情
  2144. if (id) {
  2145. const response = await IotRdDailyReportApi.getIotRdDailyReport(id)
  2146. console.log('response :>> ', response)
  2147. dailyReportData.value = response || {}
  2148. initFormData(dailyReportData.value)
  2149. // 确保油耗数据在初始化后立即渲染
  2150. await nextTick()
  2151. // 强制更新油耗数据
  2152. if (formData.value.reportFuels && formData.value.reportFuels.length > 0) {
  2153. formData.value.reportFuels = [...formData.value.reportFuels]
  2154. }
  2155. }
  2156. } catch (error) {
  2157. console.error('初始化数据失败:', error)
  2158. message.error('数据加载失败')
  2159. } finally {
  2160. formLoading.value = false
  2161. }
  2162. })
  2163. // 详细 审批 平台井 获取工作量列配置
  2164. const getWorkloadColumns = () => {
  2165. const dataSource = platformWorkloadData.value
  2166. if (!dataSource || dataSource.length === 0) return []
  2167. const columns = []
  2168. const addedIdentifiers = new Set()
  2169. dataSource.forEach((platform) => {
  2170. if (platform.extProperty && Array.isArray(platform.extProperty)) {
  2171. platform.extProperty.forEach((extProp) => {
  2172. if (!addedIdentifiers.has(extProp.identifier)) {
  2173. columns.push({
  2174. key: extProp.identifier,
  2175. identifier: extProp.identifier,
  2176. label: `${extProp.name}(${extProp.unit})`
  2177. })
  2178. addedIdentifiers.add(extProp.identifier)
  2179. }
  2180. })
  2181. }
  2182. })
  2183. return columns
  2184. }
  2185. // 添加一个深拷贝油耗数据的辅助函数
  2186. const deepCopyFuelData = (fuelData: any) => {
  2187. if (!fuelData) return null
  2188. return {
  2189. createTime: fuelData.createTime,
  2190. updateTime: fuelData.updateTime,
  2191. creator: fuelData.creator,
  2192. updater: fuelData.updater,
  2193. deleted: fuelData.deleted,
  2194. id: fuelData.id,
  2195. type: fuelData.type,
  2196. reportId: fuelData.reportId,
  2197. deviceId: fuelData.deviceId,
  2198. deviceCode: fuelData.deviceCode,
  2199. yfDeviceCode: fuelData.yfDeviceCode,
  2200. deviceName: fuelData.deviceName,
  2201. carId: fuelData.carId,
  2202. zhbdFuel: fuelData.zhbdFuel,
  2203. customFuel: fuelData.customFuel,
  2204. queryDate: fuelData.queryDate,
  2205. remark: fuelData.remark
  2206. }
  2207. }
  2208. // 强制刷新表格
  2209. const refreshFuelTable = () => {
  2210. fuelTableKey.value += 1
  2211. }
  2212. // 计算当日油耗的默认值
  2213. const calculateDailyFuel = (reportData: any) => {
  2214. let dailyFuelValue = 0
  2215. // 如果有接口返回的dailyFuel,优先使用
  2216. if (reportData.dailyFuel !== null && reportData.dailyFuel !== undefined) {
  2217. dailyFuelValue = Number(reportData.dailyFuel)
  2218. }
  2219. // 如果reportFuels有数据,累加zhbdFuel的值
  2220. if (
  2221. reportData.reportFuels &&
  2222. Array.isArray(reportData.reportFuels) &&
  2223. reportData.reportFuels.length > 0
  2224. ) {
  2225. const totalZhbdFuel = reportData.reportFuels.reduce((sum: number, item: any) => {
  2226. const zhbdFuelValue = Number(item.zhbdFuel) || 0
  2227. return sum + zhbdFuelValue
  2228. }, 0)
  2229. // 只有当累计值大于0时才覆盖原有的dailyFuel值
  2230. if (totalZhbdFuel > 0) {
  2231. dailyFuelValue = totalZhbdFuel
  2232. }
  2233. }
  2234. return formatNumber(dailyFuelValue, 2)
  2235. }
  2236. // 处理当日油耗输入
  2237. const handleDailyFuelInput = () => {
  2238. // 确保保留两位小数
  2239. if (formData.value.dailyFuel !== '') {
  2240. const numValue = parseFloat(formData.value.dailyFuel)
  2241. if (!isNaN(numValue)) {
  2242. formData.value.dailyFuel = numValue.toFixed(2)
  2243. }
  2244. }
  2245. }
  2246. // 添加数字格式化函数
  2247. const formatNumber = (value: any, decimalPlaces: number = 2) => {
  2248. if (value === null || value === undefined || value === '' || value === 'NaN') {
  2249. return '0.00'
  2250. }
  2251. // 如果已经是字符串,尝试转换为数字
  2252. if (typeof value === 'string') {
  2253. // 移除可能的非数字字符
  2254. const cleaned = value.replace(/[^\d.-]/g, '')
  2255. const num = Number(cleaned)
  2256. if (isNaN(num)) {
  2257. return '0.00'
  2258. }
  2259. return num.toFixed(decimalPlaces)
  2260. }
  2261. const num = Number(value)
  2262. if (isNaN(num)) {
  2263. return '0.00'
  2264. }
  2265. return num.toFixed(decimalPlaces)
  2266. }
  2267. // 新增:计算并更新当日油耗的方法
  2268. const calculateAndUpdateDailyFuel = () => {
  2269. if (!formData.value.reportFuels || !Array.isArray(formData.value.reportFuels)) {
  2270. return
  2271. }
  2272. // 计算所有车辆的实际油耗总和
  2273. const totalCustomFuel = formData.value.reportFuels.reduce((sum: number, item: any) => {
  2274. const customFuelValue = Number(item.customFuel) || 0
  2275. return sum + customFuelValue
  2276. }, 0)
  2277. // 只有当累计的实际油耗大于0时,才更新当日油耗
  2278. if (totalCustomFuel > 0 && !isReadonlyMode.value) {
  2279. formData.value.dailyFuel = formatNumber(totalCustomFuel, 2)
  2280. // 更新输入框显示
  2281. if (dailyFuelInput.value) {
  2282. dailyFuelInput.value = formData.value.dailyFuel
  2283. }
  2284. }
  2285. }
  2286. // 添加计算属性:获取油耗数据显示数据源
  2287. const fuelConsumptionData = computed(() => {
  2288. // 所有模式都统一使用 formData.value.reportFuels 作为数据源
  2289. // 因为 formData.value.reportFuels 在 initFormData 中已经正确处理了所有情况
  2290. return formData.value.reportFuels || []
  2291. })
  2292. // 判断是否显示油耗信息区域
  2293. const showFuelConsumption = computed(() => {
  2294. const data = fuelConsumptionData.value
  2295. return data && Array.isArray(data) && data.length > 0
  2296. })
  2297. // 重新计算当日油耗const formatNumber
  2298. const calculateTotalDailyFuel = () => {
  2299. if (!formData.value.reportFuels || !Array.isArray(formData.value.reportFuels)) {
  2300. return
  2301. }
  2302. // 计算所有车辆的实际油耗总和
  2303. const totalCustomFuel = formData.value.reportFuels.reduce((sum: number, item: any) => {
  2304. const customFuelValue = Number(item.customFuel) || 0
  2305. return sum + customFuelValue
  2306. }, 0)
  2307. // 只有当累计的实际油耗大于0时,才自动更新当日油耗
  2308. if (totalCustomFuel > 0 && !isReadonlyMode.value) {
  2309. formData.value.dailyFuel = formatNumber(totalCustomFuel, 2)
  2310. }
  2311. }
  2312. // 处理实际油耗变化
  2313. const handleCustomFuelChange = (fuelItem: any) => {
  2314. // 获取当前输入的值
  2315. let value = fuelItem.customFuel
  2316. // 如果输入为空,则重置为zhbdFuel的值
  2317. if (value === '' || value === null || value === undefined) {
  2318. fuelItem.customFuel = formatNumber(fuelItem.zhbdFuel, 2)
  2319. return
  2320. }
  2321. // 移除非数字字符(除了小数点)
  2322. const cleaned = value.toString().replace(/[^\d.]/g, '')
  2323. // 确保只有一个小数点
  2324. const parts = cleaned.split('.')
  2325. let formattedValue = cleaned
  2326. if (parts.length > 2) {
  2327. formattedValue = parts[0] + '.' + parts.slice(1).join('')
  2328. }
  2329. // 转换为数字并格式化为两位小数
  2330. const numValue = parseFloat(formattedValue)
  2331. if (!isNaN(numValue)) {
  2332. // 限制到两位小数
  2333. fuelItem.customFuel = formatNumber(numValue, 2)
  2334. } else {
  2335. // 如果转换失败,设置为0.00
  2336. fuelItem.customFuel = '0.00'
  2337. }
  2338. // 同步更新 formData.reportFuels 中的数据
  2339. // 确保通过 deviceId 正确找到并更新对应的记录
  2340. if (formData.value.reportFuels && fuelItem.deviceId) {
  2341. const index = formData.value.reportFuels.findIndex(
  2342. (item) => item.deviceId === fuelItem.deviceId
  2343. )
  2344. if (index !== -1) {
  2345. // 创建新对象,避免引用问题
  2346. const updatedFuel = {
  2347. ...formData.value.reportFuels[index],
  2348. customFuel: fuelItem.customFuel
  2349. }
  2350. // 使用 Vue.set 或直接赋值确保响应性
  2351. formData.value.reportFuels[index] = updatedFuel
  2352. // 强制刷新表格
  2353. fuelTableKey.value += 1
  2354. }
  2355. }
  2356. // 手动触发当日油耗的重新计算
  2357. calculateAndUpdateDailyFuel()
  2358. }
  2359. // 详情 审批 平台井 获取工作量值
  2360. const getWorkloadValue = (platform, identifier) => {
  2361. if (!platform.extProperty) return ''
  2362. const prop = platform.extProperty.find((item) => item.identifier === identifier)
  2363. return prop ? prop.actualValue || '' : ''
  2364. }
  2365. /** 审批操作 */
  2366. const handleApprove = async (action: 'pass' | 'reject') => {
  2367. // 只有在审批模式下才执行审批操作
  2368. if (!isApprovalMode.value) {
  2369. message.warning('当前不是审批模式')
  2370. return
  2371. }
  2372. try {
  2373. // 验证审批表单(如果需要)
  2374. // await approvalFormRef.value.validate()
  2375. formLoading.value = true
  2376. // 处理时间范围数据
  2377. if (formData.value.timeRange && formData.value.timeRange.length === 2) {
  2378. // 将时间范围转换为 LocalTime 格式的字符串
  2379. const startDate = dayjs(formData.value.timeRange[0])
  2380. const endDate = dayjs(formData.value.timeRange[1])
  2381. // 格式化为 HH:mm:ss 字符串,后端会自动转换为 LocalTime
  2382. formData.value.startTime = startDate.format('HH:mm:ss')
  2383. formData.value.endTime = endDate.format('HH:mm:ss')
  2384. }
  2385. // 构建审批数据,包含审批意见
  2386. const approveData = {
  2387. ...formData.value,
  2388. id: Number(id),
  2389. opinion: approvalForm.opinion,
  2390. auditStatus: action === 'pass' ? 20 : 30
  2391. }
  2392. // 这里可以调用审批API
  2393. if (action === 'pass') {
  2394. // 审批通过逻辑
  2395. await IotRdDailyReportApi.approveRdDailyReport(approveData)
  2396. message.success('审批通过')
  2397. } else {
  2398. // 审批驳回逻辑
  2399. await IotRdDailyReportApi.approveRdDailyReport(approveData)
  2400. message.success('审批驳回')
  2401. }
  2402. close()
  2403. } catch (error) {
  2404. console.error('审批操作失败:', error)
  2405. message.error('审批操作失败')
  2406. } finally {
  2407. formLoading.value = false
  2408. }
  2409. }
  2410. </script>
  2411. <style scoped>
  2412. .info-table {
  2413. border: 1px solid #e0e0e0;
  2414. border-radius: 4px;
  2415. overflow: hidden;
  2416. }
  2417. .table-row {
  2418. display: flex;
  2419. border-bottom: 1px solid #e0e0e0;
  2420. }
  2421. .table-row:last-child {
  2422. border-bottom: none;
  2423. }
  2424. .table-cell {
  2425. flex: 1;
  2426. border-right: 1px solid #e0e0e0;
  2427. padding: 12px 8px;
  2428. min-height: 44px;
  2429. display: flex;
  2430. align-items: center;
  2431. }
  2432. .table-cell:last-child {
  2433. border-right: none;
  2434. }
  2435. .table-cell.full-width {
  2436. flex: 1;
  2437. border-right: none;
  2438. }
  2439. .cell-content {
  2440. display: flex;
  2441. align-items: center;
  2442. width: 100%;
  2443. }
  2444. .cell-label {
  2445. font-weight: 500;
  2446. /* 统一字体大小为 14px(Element 表单默认) */
  2447. font-size: 14px;
  2448. color: #606266;
  2449. min-width: 80px;
  2450. margin-right: 8px;
  2451. flex-shrink: 0;
  2452. }
  2453. .cell-value {
  2454. /* 统一字体大小为 14px(Element 输入框默认) */
  2455. font-size: 14px;
  2456. color: #303133;
  2457. /* 统一行高为 1.5(Element 组件默认行高) */
  2458. line-height: 1.5;
  2459. flex: 1;
  2460. word-break: break-all;
  2461. }
  2462. /* 响应式设计 */
  2463. @media (max-width: 768px) {
  2464. .table-row {
  2465. flex-direction: column;
  2466. }
  2467. .table-cell {
  2468. border-right: none;
  2469. border-bottom: 1px solid #e0e0e0;
  2470. }
  2471. .table-cell:last-child {
  2472. border-bottom: none;
  2473. }
  2474. }
  2475. .daily-report-title {
  2476. text-align: center;
  2477. margin: 20px 0;
  2478. padding: 10px;
  2479. border-bottom: 2px solid #409eff;
  2480. }
  2481. .daily-report-title h2 {
  2482. margin: 0;
  2483. color: #303133;
  2484. font-size: 16px;
  2485. font-weight: bold;
  2486. }
  2487. /* 为第二、三部分增加左右留白 */
  2488. .section-padding {
  2489. padding-left: 0px;
  2490. padding-right: 40px;
  2491. }
  2492. .project-info-section {
  2493. margin: 20px 0;
  2494. padding: 20px;
  2495. background-color: #f8f9fa;
  2496. border-radius: 4px;
  2497. border: 1px solid #e9ecef;
  2498. }
  2499. .info-row {
  2500. padding: 12px 0;
  2501. border-bottom: 1px solid #e9ecef;
  2502. }
  2503. .info-row:last-child {
  2504. border-bottom: none;
  2505. }
  2506. .info-label {
  2507. font-weight: bold;
  2508. color: #495057;
  2509. margin-right: 8px;
  2510. }
  2511. .info-value {
  2512. color: #212529;
  2513. }
  2514. :deep(.el-textarea .el-textarea__inner) {
  2515. min-height: 80px;
  2516. }
  2517. /* 确保表单label不换行 */
  2518. :deep(.el-form-item__label) {
  2519. white-space: nowrap;
  2520. text-overflow: ellipsis;
  2521. overflow: hidden;
  2522. }
  2523. /* 甲方字段:单行显示+超出省略 */
  2524. .single-line-ellipsis {
  2525. /* 强制文本单行显示 */
  2526. white-space: nowrap;
  2527. /* 超出容器部分隐藏 */
  2528. overflow: hidden;
  2529. /* 超出部分显示省略号 */
  2530. text-overflow: ellipsis;
  2531. /* 避免文本被截断(可选,根据需求调整) */
  2532. word-break: normal;
  2533. }
  2534. /* 设备配置字段:换行缩进(与首行对齐) */
  2535. .indent-multiline {
  2536. /* 首行及换行后缩进 2em(与 label 宽度匹配,可根据需求调整) */
  2537. text-indent: 0em;
  2538. /* 允许长文本换行(覆盖原有 cell-value 的 break-all,确保中文换行正常) */
  2539. word-break: break-word;
  2540. /* 保证换行后文本正常显示(可选,清除可能的 nowrap 影响) */
  2541. white-space: normal;
  2542. }
  2543. /* 添加审批模式下的样式 */
  2544. .approval-notice {
  2545. margin-top: 10px;
  2546. }
  2547. /* 审批模式下表单字段的只读样式 */
  2548. :deep(.el-form-item.is-disabled .el-input__inner),
  2549. :deep(.el-form-item.is-disabled .el-textarea__inner) {
  2550. background-color: #f5f7fa;
  2551. border-color: #e4e7ed;
  2552. color: #c0c4cc;
  2553. cursor: not-allowed;
  2554. }
  2555. :deep(.el-form-item.is-disabled .el-select .el-input__inner) {
  2556. background-color: #f5f7fa;
  2557. border-color: #e4e7ed;
  2558. color: #c0c4cc;
  2559. cursor: not-allowed;
  2560. }
  2561. :deep(.el-form-item.is-disabled .el-date-editor .el-input__inner) {
  2562. background-color: #f5f7fa;
  2563. border-color: #e4e7ed;
  2564. color: #c0c4cc;
  2565. cursor: not-allowed;
  2566. }
  2567. /* 只读模式下表单字段的样式 */
  2568. :deep(.el-form-item.is-disabled .el-input__inner),
  2569. :deep(.el-form-item.is-disabled .el-textarea__inner),
  2570. :deep(.el-form-item.is-disabled .el-select .el-input__inner),
  2571. :deep(.el-form-item.is-disabled .el-date-editor .el-input__inner) {
  2572. background-color: #f5f7fa;
  2573. border-color: #e4e7ed;
  2574. color: #606266; /* 保持文字可读性 */
  2575. cursor: not-allowed;
  2576. }
  2577. /* 详情模式下的特殊样式 */
  2578. .detail-mode .cell-value {
  2579. color: #303133;
  2580. font-weight: normal;
  2581. }
  2582. /* 添加审批意见区域的样式 */
  2583. .approval-opinion-section {
  2584. margin-top: 20px;
  2585. border-top: 2px solid #f0f0f0;
  2586. padding-top: 20px;
  2587. }
  2588. /* 审批意见文本域样式 */
  2589. :deep(.approval-opinion .el-textarea__inner) {
  2590. min-height: 100px;
  2591. resize: vertical;
  2592. }
  2593. /* 审批意见标签样式 */
  2594. :deep(.approval-opinion .el-form-item__label) {
  2595. font-weight: bold;
  2596. color: #606266;
  2597. }
  2598. /* 附件列表样式 */
  2599. .attachment-list {
  2600. width: 100%;
  2601. margin-top: 5px;
  2602. border: 1px solid #e0e0e0;
  2603. border-radius: 4px;
  2604. padding: 10px;
  2605. background-color: #fafafa;
  2606. box-sizing: border-box;
  2607. }
  2608. .attachment-item {
  2609. display: flex;
  2610. justify-content: space-between;
  2611. align-items: center;
  2612. padding: 8px 12px;
  2613. border-bottom: 1px solid #f0f0f0;
  2614. }
  2615. .attachment-item:last-child {
  2616. border-bottom: none;
  2617. }
  2618. .attachment-name {
  2619. flex: 1;
  2620. color: #606266;
  2621. font-size: 11px;
  2622. }
  2623. .no-attachment {
  2624. color: #909399;
  2625. font-style: italic;
  2626. margin-top: 5px;
  2627. padding: 10px;
  2628. }
  2629. /* 附件名称链接样式 */
  2630. .attachment-name {
  2631. color: #409eff;
  2632. text-decoration: underline;
  2633. cursor: pointer;
  2634. }
  2635. .attachment-name:hover {
  2636. color: #66b1ff;
  2637. }
  2638. /* 只读模式下的设备显示样式 */
  2639. .device-display-readonly {
  2640. color: #606266;
  2641. font-size: 11px;
  2642. line-height: 1.5;
  2643. background-color: #f5f7fa;
  2644. padding: 8px 12px;
  2645. border-radius: 4px;
  2646. border: 1px solid #e4e7ed;
  2647. display: inline-block;
  2648. min-width: 200px;
  2649. }
  2650. .no-device {
  2651. margin-left: 10px;
  2652. color: #909399;
  2653. font-style: italic;
  2654. }
  2655. /* 设备选择对话框样式 */
  2656. .transfer-container {
  2657. text-align: center;
  2658. padding: 0px;
  2659. }
  2660. .transfer-component {
  2661. width: 100%;
  2662. min-width: 600px;
  2663. }
  2664. :deep(.el-transfer-panel) {
  2665. width: 40% !important;
  2666. }
  2667. :deep(.el-transfer-panel__item) {
  2668. display: flex !important;
  2669. align-items: center !important;
  2670. height: 32px !important;
  2671. line-height: 32px !important;
  2672. padding: 0 8px !important;
  2673. margin: 0 !important;
  2674. white-space: nowrap;
  2675. overflow: hidden;
  2676. text-overflow: ellipsis;
  2677. }
  2678. .transfer-option-text {
  2679. display: inline-block;
  2680. max-width: 100%;
  2681. }
  2682. :deep(.el-transfer-panel__list) {
  2683. width: 100% !important;
  2684. }
  2685. .device-display-container {
  2686. /* 与其他文本域保持相同的宽度和样式 */
  2687. display: inline-block;
  2688. width: 100%; /* 减去按钮宽度 */
  2689. min-height: 32px;
  2690. line-height: 32px;
  2691. padding: 0 12px;
  2692. margin-left: 0px;
  2693. border-radius: 4px;
  2694. border: 1px solid #e4e7ed;
  2695. background-color: #fff;
  2696. font-size: 11px;
  2697. /* 文本溢出处理 */
  2698. white-space: nowrap;
  2699. overflow: hidden;
  2700. text-overflow: ellipsis;
  2701. }
  2702. /* 只读模式下的设备显示样式 */
  2703. :deep(.is-disabled) .device-display-container {
  2704. background-color: #f5f7fa;
  2705. color: #606266;
  2706. cursor: not-allowed;
  2707. }
  2708. /* 附件容器样式调整 */
  2709. .attachment-container {
  2710. /* 与其他文本域保持相同的宽度和边距 */
  2711. width: 100%;
  2712. margin-top: 10px;
  2713. }
  2714. /* 未选择设备字段的只读样式 */
  2715. :deep(.unselected-device .el-textarea__inner) {
  2716. background-color: #f5f7fa;
  2717. border-color: #e4e7ed;
  2718. color: #909399;
  2719. cursor: not-allowed;
  2720. resize: none;
  2721. }
  2722. /* 平台井工作量区域专用样式 */
  2723. .platform-workload-section {
  2724. padding-left: 0px;
  2725. padding-right: 0px; /* 去掉右侧间距 */
  2726. }
  2727. /* 表格样式优化 */
  2728. .platform-workload-el-table {
  2729. width: 100%;
  2730. }
  2731. /* 表头不换行 */
  2732. :deep(.platform-workload-el-table .el-table__header-wrapper th) {
  2733. white-space: nowrap;
  2734. text-overflow: ellipsis;
  2735. overflow: hidden;
  2736. }
  2737. /* 单元格内容不换行 */
  2738. :deep(.platform-workload-el-table .el-table__body-wrapper td) {
  2739. white-space: nowrap;
  2740. text-overflow: ellipsis;
  2741. overflow: hidden;
  2742. }
  2743. /* 强制设置表头宽度为100% */
  2744. :deep(.platform-workload-el-table .el-table__header) {
  2745. width: 100% !important;
  2746. min-width: 100% !important;
  2747. }
  2748. /* 强制设置表格主体宽度为100% */
  2749. :deep(.platform-workload-el-table .el-table__body) {
  2750. width: 100% !important;
  2751. min-width: 100% !important;
  2752. }
  2753. /* 确保表格填满容器 */
  2754. :deep(.platform-workload-el-table .el-table) {
  2755. width: 100% !important;
  2756. }
  2757. /* 表格容器填满父容器 */
  2758. .platform-workload-table {
  2759. width: 100%;
  2760. }
  2761. /* 油耗信息区域样式 */
  2762. .fuel-consumption-section {
  2763. padding-left: 0px;
  2764. padding-right: 0px;
  2765. margin-top: 20px;
  2766. }
  2767. /* 强制表格宽度为100% */
  2768. :deep(.fuel-consumption-el-table) {
  2769. width: 100% !important;
  2770. min-width: 100% !important;
  2771. }
  2772. /* 确保表格内部元素也充满宽度 */
  2773. :deep(.fuel-consumption-el-table .el-table) {
  2774. width: 100% !important;
  2775. min-width: 100% !important;
  2776. }
  2777. /* 强制设置表头宽度为100% */
  2778. :deep(.fuel-consumption-el-table .el-table__header) {
  2779. width: 100% !important;
  2780. min-width: 100% !important;
  2781. }
  2782. /* 表头不换行 */
  2783. :deep(.fuel-consumption-el-table .el-table__header-wrapper th) {
  2784. white-space: nowrap;
  2785. text-overflow: ellipsis;
  2786. overflow: hidden;
  2787. background-color: #f5f7fa;
  2788. color: #606266;
  2789. font-weight: bold;
  2790. }
  2791. /* 强制设置表格主体宽度为100% */
  2792. :deep(.fuel-consumption-el-table .el-table__body) {
  2793. width: 100% !important;
  2794. min-width: 100% !important;
  2795. }
  2796. /* 表头和表体都设置为100%宽度 */
  2797. :deep(.fuel-consumption-el-table .el-table__header-wrapper),
  2798. :deep(.fuel-consumption-el-table .el-table__body-wrapper) {
  2799. width: 100% !important;
  2800. }
  2801. /* 单元格内容居中 */
  2802. :deep(.fuel-consumption-el-table .el-table__body-wrapper td) {
  2803. text-align: center;
  2804. white-space: nowrap;
  2805. text-overflow: ellipsis;
  2806. overflow: hidden;
  2807. }
  2808. /* 实际油耗输入框样式 */
  2809. :deep(.fuel-consumption-el-table .el-input__inner) {
  2810. text-align: center;
  2811. padding: 0 5px;
  2812. height: 28px;
  2813. line-height: 28px;
  2814. }
  2815. /* 只读模式下的数值显示 */
  2816. .fuel-consumption-el-table .readonly-value {
  2817. color: #606266;
  2818. font-weight: normal;
  2819. }
  2820. /* 强制设置表格宽度为100% */
  2821. :deep(.fuel-consumption-el-table .el-table) {
  2822. width: 100% !important;
  2823. }
  2824. /* 表格容器填满父容器 */
  2825. .fuel-consumption-table {
  2826. width: 100%;
  2827. overflow-x: auto;
  2828. }
  2829. /* 响应式调整 */
  2830. @media (max-width: 768px) {
  2831. .fuel-consumption-section {
  2832. padding-left: 10px;
  2833. padding-right: 10px;
  2834. }
  2835. :deep(.fuel-consumption-el-table .el-table__header-wrapper th),
  2836. :deep(.fuel-consumption-el-table .el-table__body-wrapper td) {
  2837. padding: 8px 5px;
  2838. font-size: 12px;
  2839. }
  2840. }
  2841. /* 当日油耗输入框样式优化 */
  2842. :deep(.el-form-item .el-input-number) {
  2843. width: 100%;
  2844. }
  2845. /* 当日油耗字段在只读模式下的样式 */
  2846. :deep(.is-disabled .el-input__inner[type='number']) {
  2847. background-color: #f5f7fa;
  2848. border-color: #e4e7ed;
  2849. color: #606266;
  2850. cursor: not-allowed;
  2851. }
  2852. /* 实际进度区域样式 */
  2853. .actual-progress-container {
  2854. margin-top: 10px;
  2855. padding: 20px;
  2856. border: 1px solid #e6e6e6;
  2857. border-radius: 8px;
  2858. background-color: #fafafa;
  2859. }
  2860. .progress-title {
  2861. margin: 0 0 16px 0;
  2862. font-size: 16px;
  2863. font-weight: bold;
  2864. color: #67c23a; /* 实际进度使用绿色标题 */
  2865. }
  2866. .no-progress-data {
  2867. text-align: center;
  2868. padding: 20px 0;
  2869. color: #909399;
  2870. font-style: italic;
  2871. }
  2872. /* 调整步骤组件样式以适应水平布局 */
  2873. :deep(.actual-progress-container .el-steps--horizontal) {
  2874. flex-wrap: nowrap;
  2875. overflow-x: auto;
  2876. padding-bottom: 10px;
  2877. }
  2878. :deep(.actual-progress-container .el-step__title) {
  2879. font-size: 12px;
  2880. line-height: 1.4;
  2881. white-space: nowrap;
  2882. overflow: hidden;
  2883. text-overflow: ellipsis;
  2884. max-width: 120px;
  2885. }
  2886. /* 确保步骤容器有足够空间 */
  2887. :deep(.actual-progress-container .el-step) {
  2888. flex-basis: auto;
  2889. flex-shrink: 0;
  2890. }
  2891. /* 响应式调整 */
  2892. @media (max-width: 768px) {
  2893. :deep(.actual-progress-container .el-step__title) {
  2894. font-size: 11px;
  2895. max-width: 100px;
  2896. }
  2897. .actual-progress-container {
  2898. padding: 15px;
  2899. }
  2900. }
  2901. </style>