pipeDetail.vue 74 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207
  1. <template>
  2. <div class="app-container">
  3. <!-- 设备清单 -->
  4. <ContentWrap title="设备信息">
  5. <!-- 搜索工作栏 -->
  6. <ContentWrap>
  7. <el-form
  8. :model="equipQueryParams"
  9. ref="equipQueryFormRef"
  10. :inline="true"
  11. label-width="80px"
  12. >
  13. <!-- 基本信息查询部分 -->
  14. <div class="flex flex-wrap items-start gap-x-2">
  15. <!-- <el-form-item label="设备代码" prop="equipCode">-->
  16. <!-- <el-input-->
  17. <!-- v-model="equipQueryParams.equipCode"-->
  18. <!-- placeholder="请输入设备注册代码"-->
  19. <!-- clearable-->
  20. <!-- @keyup.enter="handleEquipQuery"-->
  21. <!-- class="!w-240px"-->
  22. <!-- />-->
  23. <!-- </el-form-item>-->
  24. <el-form-item label="部门" prop="relateDepartment">
  25. <DeptSelect
  26. v-model="equipQueryParams.relateDepartment"
  27. placeholder="请选择部门"
  28. clearable
  29. class="!w-240px"
  30. />
  31. </el-form-item>
  32. <el-form-item label="使用状态" prop="useStatus">
  33. <el-select
  34. v-model="equipQueryParams.useStatus"
  35. placeholder="选择使用状态"
  36. clearable
  37. multiple
  38. collapse-tags
  39. collapse-tags-tooltip
  40. class="!w-240px"
  41. >
  42. <el-option
  43. v-for="dict in getStrDictOptions(DICT_TYPE.PIPE_USE_STATUS)"
  44. :key="dict.value"
  45. :label="dict.label"
  46. :value="dict.value"
  47. />
  48. </el-select>
  49. </el-form-item>
  50. <el-form-item label="临检时间" prop="nextDate">
  51. <div class="flex items-center gap-x-2">
  52. <el-select v-model="datePickerType" class="!w-[90px]">
  53. <el-option label="时间段" value="daterange" />
  54. <el-option label="月份" value="month" />
  55. </el-select>
  56. <el-date-picker
  57. v-if="datePickerType === 'daterange'"
  58. v-model="daterange"
  59. type="daterange"
  60. value-format="YYYY-MM-DD"
  61. start-placeholder="开始日期"
  62. end-placeholder="结束日期"
  63. class="!w-[210px]"
  64. @change="handleDateChange"
  65. />
  66. <el-date-picker
  67. v-else
  68. v-model="month"
  69. type="month"
  70. value-format="YYYY-MM"
  71. placeholder="选择月份"
  72. class="!w-[140px]"
  73. @change="handleMonthChange"
  74. />
  75. </div>
  76. </el-form-item>
  77. </div>
  78. <!-- 区域和时间查询部分 -->
  79. <div class="flex flex-wrap items-start gap-x-2">
  80. <el-form-item label="区域" prop="equipDistrict">
  81. <AreaSelect
  82. v-model="equipQueryParams.equipDistrict"
  83. placeholder="请选择区域"
  84. class="!w-[240px]"
  85. multiple
  86. collapse-tags
  87. collapse-tags-tooltip
  88. :clearable="true"
  89. @clear="handleAreaClear"
  90. @change="handleAreaChange"
  91. />
  92. </el-form-item>
  93. <el-form-item label="街道" prop="equipStreet">
  94. <StreetSelect
  95. v-model="equipQueryParams.equipStreet"
  96. :district-ids="equipQueryParams.equipDistrict"
  97. placeholder="请选择街道"
  98. class="!w-[240px]"
  99. multiple
  100. collapse-tags
  101. collapse-tags-tooltip
  102. :clearable="true"
  103. @clear="handleStreetClear"
  104. @change="handleStreetChange"
  105. />
  106. </el-form-item>
  107. </div>
  108. <div class="">
  109. <el-form-item class="!w-full !mr-0px" label="管道归类" prop="typeList">
  110. <el-checkbox-group
  111. v-model="equipQueryParams.typeList"
  112. class="flex flex-wrap gap-2"
  113. @change="handleTypeListChange"
  114. >
  115. <el-checkbox
  116. v-for="dict in containerTypeOptions"
  117. :key="dict.value"
  118. :label="dict.label"
  119. :value="dict.value"
  120. />
  121. </el-checkbox-group>
  122. </el-form-item>
  123. </div>
  124. <!-- 操作按钮 -->
  125. <div class="flex justify-center">
  126. <el-form-item class="!mb-0">
  127. <el-button @click="handleEquipQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
  128. <el-button @click="resetEquipQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
  129. <!-- 删除设备 -->
  130. <el-button type="danger" :disabled="selectedDetailRows.length === 0" @click="handleRejectEquip">
  131. <Icon icon="ep:failed" class="mr-5px" /> 拒绝约检
  132. </el-button>
  133. <el-button type="primary" @click="handleAddEquip"><Icon icon="ep:plus" class="mr-5px" /> 添加设备</el-button>
  134. <el-button type="primary" @click="handleBatchEditFn" :disabled="selectedRows.length === 0">
  135. <Icon icon="ep:edit" class="mr-5px" /> 批量修改约检联系人
  136. </el-button>
  137. </el-form-item>
  138. </div>
  139. </el-form>
  140. </ContentWrap>
  141. <!-- 设备列表 -->
  142. <ContentWrap>
  143. <el-table
  144. v-loading="loading"
  145. ref="tableRef"
  146. :data="equipList"
  147. stripe
  148. border
  149. @selection-change="handleSelectionChange"
  150. @sort-change="handleSortChange"
  151. :row-key="(row) => row.id"
  152. :row-class-name="getMainRowClassName"
  153. @row-click="handleMainRowClick"
  154. @expand-change="handleExpandChange"
  155. :expand-row-keys="expandRowKeys"
  156. >
  157. <el-table-column type="selection" width="50" fixed="left"/>
  158. <el-table-column type="expand" width="50">
  159. <template #default="props">
  160. <div class="ml-15px mr-15px">
  161. <el-table :data="props.row.detailSaveReqVOS" border
  162. :header-cell-style="{background: '#f5f7fa', color: '#606266'}"
  163. :ref="(el) => setDetailTableRef(el, props.row.id)"
  164. @selection-change="(selection) => handleDetailSelectionChange(selection, props.row)"
  165. :row-class-name="getRowClassName"
  166. class="inner-table"
  167. @row-click="handleRowClick"
  168. >
  169. <el-table-column type="selection" width="50" fixed="left"/>
  170. <el-table-column label="注册代码" prop="equipCode" min-width="120" show-overflow-tooltip/>
  171. <el-table-column label="管道名称" prop="pipeName" min-width="120" show-overflow-tooltip/>
  172. <el-table-column label="管道编号" prop="pipeNo" min-width="120" show-overflow-tooltip/>
  173. <el-table-column label="管道级别" prop="pipeLevel" min-width="120" show-overflow-tooltip/>
  174. <el-table-column label="管道品种" prop="pipeType" min-width="120" show-overflow-tooltip/>
  175. <el-table-column label="长度" prop="pipeLength" min-width="100" show-overflow-tooltip/>
  176. <el-table-column label="管道材质" prop="pipeMaterial" min-width="120" show-overflow-tooltip/>
  177. <el-table-column label="材料标准" prop="materialStandard" min-width="120" show-overflow-tooltip/>
  178. <el-table-column label="定期安全状况等级" prop="legalSafetyStatusLevel" min-width="130" show-overflow-tooltip/>
  179. <el-table-column label="年度安全状况等级" prop="yearSafetyStatusLevel" min-width="130" show-overflow-tooltip/>
  180. </el-table>
  181. </div>
  182. </template>
  183. </el-table-column>
  184. <el-table-column
  185. label="工程号"
  186. align="center"
  187. prop="projectNo"
  188. min-width="120"
  189. >
  190. <template #default="{ row }">
  191. <div
  192. v-if="row.projectNo"
  193. class="cursor-pointer"
  194. @click.stop="toggleExpand(row)"
  195. >
  196. <div class="flex items-center justify-center gap-1 schedule-link">
  197. <span class="text-xs color-blue">{{row.projectNo}}</span>
  198. </div>
  199. </div>
  200. </template>
  201. </el-table-column>
  202. <el-table-column label="工程名称"
  203. align="center"
  204. prop="projectName"
  205. min-width="120"
  206. show-overflow-tooltip
  207. />
  208. <!-- 区域 -->
  209. <el-table-column
  210. label="区域/街道"
  211. align="center"
  212. prop="pipeStreet"
  213. min-width="120"
  214. show-overflow-tooltip
  215. >
  216. <template #default="{ row }">
  217. <span class="text-xs">{{ row.equipStreetName ?? row.equipDistrictName}}</span>
  218. </template>
  219. </el-table-column>
  220. <!-- 管道类别 -->
  221. <el-table-column label="管道类别" align="center" prop="pipeCategory">
  222. <template #default="scope">
  223. {{ containerTypeOptions.find(i => i.value == scope.row.pipeCategory)?.label }}
  224. </template>
  225. </el-table-column>
  226. <el-table-column
  227. label="约检联系人"
  228. align="center"
  229. prop="contact"
  230. min-width="120"
  231. show-overflow-tooltip
  232. />
  233. <el-table-column
  234. label="约检联系人电话"
  235. align="center"
  236. prop="contactPhone"
  237. min-width="140"
  238. show-overflow-tooltip
  239. />
  240. </el-table>
  241. <!-- 分页 -->
  242. <Pagination
  243. :total="equipTotal"
  244. v-model:page="equipQueryParams.pageNo"
  245. v-model:limit="equipQueryParams.pageSize"
  246. @pagination="handleEquipQuery"
  247. />
  248. </ContentWrap>
  249. </ContentWrap>
  250. <!-- 受检信息表单 -->
  251. <ContentWrap title="受检信息">
  252. <el-form :model="formData" :rules="formRules" ref="formRef" label-width="120px" class="p-3">
  253. <!-- <div class="check-info-label-title">受理单位</div> -->
  254. <div class="check-info-label-title">单位信息 </div>
  255. <el-row class="form-group" :gutter="24">
  256. <el-col :span="8">
  257. <!-- 申请单位名称 unitName -->
  258. <el-form-item label="申请单位名称" prop="unitName" class="unit-form-item">
  259. <el-input v-if="useInputUnitName" v-model="formData.unitName.name" placeholder="请输入单位名称" :maxlength="50" />
  260. <CustomLargeListSelect
  261. v-else
  262. v-model:modelValue="formData.unitName"
  263. :fetchFunc="getUnitNewList"
  264. label-key="name"
  265. value-key="id"
  266. searchKeyProp="name"
  267. @change="handleChangeUnit"
  268. />
  269. <el-button type="primary" link @click="() => useInputUnitName = !useInputUnitName">{{useInputUnitName ? '选择单位' : '手动输入'}}</el-button>
  270. </el-form-item>
  271. </el-col>
  272. <el-col :span="8">
  273. <!-- 统一社会信用代码 socialCreditCode -->
  274. <el-form-item label="统一社会信用代码" prop="socialCreditCode" label-width="120px">
  275. <el-input v-model="formData.socialCreditCode" placeholder="请输入统一社会信用代码" :maxlength="50"/>
  276. </el-form-item>
  277. </el-col>
  278. <el-col :span="8">
  279. <!-- 申请单位地址 unitAddress -->
  280. <el-form-item label="申请单位地址" prop="unitAddress" label-width="120px">
  281. <el-input v-model="formData.unitAddress" placeholder="请输入申请单位地址" :maxlength="50"/>
  282. </el-form-item>
  283. </el-col>
  284. <el-col :span="8">
  285. <!-- 邮政编码 zipCode -->
  286. <el-form-item label="申请单位邮政编码" prop="zipCode">
  287. <el-input v-model="formData.zipCode" placeholder="请输入申请单位邮政编码" :maxlength="50"/>
  288. </el-form-item>
  289. </el-col>
  290. </el-row>
  291. <!-- <div class="check-info-label-title">使用单位</div> -->
  292. <el-row class="form-group" :gutter="24">
  293. <el-col :span="8">
  294. <!-- 使用单位名称 useUnitName -->
  295. <el-form-item label="使用单位名称" prop="useUnitName" class="unit-form-item">
  296. <el-input v-if="useInputUseUnitName" v-model="formData.useUnitName.name" placeholder="请输入使用单位名称" :maxlength="50" @blur="handleChangeUseUnitInput" @keyup.enter="handleChangeUseUnitInput" />
  297. <CustomLargeListSelect
  298. v-else
  299. v-model:modelValue="formData.useUnitName"
  300. :fetchFunc="getUnitNewList"
  301. label-key="name"
  302. value-key="id"
  303. searchKeyProp="name"
  304. @change="handleChangeUseUnit"
  305. />
  306. <el-button type="primary" link @click="() => useInputUseUnitName = !useInputUseUnitName">{{useInputUseUnitName ? '选择单位' : '手动输入'}}</el-button>
  307. </el-form-item>
  308. </el-col>
  309. <el-col :span="8">
  310. <!-- 统一社会信用代码 -->
  311. <el-form-item label="统一社会信用代码" prop="useUnitSocialCreditCode" label-width="120px">
  312. <el-input v-model="formData.useUnitSocialCreditCode" placeholder="请输入统一社会信用代码" :maxlength="50"/>
  313. </el-form-item>
  314. </el-col>
  315. <el-col :span="8">
  316. <!-- 使用单位地址 useUnitAddress -->
  317. <el-form-item label="使用单位地址" prop="useUnitAddress" label-width="120px">
  318. <el-input v-model="formData.useUnitAddress" placeholder="请输入使用单位地址" :maxlength="50"/>
  319. </el-form-item>
  320. </el-col>
  321. <el-col :span="8">
  322. <!-- 邮政编码 useUnitZipcode -->
  323. <el-form-item label="使用邮政编码" prop="useUnitZipcode">
  324. <el-input v-model="formData.useUnitZipcode" placeholder="请输入使用邮政编码" :maxlength="50"/>
  325. </el-form-item>
  326. </el-col>
  327. <el-col :span="8">
  328. <!-- 单位联系人 useUnitContact -->
  329. <el-form-item label="单位联系人" prop="useUnitContact" label-width="120px">
  330. <el-input v-model="formData.useUnitContact" placeholder="请输入单位联系人" :maxlength="50"/>
  331. </el-form-item>
  332. </el-col>
  333. <el-col :span="8">
  334. <!-- 联系人电话 useUnitPhone -->
  335. <el-form-item label="联系人电话" prop="useUnitPhone" label-width="120px">
  336. <el-input v-model="formData.useUnitPhone" placeholder="请输入联系人电话" :maxlength="11"/>
  337. </el-form-item>
  338. </el-col>
  339. </el-row>
  340. <div class="check-info-label-title">联系人信息</div>
  341. <el-row class="form-group" :gutter="24">
  342. <el-col :span="8">
  343. <!-- 约检联系人 unitContact -->
  344. <el-form-item label="约检联系人" prop="unitContact">
  345. <el-input v-model="formData.unitContact" placeholder="请输入约检联系人" :maxlength="50"/>
  346. </el-form-item>
  347. </el-col>
  348. <el-col :span="8">
  349. <!-- 约检联系人电话unitPhone -->
  350. <el-form-item label="联系人电话" prop="unitPhone" label-width="120px">
  351. <el-input v-model="formData.unitPhone" placeholder="请输入约检联系人电话" :maxlength="11"/>
  352. </el-form-item>
  353. </el-col>
  354. <el-col :span="8">
  355. <!-- 约检联系人电话email -->
  356. <el-form-item label="联系人邮箱" prop="email" label-width="120px">
  357. <el-input v-model="formData.email" placeholder="请输入约检联系人邮箱"/>
  358. </el-form-item>
  359. </el-col>
  360. <el-col :span="8">
  361. <!-- 电子报告接收人 recipient -->
  362. <el-form-item label="电子报告接收人" prop="recipient" label-width="120px">
  363. <el-input v-model="formData.recipient" placeholder="请输入电子报告接收人" :maxlength="50"/>
  364. </el-form-item>
  365. </el-col>
  366. <el-col :span="8">
  367. <!-- 接收人电话 recipientPhone -->
  368. <el-form-item label="接收人电话" prop="recipientPhone">
  369. <el-input v-model="formData.recipientPhone" placeholder="请输入接收人电话" :maxlength="11"/>
  370. </el-form-item>
  371. </el-col>
  372. <el-col :span="8">
  373. <el-form-item label="接收人邮箱" prop="recipientEmail">
  374. <el-input v-model="formData.recipientEmail" placeholder="请输入接收人邮箱"/>
  375. </el-form-item>
  376. </el-col>
  377. <el-col :span="8">
  378. <el-form-item label="缴费联系人" prop="payerContactName" label-width="120px">
  379. <el-input v-model="formData.payerContactName" placeholder="请输入缴费联系人" :maxlength="50"/>
  380. </el-form-item>
  381. </el-col>
  382. <el-col :span="8">
  383. <!-- 接收人电话 payerContact -->
  384. <el-form-item label="缴费人电话" prop="payerContact">
  385. <el-input v-model="formData.payerContact" placeholder="请输入缴费人电话" :maxlength="11"/>
  386. </el-form-item>
  387. </el-col>
  388. <el-col :span="8">
  389. <el-form-item label="缴费人邮箱" prop="payerMail">
  390. <el-input v-model="formData.payerMail" placeholder="请输入缴费人邮箱"/>
  391. </el-form-item>
  392. </el-col>
  393. </el-row>
  394. <div class="check-info-label-title">受理信息</div>
  395. <el-row class="form-group" :gutter="24">
  396. <el-col :span="8">
  397. <!-- 检验部门 deptId -->
  398. <el-form-item label="检验部门" prop="deptId">
  399. <!-- <el-input v-model="formData.deptId" placeholder="请输入检验部门"/> -->
  400. <DeptSelect
  401. v-model="formData.deptId"
  402. placeholder="请选择部门"
  403. clearable
  404. />
  405. </el-form-item>
  406. </el-col>
  407. <el-col :span="8">
  408. <!-- 联系电话 deptPhone -->
  409. <el-form-item label="联系电话" prop="deptPhone" label-width="120px">
  410. <el-input v-model="formData.deptPhone" placeholder="请输入联系电话" :maxlength="20"/>
  411. </el-form-item>
  412. </el-col>
  413. <el-col :span="8">
  414. <!-- 约检时间 appointmentDate -->
  415. <el-form-item label="约检时间" prop="appointmentDate" label-width="120px">
  416. <el-date-picker
  417. v-model="formData.appointmentDate"
  418. type="date"
  419. value-format="YYYY-MM-DD"
  420. placeholder="请选择约检时间"
  421. :disabled-date="disabledDate"
  422. />
  423. </el-form-item>
  424. </el-col>
  425. <el-col :span="24">
  426. <!-- 检验员 teamList -->
  427. <el-form-item label="检验员" prop="teamList">
  428. <!-- <CheckerSelect-->
  429. <!-- ref="checkerSelectRef"-->
  430. <!-- v-model="selectedCheckers"-->
  431. <!-- :dept-id="userStore.getUser.deptId?.toString() || '1'"-->
  432. <!-- :disabled="false"-->
  433. <!-- :has-data="true"-->
  434. <!-- empty-text="暂无检验员数据"-->
  435. <!-- :multiple="true"-->
  436. <!-- @change="handleCheckerChange"-->
  437. <!-- />-->
  438. <div v-if="formattedCheckers.length > 0" class="selected-checkers">
  439. <el-tag
  440. v-for="checker in formattedCheckers"
  441. :key="checker.id"
  442. effect="plain"
  443. class="mr-8px mb-8px"
  444. >
  445. {{ checker.label }}
  446. </el-tag>
  447. </div>
  448. <span v-else>-</span>
  449. </el-form-item>
  450. </el-col>
  451. </el-row>
  452. <div class="check-info-label-title">异常信息</div>
  453. <el-row class="m-b-20px">
  454. <el-button v-if="!exceptionInfo.id" type="primary" @click="addExceptionInfo">添加异常单</el-button>
  455. <el-button v-else type="primary" @click="deleteExceptionInfo">删除异常单</el-button>
  456. </el-row>
  457. <el-row v-if="exceptionInfo.id" class="form-group" :gutter="24">
  458. <el-col :span="8">
  459. <el-form-item label="有效日期调整原因" prop="effectiveDateAdjustReason">
  460. <el-input v-model="exceptionInfo.effectiveDateAdjustReason" placeholder="请输入有效日期调整原因"/>
  461. </el-form-item>
  462. </el-col>
  463. <el-col :span="8">
  464. <el-form-item label="受理建议" prop="acceptSuggeston">
  465. <el-input v-model="exceptionInfo.acceptSuggeston" placeholder="请输入受理建议"/>
  466. </el-form-item>
  467. </el-col>
  468. <el-col :span="8">
  469. <el-form-item label="调整有效日期" prop="adjustEffectiveDate">
  470. <el-date-picker
  471. v-model="exceptionInfo.adjustEffectiveDate"
  472. type="date"
  473. value-format="YYYY-MM-DD"
  474. placeholder="请选择调整有效日期"
  475. />
  476. </el-form-item>
  477. </el-col>
  478. <el-col :span="12">
  479. <el-form-item label="情况说明" prop="descrition">
  480. <component :is="getExceptionItem('descrition', 'descritionAttach')" />
  481. </el-form-item>
  482. </el-col>
  483. <el-col :span="12">
  484. <el-form-item label="重启表" prop="restartTable">
  485. <component :is="getExceptionItem('restartTable', 'restartTableAttach')" />
  486. </el-form-item>
  487. </el-col>
  488. <el-col :span="12">
  489. <el-form-item label="指令书" prop="instrustionBook">
  490. <component :is="getExceptionItem('instrustionBook', 'instrustionBookAttach')" />
  491. </el-form-item>
  492. </el-col>
  493. <el-col :span="12">
  494. <el-form-item label="受理回执" prop="acceptReceipt">
  495. <component :is="getExceptionItem('acceptReceipt', 'acceptReceiptAttach')" />
  496. </el-form-item>
  497. </el-col>
  498. <el-col :span="12">
  499. <el-form-item label="其他" prop="other">
  500. <component :is="getExceptionItem('other', 'otherAttach')" />
  501. </el-form-item>
  502. </el-col>
  503. <el-col :span="12">
  504. <el-form-item label="提前检验申请函" prop="earlyCheckApply">
  505. <component :is="getExceptionItem('earlyCheckApply', 'earlyCheckApplyAttach')" />
  506. </el-form-item>
  507. </el-col>
  508. </el-row>
  509. <div class="check-info-label-title">检验收费</div>
  510. <el-row class="form-group" :gutter="24">
  511. <el-col :span="24">
  512. <!-- 定期检查&超年限检查 -->
  513. <el-form-item label="检验性质" prop="checkType" label-width="120px">
  514. <el-select disabled v-model="formData.checkType" placeholder="请选择检验性质" clearable class="!w-120px">
  515. <el-option
  516. v-for="(item, key) in checkStatusMap"
  517. :key="key"
  518. :label="item"
  519. :value="key"
  520. />
  521. </el-select>
  522. </el-form-item>
  523. <!-- 收费性质 feeNature -->
  524. <el-form-item prop="feeNature" >
  525. <template #label>
  526. <div class="flex flex-col text-right">
  527. <span>收费性质</span>
  528. <span>(行政事业收费)</span>
  529. </div>
  530. </template>
  531. <el-radio-group v-model="formData.feeNature">
  532. <el-radio v-for="(item, key) in PressureFeeNatureMap" :key="key" :value="key" :label="item"/>
  533. </el-radio-group>
  534. </el-form-item>
  535. </el-col>
  536. <el-col :span="24">
  537. <!-- 收费形式 feeType -->
  538. <el-form-item label="收费形式" prop="feeType">
  539. <el-radio-group v-model="formData.feeType">
  540. <el-radio v-for="(item, key) in PressureFeeTypeMap" :key="key" :value="key" :label="item"/>
  541. </el-radio-group>
  542. </el-form-item>
  543. </el-col>
  544. <el-col :span="24">
  545. <!-- 收费金额 actualAmount -->
  546. <!-- <el-form-item label="收费金额" prop="actualAmount" class="actualAmountFormItem">
  547. <el-input-number
  548. :controls="false"
  549. v-model="formData.actualAmount"
  550. placeholder="请输入收费金额"
  551. :min="0"
  552. />
  553. <div style="padding-left: 8px;">大写({{numberToChinese(formData.actualAmount)}})含税</div>
  554. </el-form-item>-->
  555. <div class="flex items-center gap-32 ml-40px text-14px">
  556. <span>应收法定金额: {{ formData.shouldAmount }}</span>
  557. <span>服务收费金额:{{ formData.serviceAmount }}</span>
  558. <span>免征费用: {{ formData.reduceFee }}</span>
  559. </div>
  560. </el-col>
  561. <!-- <el-col :span="24">
  562. &lt;!&ndash; 年度检查 &ndash;&gt;
  563. <el-form-item label="" prop="feeDate" label-width="30" v-if="orderDetail?.checkType == 200">
  564. <div class="feeDate_box">
  565. 甲方在本服务单签订之日起
  566. <el-input-number :controls="false" v-model="formData.feeDate" placeholder="请输入" :maxlength="50"/>
  567. 日内一次性支付。
  568. </div>
  569. </el-form-item>
  570. </el-col>-->
  571. </el-row>
  572. </el-form>
  573. </ContentWrap>
  574. <!-- 添加设备弹窗 -->
  575. <PipeAddEquipDialog
  576. v-model:visible="addEquipVisible"
  577. type="confirm"
  578. :target-id="orderDetail?.id || ''"
  579. :unit-code="orderDetail?.unitCode || ''"
  580. :unit-name="orderDetail?.unitName || ''"
  581. @success="handleAddEquipSuccess"
  582. />
  583. <!-- 拒绝约检弹窗 -->
  584. <el-dialog
  585. v-model="rejectDialogVisible"
  586. title="拒绝约检"
  587. width="600px"
  588. destroy-on-close
  589. :close-on-click-modal="false"
  590. >
  591. <div class="mb-4">
  592. <!-- <div v-for="equip in selectedRows" :key="equip.id" class="mb-2">
  593. 工程号:{{ equip.projectNo }}
  594. </div> -->
  595. 管道数量: {{ selectedRows?.length || 0 }}
  596. </div>
  597. <div class="mb-4">
  598. 检验性质:{{ orderDetail?.checkType ? allCheckTypeMap[orderDetail.equipMainType][orderDetail.checkType] : '' }}
  599. </div>
  600. <div class="mb-4 flex flex-start items-center">
  601. <div class="!w-80px">拒绝原因:</div>
  602. <el-select v-model="rejectForm.reasonDict" placeholder="请选择拒绝原因" @change="() => {rejectForm.reason = ''}">
  603. <el-option
  604. v-for="dict in getStrDictOptions('refuseInspectedCategory')"
  605. :key="dict.value"
  606. :label="dict.label"
  607. :value="dict.value"
  608. />
  609. </el-select>
  610. </div>
  611. <div class="mb-4" v-if="rejectForm.reasonDict == '5'">
  612. <div class="mb-2">说明:</div>
  613. <el-input
  614. v-model.trim="rejectForm.reason"
  615. type="textarea"
  616. :rows="4"
  617. placeholder="请输入"
  618. resize="none"
  619. :maxlength="100"
  620. show-word-limit
  621. />
  622. </div>
  623. <template #footer>
  624. <div class="flex justify-end items-center">
  625. <!-- <el-button type="primary" :loading="rejectLoading" @click="handleRejectConfirm(200)">上报拒检</el-button> -->
  626. <!-- <el-button v-if="orderDetail?.checkType === 100 || orderDetail?.checkType === 300" type="primary" :loading="rejectLoading" @click="handleRejectConfirm(100)">上报市局</el-button>-->
  627. <el-button type="primary" :loading="rejectLoading" @click="handleRejectConfirm(100)">上报市局</el-button>
  628. <!-- <el-button v-else type="primary" :loading="rejectLoading" @click="handleRejectConfirm(200)">上报拒检</el-button>-->
  629. <el-button type="warning" :loading="rejectLoading" @click="handleRejectConfirm(300)">无需上报</el-button>
  630. <el-button @click="rejectDialogVisible = false">取消</el-button>
  631. </div>
  632. </template>
  633. </el-dialog>
  634. <!-- 页面底部按钮 -->
  635. <div class="flex justify-center pb-4">
  636. <el-button @click="handleClose" :loading="loading">关闭</el-button>
  637. <el-button v-if="!route.query.id || orderDetail?.createAcceptOrder === false" type="primary" :loading="loading" @click="handleGenerateAcceptance">
  638. 生成受理单
  639. </el-button>
  640. <el-button type="primary" @click="handleSave" :loading="loading">保存</el-button>
  641. </div>
  642. <PipeBatchEditForm ref="batchEditFormRef" @success="()=>handleEquipQuery(true)"/>
  643. <EquipContainerForm ref="EquipContainerFormRef" />
  644. <FileUploadModal ref="fileUploadModalRef" @success="handleFileUploadSuccess" />
  645. </div>
  646. </template>
  647. <script setup lang="tsx">
  648. import { ref, onMounted, computed, watch, nextTick } from 'vue'
  649. import { useRoute, useRouter } from 'vue-router'
  650. import { ElMessage, ElMessageBox } from 'element-plus'
  651. import PipeBatchEditForm from '../planNew/components/PipeBatchEditForm.vue'
  652. import {
  653. BoilerAppointmentConfirmOrderApi,
  654. BoilerAppointmentConfirmOrderEquipmentVO,
  655. BoilerAppointmentConfirmOrderVO
  656. } from '@/api/pressure2/appointmentconfirmorder'
  657. import { EquipBoilerSchedulingEquipVO } from '@/api/pressure2/equipboilerscheduling'
  658. import dayjs from 'dayjs'
  659. import { getStrDictOptions, DICT_TYPE } from '@/utils/dict'
  660. import DeptSelect from '../../pressure2/equipboilerscheduling/components/DeptSelect.vue'
  661. import AreaSelect from '../../system/equipcontainer/components/AreaSelect.vue'
  662. import StreetSelect from '../../system/equipcontainer/components/StreetSelect.vue'
  663. import PipeAddEquipDialog from '../schedule/components/PipeAddEquipDialog.vue'
  664. import {formatArrayDate, formatDate} from '@/utils/formatTime'
  665. import EquipContainerForm from '@/components/EquipContainerForm/index.vue'
  666. import { useTagsViewStore } from '@/store/modules/tagsView'
  667. import {
  668. PressureCheckTypeMap,
  669. PressureFeeTypeMap,
  670. PressureFeeNatureMap,
  671. PressureFeeNature,
  672. allCheckTypeMap
  673. } from '@/utils/constants'
  674. import { useEmitt } from '@/hooks/web/useEmitt'
  675. import { getUnitNewList, getUnitContacts } from '@/api/laboratory/unit'
  676. import { numberToChinese } from '@/utils/formatter'
  677. import FileUploadModal from './components/ImportFile.vue'
  678. import { buildFileUrl } from '@/utils'
  679. import { Icon } from '@/components/Icon'
  680. import { cloneDeep } from 'lodash-es'
  681. import {
  682. PipeEquipmentApi,
  683. PipeEquipmentDetailVO,
  684. PipeEquipmentVO
  685. } from "@/api/pressure2/pipeequipment";
  686. import {PipeAppointmentConfirmOrderApi} from "@/api/pressure2/pipeappointmentconfirmorder";
  687. import {UserQualificationsApi} from "@/api/pressure2/userQualifications";
  688. import CheckerSelect, { type CheckerItem } from '@/views/pressure2/components/CheckerSelect'
  689. import { useUserStoreWithOut } from '@/store/modules/user'
  690. const { emitter } = useEmitt()
  691. const route = useRoute()
  692. const router = useRouter()
  693. const tagsViewStore = useTagsViewStore()
  694. const batchEditFormRef = ref()
  695. const orderDetail = ref<BoilerAppointmentConfirmOrderVO>()
  696. const checkTypeMap = PressureCheckTypeMap
  697. const boilerTypeOptions = getStrDictOptions(DICT_TYPE.SYSTEM_EQUIP_BOILER_TYPE)
  698. const formData = ref<Record<string,any>>({
  699. checkType: '', //检验性质
  700. appointmentDate: '',
  701. teamList: [] as string[],
  702. unitName: {
  703. name: '',
  704. id: ''
  705. },
  706. deptName: '',
  707. equipDistrict: undefined as number | undefined,
  708. equipDistrictName: '',
  709. equipStreet: '',
  710. equipStreetName: '',
  711. unitContact: '',
  712. unitCode: '',
  713. unitContactDept: '',
  714. unitAddress: '',
  715. unitPhone: '',
  716. zipCode: '',
  717. mobile: '',
  718. remark: '',
  719. email: '',
  720. isAttach: false,
  721. sendType: '100',
  722. vehicle: '100',
  723. feeType: '100',
  724. acceptType: '100',
  725. actualAmount: 0,
  726. serviceAmount: 0,
  727. shouldAmount: 0,
  728. reduceFee: 0,
  729. planMaker: '',
  730. deptId: '',
  731. operator: '',
  732. areaDisplay: '',
  733. socialCreditCode: '',
  734. recipient: '',
  735. recipientPhone: '',
  736. recipientEmail: '',
  737. useUnitName: {
  738. name: '',
  739. id: ''
  740. },
  741. useUnitSocialCreditCode: '',
  742. useUnitAddress: '',
  743. useUnitZipcode: '',
  744. useUnitContact: '',
  745. useUnitPhone: '',
  746. deptPhone: '',
  747. feeNature: '',
  748. feeDate: 0,
  749. descritionFile: [],
  750. })
  751. const checkStatusMap = {
  752. '100': '定期检验',
  753. '200': '年度检查',
  754. }
  755. const loading = ref(false)
  756. // 检验员组件引用
  757. const checkerSelectRef = ref()
  758. // 选中的检验员对象列表
  759. const selectedCheckers = ref<CheckerItem[]>([])
  760. const userStore = useUserStoreWithOut()
  761. const EquipContainerFormRef = ref<InstanceType<typeof EquipContainerForm>>()
  762. // 记录每个区域下已选择的街道
  763. const areaStreetMap = ref(new Map<number, number[]>())
  764. const selectedRows = ref([]) // 选中的行
  765. const selectedDetailRows = ref([]) // 选中的行
  766. const tableRef = ref()
  767. // 设备查询相关
  768. const equipQueryParams = ref({
  769. pageNo: 1,
  770. pageSize: 50,
  771. equipDistrict: undefined as number[] | undefined,
  772. equipStreet: undefined as number[] | undefined,
  773. equipCode: '',
  774. productNo: '',
  775. relateDepartment: '' as string | number | undefined,
  776. typeList: [] as string[],
  777. nextDate: [] as string[],
  778. useStatus: [] as string[],
  779. })
  780. const equipQueryFormRef = ref()
  781. const equipLoading = ref(false)
  782. const equipList = ref<EquipBoilerSchedulingEquipVO[]>([])
  783. // 选中的设备
  784. const selectedEquips = ref<EquipBoilerSchedulingEquipVO[]>([])
  785. const equipTotal = ref(0)
  786. const equipCategoryOptions = getStrDictOptions(DICT_TYPE.SYSTEM_EQUIP_CONTAINER_EQUIP_CATEGORY)
  787. const containerTypeOptions = getStrDictOptions(DICT_TYPE.PIPE_TYPE)
  788. // 临检时间相关
  789. const datePickerType = ref<'daterange' | 'month'>('month')
  790. const daterange = ref<string[]>([])
  791. const month = ref<string>('')
  792. // 添加设备相关
  793. const addEquipVisible = ref(false)
  794. // 拒绝约检相关
  795. const rejectDialogVisible = ref(false)
  796. const rejectLoading = ref(false)
  797. const rejectForm = ref({
  798. reason: '',
  799. reasonDict: ''
  800. })
  801. const formRef = ref() // 表单引用
  802. /** 约检确认详情 */
  803. defineOptions({ name: 'OrderConfirmDetail' })
  804. // 表单验证规则
  805. const formRules = {
  806. // unitContact: [
  807. // { required: true, message: '请输入单位联系人', trigger: 'blur' }
  808. // ],
  809. // unitPhone: [
  810. // { required: true, message: '请输入单位联系电话', trigger: 'blur' },
  811. // ],
  812. // mobile: [
  813. // { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
  814. // ],
  815. email: [
  816. { pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, message: '请输入正确的邮箱', trigger: 'blur' }
  817. ],
  818. // unitPhone: [
  819. // { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
  820. // ],
  821. // recipientPhone: [
  822. // { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
  823. // ],
  824. recipientEmail: [
  825. { pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, message: '请输入正确的邮箱', trigger: 'blur' }
  826. ],
  827. // payerContact: [
  828. // { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
  829. // ],
  830. payerMail: [
  831. { pattern: /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/, message: '请输入正确的邮箱', trigger: 'blur' }
  832. ],
  833. sendType: [
  834. { required: true, message: '请选择送达方式', trigger: 'blur' }
  835. ],
  836. // feeType: [
  837. // { required: true, message: '请选择收费类型', trigger: 'blur' }
  838. // ],
  839. acceptType: [
  840. { required: true, message: '请选择受理方式', trigger: 'blur' }
  841. ],
  842. vehicle: [
  843. { required: true, message: '请选择交通工具', trigger: 'blur' }
  844. ],
  845. checkType: [
  846. { required: true, message: '请选择检验性质', trigger: 'blur' }
  847. ]
  848. }
  849. // 区域显示属性,与formData.areaDisplay分离
  850. const areaDisplay = computed(() => {
  851. const district = formData.value.equipDistrictName || ''
  852. const street = formData.value.equipStreetName || ''
  853. if (district && street) {
  854. return `${district}-${street}`
  855. }
  856. return district
  857. })
  858. // 获取约检详情
  859. const getDetail = async () => {
  860. const id = route.query?.id as string
  861. if (!id) return
  862. loading.value = true
  863. try {
  864. const res = await PipeAppointmentConfirmOrderApi.getAppointmentConfirmOrder(id);
  865. let { unitCode, payerContact, payerContactName, payerMail, email } = res;
  866. orderDetail.value = res || {};
  867. // 更新表单数据
  868. formData.value = {
  869. ...formData.value,
  870. checkType: `${orderDetail.value?.checkType }`,
  871. appointmentDate: formatArrayDate(orderDetail.value?.appointmentDate || []),
  872. unitName: {
  873. name: orderDetail.value?.unitName || '',
  874. id: ''
  875. },
  876. useUnitName: {
  877. name: orderDetail.value?.useUnitName || '',
  878. id: ''
  879. },
  880. deptName: orderDetail.value?.deptName || '',
  881. equipDistrict: orderDetail.value?.equipDistrict ? Number(orderDetail.value.equipDistrict) : undefined,
  882. equipDistrictName: orderDetail.value?.equipDistrictName || '',
  883. unitContact: orderDetail.value?.unitContact || '',
  884. unitCode,
  885. unitAddress: orderDetail.value?.unitAddress || '',
  886. unitPhone: orderDetail.value?.unitPhone || '',
  887. zipCode: orderDetail.value?.zipCode || '',
  888. mobile: orderDetail.value?.mobile || '',
  889. remark: orderDetail.value?.remark || '',
  890. // email: orderDetail.value?.email || '',
  891. isAttach: orderDetail.value?.isAttach || false,
  892. sendType: String(orderDetail.value?.sendType || 100),
  893. vehicle: String(orderDetail.value?.vehicle || 100),
  894. feeType: String(orderDetail.value?.feeType || 100),
  895. acceptType: String(orderDetail.value?.acceptType || 100),
  896. actualAmount: orderDetail.value?.actualAmount || 0,
  897. serviceAmount: orderDetail.value?.serviceAmount || 0,
  898. shouldAmount: orderDetail.value?.shouldAmount || 0,
  899. reduceFee: orderDetail.value?.reduceFee || 0,
  900. deptId: orderDetail.value?.deptId || '',
  901. operator: orderDetail.value?.operator || '',
  902. equipStreetName: orderDetail.value?.equipStreetName || '',
  903. areaDisplay: areaDisplay.value,
  904. socialCreditCode: orderDetail.value?.socialCreditCode,
  905. recipient: orderDetail.value?.recipient,
  906. recipientPhone: orderDetail.value?.recipientPhone,
  907. recipientEmail: orderDetail.value?.recipientEmail,
  908. useUnitSocialCreditCode: orderDetail.value?.useUnitSocialCreditCode,
  909. useUnitAddress: orderDetail.value?.useUnitAddress,
  910. useUnitZipcode: orderDetail.value?.useUnitZipcode,
  911. useUnitContact: orderDetail.value?.useUnitContact,
  912. useUnitPhone: orderDetail.value?.useUnitPhone,
  913. deptPhone: orderDetail.value?.deptPhone,
  914. feeNature: `${orderDetail.value?.feeNature}`,
  915. feeDate: orderDetail.value?.feeDate || 0,
  916. payerContact,
  917. payerContactName,
  918. payerMail,
  919. email,
  920. }
  921. // 更新异常信息数据
  922. // 更新检验员数据
  923. const orderExceptionRespVO = res.orderExceptionRespVO
  924. if(orderExceptionRespVO){
  925. exceptionInfo.value = {
  926. ...orderExceptionRespVO,
  927. adjustEffectiveDate: orderExceptionRespVO.adjustEffectiveDate ? dayjs(orderExceptionRespVO.adjustEffectiveDate.join('-')).format('YYYY-MM-DD') : '',
  928. }
  929. }
  930. console.log(exceptionInfo.value, 'exceptionInfo')
  931. const teamList = orderDetail.value?.teamList?.flatMap((team: any) => {
  932. const teamMembers: any[] = []
  933. // 处理组长
  934. if (team.leaders && team.leaders.length > 0) {
  935. team.leaders.forEach((leader: any) => {
  936. if (leader) {
  937. teamMembers.push({
  938. memberId: leader.id,
  939. groupTeamId: team.groupTeamId,
  940. isLeader: true,
  941. member: leader
  942. })
  943. }
  944. })
  945. }
  946. // 处理组员
  947. if (team.members && team.members.length > 0) {
  948. team.members.forEach((member: any) => {
  949. if (member) {
  950. teamMembers.push({
  951. memberId: member.id,
  952. groupTeamId: team.groupTeamId,
  953. isLeader: false,
  954. member: member
  955. })
  956. }
  957. })
  958. }
  959. return teamMembers
  960. }) || []
  961. if (teamList.length > 0) {
  962. selectedCheckers.value = teamList
  963. formData.value.teamList = teamList
  964. }
  965. // 加载检验员列表(默认当前登录人的部门)
  966. const deptId = userStore.getUser.deptId?.toString()
  967. if (deptId) {
  968. await nextTick()
  969. checkerSelectRef.value?.getCheckerList(deptId)
  970. }
  971. } catch (error) {
  972. console.error('获取计划详情失败:', error)
  973. ElMessage.error('获取计划详情失败')
  974. router.back()
  975. } finally {
  976. loading.value = false
  977. }
  978. }
  979. /** 处理检验员变化 */
  980. const handleCheckerChange = (checkers: CheckerItem[]) => {
  981. selectedCheckers.value = checkers
  982. // 同步更新 formData.value.teamList
  983. formData.value.teamList = checkers
  984. }
  985. /** 禁用日期 */
  986. const disabledDate = (time: Date) => {
  987. // 获取所有设备的最早检验日期
  988. const dates = equipList.value.map(item => {
  989. if (orderDetail.value?.checkType === 100) {
  990. return item.nextCheckDate ? formatArrayDate(item.nextCheckDate) : null
  991. } else if (orderDetail.value?.checkType === 200) {
  992. return item.nextYearCheckDate ? formatArrayDate(item.nextYearCheckDate) : null
  993. } else {
  994. return item.nextExpiredCheckDate ? formatArrayDate(item.nextExpiredCheckDate) : null
  995. }
  996. }).filter(Boolean) as any
  997. // 如果没有有效日期,则禁用过去的日期
  998. if (dates.length === 0) {
  999. return time.getTime() < dayjs().startOf('day').valueOf()
  1000. }
  1001. // 找到最早的检验日期
  1002. const earliestDate = new Date(Math.min(...dates.map(date => new Date(date).getTime())))
  1003. // 禁用早于最早检验日期的日期
  1004. return time.getTime() > earliestDate.getTime()
  1005. }
  1006. // 存储所有子表引用的对象
  1007. const detailTableRefs = ref<Record<string, any>>({})
  1008. const setDetailTableRef = (el: any, parentId: string) => {
  1009. if (el) {
  1010. detailTableRefs.value[parentId] = el;
  1011. }
  1012. }
  1013. const getDetailTableRef = (parentId: string) => {
  1014. return detailTableRefs.value[parentId];
  1015. }
  1016. const handleDetailSelectionChange = (selection: any[], mainRow: any) => {
  1017. // 如果正在恢复选中状态,不更新缓存(避免展开时 toggleRowSelection 触发此函数覆盖缓存)
  1018. if (isRestoringSelection.value) {
  1019. return
  1020. }
  1021. const rowId = mainRow.id
  1022. // 更新缓存中的选中状态(如果缓存不存在则创建)
  1023. if (!expandedRowsCache.value.has(rowId)) {
  1024. // 如果该行还未展开,初始化缓存
  1025. expandedRowsCache.value.set(rowId, {
  1026. pipes: mainRow.detailSaveReqVOS || [],
  1027. selectedIds: new Set<string>()
  1028. })
  1029. }
  1030. const cache = expandedRowsCache.value.get(rowId)!
  1031. cache.selectedIds = new Set(selection.map((item: any) => item.id))
  1032. // 更新全局选中的子表数据
  1033. selectedDetailRows.value = selectedDetailRows.value.filter((item) => item.equipPipeId !== mainRow.id)
  1034. selectedDetailRows.value = [...selectedDetailRows.value.filter(row => !selection.some(sel => sel.id === row.id)), ...selection]
  1035. // 如果子表有选中项,则自动选中主表行;否则取消选中主表行
  1036. nextTick(() => {
  1037. if (selection.length > 0) {
  1038. // 子表有选中项,选中主表行
  1039. tableRef.value.toggleRowSelection(mainRow, true);
  1040. } else if (!selectedDetailRows.value.map(row => row.equipPipeId).includes(mainRow.id)) {
  1041. const currentlySelected = selectedRows.value.some(row => row.id === mainRow.id);
  1042. if (currentlySelected && selection.length === 0) {
  1043. // 只有当主行当前是选中状态且子表没有任何选中项时才取消主行选择
  1044. tableRef.value.toggleRowSelection(mainRow, false);
  1045. }
  1046. }
  1047. });
  1048. }
  1049. const handleSelectionChange = async (selection: any[]) => {
  1050. // 计算取消选择的行
  1051. const deselectedRows = selectedRows.value.filter(
  1052. item => !selection.includes(item)
  1053. );
  1054. selectedRows.value = selection
  1055. // 清除取消选中行的子表选择(包括展开的和收起的)
  1056. deselectedRows.forEach(item => {
  1057. const rowId = item.id
  1058. // 如果该行是展开状态,直接清除表格选中
  1059. const tableRef = getDetailTableRef(rowId)
  1060. if (tableRef) {
  1061. tableRef.clearSelection()
  1062. }
  1063. // 无论是否展开,都清除缓存中的选中状态
  1064. if (expandedRowsCache.value.has(rowId)) {
  1065. expandedRowsCache.value.get(rowId)!.selectedIds.clear()
  1066. }
  1067. // 从全局选中的子表数据中移除
  1068. selectedDetailRows.value = selectedDetailRows.value.filter(
  1069. detailRow => detailRow.equipPipeId !== rowId
  1070. )
  1071. })
  1072. }
  1073. // 标志位:防止展开时恢复选中状态触发 handleDetailSelectionChange 覆盖缓存
  1074. const isRestoringSelection = ref(false)
  1075. // 已展开行的 key 列表
  1076. const expandRowKeys = ref<string[]>([])
  1077. // 缓存展开行的子表数据和选中状态
  1078. const expandedRowsCache = ref<Map<string, { pipes: any[], selectedIds: Set<string> }>>(new Map())
  1079. const getMainRowClassName = ({ row }) => {
  1080. const isSelected = tableRef.value?.getSelectionRows().some((item) =>
  1081. item.id === row.id
  1082. )
  1083. return isSelected ? 'selected-row' : ''
  1084. }
  1085. const getRowClassName = ({row}) => {
  1086. // 找到当前行所在的分组,从 Map 中获取对应的表格实例
  1087. const tableRef = getDetailTableRef(row.equipPipeId)
  1088. if (tableRef) {
  1089. const isSelected = tableRef.getSelectionRows().some((item) =>
  1090. item.id === row.id
  1091. )
  1092. return isSelected ? 'selected-row' : ''
  1093. }
  1094. return ''
  1095. }
  1096. const handleExpandChange = (row, expandedRows: any[]) => {
  1097. const rowId = row.id
  1098. const isExpanded = expandedRows.some((r) => r.id === rowId)
  1099. if (isExpanded) {
  1100. // 展开时,从缓存中恢复或初始化
  1101. if (!expandedRowsCache.value.has(rowId)) {
  1102. // 首次展开,初始化缓存
  1103. expandedRowsCache.value.set(rowId, {
  1104. pipes: row.detailSaveReqVOS || [],
  1105. selectedIds: new Set<string>()
  1106. })
  1107. }
  1108. // 恢复选中状态
  1109. nextTick(() => {
  1110. const cache = expandedRowsCache.value.get(rowId)
  1111. const tableRef = getDetailTableRef(rowId)
  1112. if (cache && tableRef && cache.pipes.length > 0) {
  1113. // 设置标志位,防止触发 handleDetailSelectionChange
  1114. isRestoringSelection.value = true
  1115. cache.pipes.forEach((pipe) => {
  1116. if (cache.selectedIds.has(pipe.id)) {
  1117. tableRef.toggleRowSelection(pipe, true)
  1118. }
  1119. })
  1120. // 恢复完成后,延迟重置标志位
  1121. nextTick(() => {
  1122. isRestoringSelection.value = false
  1123. })
  1124. }
  1125. })
  1126. } else {
  1127. // 收起时,保存当前选中状态
  1128. const tableRef = getDetailTableRef(rowId)
  1129. if (tableRef) {
  1130. const currentSelection = tableRef.getSelectionRows()
  1131. const selectedIds = new Set(currentSelection.map((item: any) => item.id))
  1132. expandedRowsCache.value.set(rowId, {
  1133. pipes: row.detailSaveReqVOS || [],
  1134. selectedIds: selectedIds
  1135. })
  1136. }
  1137. }
  1138. // 更新展开的行 key 列表
  1139. expandRowKeys.value = expandedRows.map((r) => r.id)
  1140. }
  1141. /** 处理行点击勾选 */
  1142. const handleMainRowClick = (row, _event,_column) => {
  1143. tableRef.value?.toggleRowSelection(row)
  1144. }
  1145. const handleRowClick = (row, _event, column) => {
  1146. // 如果点击的是选择列,不处理
  1147. if (column.type === 'selection') {
  1148. return
  1149. }
  1150. // 找到当前行所在的分组,从 Map 中获取对应的表格实例
  1151. const tableRef = getDetailTableRef(row.equipPipeId)
  1152. if (tableRef && tableRef.toggleRowSelection) {
  1153. tableRef.toggleRowSelection(row)
  1154. }
  1155. }
  1156. const toggleExpand = async (row: any) => {
  1157. const key = row.id
  1158. if (!expandRowKeys.value.includes(key)) {
  1159. await PipeAppointmentConfirmOrderApi.getPipeEquipmentDetailListByPipeEquipmentId(orderDetail.value.id, key).then((res) => {
  1160. let find = equipList.value.find(item => item.id === key);
  1161. find.detailSaveReqVOS = res.list
  1162. })
  1163. }
  1164. tableRef.value.toggleRowExpansion(row)
  1165. }
  1166. const handleSortChange = ({ prop, order }) => {
  1167. if (!prop || !order) {
  1168. equipList.value.sort((a, b) => 0) // 重置排序
  1169. return
  1170. }
  1171. equipList.value.sort((a, b) => {
  1172. const aValue = a[prop] || ''
  1173. const bValue = b[prop] || ''
  1174. if (order === 'ascending') {
  1175. return aValue > bValue ? 1 : -1
  1176. } else {
  1177. return aValue < bValue ? 1 : -1
  1178. }
  1179. })
  1180. }
  1181. // 设备查询相关
  1182. const handleEquipQuery = async (isUpdateContact: boolean = false) => {
  1183. equipLoading.value = true
  1184. try {
  1185. const params = {
  1186. pageNo: equipQueryParams.value.pageNo,
  1187. pageSize: equipQueryParams.value.pageSize,
  1188. orderId: orderDetail.value?.id,
  1189. equipCode: equipQueryParams.value.equipCode,
  1190. productNo: equipQueryParams.value.productNo,
  1191. relateDepartment: equipQueryParams.value.relateDepartment,
  1192. equipDistrict: equipQueryParams.value.equipDistrict,
  1193. equipStreet: equipQueryParams.value.equipStreet,
  1194. typeList: equipQueryParams.value.typeList || [],
  1195. nextDate: equipQueryParams.value.nextDate || [],
  1196. useStatus: equipQueryParams.value.useStatus && equipQueryParams.value.useStatus.length > 0
  1197. ? equipQueryParams.value.useStatus.map((item) => +item)
  1198. : [],
  1199. unitName: formData.value.useUnitName.name,
  1200. }
  1201. // 移除空值
  1202. Object.keys(params).forEach(key => {
  1203. if (params[key] === '' || params[key] === undefined || params[key] === null) {
  1204. delete params[key]
  1205. }
  1206. // 处理空数组
  1207. if (Array.isArray(params[key]) && params[key].length === 0) {
  1208. delete params[key]
  1209. }
  1210. })
  1211. // 重置展开状态、选中状态和缓存
  1212. expandRowKeys.value = []
  1213. selectedDetailRows.value = []
  1214. selectedRows.value = []
  1215. expandedRowsCache.value.clear()
  1216. // 调用后端API获取数据
  1217. const res = await PipeAppointmentConfirmOrderApi.getAppointmentConfirmOrderEquipmentList(params)
  1218. equipList.value = res.list
  1219. equipTotal.value = res.total
  1220. // 批量修改约检联系人后,申请单位约检联系人与手机号,报告接收人与手机号同步修改。若使用单位与申请单位一致时,也需进行同步修改
  1221. if(isUpdateContact && equipList.value.length > 0){
  1222. const { contact, contactPhone } = equipList.value[0]
  1223. formData.value.unitContact = contact
  1224. formData.value.unitPhone = contactPhone
  1225. formData.value.recipient = contact
  1226. formData.value.recipientPhone = contactPhone
  1227. if(formData.value.unitName.name === formData.value.useUnitName.name){
  1228. formData.value.useUnitContact = contact
  1229. formData.value.useUnitPhone = contactPhone
  1230. }
  1231. try {
  1232. // 组装提交数据
  1233. const submitData = {
  1234. id: orderDetail.value?.id,
  1235. unitContact: formData.value.unitContact,
  1236. unitPhone: formData.value.unitPhone,
  1237. recipient: formData.value.recipient,
  1238. recipientPhone: formData.value.recipientPhone,
  1239. useUnitContact: formData.value.useUnitContact,
  1240. useUnitPhone: formData.value.useUnitPhone,
  1241. }
  1242. await PipeAppointmentConfirmOrderApi.saveAcceptOrder(submitData)
  1243. } catch (error) {
  1244. ElMessage.error('提交失败')
  1245. }
  1246. }
  1247. } finally {
  1248. equipLoading.value = false
  1249. }
  1250. }
  1251. const handleEquipCodeFn = (id?: number) => {
  1252. EquipContainerFormRef.value.open(id)
  1253. }
  1254. const resetEquipQuery = () => {
  1255. // 重置查询参数
  1256. equipQueryParams.value = {
  1257. pageNo: 1,
  1258. pageSize: 50,
  1259. equipDistrict: undefined,
  1260. equipStreet: undefined,
  1261. equipCode: '',
  1262. productNo: '',
  1263. relateDepartment: '',
  1264. typeList: [],
  1265. nextDate: [],
  1266. useStatus: [],
  1267. }
  1268. // 重置区域-街道映射
  1269. areaStreetMap.value.clear()
  1270. // 重置日期相关字段
  1271. daterange.value = []
  1272. month.value = ''
  1273. datePickerType.value = 'month'
  1274. // 重新查询数据
  1275. handleEquipQuery()
  1276. }
  1277. const handleEquipSelectionChange = (selection) => {
  1278. // 实现设备选择逻辑
  1279. selectedEquips.value = selection
  1280. }
  1281. const handleEquipSortChange = (sort) => {
  1282. // 实现设备排序逻辑
  1283. }
  1284. /** 处理区域变化 */
  1285. const handleAreaChange = (areas: number[]) => {
  1286. // 获取移除的区域(通过比较之前的区域列表和现在的区域列表)
  1287. const prevAreas = Array.from(areaStreetMap.value.keys())
  1288. const removedAreas = prevAreas.filter(area => !areas.includes(area))
  1289. // 移除取消选择的区域下的街道
  1290. if (removedAreas.length > 0) {
  1291. const currentStreets = equipQueryParams.value.equipStreet || []
  1292. removedAreas.forEach(areaId => {
  1293. const streetsToRemove = areaStreetMap.value.get(areaId) || []
  1294. // 从当前选中的街道中移除这些街道
  1295. equipQueryParams.value.equipStreet = currentStreets.filter(
  1296. streetId => !streetsToRemove.includes(streetId)
  1297. )
  1298. // 从映射中移除该区域
  1299. areaStreetMap.value.delete(areaId)
  1300. })
  1301. }
  1302. }
  1303. /** 处理区域清空 */
  1304. const handleAreaClear = () => {
  1305. // 清空所有选中的街道
  1306. equipQueryParams.value.equipStreet = []
  1307. // 清空区域-街道映射
  1308. areaStreetMap.value.clear()
  1309. }
  1310. /** 处理街道变化 */
  1311. const handleStreetChange = (streets: number[], areaId: number) => {
  1312. // 更新区域-街道映射
  1313. if (equipQueryParams.value.equipDistrict?.includes(areaId)) {
  1314. areaStreetMap.value.set(areaId, streets)
  1315. }
  1316. }
  1317. /** 处理街道清空 */
  1318. const handleStreetClear = () => {
  1319. // 清空所有区域下的街道选择
  1320. areaStreetMap.value.clear()
  1321. }
  1322. /** 处理日期范围变化 */
  1323. const handleDateChange = (val: [string, string] | null) => {
  1324. daterange.value = val || []
  1325. equipQueryParams.value.nextDate = val
  1326. ? [
  1327. dayjs(val[0]).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
  1328. dayjs(val[1]).endOf('day').format('YYYY-MM-DD HH:mm:ss')
  1329. ]
  1330. : []
  1331. }
  1332. /** 处理月份变化 */
  1333. const handleMonthChange = (val: string | null) => {
  1334. if (!val) {
  1335. equipQueryParams.value.nextDate = []
  1336. return
  1337. }
  1338. const date = dayjs(val)
  1339. equipQueryParams.value.nextDate = [
  1340. date.startOf('month').format('YYYY-MM-DD HH:mm:ss'),
  1341. date.endOf('month').format('YYYY-MM-DD HH:mm:ss')
  1342. ]
  1343. month.value = val
  1344. }
  1345. // 监听日期类型变化
  1346. watch(datePickerType, (newVal) => {
  1347. // 清空日期相关字段
  1348. daterange.value = []
  1349. month.value = ''
  1350. equipQueryParams.value.nextDate = []
  1351. })
  1352. /** 拒绝约检 */
  1353. const handleRejectEquip = async () => {
  1354. if (selectedRows.value.length === 0 || selectedDetailRows.value.length === 0) {
  1355. ElMessage.warning('请选择要拒绝约检的设备')
  1356. return
  1357. }
  1358. rejectForm.value.reasonDict = ''
  1359. rejectForm.value.reason = ''
  1360. rejectDialogVisible.value = true
  1361. }
  1362. /** 处理批量修改 */
  1363. const handleBatchEditFn = () => {
  1364. if (selectedRows.value.length === 0) {
  1365. ElMessage.warning('请至少选择一条设备数据')
  1366. return
  1367. }
  1368. batchEditFormRef.value.open(selectedRows.value, '1')
  1369. }
  1370. /** 确认拒绝约检 */
  1371. const handleRejectConfirm = async (type) => {
  1372. if (!rejectForm.value.reasonDict) {
  1373. ElMessage.warning('请选择拒绝原因')
  1374. return
  1375. }
  1376. if (rejectForm.value.reasonDict == '5' && !rejectForm.value.reason) {
  1377. ElMessage.warning('请输入说明')
  1378. return
  1379. }
  1380. const equipIds = selectedRows.value.map(e => e.id)
  1381. const submitData = {
  1382. equipIds,
  1383. orderId: orderDetail.value?.id,
  1384. detailEquipRows: selectedDetailRows.value,
  1385. type,
  1386. reasonDict: rejectForm.value.reasonDict,
  1387. reason: rejectForm.value.reason,
  1388. orderNo: orderDetail.value?.appointmentNo,
  1389. checkType: orderDetail.value?.checkType
  1390. }
  1391. let alertText = ''
  1392. switch(type) {
  1393. case 100:
  1394. alertText = '上报市局'
  1395. break
  1396. case 200:
  1397. alertText = '拒绝约检'
  1398. break
  1399. case 300:
  1400. alertText = '无需上报'
  1401. break
  1402. default:
  1403. alertText = ''
  1404. }
  1405. rejectLoading.value = true
  1406. try {
  1407. // 调用api
  1408. const res = await PipeAppointmentConfirmOrderApi.refuseAppointmentConfirmOrder(submitData)
  1409. ElMessage.success(alertText + '操作成功')
  1410. rejectLoading.value = false
  1411. // 提交成功后重置表单和关闭对话框
  1412. rejectForm.value.reason = ''
  1413. rejectForm.value.reasonDict = ''
  1414. rejectDialogVisible.value = false
  1415. handleEquipQuery() // 刷新设备列表
  1416. } catch (e) {
  1417. rejectLoading.value = false
  1418. ElMessage.error(alertText + '操作失败')
  1419. }
  1420. }
  1421. /** 添加设备 */
  1422. const handleAddEquip = () => {
  1423. addEquipVisible.value = true
  1424. }
  1425. /** 添加设备成功 */
  1426. const handleAddEquipSuccess = (selectedEquips) => {
  1427. ElMessage.success('添加设备成功')
  1428. // 更新设备列表
  1429. handleEquipQuery()
  1430. }
  1431. /** 生成受理单 */
  1432. const handleGenerateAcceptance = () => {
  1433. console.log('开始验证表单', formRef.value)
  1434. // 表单验证
  1435. if (!formRef.value) {
  1436. ElMessage.warning('表单引用获取失败')
  1437. return
  1438. }
  1439. // 手动触发表单验证
  1440. formRef.value.validate((valid, fields) => {
  1441. if (!valid) {
  1442. ElMessage.warning('请完善必填信息')
  1443. console.log('验证失败的字段:', fields)
  1444. return
  1445. }
  1446. // 检查排期时间和检验员
  1447. if (!formData.value.appointmentDate) {
  1448. ElMessage.warning('请选择约检时间')
  1449. return
  1450. }
  1451. if(!equipList.value.length){
  1452. ElMessage.warning('请添加设备')
  1453. return
  1454. }
  1455. // 二次确认
  1456. ElMessageBox.confirm(
  1457. '确认要生成受理单吗?提交后将无法修改。',
  1458. '操作确认',
  1459. {
  1460. confirmButtonText: '确认',
  1461. cancelButtonText: '取消',
  1462. type: 'warning'
  1463. }
  1464. ).then(async () => {
  1465. if(exceptionInfo.value.id){
  1466. await handleSaveException()
  1467. }
  1468. // 确认后提交
  1469. submitAcceptance()
  1470. }).catch(() => {
  1471. // 用户取消操作
  1472. console.log('用户取消操作')
  1473. })
  1474. })
  1475. }
  1476. /** 提交受理单 */
  1477. const submitAcceptance = async () => {
  1478. try {
  1479. let { unitCode='' } = formData.value;
  1480. loading.value = true
  1481. // 组装提交数据
  1482. const submitData: Record<string, any> = {
  1483. id: orderDetail.value?.id,
  1484. ...formData.value,
  1485. appointmentDate: formData.value.appointmentDate,
  1486. unitName: formData.value?.unitName?.name,
  1487. unitAddress: formData.value?.unitAddress,
  1488. unitCode,
  1489. unitContact: formData.value.unitContact,
  1490. unitContactDept: formData.value.unitContactDept,
  1491. unitPhone: formData.value.unitPhone,
  1492. equipDistrict: orderDetail.value?.equipDistrict,
  1493. equipStreet: orderDetail.value?.equipStreet,
  1494. zipCode: formData.value.zipCode,
  1495. checkType: orderDetail.value?.checkType,
  1496. equipType: orderDetail.value?.equipType,
  1497. equipMainType: orderDetail.value?.equipMainType,
  1498. deptId: orderDetail.value?.deptId,
  1499. email: formData.value.email,
  1500. mobile: formData.value.mobile,
  1501. remark: formData.value.remark,
  1502. isAttach: formData.value.isAttach,
  1503. sendType: formData.value.sendType,
  1504. vehicle: formData.value.vehicle,
  1505. feeType: formData.value.feeType,
  1506. acceptType: formData.value.acceptType,
  1507. actualAmount: formData.value.actualAmount,
  1508. serviceAmount: formData.value.serviceAmount,
  1509. shouldAmount: formData.value.shouldAmount,
  1510. reduceFee: formData.value.reduceFee,
  1511. useUnitName: formData.value?.useUnitName?.name,
  1512. teamList: selectedCheckers.value.reduce((acc: any[], item: any) => {
  1513. const existingGroup = acc.find(
  1514. group => group.groupTeamId === item.groupTeamId
  1515. );
  1516. if (existingGroup) {
  1517. // 避免重复添加相同的 userId,且不添加 leaderId
  1518. if (!item.isLeader && !existingGroup.userIds.includes(item.memberId)) {
  1519. existingGroup.userIds.push(item.memberId);
  1520. }
  1521. } else {
  1522. acc.push({
  1523. groupTeamId: item.groupTeamId,
  1524. leaderId: item.isLeader ? item.memberId : '',
  1525. userIds: item.isLeader ? [] : [item.memberId]
  1526. });
  1527. }
  1528. return acc;
  1529. }, []),
  1530. equipIds: equipList.value.map(e => e.id),
  1531. }
  1532. if(exceptionInfo.value.id){
  1533. submitData.boilerOrderExceptionSaveReqVO = cloneDeep({
  1534. ...exceptionInfo.value,
  1535. adjustEffectiveDate: unref(exceptionInfo).adjustEffectiveDate && unref(exceptionInfo).adjustEffectiveDate.split('-').map(item=>Number(item)),
  1536. })
  1537. }
  1538. // console.log('submitData:', submitData);
  1539. // 调用API提交受理单
  1540. await PipeAppointmentConfirmOrderApi.submitAppointmentConfirmOrder(submitData);
  1541. emitter.emit('refresh-list')
  1542. ElMessage.success('受理单生成成功')
  1543. handleClose()
  1544. } catch (error) {
  1545. console.error('生成受理单失败:', error)
  1546. ElMessage.error('生成受理单失败')
  1547. } finally {
  1548. loading.value = false
  1549. }
  1550. }
  1551. /** 保存约检单 */
  1552. const handleSave = () => {
  1553. // console.log('开始验证表单', formRef.value)
  1554. // 表单验证
  1555. if (!formRef.value) return ElMessage.warning('表单引用获取失败')
  1556. // 手动触发表单验证
  1557. formRef.value.validate((valid, fields) => {
  1558. if (!valid) return ElMessage.warning('请完善必填信息')
  1559. // 检查排期时间
  1560. if (!formData.value.appointmentDate) return ElMessage.warning('请选择约检时间')
  1561. // 二次确认
  1562. ElMessageBox.confirm(
  1563. '确认保存吗?',
  1564. '操作确认',
  1565. {
  1566. confirmButtonText: '确认',
  1567. cancelButtonText: '取消',
  1568. type: 'warning'
  1569. }
  1570. ).then(async () => {
  1571. if(exceptionInfo.value.id){
  1572. await handleSaveException()
  1573. }
  1574. saveAcceptance()
  1575. })
  1576. })
  1577. }
  1578. const saveAcceptance = async () => {
  1579. try {
  1580. loading.value = true
  1581. // 组装提交数据
  1582. let submitData = {
  1583. id: orderDetail.value?.id,
  1584. ...formData.value,
  1585. equipType: orderDetail.value?.equipType,
  1586. equipNum: orderDetail.value?.equipNum,
  1587. unitName: orderDetail.value?.unitName?.name,
  1588. warningDay: orderDetail.value?.warningDay,
  1589. useUnitName: formData.value?.useUnitName?.name,
  1590. teamList: formData.value.teamList.reduce((acc: any[], item: any) => {
  1591. const existingGroup = acc.find(
  1592. group => group.groupTeamId === item.groupTeamId
  1593. );
  1594. if (existingGroup) {
  1595. // 避免重复添加相同的 userId,且不添加 leaderId
  1596. if (!item.isLeader && !existingGroup.userIds.includes(item.memberId)) {
  1597. existingGroup.userIds.push(item.memberId);
  1598. }
  1599. } else {
  1600. acc.push({
  1601. groupTeamId: item.groupTeamId,
  1602. leaderId: item.isLeader ? item.memberId : '',
  1603. userIds: item.isLeader ? [] : [item.memberId]
  1604. });
  1605. }
  1606. return acc;
  1607. }, [])
  1608. }
  1609. // 调用API保存
  1610. const res2 = await PipeAppointmentConfirmOrderApi.saveAcceptOrder(submitData)
  1611. if(res2) {
  1612. ElMessage.success('保存成功')
  1613. emitter.emit('refresh-list')
  1614. handleClose()
  1615. }
  1616. } catch (error) {
  1617. ElMessage.error('保存失败')
  1618. } finally {
  1619. loading.value = false
  1620. }
  1621. }
  1622. // 手动输入单位
  1623. const useInputUseUnitName = ref(false)
  1624. const useInputUnitName = ref(false)
  1625. // 清空与单位相关字段的函数
  1626. const clearUnitFields = () => {
  1627. formData.value.unitName.name = ''
  1628. formData.value.socialCreditCode = ''
  1629. formData.value.unitAddress = ''
  1630. formData.value.unitContact = ''
  1631. formData.value.unitPhone = ''
  1632. formData.value.recipient = ''
  1633. formData.value.recipientPhone = ''
  1634. }
  1635. // 监听 useInputUnitName 变化
  1636. watch(useInputUnitName, (newValue) => {
  1637. if (newValue) {
  1638. clearUnitFields()
  1639. }
  1640. })
  1641. // 清空与使用单位相关的字段
  1642. const clearUseUnitFields = () => {
  1643. formData.value.useUnitName.name = ''
  1644. formData.value.useUnitSocialCreditCode = ''
  1645. formData.value.useUnitAddress = ''
  1646. formData.value.useUnitZipcode = ''
  1647. formData.value.useUnitContact = ''
  1648. formData.value.useUnitPhone = ''
  1649. }
  1650. // 监听 useInputUseUnitName 变化
  1651. watch(useInputUseUnitName, (newValue) => {
  1652. if (newValue) {
  1653. clearUseUnitFields()
  1654. }
  1655. })
  1656. // 申请单位名称 change
  1657. const handleChangeUnit = async (unitInfo:any) => {
  1658. formData.value.socialCreditCode = unitInfo.socialCreditCode;
  1659. formData.value.unitAddress = unitInfo.addr
  1660. formData.value.zipCode = unitInfo.post
  1661. if(orderDetail.value?.checkType != 200){
  1662. formData.value.feeNature = unitInfo.isExempt === '1' ? `${PressureFeeNature.SMZFC}` : `${PressureFeeNature.BSMZFC}`
  1663. }
  1664. // 获取单位默认联系人
  1665. const {list: unitDefaultContact} = await getUnitContacts({
  1666. pageNo: 1,
  1667. pageSize: 10,
  1668. unitId: unitInfo.id,
  1669. isDefault: '1'
  1670. })
  1671. if(unitDefaultContact && unitDefaultContact?.length > 0) {
  1672. formData.value.unitContact = unitDefaultContact[0].contactName
  1673. formData.value.unitPhone = unitDefaultContact[0].contactPhone
  1674. formData.value.recipient = unitDefaultContact[0].contactName
  1675. formData.value.recipientPhone = unitDefaultContact[0].contactPhone
  1676. }
  1677. }
  1678. // 使用单位名称 change
  1679. const handleChangeUseUnit = async (unitInfo:any) => {
  1680. console.log('使用单位名称 change:', unitInfo)
  1681. formData.value.useUnitSocialCreditCode = unitInfo.socialCreditCode;
  1682. formData.value.useUnitAddress = unitInfo.addr
  1683. formData.value.useUnitZipcode = unitInfo.post
  1684. // 获取单位默认联系人
  1685. const {list: unitDefaultContact} = await getUnitContacts({
  1686. pageNo: 1,
  1687. pageSize: 10,
  1688. unitId: unitInfo.id,
  1689. isDefault: '1'
  1690. })
  1691. if(unitDefaultContact && unitDefaultContact?.length > 0) {
  1692. formData.value.useUnitContact = unitDefaultContact[0].contactName
  1693. formData.value.useUnitPhone = unitDefaultContact[0].contactPhone
  1694. }
  1695. await handleEquipQuery()
  1696. }
  1697. // 使用单位名称input框失焦 & 回车按钮进行查询
  1698. const handleChangeUseUnitInput = () => {
  1699. handleEquipQuery()
  1700. }
  1701. /** 关闭 */
  1702. const handleClose = () => {
  1703. tagsViewStore.closeSelectedTag(route)
  1704. router.push({
  1705. name: 'OrderConfirm',
  1706. })
  1707. }
  1708. // 监听表单区域相关字段变化,更新areaDisplay
  1709. watch([() => formData.value.equipDistrictName, () => formData.value.equipStreetName], () => {
  1710. formData.value.areaDisplay = areaDisplay.value
  1711. })
  1712. const exceptionInfo = ref<Record<string, any>>({})
  1713. const getExceptionItem = (field: string, fieldAttr: string)=>{
  1714. const fileUrl = exceptionInfo.value[fieldAttr] && buildFileUrl(exceptionInfo.value[fieldAttr])
  1715. const fileName = exceptionInfo.value[field]
  1716. if(fileUrl) {
  1717. return (
  1718. <>
  1719. <div class="flex items-center w-full p-l-10px p-r-10px hover:bg-gray-100 rounded-6px">
  1720. {/* <el-tooltip content={fileName}> */}
  1721. <span class="flex-1 text-ellipsis overflow-hidden whitespace-nowrap">{fileName}</span>
  1722. {/* </el-tooltip> */}
  1723. <div class="ml-10px">
  1724. <el-link href={fileUrl} underline={false} download target="_blank" type="primary">下载</el-link>
  1725. </div>
  1726. <div class="ml-10px">
  1727. <el-button link type="danger" onClick={()=>handleRemove(field, fieldAttr)}>删除</el-button>
  1728. </div>
  1729. </div>
  1730. </>
  1731. )
  1732. } else {
  1733. return (
  1734. <el-button type="primary" onClick={()=>handleOpenFileUploadModal(field, fieldAttr)}>
  1735. <Icon icon="ep:upload-filled" />
  1736. <span>附件上传</span>
  1737. </el-button>
  1738. )
  1739. }
  1740. }
  1741. const addExceptionInfo = () => {
  1742. const orderId = formData.value.id || route.query?.id;
  1743. if(!orderId) {
  1744. // ElMessage.error('请先保存订单')
  1745. return
  1746. }
  1747. PipeAppointmentConfirmOrderApi.createExceptionInfo({
  1748. orderId: formData.value.id || route.query?.id,
  1749. orderType: 100,
  1750. }).then((res) => {
  1751. if(res) {
  1752. ElMessage.success('生成成功')
  1753. handleGetExceptionInfo(res)
  1754. }
  1755. }).catch((error)=>{
  1756. ElMessage.error(error)
  1757. })
  1758. }
  1759. const deleteExceptionInfo = () => {
  1760. ElMessageBox.confirm('确定删除异常单吗?', '提示', {
  1761. confirmButtonText: '确定',
  1762. cancelButtonText: '取消',
  1763. type: 'warning',
  1764. }).then(() => {
  1765. PipeAppointmentConfirmOrderApi.deleteExceptionInfo(exceptionInfo.value.id).then(res=>{
  1766. if(res) {
  1767. ElMessage.success('删除成功')
  1768. exceptionInfo.value = {}
  1769. }
  1770. })
  1771. }).catch(() => {
  1772. // 取消删除
  1773. });
  1774. }
  1775. // 更新异常信息
  1776. const handleSaveException=()=>{
  1777. return new Promise((resolve, reject)=>{
  1778. const { adjustEffectiveDate } = unref(exceptionInfo)
  1779. const params = {
  1780. ...exceptionInfo.value,
  1781. adjustEffectiveDate: adjustEffectiveDate && adjustEffectiveDate.split('-').map(item=>Number(item)),
  1782. }
  1783. PipeAppointmentConfirmOrderApi.updateExceptionInfo(params).then(res=>{
  1784. resolve(res)
  1785. }).catch((error)=>{
  1786. reject(error)
  1787. })
  1788. })
  1789. }
  1790. const getExceptionInfoList = () => {
  1791. PipeAppointmentConfirmOrderApi.getExceptionInfoList({
  1792. orderId: formData.value.id || route.query?.id,
  1793. }).then(res=>{
  1794. console.log('getExceptionInfoList:',res)
  1795. })
  1796. }
  1797. const handleGetExceptionInfo = (id: string) => {
  1798. PipeAppointmentConfirmOrderApi.getExceptionInfo(id).then((res) => {
  1799. if(res) exceptionInfo.value = {
  1800. ...res,
  1801. adjustEffectiveDate: res.adjustEffectiveDate?.join('-')
  1802. }
  1803. }).catch((error)=>{
  1804. console.log(error, '-----error--->')
  1805. ElMessage.error(error)
  1806. })
  1807. }
  1808. const fileUploadModalRef = ref<InstanceType<typeof FileUploadModal>>();
  1809. const currentUploadMap = reactive({
  1810. field: '',
  1811. fieldAttr: '',
  1812. })
  1813. const handleOpenFileUploadModal = (type: string, attach: string) => {
  1814. currentUploadMap.field = type
  1815. currentUploadMap.fieldAttr = attach
  1816. fileUploadModalRef.value?.open()
  1817. }
  1818. const handleFileUploadSuccess = (fileInfo:{fileName: string, fileUrl: string}) => {
  1819. exceptionInfo.value[currentUploadMap.field] = fileInfo.fileName
  1820. exceptionInfo.value[currentUploadMap.fieldAttr] = fileInfo.fileUrl
  1821. console.log('handleFileUploadSuccess:', exceptionInfo.value)
  1822. }
  1823. const handleRemove = (type: string, attach: string) => {
  1824. exceptionInfo.value[type] = ''
  1825. exceptionInfo.value[attach] = ''
  1826. }
  1827. // 页面加载时获取详情
  1828. onMounted(async () => {
  1829. await getDetail()
  1830. if(route.query?.id) {
  1831. await handleEquipQuery()
  1832. // await handleGetExceptionInfo('eb9a57fe2ed98a45e1a892aef4cc13f0')
  1833. }
  1834. })
  1835. const formattedCheckers = computed(() => {
  1836. const teamList = formData.value.teamList
  1837. console.log("teamList",teamList)
  1838. if (!teamList || !Array.isArray(teamList) || teamList.length === 0) return []
  1839. const result: { id: string; label: string }[] = []
  1840. teamList.forEach((team: any, teamIndex: number) => {
  1841. if (team.isLeader) {
  1842. result.push({
  1843. id: `leader-${team.member.id || teamIndex}`,
  1844. label: `组长: ${team.member.nickname || ''}`
  1845. })
  1846. }else{
  1847. result.push({
  1848. id: `member-${team.member.id || teamIndex}`,
  1849. label: `${team.member.nickname || ''}`
  1850. })
  1851. }
  1852. })
  1853. return result
  1854. })
  1855. </script>
  1856. <style lang="scss" scoped>
  1857. .form-group {
  1858. &:not(:last-child) {
  1859. margin-bottom: 16px;
  1860. }
  1861. &-title {
  1862. color: #606266;
  1863. font-size: 13px;
  1864. font-weight: 500;
  1865. margin-bottom: 0;
  1866. }
  1867. }
  1868. .p-3 {
  1869. padding-top: 0;
  1870. }
  1871. .check-info-label-title {
  1872. width: 100%;
  1873. line-height: 32px;
  1874. display: flex;
  1875. align-items: center;
  1876. margin-bottom: 12px;
  1877. &::before {
  1878. content: '';
  1879. display: inline-block;
  1880. width: 4px;
  1881. height: 16px;
  1882. background: var(--el-color-primary);
  1883. margin-right: 8px;
  1884. }
  1885. }
  1886. .feeDate_box {
  1887. color: #606266;
  1888. :deep(.el-input-number) {
  1889. width: 120px !important;
  1890. }
  1891. }
  1892. .unit-form-item {
  1893. :deep(.el-form-item__content) {
  1894. display: flex;
  1895. align-items: center;
  1896. flex-wrap: nowrap;
  1897. .el-button {
  1898. margin-left: 8px;
  1899. }
  1900. }
  1901. }
  1902. .unit-name {
  1903. display: flex;
  1904. align-items: center;
  1905. padding: 0 0 8px;
  1906. .label {
  1907. width: 90px;
  1908. color: #606266;
  1909. font-size: 13px;
  1910. text-align: right;
  1911. padding-right: 12px;
  1912. }
  1913. .value {
  1914. flex: 1;
  1915. font-size: 14px;
  1916. color: #303133;
  1917. }
  1918. }
  1919. :deep(.el-form) {
  1920. .el-form-item {
  1921. margin-bottom: 12px;
  1922. align-items: center;
  1923. &__label {
  1924. height: auto;
  1925. font-size: 13px;
  1926. line-height: 20px;
  1927. color: #606266;
  1928. padding-right: 8px;
  1929. }
  1930. .el-date-editor.el-input, .el-input-number {
  1931. width: 100%;
  1932. .el-input__inner {
  1933. text-align: left;
  1934. }
  1935. }
  1936. &.actualAmountFormItem {
  1937. .el-form-item__content {
  1938. display: flex;
  1939. .el-input-number {
  1940. width: 300px;
  1941. }
  1942. }
  1943. }
  1944. }
  1945. }
  1946. .selected-checkers {
  1947. display: inline-flex;
  1948. flex-wrap: wrap;
  1949. gap: 8px;
  1950. min-height: 32px;
  1951. padding: 4px 8px;
  1952. .el-tag {
  1953. margin: 0;
  1954. height: 24px;
  1955. line-height: 22px;
  1956. &:hover {
  1957. cursor: default;
  1958. }
  1959. }
  1960. }
  1961. .reject-dialog {
  1962. :deep(.el-dialog__header) {
  1963. margin: 0;
  1964. padding: 16px 20px;
  1965. border-bottom: 1px solid #dcdfe6;
  1966. }
  1967. :deep(.el-dialog__body) {
  1968. padding: 20px;
  1969. }
  1970. .reject-content {
  1971. .equip-list {
  1972. margin-bottom: 20px;
  1973. color: #606266;
  1974. line-height: 1.6;
  1975. }
  1976. .check-type {
  1977. color: #606266;
  1978. }
  1979. .reject-reason {
  1980. .reason-label {
  1981. color: #606266;
  1982. }
  1983. :deep(.el-textarea__inner) {
  1984. background-color: #f5f7fa;
  1985. border-color: #e4e7ed;
  1986. &:focus {
  1987. background-color: #fff;
  1988. }
  1989. }
  1990. }
  1991. }
  1992. .reject-footer {
  1993. margin-top: 20px;
  1994. display: flex;
  1995. align-items: center;
  1996. justify-content: space-between;
  1997. .time-tag {
  1998. :deep(.el-tag) {
  1999. height: 32px;
  2000. line-height: 30px;
  2001. font-size: 16px;
  2002. padding: 0 12px;
  2003. }
  2004. }
  2005. .action-buttons {
  2006. .el-button + .el-button {
  2007. margin-left: 12px;
  2008. }
  2009. }
  2010. }
  2011. }
  2012. // 内层表格复选框样式
  2013. ::v-deep .inner-table .el-checkbox__input.is-checked .el-checkbox__inner,
  2014. ::v-deep .inner-table .el-checkbox__input.is-indeterminate .el-checkbox__inner {
  2015. background-color: #67c23a; // 绿色
  2016. border-color: #67c23a;
  2017. }
  2018. ::v-deep .inner-table .el-checkbox__input.is-checked .el-checkbox__inner::after {
  2019. border-color: #fff;
  2020. }
  2021. // 内层表格选中行背景色
  2022. ::v-deep .inner-table .el-table__body tr.current-row > td {
  2023. background-color: #f0f9eb;
  2024. }
  2025. :deep(.selected-row){
  2026. background-color: #ecf5ff !important;
  2027. }
  2028. // 子表选中行特殊背景色(浅绿色)
  2029. :deep(.inner-table .selected-row) {
  2030. background-color: #e8f5e9 !important;
  2031. }
  2032. .schedule-link {
  2033. color: var(--el-color-primary);
  2034. &:hover {
  2035. color: var(--el-color-primary-light-3);
  2036. }
  2037. }
  2038. </style>