FillDailyReportForm.vue 100 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270
  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 && formData.auditStatus !== 20"
  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="isReadonlyMode && formData.auditStatus !== 20"
  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="isReadonlyMode && formData.auditStatus !== 20"
  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 } = 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. }
  1451. console.log('baseSubmitData:', baseSubmitData)
  1452. // 删除不需要复制的字段
  1453. const {
  1454. id: currentId,
  1455. platformId,
  1456. platformWell,
  1457. taskId,
  1458. rdStatus,
  1459. techniqueIds,
  1460. extProperty,
  1461. ...baseData
  1462. } = baseSubmitData
  1463. const submitDatas: any[] = []
  1464. if (dailyReportData.value.platformWell === 1 && platformWellPairs.value.length > 0) {
  1465. // 平台井模式:处理所有平台井数据
  1466. platformWellPairs.value.forEach((pair) => {
  1467. console.log('pair:', pair)
  1468. // 复制基础数据
  1469. const platformData: any = { ...baseData }
  1470. // 设置平台井特定字段
  1471. platformData.id = pair.reportId // 使用 platformWellPairs 中的 reportId
  1472. platformData.platformId = pair.taskId // 使用 platformWellPairs 中的 taskId
  1473. platformData.rdStatus = pair.rdStatus || ''
  1474. platformData.techniqueIds = pair.techniqueIds || []
  1475. platformData.extProperty = pair.extProperty || []
  1476. platformData.repairTime = pair.repairTime || 0
  1477. platformData.selfStopTime = pair.selfStopTime || 0
  1478. platformData.accidentTime = pair.accidentTime || 0
  1479. platformData.complexityTime = pair.complexityTime || 0
  1480. platformData.rectificationTime = pair.rectificationTime || 0
  1481. platformData.waitingStopTime = pair.waitingStopTime || 0
  1482. platformData.partyaDesign = pair.partyaDesign || 0
  1483. platformData.partyaPrepare = pair.partyaPrepare || 0
  1484. platformData.partyaResource = pair.partyaResource || 0
  1485. platformData.relocationTime = pair.relocationTime || 0
  1486. platformData.winterBreakTime = pair.winterBreakTime || 0
  1487. platformData.otherNptTime = pair.otherNptTime || 0
  1488. platformData.otherNptReason = pair.otherNptReason || ''
  1489. // 处理附件:复制并修改 bizId 为当前 pair 的 reportId
  1490. platformData.attachments = (baseData.attachments || []).map((attachment) => ({
  1491. ...attachment, // 深拷贝单个附件
  1492. bizId: pair.reportId // 替换 bizId 为当前平台井的 reportId
  1493. }))
  1494. // 车辆油耗:复制并修改 reportId 为当前 pair 的 reportId
  1495. platformData.reportFuels = (baseData.reportFuels || []).map((reportFuel) => ({
  1496. ...reportFuel, // 深拷贝单个油耗
  1497. reportId: pair.reportId // 替换 reportId 为当前平台井的 reportId
  1498. }))
  1499. // 重新构建 dynamicFields(如果需要)
  1500. const dynamicFields = {}
  1501. if (platformData.extProperty && platformData.extProperty.length > 0) {
  1502. platformData.extProperty.forEach((prop) => {
  1503. if (prop.identifier) {
  1504. dynamicFields[prop.identifier] = prop.actualValue || ''
  1505. }
  1506. })
  1507. }
  1508. platformData.dynamicFields = dynamicFields
  1509. submitDatas.push(platformData)
  1510. })
  1511. console.log('平台井模式提交数据:', JSON.stringify(submitDatas, null, 2))
  1512. } else {
  1513. // 非平台井模式:只提交当前数据
  1514. submitDatas.push(baseSubmitData)
  1515. }
  1516. // 提交请求
  1517. formLoading.value = true
  1518. try {
  1519. // 调用更新接口
  1520. await IotRdDailyReportApi.saveBatch(submitDatas)
  1521. message.success(t('common.updateSuccess'))
  1522. close()
  1523. // 发送操作成功的事件
  1524. emit('success')
  1525. } catch (error) {
  1526. console.error('提交失败:', error)
  1527. } finally {
  1528. formLoading.value = false
  1529. }
  1530. }
  1531. /** 重置表单 */
  1532. const resetForm = () => {
  1533. formRef.value?.resetFields()
  1534. }
  1535. // 初始化动态属性
  1536. const initDynamicAttrs = (reportData: any) => {
  1537. if (reportData.dailyReportAttrs && reportData.dailyReportAttrs.length > 0) {
  1538. dynamicAttrs.value = reportData.dailyReportAttrs
  1539. // 初始化动态字段的值
  1540. const initialDynamicFields: Record<string, any> = {}
  1541. // 优先从 extProperty 中获取实际值(编辑时)
  1542. if (reportData.extProperty && reportData.extProperty.length > 0) {
  1543. reportData.extProperty.forEach((extProp: any) => {
  1544. if (extProp.identifier) {
  1545. initialDynamicFields[extProp.identifier] = extProp.actualValue || ''
  1546. }
  1547. })
  1548. }
  1549. reportData.dailyReportAttrs.forEach((attr: any) => {
  1550. if (!initialDynamicFields.hasOwnProperty(attr.identifier)) {
  1551. // 优先使用实际值,如果没有则使用默认值
  1552. const value =
  1553. attr.extProperty &&
  1554. attr.extProperty.actualValue !== undefined &&
  1555. attr.extProperty.actualValue !== null &&
  1556. attr.extProperty.actualValue !== ''
  1557. ? attr.extProperty.actualValue
  1558. : attr.defaultValue || attr.extProperty?.defaultValue || ''
  1559. initialDynamicFields[attr.identifier] = value
  1560. }
  1561. })
  1562. formData.value.dynamicFields = initialDynamicFields
  1563. }
  1564. }
  1565. // 获取动态字段的验证规则
  1566. const getDynamicAttrRules = (attr: any) => {
  1567. const rules = []
  1568. if (attr.required === 1) {
  1569. rules.push({
  1570. required: true,
  1571. message: `${attr.name}不能为空`,
  1572. trigger: 'blur'
  1573. })
  1574. }
  1575. // 数字类型验证
  1576. if (attr.dataType === 'double') {
  1577. rules.push({
  1578. validator: (rule: any, value: any, callback: any) => {
  1579. if (value === '' || value === null || value === undefined) {
  1580. callback()
  1581. return
  1582. }
  1583. const numValue = Number(value)
  1584. if (isNaN(numValue)) {
  1585. callback(new Error(`${attr.name}必须是数字`))
  1586. } else if (attr.minValue && numValue < Number(attr.minValue)) {
  1587. callback(new Error(`${attr.name}不能小于${attr.minValue}`))
  1588. } else if (attr.maxValue && numValue > Number(attr.maxValue)) {
  1589. callback(new Error(`${attr.name}不能大于${attr.maxValue}`))
  1590. } else {
  1591. callback()
  1592. }
  1593. },
  1594. trigger: 'blur'
  1595. })
  1596. }
  1597. return rules
  1598. }
  1599. // 更新动态属性(处理交集、新增和删除)
  1600. const updateDynamicAttrs = async (
  1601. newAttrs: any[],
  1602. newTechniqueIds: string[],
  1603. oldTechniqueIds?: string[]
  1604. ) => {
  1605. const oldAttrs = [...dynamicAttrs.value]
  1606. const oldDynamicFields = { ...formData.value.dynamicFields }
  1607. // 计算需要保留的字段(交集)
  1608. const commonAttrs = oldAttrs.filter((oldAttr) =>
  1609. newAttrs.some((newAttr) => newAttr.identifier === oldAttr.identifier)
  1610. )
  1611. // 计算需要新增的字段
  1612. const addedAttrs = newAttrs.filter(
  1613. (newAttr) => !oldAttrs.some((oldAttr) => oldAttr.identifier === newAttr.identifier)
  1614. )
  1615. // 计算需要删除的字段
  1616. const removedAttrs = oldAttrs.filter(
  1617. (oldAttr) => !newAttrs.some((newAttr) => newAttr.identifier === oldAttr.identifier)
  1618. )
  1619. // 构建新的动态属性数组
  1620. const updatedAttrs = [...commonAttrs, ...addedAttrs]
  1621. // 构建新的动态字段对象
  1622. const updatedDynamicFields = { ...oldDynamicFields }
  1623. // 移除已删除的字段
  1624. removedAttrs.forEach((attr) => {
  1625. delete updatedDynamicFields[attr.identifier]
  1626. })
  1627. // 初始化新增字段的值
  1628. addedAttrs.forEach((attr) => {
  1629. if (!updatedDynamicFields[attr.identifier]) {
  1630. // 如果有默认值使用默认值,否则为空
  1631. updatedDynamicFields[attr.identifier] =
  1632. attr.defaultValue || attr.extProperty?.defaultValue || ''
  1633. }
  1634. })
  1635. // 更新响应式数据
  1636. dynamicAttrs.value = updatedAttrs
  1637. formData.value.dynamicFields = updatedDynamicFields
  1638. }
  1639. // 加载动态属性
  1640. const loadDynamicAttrs = async (newTechniqueIds: string[], oldTechniqueIds?: string[]) => {
  1641. try {
  1642. formLoading.value = true
  1643. const queryParams = {
  1644. techniqueIds: newTechniqueIds.join(',')
  1645. }
  1646. const response = await IotDailyReportAttrsApi.dailyReportAttrs(queryParams)
  1647. const newAttrs = response || []
  1648. // 处理动态属性更新
  1649. await updateDynamicAttrs(newAttrs, newTechniqueIds, oldTechniqueIds)
  1650. } catch (error) {
  1651. console.error('加载动态属性失败:', error)
  1652. message.error('加载动态属性失败')
  1653. } finally {
  1654. formLoading.value = false
  1655. }
  1656. }
  1657. // 计算属性:获取平台井工作量数据(在详情/审批模式下优先使用 platforms,不存在则使用 finishedPlatforms)
  1658. const platformWorkloadData = computed(() => {
  1659. if (!dailyReportData.value) return []
  1660. // 在详情或审批模式下
  1661. if (isDetailMode.value || isApprovalMode.value) {
  1662. // 优先使用 platforms,如果不存在则使用 finishedPlatforms
  1663. return dailyReportData.value.platforms || dailyReportData.value.finishedPlatforms || []
  1664. }
  1665. // 其他模式只使用 platforms
  1666. return dailyReportData.value.platforms || []
  1667. })
  1668. // 在 watch 监听平台井选择变化的部分附近,添加施工工艺转换函数
  1669. // 添加施工工艺数值到标签的转换函数
  1670. const convertTechniqueIdsToLabels = (techniqueIds: number[]): string[] => {
  1671. if (!techniqueIds || !Array.isArray(techniqueIds)) {
  1672. return []
  1673. }
  1674. return techniqueIds.map((id) => {
  1675. const dict = techniqueOptions.value.find((option) => option.value === id.toString())
  1676. return dict ? dict.label : id.toString()
  1677. })
  1678. }
  1679. // 监听施工工艺变化
  1680. watch(
  1681. () => formData.value.techniqueIds,
  1682. async (newTechniqueIds, oldTechniqueIds) => {
  1683. if (newTechniqueIds && newTechniqueIds.length > 0) {
  1684. await loadDynamicAttrs(newTechniqueIds, oldTechniqueIds)
  1685. // 动态属性加载完成后,更新当前平台井的 extProperty
  1686. if (currentPlatformId.value) {
  1687. updateCurrentPlatformExtProperty()
  1688. }
  1689. } else {
  1690. dynamicAttrs.value = []
  1691. formData.value.dynamicFields = {}
  1692. // 清空当前平台井的 extProperty
  1693. if (currentPlatformId.value) {
  1694. updateCurrentPlatformExtProperty()
  1695. }
  1696. }
  1697. },
  1698. { deep: true }
  1699. )
  1700. // 监听 formData.dailyFuel 变化,同步到输入变量
  1701. watch(
  1702. () => formData.value.dailyFuel,
  1703. (newVal) => {
  1704. if (newVal !== null && newVal !== undefined && newVal !== '') {
  1705. // 将数字转换为字符串显示,但不干扰输入
  1706. dailyFuelInput.value = String(newVal)
  1707. } else {
  1708. dailyFuelInput.value = ''
  1709. }
  1710. dailyFuelManuallyModified.value = true
  1711. },
  1712. { immediate: true }
  1713. )
  1714. // 监听reportFuels的变化,自动更新当日油耗
  1715. watch(
  1716. () => formData.value.reportFuels,
  1717. (newFuels) => {
  1718. // 只有在编辑模式且用户没有手动修改过当日油耗时才自动计算
  1719. if (!isReadonlyMode.value) {
  1720. calculateAndUpdateDailyFuel()
  1721. }
  1722. },
  1723. { deep: true }
  1724. )
  1725. // 更新当前平台井的 extProperty
  1726. const updateCurrentPlatformExtProperty = () => {
  1727. if (!currentPlatformId.value) return
  1728. const index = platformWellPairs.value.findIndex((item) => item.taskId === currentPlatformId.value)
  1729. if (index !== -1) {
  1730. platformWellPairs.value[index].extProperty = getCurrentExtProperties()
  1731. }
  1732. }
  1733. // 监听平台井选择变化
  1734. watch(
  1735. () => formData.value.platformId,
  1736. (newPlatformId, oldPlatformId) => {
  1737. if (newPlatformId && newPlatformId !== oldPlatformId) {
  1738. // 保存当前平台井的数据到 platformWellPairs
  1739. if (oldPlatformId) {
  1740. saveCurrentPlatformData(oldPlatformId)
  1741. }
  1742. // 加载新平台井的数据到表单
  1743. loadPlatformData(newPlatformId)
  1744. currentPlatformId.value = newPlatformId
  1745. }
  1746. }
  1747. )
  1748. // 监听动态字段变化,实时更新到 platformWellPairs
  1749. watch(
  1750. () => formData.value.dynamicFields,
  1751. (newFields) => {
  1752. if (currentPlatformId.value) {
  1753. updateCurrentPlatformExtProperty()
  1754. }
  1755. },
  1756. { deep: true }
  1757. )
  1758. // 监听施工状态变化
  1759. watch(
  1760. () => formData.value.rdStatus,
  1761. (newStatus) => {
  1762. if (currentPlatformId.value) {
  1763. const index = platformWellPairs.value.findIndex(
  1764. (item) => item.taskId === currentPlatformId.value
  1765. )
  1766. if (index !== -1) {
  1767. platformWellPairs.value[index].rdStatus = newStatus
  1768. }
  1769. }
  1770. }
  1771. )
  1772. // 保存当前平台井数据到 platformWellPairs
  1773. const saveCurrentPlatformData = (platformId: number) => {
  1774. const index = platformWellPairs.value.findIndex((item) => item.taskId === platformId)
  1775. if (index !== -1) {
  1776. platformWellPairs.value[index] = {
  1777. ...platformWellPairs.value[index],
  1778. rdStatus: formData.value.rdStatus,
  1779. techniqueIds: [...formData.value.techniqueIds],
  1780. extProperty: getCurrentExtProperties(),
  1781. repairTime: formData.value.repairTime ?? 0,
  1782. selfStopTime: formData.value.selfStopTime ?? 0,
  1783. accidentTime: formData.value.accidentTime ?? 0,
  1784. complexityTime: formData.value.complexityTime ?? 0,
  1785. rectificationTime: formData.value.rectificationTime ?? 0,
  1786. waitingStopTime: formData.value.waitingStopTime ?? 0,
  1787. partyaDesign: formData.value.partyaDesign ?? 0,
  1788. partyaPrepare: formData.value.partyaPrepare ?? 0,
  1789. partyaResource: formData.value.partyaResource ?? 0,
  1790. relocationTime: formData.value.relocationTime ?? 0,
  1791. winterBreakTime: formData.value.winterBreakTime ?? 0,
  1792. otherNptTime: formData.value.otherNptTime ?? 0,
  1793. otherNptReason: formData.value.otherNptReason || ''
  1794. }
  1795. } else {
  1796. // 如果找不到对应的平台井,添加新的记录
  1797. const platform = platformOptions.value.find((p) => p.id === platformId)
  1798. if (platform) {
  1799. platformWellPairs.value.push({
  1800. taskId: platformId,
  1801. reportId: undefined, // 新记录没有 reportId
  1802. wellName: platform.wellName,
  1803. rdStatus: formData.value.rdStatus,
  1804. techniqueIds: [...formData.value.techniqueIds],
  1805. extProperty: getCurrentExtProperties(),
  1806. repairTime: formData.value.repairTime ?? 0,
  1807. selfStopTime: formData.value.selfStopTime ?? 0,
  1808. accidentTime: formData.value.accidentTime ?? 0,
  1809. complexityTime: formData.value.complexityTime ?? 0,
  1810. rectificationTime: formData.value.rectificationTime ?? 0,
  1811. waitingStopTime: formData.value.waitingStopTime ?? 0,
  1812. partyaDesign: formData.value.partyaDesign ?? 0,
  1813. partyaPrepare: formData.value.partyaPrepare ?? 0,
  1814. partyaResource: formData.value.partyaResource ?? 0,
  1815. relocationTime: formData.value.relocationTime ?? 0,
  1816. winterBreakTime: formData.value.winterBreakTime ?? 0,
  1817. otherNptTime: formData.value.otherNptTime ?? 0,
  1818. otherNptReason: formData.value.otherNptReason || ''
  1819. })
  1820. }
  1821. }
  1822. }
  1823. // 从 platformWellPairs 加载平台井数据到表单
  1824. const loadPlatformData = (platformId: number) => {
  1825. console.log('11 :>> ', 11)
  1826. const platformData = platformWellPairs.value.find((item) => item.taskId === platformId)
  1827. if (platformData) {
  1828. // 更新表单字段
  1829. formData.value.rdStatus = platformData.rdStatus || ''
  1830. // formData.value.dailyFuel = platformData.dailyFuel ? [...platformData.dailyFuel] : []
  1831. // 将施工工艺数值转换为对应的标签
  1832. if (platformData.techniqueIds && Array.isArray(platformData.techniqueIds)) {
  1833. // 如果是数字数组,转换为字符串数组(与数据字典格式匹配)
  1834. formData.value.techniqueIds = platformData.techniqueIds.map((id) => id.toString())
  1835. } else {
  1836. formData.value.techniqueIds = platformData.techniqueIds ? [...platformData.techniqueIds] : []
  1837. }
  1838. // 在详情或审批模式下,更新 dailyFuel 为当前平台井的值
  1839. if (isDetailMode.value || isApprovalMode.value) {
  1840. // 使用平台井的 dailyFuel 值
  1841. const platformDailyFuel = platformData.dailyFuel || ''
  1842. formData.value.dailyFuel = platformDailyFuel ? formatNumber(platformDailyFuel, 2) : ''
  1843. // 同步更新输入框
  1844. dailyFuelInput.value = formData.value.dailyFuel
  1845. }
  1846. // 更新动态属性
  1847. if (platformData.extProperty && platformData.extProperty.length > 0) {
  1848. const dynamicFields: Record<string, any> = {}
  1849. platformData.extProperty.forEach((prop: any) => {
  1850. if (prop.identifier) {
  1851. dynamicFields[prop.identifier] = prop.actualValue || ''
  1852. }
  1853. })
  1854. formData.value.dynamicFields = dynamicFields
  1855. } else {
  1856. formData.value.dynamicFields = {}
  1857. }
  1858. formData.value.repairTime = platformData.repairTime ?? 0
  1859. formData.value.selfStopTime = platformData.selfStopTime ?? 0
  1860. formData.value.accidentTime = platformData.accidentTime ?? 0
  1861. formData.value.complexityTime = platformData.complexityTime ?? 0
  1862. formData.value.rectificationTime = platformData.rectificationTime ?? 0
  1863. formData.value.waitingStopTime = platformData.waitingStopTime ?? 0
  1864. formData.value.partyaDesign = platformData.partyaDesign ?? 0
  1865. formData.value.partyaPrepare = platformData.partyaPrepare ?? 0
  1866. formData.value.partyaResource = platformData.partyaResource ?? 0
  1867. formData.value.relocationTime = platformData.relocationTime ?? 0
  1868. formData.value.winterBreakTime = platformData.winterBreakTime ?? 0
  1869. formData.value.otherNptTime = platformData.otherNptTime ?? 0
  1870. formData.value.otherNptReason = platformData.otherNptReason || ''
  1871. } else {
  1872. // 如果没有找到数据,初始化默认值
  1873. formData.value.rdStatus = ''
  1874. formData.value.techniqueIds = []
  1875. formData.value.dynamicFields = {}
  1876. // 初始化其他时间字段
  1877. formData.value.repairTime = 0
  1878. formData.value.selfStopTime = 0
  1879. formData.value.accidentTime = 0
  1880. formData.value.complexityTime = 0
  1881. formData.value.rectificationTime = 0
  1882. formData.value.waitingStopTime = 0
  1883. formData.value.partyaDesign = 0
  1884. formData.value.partyaPrepare = 0
  1885. formData.value.partyaResource = 0
  1886. formData.value.relocationTime = 0
  1887. formData.value.winterBreakTime = 0
  1888. formData.value.otherNptTime = 0
  1889. formData.value.otherNptReason = ''
  1890. // 在详情或审批模式下,清空 dailyFuel
  1891. if (isDetailMode.value || isApprovalMode.value) {
  1892. formData.value.dailyFuel = ''
  1893. dailyFuelInput.value = ''
  1894. }
  1895. }
  1896. }
  1897. // 获取当前动态属性数据
  1898. const getCurrentExtProperties = () => {
  1899. return dynamicAttrs.value.map((attr) => {
  1900. return {
  1901. name: attr.name,
  1902. sort: attr.sort,
  1903. unit: attr.unit,
  1904. actualValue: formData.value.dynamicFields[attr.identifier] || '',
  1905. dataType: attr.dataType,
  1906. maxValue: attr.maxValue,
  1907. minValue: attr.minValue,
  1908. required: attr.required,
  1909. accessMode: attr.accessMode,
  1910. identifier: attr.identifier,
  1911. defaultValue: attr.defaultValue
  1912. }
  1913. })
  1914. }
  1915. // 是否显示实际进度
  1916. const showActualProgress = computed(() => {
  1917. // 在所有模式下都显示,如果有数据就显示
  1918. return dailyReportData.value?.taskProgresses && dailyReportData.value.taskProgresses.length > 0
  1919. })
  1920. // 实际进度数据
  1921. const actualProgressData = computed(() => {
  1922. if (
  1923. !dailyReportData.value?.taskProgresses ||
  1924. !Array.isArray(dailyReportData.value.taskProgresses)
  1925. ) {
  1926. return []
  1927. }
  1928. // 将 taskProgresses 转换为 el-steps 需要的格式
  1929. return dailyReportData.value.taskProgresses.map((progress: any, index: number) => {
  1930. // 格式化日期:如果只有日期部分,使用日期格式;如果有时间,使用日期时间格式
  1931. let formattedDate = ''
  1932. if (progress.createTime) {
  1933. // 判断日期格式,如果包含时间则显示完整时间,否则只显示日期
  1934. if (progress.createTime.includes(' ')) {
  1935. // 已经是完整的日期时间格式
  1936. formattedDate = progress.createTime
  1937. } else {
  1938. // 只有日期部分
  1939. formattedDate = progress.createTime
  1940. }
  1941. }
  1942. // 构建标题:日期 + 状态
  1943. const title =
  1944. formattedDate && progress.rdStatusLabel
  1945. ? `${formattedDate} ${progress.rdStatusLabel}`
  1946. : progress.rdStatusLabel || '未知状态'
  1947. return {
  1948. title: title,
  1949. description: '', // 可以根据需要添加描述信息
  1950. status: undefined, // el-steps 会自动计算状态
  1951. // 保留原始数据,便于调试
  1952. rawData: progress
  1953. }
  1954. })
  1955. })
  1956. // 初始化表单数据
  1957. const initFormData = (reportData: any) => {
  1958. // 处理附件数据格式转换
  1959. const formattedAttachments = (reportData.attachments || []).map((attachment: any) => ({
  1960. id: attachment.id,
  1961. category: attachment.category?.toLowerCase() || 'daily_report',
  1962. bizId: attachment.bizId,
  1963. type: attachment.type?.toLowerCase() || 'attachment',
  1964. filename: attachment.filename,
  1965. fileType: attachment.fileType, // 使用辅助函数获取文件类型
  1966. filePath: attachment.filePath,
  1967. fileSize: attachment.fileSize,
  1968. remark: attachment.remark || ''
  1969. }))
  1970. // 确保 techniqueIds 是字符串数组格式
  1971. let techniqueIds = []
  1972. if (reportData.techniqueIds && Array.isArray(reportData.techniqueIds)) {
  1973. techniqueIds = reportData.techniqueIds.map((id: number) => id.toString())
  1974. }
  1975. formData.value = {
  1976. ...formData.value,
  1977. id: reportData.id,
  1978. deptId: reportData.deptId,
  1979. taskId: reportData.taskId,
  1980. platformWell: reportData.platformWell,
  1981. rdStatus: reportData.rdStatus || '',
  1982. techniqueIds: techniqueIds,
  1983. dailyFuel: reportData.dailyFuel, // 当日油耗默认值
  1984. productionStatus: reportData.productionStatus || '',
  1985. nextPlan: reportData.nextPlan || '',
  1986. externalRental: reportData.externalRental || '',
  1987. malfunction: reportData.malfunction || '',
  1988. faultDowntime: reportData.faultDowntime || '',
  1989. startTime: reportData.startTime || undefined,
  1990. endTime: reportData.endTime || undefined,
  1991. companyId: reportData.companyId || '',
  1992. dynamicFields: {}, // 确保有初始值
  1993. auditStatus: reportData.auditStatus,
  1994. // 初始化附件数据
  1995. attachments: formattedAttachments,
  1996. repairTime: reportData.repairTime ?? 0,
  1997. selfStopTime: reportData.selfStopTime ?? 0,
  1998. accidentTime: reportData.accidentTime ?? 0,
  1999. complexityTime: reportData.complexityTime ?? 0,
  2000. rectificationTime: reportData.rectificationTime ?? 0,
  2001. waitingStopTime: reportData.waitingStopTime ?? 0,
  2002. partyaDesign: reportData.partyaDesign ?? 0,
  2003. partyaPrepare: reportData.partyaPrepare ?? 0,
  2004. partyaResource: reportData.partyaResource ?? 0,
  2005. relocationTime: reportData.relocationTime ?? 0,
  2006. winterBreakTime: reportData.winterBreakTime ?? 0,
  2007. otherNptTime: reportData.otherNptTime ?? 0,
  2008. otherNptReason: reportData.otherNptReason || ''
  2009. }
  2010. // 初始化审批意见数据
  2011. approvalForm.opinion = reportData.auditOpinion || reportData.opinion || ''
  2012. queryParams.deptId = reportData.companyId
  2013. // 设置时间范围选择器
  2014. if (
  2015. reportData.startTime &&
  2016. Array.isArray(reportData.startTime) &&
  2017. reportData.endTime &&
  2018. Array.isArray(reportData.endTime)
  2019. ) {
  2020. // 基于日报的施工开始日期作为基准日期
  2021. const baseDate = reportData.constructionStartDate || Date.now()
  2022. const startTime = parseTimeArrayToDate(reportData.startTime, baseDate)
  2023. const endTime = parseTimeArrayToDate(reportData.endTime, baseDate)
  2024. if (startTime && endTime) {
  2025. formData.value.timeRange = [startTime, endTime]
  2026. }
  2027. }
  2028. // 初始化平台井数据
  2029. initPlatformData(reportData)
  2030. // 初始化动态属性
  2031. initDynamicAttrs(reportData)
  2032. // 初始化设备数据
  2033. initDeviceData(reportData)
  2034. // 初始化油耗数据 - 根据模式选择数据源
  2035. if (isDetailMode.value || isApprovalMode.value) {
  2036. // 详情或审批模式:优先使用 reportedFuels
  2037. let fuelSource = reportData.reportedFuels
  2038. if (fuelSource && Array.isArray(fuelSource) && fuelSource.length > 0) {
  2039. // 处理每个油耗数据项,设置 customFuel 的默认值并确保格式正确
  2040. const processedFuels = fuelSource.map((fuel: any) => {
  2041. // 创建全新的对象,避免引用共享
  2042. const newFuel = {
  2043. ...fuel, // 使用展开运算符创建浅拷贝
  2044. // 确保每个字段都有独立的值
  2045. createTime: fuel.createTime,
  2046. updateTime: fuel.updateTime,
  2047. creator: fuel.creator,
  2048. updater: fuel.updater,
  2049. deleted: fuel.deleted,
  2050. id: fuel.id,
  2051. type: fuel.type,
  2052. reportId: fuel.reportId,
  2053. deviceId: fuel.deviceId,
  2054. deviceCode: fuel.deviceCode,
  2055. yfDeviceCode: fuel.yfDeviceCode,
  2056. deviceName: fuel.deviceName,
  2057. carId: fuel.carId,
  2058. zhbdFuel: fuel.zhbdFuel,
  2059. customFuel: null, // 初始化为null
  2060. queryDate: fuel.queryDate,
  2061. remark: fuel.remark
  2062. }
  2063. let customFuelValue
  2064. // 如果 customFuel 不为空,确保它是字符串格式并已正确格式化
  2065. if (fuel.customFuel !== null && fuel.customFuel !== undefined) {
  2066. const numValue = parseFloat(fuel.customFuel)
  2067. customFuelValue = !isNaN(numValue) ? formatNumber(numValue, 2) : '0.00'
  2068. } else {
  2069. // 如果 customFuel 为空,则使用 zhbdFuel 的值
  2070. const zhbdValue = parseFloat(fuel.zhbdFuel)
  2071. customFuelValue = !isNaN(zhbdValue) ? formatNumber(zhbdValue, 2) : '0.00'
  2072. }
  2073. return {
  2074. ...newFuel,
  2075. customFuel: customFuelValue
  2076. }
  2077. })
  2078. formData.value.reportFuels = processedFuels
  2079. // 计算初始的当日油耗
  2080. calculateTotalDailyFuel()
  2081. } else {
  2082. // 如果 reportedFuels 不存在或为空,设置空数组
  2083. formData.value.reportFuels = []
  2084. }
  2085. } else {
  2086. // 编辑模式:优先使用 reportedFuels,不存在则使用 reportFuels
  2087. let fuelSource = reportData.reportedFuels || reportData.reportFuels || []
  2088. if (fuelSource && Array.isArray(fuelSource) && fuelSource.length > 0) {
  2089. // 处理每个油耗数据项,设置 customFuel 的默认值并确保格式正确
  2090. const processedFuels = fuelSource.map((fuel: any) => {
  2091. // 创建全新的对象,避免引用共享
  2092. const newFuel = {
  2093. ...fuel, // 使用展开运算符创建浅拷贝
  2094. // 确保每个字段都有独立的值
  2095. createTime: fuel.createTime,
  2096. updateTime: fuel.updateTime,
  2097. creator: fuel.creator,
  2098. updater: fuel.updater,
  2099. deleted: fuel.deleted,
  2100. id: fuel.id,
  2101. type: fuel.type,
  2102. reportId: fuel.reportId,
  2103. deviceId: fuel.deviceId,
  2104. deviceCode: fuel.deviceCode,
  2105. yfDeviceCode: fuel.yfDeviceCode,
  2106. deviceName: fuel.deviceName,
  2107. carId: fuel.carId,
  2108. zhbdFuel: fuel.zhbdFuel,
  2109. customFuel: null, // 初始化为null
  2110. queryDate: fuel.queryDate,
  2111. remark: fuel.remark
  2112. }
  2113. let customFuelValue
  2114. // 如果 customFuel 不为空,确保它是字符串格式并已正确格式化
  2115. if (fuel.customFuel !== null && fuel.customFuel !== undefined && fuel.customFuel !== '') {
  2116. const numValue = parseFloat(fuel.customFuel)
  2117. customFuelValue = !isNaN(numValue) ? formatNumber(numValue, 2) : '0.00'
  2118. } else {
  2119. // 如果 customFuel 为空,则使用 zhbdFuel 的值
  2120. const zhbdValue = parseFloat(fuel.zhbdFuel)
  2121. customFuelValue = !isNaN(zhbdValue) ? formatNumber(zhbdValue, 2) : '0.00'
  2122. }
  2123. return {
  2124. ...newFuel,
  2125. customFuel: customFuelValue
  2126. }
  2127. })
  2128. formData.value.reportFuels = processedFuels
  2129. // 计算初始的当日油耗
  2130. calculateTotalDailyFuel()
  2131. } else {
  2132. formData.value.reportFuels = []
  2133. }
  2134. }
  2135. }
  2136. onMounted(async () => {
  2137. formLoading.value = true
  2138. try {
  2139. // 加载当前登录人所属部门
  2140. const deptId = userStore.getUser.deptId
  2141. const dept = await DeptApi.getDept(deptId)
  2142. // 查询瑞都日报详情
  2143. if (id) {
  2144. const response = await IotRdDailyReportApi.getIotRdDailyReport(id)
  2145. console.log('response :>> ', response)
  2146. dailyReportData.value = response || {}
  2147. initFormData(dailyReportData.value)
  2148. // 确保油耗数据在初始化后立即渲染
  2149. await nextTick()
  2150. // 强制更新油耗数据
  2151. if (formData.value.reportFuels && formData.value.reportFuels.length > 0) {
  2152. formData.value.reportFuels = [...formData.value.reportFuels]
  2153. }
  2154. }
  2155. } catch (error) {
  2156. console.error('初始化数据失败:', error)
  2157. message.error('数据加载失败')
  2158. } finally {
  2159. formLoading.value = false
  2160. }
  2161. })
  2162. // 详细 审批 平台井 获取工作量列配置
  2163. const getWorkloadColumns = () => {
  2164. const dataSource = platformWorkloadData.value
  2165. if (!dataSource || dataSource.length === 0) return []
  2166. const columns = []
  2167. const addedIdentifiers = new Set()
  2168. dataSource.forEach((platform) => {
  2169. if (platform.extProperty && Array.isArray(platform.extProperty)) {
  2170. platform.extProperty.forEach((extProp) => {
  2171. if (!addedIdentifiers.has(extProp.identifier)) {
  2172. columns.push({
  2173. key: extProp.identifier,
  2174. identifier: extProp.identifier,
  2175. label: `${extProp.name}(${extProp.unit})`
  2176. })
  2177. addedIdentifiers.add(extProp.identifier)
  2178. }
  2179. })
  2180. }
  2181. })
  2182. return columns
  2183. }
  2184. // 添加一个深拷贝油耗数据的辅助函数
  2185. const deepCopyFuelData = (fuelData: any) => {
  2186. if (!fuelData) return null
  2187. return {
  2188. createTime: fuelData.createTime,
  2189. updateTime: fuelData.updateTime,
  2190. creator: fuelData.creator,
  2191. updater: fuelData.updater,
  2192. deleted: fuelData.deleted,
  2193. id: fuelData.id,
  2194. type: fuelData.type,
  2195. reportId: fuelData.reportId,
  2196. deviceId: fuelData.deviceId,
  2197. deviceCode: fuelData.deviceCode,
  2198. yfDeviceCode: fuelData.yfDeviceCode,
  2199. deviceName: fuelData.deviceName,
  2200. carId: fuelData.carId,
  2201. zhbdFuel: fuelData.zhbdFuel,
  2202. customFuel: fuelData.customFuel,
  2203. queryDate: fuelData.queryDate,
  2204. remark: fuelData.remark
  2205. }
  2206. }
  2207. // 强制刷新表格
  2208. const refreshFuelTable = () => {
  2209. fuelTableKey.value += 1
  2210. }
  2211. // 计算当日油耗的默认值
  2212. const calculateDailyFuel = (reportData: any) => {
  2213. let dailyFuelValue = 0
  2214. // 如果有接口返回的dailyFuel,优先使用
  2215. if (reportData.dailyFuel !== null && reportData.dailyFuel !== undefined) {
  2216. dailyFuelValue = Number(reportData.dailyFuel)
  2217. }
  2218. // 如果reportFuels有数据,累加zhbdFuel的值
  2219. if (
  2220. reportData.reportFuels &&
  2221. Array.isArray(reportData.reportFuels) &&
  2222. reportData.reportFuels.length > 0
  2223. ) {
  2224. const totalZhbdFuel = reportData.reportFuels.reduce((sum: number, item: any) => {
  2225. const zhbdFuelValue = Number(item.zhbdFuel) || 0
  2226. return sum + zhbdFuelValue
  2227. }, 0)
  2228. // 只有当累计值大于0时才覆盖原有的dailyFuel值
  2229. if (totalZhbdFuel > 0) {
  2230. dailyFuelValue = totalZhbdFuel
  2231. }
  2232. }
  2233. return formatNumber(dailyFuelValue, 2)
  2234. }
  2235. // 处理当日油耗输入
  2236. const handleDailyFuelInput = () => {
  2237. // 确保保留两位小数
  2238. if (formData.value.dailyFuel !== '') {
  2239. const numValue = parseFloat(formData.value.dailyFuel)
  2240. if (!isNaN(numValue)) {
  2241. formData.value.dailyFuel = numValue.toFixed(2)
  2242. }
  2243. }
  2244. }
  2245. // 添加数字格式化函数
  2246. const formatNumber = (value: any, decimalPlaces: number = 2) => {
  2247. if (value === null || value === undefined || value === '' || value === 'NaN') {
  2248. return '0.00'
  2249. }
  2250. // 如果已经是字符串,尝试转换为数字
  2251. if (typeof value === 'string') {
  2252. // 移除可能的非数字字符
  2253. const cleaned = value.replace(/[^\d.-]/g, '')
  2254. const num = Number(cleaned)
  2255. if (isNaN(num)) {
  2256. return '0.00'
  2257. }
  2258. return num.toFixed(decimalPlaces)
  2259. }
  2260. const num = Number(value)
  2261. if (isNaN(num)) {
  2262. return '0.00'
  2263. }
  2264. return num.toFixed(decimalPlaces)
  2265. }
  2266. // 新增:计算并更新当日油耗的方法
  2267. const calculateAndUpdateDailyFuel = () => {
  2268. if (!formData.value.reportFuels || !Array.isArray(formData.value.reportFuels)) {
  2269. return
  2270. }
  2271. // 计算所有车辆的实际油耗总和
  2272. const totalCustomFuel = formData.value.reportFuels.reduce((sum: number, item: any) => {
  2273. const customFuelValue = Number(item.customFuel) || 0
  2274. return sum + customFuelValue
  2275. }, 0)
  2276. // 只有当累计的实际油耗大于0时,才更新当日油耗
  2277. if (totalCustomFuel > 0 && !isReadonlyMode.value) {
  2278. formData.value.dailyFuel = formatNumber(totalCustomFuel, 2)
  2279. // 更新输入框显示
  2280. if (dailyFuelInput.value) {
  2281. dailyFuelInput.value = formData.value.dailyFuel
  2282. }
  2283. }
  2284. }
  2285. // 添加计算属性:获取油耗数据显示数据源
  2286. const fuelConsumptionData = computed(() => {
  2287. // 所有模式都统一使用 formData.value.reportFuels 作为数据源
  2288. // 因为 formData.value.reportFuels 在 initFormData 中已经正确处理了所有情况
  2289. return formData.value.reportFuels || []
  2290. })
  2291. // 判断是否显示油耗信息区域
  2292. const showFuelConsumption = computed(() => {
  2293. const data = fuelConsumptionData.value
  2294. return data && Array.isArray(data) && data.length > 0
  2295. })
  2296. // 重新计算当日油耗const formatNumber
  2297. const calculateTotalDailyFuel = () => {
  2298. if (!formData.value.reportFuels || !Array.isArray(formData.value.reportFuels)) {
  2299. return
  2300. }
  2301. // 计算所有车辆的实际油耗总和
  2302. const totalCustomFuel = formData.value.reportFuels.reduce((sum: number, item: any) => {
  2303. const customFuelValue = Number(item.customFuel) || 0
  2304. return sum + customFuelValue
  2305. }, 0)
  2306. // 只有当累计的实际油耗大于0时,才自动更新当日油耗
  2307. if (totalCustomFuel > 0 && !isReadonlyMode.value) {
  2308. formData.value.dailyFuel = formatNumber(totalCustomFuel, 2)
  2309. }
  2310. }
  2311. // 处理实际油耗变化
  2312. const handleCustomFuelChange = (fuelItem: any) => {
  2313. // 获取当前输入的值
  2314. let value = fuelItem.customFuel
  2315. // 如果输入为空,则重置为zhbdFuel的值
  2316. if (value === '' || value === null || value === undefined) {
  2317. fuelItem.customFuel = formatNumber(fuelItem.zhbdFuel, 2)
  2318. return
  2319. }
  2320. // 移除非数字字符(除了小数点)
  2321. const cleaned = value.toString().replace(/[^\d.]/g, '')
  2322. // 确保只有一个小数点
  2323. const parts = cleaned.split('.')
  2324. let formattedValue = cleaned
  2325. if (parts.length > 2) {
  2326. formattedValue = parts[0] + '.' + parts.slice(1).join('')
  2327. }
  2328. // 转换为数字并格式化为两位小数
  2329. const numValue = parseFloat(formattedValue)
  2330. if (!isNaN(numValue)) {
  2331. // 限制到两位小数
  2332. fuelItem.customFuel = formatNumber(numValue, 2)
  2333. } else {
  2334. // 如果转换失败,设置为0.00
  2335. fuelItem.customFuel = '0.00'
  2336. }
  2337. // 同步更新 formData.reportFuels 中的数据
  2338. // 确保通过 deviceId 正确找到并更新对应的记录
  2339. if (formData.value.reportFuels && fuelItem.deviceId) {
  2340. const index = formData.value.reportFuels.findIndex(
  2341. (item) => item.deviceId === fuelItem.deviceId
  2342. )
  2343. if (index !== -1) {
  2344. // 创建新对象,避免引用问题
  2345. const updatedFuel = {
  2346. ...formData.value.reportFuels[index],
  2347. customFuel: fuelItem.customFuel
  2348. }
  2349. // 使用 Vue.set 或直接赋值确保响应性
  2350. formData.value.reportFuels[index] = updatedFuel
  2351. // 强制刷新表格
  2352. fuelTableKey.value += 1
  2353. }
  2354. }
  2355. // 手动触发当日油耗的重新计算
  2356. calculateAndUpdateDailyFuel()
  2357. }
  2358. // 详情 审批 平台井 获取工作量值
  2359. const getWorkloadValue = (platform, identifier) => {
  2360. if (!platform.extProperty) return ''
  2361. const prop = platform.extProperty.find((item) => item.identifier === identifier)
  2362. return prop ? prop.actualValue || '' : ''
  2363. }
  2364. /** 审批操作 */
  2365. const handleApprove = async (action: 'pass' | 'reject') => {
  2366. // 只有在审批模式下才执行审批操作
  2367. if (!isApprovalMode.value) {
  2368. message.warning('当前不是审批模式')
  2369. return
  2370. }
  2371. try {
  2372. // 验证审批表单(如果需要)
  2373. // await approvalFormRef.value.validate()
  2374. formLoading.value = true
  2375. // 处理时间范围数据
  2376. if (formData.value.timeRange && formData.value.timeRange.length === 2) {
  2377. // 将时间范围转换为 LocalTime 格式的字符串
  2378. const startDate = dayjs(formData.value.timeRange[0])
  2379. const endDate = dayjs(formData.value.timeRange[1])
  2380. // 格式化为 HH:mm:ss 字符串,后端会自动转换为 LocalTime
  2381. formData.value.startTime = startDate.format('HH:mm:ss')
  2382. formData.value.endTime = endDate.format('HH:mm:ss')
  2383. }
  2384. // 构建审批数据,包含审批意见
  2385. const approveData = {
  2386. ...formData.value,
  2387. id: Number(id),
  2388. opinion: approvalForm.opinion,
  2389. auditStatus: action === 'pass' ? 20 : 30
  2390. }
  2391. // 这里可以调用审批API
  2392. if (action === 'pass') {
  2393. // 审批通过逻辑
  2394. await IotRdDailyReportApi.approveRdDailyReport(approveData)
  2395. message.success('审批通过')
  2396. } else {
  2397. // 审批驳回逻辑
  2398. await IotRdDailyReportApi.approveRdDailyReport(approveData)
  2399. message.success('审批驳回')
  2400. }
  2401. close()
  2402. } catch (error) {
  2403. console.error('审批操作失败:', error)
  2404. message.error('审批操作失败')
  2405. } finally {
  2406. formLoading.value = false
  2407. }
  2408. }
  2409. </script>
  2410. <style scoped>
  2411. .info-table {
  2412. border: 1px solid #e0e0e0;
  2413. border-radius: 4px;
  2414. overflow: hidden;
  2415. }
  2416. .table-row {
  2417. display: flex;
  2418. border-bottom: 1px solid #e0e0e0;
  2419. }
  2420. .table-row:last-child {
  2421. border-bottom: none;
  2422. }
  2423. .table-cell {
  2424. flex: 1;
  2425. border-right: 1px solid #e0e0e0;
  2426. padding: 12px 8px;
  2427. min-height: 44px;
  2428. display: flex;
  2429. align-items: center;
  2430. }
  2431. .table-cell:last-child {
  2432. border-right: none;
  2433. }
  2434. .table-cell.full-width {
  2435. flex: 1;
  2436. border-right: none;
  2437. }
  2438. .cell-content {
  2439. display: flex;
  2440. align-items: center;
  2441. width: 100%;
  2442. }
  2443. .cell-label {
  2444. font-weight: 500;
  2445. /* 统一字体大小为 14px(Element 表单默认) */
  2446. font-size: 14px;
  2447. color: #606266;
  2448. min-width: 80px;
  2449. margin-right: 8px;
  2450. flex-shrink: 0;
  2451. }
  2452. .cell-value {
  2453. /* 统一字体大小为 14px(Element 输入框默认) */
  2454. font-size: 14px;
  2455. color: #303133;
  2456. /* 统一行高为 1.5(Element 组件默认行高) */
  2457. line-height: 1.5;
  2458. flex: 1;
  2459. word-break: break-all;
  2460. }
  2461. /* 响应式设计 */
  2462. @media (max-width: 768px) {
  2463. .table-row {
  2464. flex-direction: column;
  2465. }
  2466. .table-cell {
  2467. border-right: none;
  2468. border-bottom: 1px solid #e0e0e0;
  2469. }
  2470. .table-cell:last-child {
  2471. border-bottom: none;
  2472. }
  2473. }
  2474. .daily-report-title {
  2475. text-align: center;
  2476. margin: 20px 0;
  2477. padding: 10px;
  2478. border-bottom: 2px solid #409eff;
  2479. }
  2480. .daily-report-title h2 {
  2481. margin: 0;
  2482. color: #303133;
  2483. font-size: 16px;
  2484. font-weight: bold;
  2485. }
  2486. /* 为第二、三部分增加左右留白 */
  2487. .section-padding {
  2488. padding-left: 0px;
  2489. padding-right: 40px;
  2490. }
  2491. .project-info-section {
  2492. margin: 20px 0;
  2493. padding: 20px;
  2494. background-color: #f8f9fa;
  2495. border-radius: 4px;
  2496. border: 1px solid #e9ecef;
  2497. }
  2498. .info-row {
  2499. padding: 12px 0;
  2500. border-bottom: 1px solid #e9ecef;
  2501. }
  2502. .info-row:last-child {
  2503. border-bottom: none;
  2504. }
  2505. .info-label {
  2506. font-weight: bold;
  2507. color: #495057;
  2508. margin-right: 8px;
  2509. }
  2510. .info-value {
  2511. color: #212529;
  2512. }
  2513. :deep(.el-textarea .el-textarea__inner) {
  2514. min-height: 80px;
  2515. }
  2516. /* 确保表单label不换行 */
  2517. :deep(.el-form-item__label) {
  2518. white-space: nowrap;
  2519. text-overflow: ellipsis;
  2520. overflow: hidden;
  2521. }
  2522. /* 甲方字段:单行显示+超出省略 */
  2523. .single-line-ellipsis {
  2524. /* 强制文本单行显示 */
  2525. white-space: nowrap;
  2526. /* 超出容器部分隐藏 */
  2527. overflow: hidden;
  2528. /* 超出部分显示省略号 */
  2529. text-overflow: ellipsis;
  2530. /* 避免文本被截断(可选,根据需求调整) */
  2531. word-break: normal;
  2532. }
  2533. /* 设备配置字段:换行缩进(与首行对齐) */
  2534. .indent-multiline {
  2535. /* 首行及换行后缩进 2em(与 label 宽度匹配,可根据需求调整) */
  2536. text-indent: 0em;
  2537. /* 允许长文本换行(覆盖原有 cell-value 的 break-all,确保中文换行正常) */
  2538. word-break: break-word;
  2539. /* 保证换行后文本正常显示(可选,清除可能的 nowrap 影响) */
  2540. white-space: normal;
  2541. }
  2542. /* 添加审批模式下的样式 */
  2543. .approval-notice {
  2544. margin-top: 10px;
  2545. }
  2546. /* 审批模式下表单字段的只读样式 */
  2547. :deep(.el-form-item.is-disabled .el-input__inner),
  2548. :deep(.el-form-item.is-disabled .el-textarea__inner) {
  2549. background-color: #f5f7fa;
  2550. border-color: #e4e7ed;
  2551. color: #c0c4cc;
  2552. cursor: not-allowed;
  2553. }
  2554. :deep(.el-form-item.is-disabled .el-select .el-input__inner) {
  2555. background-color: #f5f7fa;
  2556. border-color: #e4e7ed;
  2557. color: #c0c4cc;
  2558. cursor: not-allowed;
  2559. }
  2560. :deep(.el-form-item.is-disabled .el-date-editor .el-input__inner) {
  2561. background-color: #f5f7fa;
  2562. border-color: #e4e7ed;
  2563. color: #c0c4cc;
  2564. cursor: not-allowed;
  2565. }
  2566. /* 只读模式下表单字段的样式 */
  2567. :deep(.el-form-item.is-disabled .el-input__inner),
  2568. :deep(.el-form-item.is-disabled .el-textarea__inner),
  2569. :deep(.el-form-item.is-disabled .el-select .el-input__inner),
  2570. :deep(.el-form-item.is-disabled .el-date-editor .el-input__inner) {
  2571. background-color: #f5f7fa;
  2572. border-color: #e4e7ed;
  2573. color: #606266; /* 保持文字可读性 */
  2574. cursor: not-allowed;
  2575. }
  2576. /* 详情模式下的特殊样式 */
  2577. .detail-mode .cell-value {
  2578. color: #303133;
  2579. font-weight: normal;
  2580. }
  2581. /* 添加审批意见区域的样式 */
  2582. .approval-opinion-section {
  2583. margin-top: 20px;
  2584. border-top: 2px solid #f0f0f0;
  2585. padding-top: 20px;
  2586. }
  2587. /* 审批意见文本域样式 */
  2588. :deep(.approval-opinion .el-textarea__inner) {
  2589. min-height: 100px;
  2590. resize: vertical;
  2591. }
  2592. /* 审批意见标签样式 */
  2593. :deep(.approval-opinion .el-form-item__label) {
  2594. font-weight: bold;
  2595. color: #606266;
  2596. }
  2597. /* 附件列表样式 */
  2598. .attachment-list {
  2599. width: 100%;
  2600. margin-top: 5px;
  2601. border: 1px solid #e0e0e0;
  2602. border-radius: 4px;
  2603. padding: 10px;
  2604. background-color: #fafafa;
  2605. box-sizing: border-box;
  2606. }
  2607. .attachment-item {
  2608. display: flex;
  2609. justify-content: space-between;
  2610. align-items: center;
  2611. padding: 8px 12px;
  2612. border-bottom: 1px solid #f0f0f0;
  2613. }
  2614. .attachment-item:last-child {
  2615. border-bottom: none;
  2616. }
  2617. .attachment-name {
  2618. flex: 1;
  2619. color: #606266;
  2620. font-size: 11px;
  2621. }
  2622. .no-attachment {
  2623. color: #909399;
  2624. font-style: italic;
  2625. margin-top: 5px;
  2626. padding: 10px;
  2627. }
  2628. /* 附件名称链接样式 */
  2629. .attachment-name {
  2630. color: #409eff;
  2631. text-decoration: underline;
  2632. cursor: pointer;
  2633. }
  2634. .attachment-name:hover {
  2635. color: #66b1ff;
  2636. }
  2637. /* 只读模式下的设备显示样式 */
  2638. .device-display-readonly {
  2639. color: #606266;
  2640. font-size: 11px;
  2641. line-height: 1.5;
  2642. background-color: #f5f7fa;
  2643. padding: 8px 12px;
  2644. border-radius: 4px;
  2645. border: 1px solid #e4e7ed;
  2646. display: inline-block;
  2647. min-width: 200px;
  2648. }
  2649. .no-device {
  2650. margin-left: 10px;
  2651. color: #909399;
  2652. font-style: italic;
  2653. }
  2654. /* 设备选择对话框样式 */
  2655. .transfer-container {
  2656. text-align: center;
  2657. padding: 0px;
  2658. }
  2659. .transfer-component {
  2660. width: 100%;
  2661. min-width: 600px;
  2662. }
  2663. :deep(.el-transfer-panel) {
  2664. width: 40% !important;
  2665. }
  2666. :deep(.el-transfer-panel__item) {
  2667. display: flex !important;
  2668. align-items: center !important;
  2669. height: 32px !important;
  2670. line-height: 32px !important;
  2671. padding: 0 8px !important;
  2672. margin: 0 !important;
  2673. white-space: nowrap;
  2674. overflow: hidden;
  2675. text-overflow: ellipsis;
  2676. }
  2677. .transfer-option-text {
  2678. display: inline-block;
  2679. max-width: 100%;
  2680. }
  2681. :deep(.el-transfer-panel__list) {
  2682. width: 100% !important;
  2683. }
  2684. .device-display-container {
  2685. /* 与其他文本域保持相同的宽度和样式 */
  2686. display: inline-block;
  2687. width: 100%; /* 减去按钮宽度 */
  2688. min-height: 32px;
  2689. line-height: 32px;
  2690. padding: 0 12px;
  2691. margin-left: 0px;
  2692. border-radius: 4px;
  2693. border: 1px solid #e4e7ed;
  2694. background-color: #fff;
  2695. font-size: 11px;
  2696. /* 文本溢出处理 */
  2697. white-space: nowrap;
  2698. overflow: hidden;
  2699. text-overflow: ellipsis;
  2700. }
  2701. /* 只读模式下的设备显示样式 */
  2702. :deep(.is-disabled) .device-display-container {
  2703. background-color: #f5f7fa;
  2704. color: #606266;
  2705. cursor: not-allowed;
  2706. }
  2707. /* 附件容器样式调整 */
  2708. .attachment-container {
  2709. /* 与其他文本域保持相同的宽度和边距 */
  2710. width: 100%;
  2711. margin-top: 10px;
  2712. }
  2713. /* 未选择设备字段的只读样式 */
  2714. :deep(.unselected-device .el-textarea__inner) {
  2715. background-color: #f5f7fa;
  2716. border-color: #e4e7ed;
  2717. color: #909399;
  2718. cursor: not-allowed;
  2719. resize: none;
  2720. }
  2721. /* 平台井工作量区域专用样式 */
  2722. .platform-workload-section {
  2723. padding-left: 0px;
  2724. padding-right: 0px; /* 去掉右侧间距 */
  2725. }
  2726. /* 表格样式优化 */
  2727. .platform-workload-el-table {
  2728. width: 100%;
  2729. }
  2730. /* 表头不换行 */
  2731. :deep(.platform-workload-el-table .el-table__header-wrapper th) {
  2732. white-space: nowrap;
  2733. text-overflow: ellipsis;
  2734. overflow: hidden;
  2735. }
  2736. /* 单元格内容不换行 */
  2737. :deep(.platform-workload-el-table .el-table__body-wrapper td) {
  2738. white-space: nowrap;
  2739. text-overflow: ellipsis;
  2740. overflow: hidden;
  2741. }
  2742. /* 强制设置表头宽度为100% */
  2743. :deep(.platform-workload-el-table .el-table__header) {
  2744. width: 100% !important;
  2745. min-width: 100% !important;
  2746. }
  2747. /* 强制设置表格主体宽度为100% */
  2748. :deep(.platform-workload-el-table .el-table__body) {
  2749. width: 100% !important;
  2750. min-width: 100% !important;
  2751. }
  2752. /* 确保表格填满容器 */
  2753. :deep(.platform-workload-el-table .el-table) {
  2754. width: 100% !important;
  2755. }
  2756. /* 表格容器填满父容器 */
  2757. .platform-workload-table {
  2758. width: 100%;
  2759. }
  2760. /* 油耗信息区域样式 */
  2761. .fuel-consumption-section {
  2762. padding-left: 0px;
  2763. padding-right: 0px;
  2764. margin-top: 20px;
  2765. }
  2766. /* 强制表格宽度为100% */
  2767. :deep(.fuel-consumption-el-table) {
  2768. width: 100% !important;
  2769. min-width: 100% !important;
  2770. }
  2771. /* 确保表格内部元素也充满宽度 */
  2772. :deep(.fuel-consumption-el-table .el-table) {
  2773. width: 100% !important;
  2774. min-width: 100% !important;
  2775. }
  2776. /* 强制设置表头宽度为100% */
  2777. :deep(.fuel-consumption-el-table .el-table__header) {
  2778. width: 100% !important;
  2779. min-width: 100% !important;
  2780. }
  2781. /* 表头不换行 */
  2782. :deep(.fuel-consumption-el-table .el-table__header-wrapper th) {
  2783. white-space: nowrap;
  2784. text-overflow: ellipsis;
  2785. overflow: hidden;
  2786. background-color: #f5f7fa;
  2787. color: #606266;
  2788. font-weight: bold;
  2789. }
  2790. /* 强制设置表格主体宽度为100% */
  2791. :deep(.fuel-consumption-el-table .el-table__body) {
  2792. width: 100% !important;
  2793. min-width: 100% !important;
  2794. }
  2795. /* 表头和表体都设置为100%宽度 */
  2796. :deep(.fuel-consumption-el-table .el-table__header-wrapper),
  2797. :deep(.fuel-consumption-el-table .el-table__body-wrapper) {
  2798. width: 100% !important;
  2799. }
  2800. /* 单元格内容居中 */
  2801. :deep(.fuel-consumption-el-table .el-table__body-wrapper td) {
  2802. text-align: center;
  2803. white-space: nowrap;
  2804. text-overflow: ellipsis;
  2805. overflow: hidden;
  2806. }
  2807. /* 实际油耗输入框样式 */
  2808. :deep(.fuel-consumption-el-table .el-input__inner) {
  2809. text-align: center;
  2810. padding: 0 5px;
  2811. height: 28px;
  2812. line-height: 28px;
  2813. }
  2814. /* 只读模式下的数值显示 */
  2815. .fuel-consumption-el-table .readonly-value {
  2816. color: #606266;
  2817. font-weight: normal;
  2818. }
  2819. /* 强制设置表格宽度为100% */
  2820. :deep(.fuel-consumption-el-table .el-table) {
  2821. width: 100% !important;
  2822. }
  2823. /* 表格容器填满父容器 */
  2824. .fuel-consumption-table {
  2825. width: 100%;
  2826. overflow-x: auto;
  2827. }
  2828. /* 响应式调整 */
  2829. @media (max-width: 768px) {
  2830. .fuel-consumption-section {
  2831. padding-left: 10px;
  2832. padding-right: 10px;
  2833. }
  2834. :deep(.fuel-consumption-el-table .el-table__header-wrapper th),
  2835. :deep(.fuel-consumption-el-table .el-table__body-wrapper td) {
  2836. padding: 8px 5px;
  2837. font-size: 12px;
  2838. }
  2839. }
  2840. /* 当日油耗输入框样式优化 */
  2841. :deep(.el-form-item .el-input-number) {
  2842. width: 100%;
  2843. }
  2844. /* 当日油耗字段在只读模式下的样式 */
  2845. :deep(.is-disabled .el-input__inner[type='number']) {
  2846. background-color: #f5f7fa;
  2847. border-color: #e4e7ed;
  2848. color: #606266;
  2849. cursor: not-allowed;
  2850. }
  2851. /* 实际进度区域样式 */
  2852. .actual-progress-container {
  2853. margin-top: 10px;
  2854. padding: 20px;
  2855. border: 1px solid #e6e6e6;
  2856. border-radius: 8px;
  2857. background-color: #fafafa;
  2858. }
  2859. .progress-title {
  2860. margin: 0 0 16px 0;
  2861. font-size: 16px;
  2862. font-weight: bold;
  2863. color: #67c23a; /* 实际进度使用绿色标题 */
  2864. }
  2865. .no-progress-data {
  2866. text-align: center;
  2867. padding: 20px 0;
  2868. color: #909399;
  2869. font-style: italic;
  2870. }
  2871. /* 调整步骤组件样式以适应水平布局 */
  2872. :deep(.actual-progress-container .el-steps--horizontal) {
  2873. flex-wrap: nowrap;
  2874. overflow-x: auto;
  2875. padding-bottom: 10px;
  2876. }
  2877. :deep(.actual-progress-container .el-step__title) {
  2878. font-size: 12px;
  2879. line-height: 1.4;
  2880. white-space: nowrap;
  2881. overflow: hidden;
  2882. text-overflow: ellipsis;
  2883. max-width: 120px;
  2884. }
  2885. /* 确保步骤容器有足够空间 */
  2886. :deep(.actual-progress-container .el-step) {
  2887. flex-basis: auto;
  2888. flex-shrink: 0;
  2889. }
  2890. /* 响应式调整 */
  2891. @media (max-width: 768px) {
  2892. :deep(.actual-progress-container .el-step__title) {
  2893. font-size: 11px;
  2894. max-width: 100px;
  2895. }
  2896. .actual-progress-container {
  2897. padding: 15px;
  2898. }
  2899. }
  2900. </style>