StatusOperationPanel.vue 106 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402
  1. <template>
  2. <div class="container-panel">
  3. <el-row class="info-panel">
  4. <div class="info-item">
  5. <span class="label">报告编号:</span>
  6. <span class="value">{{ selectedItem?.reportNo || '-' }}</span>
  7. </div>
  8. <div class="info-item" style="margin: 0 auto;" v-if="isShowRecordOrReportBtn">
  9. <!-- <el-switch-->
  10. <!-- v-model="isShowReportPdf"-->
  11. <!-- active-text="报告"-->
  12. <!-- inactive-text="记录"-->
  13. <!-- @change="handleChangeShowReportPdf"-->
  14. <!-- />-->
  15. <!-- <el-radio-group v-model="showReportPdfType" @change="handleChangeShowReportPdf">-->
  16. <!-- <el-radio-button label="record">记录</el-radio-button>-->
  17. <!-- <el-radio-button label="report">报告</el-radio-button>-->
  18. <!-- <el-radio-button label="result" v-if="selectedItem?.reportType === 100">结论报告</el-radio-button>-->
  19. <!-- </el-radio-group>-->
  20. <div class="capsule-tabs">
  21. <div
  22. class="tab-item"
  23. :class="{ active: showReportPdfType === 'record' }"
  24. @click="handleChangeShowReportPdf('record')"
  25. v-if="hasRecord"
  26. >
  27. <span>记录</span>
  28. </div>
  29. <div
  30. class="tab-item"
  31. :class="{ active: showReportPdfType === 'report' }"
  32. @click="handleChangeShowReportPdf('report')"
  33. >
  34. <span>报告</span>
  35. </div>
  36. </div>
  37. </div>
  38. <div class="info-item" style="margin: 0 auto;">
  39. <span class="label">检验项目:</span>
  40. <span class="value" :title="selectedItem?.reportName || '-'">{{
  41. selectedItem?.reportName || '-'
  42. }}</span>
  43. </div>
  44. <div v-if="selectedItem?.reportType === 300">
  45. <span>{{ selectedItem.instructionId ? '已' : '未'}}关联操作指导书</span>
  46. <el-button
  47. type="primary"
  48. size="small"
  49. link
  50. class="edit-checker-btn"
  51. @click="handleShowAssociationOperationManual"
  52. >
  53. <Icon icon="ep:edit" />
  54. </el-button>
  55. </div>
  56. <div class="info-item" style="flex-basis: 300px; text-align: end;">
  57. <span class="label">{{ selectedItem?.reportType === 100 ? '主检人' : '检验员' }}:</span>
  58. <span
  59. class="value"
  60. :title="
  61. selectedItem?.reportType === 100
  62. ? taskOrderItem?.mainCheckerUser?.nickname || '-'
  63. : getCheckersName()
  64. "
  65. >{{
  66. selectedItem?.reportType === 100
  67. ? taskOrderItem?.mainCheckerUser?.nickname || '-'
  68. : getCheckersName()
  69. }}</span
  70. >
  71. <el-button
  72. v-if="!isAuditMode"
  73. type="primary"
  74. size="small"
  75. link
  76. class="edit-checker-btn"
  77. @click="
  78. () =>
  79. selectedItem?.reportType === 100 ? handleModifyMainChecker() : handleModifyChecker()
  80. "
  81. :disabled="(selectedItem?.reportType === 100 && taskOrderItem?.mainCheckerUser?.id !== userStore?.user?.id)
  82. || (selectedItem?.checkUsers?.length > 0 && selectedItem?.checkUsers?.[0]?.id !== userStore?.user?.id && taskOrderItem?.mainCheckerUser?.id !== userStore?.user?.id)
  83. || isCompleteInput"
  84. >
  85. <!-- 主报告:登录用户为主检人的时,才可以修改主检人 -->
  86. <!-- 子报告:登录用户为检验员或主检人的时候,才可以修改检验员 -->
  87. <Icon icon="ep:edit" />
  88. </el-button>
  89. </div>
  90. </el-row>
  91. <el-row class="status-operation-panel">
  92. <!-- 未选择任何项目时的提示 -->
  93. <div v-if="!selectedItem" class="no-selection">
  94. <el-empty description="请选择一个检验项目查看详情" :image-size="100" />
  95. </div>
  96. <!-- 选择单个项目时显示详情操作 -->
  97. <div v-else class="single-item-panel">
  98. <!--
  99. 1、重大问题线索告知表
  100. 2、作业指导书
  101. 3、检验方案
  102. 以上报告类型不显示做进度栏目
  103. -->
  104. <InspectionItemProgress
  105. v-if="!onlyShowPdf"
  106. :selected-item="selectedItem"
  107. :task-info="taskInfo"
  108. :is-audit-mode="isAuditMode"
  109. @modify-checker="handleModifyChecker"
  110. />
  111. <!-- <div class="pdf-panel" :style="{ maxWidth: !onlyShowPdf ? '1200px' : 'unset' }"> -->
  112. <div ref="pdfPanelRef" class="pdf-panel" >
  113. <!-- PDF预览区域 -->
  114. <div class="!h-full" :style="{ width: pdfContentWidth + 'px'}">
  115. <SpreadViewer :initData="initData" ref="spreadRef" isFullscreen @saveSuccess="saveSuccess"/>
  116. </div>
  117. </div>
  118. <!--
  119. 1、重大问题线索告知表
  120. 2、作业指导书
  121. 3、检验方案
  122. 以上报告类型不显示右侧栏目
  123. -->
  124. <template v-if="showCheckBook && getReportStatusEnd">
  125. <div class="right-panel-container">
  126. <!-- 收缩展开按钮 -->
  127. <div class="toggle-btn" @click="togglePanel" :class="{ 'collapsed': !isExpanded }">
  128. <el-icon>
  129. <Back v-if="!isExpanded" />
  130. <Right v-else />
  131. </el-icon>
  132. </div>
  133. <div class="operation-panel" :class="{ 'expanded': isExpanded, 'collapsed': !isExpanded }">
  134. <div class="operation-inner custom-inner">
  135. <template v-if="checkBookDetail.rectificationStatus === 2">
  136. <div class="operation-item">
  137. <div class="item-header"> 回退原因 </div>
  138. <div class="item-content">
  139. <el-form
  140. ref="returnFormRef"
  141. :model="returnForm"
  142. :rules="returnFormRules"
  143. label-position="right"
  144. label-width="80px"
  145. >
  146. <el-form-item label="回退原因" prop="rejectionReason">
  147. <el-input
  148. v-model="returnForm.rejectionReason"
  149. type="textarea"
  150. :rows="5"
  151. maxlength="100"
  152. placeholder="请输入回退原因"
  153. />
  154. </el-form-item>
  155. </el-form>
  156. </div>
  157. </div>
  158. </template>
  159. <div class="operation-item1">
  160. <div class="item-header"> 处理人信息 </div>
  161. <div class="item-content">
  162. <div class="item-content-item">
  163. <span class="item-content-item-label">联系人姓名:</span>
  164. <span class="item-content-item-value">{{ checkBookDetail?.recipient || '-' }}</span>
  165. </div>
  166. <div class="item-content-item">
  167. <span class="item-content-item-label">联系人电话:</span>
  168. <span class="item-content-item-value">{{ checkBookDetail?.recipientPhone || '-'}}</span>
  169. </div>
  170. <div class="item-content-item">
  171. <span class="item-content-item-label">当前状态:</span>
  172. <span class="item-content-item-value">{{
  173. rectificationStatusMap[checkBookDetail?.rectificationStatus] || '-'
  174. }}</span>
  175. </div>
  176. </div>
  177. </div>
  178. <div class="operation-item1 videoAndImg">
  179. <div class="item-header"> 整改材料 </div>
  180. <div class="item-content1">
  181. <div class="item-content1-item">图片:</div>
  182. <div class="item-content1-img">
  183. <template
  184. v-for="(path, index) in checkBookDetail.rectificationImage?.split(',') || []"
  185. :key="'img' + index"
  186. >
  187. <div class="item-content1-img-item">
  188. <el-image
  189. class="img-item"
  190. :src="buildFileUrl(path)"
  191. :preview-src-list="[buildFileUrl(path)]"
  192. fit="cover"
  193. preview-teleported
  194. />
  195. </div>
  196. </template>
  197. </div>
  198. </div>
  199. <div class="item-content1">
  200. <div class="item-content1-item">视频:</div>
  201. <div class="item-content1-img">
  202. <template
  203. v-for="(path, index) in checkBookDetail.rectificationVideo?.split(',') || []"
  204. :key="'vid' + index"
  205. >
  206. <div class="item-content1-img-item" @click="handlePreview(buildFileUrl(path), 'video')">
  207. <video class="img-item" :src="buildFileUrl(path)"></video>
  208. <div class="video-play-overlay">
  209. <el-icon :size="28"><VideoPlay /></el-icon>
  210. </div>
  211. </div>
  212. </template>
  213. </div>
  214. </div>
  215. </div>
  216. </div>
  217. </div>
  218. </div>
  219. </template>
  220. <template v-else>
  221. <div class="right-panel-container">
  222. <!-- 收缩展开按钮 -->
  223. <div class="toggle-btn" @click="togglePanel" :class="{ 'collapsed': !isExpanded }" v-if="!onlyShowPdf">
  224. <el-icon>
  225. <Back v-if="!isExpanded" />
  226. <Right v-else />
  227. </el-icon>
  228. </div>
  229. <div class="operation-panel" :class="{ 'expanded': isExpanded, 'collapsed': !isExpanded }" v-if="!onlyShowPdf">
  230. <div class="operation-inner">
  231. <!-- 流转记录 -->
  232. <div class="operation-item">
  233. <div class="item-header"> 流转记录 </div>
  234. <div class="item-content">
  235. <el-empty
  236. v-if="!recordList.length"
  237. description="暂无流转记录"
  238. :image-size="120"
  239. />
  240. <div class="record-item" v-for="record in recordList" :key="record.id">
  241. <div class="record-item-title">{{ PressureReportType['SUGGUESTION'] === props.selectedItem?.reportType ? record.processName : PressureCheckerMyTaskStatusMap[record.process]
  242. }}</div>
  243. <div class="record-item-inner">
  244. <div class="content">
  245. <span>{{ record.createUser?.nickname }}</span>
  246. <el-button
  247. :type="
  248. OAApprovalResultType[record.result] ||
  249. (record.result === 100 || record.result === '100'
  250. ? 'success'
  251. : record.result === 200 || record.result === '200'
  252. ? 'danger'
  253. : 'default')
  254. "
  255. round
  256. size="small"
  257. >{{
  258. OAApprovalResultMap[record.result] ||
  259. (record.result === 100 || record.result === '100'
  260. ? '通过'
  261. : record.result === 200 || record.result === '200'
  262. ? '拒绝'
  263. : record.result || '-')
  264. }}</el-button
  265. >
  266. <span class="time">{{
  267. !record.createTime
  268. ? '-'
  269. : dayjs(record.createTime).format('YYYY-MM-DD HH:mm:ss')
  270. }}</span>
  271. </div>
  272. <p class="desc">
  273. <span>描述</span>
  274. {{ record.remark && record.remark.trim() !== '' ? record.remark : '-' }}
  275. </p>
  276. </div>
  277. </div>
  278. </div>
  279. </div>
  280. <!-- 智能纠错 -->
  281. <div class="operation-item">
  282. <div class="item-header"> 智能纠错 </div>
  283. <div class="item-content">
  284. <el-empty
  285. v-if="!checkInputList.length"
  286. description="暂无纠错内容"
  287. :image-size="120"
  288. />
  289. <div class="error-item" v-for="input in checkInputList" :key="input.code">
  290. <el-icon color="#E0534E" :size="20"><InfoFilled /></el-icon>
  291. {{ input.code }}:{{ input.name }}
  292. </div>
  293. </div>
  294. </div>
  295. <!-- 历史版本 -->
  296. <div class="operation-item">
  297. <div class="item-header"> 历史版本 </div>
  298. <div class="item-content" v-if="historyList.length">
  299. <!-- <template> -->
  300. <div class="history-item" v-for="item in historyList" :key="item.id">
  301. <div class="history-title">{{ item.versionNoStr || '-' }}号版本</div>
  302. <p
  303. >修改原因:<span>{{ item.modifiedReason || '人工修改' }}</span></p
  304. >
  305. <div class="history-footer">
  306. <span class="name">{{ item.creatorName || '-' }}</span>
  307. <span class="time"
  308. >修改于{{
  309. dayjs(item.updateTime).format('YYYY-MM-DD HH:mm:ss') || '-'
  310. }}</span
  311. >
  312. </div>
  313. <div class="my-2">
  314. <el-button type="primary" link @click="handleShowVersionInfo(item)"
  315. >查看详情</el-button
  316. >
  317. <el-button v-if="!checkerIsLoginUser" type="primary" link @click="restoreVersion(item)"
  318. >恢复版本</el-button
  319. >
  320. </div>
  321. </div>
  322. <!-- </template> -->
  323. </div>
  324. <template v-else>
  325. <el-empty description="暂无历史版本" :image-size="120" />
  326. </template>
  327. </div>
  328. </div>
  329. </div>
  330. </div>
  331. </template>
  332. </div>
  333. <!-- 审批人选择对话框 -->
  334. <AuditUserDialog
  335. v-if="isShowApprovalDialog"
  336. v-model="isShowApprovalDialog"
  337. :apiFn="getRecheckUserList"
  338. title="请选择审批人"
  339. selectedAlertText="已选择审批人"
  340. width="700px"
  341. :columns="[
  342. {
  343. type: 'selection',
  344. fieldProps: {
  345. reserveSelection: true
  346. }
  347. },
  348. {
  349. label: '姓名',
  350. prop: 'nickname',
  351. search: {
  352. type: 'input',
  353. span: 12,
  354. prop: 'nickName'
  355. }
  356. },
  357. {
  358. label: '部门',
  359. prop: 'deptName',
  360. search: {
  361. type: 'input',
  362. span: 12
  363. }
  364. }
  365. ]"
  366. @confirm="handleApprovalSelectConfirm"
  367. />
  368. <Dialog
  369. v-model="isShowAuditDialog"
  370. width="400"
  371. :title="schemaFlag == 'proofread' ? '请选择校核人' : '请选择审核人'"
  372. >
  373. <CustomLargeListSelect
  374. v-model="form.recheckUser"
  375. :fetchFunc="getRecheckUserList"
  376. label-key="nickname"
  377. value-key="id"
  378. searchKeyProp="nickName"
  379. @change="handleChangeEntrustUnit"
  380. />
  381. <template #footer>
  382. <el-button type="primary" @click="handleAuditSelectConfirm" :loading="submitting">保 存</el-button>
  383. <el-button @click="isShowAuditDialog = false">取 消</el-button>
  384. </template>
  385. </Dialog>
  386. <!-- 批量提交校核 -->
  387. <Dialog
  388. v-model="showBatchSubmitToRecheckDialog"
  389. width="400"
  390. title="批量提交校核"
  391. >
  392. <el-form ref="batchRecheckFormRef" :model="batchRecheckForm" :rules="batchRecheckFormRules" label-width="80px">
  393. <el-form-item label="提交记录" prop="reportIds">
  394. <el-select
  395. v-model="batchRecheckForm.reportIds"
  396. multiple
  397. clearable
  398. placeholder="请选择报告"
  399. >
  400. <el-option
  401. v-for="item in getCanSubmitRecheckReport"
  402. :key="item.id"
  403. :value="item.id"
  404. :label="item.reportName"
  405. />
  406. </el-select>
  407. </el-form-item>
  408. <el-form-item label="校核人" prop="recheckUser">
  409. <CustomLargeListSelect
  410. v-model="batchRecheckForm.recheckUser"
  411. :fetchFunc="getRecheckUserList"
  412. label-key="nickname"
  413. value-key="id"
  414. searchKeyProp="nickName"
  415. @change="handleChangeEntrustUnit"
  416. placeholder="请选择校核人"
  417. />
  418. </el-form-item>
  419. <el-form-item label="">
  420. <span style="font-size: 13px; white-space: nowrap; margin-top: -16px; color: #999;">
  421. 您已完成{{getCanSubmitRecheckReport.length}}份记录录入,可以进行批量提交校核
  422. </span>
  423. </el-form-item>
  424. </el-form>
  425. <template #footer>
  426. <el-button type="primary" @click="handleBatchSubmitToRecheck">提 交</el-button>
  427. <el-button @click="showBatchSubmitToRecheckDialog = false">取 消</el-button>
  428. </template>
  429. </Dialog>
  430. <!-- 校核人选择对话框 暂时作废
  431. -->
  432. <!-- <AuditUserDialog
  433. v-if="isShowAuditDialog"
  434. v-model="isShowAuditDialog"
  435. :apiFn="getRecheckUserList"
  436. title="请选择校核人"
  437. selectedAlertText="已选择校核人"
  438. width="700px"
  439. :pageType="0"
  440. :columns="[
  441. {
  442. type: 'selection',
  443. fieldProps: {
  444. reserveSelection: true
  445. }
  446. },
  447. {
  448. label: '姓名',
  449. prop: 'nickname',
  450. search: {
  451. type: 'input',
  452. span: 12,
  453. prop: 'nickName'
  454. }
  455. },
  456. {
  457. label: '部门',
  458. prop: 'deptName',
  459. search: {
  460. type: 'input',
  461. span: 12
  462. }
  463. }
  464. ]"
  465. @confirm="handleAuditSelectConfirm"
  466. /> -->
  467. <!-- 报告审核人选择对话框 -->
  468. <Dialog
  469. v-model="isShowReportAuditDialog"
  470. width="400"
  471. title="请选择审核人"
  472. >
  473. <el-select
  474. v-model="reportAuditUserCode"
  475. filterable
  476. placeholder="请选择审核人"
  477. :loading="reportAuditUserLoading"
  478. clearable
  479. style="width: 100%"
  480. >
  481. <el-option
  482. v-for="item in reportAuditUserOptions"
  483. :key="item.code"
  484. :label="item.name"
  485. :value="item.code"
  486. />
  487. </el-select>
  488. <template #footer>
  489. <el-button type="primary" :loading="reportAuditSubmitting" @click="handleReportAuditSelectConfirm">确 定</el-button>
  490. <el-button @click="isShowReportAuditDialog = false">取 消</el-button>
  491. </template>
  492. </Dialog>
  493. </el-row>
  494. <!-- 检验结果录入-葡萄城模板 -->
  495. <InlineInspectionResultInput
  496. v-if="showInlineInspectionResultInput"
  497. v-model:visible="showInlineInspectionResultInput"
  498. :template-params="inspectionResultTemplateParams"
  499. @confirm="handleInspectionResultConfirm"
  500. @cancel="handleReturnToNormalMode"
  501. />
  502. <!-- 检验录入-模板 -->
  503. <!-- <InlineEditCheckRecord-->
  504. <!-- v-if="showInlineEditCheckRecord"-->
  505. <!-- :templateId="templateId"-->
  506. <!-- v-model:visible="showInlineEditCheckRecord"-->
  507. <!-- :report-list="reportList"-->
  508. <!-- :task-order-item="taskOrderItem"-->
  509. <!-- :template-params="templateParams"-->
  510. <!-- @confirm="handleTemplateConfirm"-->
  511. <!-- @cancel="handleRefresh"-->
  512. <!-- />-->
  513. <!-- 检验录入-模板 -->
  514. <CustomDialog v-model="showInlineEditCheckRecord" :show-footer="false" fullscreen :show-close="false">
  515. <SpreadViewer :initData="editData" ref="editSpreadRecordRef" @saveSuccess="saveSuccessRecord" @close="handleClose">
  516. <template #title>
  517. <div style="font-size: 20px; ">检验录入</div>
  518. </template>
  519. </SpreadViewer>
  520. </CustomDialog>
  521. <!-- 报告编制 - 模板 -->
  522. <!-- <InlineReportEdit-->
  523. <!-- v-if="showInlineReportEdit"-->
  524. <!-- v-model:visible="showInlineReportEdit"-->
  525. <!-- :template-params="templateParams"-->
  526. <!-- :report-list="reportList || []"-->
  527. <!-- :report-type="showReportPdfType"-->
  528. <!-- @confirm="handleTemplateConfirm"-->
  529. <!-- @cancel="handleRefresh"-->
  530. <!-- @refresh="()=>{refreshPdf = !refreshPdf}"-->
  531. <!-- />-->
  532. <!-- 报告编制 - 模板 -->
  533. <CustomDialog v-model="showInlineReportEdit" :show-footer="false" fullscreen :show-close="false">
  534. <SpreadViewer :initData="editData" ref="editSpreadReportRef" @saveSuccess="saveSuccessReport" @close="handleClose">
  535. <template #title>
  536. <div style="font-size: 20px; ">报告编制</div>
  537. </template>
  538. </SpreadViewer>
  539. </CustomDialog>
  540. <!-- 修改主检人弹窗 -->
  541. <UserSelectForm ref="userSelectFormRef" @confirm="handleUserSelect" :single="true" />
  542. <CustomDialog v-model="showReportDialog" :show-footer="false" width="1000px">
  543. <!-- <VuePdfEmbed
  544. v-loading="pdfLoading"
  545. :height="500"
  546. :width="968"
  547. :source="reportSource"
  548. @rendered="handlePdfRendered"
  549. />-->
  550. <SpreadViewer :initData="reportInitData" ref="reportSpreadRef" />
  551. </CustomDialog>
  552. <!-- 查看版本详情的弹窗 -->
  553. <CustomDialog v-model="showVersionDetail" title="查看详情" :show-footer="false" width="1000px">
  554. <SmartTable
  555. v-model:columns="versionColumns"
  556. :buttons="versionDetailButtons"
  557. :data="versionDataCompareList"
  558. :is-pagination="false"
  559. :show-refresh="false"
  560. />
  561. </CustomDialog>
  562. <!-- 图片预览对话框 -->
  563. <el-dialog
  564. v-model="previewVisible"
  565. class="preview-dialog"
  566. title="Warning"
  567. width="500"
  568. :show-footer="false"
  569. :show-close="false"
  570. align-center
  571. >
  572. <div class="preview-container">
  573. <video class="preview-video" v-if="previewType === 'video'" :src="previewUrl" auto-play controls></video>
  574. <img class="preview-image" v-else :src="previewUrl" />
  575. </div>
  576. </el-dialog>
  577. <dialog ref="previewDialogRef"></dialog >
  578. <ReportListUploadModal
  579. ref="reportListUploadModalRef"
  580. :selectedItem="selectedItem"
  581. :reportList="reportList"
  582. @confirm="handleRefresh"
  583. />
  584. <!-- 回退原因弹窗(面板收缩时使用) -->
  585. <el-dialog
  586. v-model="rejectionReasonDialogVisible"
  587. title="请输入回退原因"
  588. width="450px"
  589. :close-on-click-modal="false"
  590. >
  591. <el-form ref="rejectionReasonDialogFormRef" :model="tempRejectionReason" :rules="returnFormRules">
  592. <el-form-item label="回退原因" prop="rejectionReason">
  593. <el-input
  594. v-model="tempRejectionReason.rejectionReason"
  595. type="textarea"
  596. :rows="5"
  597. maxlength="100"
  598. placeholder="请输入回退原因"
  599. />
  600. </el-form-item>
  601. </el-form>
  602. <template #footer>
  603. <el-button @click="rejectionReasonDialogVisible = false">取消</el-button>
  604. <el-button type="primary" @click="handleRejectionReasonDialogConfirm">确定回退</el-button>
  605. </template>
  606. </el-dialog>
  607. </div>
  608. <!-- 新加 -->
  609. <Teleport v-if="teleportBtnRef" to=".teleport-btn">
  610. <template v-if="showCheckBook && getReportStatusEnd">
  611. <div v-if="[0, 2].includes(checkBookDetail.rectificationStatus)" class="default-toolbar">
  612. <el-button type="success" size="small" @click="() => handlePass(0)">整改通过</el-button>
  613. <el-button type="success" size="small" @click="() => handlePass(2)">整改不通过</el-button>
  614. <el-button type="primary" size="small" @click="handleRectificationMaterials">上传整改材料</el-button>
  615. <template v-if="checkBookDetail.rectificationStatus === 2">
  616. <el-button type="primary" size="small" @click="() => handlePass(1)">回退</el-button>
  617. </template>
  618. </div>
  619. <div v-if="[3].includes(checkBookDetail.rectificationStatus)" class="default-toolbar">
  620. <el-button type="primary" size="small" @click="handleRectificationMaterials">上传整改材料</el-button>
  621. </div>
  622. </template>
  623. <template v-else>
  624. <div class="operation-btns">
  625. <!-- 报告办结后,这部分按钮不再显示 -->
  626. <template v-if="!getReportStatusEnd && selectedItem">
  627. <!-- 根据报告类型和配置显示相应按钮 -->
  628. <template
  629. v-if="selectedItem.reportType === PressureReportType.SINGLE"
  630. >
  631. <!-- 独审报告根据配置显示按钮 -->
  632. <el-button
  633. v-if="isCanSubmitToAudit"
  634. type="danger"
  635. @click="handleSubmitAudit"
  636. :disabled="checkerIsLoginUser"
  637. size="small"
  638. >
  639. 提交报告审核
  640. </el-button>
  641. <!-- 没有审核只有审批 -->
  642. <!-- <el-button
  643. v-if="
  644. currentReportConfig?.isRatify &&
  645. !currentReportConfig?.isApproval &&
  646. isCanSubmitToAudit
  647. "
  648. type="danger"
  649. @click="handleApprovalSelectConfirm"
  650. :disabled="checkerIsLoginUser"
  651. size="small"
  652. >
  653. 提交报告审批
  654. </el-button> -->
  655. </template>
  656. <!-- 非独审报告默认显示审核按钮 -->
  657. <el-button
  658. v-if="
  659. selectedItem.reportType !== PressureReportType.SUB &&
  660. selectedItem.reportType !== PressureReportType.SINGLE &&
  661. isCanSubmitToAudit &&
  662. 'BoilerCheckerTaskDetail' === routeName
  663. "
  664. type="danger"
  665. @click="handleSubmitAudit"
  666. :disabled="checkerIsLoginUser"
  667. size="small"
  668. >
  669. {{ selectedItem.reportType === PressureReportType['SUGGUESTION'] ? '提交审核' : '提交报告审核' }}
  670. </el-button>
  671. <el-button
  672. v-if="selectedItem.reportType === PressureReportType.SUB && selectedItem.taskStatus == PressureCheckerMyTaskStatus.REPORT_INPUT "
  673. type="danger"
  674. @click="handleCompletion"
  675. :disabled="checkerIsLoginUser"
  676. size="small"
  677. >
  678. 报告办结
  679. </el-button>
  680. <el-button type="primary" size="small" plain v-if="isCanEditReport" @click="handleEditRreport" :disabled="checkerIsLoginUser"
  681. >编制报告</el-button>
  682. <el-button v-if="isCanSubmitRecheck" type="primary" @click="handleSelectVerfiyer" :disabled="checkerIsLoginUser" size="small"
  683. >提交校核</el-button>
  684. <el-button
  685. type="primary"
  686. v-if="isCanEditTestRecord"
  687. plain
  688. @click="handleEditSpreadRecord"
  689. :disabled="checkerIsLoginUser"
  690. size="small"
  691. >{{ selectedItem.reportType === PressureReportType['SUGGUESTION'] ? '编制意见书' : '填写记录' }}</el-button>
  692. <!-- 撤销流程:报告审核或审批阶段,有OA流程ID且当前用户是发起人时显示 -->
  693. <el-button
  694. v-if="isCanCancelFlow"
  695. type="warning"
  696. @click="handleCancelFlow"
  697. size="small"
  698. >撤销流程</el-button>
  699. <!-- OA审核:报告审核或审批阶段,有OA流程ID时显示 -->
  700. <el-button
  701. v-if="isCanOAAudit"
  702. type="success"
  703. @click="handleOpenOAAudit"
  704. size="small"
  705. >OA审核</el-button>
  706. </template>
  707. </div>
  708. </template>
  709. </Teleport>
  710. <AssociationOperationManual
  711. v-if="showAssociationOperationManual"
  712. :selectedItem="selectedItem"
  713. :reportList="reportList"
  714. :equipCode="props.taskOrderItem?.equipCode"
  715. @close="()=>{
  716. showAssociationOperationManual = false
  717. handleRefresh()
  718. }"
  719. />
  720. <!-- 上传整改材料弹窗 -->
  721. <Dialog
  722. v-model="ectificationMaterialsVisible"
  723. title="上传整改材料"
  724. width="750px"
  725. style="margin-bottom: 0px; height: 100vh"
  726. top="0vh"
  727. :scroll="true"
  728. height="100vh"
  729. :maxHeight="'calc(100vh - 55px - 60px - 63px)'"
  730. @close="handleClosePectificationMaterialsDialog"
  731. >
  732. <div v-loading="ectificationMaterialsLoading" class="rectification-dialog">
  733. <!-- 检验意见通知书上传 -->
  734. <div class="upload-section required">
  735. <div class="section-header">
  736. <div class="header-left">
  737. <i class="icon-document"></i>
  738. <span class="section-title">检验意见通知书</span>
  739. <span class="required-mark">*</span>
  740. </div>
  741. <span class="section-tip">仅支持PDF格式,限1个文件</span>
  742. </div>
  743. <div class="upload-area">
  744. <RectificationUpload
  745. v-model:fileList="ectificationMaterialsData.rectificationUrl"
  746. apiUrl="infra/file/upload"
  747. :limit="1"
  748. :disabled="ectificationMaterialsData.rectificationUrl.length >= 1 || ectificationMaterialsLoading"
  749. accept=".pdf"
  750. :default-file-name="selectedItem?.reportName || '检验意见通知书'"
  751. />
  752. </div>
  753. </div>
  754. <!-- 整改图片上传 -->
  755. <div class="upload-section">
  756. <div class="section-header">
  757. <div class="header-left">
  758. <i class="icon-image"></i>
  759. <span class="section-title">整改图片</span>
  760. </div>
  761. <span class="section-tip">支持JPG、PNG格式,最多20张,单个不超过2MB</span>
  762. </div>
  763. <div class="upload-area">
  764. <RectificationUpload
  765. v-model:fileList="ectificationMaterialsData.rectificationImage"
  766. apiUrl="infra/file/upload"
  767. :limit="20"
  768. :disabled="ectificationMaterialsData.rectificationImage.length >= 20 || ectificationMaterialsLoading"
  769. accept=".jpg,.jpeg,.png"
  770. :multiple="true"
  771. :maxSize="2097152"
  772. />
  773. </div>
  774. </div>
  775. <!-- 整改视频上传 -->
  776. <div class="upload-section">
  777. <div class="section-header">
  778. <div class="header-left">
  779. <i class="icon-video"></i>
  780. <span class="section-title">整改视频</span>
  781. </div>
  782. <span class="section-tip">支持MP4格式,最多5个,单个不超过10MB</span>
  783. </div>
  784. <div class="upload-area">
  785. <RectificationUpload
  786. v-model:fileList="ectificationMaterialsData.rectificationVideo"
  787. apiUrl="infra/file/upload"
  788. :limit="5"
  789. :disabled="ectificationMaterialsData.rectificationVideo.length >= 5 || ectificationMaterialsLoading"
  790. accept=".mp4"
  791. :multiple="true"
  792. :maxSize="10485760"
  793. />
  794. </div>
  795. </div>
  796. <!-- 温馨提示 -->
  797. <div class="info-tips">
  798. <i class="icon-info"></i>
  799. <div class="tips-content">
  800. <p class="tips-title">温馨提示:</p>
  801. <p>1. 请确保上传的文件清晰可辨,以便审核</p>
  802. <p>2. 整改材料将作为审核依据,请如实上传</p>
  803. </div>
  804. </div>
  805. </div>
  806. <template #footer>
  807. <el-button @click="()=> ectificationMaterialsVisible = false">取消</el-button>
  808. <el-button
  809. :loading="ectificationMaterialsLoading"
  810. type="primary"
  811. @click="handleSubmitChangeEctificationMaterialsDialog"
  812. >
  813. 确定提交
  814. </el-button>
  815. </template>
  816. </Dialog>
  817. <CustomDialog
  818. ref="customUnitDialogRef"
  819. title="选择审核人"
  820. v-model="approvalUserVisible"
  821. width="650px"
  822. :dialogAttrs="{ zIndex: 10006 }"
  823. >
  824. <el-form ref="formRef" :model="formData" :rules="formRules" label-width="150px">
  825. <el-form-item label="意见通知书审核人" prop="inspectionAuditId">
  826. <el-select v-model="formData.inspectionAuditId" clearable placeholder="请选择">
  827. <el-option
  828. v-for="item in optionList.schemeReviewerList.list"
  829. :key="item.id"
  830. :label="item.nickname"
  831. :value="item.id"
  832. />
  833. </el-select>
  834. </el-form-item>
  835. </el-form>
  836. <template #footer>
  837. <el-button @click="approvalUserVisible = false">取消</el-button>
  838. <el-button type="primary" @click="handleSubmitUser">确定</el-button>
  839. </template>
  840. </CustomDialog>
  841. </template>
  842. <script setup lang="tsx">
  843. import SmartTable from '@/components/SmartTable/SmartTable'
  844. import RectificationUpload from '@/views/pressure2/components/RectificationUpload.vue'
  845. import { computed, nextTick, reactive, ref, watch } from 'vue'
  846. import { useRouter, useRoute } from 'vue-router'
  847. import { InfoFilled, View, Back, Right, WarningFilled, VideoPlay } from '@element-plus/icons-vue'
  848. import {dayjs, ElLoading, ElMessage, ElMessageBox, FormInstance, FormRules} from 'element-plus'
  849. const CustomDialog = defineAsyncComponent(() => import('@/components/CustomDialog/index.vue'))
  850. import VuePdfEmbed from 'vue-pdf-embed'
  851. import { getReportAllowedApplyModify, getReportCheckPdf } from '@/api/laboratory/functional/report'
  852. import * as UserApi from '@/api/system/user'
  853. import {
  854. PressureCheckerMyTaskStatusMap,
  855. PressureCheckerMyTaskStatus,
  856. PressureReportType,
  857. PressureTaskOrderTaskStatus,
  858. SugguestionApproveConfig,
  859. } from '@/utils/constants'
  860. import { OAApprovalResultMap, OAApprovalResultType } from '@/utils/pressure2/oaConstants'
  861. import type {
  862. ReportItemVO,
  863. BoilerTaskOrderDetailVO,
  864. BoilerTaskOrderOrderItemVO
  865. } from '@/api/pressure2/boilertaskorder'
  866. import InspectionItemProgress from './InspectionItemProgress.vue'
  867. import InlineEditCheckRecord from './InlineEditCheckRecord.vue'
  868. import InlinePdfViewer from './InlinePdfViewer.vue'
  869. import InlineInspectionResultInput from './InlineInspectionResultInput.vue'
  870. import InlineReportEdit from './InlineReportEdit.vue'
  871. import { BoilerTaskOrderApi } from '@/api/pressure2/boilertaskorder'
  872. import { defineAsyncComponent } from 'vue'
  873. import UserSelectForm from '@/components/UserSelectForm/index.vue'
  874. import { reportInfoApi } from '@/api/reportInfoService'
  875. import { buildFileUrl } from '@/utils'
  876. import { useUserStore } from '@/store/modules/user'
  877. const AuditUserDialog = defineAsyncComponent(
  878. () => import('@/views/Functional/components/AuditUserDialog.vue')
  879. )
  880. import { is } from '@/utils/is'
  881. import _ from 'lodash'
  882. import { uploadFile } from '@/api/common/index'
  883. import ReportListUploadModal from './reportListUploadModal.vue'
  884. import AssociationOperationManual from './AssociationOperationManual.vue'
  885. import {DynamicTbInsApi} from "@/api/pressure2/dynamictbins";
  886. import {SpreadViewer} from "@/components/DynamicReport";
  887. import {InitParams} from "@/components/DynamicReport/SpreadInterface";
  888. import { cloneDeep, debounce } from 'lodash-es'
  889. import {PipeTaskOrderApi} from "@/api/pressure2/pipetaskorder";
  890. import * as UserGroupApi from '@/api/bpm/userGroup'
  891. interface Props {
  892. selectedItem: ReportItemVO | null
  893. taskInfo: BoilerTaskOrderDetailVO | null
  894. taskOrderItem: BoilerTaskOrderOrderItemVO
  895. isAuditMode?: boolean
  896. reportId?: string | null
  897. reportList?: ReportItemVO[]
  898. taskId: string
  899. teleportBtnRef: Ref<HTMLDivElement | null>
  900. isFullscreen?: boolean
  901. }
  902. interface Emits {
  903. (e: 'refresh'): void
  904. (e: 'template-confirm', templateUrl?: string): void
  905. (e: 'modify-checker', item: ReportItemVO): void
  906. (e: 'submit-approval'): void
  907. (e: 'reject'): void
  908. (e: 'cancel'): void
  909. (e: 'submit-ratify'): void
  910. (e: 'update:selected-item', item: ReportItemVO): void
  911. }
  912. const props = defineProps<Props>()
  913. const emit = defineEmits<Emits>()
  914. const route = useRoute()
  915. const userStore = useUserStore()
  916. const schemaFlag = ref<string | null>(null) //等于proofread = 校核 audit=审核
  917. // 获取当前的路由Name
  918. const routeName = computed(() => route.name)
  919. const form = ref<Record<string, any>>({
  920. recheckUser: {}
  921. })
  922. const refreshPdf = ref(true)
  923. // 视图模式状态管理
  924. const viewMode = ref<'normal' | 'inspection-result'>('normal')
  925. // 批量提交的表单
  926. const batchRecheckForm = ref({
  927. reportIds: [],
  928. recheckUser: {}
  929. })
  930. const batchRecheckFormRules = {
  931. reportIds: [{required: true, message: '请选择提交记录', trigger: 'change'}],
  932. recheckUser: [{required: true, message: '请选择校核人', trigger: 'change'}]
  933. }
  934. const batchRecheckFormRef = ref()
  935. // const isShowReportPdf = ref([PressureCheckerMyTaskStatus.REPORT_INPUT,PressureCheckerMyTaskStatus.REPORT_AUDIT,PressureCheckerMyTaskStatus.REPORT_APPROVE,PressureCheckerMyTaskStatus.REPORT_END].includes(props.selectedItem.taskStatus)
  936. // && ![400,500,600,700].includes(props.selectedItem.reportType))
  937. const showReportPdfType = ref('record')
  938. const refreshPdfFlag = ref(true)
  939. // const pdfPanelRef = ref<HTMLElement | null>(null)
  940. const hasRecord = ref(true)
  941. const hasReport = ref(true)
  942. // 格式化记录结果状态显示
  943. const formatRecordResult = (record) => {
  944. const resultMap = {
  945. 100: '通过',
  946. 200: '拒绝',
  947. 300: '已阅',
  948. 400: '同意',
  949. 500: '不同意'
  950. }
  951. return resultMap[record.result] || record.result || '-'
  952. }
  953. // 获取记录结果的按钮类型
  954. const getRecordResultButtonType = (record) => {
  955. if ([100, 300, 400].includes(record.result)) {
  956. return 'success'
  957. } else if ([200, 500].includes(record.result)) {
  958. return 'danger'
  959. } else {
  960. return 'default'
  961. }
  962. }
  963. const reportSpreadRef=ref();
  964. const spreadRef=ref();
  965. const initData=ref<InitParams>(
  966. {
  967. templateId: '',
  968. refId: '',
  969. refName:'',
  970. insId:'',
  971. opType: 0, // 0:excel,1: pdf
  972. manualUrl: '',
  973. });
  974. const editSpreadRecordRef=ref();
  975. const editSpreadReportRef=ref();
  976. const editData=ref<InitParams>(
  977. {
  978. templateId: '',
  979. refId: '',
  980. refName:'',
  981. insId:'',
  982. opType: 0, // 0:excel,1: pdf
  983. manualUrl: '',
  984. });
  985. const reportInitData=ref<InitParams>(
  986. {
  987. templateId: '',
  988. refId: '',
  989. refName:'',
  990. insId:'',
  991. opType: 1, // 0:excel,1: pdf
  992. });
  993. // 选择检验意见审核人
  994. const formRef = ref<InstanceType<typeof ElForm>>()
  995. const optionList = reactive<{
  996. schemeReviewerList: Recordable[]
  997. }>({
  998. schemeReviewerList: []
  999. })
  1000. const formData = ref<Recordable>({})
  1001. const formRules = reactive({
  1002. inspectionAuditId: [{ required: true, message: '请选择审核人', trigger: 'change' }]
  1003. })
  1004. const approvalUserVisible = ref(false)
  1005. const handleSubmitUser = () => {
  1006. formRef.value?.validate(async (valid) => {
  1007. if (valid) {
  1008. const submitResult = await BoilerTaskOrderApi.submitOpinionNoticeApproval({
  1009. id: templateParams.value?.id,
  1010. auditUserIds: [formData.value.inspectionAuditId],
  1011. reportType: PressureReportType['SUGGUESTION']
  1012. })
  1013. if (submitResult) {
  1014. approvalUserVisible.value = false
  1015. // 这里可以做页面刷新
  1016. emit('template-confirm')
  1017. }
  1018. }
  1019. })
  1020. }
  1021. // 获取可以批量提交的报告
  1022. const getCanSubmitRecheckReport = computed(() => {
  1023. if(!props.reportList || !props.reportList.length) return []
  1024. return props.reportList.filter((report: any) => {
  1025. // 主报告、子报告、独审报告才有记录校核
  1026. const reportTypes = [PressureReportType['SUB'], PressureReportType['SINGLE']]
  1027. const status = PressureTaskOrderTaskStatus['RECORD_INPUT'] // 记录录入状态
  1028. // report.reportUrl 已经录入数据
  1029. // if(report.taskStatus !== status || !report.reportUrl) return false
  1030. if(report.taskStatus !== status) return false
  1031. else if(report.reportType === PressureReportType['MAIN'] && userStore.user.id === props.taskOrderItem?.mainCheckerUser?.id) return true
  1032. else
  1033. return reportTypes.includes(report.reportType) && (report.checkUsers?.[0]?.id === userStore.user.id || !report.checkUsers || !report.checkUsers.length)
  1034. // 过滤掉主报告存在未办结检验意见通知书时
  1035. }).filter((report: any) => {
  1036. if(report.reportType !== PressureReportType['MAIN']){
  1037. return true
  1038. }
  1039. const sugguestionReport = props.reportList?.find(x => x.reportType === PressureReportType['SUGGUESTION'])
  1040. // 不存在检验意见通知书返回true
  1041. if(!sugguestionReport) return true
  1042. // 检验意见通知书已办结,返回true
  1043. return sugguestionReport.taskStatus === PressureCheckerMyTaskStatus['REPORT_END'];
  1044. })
  1045. })
  1046. // 当意见通知书未办结时,主报告不能流转到后续流程(提交校核)
  1047. const canSubmitMainReportType = (tipsText: string)=>{
  1048. // 不是主报告,直接返回true
  1049. if(PressureReportType.MAIN !== props?.selectedItem?.reportType) return true
  1050. // 判断检验意见通知书的状态
  1051. const report = props.reportList?.find(x => x.reportType === PressureReportType['SUGGUESTION'])
  1052. console.log('检验意见通知书', report)
  1053. // 不存在检验意见通知书返回true
  1054. if(!report) return true
  1055. // 检验意见通知书已办结,返回true
  1056. if(report.taskStatus === PressureCheckerMyTaskStatus['REPORT_END']) return true
  1057. ElMessageBox.alert(tipsText, '提示', {
  1058. showCancelButton: false,
  1059. confirmButtonText: '确定',
  1060. type: 'warning'
  1061. })
  1062. return false
  1063. }
  1064. // 校核人选择对话框状态
  1065. const showBatchSubmitToRecheckDialog = ref(false)
  1066. const isShowAuditDialog = ref(false)
  1067. const btnLoading = ref(false)
  1068. // 检验意见通知书提交审核
  1069. const sugguestionApproveConfig = ref<any[]>(cloneDeep(SugguestionApproveConfig))
  1070. const isShowSuggestionAuditDialog = ref(false)
  1071. const suggestionFormRef = ref<FormInstance>()
  1072. const suggestionFormData = ref<any>({})
  1073. const suggestionFormRules = reactive({
  1074. sugguestionAuditId: [
  1075. { required: true, message: '请选择审核人', trigger: 'blur' }
  1076. ],
  1077. sugguestionApproveId: [
  1078. { required: true, message: '请选择审批人', trigger: 'blur' }
  1079. ]
  1080. })
  1081. const handleSelectVerfiyer = async () => {
  1082. try {
  1083. await spreadRef.value?.handleSave()
  1084. } catch (error) {
  1085. console.error('保存数据失败:', error)
  1086. ElMessage.error('保存数据失败,请重试')
  1087. return
  1088. }
  1089. const canSubmit = canSubmitMainReportType('该报告存在未办结检验意见通知书,无法提交校核')
  1090. if(!canSubmit) return
  1091. // 获取审核人\校核人配置信息
  1092. let res = await UserApi.getApprovalDetail({})
  1093. // 如果存在多份待提交校核的报告,执行批量校核弹窗
  1094. if(getCanSubmitRecheckReport.value.length > 1) {
  1095. if(res && res.recheckUser) {
  1096. batchRecheckForm.value.recheckUser = res.recheckUser
  1097. }
  1098. batchRecheckForm.value.reportIds = getCanSubmitRecheckReport.value.map(x => x?.id)
  1099. showBatchSubmitToRecheckDialog.value = true
  1100. return
  1101. }
  1102. // 只有一份待提交校核的报告,执行以下逻辑
  1103. if(res && res.recheckUser) {
  1104. form.value.recheckUser = res.recheckUser
  1105. }
  1106. schemaFlag.value = 'proofread'
  1107. isShowAuditDialog.value = true
  1108. }
  1109. // const handleAuditSelectConfirm = async (res: any) => {
  1110. // console.log('handleAuditSelectConfirm', res)
  1111. // if (!props.selectedItem || !res[0]) {
  1112. // return
  1113. // }
  1114. // const saveResult = await BoilerTaskOrderApi.submitTaskReportTemplate({
  1115. // id: props.selectedItem.id,
  1116. // recheckId: res[0]
  1117. // })
  1118. // if (saveResult) {
  1119. // ElMessage.success('提交校核成功!')
  1120. // // 这里可以做页面刷新
  1121. // emit('template-confirm')
  1122. // }
  1123. // }
  1124. // 批量提交
  1125. const handleBatchSubmitToRecheck = async () => {
  1126. try {
  1127. await batchRecheckFormRef.value.validate()
  1128. } catch (err) {
  1129. ElMessage.error('请完善表单数据!')
  1130. return
  1131. }
  1132. if (_.isEmpty(batchRecheckForm.value.recheckUser)) {
  1133. return ElMessage.error('请选择校核人')
  1134. }
  1135. // 待提交的数据
  1136. const params = {
  1137. reportList: getCanSubmitRecheckReport.value.filter((x: any) => batchRecheckForm.value.reportIds.includes(x?.id)).map((x: any) => ({
  1138. id: x.id,
  1139. reportUrl: x.reportUrl,
  1140. dataJson: x?.prepareJson
  1141. })),
  1142. recheckId: batchRecheckForm.value.recheckUser?.id
  1143. }
  1144. const submitResult = await BoilerTaskOrderApi.batchSubmitToRecheck(params)
  1145. if (submitResult) {
  1146. ElMessage.success('提交校核成功!')
  1147. showBatchSubmitToRecheckDialog.value = false
  1148. selectNextItem(getCanSubmitRecheckReport.value.filter((x: any) => batchRecheckForm.value.reportIds.includes(x?.id)))
  1149. // 这里可以做页面刷新
  1150. emit('template-confirm')
  1151. }
  1152. }
  1153. const submitting = ref(false)
  1154. const handleAuditSelectConfirm = async () => {
  1155. if (_.isEmpty(form.value.recheckUser)) {
  1156. return ElMessage.error(schemaFlag.value == 'proofread' ? '请选择校核人' : '请选择审核人')
  1157. }
  1158. // 审核
  1159. if (schemaFlag.value === 'audit') {
  1160. submitting.value = true
  1161. const submitResult = await BoilerTaskOrderApi.submitReportAudit({
  1162. id: templateParams.value?.id,
  1163. approveId: form.value?.recheckUser?.id
  1164. })
  1165. submitting.value = false
  1166. if (submitResult) {
  1167. ElMessage.success('提交审核成功!')
  1168. isShowAuditDialog.value = false
  1169. selectNextItem([props.selectedItem])
  1170. // 这里可以做页面刷新
  1171. emit('template-confirm')
  1172. }
  1173. } else if (schemaFlag.value === 'proofread') {
  1174. // 校核
  1175. const saveResult = await BoilerTaskOrderApi.submitTaskReportTemplate({
  1176. id: props?.selectedItem?.id,
  1177. recheckId: form.value?.recheckUser?.id
  1178. })
  1179. if (saveResult) {
  1180. ElMessage.success('提交校核成功!')
  1181. isShowAuditDialog.value = false
  1182. selectNextItem([props.selectedItem])
  1183. // 这里可以做页面刷新
  1184. emit('template-confirm')
  1185. }
  1186. }
  1187. }
  1188. /**
  1189. * 项目切换
  1190. */
  1191. const selectNextItem = (items:ReportItemVO[]) => {
  1192. // 找到其他在记录录入或者报告编制状态的第一项目
  1193. const ids = items.map(item => item.id)
  1194. const inputReportList= props.reportList.filter(
  1195. item => {
  1196. return !ids.includes(item.id)
  1197. }
  1198. ).filter(item =>{
  1199. return item.taskStatus === PressureTaskOrderTaskStatus['RECORD_INPUT'] || item.taskStatus === PressureTaskOrderTaskStatus['REPORT_INPUT']
  1200. })
  1201. console.log(inputReportList)
  1202. if (inputReportList.length > 0){
  1203. emit('item-select', inputReportList[0])
  1204. }
  1205. }
  1206. // 审批人选择对话框状态
  1207. const isShowApprovalDialog = ref(false)
  1208. // 监听项目切换,自动重置视图模式
  1209. watch(
  1210. () => props.selectedItem,
  1211. (newItem, oldItem) => {
  1212. if (newItem) {
  1213. showReportPdfType.value = [PressureCheckerMyTaskStatus.REPORT_INPUT,PressureCheckerMyTaskStatus.REPORT_AUDIT,PressureCheckerMyTaskStatus.REPORT_APPROVE,PressureCheckerMyTaskStatus.REPORT_END].includes(props.selectedItem.taskStatus)
  1214. && ![400,500,600,700].includes(props.selectedItem.reportType) ? 'report':'record'
  1215. handleRefreshData()
  1216. props.taskId && getReportAuditInfo()
  1217. hasRecord.value = props.selectedItem.templateId != null
  1218. if (props.selectedItem?.reportTemplateId == null){
  1219. showReportPdfType.value = 'record'
  1220. }
  1221. if (props.selectedItem?.templateId == null){
  1222. showReportPdfType.value = 'report'
  1223. }
  1224. }
  1225. // 当切换到不同的项目时,重置视图模式为normal
  1226. if (newItem?.id !== oldItem?.id) {
  1227. viewMode.value = 'normal'
  1228. }
  1229. },
  1230. { immediate: false }
  1231. )
  1232. const previewUrl = ref('')
  1233. const previewVisible = ref(false)
  1234. const previewType = ref('')
  1235. const handlePreview = (path: string, type: 'video' | 'image') => {
  1236. previewUrl.value = path;
  1237. previewType.value = type;
  1238. previewVisible.value = true;
  1239. }
  1240. /**
  1241. * 如果是"已经审核完的检验意见通知书,则查询相关的检验意见通知信息,显示在最右侧
  1242. */
  1243. const checkBookDetail = ref<Record<string, any>>({})
  1244. const showCheckBook = computed(() => {
  1245. const { reportType, taskStatus } = props.selectedItem || {}
  1246. // console.log(taskStatus, 'taskStatus', reportType)
  1247. // taskStatus: 800
  1248. return reportType === PressureReportType['SUGGUESTION']
  1249. })
  1250. const rectificationStatusMap = reactive({
  1251. 0: '待确认',
  1252. 1: '待整改',
  1253. 2: '已递交',
  1254. 3: '材料有误',
  1255. 4: '整改通过',
  1256. 5: '整改不通过'
  1257. })
  1258. const getReportAuditInfo = async () => {
  1259. if (props.selectedItem?.reportType === PressureReportType['SUGGUESTION']) {
  1260. const res = await BoilerTaskOrderApi.exportCheckBookDetail({
  1261. id: props.taskId
  1262. })
  1263. console.log(res, '查询检验意见通知书详细信息')
  1264. if (res) {
  1265. checkBookDetail.value = res
  1266. }
  1267. }
  1268. }
  1269. const returnForm = ref({
  1270. rejectionReason: ''
  1271. })
  1272. const returnFormRules = ref({
  1273. rejectionReason: [{ required: true, message: '请输入回退原因', trigger: 'blur' }]
  1274. })
  1275. const returnFormRef = ref()
  1276. const rejectionReasonDialogVisible = ref(false)
  1277. const tempRejectionReason = ref({ rejectionReason: '' })
  1278. const rejectionReasonDialogFormRef = ref()
  1279. const isCompleteInput = computed(() => {
  1280. return props.selectedItem?.taskStatus >= PressureCheckerMyTaskStatus.RECORD_CHECK
  1281. })
  1282. const isShowRecordOrReportBtn = computed(() => {
  1283. return [PressureReportType.MAIN,PressureReportType.SUB,PressureReportType.SINGLE].includes(props.selectedItem?.reportType)
  1284. })
  1285. // 通过 退回
  1286. const handlePass = async (type) => {
  1287. const params: Record<string, any> = {
  1288. reportId: props.selectedItem?.id,
  1289. approvalType: type
  1290. }
  1291. if (type === 1) {
  1292. if (isExpanded.value) {
  1293. try {
  1294. await returnFormRef.value.validate()
  1295. } catch {
  1296. ElMessage.warning('请填写回退原因')
  1297. return
  1298. }
  1299. params.rejectionReason = returnForm.value.rejectionReason
  1300. executePass(params, type)
  1301. } else {
  1302. tempRejectionReason.value.rejectionReason = returnForm.value.rejectionReason
  1303. rejectionReasonDialogVisible.value = true
  1304. }
  1305. return
  1306. }
  1307. executePass(params, type)
  1308. }
  1309. // 弹出框确认回退
  1310. const handleRejectionReasonDialogConfirm = async () => {
  1311. try {
  1312. await rejectionReasonDialogFormRef.value.validate()
  1313. } catch {
  1314. return
  1315. }
  1316. rejectionReasonDialogVisible.value = false
  1317. // 同步到主表单以便保持数据一致
  1318. returnForm.value.rejectionReason = tempRejectionReason.value.rejectionReason
  1319. const params: Record<string, any> = {
  1320. reportId: props.selectedItem?.id,
  1321. approvalType: 1,
  1322. rejectionReason: tempRejectionReason.value.rejectionReason
  1323. }
  1324. executePass(params, 1)
  1325. }
  1326. // 执行通过/退回操作
  1327. const executePass = (params: Record<string, any>, type: number) => {
  1328. const tipText = type === 1 ? '退回' : type === 2 ? '整改不通过' : '整改通过'
  1329. ElMessageBox.confirm(`确定${tipText}吗?`, '提示', {
  1330. confirmButtonText: '确定',
  1331. cancelButtonText: '取消',
  1332. type: 'warning'
  1333. }).then(() => {
  1334. BoilerTaskOrderApi.inspectionApproval(params).then((res) => {
  1335. props.taskId && getReportAuditInfo()
  1336. emit('refresh')
  1337. }).catch(() => {
  1338. ElMessage.error('操作失败')
  1339. })
  1340. })
  1341. }
  1342. // 上传整改材料
  1343. const ectificationMaterialsData = ref<any>({
  1344. rectificationUrl: [],
  1345. rectificationImage: [],
  1346. rectificationVideo: [],
  1347. })
  1348. const ectificationMaterialsVisible = ref(false)
  1349. const ectificationMaterialsLoading = ref(false)
  1350. const handleRectificationMaterials = () => {
  1351. const detail = checkBookDetail.value || {}
  1352. ectificationMaterialsData.value = {
  1353. rectificationUrl: detail.rectificationUrl ? detail.rectificationUrl.split(',').filter(Boolean).map((url: string) => ({ url })) : [],
  1354. rectificationImage: detail.rectificationImage ? detail.rectificationImage.split(',').filter(Boolean).map((url: string) => ({ url })) : [],
  1355. rectificationVideo: detail.rectificationVideo ? detail.rectificationVideo.split(',').filter(Boolean).map((url: string) => ({ url })) : [],
  1356. }
  1357. ectificationMaterialsVisible.value = true
  1358. }
  1359. const handleClosePectificationMaterialsDialog = () => {
  1360. ectificationMaterialsVisible.value = false
  1361. }
  1362. const handleSubmitChangeEctificationMaterialsDialog = async () => {
  1363. if (ectificationMaterialsData.value?.rectificationUrl?.length == 0) {
  1364. return ElMessage.error('请上传证明材料')
  1365. }
  1366. ectificationMaterialsLoading.value = true
  1367. try {
  1368. const data = {
  1369. reportId: props.selectedItem?.id || '',
  1370. businessType: 1,
  1371. rectificationUrl: ectificationMaterialsData.value?.rectificationUrl?.length ? ectificationMaterialsData.value?.rectificationUrl?.map(item => item.url)?.join(',') : '',
  1372. rectificationImage: ectificationMaterialsData.value?.rectificationImage?.length ? ectificationMaterialsData.value?.rectificationImage?.map(item => item.url)?.join(',') : '',
  1373. rectificationVideo: ectificationMaterialsData.value?.rectificationVideo?.length ? ectificationMaterialsData.value?.rectificationVideo?.map(item => item.url)?.join(',') : '',
  1374. }
  1375. const response = await BoilerTaskOrderApi.inspectionOpinionRectifyApi(data)
  1376. if (response) {
  1377. ectificationMaterialsLoading.value = false
  1378. ElMessage.success('上传成功')
  1379. ectificationMaterialsVisible.value = false
  1380. emit('refresh')
  1381. }
  1382. } catch (error) {
  1383. ectificationMaterialsLoading.value = false
  1384. }
  1385. }
  1386. // 获取检验员名称
  1387. const getCheckersName = (): string => {
  1388. if (!props.selectedItem?.checkUsers || props.selectedItem.checkUsers.length === 0) {
  1389. return '未分配'
  1390. }
  1391. return props.selectedItem.checkUsers.map((user) => user.nickname).join('、')
  1392. }
  1393. // 切换主检人
  1394. const handleUserSelect = async (id, userList) => {
  1395. const result = await BoilerTaskOrderApi.updateReportMainChecker({
  1396. id: props.taskOrderItem?.id,
  1397. mainChecker: userList[0]?.id
  1398. })
  1399. if (result) {
  1400. ElMessage.success('修改主检人成功!')
  1401. emit('refresh')
  1402. }
  1403. }
  1404. const userSelectFormRef = ref()
  1405. const handleModifyMainChecker = () => {
  1406. userSelectFormRef.value.open(props.taskOrderItem?.mainCheckerUser?.id)
  1407. }
  1408. // 判断当前检验员或主检人是否为登录用户,如果不是,则功能按钮disabled
  1409. const checkerIsLoginUser = computed(() => {
  1410. const checkerUserIds = props.selectedItem?.checkUsers.map(checker => checker?.id)
  1411. return !(checkerUserIds?.includes(userStore?.user?.id) || props.taskOrderItem?.mainCheckerUser?.id === userStore?.user?.id)
  1412. })
  1413. // 判断当前项目是否已经到报告办结阶段
  1414. const getReportStatusEnd = computed(
  1415. () =>{
  1416. return props.selectedItem && props.selectedItem.taskStatus >= PressureCheckerMyTaskStatus['REPORT_CONFIRMATION']
  1417. }
  1418. )
  1419. // 判断“提交校核”按钮是否显示
  1420. const isCanSubmitRecheck = computed(() => {
  1421. if (props.selectedItem?.reportType === PressureReportType['SUGGUESTION'])
  1422. return props.selectedItem?.isRecheck || false
  1423. else if (props.selectedItem?.reportType === PressureReportType['MAINQUESTION']) return false
  1424. else
  1425. return (
  1426. props.selectedItem &&
  1427. props.selectedItem.taskStatus === PressureCheckerMyTaskStatus['RECORD_INPUT']
  1428. // && props.selectedItem.reportUrl
  1429. )
  1430. })
  1431. // 判断“提交审核&提交审批”按钮是否显示
  1432. const isCanSubmitToAudit = computed(() => {
  1433. const { selectedItem } = props
  1434. if (selectedItem && selectedItem?.reportType === PressureReportType['SUBCONTRACT']){
  1435. return selectedItem.taskStatus === PressureCheckerMyTaskStatus['CONFIRMED']
  1436. }
  1437. if (props.selectedItem && props.selectedItem?.reportType === PressureReportType['SUGGUESTION'])
  1438. return (
  1439. props.selectedItem?.isApproval &&
  1440. props.selectedItem.taskStatus === PressureCheckerMyTaskStatus['RECORD_INPUT']
  1441. )
  1442. else if (
  1443. props.selectedItem &&
  1444. props.selectedItem?.reportType === PressureReportType['MAINQUESTION']
  1445. )
  1446. return (
  1447. props.selectedItem.taskStatus === PressureCheckerMyTaskStatus['RECORD_INPUT'] &&
  1448. props.selectedItem?.reportUrl
  1449. )
  1450. else
  1451. return (
  1452. props.selectedItem &&
  1453. props.selectedItem.taskStatus === PressureCheckerMyTaskStatus['REPORT_INPUT']
  1454. )
  1455. })
  1456. // 判断“填写记录”按钮是否显示
  1457. const isCanEditTestRecord = computed(
  1458. () =>
  1459. props.selectedItem &&
  1460. (props.selectedItem.taskStatus === PressureCheckerMyTaskStatus['RECORD_INPUT'] ||
  1461. props.selectedItem.taskStatus === PressureCheckerMyTaskStatus['CONFIRMED']) && props.selectedItem.reportType !== PressureReportType['SUBCONTRACT']
  1462. )
  1463. // 判断“编制报告”按钮是否显示
  1464. const isCanEditReport = computed(
  1465. () =>
  1466. props.selectedItem &&
  1467. props.selectedItem.taskStatus === PressureCheckerMyTaskStatus['REPORT_INPUT']
  1468. )
  1469. // 判断"撤销流程"按钮是否显示:报告审核或审批阶段,且有OA流程ID,且当前用户是发起人
  1470. const isCanCancelFlow = computed(() => {
  1471. if (!props.selectedItem) return false
  1472. const isAuditOrApprove = [
  1473. PressureCheckerMyTaskStatus['REPORT_AUDIT'],
  1474. PressureCheckerMyTaskStatus['REPORT_APPROVE']
  1475. ].includes(props.selectedItem.taskStatus)
  1476. if (!isAuditOrApprove) return false
  1477. if (!props.selectedItem.summaryId) return false
  1478. return !checkerIsLoginUser.value
  1479. })
  1480. // 撤销流程
  1481. const handleCancelFlow = async () => {
  1482. if (!props.selectedItem?.summaryId) {
  1483. ElMessage.warning('未找到OA流程ID,无法撤销')
  1484. return
  1485. }
  1486. ElMessageBox.confirm('确定要撤销该流程吗?撤销后报告将回到报告编制阶段,您可以重新发起。', '撤销流程', {
  1487. confirmButtonText: '确定',
  1488. cancelButtonText: '取消',
  1489. type: 'warning'
  1490. }).then(async () => {
  1491. try {
  1492. const result = await BoilerTaskOrderApi.cancelOAFlow(props.selectedItem.summaryId)
  1493. if (result) {
  1494. ElMessage.success('流程已撤销')
  1495. emit('refresh')
  1496. }else {
  1497. ElMessage.error('流程已撤销')
  1498. emit('refresh')
  1499. }
  1500. } catch (error) {
  1501. console.error('撤销流程失败:', error)
  1502. ElMessage.error('撤销流程失败,请稍后重试')
  1503. }
  1504. }).catch(() => {})
  1505. }
  1506. const oaSummaryList = ref([])
  1507. // 判断"OA审核"按钮是否显示:报告审核或审批阶段,且有OA流程ID,且当前用户是对应阶段的审核人/审批人
  1508. const isCanOAAudit = computed(() => {
  1509. if (!props.selectedItem) return false
  1510. // const { taskStatus, summaryId, approvalId, ratifyId } = props.selectedItem
  1511. const {summaryId,taskStatus} = props.selectedItem
  1512. // const currentUserId = userStore?.user?.id
  1513. // 审核阶段:只有审核人才能点击
  1514. // 审批阶段:只有审批人才能点击
  1515. if (taskStatus === PressureCheckerMyTaskStatus['REPORT_AUDIT'] || taskStatus === PressureCheckerMyTaskStatus['REPORT_APPROVE']) {
  1516. // 检查是否在OA待办事项中
  1517. const isOaSummary = oaSummaryList.value.some(item => item == summaryId)
  1518. if (!isOaSummary) return false
  1519. return true
  1520. }
  1521. return false
  1522. })
  1523. // OA审核:弹出独立窗口
  1524. const oaAuditTimer = ref<ReturnType<typeof setInterval> | null>(null)
  1525. onMounted(() => {
  1526. setTimeout(() => {
  1527. if (route.query?.type == 'reportRatify' || route.query?.type == 'reportAudit') {
  1528. if (isCanOAAudit.value) {
  1529. handleOpenOAAudit()
  1530. }
  1531. }
  1532. }, 500)
  1533. })
  1534. const handleOpenOAAudit = async () => {
  1535. // 清除之前的定时器
  1536. if (oaAuditTimer.value) {
  1537. clearInterval(oaAuditTimer.value)
  1538. oaAuditTimer.value = null
  1539. }
  1540. // 先打开空白弹窗
  1541. // 先打开全屏空白弹窗
  1542. const win = window.open('', 'OAAudit', 'width=' + screen.width + ',height=' + screen.height + ',left=0,top=0,menubar=no,toolbar=no,location=no,status=no,scrollbars=yes,resizable=yes')
  1543. try {
  1544. const link = await BoilerTaskOrderApi.getAffairLink(props.selectedItem.summaryId)
  1545. if (link && win) {
  1546. win.location.href = link
  1547. // 轮询监听窗口关闭,关闭后触发报告状态更新
  1548. oaAuditTimer.value = setInterval(() => {
  1549. if (win.closed) {
  1550. clearInterval(oaAuditTimer.value!)
  1551. oaAuditTimer.value = null
  1552. BoilerTaskOrderApi.updateReportBySummaryId(props.selectedItem.summaryId).finally(() => emit('refresh'))
  1553. BoilerTaskOrderApi.getAffairSummaryList().then((res) => {
  1554. oaSummaryList.value = res || []
  1555. })
  1556. }
  1557. }, 500)
  1558. } else if (!win) {
  1559. ElMessage.warning('浏览器拦截了弹窗,请允许弹窗后重试')
  1560. } else {
  1561. win.close()
  1562. ElMessage.warning('未找到对应的OA审批链接')
  1563. }
  1564. } catch (error) {
  1565. console.error('获取OA审批链接失败:', error)
  1566. win?.close()
  1567. ElMessage.error('获取OA审批链接失败,请稍后重试')
  1568. }
  1569. }
  1570. // 判断"填写结果"按钮是否显示
  1571. const isCanEditRecordResult = computed(() => {
  1572. // 非我的任务详情页面,不显示
  1573. if ('BoilerCheckerTaskDetail' !== routeName.value) return false
  1574. if (!props.selectedItem?.isAutoAmount || props.selectedItem?.isAutoAmount === '0') return false
  1575. if (
  1576. (props.selectedItem?.reportType === 100 || props.selectedItem?.reportType === 300) &&
  1577. props.selectedItem.taskStatus !== PressureCheckerMyTaskStatus['REPORT_END']
  1578. ) {
  1579. // 独审报告&主报告--“填写结果”:报告办结之前都可以填写
  1580. return true
  1581. }
  1582. // 查找主报告
  1583. const mainReport = props.reportList?.find((item) => item.reportType === 100) || {
  1584. taskStatus: null
  1585. }
  1586. if (
  1587. props.selectedItem?.reportType !== 100 &&
  1588. props.selectedItem?.reportType !== 300 &&
  1589. mainReport?.taskStatus !== PressureCheckerMyTaskStatus['REPORT_END']
  1590. ) {
  1591. // 非主报告 & 非独审报告 -- “填写结果”:主报告办结之前都可以填写
  1592. return true
  1593. }
  1594. return false
  1595. })
  1596. // 1、重大问题线索告知表 2、作业指导书 3、检验方案 只显示pdf
  1597. const onlyShowPdf = computed(() => {
  1598. const reportTypes = [
  1599. PressureReportType.MAINQUESTION,
  1600. PressureReportType.INSPECTIONPLAN,
  1601. PressureReportType.WORKINSTRUCTION
  1602. ]
  1603. return !props.selectedItem?.reportType
  1604. ? false
  1605. : reportTypes.includes(props.selectedItem?.reportType)
  1606. })
  1607. // 模板参数
  1608. const templateParams = computed(() => {
  1609. if (!props.selectedItem) return {}
  1610. return {
  1611. ...props.selectedItem,
  1612. equipCode: props.taskOrderItem?.equipCode || '',
  1613. equipId: props.taskOrderItem?.id || ''
  1614. }
  1615. })
  1616. // 判断是否为独审报告且不需要审批
  1617. const isApprovalWithoutRatify = computed(() => {
  1618. if (!props.selectedItem) return false
  1619. const reportType = (props.selectedItem as any).reportType
  1620. const isApproval = (props.selectedItem as any).isApproval
  1621. const isRatify = (props.selectedItem as any).isRatify
  1622. return (
  1623. reportType === PressureReportType.SINGLE &&
  1624. isApproval === true &&
  1625. isRatify === false &&
  1626. props.reportId === props.selectedItem.id
  1627. )
  1628. })
  1629. /**** 葡萄城:记录录入的显示和隐藏 ****/
  1630. const showInlineEditCheckRecord = ref(false)
  1631. const templateId = ref('')
  1632. const handleEditSpreadRecord = () => {
  1633. // console.log('templateParams', templateParams.value)
  1634. // DynamicTbInsApi.getOrCreatePreviewData('dc8fbb6078b2a2f0eadc0f6aedbeafa0').then(res => {
  1635. // showInlineEditCheckRecord.value = true
  1636. // templateId.value = res.id
  1637. // })
  1638. editPreview('record')
  1639. showInlineEditCheckRecord.value = true
  1640. }
  1641. const handleTemplateConfirm = (templateUrl: string) => {
  1642. console.log('templateUrl', templateUrl)
  1643. emit('template-confirm', templateUrl)
  1644. }
  1645. const handleRefresh = () => {
  1646. emit('refresh')
  1647. }
  1648. /**** 葡萄城:记录录入的显示和隐藏 end****/
  1649. /**** 葡萄城:检验结果录入的显示和隐藏 ****/
  1650. const showInlineInspectionResultInput = ref(false)
  1651. const inspectionResultTemplateParams = ref({})
  1652. // 检验结果录入模板参数
  1653. const handleInputCheckConclusion = () => {
  1654. showInlineInspectionResultInput.value = true
  1655. if (!props.selectedItem) return {}
  1656. inspectionResultTemplateParams.value = {
  1657. ...props.selectedItem,
  1658. equipCode: props.taskOrderItem?.equipCode || ''
  1659. }
  1660. }
  1661. const handleInspectionResultConfirm = (resultUrl: string) => {
  1662. emit('template-confirm', resultUrl)
  1663. viewMode.value = 'normal'
  1664. }
  1665. const handleReturnToNormalMode = () => {
  1666. viewMode.value = 'normal'
  1667. }
  1668. /**** 葡萄城:检验结果录入的显示和隐藏 end ****/
  1669. /**** 葡萄城:报告编制的显示和隐藏 ****/
  1670. const showInlineReportEdit = ref(false)
  1671. const handleEditRreport = () => {
  1672. if (showReportPdfType.value !== 'report' && showReportPdfType.value !== 'result'){
  1673. ElMessage.warning('请切换报告后再进行报告编制!')
  1674. return
  1675. }
  1676. editPreview('report')
  1677. showInlineReportEdit.value = true
  1678. }
  1679. // 获取当前报告的配置信息
  1680. const currentReportConfig = computed(() => {
  1681. if (
  1682. !props.selectedItem ||
  1683. props.selectedItem.reportType !== PressureReportType.SINGLE ||
  1684. !props.reportList
  1685. ) {
  1686. return null
  1687. }
  1688. // 从reportList中找到当前报告的配置
  1689. const currentReport = props.reportList.find((report) => report.id === templateParams.value?.id)
  1690. return currentReport
  1691. ? {
  1692. isApproval: (currentReport as any).isApproval,
  1693. isRatify: (currentReport as any).isRatify
  1694. }
  1695. : null
  1696. })
  1697. // 选择报告审核人弹窗的显示和隐藏
  1698. const isShowReportAuditDialog = ref(false)
  1699. // 弹窗打开时预加载审核人列表
  1700. watch(isShowReportAuditDialog, (val) => {
  1701. if (val) {
  1702. reportAuditUserLoading.value = true
  1703. getReviewUserList({ pageNo: 1, pageSize: 200, name: '' }).then((res) => {
  1704. reportAuditUserOptions.value = res || []
  1705. }).finally(() => {
  1706. reportAuditUserLoading.value = false
  1707. })
  1708. }
  1709. })
  1710. // 提交报告审核
  1711. const handleSubmitAudit = async () => {
  1712. try {
  1713. await spreadRef.value?.handleSave()
  1714. } catch (error) {
  1715. console.error('保存数据失败:', error)
  1716. ElMessage.error('保存数据失败,请重试')
  1717. return
  1718. }
  1719. // if([PressureReportType.SUGGUESTION, PressureReportType.MAINQUESTION].includes(props?.selectedItem?.reportType) && !props?.selectedItem?.reportUrl) {
  1720. // // 【重大问题线索告知表,检验意见通知书】必须先填写记录,才能提交审核
  1721. // ElMessage.warning('请先“填写记录”!再提交审核!')
  1722. // return
  1723. // }
  1724. if(PressureReportType.SINGLE === props?.selectedItem?.reportType && !props?.selectedItem?.prepareUrl) {
  1725. // 独审报告:用户可以不走报告编制环节直接提交审核,需要调用handleUploadAPIReportPreviewBlob将reportPreview接口的文件流拿来提交一遍
  1726. // return handleUploadAPIReportPreviewBlob(handleSubmitAudit)
  1727. isShowReportAuditDialog.value = true
  1728. return
  1729. }
  1730. // 检查检验意见通知书是否“报告完结”
  1731. const canSubmit = canSubmitMainReportType('该报告存在未办结检验意见通知书,无法提交审核、审批')
  1732. if(!canSubmit) return
  1733. // 检查其他报告状态
  1734. const checkResult = checkOtherReportsFinished()
  1735. if (!checkResult.canSubmit) {
  1736. try {
  1737. await ElMessageBox.alert(
  1738. checkResult.message || '请先办结其他报告再提交主报告审核',
  1739. '无法提交审核'
  1740. )
  1741. } catch (error) {
  1742. // 用户关闭对话框,不需要处理
  1743. }
  1744. return
  1745. }
  1746. // if(PressureReportType.MAIN === props?.selectedItem?.reportType && !props?.selectedItem?.prepareUrl) {
  1747. // // 主报告:用户可以不走报告编制环节直接提交审核,需要调用handleUploadAPIReportPreviewBlob将reportPreview接口的文件流拿来提交一遍
  1748. // return handleUploadAPIReportPreviewBlob(handleSubmitAudit)
  1749. // }
  1750. // 重大问题线索通知 不需要选择审核人
  1751. if ([PressureReportType['MAINQUESTION']].includes(templateParams.value?.reportType)) {
  1752. ElMessageBox.confirm(`确定提交【${templateParams.value?.reportName}】`, '提示', {
  1753. confirmButtonText: '确定',
  1754. cancelButtonText: '取消'
  1755. })
  1756. .then(async () => {
  1757. const submitResult = await BoilerTaskOrderApi.submitOpinionNoticeApproval({
  1758. id: templateParams.value?.id
  1759. })
  1760. if (submitResult) {
  1761. // 这里可以做页面刷新
  1762. emit('template-confirm')
  1763. }
  1764. })
  1765. .catch(() => {
  1766. console.log('用户取消提交审核')
  1767. })
  1768. } else if ([PressureReportType['SUGGUESTION']].includes(templateParams.value?.reportType)) {
  1769. optionList.schemeReviewerList = await BoilerTaskOrderApi.getAuditList({
  1770. roleCode: 'Boiler Director'
  1771. })
  1772. approvalUserVisible.value = true
  1773. } else {
  1774. // 确定审核人:使用当前用户,否则使用默认审核人
  1775. // const approveId = userStore.user.id
  1776. // const approveId = 'b5369aeb73954430eef53a9c8b7586ee'
  1777. //
  1778. // ElMessageBox.confirm('确定提交审核吗?', '提示', {
  1779. // confirmButtonText: '确定',
  1780. // cancelButtonText: '取消',
  1781. // type: 'warning'
  1782. // }).then(async () => {
  1783. // const loadingInstance = ElLoading.service({
  1784. // fullscreen: true,
  1785. // text: '正在提交审核...'
  1786. // })
  1787. // try {
  1788. // const submitResult = await BoilerTaskOrderApi.submitReportAudit({
  1789. // id: templateParams.value?.id,
  1790. // approveId
  1791. // })
  1792. // if (submitResult) {
  1793. // ElMessage.success('提交审核成功!')
  1794. // selectNextItem([props.selectedItem])
  1795. // emit('template-confirm')
  1796. // }
  1797. // } finally {
  1798. // loadingInstance.close()
  1799. // }
  1800. // }).catch(() => {
  1801. // console.log('用户取消提交审核')
  1802. // })
  1803. isShowReportAuditDialog.value = true
  1804. }
  1805. }
  1806. const handleCompletion = async () => {
  1807. await BoilerTaskOrderApi.handleCompletion(props.selectedItem.id)
  1808. handleRefresh()
  1809. ElMessage.success('报告已办结')
  1810. }
  1811. const handleOpenSuggestionAuditDialog = () => {
  1812. isShowSuggestionAuditDialog.value = true
  1813. btnLoading.value = true
  1814. UserApi.getApprovalDetail({})
  1815. .then(async (res) => {
  1816. if (res) {
  1817. const opinionNotificationJsonData = res.opinionNotificationJson ? JSON.parse(res.opinionNotificationJson) : {}
  1818. suggestionFormData.value = {
  1819. ...res,
  1820. ...opinionNotificationJsonData,
  1821. }
  1822. const suggestionFilds = sugguestionApproveConfig.value.map((item) => item.filed)
  1823. const suggestionPromise = suggestionFilds.map((category) =>
  1824. UserGroupApi.getUserGroupUserList({ category: category, status: 0 })
  1825. )
  1826. const suggestionResult = await Promise.all(suggestionPromise)
  1827. sugguestionApproveConfig.value.forEach((item, index) => {
  1828. const auditOptions = suggestionResult[index] || []
  1829. item.auditOptions = auditOptions
  1830. const auditId = suggestionFormData.value[item.auditIdProp]
  1831. const auditUser = auditOptions.find((v) => v.id === auditId) || {}
  1832. suggestionFormData.value[item.auditIdPropUser] = { ...auditUser, nickname: `${auditUser.nickname || ''}${auditUser.employeeNo ? `(${auditUser.employeeNo})` : ''}` }
  1833. })
  1834. }
  1835. }).finally(() => {
  1836. btnLoading.value = false
  1837. })
  1838. }
  1839. const handleSubmitSuggestionAudit = () => {
  1840. suggestionFormRef.value?.validate().then(async (valid) => {
  1841. if (valid) {
  1842. btnLoading.value = true
  1843. const params = {
  1844. id: templateParams.value?.id,
  1845. auditUserIds: [suggestionFormData.value?.sugguestionAuditId],
  1846. approveUserIds: [suggestionFormData.value?.sugguestionApproveId]
  1847. }
  1848. BoilerTaskOrderApi.submitOpinionNoticeApproval(params).then((submitResult) => {
  1849. if (submitResult) {
  1850. emit('template-confirm')
  1851. }
  1852. }).catch(() => {
  1853. btnLoading.value = false
  1854. ElMessage.error('提交失败')
  1855. }).finally(() => {
  1856. btnLoading.value = false
  1857. isShowSuggestionAuditDialog.value = false
  1858. })
  1859. }
  1860. })
  1861. }
  1862. const getUserLists = async (userList, params) => {
  1863. const nickname = params.nickname
  1864. const list = nickname ? userList.filter((item) => item.nickname.includes(nickname)) : userList
  1865. const optList = list.map((item) => ({
  1866. ...item,
  1867. nickname: `${item.nickname}${item.employeeNo ? `(${item.employeeNo})` : ''}`,
  1868. }))
  1869. return Promise.resolve({ list: optList, total: list.length })
  1870. }
  1871. const handleChangeHandler = (val, prop) => {
  1872. const { id } = val
  1873. suggestionFormData.value[prop] = id
  1874. }
  1875. const getRecheckUserList = async (params) => {
  1876. params.orderId = props.taskInfo.id
  1877. params.orderItemId = props.taskOrderItem?.id
  1878. return await BoilerTaskOrderApi.getRecheckUserList(params)
  1879. }
  1880. const getReviewUserList = async (params) => {
  1881. params.reportId = props.selectedItem.id
  1882. return await BoilerTaskOrderApi.getReviewUserList(params)
  1883. }
  1884. // 上传主报告文件流
  1885. const handleUploadAPIReportPreviewBlob = async (submitApprovalFn) => {
  1886. if(!props.selectedItem || props.selectedItem.taskStatus !== PressureTaskOrderTaskStatus.REPORT_INPUT) return
  1887. // 获取文件流
  1888. const blob = await BoilerTaskOrderApi.getReportPreview({
  1889. reportId: props.selectedItem?.id,
  1890. type: 300, // 报告模板
  1891. fileType: 100 // xlsx
  1892. })
  1893. if(blob) {
  1894. // 上传文件流
  1895. const formData = new FormData()
  1896. formData.append('file', blob)
  1897. const response = await uploadFile(formData)
  1898. // 保存报告编制的url
  1899. const saveResult = await BoilerTaskOrderApi.saveReportPrepare({
  1900. id: props.selectedItem.id,
  1901. prepareUrl: response,
  1902. })
  1903. if(saveResult) {
  1904. await emit('update:selected-item', {...props.selectedItem, prepareUrl: response})
  1905. // 提交审核
  1906. await submitApprovalFn()
  1907. }
  1908. }
  1909. }
  1910. const handleChangeEntrustUnit = (unit: Record<string, any>) => {
  1911. form.value.recheckUser = unit
  1912. }
  1913. // 检查其他报告是否已办结
  1914. const checkOtherReportsFinished = (): { canSubmit: boolean; message?: string } => {
  1915. if (!props.selectedItem) return { canSubmit: false }
  1916. // 如果不是主报告,直接允许提交
  1917. if (props.selectedItem.reportType !== PressureReportType.MAIN) {
  1918. return { canSubmit: true }
  1919. }
  1920. // 主报告需要检查其他报告状态
  1921. const unfinishedReports = !props.reportList
  1922. ? []
  1923. : props.reportList.filter((report) => {
  1924. // 排除当前报告
  1925. if (report.id === templateParams.value?.id) {
  1926. return false
  1927. }
  1928. // 检查子报告和独审报告是否已办结
  1929. const isSubReport = report.reportType === PressureReportType.SUB
  1930. const isSingleReport = report.reportType === PressureReportType.SINGLE
  1931. const isSUGGUESTIONReport = report.reportType === PressureReportType.SUGGUESTION
  1932. if (isSubReport || isSingleReport || isSUGGUESTIONReport) {
  1933. return report.taskStatus !== PressureCheckerMyTaskStatus.REPORT_END
  1934. }
  1935. return false
  1936. })
  1937. if (unfinishedReports.length > 0) {
  1938. const unfinishedNames = unfinishedReports.map((report) => report.reportName).join('、')
  1939. return {
  1940. canSubmit: false,
  1941. message: `请先办结其他报告再提交主报告审核。未办结的报告:${unfinishedNames}`
  1942. }
  1943. }
  1944. // 检查是否确认费用
  1945. // const reportsWithoutConfirmFee: any[] = []
  1946. // 检查当前报告中是否存在应确认费用,但未确认的报错
  1947. const reportsWithoutConfirmFee: any[] = !props.reportList
  1948. ? []
  1949. : props.reportList.filter(
  1950. (report: any) => report.isAutoAmount === '1' && report.feeConfirm === false
  1951. )
  1952. // if (currentReport) {
  1953. // reportsWithoutConfirmFee.push(currentReport)
  1954. // }
  1955. // 检查子报告是否都已录入结果
  1956. // const subReports = !props.reportList ? [] : props.reportList.filter(report => {
  1957. // return report.id !== templateParams.value?.id &&
  1958. // report.reportType === PressureReportType.SUB
  1959. // })
  1960. // subReports.forEach(report => {
  1961. // if (!report.formulaJson) {
  1962. // reportsWithoutConfirmFee.push(report)
  1963. // }
  1964. // })
  1965. if (reportsWithoutConfirmFee.length > 0) {
  1966. const reportNames = reportsWithoutConfirmFee.map((report) => report.reportName).join('、')
  1967. return {
  1968. canSubmit: false,
  1969. message: `请先确认费用再提交主报告审核。未确认费用的报告:${reportNames}`
  1970. }
  1971. }
  1972. return { canSubmit: true }
  1973. }
  1974. // 报告审核人选择对话框 - 远程搜索选项
  1975. const reportAuditUserCode = ref<string>('')
  1976. const reportAuditUserOptions = ref<Recordable[]>([])
  1977. const reportAuditUserLoading = ref(false)
  1978. const reportAuditSubmitting = ref(false)
  1979. // 报告审核人选择确认
  1980. const handleReportAuditSelectConfirm = async () => {
  1981. if (!reportAuditUserCode.value) {
  1982. ElMessage.warning('请选择审核人')
  1983. return
  1984. }
  1985. reportAuditSubmitting.value = true
  1986. try {
  1987. const submitResult = await BoilerTaskOrderApi.submitReportAudit({
  1988. id: templateParams.value?.id,
  1989. auditor: reportAuditUserCode.value
  1990. })
  1991. if (submitResult) {
  1992. emit('template-confirm')
  1993. }
  1994. isShowReportAuditDialog.value = false
  1995. } finally {
  1996. reportAuditSubmitting.value = false
  1997. }
  1998. }
  1999. /**** 葡萄城:报告编制的显示和隐藏 end ****/
  2000. const handleModifyChecker = () => {
  2001. if (props.selectedItem) {
  2002. emit('modify-checker', props.selectedItem)
  2003. }
  2004. }
  2005. // 审批人选择确认
  2006. const handleApprovalSelectConfirm = async (res: any) => {
  2007. if (!props.selectedItem || !res || res.length === 0) {
  2008. ElMessage.warning('请选择审批人')
  2009. return
  2010. }
  2011. const ratifyId = res[0] // 获取选中的审批人ID
  2012. try {
  2013. await BoilerTaskOrderApi.submitReportApprove({
  2014. id: props.selectedItem.id,
  2015. ratifyId: ratifyId
  2016. })
  2017. ElMessage.success('提交审批成功')
  2018. isShowApprovalDialog.value = false
  2019. emit('submit-approval')
  2020. } catch (error: any) {
  2021. console.error('提交审批失败:', error)
  2022. ElMessage.error('提交审批失败,请稍后重试')
  2023. isShowApprovalDialog.value = false
  2024. }
  2025. }
  2026. // 加载流转记录列表
  2027. const recordList = ref<any[]>([])
  2028. const recordListLoading = ref(false)
  2029. const loadRecordList = async () => {
  2030. if (!props.selectedItem) return
  2031. try {
  2032. let response = null
  2033. // 检验意见通知书&重大问题线索
  2034. switch (props.selectedItem.reportType) {
  2035. case PressureReportType['SUGGUESTION']:
  2036. case PressureReportType['MAINQUESTION']:
  2037. response = await BoilerTaskOrderApi.getOpinionNoticeApprovalRecordList({
  2038. id: props.selectedItem.id
  2039. })
  2040. break
  2041. default:
  2042. // 其他
  2043. response = await BoilerTaskOrderApi.getTaskOrderItemReportRecordPage({
  2044. pageSize: 10,
  2045. pageNo: 1,
  2046. reportId: props.selectedItem.id
  2047. })
  2048. }
  2049. // 根据实际接口返回的数据结构获取数据
  2050. recordList.value = response?.data?.list || response?.list || response || []
  2051. } catch (error: any) {
  2052. console.error('获取流转记录失败:', error)
  2053. ElMessage.error('获取流转记录失败,请稍后重试')
  2054. recordList.value = []
  2055. } finally {
  2056. recordListLoading.value = false
  2057. }
  2058. }
  2059. // 获取报告字段纠错列表
  2060. const checkInputList = ref<any[]>([])
  2061. const handleGetCheckKeyInputs = async () => {
  2062. checkInputList.value = []
  2063. //if (!dataJson) return
  2064. if (!props.selectedItem?.id) return ElMessage.warning('字段纠错传参有误')
  2065. let reportId = props.selectedItem?.id
  2066. // const templateId = isShowReportPdf.value? props.selectedItem?.reportTemplateId : props.selectedItem?.templateId;
  2067. // if (isShowReportPdf.value){
  2068. // reportId = "report_" + reportId;
  2069. // }
  2070. let templateId = showReportPdfType.value === 'report' ? props.selectedItem?.reportTemplateId :
  2071. showReportPdfType.value === 'result' ? props.selectedItem?.resultTemplateId : props.selectedItem?.templateId;
  2072. if (showReportPdfType.value === 'report'){
  2073. reportId = "report_" + reportId;
  2074. }else if (showReportPdfType.value === 'result'){
  2075. reportId = "result_" + reportId;
  2076. }
  2077. const response = await BoilerTaskOrderApi.getCheckKeyIsInput(
  2078. { id: templateId,reportId: reportId },
  2079. JSON.parse("{}")
  2080. )
  2081. //console.log(JSON.parse(dataJson))
  2082. if (response) checkInputList.value = formatCheckInputList(response)
  2083. }
  2084. function formatCheckInputList(response, checkInputList = [] as any[]) {
  2085. if (!response) return []
  2086. for (let checkItem of response) {
  2087. if (!checkItem.child) {
  2088. checkInputList.push({ code: checkItem.code, name: checkItem.name })
  2089. }
  2090. if (is(checkItem.child, 'Array') && checkItem.type === 'object') {
  2091. formatCheckInputList(checkItem.child, checkInputList)
  2092. }
  2093. if (is(checkItem.child, 'Array') && checkItem.type === 'array') {
  2094. const childMapper = checkItem.child.map((item, i) => {
  2095. return {
  2096. code: `${checkItem.code}: ${item.code}`,
  2097. name: item.name
  2098. }
  2099. })
  2100. formatCheckInputList(childMapper, checkInputList)
  2101. }
  2102. }
  2103. return checkInputList
  2104. }
  2105. // 获取版本记录列表
  2106. const showVersionDetail = ref(false)
  2107. const historyList = ref<any[]>([])
  2108. async function getOrderHistoryVersion(id: string) {
  2109. try {
  2110. const response = await BoilerTaskOrderApi.getSafetyCheckRecordVersionPage({
  2111. pageNo: 1,
  2112. pageSize: 100,
  2113. businessType: 0,
  2114. orderItemReportId: id
  2115. })
  2116. historyList.value = response.list || []
  2117. } catch (error) {
  2118. console.error('获取历史版本失败:', error)
  2119. ElMessage.error('获取历史版本失败')
  2120. }
  2121. }
  2122. const versionDataCompareList = ref([])
  2123. const versionColumns = ref([
  2124. {
  2125. label: '字段名',
  2126. prop: 'displayName'
  2127. },
  2128. {
  2129. label: '修改前',
  2130. prop: 'oldValue',
  2131. render: (row, value) => (
  2132. <el-text link type="default">
  2133. {value || '-'}
  2134. </el-text>
  2135. )
  2136. },
  2137. {
  2138. label: '修改后',
  2139. prop: 'newValue',
  2140. render: (row, value) => <el-text type="primary">{value || '-'}</el-text>
  2141. }
  2142. ])
  2143. const versionDetailButtons = ref([
  2144. {
  2145. render: () => (
  2146. <el-button type="success" onClick={() => handleShowReportPdf()}>
  2147. 查看原文件PDF
  2148. </el-button>
  2149. )
  2150. }
  2151. ])
  2152. const showReportDialog = ref(false)
  2153. const currentUrl = ref('')
  2154. const reportSource = ref<any | null>(null)
  2155. const pdfLoading = ref(false)
  2156. // 恢复版本
  2157. const restoreVersion = (item: any) => {
  2158. ElMessageBox.confirm('是否确定恢复到该版本?', '提示', {
  2159. confirmButtonText: '确定',
  2160. cancelButtonText: '取消',
  2161. type: 'info',
  2162. center: true
  2163. })
  2164. .then(async () => {
  2165. const result = await BoilerTaskOrderApi.saveTaskReportTemplate({
  2166. id: props.selectedItem?.id,
  2167. reportUrl: item.oldReportUrl,
  2168. prepareJson: item.oldPrepareJson,
  2169. reportType: showReportPdfType.value,
  2170. modifiedReason: '恢复版本'
  2171. })
  2172. console.log('restoreVersion', result)
  2173. if (result) {
  2174. ElMessage({
  2175. type: 'success',
  2176. message: '操作成功'
  2177. })
  2178. handleRefresh()
  2179. } else {
  2180. ElMessage.warning('操作失败,请联系管理员!')
  2181. }
  2182. })
  2183. .catch(() => {})
  2184. }
  2185. // 查看版本详情的pdf
  2186. const handleShowReportPdf = async () => {
  2187. if (currentUrl.value) {
  2188. const reportId = props.selectedItem?.id;
  2189. reportInitData.value.refId = showReportPdfType.value === 'report' ? "report_" + reportId :
  2190. showReportPdfType.value === 'result' ? "result_" + reportId : reportId
  2191. reportInitData.value.templateId = showReportPdfType.value === 'report' ? templateParams.value.reportTemplateId :
  2192. showReportPdfType.value === 'result' ? templateParams.value.resultTemplateId : templateParams.value.templateId
  2193. reportInitData.value.dataSource = currentUrl.value.oldPrepareJson ? JSON.parse(currentUrl.value.oldPrepareJson) : {}
  2194. console.log(currentUrl)
  2195. showReportDialog.value = true
  2196. setTimeout(()=> {
  2197. reportSpreadRef.value?.reloadView();
  2198. })
  2199. pdfLoading.value = false
  2200. } else {
  2201. ElMessage.warning('报告缺失,请联系管理员!')
  2202. }
  2203. }
  2204. const handlePdfRendered = () => {
  2205. pdfLoading.value = false
  2206. currentUrl.value = ''
  2207. }
  2208. // 查看详情
  2209. const handleShowVersionInfo = (item) => {
  2210. showVersionDetail.value = true
  2211. currentUrl.value = item
  2212. versionDataCompareList.value = JSON.parse(item.modifiedObject)
  2213. }
  2214. // 检验项目添加上传附件功能
  2215. const reportListUploadModalRef = ref<InstanceType<typeof ReportListUploadModal>>()
  2216. const handleUploadItem = () => {
  2217. reportListUploadModalRef.value?.openModal(route.query.type === 'BoilerMyTask')
  2218. }
  2219. //切换记录和报告的显示
  2220. const handleChangeShowReportPdf = (showType: string) => {
  2221. if (showType === 'report' || showType === 'result'){
  2222. if (![PressureCheckerMyTaskStatus.REPORT_INPUT,PressureCheckerMyTaskStatus.REPORT_AUDIT,PressureCheckerMyTaskStatus.REPORT_APPROVE,PressureCheckerMyTaskStatus.REPORT_END].includes(props.selectedItem.taskStatus)){
  2223. ElMessage.error('该检验项目暂未生成报告!')
  2224. showReportPdfType.value = 'record';
  2225. return;
  2226. }
  2227. }
  2228. if (showType === 'result'){
  2229. if (!props.selectedItem?.resultTemplateId){
  2230. ElMessage.error('该检验项目没有结论报告!')
  2231. showReportPdfType.value = 'record';
  2232. return;
  2233. }
  2234. }
  2235. refreshPdfFlag.value = false;
  2236. showReportPdfType.value = showType;
  2237. handleRefreshData()
  2238. }
  2239. const isCanSyncReportData = computed(() => {
  2240. return true
  2241. //return [PressureCheckerMyTaskStatus.CONFIRMED,PressureCheckerMyTaskStatus.RECORD_INPUT].includes(props.selectedItem.taskStatus)
  2242. })
  2243. const handleSyncReportData = async () => {
  2244. try {
  2245. const response = await BoilerTaskOrderApi.syncReportData({
  2246. refId: props.selectedItem.id,
  2247. reportType : showReportPdfType.value
  2248. })
  2249. if (response){
  2250. ElMessage.success('同步数据成功')
  2251. handleRefresh()
  2252. }else{
  2253. ElMessage.error('同步数据失败,请稍后重试')
  2254. }
  2255. } catch (error: any) {
  2256. console.error('同步数据失败:', error)
  2257. ElMessage.error('同步数据失败,请稍后重试')
  2258. }
  2259. }
  2260. const showAssociationOperationManual = ref(false)
  2261. const handleShowAssociationOperationManual = () => {
  2262. showAssociationOperationManual.value = true
  2263. }
  2264. onMounted(() => {
  2265. console.log(props)
  2266. //initPreview()
  2267. BoilerTaskOrderApi.getAffairSummaryList().then((res) => {
  2268. oaSummaryList.value = res || []
  2269. })
  2270. })
  2271. const initPreview=()=>{
  2272. const reportId = props.selectedItem?.id;
  2273. const refId = showReportPdfType.value === 'report' ? "report_" + reportId :
  2274. showReportPdfType.value === 'result' ? "result_" + reportId : reportId
  2275. const templateId = showReportPdfType.value === 'report' ? templateParams.value.reportTemplateId :
  2276. showReportPdfType.value === 'result' ? templateParams.value.resultTemplateId : templateParams.value.templateId
  2277. initData.value.templateId = templateId;
  2278. initData.value.refId = refId;
  2279. initData.value.refName = props.selectedItem?.reportName;
  2280. initData.value.opType = (props.selectedItem.taskStatus == 510 || props.selectedItem.taskStatus >= 600 || (showReportPdfType.value === 'record' && props.selectedItem.taskStatus > 500) || props?.selectedItem?.checkUsers?.[0]?.id != userStore?.user?.id) ? 1 : 0;
  2281. if (props.selectedItem.reportType === PressureReportType['INSPECTIONPLAN'] && props.selectedItem.manualUrl){
  2282. initData.value.manualUrl = props.selectedItem.manualUrl;
  2283. }else{
  2284. initData.value.manualUrl = '';
  2285. }
  2286. setTimeout(()=>{
  2287. spreadRef.value?.reloadView();
  2288. },50)
  2289. console.log('initPreview', initData.value)
  2290. }
  2291. const editPreview=(reportType: string)=>{
  2292. const reportId = props.selectedItem?.id;
  2293. const refId = showReportPdfType.value === 'report' ? "report_" + reportId :
  2294. showReportPdfType.value === 'result' ? "result_" + reportId : reportId
  2295. const templateId = showReportPdfType.value === 'report' ? templateParams.value.reportTemplateId :
  2296. showReportPdfType.value === 'result' ? templateParams.value.resultTemplateId : templateParams.value.templateId
  2297. editData.value.templateId = templateId;
  2298. editData.value.refId = refId;
  2299. editData.value.opType = 0;
  2300. setTimeout(()=>{
  2301. if (reportType === 'record'){
  2302. editSpreadRecordRef.value?.reloadView();
  2303. }else if(reportType === 'report'){
  2304. editSpreadReportRef.value?.reloadView();
  2305. }
  2306. },50)
  2307. console.log('editDataPreview', editData.value)
  2308. }
  2309. const saveSuccess = async (data)=>{
  2310. if (props.selectedItem.taskStatus >= PressureCheckerMyTaskStatus.REPORT_INPUT){
  2311. saveSuccessReport(data)
  2312. }else {
  2313. saveSuccessRecord(data)
  2314. }
  2315. }
  2316. const saveSuccessRecord = async (data)=>{
  2317. const dataJson = !data.dataSource ? '' : JSON.stringify(data.dataSource)
  2318. const params = {
  2319. id: props.selectedItem?.id,
  2320. reportUrl: props.selectedItem?.id,
  2321. prepareJson: dataJson
  2322. }
  2323. const saveResult = await BoilerTaskOrderApi.saveTaskReportTemplate(params)
  2324. if (saveResult) {
  2325. showInlineEditCheckRecord.value = false
  2326. handleRefresh()
  2327. }
  2328. }
  2329. const handleClose = () => {
  2330. ElMessageBox.confirm('是否关闭?', {
  2331. confirmButtonText: '确认',
  2332. cancelButtonText: '取消',
  2333. type: 'warning',
  2334. }).then(() => {
  2335. showInlineEditCheckRecord.value = false
  2336. showInlineReportEdit.value = false
  2337. handleRefresh()
  2338. })
  2339. }
  2340. const saveSuccessReport = async (data)=>{
  2341. const dataJson = !data.dataSource ? '' : JSON.stringify(data.dataSource)
  2342. const saveResult = BoilerTaskOrderApi.saveReportPrepare({
  2343. id: props.selectedItem?.id,
  2344. prepareJson: dataJson,
  2345. })
  2346. if(saveResult) {
  2347. showInlineReportEdit.value = false
  2348. handleRefresh()
  2349. }
  2350. }
  2351. const handleRefreshData = () => {
  2352. loadRecordList()
  2353. initPreview()
  2354. handleGetCheckKeyInputs()
  2355. getOrderHistoryVersion(props.selectedItem?.id)
  2356. }
  2357. // 应用项目 - 使用模板数据填充当前记录
  2358. const handleApplyTemplate = (defaultJson: Recordable) => {
  2359. if (!isCanEditTestRecord.value) {
  2360. return ElMessage.warning('非可编辑状态,不能应用模板')
  2361. }
  2362. // 将模板数据保存到 initData 的 dataSource 中,供 SpreadViewer 使用
  2363. initData.value = {
  2364. ...initData.value,
  2365. dataSource: defaultJson
  2366. }
  2367. setTimeout(()=>{
  2368. spreadRef.value?.reloadView();
  2369. },50)
  2370. console.log(initData);
  2371. }
  2372. // 更新模板 - 打开记录编辑器供用户更新模板
  2373. const handleUpdateTemplateUrl = (item: ReportItemVO) => {
  2374. handleEditSpreadRecord()
  2375. }
  2376. const getData = () =>{
  2377. return spreadRef.value?.getData()
  2378. }
  2379. defineExpose({
  2380. handleShowAssociationOperationManual,
  2381. handleApplyTemplate,
  2382. handleUpdateTemplateUrl,
  2383. getData
  2384. })
  2385. //新加
  2386. // 添加控制展开/收缩状态的变量
  2387. const isExpanded = ref(false);
  2388. // 添加切换面板展开/收缩的方法
  2389. const togglePanel = () => {
  2390. isExpanded.value = !isExpanded.value;
  2391. setTimeout((() => {
  2392. nextTick(handleWindowResize)
  2393. }), 300);
  2394. };
  2395. // 获取PDF宽度
  2396. const pdfContentWidth = ref<number>(1100)
  2397. const pdfPanelRef = ref<HTMLDivElement>()
  2398. const handleWindowResize = debounce(() => {
  2399. if(!pdfPanelRef.value) return
  2400. const width = pdfPanelRef.value?.clientWidth - 20
  2401. pdfContentWidth.value = props.isFullscreen ? width : (width > 1030 ? 1030 : width)
  2402. //重新加载适应宽度
  2403. if (initData.value.opType == 1){
  2404. spreadRef.value?.reloadView();
  2405. }else {
  2406. spreadRef.value?.handleSave();
  2407. }
  2408. }, 100)
  2409. onMounted(() => {
  2410. handleWindowResize()
  2411. window.addEventListener('resize', handleWindowResize)
  2412. })
  2413. onUnmounted(() => {
  2414. window.removeEventListener('resize', handleWindowResize)
  2415. })
  2416. // 全屏切换时重新计算PDF区域宽度
  2417. watch(() => props.isFullscreen, () => {
  2418. nextTick(() => {
  2419. handleWindowResize()
  2420. })
  2421. })
  2422. </script>
  2423. <style lang="scss" scoped>
  2424. .capsule-tabs {
  2425. display: inline-flex;
  2426. align-items: center;
  2427. gap: 2px;
  2428. padding: 2px;
  2429. border-radius: 20px;
  2430. margin-right: 12px;
  2431. flex-shrink: 0;
  2432. .tab-item {
  2433. display: inline-flex;
  2434. align-items: center;
  2435. justify-content: center;
  2436. padding: 4px 12px;
  2437. font-size: 12px;
  2438. background: white;
  2439. border-radius: 16px;
  2440. cursor: pointer;
  2441. transition: all 0.3s ease;
  2442. color: #606266;
  2443. font-weight: 500;
  2444. border: 1px solid transparent;
  2445. user-select: none;
  2446. white-space: nowrap;
  2447. min-width: 50px;
  2448. span {
  2449. font-size: 12px !important;
  2450. }
  2451. &:hover {
  2452. color: #409eff;
  2453. border-color: #409eff;
  2454. }
  2455. &.active {
  2456. background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%);
  2457. color: white;
  2458. border-color: #409eff;
  2459. box-shadow: 0 2px 8px rgba(64, 158, 255, 0.3);
  2460. }
  2461. }
  2462. }
  2463. .container-panel {
  2464. height: 100%;
  2465. border: 1px solid var(--el-border-color);
  2466. .info-panel {
  2467. display: flex;
  2468. height: 32px;
  2469. padding: 0 8px 0 22px;
  2470. font-size: 14px;
  2471. color: #41475c;
  2472. background-color: #effaff;
  2473. justify-content: space-between;
  2474. align-items: center;
  2475. }
  2476. .status-operation-panel {
  2477. height: calc(100% - 32px);
  2478. .single-item-panel {
  2479. display: flex;
  2480. align-items: stretch;
  2481. justify-content: space-between;
  2482. width: 100%;
  2483. height: 100%;
  2484. }
  2485. .pdf-panel {
  2486. height: 100%;
  2487. padding: 10px;
  2488. flex: 1;
  2489. overflow: hidden;
  2490. }
  2491. .right-panel-container {
  2492. display: flex;
  2493. position: relative;
  2494. // 收缩展开按钮样式
  2495. .toggle-btn {
  2496. position: absolute;
  2497. left: -30px;
  2498. top: 50%;
  2499. transform: translateY(-50%);
  2500. width: 30px;
  2501. height: 60px;
  2502. background-color: #fff;
  2503. border: 1px solid var(--el-border-color);
  2504. border-radius: 10px 0 0 10px;
  2505. display: flex;
  2506. align-items: center;
  2507. justify-content: center;
  2508. cursor: pointer;
  2509. z-index: 100;
  2510. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  2511. transition: all 0.3s ease;
  2512. &:hover {
  2513. background-color: #f5f5f5;
  2514. }
  2515. &.collapsed {
  2516. left: -18px;
  2517. // border-radius: 0 10px 10px 0;
  2518. }
  2519. }
  2520. }
  2521. .operation-panel {
  2522. flex-basis: 300px;
  2523. padding: 10px 10px 10px 0;
  2524. box-sizing: border-box;
  2525. transition: all 0.3s ease;
  2526. // 展开状态样式
  2527. &.expanded {
  2528. width: 300px;
  2529. }
  2530. // 收缩状态样式
  2531. &.collapsed {
  2532. width: 0;
  2533. padding: 0;
  2534. border: none;
  2535. .default-toolbar,
  2536. .operation-inner {
  2537. display: none;
  2538. }
  2539. }
  2540. .operation-btns {
  2541. display: flex;
  2542. flex-wrap: wrap;
  2543. gap: 12px;
  2544. .el-button {
  2545. margin-bottom: 10px;
  2546. margin-left: 0;
  2547. }
  2548. }
  2549. }
  2550. .operation-inner {
  2551. height: 100%;
  2552. padding: 7px 10px 20px;
  2553. background-color: #fff;
  2554. border: 1px solid var(--el-border-color);
  2555. box-sizing: border-box;
  2556. }
  2557. .custom-inner{
  2558. display: flex;
  2559. flex-direction: column;
  2560. overflow: hidden;
  2561. height: 100%;
  2562. padding: 7px 10px 20px;
  2563. background-color: #fff;
  2564. border: 1px solid var(--el-border-color);
  2565. box-sizing: border-box;
  2566. .operation-item1 {
  2567. position: relative;
  2568. margin-bottom: 10px;
  2569. overflow-y: auto;
  2570. .item-header {
  2571. position: sticky;
  2572. top: 0;
  2573. left: 0;
  2574. z-index: 1000;
  2575. width: 100%;
  2576. height: 28px;
  2577. padding-left: 20px;
  2578. font-size: 16px;
  2579. line-height: 28px;
  2580. color: #fff;
  2581. background: #fff url('@/assets/imgs/pressure/my-task-detail-operation-bg.png') no-repeat
  2582. left top;
  2583. background-size: 100% 28px;
  2584. }
  2585. .item-content1 {
  2586. display: flex;
  2587. margin-top: 10px;
  2588. &-item {
  2589. font-size: 14px;
  2590. }
  2591. &-img {
  2592. flex: 1;
  2593. display: grid;
  2594. grid-template-columns: repeat(3, 1fr);
  2595. grid-gap: 10px;
  2596. .item-content1-img-item{
  2597. width: 100px;
  2598. height: 100px;
  2599. position: relative;
  2600. border-radius: 8px;
  2601. overflow: hidden;
  2602. cursor: pointer;
  2603. .video-play-overlay {
  2604. position: absolute;
  2605. top: 0;
  2606. left: 0;
  2607. width: 100%;
  2608. height: 100%;
  2609. border-radius: 8px;
  2610. transition: all 0.3s ease;
  2611. display: flex;
  2612. justify-content: center;
  2613. align-items: center;
  2614. background-color: rgba(0, 0, 0, 0.2);
  2615. color: #ffffffcc;
  2616. pointer-events: none;
  2617. &:hover {
  2618. background-color: rgba(0, 0, 0, 0.45);
  2619. color: #ffffff;
  2620. }
  2621. }
  2622. }
  2623. .img-item {
  2624. width: 100px;
  2625. height: 100px;
  2626. }
  2627. }
  2628. }
  2629. .record-item {
  2630. margin-bottom: 10px;
  2631. background-color: #f5f5fa;
  2632. border-radius: 2px;
  2633. }
  2634. .item-content {
  2635. padding: 10px 0;
  2636. font-size: 14px;
  2637. .el-empty {
  2638. width: 100%;
  2639. height: 150px;
  2640. padding: 0;
  2641. box-sizing: border-box;
  2642. }
  2643. &-item {
  2644. height: 32px;
  2645. display: flex;
  2646. align-items: center;
  2647. gap: 8px;
  2648. }
  2649. }
  2650. .record-item-title {
  2651. height: 31px;
  2652. padding: 0 13px;
  2653. line-height: 31px;
  2654. background: linear-gradient(270deg, rgb(128 176 251 / 20%) 0%, rgb(73 120 246 / 20%) 100%);
  2655. border-radius: 2px;
  2656. }
  2657. .record-item-inner {
  2658. padding: 11px 13px 8px;
  2659. .content {
  2660. display: flex;
  2661. align-items: center;
  2662. .el-button {
  2663. margin-left: 8px;
  2664. }
  2665. :deep(.el-button--small.is-round) {
  2666. height: auto;
  2667. padding: 3px 11px;
  2668. }
  2669. .time {
  2670. color: rgb(31 31 31 / 88%);
  2671. text-align: right;
  2672. opacity: 0.5;
  2673. flex: 1;
  2674. }
  2675. }
  2676. .desc {
  2677. margin-top: 7px;
  2678. color: #41475c;
  2679. opacity: 0.8;
  2680. span {
  2681. opacity: 0.5;
  2682. }
  2683. }
  2684. }
  2685. .error-item {
  2686. display: flex;
  2687. padding: 7px 10px;
  2688. color: #41475c;
  2689. background-color: #f5f5fa;
  2690. border: 1px solid var(--el-border-color);
  2691. border-width: 1px 0;
  2692. align-items: center;
  2693. .el-icon {
  2694. margin-right: 6px;
  2695. }
  2696. &:nth-child(n + 1) {
  2697. margin-top: -1px;
  2698. }
  2699. &:nth-child(even) {
  2700. background-color: #fff;
  2701. }
  2702. }
  2703. .history-item {
  2704. padding: 7px 10px 10px;
  2705. margin-bottom: 13px;
  2706. color: #41475c;
  2707. background-color: #f5f5fa;
  2708. border-radius: 2px;
  2709. .history-title {
  2710. display: flex;
  2711. align-items: center;
  2712. margin-left: -10px;
  2713. font-weight: 600;
  2714. line-height: 20px;
  2715. &::before {
  2716. display: block;
  2717. width: 4px;
  2718. height: 15px;
  2719. margin-right: 7px;
  2720. background: #4978f6;
  2721. content: '';
  2722. }
  2723. }
  2724. p {
  2725. margin: 10px 0;
  2726. line-height: 20px;
  2727. }
  2728. .history-footer {
  2729. display: flex;
  2730. // align-items: center;
  2731. font-size: 12px;
  2732. .name {
  2733. color: #4978f6;
  2734. }
  2735. .time {
  2736. margin-left: 2px;
  2737. color: rgb(65 71 92 / 50%);
  2738. }
  2739. div {
  2740. flex: 1;
  2741. display: flex;
  2742. justify-content: flex-end;
  2743. .el-button span {
  2744. font-size: 12px;
  2745. }
  2746. }
  2747. }
  2748. }
  2749. }
  2750. .videoAndImg {
  2751. flex: 1;
  2752. width: 100%;
  2753. overflow-y: auto;
  2754. }
  2755. }
  2756. .operation-item {
  2757. position: relative;
  2758. max-height: calc(100% / 3 - 10px);
  2759. margin-bottom: 10px;
  2760. overflow-y: auto;
  2761. .item-header {
  2762. position: sticky;
  2763. top: 0;
  2764. left: 0;
  2765. z-index: 1000;
  2766. width: 100%;
  2767. height: 28px;
  2768. padding-left: 20px;
  2769. font-size: 16px;
  2770. line-height: 28px;
  2771. color: #fff;
  2772. background: #fff url('@/assets/imgs/pressure/my-task-detail-operation-bg.png') no-repeat
  2773. left top;
  2774. background-size: 100% 28px;
  2775. }
  2776. .item-content1 {
  2777. display: flex;
  2778. margin-top: 10px;
  2779. &-item {
  2780. font-size: 14px;
  2781. }
  2782. &-img {
  2783. flex: 1;
  2784. display: grid;
  2785. grid-template-columns: repeat(3, 1fr);
  2786. grid-gap: 10px;
  2787. .item-content1-img-item{
  2788. width: 100px;
  2789. height: 100px;
  2790. position: relative;
  2791. border-radius: 8px;
  2792. overflow: hidden;
  2793. cursor: pointer;
  2794. .video-play-overlay {
  2795. position: absolute;
  2796. top: 0;
  2797. left: 0;
  2798. width: 100%;
  2799. height: 100%;
  2800. border-radius: 8px;
  2801. transition: all 0.3s ease;
  2802. display: flex;
  2803. justify-content: center;
  2804. align-items: center;
  2805. background-color: rgba(0, 0, 0, 0.2);
  2806. color: #ffffffcc;
  2807. pointer-events: none;
  2808. &:hover {
  2809. background-color: rgba(0, 0, 0, 0.45);
  2810. color: #ffffff;
  2811. }
  2812. }
  2813. }
  2814. .img-item {
  2815. width: 100px;
  2816. height: 100px;
  2817. }
  2818. }
  2819. }
  2820. .record-item {
  2821. margin-bottom: 10px;
  2822. background-color: #f5f5fa;
  2823. border-radius: 2px;
  2824. }
  2825. .item-content {
  2826. padding: 10px 0;
  2827. font-size: 14px;
  2828. .el-empty {
  2829. width: 100%;
  2830. height: 150px;
  2831. padding: 0;
  2832. box-sizing: border-box;
  2833. }
  2834. &-item {
  2835. height: 32px;
  2836. display: flex;
  2837. align-items: center;
  2838. gap: 8px;
  2839. }
  2840. }
  2841. .record-item-title {
  2842. height: 31px;
  2843. padding: 0 13px;
  2844. line-height: 31px;
  2845. background: linear-gradient(270deg, rgb(128 176 251 / 20%) 0%, rgb(73 120 246 / 20%) 100%);
  2846. border-radius: 2px;
  2847. }
  2848. .record-item-inner {
  2849. padding: 11px 13px 8px;
  2850. .content {
  2851. display: flex;
  2852. align-items: center;
  2853. .el-button {
  2854. margin-left: 8px;
  2855. }
  2856. :deep(.el-button--small.is-round) {
  2857. height: auto;
  2858. padding: 3px 11px;
  2859. }
  2860. .time {
  2861. color: rgb(31 31 31 / 88%);
  2862. text-align: right;
  2863. opacity: 0.5;
  2864. flex: 1;
  2865. }
  2866. }
  2867. .desc {
  2868. margin-top: 7px;
  2869. color: #41475c;
  2870. opacity: 0.8;
  2871. span {
  2872. opacity: 0.5;
  2873. }
  2874. }
  2875. }
  2876. .error-item {
  2877. display: flex;
  2878. padding: 7px 10px;
  2879. color: #41475c;
  2880. background-color: #f5f5fa;
  2881. border: 1px solid var(--el-border-color);
  2882. border-width: 1px 0;
  2883. align-items: center;
  2884. .el-icon {
  2885. margin-right: 6px;
  2886. }
  2887. &:nth-child(n + 1) {
  2888. margin-top: -1px;
  2889. }
  2890. &:nth-child(even) {
  2891. background-color: #fff;
  2892. }
  2893. }
  2894. .history-item {
  2895. padding: 7px 10px 10px;
  2896. margin-bottom: 13px;
  2897. color: #41475c;
  2898. background-color: #f5f5fa;
  2899. border-radius: 2px;
  2900. .history-title {
  2901. display: flex;
  2902. align-items: center;
  2903. margin-left: -10px;
  2904. font-weight: 600;
  2905. line-height: 20px;
  2906. &::before {
  2907. display: block;
  2908. width: 4px;
  2909. height: 15px;
  2910. margin-right: 7px;
  2911. background: #4978f6;
  2912. content: '';
  2913. }
  2914. }
  2915. p {
  2916. margin: 10px 0;
  2917. line-height: 20px;
  2918. }
  2919. .history-footer {
  2920. display: flex;
  2921. // align-items: center;
  2922. font-size: 12px;
  2923. .name {
  2924. color: #4978f6;
  2925. }
  2926. .time {
  2927. margin-left: 2px;
  2928. color: rgb(65 71 92 / 50%);
  2929. }
  2930. div {
  2931. flex: 1;
  2932. display: flex;
  2933. justify-content: flex-end;
  2934. .el-button span {
  2935. font-size: 12px;
  2936. }
  2937. }
  2938. }
  2939. }
  2940. }
  2941. }
  2942. }
  2943. .default-toolbar {
  2944. display: flex;
  2945. justify-content: flex-end;
  2946. gap: 16px;
  2947. white-space: nowrap;
  2948. ::v-deep .el-button {
  2949. margin-left: 0;
  2950. }
  2951. }
  2952. // 退回对话框样式
  2953. .reject-dialog-content {
  2954. .form-item {
  2955. margin-bottom: 20px;
  2956. &:last-child {
  2957. margin-bottom: 0;
  2958. }
  2959. .form-label {
  2960. display: block;
  2961. margin-bottom: 8px;
  2962. font-size: 14px;
  2963. font-weight: 500;
  2964. color: #606266;
  2965. }
  2966. }
  2967. }
  2968. :deep(.preview-dialog) {
  2969. background-color: transparent !important;
  2970. width: auto !important;
  2971. padding: 0 !important;
  2972. .el-dialog__header {
  2973. display: none;
  2974. }
  2975. .el-dialog__body{
  2976. padding: 0;
  2977. .preview-container{
  2978. padding: 0;
  2979. position: relative;
  2980. }
  2981. .preview-image,.preview-video {
  2982. height: 65vh;
  2983. max-width: 100vw;
  2984. }
  2985. }
  2986. }
  2987. .rectification-dialog {
  2988. padding: 8px 0;
  2989. .upload-section {
  2990. margin-bottom: 24px;
  2991. padding: 20px;
  2992. background: #fafafa;
  2993. border-radius: 8px;
  2994. transition: all 0.3s ease;
  2995. &:hover {
  2996. background: #f5f5f5;
  2997. }
  2998. &.required {
  2999. border: 1px solid #e8f4ff;
  3000. background: #f7fbff;
  3001. &:hover {
  3002. background: #f0f8ff;
  3003. }
  3004. }
  3005. .section-header {
  3006. display: flex;
  3007. align-items: center;
  3008. justify-content: space-between;
  3009. margin-bottom: 16px;
  3010. .header-left {
  3011. display: flex;
  3012. align-items: center;
  3013. gap: 8px;
  3014. .section-title {
  3015. font-size: 15px;
  3016. font-weight: 600;
  3017. color: #303133;
  3018. }
  3019. .required-mark {
  3020. color: #f56c6c;
  3021. font-size: 16px;
  3022. font-weight: bold;
  3023. }
  3024. i[class^="icon-"] {
  3025. font-size: 18px;
  3026. color: #409eff;
  3027. }
  3028. }
  3029. .section-tip {
  3030. font-size: 12px;
  3031. color: #909399;
  3032. background: #fff;
  3033. padding: 4px 12px;
  3034. border-radius: 12px;
  3035. border: 1px solid #e4e7ed;
  3036. }
  3037. }
  3038. .upload-area {
  3039. min-height: 80px;
  3040. }
  3041. }
  3042. .info-tips {
  3043. display: flex;
  3044. gap: 12px;
  3045. padding: 16px;
  3046. background: linear-gradient(135deg, #fff9e6 0%, #fff5cc 100%);
  3047. border-left: 4px solid #e6a23c;
  3048. border-radius: 6px;
  3049. margin-top: 20px;
  3050. .icon-info {
  3051. font-size: 20px;
  3052. color: #e6a23c;
  3053. flex-shrink: 0;
  3054. margin-top: 2px;
  3055. }
  3056. .tips-content {
  3057. flex: 1;
  3058. .tips-title {
  3059. font-weight: 600;
  3060. color: #606266;
  3061. margin-bottom: 8px;
  3062. font-size: 14px;
  3063. }
  3064. p {
  3065. font-size: 13px;
  3066. color: #606266;
  3067. line-height: 1.8;
  3068. margin: 0;
  3069. &:not(.tips-title) {
  3070. padding-left: 12px;
  3071. position: relative;
  3072. &::before {
  3073. content: "•";
  3074. position: absolute;
  3075. left: 0;
  3076. color: #e6a23c;
  3077. }
  3078. }
  3079. }
  3080. }
  3081. }
  3082. }
  3083. .icon-document::before {
  3084. content: "📄";
  3085. }
  3086. .icon-image::before {
  3087. content: "🖼️";
  3088. }
  3089. .icon-video::before {
  3090. content: "🎬";
  3091. }
  3092. .icon-info::before {
  3093. content: "ℹ️";
  3094. }
  3095. .dialog-content {
  3096. font-size: 15px;
  3097. line-height: 30px;
  3098. display: flex;
  3099. justify-content: center;
  3100. align-items: center;
  3101. height: 100%;
  3102. }
  3103. </style>