IotProjectTaskForm.vue 62 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934
  1. <template>
  2. <ContentWrap v-loading="formLoading">
  3. <el-form
  4. ref="formRef"
  5. :model="formData"
  6. :rules="formRules"
  7. label-width="100px"
  8. v-loading="formLoading"
  9. >
  10. <el-row>
  11. <el-col :span="12">
  12. <el-form-item label="合同名称" prop="contractName">
  13. <el-select v-model="formData.contractId" placeholder="请选择" @change="getProjectInfo" disabled>
  14. <el-option
  15. v-for="item in projectList"
  16. :key="item.id"
  17. :label="item.contractName"
  18. :value="item.id"
  19. clearable
  20. />
  21. </el-select>
  22. </el-form-item>
  23. </el-col>
  24. <el-col :span="12">
  25. <el-form-item label="合同编号" prop="contractCode">
  26. <el-input v-model="formData.contractCode" placeholder="请输入合同编号" disabled/>
  27. </el-form-item>
  28. </el-col>
  29. </el-row>
  30. <el-row>
  31. <el-col :span="12">
  32. <el-form-item label="客户名称" prop="manufacturerId" >
  33. <el-select
  34. clearable
  35. @clear="zzClear"
  36. v-model="formData.manufacturerId"
  37. :placeholder="t('deviceForm.mfgHolder')"
  38. disabled
  39. >
  40. <el-option :label="formData.manufactureName" :value="formData.manufacturerId" />
  41. </el-select>
  42. </el-form-item>
  43. </el-col>
  44. <el-col :span="12">
  45. <el-form-item :label="t('project.payment')" prop="payment">
  46. <el-select v-model="formData.payment" placeholder="请选择" disabled>
  47. <el-option
  48. v-for="dict in getStrDictOptions(DICT_TYPE.PMS_PROJECT_SETTLEMENT)"
  49. :key="dict.id"
  50. :label="dict.label"
  51. :value="dict.value"
  52. />
  53. </el-select>
  54. </el-form-item>
  55. </el-col>
  56. </el-row>
  57. <!--
  58. <el-row>
  59. <el-col :span="12">
  60. <el-form-item label="总数" prop="workloadTotal">
  61. <el-input v-model="formData.workloadTotal" placeholder="请输入工作量总数" disabled/>
  62. </el-form-item>
  63. </el-col>
  64. <el-col :span="12">
  65. <el-form-item label="已完成" prop="workloadFinish">
  66. <el-input v-model="formData.workloadFinish" placeholder="已完成工作量" disabled/>
  67. </el-form-item>
  68. </el-col>
  69. </el-row> -->
  70. <el-row>
  71. <el-col :span="12">
  72. <el-form-item label="开始时间" prop="startTime">
  73. <el-date-picker
  74. style="width: 150%"
  75. v-model="formData.startTime"
  76. type="date"
  77. value-format="x"
  78. placeholder="选择开始时间"
  79. disabled
  80. />
  81. </el-form-item>
  82. </el-col>
  83. <el-col :span="12">
  84. <el-form-item label="完成时间" prop="endTime">
  85. <el-date-picker
  86. style="width: 150%"
  87. v-model="formData.endTime"
  88. type="date"
  89. value-format="x"
  90. placeholder="选择完成时间"
  91. disabled
  92. />
  93. </el-form-item>
  94. </el-col>
  95. </el-row>
  96. <el-form-item label="备注" prop="remark">
  97. <el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" disabled/>
  98. </el-form-item>
  99. </el-form>
  100. </ContentWrap>
  101. <ContentWrap v-if="false">
  102. <div class="content">
  103. <div class="toolbar">
  104. <div class="actions">
  105. <!-- 操作按钮已移动到表单区域 -->
  106. </div>
  107. </div>
  108. <div class="table-container">
  109. <el-table
  110. :data="tableData"
  111. empty-text="暂无数据"
  112. highlight-current-row
  113. @current-change="handleRowClick"
  114. >
  115. <el-table-column prop="wellName" label="井号" />
  116. <el-table-column prop="wellType" label="井型">
  117. <template #default="{ row }">
  118. {{ getWellTypeLabel(row.wellType) }}
  119. </template>
  120. </el-table-column>
  121. <el-table-column prop="wellCategory" label="井别">
  122. <template #default="{ row }">
  123. {{ getWellCategoryLabel(row.wellCategory) }}
  124. </template>
  125. </el-table-column>
  126. <el-table-column prop="location" label="施工地点" />
  127. <el-table-column :label="t('project.technology')">
  128. <template #default="{ row }">
  129. {{ getTechniqueLabel(row.technique) }}
  130. </template>
  131. </el-table-column>
  132. <el-table-column prop="workloadDesign" label="设计工作量" />
  133. <el-table-column :label="t('project.unit')">
  134. <template #default="{ row }">
  135. {{ getWorkloadUnitLabel(row.workloadUnit) }}
  136. </template>
  137. </el-table-column>
  138. <el-table-column prop="deptIds" label="施工队伍" >
  139. <template #default="{ row }">
  140. <el-tooltip
  141. effect="dark"
  142. :content="getAllDeptNames(row.deptIds)"
  143. placement="top"
  144. :disabled="!row.deptIds || row.deptIds.length <= 1"
  145. >
  146. <span class="dept-names">
  147. {{ getDeptNames(row.deptIds) }}
  148. </span>
  149. </el-tooltip>
  150. </template>
  151. </el-table-column>
  152. <el-table-column prop="deviceIds" label="施工设备" >
  153. <template #default="{ row }">
  154. <el-tooltip
  155. :content="getAllDeviceNames(row.deviceIds)"
  156. placement="top"
  157. :disabled="row.deviceIds && row.deviceIds.length <= 1"
  158. >
  159. <span class="device-names">
  160. {{ getDeviceNames(row.deviceIds) }}
  161. </span>
  162. </el-tooltip>
  163. </template>
  164. </el-table-column>
  165. <el-table-column prop="responsiblePerson" label="责任人">
  166. <template #default="{ row }">
  167. <el-tooltip
  168. :content="getAllResponsiblePersonNames(row.responsiblePerson)"
  169. placement="top"
  170. :disabled="!row.responsiblePerson || row.responsiblePerson.length <= 1"
  171. >
  172. <span class="responsible-names">
  173. {{ getResponsiblePersonNames(row.responsiblePerson) }}
  174. </span>
  175. </el-tooltip>
  176. </template>
  177. </el-table-column>
  178. <el-table-column prop="remark" label="备注" />
  179. </el-table>
  180. </div>
  181. </div>
  182. </ContentWrap>
  183. <!-- 新增任务详情编辑表单 -->
  184. <ContentWrap>
  185. <h3 style="margin-bottom: 20px;">任务详情</h3>
  186. <el-form
  187. ref="taskFormRef"
  188. :model="currentTask"
  189. :rules="taskFormRules"
  190. label-width="120px"
  191. class="task-edit-form"
  192. >
  193. <el-row>
  194. <el-col :span="8">
  195. <el-form-item label="井号" prop="wellName">
  196. <el-input v-model="currentTask.wellName" placeholder="请输入井号" />
  197. </el-form-item>
  198. </el-col>
  199. <el-col :span="8">
  200. <el-form-item :label="t('project.workArea')" prop="location">
  201. <el-autocomplete
  202. ref="workAreaAutocomplete"
  203. v-model="currentTask.location"
  204. :fetch-suggestions="querySearch"
  205. :trigger-on-focus="true"
  206. placeholder="请输入施工区域"
  207. @focus="handleWorkAreaFocus"
  208. @select="handleSelect"
  209. :disabled="!workAreaOptions.length"
  210. popper-class="work-area-autocomplete"
  211. >
  212. <template #prefix>
  213. <el-icon v-if="loadingWorkAreaOptions" class="is-loading">
  214. <Loading />
  215. </el-icon>
  216. </template>
  217. </el-autocomplete>
  218. <div v-if="!workAreaOptions.length" class="el-form-item__error">
  219. 暂无可用施工区域选项,请手动输入
  220. </div>
  221. </el-form-item>
  222. </el-col>
  223. <el-col :span="8">
  224. <el-form-item :label="t('project.technology')" prop="technique">
  225. <el-select v-model="currentTask.technique"
  226. placeholder="请选择施工工艺"
  227. clearable
  228. :loading="loadingTechnologyOptions"
  229. >
  230. <el-option
  231. v-for="dict in technologyOptions"
  232. :key="dict.value"
  233. :label="dict.label"
  234. :value="dict.value"
  235. />
  236. </el-select>
  237. <div v-if="!technologyOptions.length && !loadingTechnologyOptions" class="el-form-item__error">
  238. 暂无可用施工工艺选项
  239. </div>
  240. </el-form-item>
  241. </el-col>
  242. <!--
  243. <el-col :span="8">
  244. <el-form-item label="井型" prop="wellType">
  245. <el-select v-model="currentTask.wellType" placeholder="请选择井型" clearable>
  246. <el-option
  247. v-for="dict in getStrDictOptions(DICT_TYPE.PMS_PROJECT_WELL_TYPE)"
  248. :key="dict.value"
  249. :label="dict.label"
  250. :value="dict.value"
  251. />
  252. </el-select>
  253. </el-form-item>
  254. </el-col>
  255. <el-col :span="8">
  256. <el-form-item label="井别" prop="wellCategory">
  257. <el-select v-model="currentTask.wellCategory" placeholder="请选择井别" clearable>
  258. <el-option
  259. v-for="dict in getStrDictOptions(DICT_TYPE.PMS_PROJECT_WELL_CATEGORY)"
  260. :key="dict.value"
  261. :label="dict.label"
  262. :value="dict.value"
  263. />
  264. </el-select>
  265. </el-form-item>
  266. </el-col> -->
  267. </el-row>
  268. <el-row>
  269. <el-col :span="8">
  270. <el-form-item label="设计工作量" prop="workloadDesign">
  271. <el-input v-model="currentTask.workloadDesign" placeholder="请输入设计工作量" />
  272. </el-form-item>
  273. </el-col>
  274. <el-col :span="8">
  275. <el-form-item :label="t('project.unit')" prop="workloadUnit">
  276. <el-select v-model="currentTask.workloadUnit" placeholder="请选择工作量单位" clearable>
  277. <el-option
  278. v-for="dict in getStrDictOptions(DICT_TYPE.PMS_PROJECT_WORKLOAD_UNIT)"
  279. :key="dict.value"
  280. :label="dict.label"
  281. :value="dict.value"
  282. />
  283. </el-select>
  284. </el-form-item>
  285. </el-col>
  286. <el-col :span="8">
  287. <el-form-item label="施工队伍" prop="deptIds">
  288. <el-tree-select
  289. multiple
  290. v-model="currentTask.deptIds"
  291. :data="deptList"
  292. :props="defaultProps"
  293. :default-expanded-keys="defaultExpandedKeys"
  294. check-strictly
  295. node-key="id"
  296. filterable
  297. placeholder="请选择施工队伍"
  298. clearable
  299. @visible-change="handleTreeVisibleChange"
  300. collapse-tags
  301. collapse-tags-tooltip
  302. :max-collapse-tags="1"
  303. class="department-tree-select"
  304. />
  305. </el-form-item>
  306. </el-col>
  307. </el-row>
  308. <el-row>
  309. <el-col :span="8">
  310. <el-form-item label="施工设备" prop="deviceIds">
  311. <el-button
  312. @click="openDeviceDialogForForm"
  313. type="primary"
  314. size="small"
  315. >
  316. 选择设备
  317. </el-button>
  318. <!--
  319. <span v-if="currentTask.deviceIds && currentTask.deviceIds.length > 0" style="margin-left: 10px;">
  320. 已选 {{ currentTask.deviceIds.length }} 台设备
  321. </span> -->
  322. <el-tooltip
  323. v-if="currentTask.deviceIds && currentTask.deviceIds.length > 0"
  324. :content="getAllDeviceNamesForForm(currentTask.deviceIds)"
  325. placement="top"
  326. >
  327. <span style="margin-left: 10px;">
  328. {{ formatDevicesForForm(currentTask.deviceIds) }}
  329. </span>
  330. </el-tooltip>
  331. </el-form-item>
  332. </el-col>
  333. <el-col :span="8">
  334. <el-form-item :label="isSpecialDept ? '带班干部' : '责任人'" prop="responsiblePerson">
  335. <el-button
  336. @click="openResponsiblePersonDialogForForm"
  337. type="primary"
  338. size="small"
  339. >
  340. 选择{{ isSpecialDept ? '带班干部' : '责任人' }}
  341. </el-button>
  342. <el-tooltip
  343. v-if="currentTask.responsiblePerson && currentTask.responsiblePerson.length > 0"
  344. :content="getAllResponsiblePersonNamesForForm(currentTask.responsiblePerson)"
  345. placement="top"
  346. >
  347. <span style="margin-left: 10px;">
  348. {{ formatResponsiblePersonsForForm(currentTask.responsiblePerson) }}
  349. </span>
  350. </el-tooltip>
  351. </el-form-item>
  352. </el-col>
  353. <el-col :span="8" v-if="isSpecialDept">
  354. <el-form-item label="填报人" prop="submitter">
  355. <el-button
  356. @click="openSubmitterDialogForForm"
  357. type="primary"
  358. size="small"
  359. >
  360. 选择填报人
  361. </el-button>
  362. <el-tooltip
  363. v-if="currentTask.submitter && currentTask.submitter.length > 0"
  364. :content="getAllSubmitterNamesForForm(currentTask.submitter)"
  365. placement="top"
  366. >
  367. <span style="margin-left: 10px;">
  368. {{ formatSubmittersForForm(currentTask.submitter) }}
  369. </span>
  370. </el-tooltip>
  371. </el-form-item>
  372. </el-col>
  373. </el-row>
  374. <!-- 动态属性部分 -->
  375. <el-row v-if="dynamicAttrs.length > 0">
  376. <el-col v-for="attr in dynamicAttrs" :key="attr.id" :span="attr.dataType === 'textarea' ? 24 : 8">
  377. <el-form-item
  378. :label="attr.name + (attr.unit ? `(${attr.unit})` : '')"
  379. :prop="attr.identifier"
  380. :rules="getDynamicAttrRules(attr)"
  381. >
  382. <!-- 文本类型 -->
  383. <el-input
  384. v-if="attr.dataType === 'text'"
  385. v-model="currentTask[attr.identifier]"
  386. :placeholder="`请输入${attr.name}`"
  387. />
  388. <!-- 文本域类型 -->
  389. <el-input
  390. v-else-if="attr.dataType === 'textarea'"
  391. v-model="currentTask[attr.identifier]"
  392. :placeholder="`请输入${attr.name}`"
  393. type="textarea"
  394. :rows="3"
  395. />
  396. <!-- 数字类型 -->
  397. <el-input
  398. v-else-if="attr.dataType === 'double'"
  399. v-model="currentTask[attr.identifier]"
  400. :placeholder="`请输入${attr.name}`"
  401. type="number"
  402. :min="attr.minValue || undefined"
  403. :max="attr.maxValue || undefined"
  404. />
  405. <!-- 日期类型 -->
  406. <el-date-picker
  407. v-else-if="attr.dataType === 'date'"
  408. v-model="currentTask[attr.identifier]"
  409. type="date"
  410. value-format="x"
  411. :placeholder="`选择${attr.name}`"
  412. style="width: 100%"
  413. />
  414. <!-- 默认文本输入 -->
  415. <el-input
  416. v-else
  417. v-model="currentTask[attr.identifier]"
  418. :placeholder="`请输入${attr.name}`"
  419. />
  420. </el-form-item>
  421. </el-col>
  422. </el-row>
  423. <el-row>
  424. <el-col :span="24">
  425. <el-form-item label="备注" prop="remark">
  426. <el-input v-model="currentTask.remark" placeholder="请输入备注" type="textarea" />
  427. </el-form-item>
  428. </el-col>
  429. </el-row>
  430. <el-form-item>
  431. <!-- <el-button type="primary" @click="saveTask" :disabled="!currentTask.id && !isNewTask">保存任务</el-button> -->
  432. <el-button @click="resetTaskForm" v-if="isNewTask">重置</el-button>
  433. <!--
  434. <el-button type="danger" @click="deleteCurrentTask" v-if="currentTask.id">删除任务</el-button>
  435. <el-button @click="addNewTask" v-if="!isNewTask">新增任务</el-button> -->
  436. </el-form-item>
  437. </el-form>
  438. </ContentWrap>
  439. <ContentWrap>
  440. <el-form style="float: right">
  441. <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
  442. <el-button @click="close">取 消</el-button>
  443. </el-form>
  444. </ContentWrap>
  445. <!-- 设备选择对话框 -->
  446. <el-dialog
  447. v-model="deviceDialogVisible"
  448. title="选择施工设备"
  449. width="1000px"
  450. :before-close="handleDeviceDialogClose"
  451. class="device-select-dialog"
  452. >
  453. <div class="transfer-container">
  454. <el-transfer
  455. v-model="selectedDeviceIds"
  456. :data="filteredDeviceList"
  457. :titles="['可选设备', '已选设备']"
  458. :props="{ key: 'id', label: 'deviceCode' }"
  459. filterable
  460. class="transfer-component"
  461. @change="handleTransferChange"
  462. >
  463. <template #default="{ option }">
  464. <el-tooltip
  465. effect="dark"
  466. placement="top"
  467. :content="`${option.deviceCode || ''} - ${option.deviceName || ''}`"
  468. :disabled="!option.deviceCode && !option.deviceName"
  469. transition="fade-in-linear"
  470. >
  471. <span class="transfer-option-text">
  472. {{ option.deviceCode }} - {{ option.deviceName }}
  473. </span>
  474. </el-tooltip>
  475. </template>
  476. </el-transfer>
  477. </div>
  478. <template #footer>
  479. <span class="dialog-footer">
  480. <el-button @click="handleDeviceDialogClose">取消</el-button>
  481. <el-button type="primary" @click="confirmDeviceSelection">确定</el-button>
  482. </span>
  483. </template>
  484. </el-dialog>
  485. <!-- 责任人选择对话框 -->
  486. <el-dialog
  487. v-model="responsiblePersonDialogVisible"
  488. title="选择责任人"
  489. width="1000px"
  490. :before-close="handleResponsiblePersonDialogClose"
  491. class="responsible-person-select-dialog"
  492. >
  493. <div class="transfer-container">
  494. <el-transfer
  495. v-model="selectedResponsiblePersonIds"
  496. :data="responsiblePersonList"
  497. :titles="['可选人员', '已选人员']"
  498. :props="{ key: 'id', label: 'nickname' }"
  499. filterable
  500. class="transfer-component"
  501. >
  502. <template #default="{ option }">
  503. <el-tooltip
  504. effect="dark"
  505. placement="top"
  506. :content="`${option.nickname} - ${option.deptName || '未分配部门'}`"
  507. >
  508. <span class="transfer-option-text">
  509. {{ option.nickname }} - {{ option.deptName || '未分配部门' }}
  510. </span>
  511. </el-tooltip>
  512. </template>
  513. </el-transfer>
  514. </div>
  515. <template #footer>
  516. <span class="dialog-footer">
  517. <el-button @click="handleResponsiblePersonDialogClose">取消</el-button>
  518. <el-button type="primary" @click="confirmResponsiblePersonSelection">确定</el-button>
  519. </span>
  520. </template>
  521. </el-dialog>
  522. <!-- 填报人选择对话框 -->
  523. <el-dialog
  524. v-model="submitterDialogVisible"
  525. title="选择填报人"
  526. width="1000px"
  527. :before-close="handleSubmitterDialogClose"
  528. class="responsible-person-select-dialog"
  529. >
  530. <div class="transfer-container">
  531. <el-transfer
  532. v-model="selectedSubmitterIds"
  533. :data="submitterList"
  534. :titles="['可选人员', '已选人员']"
  535. :props="{ key: 'id', label: 'nickname' }"
  536. filterable
  537. class="transfer-component"
  538. >
  539. <template #default="{ option }">
  540. <el-tooltip
  541. effect="dark"
  542. placement="top"
  543. :content="`${option.nickname} - ${option.deptName || '未分配部门'}`"
  544. >
  545. <span class="transfer-option-text">
  546. {{ option.nickname }} - {{ option.deptName || '未分配部门' }}
  547. </span>
  548. </el-tooltip>
  549. </template>
  550. </el-transfer>
  551. </div>
  552. <template #footer>
  553. <span class="dialog-footer">
  554. <el-button @click="handleSubmitterDialogClose">取消</el-button>
  555. <el-button type="primary" @click="confirmSubmitterSelection">确定</el-button>
  556. </span>
  557. </template>
  558. </el-dialog>
  559. </template>
  560. <script setup lang="ts">
  561. // 导入部分保持不变,与原始代码相同
  562. import { IotProjectInfoApi, IotProjectInfoVO } from '@/api/pms/iotprojectinfo'
  563. import {defaultProps,handleTree} from "@/utils/tree";
  564. import * as DeptApi from "@/api/system/dept";
  565. import {ref, reactive, computed, onMounted, watch} from "vue";
  566. import {useUserStore} from "@/store/modules/user";
  567. import {IotProjectTaskApi, IotProjectTaskVO} from "@/api/pms/iotprojecttask";
  568. import {ElMessageBox, ElMessage} from "element-plus";
  569. import {useTagsViewStore} from "@/store/modules/tagsView";
  570. import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
  571. import * as UserApi from "@/api/system/user";
  572. import {IotProjectTaskAttrsApi, IotProjectTaskAttrsVO} from "@/api/pms/iotprojecttaskattrs";
  573. import {DICT_TYPE, getIntDictOptions, getStrDictOptions, getDictLabel} from "@/utils/dict";
  574. const { query, params, name } = useRoute() // 查询参数
  575. const id = params.id
  576. // 修改projectId获取逻辑:优先使用params.projectId,其次使用query.projectId
  577. const projectId = ref<string>('')
  578. // 获取projectId的逻辑
  579. if (params.projectId) {
  580. projectId.value = Array.isArray(params.projectId) ? params.projectId[0] : params.projectId
  581. } else if (query.projectId) {
  582. projectId.value = Array.isArray(query.projectId) ? query.projectId[0] : query.projectId as string
  583. }
  584. // 施工队伍 选择树 响应式变量
  585. const defaultExpandedKeys = ref<number[]>([]); // 默认展开的部门节点keys
  586. const treeSelectRef = ref(); // 树选择组件的引用
  587. const { delView } = useTagsViewStore() // 视图操作
  588. const { currentRoute, push } = useRouter()
  589. const { t } = useI18n() // 国际化
  590. const message = useMessage() // 消息弹窗
  591. const deptList = ref<Tree[]>([]) // 树形结构
  592. const deviceList = ref<IotDeviceVO[]>([]) // 设备列表
  593. // 设备映射表(ID->设备对象)
  594. const deviceMap = ref<Record<number, IotDeviceVO>>({});
  595. const dialogVisible = ref(true) // 弹窗的是否展示
  596. const dialogTitle = ref('') // 弹窗的标题
  597. const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  598. const taskList = ref<IotProjectTaskVO[]>([]) // 列表的数据
  599. const projectList = ref<IotProjectInfoVO[]>([])
  600. const formType = ref('') // 表单的类型:create - 新增;update - 修改
  601. const loading = ref(true) // 列表的加载中
  602. // 设备选择相关变量
  603. const deviceDialogVisible = ref(false);
  604. const filteredDeviceList = ref<IotDeviceVO[]>([]); // 过滤后的设备列表
  605. const selectedDeviceIds = ref<number[]>([]); // 选中的设备ID
  606. // 责任人相关变量
  607. const responsiblePersonDialogVisible = ref(false);
  608. const responsiblePersonList = ref([]); // 所有责任人列表
  609. const selectedResponsiblePersonIds = ref([]); // 选中的责任人ID
  610. const currentEditingRowForResponsible = ref(null); // 当前正在编辑责任人的行
  611. const submitterDialogVisible = ref(false);
  612. const submitterList = ref([]); // 所有填报人列表
  613. const selectedSubmitterIds = ref([]); // 选中的日报填报人ID
  614. // 动态属性相关变量
  615. const dynamicAttrs = ref([]); // 存储动态属性列表
  616. // 跟踪是否已从任务数据中获取动态属性
  617. const hasDynamicAttrsFromTask = ref(false);
  618. // 添加 workAreaOptions 引用和加载状态
  619. const workAreaOptions = ref<any[]>([])
  620. const loadingWorkAreaOptions = ref(false)
  621. const workAreaAutocomplete = ref() // 添加对el-autocomplete的引用
  622. // currentDictLabel 响应式变量
  623. const currentDictLabel = ref(''); // 存储当前项目对应的施工区域字典类型
  624. // 施工工艺相关变量
  625. const technologyOptions = ref<any[]>([])
  626. const loadingTechnologyOptions = ref(false)
  627. const currentTechnologyDictLabel = ref(''); // 存储当前项目对应的施工工艺字典类型
  628. /** 项目信息 表单 */
  629. defineOptions({ name: 'IotProjectTaskInfo' })
  630. const formData = ref({
  631. id: undefined,
  632. contractId: undefined,
  633. contractName: undefined,
  634. contractCode: undefined,
  635. workloadTotal: undefined,
  636. workloadFinish: undefined,
  637. workloadUnit: undefined,
  638. startTime: undefined,
  639. endTime: undefined,
  640. location: undefined,
  641. technique: undefined,
  642. payment: undefined,
  643. remark:undefined,
  644. manufactureName: undefined,
  645. manufacturerId: undefined,
  646. userName: undefined,
  647. userId: undefined,
  648. deptId: undefined, // 新增项目部门ID字段
  649. })
  650. const close = () => {
  651. delView(unref(currentRoute))
  652. push({ name: 'IotProjectTask', params:{}})
  653. }
  654. // 添加计算属性来判断是否为特定部门
  655. const isSpecialDept = computed(() => {
  656. return formData.value.deptId === 163;
  657. });
  658. const getProjectInfo = async (contractId: number) => {
  659. const project = projectList.value.find(item => item.id === contractId);
  660. if (project) {
  661. formData.value.contractName = project.contractName;
  662. formData.value.contractCode = project.contractCode;
  663. formData.value.payment = project.payment;
  664. formData.value.workloadTotal = project.workloadTotal;
  665. formData.value.startTime = project.startTime;
  666. formData.value.endTime = project.endTime;
  667. formData.value.remark = project.remark;
  668. formData.value.manufactureName = project.manufactureName;
  669. formData.value.manufacturerId = project.manufacturerId;
  670. formData.value.id = project.id;
  671. formData.value.deptId = project.deptId; // 保存项目部门ID
  672. // 获取施工区域数据字典集合
  673. await loadWorkAreaOptions(formData.value.deptId);
  674. // 获取施工工艺数据字典集合
  675. await loadTechnologyOptions(formData.value.deptId);
  676. // 获取动态属性
  677. await fetchDynamicAttrs();
  678. }
  679. }
  680. const queryParams = reactive({
  681. projectId:undefined,
  682. id:undefined
  683. })
  684. const formRules = reactive({
  685. contractId: [{ required: true, message: '合同名称不能为空', trigger: 'blur' }],
  686. manufacturerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }],
  687. payment: [{ required: true, message: '结算方式不能为空', trigger: 'blur' }],
  688. // workloadTotal: [{ required: true, message: '工作量总数不能为空', trigger: 'blur' }],
  689. startTime: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }],
  690. endTime: [{ required: true, message: '结束时间不能为空', trigger: 'blur' }],
  691. })
  692. const formRef = ref() // 表单 Ref
  693. const zzClear = () =>{
  694. formData.value.manufacturerId = undefined
  695. formData.value.manufactureName = undefined
  696. }
  697. // 根据部门ID获取部门名称
  698. const getDeptNames = (deptIds) => {
  699. if (!deptIds || deptIds.length === 0) return '';
  700. const names = [];
  701. const findDept = (list, id) => {
  702. for (const item of list) {
  703. if (item.id === id) {
  704. names.push(item.name);
  705. return true;
  706. }
  707. if (item.children && findDept(item.children, id)) {
  708. return true;
  709. }
  710. }
  711. return false;
  712. };
  713. deptIds.forEach(id => findDept(deptList.value, id));
  714. if (names.length > 1) {
  715. return `${names[0]}...`;
  716. }
  717. return names.join(', ');
  718. };
  719. const getAllDeptNames = (deptIds) => {
  720. if (!deptIds || deptIds.length === 0) return '无施工队伍';
  721. const names = [];
  722. const findDept = (list, id) => {
  723. for (const item of list) {
  724. if (item.id === id) {
  725. names.push(item.name);
  726. return true;
  727. }
  728. if (item.children && findDept(item.children, id)) {
  729. return true;
  730. }
  731. }
  732. return false;
  733. };
  734. deptIds.forEach(id => findDept(deptList.value, id));
  735. return names.join(', ') || '无有效施工队伍';
  736. };
  737. const getDeviceNames = (deviceIds: number[]) => {
  738. if (!deviceIds || deviceIds.length === 0) return '';
  739. // 获取所有有效设备名称
  740. const deviceNames = deviceIds
  741. .map(id => deviceMap.value[id]?.deviceCode)
  742. .filter(name => name !== undefined && name !== '');
  743. if (deviceNames.length === 0) return '';
  744. // 如果设备数量超过2个,显示前两个加省略号
  745. if (deviceNames.length > 2) {
  746. return `${deviceNames[0]}, ${deviceNames[1]}...`;
  747. }
  748. // 设备数量不超过2个,正常显示所有
  749. return deviceNames.join(', ');
  750. };
  751. // 初始化动态属性到当前任务
  752. /* const initDynamicAttrsToCurrentTask = () => {
  753. dynamicAttrs.value.forEach(attr => {
  754. // 如果当前任务中还没有这个属性,则初始化
  755. if (!currentTask.value.hasOwnProperty(attr.identifier)) {
  756. // 如果是编辑模式且有已有值,使用已有值,否则使用默认值
  757. if (formType.value === 'update' && tableData.value[0] && tableData.value[0][attr.identifier] !== undefined) {
  758. currentTask.value[attr.identifier] = tableData.value[0][attr.identifier];
  759. } else {
  760. currentTask.value[attr.identifier] = attr.defaultValue || '';
  761. }
  762. }
  763. });
  764. }; */
  765. const initDynamicAttrsToCurrentTask = () => {
  766. dynamicAttrs.value.forEach(attr => {
  767. if (!currentTask.value.hasOwnProperty(attr.identifier)) {
  768. // 如果是从任务数据中获取的属性,并且有实际值,使用实际值
  769. if (hasDynamicAttrsFromTask.value && attr.actualValue !== undefined && attr.actualValue !== null && attr.actualValue !== '') {
  770. currentTask.value[attr.identifier] = attr.actualValue;
  771. } else {
  772. // 否则使用默认值
  773. currentTask.value[attr.identifier] = attr.defaultValue || '';
  774. }
  775. }
  776. });
  777. };
  778. // 当前编辑的任务
  779. const currentTask = ref({
  780. id: undefined,
  781. wellName: '',
  782. wellType: '',
  783. wellCategory: '',
  784. location: '',
  785. dictType: '',
  786. technique: '',
  787. workloadDesign: '',
  788. workloadUnit: '',
  789. deptIds: [],
  790. deviceIds: [],
  791. responsiblePerson: [],
  792. submitter: [],
  793. remark: '',
  794. projectId: ''
  795. });
  796. const isNewTask = ref(false); // 是否是新任务
  797. const taskFormRef = ref(); // 任务表单ref
  798. const currentEditingIndex = ref(-1); // 当前编辑的行索引
  799. // 任务表单验证规则
  800. const taskFormRules = reactive({
  801. wellName: [{ required: true, message: '井号不能为空', trigger: 'blur' }],
  802. wellType: [{ required: true, message: '井型不能为空', trigger: 'change' }],
  803. location: [{ required: true, message: '施工地点不能为空', trigger: 'blur' }],
  804. technique: [{ required: true, message: '施工工艺不能为空', trigger: 'change' }],
  805. workloadDesign: [{ required: true, message: '设计工作量不能为空', trigger: 'blur' }],
  806. workloadUnit: [{ required: true, message: '工作量单位不能为空', trigger: 'change' }],
  807. deptIds: [{ required: true, message: '施工队伍不能为空', trigger: 'change' }],
  808. deviceIds: [{ required: true, message: '施工设备不能为空', trigger: 'change' }],
  809. responsiblePerson: [{ required: true, message: '责任人不能为空', trigger: 'change' }],
  810. // 动态添加填报人验证规则
  811. ...(isSpecialDept.value ? {
  812. submitter: [{ required: true, message: '填报人不能为空', trigger: 'change' }]
  813. } : {})
  814. });
  815. // 动态属性验证规则
  816. const getDynamicAttrRules = (attr) => {
  817. const rules = [];
  818. if (attr.required === 1) {
  819. rules.push({ required: true, message: `${attr.name}不能为空`, trigger: 'blur' });
  820. }
  821. // 数字类型验证
  822. if (attr.dataType === 'double') {
  823. rules.push({
  824. validator: (rule, value, callback) => {
  825. if (value === '' || value === null || value === undefined) {
  826. callback();
  827. return;
  828. }
  829. const numValue = Number(value);
  830. if (isNaN(numValue)) {
  831. callback(new Error(`${attr.name}必须是数字`));
  832. } else if (attr.minValue !== null && numValue < Number(attr.minValue)) {
  833. // callback(new Error(`${attr.name}不能小于${attr.minValue}`));
  834. } else if (attr.maxValue !== null && numValue > Number(attr.maxValue)) {
  835. // callback(new Error(`${attr.name}不能大于${attr.maxValue}`));
  836. } else {
  837. callback();
  838. }
  839. },
  840. trigger: 'blur'
  841. });
  842. }
  843. return rules;
  844. };
  845. const tableData = ref([]);
  846. // 打开责任人选择对话框(用于表单)
  847. const openResponsiblePersonDialogForForm = async () => {
  848. if (!currentTask.value.deptIds || currentTask.value.deptIds.length === 0) {
  849. ElMessage.warning('请先选择施工队伍');
  850. return;
  851. }
  852. selectedResponsiblePersonIds.value = [...(currentTask.value.responsiblePerson || [])];
  853. try {
  854. const params = {
  855. deptIds: currentTask.value.deptIds // 使用当前项目所属部门的deptId
  856. };
  857. const response = await UserApi.selectedDeptsEmployee(params);
  858. responsiblePersonList.value = response;
  859. responsiblePersonDialogVisible.value = true;
  860. } catch (error) {
  861. ElMessage.error('获取责任人列表失败');
  862. console.error('获取责任人列表失败:', error);
  863. }
  864. };
  865. // 打开工单人选择对话框(用于表单)
  866. const openSubmitterDialogForForm = async () => {
  867. if (!currentTask.value.deptIds || currentTask.value.deptIds.length === 0) {
  868. ElMessage.warning('请先选择施工队伍');
  869. return;
  870. }
  871. selectedSubmitterIds.value = [...(currentTask.value.submitter || [])];
  872. try {
  873. const params = {
  874. deptIds: currentTask.value.deptIds // 使用当前项目所属部门的deptId
  875. };
  876. const response = await UserApi.selectedDeptsEmployee(params);
  877. submitterList.value = response;
  878. submitterDialogVisible.value = true;
  879. } catch (error) {
  880. ElMessage.error('获取填报人列表失败');
  881. console.error('获取填报人列表失败:', error);
  882. }
  883. };
  884. // 获取动态属性
  885. const fetchDynamicAttrs = async () => {
  886. // 如果已经通过任务数据获取了动态属性,则不再调用接口
  887. if (hasDynamicAttrsFromTask.value) {
  888. return;
  889. }
  890. if (!formData.value.deptId) {
  891. console.warn('部门ID为空,无法获取动态属性');
  892. return;
  893. }
  894. try {
  895. const queryParams = {
  896. deptId: formData.value.deptId
  897. };
  898. const response = await IotProjectTaskAttrsApi.getIotProjectTaskAttrsList(queryParams);
  899. dynamicAttrs.value = response || [];
  900. // 初始化动态属性到当前任务
  901. initDynamicAttrsToCurrentTask();
  902. } catch (error) {
  903. console.error('获取动态属性失败:', error);
  904. ElMessage.error('获取动态属性失败');
  905. }
  906. };
  907. // 打开设备选择对话框(用于表单)
  908. const openDeviceDialogForForm = async () => {
  909. if (!currentTask.value.deptIds || currentTask.value.deptIds.length === 0) {
  910. ElMessage.warning('请先选择施工队伍');
  911. return;
  912. }
  913. selectedDeviceIds.value = [...(currentTask.value.deviceIds || [])];
  914. try {
  915. const params = {
  916. deptIds: currentTask.value.deptIds
  917. };
  918. const data = await IotDeviceApi.getDevicesByDepts(params);
  919. // 更新设备映射表
  920. const newDeviceMap = {...deviceMap.value};
  921. data.forEach(device => {
  922. newDeviceMap[device.id] = device;
  923. });
  924. deviceMap.value = newDeviceMap;
  925. filteredDeviceList.value = data;
  926. deviceDialogVisible.value = true;
  927. } catch (error) {
  928. ElMessage.error('获取设备列表失败');
  929. console.error('获取设备列表失败:', error);
  930. }
  931. };
  932. // 处理穿梭框变化
  933. const handleTransferChange = (value, direction, movedKeys) => {
  934. // 可以添加额外的处理逻辑
  935. };
  936. // 确认设备选择(用于表单)
  937. const confirmDeviceSelection = () => {
  938. currentTask.value.deviceIds = [...selectedDeviceIds.value];
  939. // 强制更新视图
  940. currentTask.value = {...currentTask.value};
  941. deviceDialogVisible.value = false;
  942. };
  943. // 确认责任人选择(用于表单)
  944. const confirmResponsiblePersonSelection = () => {
  945. currentTask.value.responsiblePerson = [...selectedResponsiblePersonIds.value];
  946. responsiblePersonDialogVisible.value = false;
  947. };
  948. // 确认填报人选择(用于表单)
  949. const confirmSubmitterSelection = () => {
  950. currentTask.value.submitter = [...selectedSubmitterIds.value];
  951. submitterDialogVisible.value = false;
  952. };
  953. // 关闭设备选择对话框
  954. const handleDeviceDialogClose = () => {
  955. deviceDialogVisible.value = false;
  956. };
  957. // 关闭责任人选择对话框
  958. const handleResponsiblePersonDialogClose = () => {
  959. responsiblePersonDialogVisible.value = false;
  960. };
  961. // 关闭填报人选择对话框
  962. const handleSubmitterDialogClose = () => {
  963. submitterDialogVisible.value = false;
  964. };
  965. // 获取井型标签的方法
  966. const getWellTypeLabel = (value) => {
  967. const options = getStrDictOptions(DICT_TYPE.PMS_PROJECT_WELL_TYPE)
  968. const option = options.find(opt => opt.value === value)
  969. return option ? option.label : value
  970. }
  971. // 获取井别标签的方法
  972. const getWellCategoryLabel = (value) => {
  973. const options = getStrDictOptions(DICT_TYPE.PMS_PROJECT_WELL_CATEGORY)
  974. const option = options.find(opt => opt.value === value)
  975. return option ? option.label : value
  976. }
  977. // 获取施工工艺标签的方法
  978. const getTechniqueLabel = (value) => {
  979. const options = getStrDictOptions(DICT_TYPE.PMS_PROJECT_TECHNOLOGY)
  980. const option = options.find(opt => opt.value === value)
  981. return option ? option.label : value
  982. }
  983. // 获取 工作量单位 标签的方法
  984. const getWorkloadUnitLabel = (value) => {
  985. const options = getStrDictOptions(DICT_TYPE.PMS_PROJECT_WORKLOAD_UNIT)
  986. const option = options.find(opt => opt.value === value)
  987. return option ? option.label : value
  988. }
  989. /** 同步当前任务表单数据到表格数据 */
  990. const syncCurrentTaskToTable = () => {
  991. if (!currentTask.value.wellName) {
  992. ElMessage.warning('请先填写任务详情');
  993. return false;
  994. }
  995. // 设置项目ID
  996. currentTask.value.projectId = formData.value.id;
  997. if (isNewTask.value) {
  998. // 新增任务 - 生成唯一ID并添加到表格
  999. const newId = tableData.value.length > 0
  1000. ? Math.max(...tableData.value.map(item => item.id)) + 1
  1001. : 1;
  1002. currentTask.value.id = newId;
  1003. tableData.value.unshift({...currentTask.value});
  1004. isNewTask.value = false;
  1005. // ElMessage.success('任务已添加到列表');
  1006. } else {
  1007. // 更新现有任务
  1008. const index = tableData.value.findIndex(item => item.id === currentTask.value.id);
  1009. if (index !== -1) {
  1010. tableData.value.splice(index, 1, {...currentTask.value});
  1011. // ElMessage.success('任务已更新');
  1012. } else {
  1013. // 如果没有找到,添加到表格(可能是意外情况)
  1014. tableData.value.unshift({...currentTask.value});
  1015. }
  1016. }
  1017. return true;
  1018. };
  1019. // 重置任务表单
  1020. const resetTaskForm = () => {
  1021. currentTask.value = {
  1022. id: undefined,
  1023. wellName: '',
  1024. wellType: '',
  1025. wellCategory: '',
  1026. location: '',
  1027. technique: '',
  1028. workloadDesign: '',
  1029. workloadUnit: '',
  1030. deptIds: [],
  1031. deviceIds: [],
  1032. responsiblePerson: [],
  1033. remark: '',
  1034. projectId: formData.value.id,
  1035. // 如果是特殊部门,初始化填报人字段
  1036. ...(isSpecialDept.value ? { submitter: [] } : {})
  1037. };
  1038. // 重新初始化动态属性
  1039. initDynamicAttrsToCurrentTask();
  1040. isNewTask.value = false;
  1041. taskFormRef.value?.resetFields();
  1042. };
  1043. // 添加新任务
  1044. const addNewTask = () => {
  1045. resetTaskForm();
  1046. isNewTask.value = true;
  1047. };
  1048. // 保存任务
  1049. const saveTask = async () => {
  1050. // 验证表单
  1051. try {
  1052. await taskFormRef.value.validate();
  1053. } catch (error) {
  1054. ElMessage.error('请完善任务信息');
  1055. return;
  1056. }
  1057. // 设置项目ID
  1058. currentTask.value.projectId = formData.value.id;
  1059. if (isNewTask.value) {
  1060. // 新增任务
  1061. const newId = tableData.value.length > 0
  1062. ? Math.max(...tableData.value.map(item => item.id)) + 1
  1063. : 1;
  1064. currentTask.value.id = newId;
  1065. tableData.value.unshift({...currentTask.value});
  1066. ElMessage.success('任务添加成功');
  1067. resetTaskForm();
  1068. } else {
  1069. // 更新任务
  1070. const index = tableData.value.findIndex(item => item.id === currentTask.value.id);
  1071. if (index !== -1) {
  1072. tableData.value.splice(index, 1, {...currentTask.value});
  1073. ElMessage.success('任务更新成功');
  1074. }
  1075. }
  1076. };
  1077. // 删除当前任务
  1078. const deleteCurrentTask = () => {
  1079. if (!currentTask.value.id) return;
  1080. ElMessageBox.confirm('确定要删除这个任务吗?', '提示', {
  1081. confirmButtonText: '确定',
  1082. cancelButtonText: '取消',
  1083. type: 'warning'
  1084. }).then(() => {
  1085. const index = tableData.value.findIndex(item => item.id === currentTask.value.id);
  1086. if (index !== -1) {
  1087. tableData.value.splice(index, 1);
  1088. resetTaskForm();
  1089. ElMessage.success('任务删除成功');
  1090. }
  1091. }).catch(() => {
  1092. // 取消删除
  1093. });
  1094. };
  1095. // 处理行点击事件
  1096. const handleRowClick = (row, index) => {
  1097. if (row) {
  1098. currentTask.value = {...row};
  1099. currentEditingIndex.value = index; // 保存当前编辑的行索引
  1100. isNewTask.value = false;
  1101. }
  1102. };
  1103. // 根据责任人ID获取责任人名称
  1104. const getResponsiblePersonNames = (responsiblePersonIds: number[]) => {
  1105. if (!responsiblePersonIds || responsiblePersonIds.length === 0) return '';
  1106. // 获取所有有效责任人名称
  1107. const personNames = responsiblePersonIds
  1108. .map(id => {
  1109. const person = responsiblePersonList.value.find(p => p.id === id);
  1110. return person ? person.nickname : '';
  1111. })
  1112. .filter(name => name !== undefined && name !== '');
  1113. if (personNames.length === 0) return '';
  1114. // 如果责任人数量超过2个,显示前两个加省略号
  1115. if (personNames.length > 2) {
  1116. return `${personNames[0]}, ${personNames[1]}...`;
  1117. }
  1118. // 责任人数量不超过2个,正常显示所有
  1119. return personNames.join(', ');
  1120. };
  1121. // 获取所有责任人名称(用于tooltip提示)
  1122. const getAllResponsiblePersonNames = (responsiblePersonIds: number[]) => {
  1123. if (!responsiblePersonIds || responsiblePersonIds.length === 0) return '无责任人';
  1124. const personNames = responsiblePersonIds
  1125. .map(id => {
  1126. const person = responsiblePersonList.value.find(p => p.id === id);
  1127. return person ? person.nickname : '未知人员';
  1128. })
  1129. .filter(name => name !== '未知人员');
  1130. return personNames.join(', ') || '无有效责任人';
  1131. };
  1132. // 获取所有设备名称(用于tooltip提示)
  1133. const getAllDeviceNames = (deviceIds: number[]) => {
  1134. if (!deviceIds || deviceIds.length === 0) return '无设备';
  1135. const deviceNames = deviceIds
  1136. .map(id => deviceMap.value[id]?.deviceCode || '未知设备')
  1137. .filter(name => name !== '未知设备');
  1138. return deviceNames.join(', ') || '无有效设备';
  1139. };
  1140. /** 打开弹窗 */
  1141. const open = async () => {
  1142. resetForm()
  1143. resetTaskForm();
  1144. hasDynamicAttrsFromTask.value = false; // 重置标志
  1145. // 修改时,设置数据
  1146. if (id) {
  1147. formType.value = 'update';
  1148. formLoading.value = true
  1149. try {
  1150. queryParams.id = id;
  1151. const data = await IotProjectTaskApi.getIotProjectTaskPage(queryParams);
  1152. tableData.value = data.list
  1153. // 收集所有设备ID
  1154. const allDeviceIds = new Set<number>();
  1155. // 收集所有责任人ID
  1156. const allResponsiblePersonIds = new Set<number>();
  1157. // 收集所有填报人ID
  1158. const allSubmitterIds = new Set<number>();
  1159. data.list.forEach(item => {
  1160. if (item.deviceIds?.length) {
  1161. item.deviceIds.forEach(id => allDeviceIds.add(id));
  1162. }
  1163. if (item.responsiblePerson?.length) {
  1164. item.responsiblePerson.forEach(id => allResponsiblePersonIds.add(id));
  1165. }
  1166. if (item.submitter?.length) {
  1167. item.submitter.forEach(id => allSubmitterIds.add(id));
  1168. }
  1169. });
  1170. // 批量获取设备信息
  1171. if (allDeviceIds.size > 0) {
  1172. const deviceIdsArray = Array.from(allDeviceIds);
  1173. const devices = await IotDeviceApi.getDevicesByDepts({
  1174. deviceIds: deviceIdsArray
  1175. });
  1176. // 更新设备列表和映射表
  1177. deviceList.value = devices;
  1178. deviceMap.value = {};
  1179. devices.forEach(device => {
  1180. deviceMap.value[device.id] = device;
  1181. });
  1182. }
  1183. // 批量获取责任人信息
  1184. if (allResponsiblePersonIds.size > 0) {
  1185. const personIdsArray = Array.from(allResponsiblePersonIds);
  1186. const params = {
  1187. userIds: personIdsArray
  1188. };
  1189. const persons = await UserApi.companyDeptsEmployee(params);
  1190. // 更新责任人列表
  1191. responsiblePersonList.value = persons;
  1192. }
  1193. // 批量获取填报人信息
  1194. if (allSubmitterIds.size > 0) {
  1195. const personIdsArray = Array.from(allSubmitterIds);
  1196. const params = {
  1197. userIds: personIdsArray
  1198. };
  1199. const persons = await UserApi.companyDeptsEmployee(params);
  1200. // 更新责任人列表
  1201. submitterList.value = persons;
  1202. }
  1203. if (tableData.value && tableData.value.length > 0) {
  1204. // 使用深拷贝,避免修改表单时直接影响表格数据
  1205. currentTask.value = JSON.parse(JSON.stringify(tableData.value[0]));
  1206. isNewTask.value = false;
  1207. // 新增代码:检查任务数据中是否有extProperty
  1208. if (tableData.value[0].extProperty && tableData.value[0].extProperty.length > 0) {
  1209. // 使用任务数据中的extProperty作为动态属性
  1210. dynamicAttrs.value = tableData.value[0].extProperty;
  1211. hasDynamicAttrsFromTask.value = true;
  1212. // 将extProperty中的值设置到当前任务
  1213. tableData.value[0].extProperty.forEach(attr => {
  1214. if (attr.identifier) {
  1215. // 优先使用actualValue,如果没有则使用defaultValue
  1216. currentTask.value[attr.identifier] =
  1217. attr.actualValue !== undefined && attr.actualValue !== null && attr.actualValue !== ''
  1218. ? attr.actualValue
  1219. : attr.defaultValue || '';
  1220. }
  1221. });
  1222. }
  1223. }
  1224. } finally {
  1225. formLoading.value = false
  1226. }
  1227. } else {
  1228. formType.value = 'create';
  1229. }
  1230. // 如果有projectId参数,自动选中对应的合同
  1231. if (projectId.value) {
  1232. autoSelectContract(projectId.value);
  1233. }
  1234. }
  1235. defineExpose({ open }) // 提供 open 方法,用于打开弹窗
  1236. /** 自动选择合同 */
  1237. const autoSelectContract = async (projectId: string) => {
  1238. console.log('项目id:' + projectId);
  1239. // 查找匹配的合同
  1240. const project = projectList.value.find(item => item.id === Number(projectId));
  1241. if (project) {
  1242. // 设置选中值
  1243. formData.value.contractId = project.id;
  1244. // 触发合同信息加载
  1245. getProjectInfo(project.id);
  1246. } else {
  1247. ElMessage.warning('未找到指定的合同信息');
  1248. }
  1249. }
  1250. /** 校验所有行数据 */
  1251. const validateAllRows = (): boolean => {
  1252. // 判断任务列表是否为空
  1253. if (!tableData.value || tableData.value.length === 0) {
  1254. ElMessage.error('没有任务数据,无法保存');
  1255. return false;
  1256. }
  1257. let allValid = true;
  1258. tableData.value.forEach(row => {
  1259. if (!row.wellName || row.wellName.trim() === '') {
  1260. allValid = false;
  1261. }
  1262. /* if (!row.wellType || row.wellType.trim() === '') {
  1263. allValid = false;
  1264. } */
  1265. if (!row.location || row.location.trim() === '') {
  1266. allValid = false;
  1267. }
  1268. if (!row.technique || row.technique.trim() === '') {
  1269. allValid = false;
  1270. }
  1271. if (!row.deptIds || row.deptIds.length === 0) {
  1272. allValid = false;
  1273. }
  1274. if (!row.deviceIds || row.deviceIds.length === 0) {
  1275. allValid = false;
  1276. }
  1277. if (!row.responsiblePerson || row.responsiblePerson.length === 0) {
  1278. allValid = false;
  1279. }
  1280. // 如果是特殊部门,验证填报人
  1281. if (isSpecialDept.value && (!row.submitter || row.submitter.length === 0)) {
  1282. allValid = false;
  1283. }
  1284. });
  1285. if (!allValid) {
  1286. ElMessage.error('请检查表格中的数据,部分必填项未填写或格式不正确');
  1287. }
  1288. return allValid;
  1289. };
  1290. // 添加表单专用的格式化方法
  1291. // 格式化设备显示(用于表单)
  1292. const formatDevicesForForm = (deviceIds: number[]) => {
  1293. if (!deviceIds || deviceIds.length === 0) return '';
  1294. const deviceNames = deviceIds
  1295. .map(id => deviceMap.value[id]?.deviceCode)
  1296. .filter(name => name !== undefined && name !== '');
  1297. if (deviceNames.length === 0) return '';
  1298. if (deviceNames.length > 2) {
  1299. return `${deviceNames[0]}, ${deviceNames[1]}...`;
  1300. }
  1301. return deviceNames.join(', ');
  1302. };
  1303. // 获取所有设备名称(用于表单的tooltip)
  1304. const getAllDeviceNamesForForm = (deviceIds: number[]) => {
  1305. if (!deviceIds || deviceIds.length === 0) return '无设备';
  1306. const deviceNames = deviceIds
  1307. .map(id => deviceMap.value[id]?.deviceCode || '未知设备')
  1308. .filter(name => name !== '未知设备');
  1309. return deviceNames.join(', ') || '无有效设备';
  1310. };
  1311. // 格式化责任人显示(用于表单)
  1312. const formatResponsiblePersonsForForm = (responsiblePersonIds: number[]) => {
  1313. if (!responsiblePersonIds || responsiblePersonIds.length === 0) return '';
  1314. const personNames = responsiblePersonIds
  1315. .map(id => {
  1316. const person = responsiblePersonList.value.find(p => p.id === id);
  1317. return person ? person.nickname : '';
  1318. })
  1319. .filter(name => name !== undefined && name !== '');
  1320. if (personNames.length === 0) return '';
  1321. if (personNames.length > 2) {
  1322. return `${personNames[0]}, ${personNames[1]}...`;
  1323. }
  1324. return personNames.join(', ');
  1325. };
  1326. // 格式化填报人显示(用于表单)
  1327. const formatSubmittersForForm = (submitterIds: number[]) => {
  1328. if (!submitterIds || submitterIds.length === 0) return '';
  1329. const personNames = submitterIds
  1330. .map(id => {
  1331. const person = submitterList.value.find(p => p.id === id);
  1332. return person ? person.nickname : '';
  1333. })
  1334. .filter(name => name !== undefined && name !== '');
  1335. if (personNames.length === 0) return '';
  1336. if (personNames.length > 2) {
  1337. return `${personNames[0]}, ${personNames[1]}...`;
  1338. }
  1339. return personNames.join(', ');
  1340. };
  1341. // 获取所有责任人名称(用于表单的tooltip)
  1342. const getAllResponsiblePersonNamesForForm = (responsiblePersonIds: number[]) => {
  1343. if (!responsiblePersonIds || responsiblePersonIds.length === 0) return '无责任人';
  1344. const personNames = responsiblePersonIds
  1345. .map(id => {
  1346. const person = responsiblePersonList.value.find(p => p.id === id);
  1347. return person ? person.nickname : '未知人员';
  1348. })
  1349. .filter(name => name !== '未知人员');
  1350. return personNames.join(', ') || '无有效责任人';
  1351. };
  1352. // 获取所有填报人名称(用于表单的tooltip)
  1353. const getAllSubmitterNamesForForm = (submitterIds: number[]) => {
  1354. if (!submitterIds || submitterIds.length === 0) return '无填报人';
  1355. const personNames = submitterIds
  1356. .map(id => {
  1357. const person = submitterList.value.find(p => p.id === id);
  1358. return person ? person.nickname : '未知人员';
  1359. })
  1360. .filter(name => name !== '未知人员');
  1361. return personNames.join(', ') || '无有效填报人';
  1362. };
  1363. /** 提交表单 */
  1364. const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
  1365. const submitForm = async () => {
  1366. // 施工区域的数据字典类型
  1367. currentTask.value.dictType = currentDictLabel.value;
  1368. // 先同步当前任务表单数据到表格
  1369. if (!syncCurrentTaskToTable()) {
  1370. return;
  1371. }
  1372. // 校验表单
  1373. await formRef.value.validate()
  1374. // 校验所有行数据
  1375. if (!validateAllRows()) {
  1376. return;
  1377. }
  1378. // 处理动态属性数据
  1379. const processedTableData = tableData.value.map(task => {
  1380. // 为每个任务构建extProperty数组
  1381. const extProperties = dynamicAttrs.value.map(attr => {
  1382. return {
  1383. name: attr.name,
  1384. sort: attr.sort,
  1385. unit: attr.unit,
  1386. actualValue: task[attr.identifier] || '', // 从任务中获取用户填写的值
  1387. dataType: attr.dataType,
  1388. maxValue: attr.maxValue,
  1389. minValue: attr.minValue,
  1390. required: attr.required,
  1391. accessMode: attr.accessMode,
  1392. identifier: attr.identifier,
  1393. defaultValue: attr.defaultValue
  1394. };
  1395. });
  1396. // 返回处理后的任务数据,包含extProperty字段
  1397. return {
  1398. ...task,
  1399. dictType: task.dictType || currentDictLabel.value, // 确保每个任务都有dictType
  1400. extProperty: extProperties
  1401. };
  1402. });
  1403. // 提交请求
  1404. formLoading.value = true
  1405. try {
  1406. tableData.value.forEach(item=>{
  1407. item.projectId = formData.value.id;
  1408. })
  1409. const data = {
  1410. taskList: processedTableData
  1411. }
  1412. if (formType.value === 'create') {
  1413. await IotProjectTaskApi.createIotProjectTask(data)
  1414. message.success(t('common.createSuccess'))
  1415. } else {
  1416. await IotProjectTaskApi.updateIotProjectTask(data)
  1417. message.success(t('common.updateSuccess'))
  1418. }
  1419. // 发送操作成功的事件
  1420. emit('success')
  1421. close()
  1422. } finally {
  1423. formLoading.value = false
  1424. }
  1425. }
  1426. /** 重置表单 */
  1427. const resetForm = () => {
  1428. formData.value = {
  1429. id: undefined,
  1430. contractId: undefined,
  1431. contractName: undefined,
  1432. contractCode: undefined,
  1433. workloadTotal: undefined,
  1434. workloadFinish: undefined,
  1435. workloadUnit: undefined,
  1436. startTime: undefined,
  1437. endTime: undefined,
  1438. location: undefined,
  1439. technique: undefined,
  1440. payment: undefined,
  1441. remark: undefined,
  1442. manufactureName: undefined,
  1443. manufacturerId: undefined,
  1444. userName: undefined,
  1445. userId: undefined,
  1446. }
  1447. formRef.value?.resetFields()
  1448. }
  1449. // 加载工作区域选项的方法
  1450. const loadWorkAreaOptions = async (deptId: number) => {
  1451. // 如果正在加载,直接返回
  1452. if (loadingWorkAreaOptions.value) return;
  1453. loadingWorkAreaOptions.value = true;
  1454. try {
  1455. // 先尝试通过 getDictLabel 获取字典标签
  1456. const dictLabel = await getDictLabel(DICT_TYPE.PMS_PROJECT_WORK_AREA, deptId);
  1457. console.log('当前专业公司的施工区域数据字典类型:' + dictLabel)
  1458. // 将 dictLabel 存储到 currentDictLabel
  1459. currentDictLabel.value = dictLabel || ''; // 如果没有获取到,设为空字符串
  1460. if (dictLabel) {
  1461. // 如果获取到标签,再获取完整的字典选项
  1462. const dictOptions = getStrDictOptions(dictLabel);
  1463. console.log('当前专业公司的施工区域数据字典集合长度:' + dictOptions.length)
  1464. workAreaOptions.value = dictOptions.map(option => ({
  1465. value: option.label, // 使用标签作为显示值
  1466. id: option.value // 保留原始值
  1467. }));
  1468. } else {
  1469. // 如果获取不到标签,清空选项
  1470. workAreaOptions.value = [];
  1471. }
  1472. } catch (error) {
  1473. console.error('获取工作区域选项失败:', error);
  1474. workAreaOptions.value = [];
  1475. } finally {
  1476. loadingWorkAreaOptions.value = false;
  1477. }
  1478. }
  1479. // 当没有查询字符串时返回所有选项
  1480. const querySearch = (queryString: string, cb: any) => {
  1481. // 如果没有查询字符串,返回所有选项
  1482. if (!queryString) {
  1483. cb(workAreaOptions.value);
  1484. return;
  1485. }
  1486. // 有查询字符串时进行筛选
  1487. const results = workAreaOptions.value.filter(option =>
  1488. option.value.toLowerCase().includes(queryString.toLowerCase())
  1489. );
  1490. // 调用 callback 返回建议列表的数据
  1491. cb(results);
  1492. }
  1493. // 添加处理施工区域获取焦点的方法
  1494. const handleWorkAreaFocus = async () => {
  1495. // 如果已经有选项数据,直接显示下拉框
  1496. if (workAreaOptions.value.length > 0) {
  1497. // 使用Element Plus提供的方法触发下拉框
  1498. try {
  1499. // 直接调用el-autocomplete的focus方法
  1500. if (workAreaAutocomplete.value && typeof workAreaAutocomplete.value.focus === 'function') {
  1501. workAreaAutocomplete.value.focus();
  1502. }
  1503. } catch (error) {
  1504. console.error('触发下拉框显示失败:', error)
  1505. }
  1506. return
  1507. }
  1508. // 如果没有选项数据,尝试加载
  1509. if (formData.value.deptId && !loadingWorkAreaOptions.value) {
  1510. await loadWorkAreaOptions(formData.value.deptId)
  1511. // 加载完成后尝试显示下拉框
  1512. if (workAreaOptions.value.length > 0) {
  1513. try {
  1514. if (workAreaAutocomplete.value && typeof workAreaAutocomplete.value.focus === 'function') {
  1515. workAreaAutocomplete.value.focus();
  1516. }
  1517. } catch (error) {
  1518. console.error('触发下拉框显示失败:', error)
  1519. }
  1520. }
  1521. }
  1522. }
  1523. // 添加选择处理方法
  1524. const handleSelect = (item: any) => {
  1525. // 这里可以根据需要处理选择后的逻辑
  1526. console.log('选择了:', item.value);
  1527. }
  1528. // 加载施工工艺选项的方法
  1529. const loadTechnologyOptions = async (deptId: number) => {
  1530. if (loadingTechnologyOptions.value) return;
  1531. loadingTechnologyOptions.value = true;
  1532. try {
  1533. // 先尝试通过 getDictLabel 获取字典标签
  1534. const dictLabel = await getDictLabel(DICT_TYPE.PMS_PROJECT_TECHNOLOGY, deptId);
  1535. console.log('当前专业公司的施工工艺数据字典类型:' + dictLabel)
  1536. // 将 dictLabel 存储到 currentTechnologyDictLabel
  1537. currentTechnologyDictLabel.value = dictLabel || ''; // 如果没有获取到,设为空字符串
  1538. if (dictLabel) {
  1539. // 如果获取到标签,再获取完整的字典选项
  1540. const dictOptions = getStrDictOptions(dictLabel);
  1541. console.log('当前专业公司的施工工艺数据字典集合长度:' + dictOptions.length)
  1542. technologyOptions.value = dictOptions.map(option => ({
  1543. value: option.value,
  1544. label: option.label
  1545. }));
  1546. } else {
  1547. // 如果获取不到标签,清空选项
  1548. technologyOptions.value = [];
  1549. }
  1550. } catch (error) {
  1551. console.error('获取施工工艺选项失败:', error);
  1552. technologyOptions.value = [];
  1553. } finally {
  1554. loadingTechnologyOptions.value = false;
  1555. }
  1556. }
  1557. // 监听当前任务的变化,设置默认展开的keys
  1558. watch(() => currentTask.value.deptIds, (newVal) => {
  1559. if (newVal && newVal.length > 0) {
  1560. defaultExpandedKeys.value = [...newVal];
  1561. }
  1562. }, { immediate: true, deep: true });
  1563. // 监听部门列表加载完成
  1564. watch(() => deptList.value, (newVal) => {
  1565. if (newVal && newVal.length > 0 && currentTask.value.deptIds && currentTask.value.deptIds.length > 0) {
  1566. defaultExpandedKeys.value = [...currentTask.value.deptIds];
  1567. }
  1568. }, { immediate: true, deep: true });
  1569. // 监听项目列表变化,确保列表加载完成后才执行自动选择
  1570. watch(projectList, (newVal) => {
  1571. if (newVal && newVal.length > 0) {
  1572. // 如果是编辑模式,使用任务数据中的合同ID
  1573. if (id && tableData.value.length > 0 && tableData.value[0].projectId) {
  1574. autoSelectContract(tableData.value[0].projectId.toString());
  1575. }
  1576. // 如果是新增模式且有传递的projectId,使用传递的projectId
  1577. else if (!id && projectId.value) {
  1578. console.log('watch-新增模式且有传递的projectId' + projectId.value + ' projectId not found');
  1579. autoSelectContract(projectId.value);
  1580. }
  1581. }
  1582. });
  1583. // 处理树形下拉框可见性变化
  1584. const handleTreeVisibleChange = (visible: boolean) => {
  1585. if (visible && currentTask.value.deptIds && currentTask.value.deptIds.length > 0) {
  1586. // 当下拉框显示且当前任务有已选部门时,设置默认展开的keys
  1587. defaultExpandedKeys.value = [...currentTask.value.deptIds];
  1588. // 使用nextTick确保DOM更新后再执行展开操作
  1589. nextTick(() => {
  1590. if (treeSelectRef.value) {
  1591. // 尝试访问内部树实例并展开节点
  1592. const treeInstance = treeSelectRef.value.treeRef;
  1593. if (treeInstance && treeInstance.setExpandedKeys) {
  1594. treeInstance.setExpandedKeys(defaultExpandedKeys.value);
  1595. }
  1596. }
  1597. });
  1598. }
  1599. };
  1600. onMounted(async () => {
  1601. // 查询当前登录人所属公司下的所有部门
  1602. deptList.value = handleTree(await DeptApi.companyLevelChildrenDepts())
  1603. let deptId = useUserStore().getUser.deptId
  1604. projectList.value = await IotProjectInfoApi.getIotProjectInfoUser(deptId);
  1605. // 查询当前任务已经选中的设备信息
  1606. open();
  1607. })
  1608. </script>
  1609. <style scoped>
  1610. .edit-input {
  1611. margin-right: 10px;
  1612. }
  1613. .error-message {
  1614. color: #f56c6c;
  1615. font-size: 12px;
  1616. margin-top: 5px;
  1617. }
  1618. .action-cell {
  1619. display: flex;
  1620. gap: 8px;
  1621. }
  1622. /* 1. 穿梭框父容器:居中 + 内边距 */
  1623. .transfer-container {
  1624. text-align: center;
  1625. padding: 0px; /* 上下内边距,避免紧贴对话框边缘 */
  1626. }
  1627. /* 2. 穿梭框组件:控制宽度,避免过窄或过宽 */
  1628. .transfer-component {
  1629. width: 100%; /* 占对话框90%宽度,兼顾美观和内容显示 */
  1630. min-width: 600px;
  1631. }
  1632. /* 3. 直接调整 el-transfer 左右窗口的宽度 */
  1633. :deep(.el-transfer-panel) {
  1634. width: 40% !important; /* 强制设置左右面板宽度,确保两侧面板对等 */
  1635. }
  1636. /* 3. 深度选择器:修改el-transfer选项样式(解决内容省略问题) */
  1637. :deep(.el-transfer-panel__item) {
  1638. /* white-space: nowrap; 文本不换行,保证一行显示 */
  1639. /* overflow: hidden; 超出部分隐藏 */
  1640. /* text-overflow: ellipsis; 超出显示省略号 */
  1641. /* max-width: 100%; 确保文本不超出选项容器宽度 */
  1642. /* padding: 6px 6px; 适当增加内边距,优化点击体验 */
  1643. }
  1644. :deep(.el-transfer-panel__item) {
  1645. display: flex !important;
  1646. align-items: center !important;
  1647. height: 32px !important;
  1648. line-height: 32px !important;
  1649. padding: 0 8px !important;
  1650. margin: 0 !important;
  1651. white-space: nowrap;
  1652. overflow: hidden;
  1653. text-overflow: ellipsis;
  1654. }
  1655. /* 4. 选项文本:确保继承父容器宽度,省略号正常生效 */
  1656. .transfer-option-text {
  1657. display: inline-block;
  1658. max-width: 100%;
  1659. }
  1660. /* 设备名称显示区域样式 */
  1661. .device-names {
  1662. white-space: nowrap;
  1663. overflow: hidden;
  1664. text-overflow: ellipsis;
  1665. max-width: 200px; /* 根据实际列宽调整 */
  1666. }
  1667. :deep(.el-transfer-panel__list) {
  1668. width: 100% !important; /* 使面板内部内容宽度占满 */
  1669. }
  1670. /* 责任人名称显示区域样式 */
  1671. .responsible-names {
  1672. white-space: nowrap;
  1673. overflow: hidden;
  1674. text-overflow: ellipsis;
  1675. max-width: 200px; /* 根据实际列宽调整 */
  1676. }
  1677. /* 添加部门名称的样式 */
  1678. .dept-names {
  1679. white-space: nowrap;
  1680. overflow: hidden;
  1681. text-overflow: ellipsis;
  1682. max-width: 200px; /* 根据实际列宽调整 */
  1683. display: inline-block;
  1684. vertical-align: bottom;
  1685. }
  1686. /* 部门选择器样式优化 */
  1687. :deep(.department-tree-select .el-select__tags) {
  1688. white-space: nowrap;
  1689. overflow: hidden;
  1690. text-overflow: ellipsis;
  1691. flex-wrap: nowrap;
  1692. }
  1693. :deep(.department-tree-select .el-select__tags .el-tag) {
  1694. max-width: 120px;
  1695. overflow: hidden;
  1696. text-overflow: ellipsis;
  1697. display: inline-block;
  1698. }
  1699. :deep(.department-tree-select .el-select__tags .el-tag + .el-tag) {
  1700. margin-left: 6px;
  1701. }
  1702. :deep(.department-tree-select .el-select__tags-text) {
  1703. display: inline-block;
  1704. max-width: 100px;
  1705. overflow: hidden;
  1706. text-overflow: ellipsis;
  1707. }
  1708. /* 当有多个标签被折叠时显示的 "+N" 标签样式 */
  1709. :deep(.department-tree-select .el-select__collapse-tags) {
  1710. white-space: nowrap;
  1711. display: inline-block;
  1712. }
  1713. .task-edit-form {
  1714. margin-bottom: 20px;
  1715. padding: 20px;
  1716. border: 1px solid #ebeef5;
  1717. border-radius: 4px;
  1718. background-color: #fafafa;
  1719. }
  1720. </style>