IotProjectTaskForm.vue 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336
  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="结算方式" prop="payment" >
  46. <el-input v-model="formData.payment" placeholder="请输入结算方式" disabled/>
  47. </el-form-item> -->
  48. <el-form-item :label="t('project.payment')" prop="payment">
  49. <el-select v-model="formData.payment" placeholder="请选择" disabled>
  50. <el-option
  51. v-for="dict in getStrDictOptions(DICT_TYPE.PMS_PROJECT_SETTLEMENT)"
  52. :key="dict.id"
  53. :label="dict.label"
  54. :value="dict.value"
  55. />
  56. </el-select>
  57. </el-form-item>
  58. </el-col>
  59. </el-row>
  60. <el-row>
  61. <el-col :span="12">
  62. <el-form-item label="总数" prop="workloadTotal">
  63. <el-input v-model="formData.workloadTotal" placeholder="请输入工作量总数" disabled/>
  64. </el-form-item>
  65. </el-col>
  66. <el-col :span="12">
  67. <el-form-item label="已完成" prop="workloadFinish">
  68. <el-input v-model="formData.workloadFinish" placeholder="已完成工作量" disabled/>
  69. </el-form-item>
  70. </el-col>
  71. </el-row>
  72. <el-row>
  73. <el-col :span="12">
  74. <el-form-item label="开始时间" prop="startTime">
  75. <el-date-picker
  76. style="width: 150%"
  77. v-model="formData.startTime"
  78. type="date"
  79. value-format="x"
  80. placeholder="选择开始时间"
  81. disabled
  82. />
  83. </el-form-item>
  84. </el-col>
  85. <el-col :span="12">
  86. <el-form-item label="完成时间" prop="endTime">
  87. <el-date-picker
  88. style="width: 150%"
  89. v-model="formData.endTime"
  90. type="date"
  91. value-format="x"
  92. placeholder="选择完成时间"
  93. disabled
  94. />
  95. </el-form-item>
  96. </el-col>
  97. </el-row>
  98. <el-form-item label="备注" prop="remark">
  99. <el-input v-model="formData.remark" placeholder="请输入备注" type="textarea" disabled/>
  100. </el-form-item>
  101. </el-form>
  102. </ContentWrap>
  103. <ContentWrap>
  104. <div class="content">
  105. <div class="toolbar">
  106. <div class="actions">
  107. <!--
  108. <el-button type="primary" @click="addNewRow" icon="plus" >
  109. 新增行
  110. </el-button>
  111. <el-button type="success" @click="saveAll" :disabled="editingRowsCount === 0" icon="check">
  112. 保存所有更改
  113. </el-button> -->
  114. </div>
  115. </div>
  116. <div class="table-container">
  117. <el-table :data="tableData" :row-class-name="rowClassName" empty-text="暂无数据">
  118. <el-table-column prop="wellName" label="井号">
  119. <template #default="{ row }">
  120. <span v-if="!row.editing">{{ row.wellName }}</span>
  121. <div v-else>
  122. <el-input v-model="row.editData.wellName" class="edit-input" placeholder="请输入井号" />
  123. <div v-if="row.errors.wellName" class="error-message">{{ row.errors.wellName }}</div>
  124. </div>
  125. </template>
  126. </el-table-column>
  127. <!-- 修改井型/井别列 -->
  128. <el-table-column prop="wellType" label="井型">
  129. <template #default="{ row }">
  130. <span v-if="!row.editing">{{ getWellTypeLabel(row.wellType) }}</span>
  131. <div v-else>
  132. <el-select v-model="row.editData.wellType" placeholder="请选择井型" clearable>
  133. <el-option
  134. v-for="dict in getStrDictOptions(DICT_TYPE.PMS_PROJECT_WELL_TYPE)"
  135. :key="dict.value"
  136. :label="dict.label"
  137. :value="dict.value"
  138. />
  139. </el-select>
  140. <div v-if="row.errors.wellType" class="error-message">{{ row.errors.wellType }}</div>
  141. </div>
  142. </template>
  143. </el-table-column>
  144. <!-- 新增井别列 -->
  145. <el-table-column prop="wellCategory" label="井别">
  146. <template #default="{ row }">
  147. <span v-if="!row.editing">{{ getWellCategoryLabel(row.wellCategory) }}</span>
  148. <div v-else>
  149. <el-select v-model="row.editData.wellCategory" placeholder="请选择井别" clearable>
  150. <el-option
  151. v-for="dict in getStrDictOptions(DICT_TYPE.PMS_PROJECT_WELL_CATEGORY)"
  152. :key="dict.value"
  153. :label="dict.label"
  154. :value="dict.value"
  155. />
  156. </el-select>
  157. <div v-if="row.errors.wellCategory" class="error-message">{{ row.errors.wellCategory }}</div>
  158. </div>
  159. </template>
  160. </el-table-column>
  161. <el-table-column prop="location" label="施工地点" >
  162. <template #default="{ row }">
  163. <span v-if="!row.editing">{{ row.location }}</span>
  164. <div v-else>
  165. <el-input v-model="row.editData.location" class="edit-input" placeholder="请输入施工地点" />
  166. <div v-if="row.errors.location" class="error-message">{{ row.errors.location }}</div>
  167. </div>
  168. </template>
  169. </el-table-column>
  170. <el-table-column prop="technique" label="施工工艺">
  171. <template #default="{ row }">
  172. <span v-if="!row.editing">{{ getTechniqueLabel(row.technique) }}</span>
  173. <div v-else>
  174. <el-select v-model="row.editData.technique" placeholder="请选择施工工艺" clearable>
  175. <el-option
  176. v-for="dict in getStrDictOptions(DICT_TYPE.PMS_PROJECT_TECHNOLOGY)"
  177. :key="dict.value"
  178. :label="dict.label"
  179. :value="dict.value"
  180. />
  181. </el-select>
  182. <div v-if="row.errors.technique" class="error-message">{{ row.errors.technique }}</div>
  183. </div>
  184. </template>
  185. </el-table-column>
  186. <el-table-column prop="workloadDesign" label="设计工作量">
  187. <template #default="{ row }">
  188. <span v-if="!row.editing">{{ row.workloadDesign }}</span>
  189. <div v-else>
  190. <el-input v-model="row.editData.workloadDesign" class="edit-input" placeholder="请输入设计工作量" />
  191. <div v-if="row.errors.workloadDesign" class="error-message">{{ row.errors.workloadDesign }}</div>
  192. </div>
  193. </template>
  194. </el-table-column>
  195. <el-table-column prop="deptIds" label="施工队伍" >
  196. <template #default="{ row }">
  197. <span v-if="!row.editing">
  198. <el-tooltip
  199. effect="dark"
  200. :content="getAllDeptNames(row.deptIds)"
  201. placement="top"
  202. :disabled="!row.deptIds || row.deptIds.length <= 1"
  203. >
  204. <span class="dept-names">
  205. {{ getDeptNames(row.deptIds) }}
  206. </span>
  207. </el-tooltip>
  208. </span>
  209. <div v-else>
  210. <el-tree-select
  211. multiple
  212. v-model="row.editData.deptIds"
  213. :data="deptList"
  214. :props="defaultProps"
  215. check-strictly
  216. node-key="id"
  217. filterable
  218. placeholder="请选择施工队伍"
  219. clearable
  220. />
  221. <div v-if="row.errors.deptIds" class="error-message">{{ row.errors.deptIds }}</div>
  222. </div>
  223. </template>
  224. </el-table-column>
  225. <el-table-column prop="deviceIds" label="施工设备" >
  226. <template #default="{ row }">
  227. <el-tooltip
  228. :content="getAllDeviceNames(row.deviceIds)"
  229. placement="top"
  230. :disabled="row.deviceIds && row.deviceIds.length <= 1"
  231. >
  232. <span v-if="!row.editing" class="device-names">
  233. {{ getDeviceNames(row.deviceIds) }}
  234. </span>
  235. <div v-else>
  236. <el-button
  237. @click="openDeviceDialog(row)"
  238. type="primary"
  239. size="small"
  240. >
  241. 选择设备
  242. </el-button>
  243. <div v-if="row.errors.deviceIds" class="error-message">{{ row.errors.deviceIds }}</div>
  244. </div>
  245. </el-tooltip>
  246. </template>
  247. </el-table-column>
  248. <el-table-column prop="responsiblePerson" label="责任人">
  249. <template #default="{ row }">
  250. <el-tooltip
  251. :content="getAllResponsiblePersonNames(row.responsiblePerson)"
  252. placement="top"
  253. :disabled="!row.responsiblePerson || row.responsiblePerson.length <= 1"
  254. >
  255. <span v-if="!row.editing" class="responsible-names">
  256. {{ getResponsiblePersonNames(row.responsiblePerson) }}
  257. </span>
  258. <div v-else>
  259. <el-button
  260. @click="openResponsiblePersonDialog(row)"
  261. type="primary"
  262. size="small"
  263. >
  264. 选择责任人
  265. </el-button>
  266. <div v-if="row.errors.responsiblePerson" class="error-message">{{ row.errors.responsiblePerson }}</div>
  267. </div>
  268. </el-tooltip>
  269. </template>
  270. </el-table-column>
  271. <el-table-column prop="remark" label="备注">
  272. <template #default="{ row }">
  273. <span v-if="!row.editing">{{ row.remark }}</span>
  274. <div v-else>
  275. <el-input v-model="row.editData.remark" class="edit-input" placeholder="请输入备注" />
  276. <div v-if="row.errors.remark" class="error-message">{{ row.errors.remark }}</div>
  277. </div>
  278. </template>
  279. </el-table-column>
  280. <el-table-column label="操作" width="200" fixed="right">
  281. <template #default="{ row, $index }">
  282. <div class="action-cell">
  283. <template v-if="!row.editing">
  284. <el-button size="small" type="primary" @click="editRow(row)" icon="edit">编辑</el-button>
  285. <el-button size="small" type="danger" @click="deleteRow($index)" icon="delete">删除</el-button>
  286. </template>
  287. <template v-else>
  288. <el-button size="small" type="success" @click="saveRow(row)" icon="check">保存</el-button>
  289. <el-button size="small" @click="cancelEdit(row)" icon="close">取消</el-button>
  290. </template>
  291. </div>
  292. </template>
  293. </el-table-column>
  294. </el-table>
  295. </div>
  296. </div>
  297. </ContentWrap>
  298. <ContentWrap>
  299. <el-form style="float: right">
  300. <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
  301. <el-button @click="close">取 消</el-button>
  302. </el-form>
  303. </ContentWrap>
  304. <!-- 设备选择对话框 -->
  305. <el-dialog
  306. v-model="deviceDialogVisible"
  307. title="选择施工设备"
  308. width="1000px"
  309. :before-close="handleDeviceDialogClose"
  310. class="device-select-dialog"
  311. >
  312. <!-- 父容器加text-center确保穿梭框居中,移除原text-align:left -->
  313. <!-- 移除text-align:left,保留inline-block让父容器text-center生效 -->
  314. <div class="transfer-container">
  315. <el-transfer
  316. v-model="selectedDeviceIds"
  317. :data="filteredDeviceList"
  318. :titles="['可选设备', '已选设备']"
  319. :props="{ key: 'id', label: 'deviceCode' }"
  320. filterable
  321. class="transfer-component"
  322. @change="handleTransferChange"
  323. >
  324. <template #default="{ option }">
  325. <!-- 无内容时禁用tooltip -->
  326. <el-tooltip
  327. effect="dark"
  328. placement="top"
  329. :content="`${option.deviceCode || ''} - ${option.deviceName || ''}`"
  330. :disabled="!option.deviceCode && !option.deviceName"
  331. transition="fade-in-linear"
  332. >
  333. <span class="transfer-option-text">
  334. {{ option.deviceCode }} - {{ option.deviceName }}
  335. </span>
  336. </el-tooltip>
  337. </template>
  338. </el-transfer>
  339. </div>
  340. <template #footer>
  341. <span class="dialog-footer">
  342. <el-button @click="handleDeviceDialogClose">取消</el-button>
  343. <el-button type="primary" @click="confirmDeviceSelection">确定</el-button>
  344. </span>
  345. </template>
  346. </el-dialog>
  347. <!-- 新增责任人选择对话框 -->
  348. <el-dialog
  349. v-model="responsiblePersonDialogVisible"
  350. title="选择责任人"
  351. width="1000px"
  352. :before-close="handleResponsiblePersonDialogClose"
  353. class="responsible-person-select-dialog"
  354. >
  355. <div class="transfer-container">
  356. <el-transfer
  357. v-model="selectedResponsiblePersonIds"
  358. :data="responsiblePersonList"
  359. :titles="['可选人员', '已选人员']"
  360. :props="{ key: 'id', label: 'nickname' }"
  361. filterable
  362. class="transfer-component"
  363. >
  364. <template #default="{ option }">
  365. <el-tooltip
  366. effect="dark"
  367. placement="top"
  368. :content="`${option.nickname} - ${option.deptName || '未分配部门'}`"
  369. >
  370. <span class="transfer-option-text">
  371. {{ option.nickname }} - {{ option.deptName || '未分配部门' }}
  372. </span>
  373. </el-tooltip>
  374. </template>
  375. </el-transfer>
  376. </div>
  377. <template #footer>
  378. <span class="dialog-footer">
  379. <el-button @click="handleResponsiblePersonDialogClose">取消</el-button>
  380. <el-button type="primary" @click="confirmResponsiblePersonSelection">确定</el-button>
  381. </span>
  382. </template>
  383. </el-dialog>
  384. </template>
  385. <script setup lang="ts">
  386. import { IotProjectInfoApi, IotProjectInfoVO } from '@/api/pms/iotprojectinfo'
  387. import {defaultProps,handleTree} from "@/utils/tree";
  388. import * as DeptApi from "@/api/system/dept";
  389. import CustomerList from '@/views/pms/device/CustomerList.vue'
  390. import {ref, reactive, computed, onMounted, watch} from "vue";
  391. import {useUserStore} from "@/store/modules/user";
  392. import {IotProjectTaskApi, IotProjectTaskVO} from "@/api/pms/iotprojecttask";
  393. import {ElMessageBox, ElMessage} from "element-plus";
  394. import {useTagsViewStore} from "@/store/modules/tagsView";
  395. import {companyLevelChildrenDepts} from "@/api/system/dept";
  396. import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
  397. import * as UserApi from "@/api/system/user";
  398. import {UserVO} from "@/api/system/user";
  399. import {DICT_TYPE, getIntDictOptions, getStrDictOptions} from "@/utils/dict";
  400. const { query, params, name } = useRoute() // 查询参数
  401. const id = params.id
  402. // 修改projectId获取逻辑:优先使用params.projectId,其次使用query.projectId
  403. const projectId = ref<string>('')
  404. // 获取projectId的逻辑
  405. if (params.projectId) {
  406. projectId.value = Array.isArray(params.projectId) ? params.projectId[0] : params.projectId
  407. } else if (query.projectId) {
  408. projectId.value = Array.isArray(query.projectId) ? query.projectId[0] : query.projectId as string
  409. }
  410. const { delView } = useTagsViewStore() // 视图操作
  411. const { currentRoute, push } = useRouter()
  412. const { t } = useI18n() // 国际化
  413. const message = useMessage() // 消息弹窗
  414. const deptList = ref<Tree[]>([]) // 树形结构
  415. const deviceList = ref<IotDeviceVO[]>([]) // 设备列表
  416. // 设备映射表(ID->设备对象)
  417. const deviceMap = ref<Record<number, IotDeviceVO>>({});
  418. const dialogVisible = ref(true) // 弹窗的是否展示
  419. const dialogTitle = ref('') // 弹窗的标题
  420. const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  421. const taskList = ref<IotProjectTaskVO[]>([]) // 列表的数据
  422. const projectList = ref<IotProjectInfoVO[]>([])
  423. const formType = ref('') // 表单的类型:create - 新增;update - 修改
  424. const loading = ref(true) // 列表的加载中
  425. // 新增责任人相关变量
  426. const responsiblePersonDialogVisible = ref(false);
  427. const responsiblePersonList = ref([]); // 所有责任人列表
  428. const selectedResponsiblePersonIds = ref([]); // 选中的责任人ID
  429. const currentEditingRowForResponsible = ref(null); // 当前正在编辑责任人的行
  430. /** 项目信息 表单 */
  431. defineOptions({ name: 'IotProjectTaskInfo' })
  432. const formData = ref({
  433. id: undefined,
  434. contractId: undefined,
  435. contractName: undefined,
  436. contractCode: undefined,
  437. workloadTotal: undefined,
  438. workloadFinish: undefined,
  439. startTime: undefined,
  440. endTime: undefined,
  441. location: undefined,
  442. technique: undefined,
  443. payment: undefined,
  444. remark:undefined,
  445. manufactureName: undefined,
  446. manufacturerId: undefined,
  447. userName: undefined,
  448. userId: undefined,
  449. deptId: undefined, // 新增项目部门ID字段
  450. })
  451. const close = () => {
  452. delView(unref(currentRoute))
  453. push({ name: 'IotProjectTask', params:{}})
  454. }
  455. const getProjectInfo = (contractId: number) => {
  456. const project = projectList.value.find(item => item.id === contractId);
  457. if (project) {
  458. formData.value.contractName = project.contractName;
  459. formData.value.contractCode = project.contractCode;
  460. formData.value.payment = project.payment;
  461. formData.value.workloadTotal = project.workloadTotal;
  462. formData.value.startTime = project.startTime;
  463. formData.value.endTime = project.endTime;
  464. formData.value.remark = project.remark;
  465. formData.value.manufactureName = project.manufactureName;
  466. formData.value.manufacturerId = project.manufacturerId;
  467. formData.value.id = project.id;
  468. formData.value.deptId = project.deptId; // 保存项目部门ID
  469. }
  470. }
  471. const queryParams = reactive({
  472. projectId:undefined,
  473. id:undefined
  474. })
  475. const formRules = reactive({
  476. contractId: [{ required: true, message: '合同名称不能为空', trigger: 'blur' }],
  477. manufacturerId: [{ required: true, message: '客户不能为空', trigger: 'blur' }],
  478. payment: [{ required: true, message: '结算方式不能为空', trigger: 'blur' }],
  479. workloadTotal: [{ required: true, message: '工作量总数不能为空', trigger: 'blur' }],
  480. startTime: [{ required: true, message: '开始时间不能为空', trigger: 'blur' }],
  481. endTime: [{ required: true, message: '结束时间不能为空', trigger: 'blur' }],
  482. })
  483. const formRef = ref() // 表单 Ref
  484. const zzClear = () =>{
  485. formData.value.manufacturerId = undefined
  486. formData.value.manufactureName = undefined
  487. }
  488. // 根据部门ID获取部门名称
  489. const getDeptNames = (deptIds) => {
  490. if (!deptIds || deptIds.length === 0) return '';
  491. const names = [];
  492. const findDept = (list, id) => {
  493. for (const item of list) {
  494. if (item.id === id) {
  495. names.push(item.name);
  496. return true;
  497. }
  498. if (item.children && findDept(item.children, id)) {
  499. return true;
  500. }
  501. }
  502. return false;
  503. };
  504. deptIds.forEach(id => findDept(deptList.value, id));
  505. if (names.length > 1) {
  506. return `${names[0]}...`;
  507. }
  508. return names.join(', ');
  509. };
  510. const getAllDeptNames = (deptIds) => {
  511. if (!deptIds || deptIds.length === 0) return '无施工队伍';
  512. const names = [];
  513. const findDept = (list, id) => {
  514. for (const item of list) {
  515. if (item.id === id) {
  516. names.push(item.name);
  517. return true;
  518. }
  519. if (item.children && findDept(item.children, id)) {
  520. return true;
  521. }
  522. }
  523. return false;
  524. };
  525. deptIds.forEach(id => findDept(deptList.value, id));
  526. return names.join(', ') || '无有效施工队伍';
  527. };
  528. const getDeviceNames = (deviceIds: number[]) => {
  529. if (!deviceIds || deviceIds.length === 0) return '';
  530. // 获取所有有效设备名称
  531. const deviceNames = deviceIds
  532. .map(id => deviceMap.value[id]?.deviceCode)
  533. .filter(name => name !== undefined && name !== '');
  534. if (deviceNames.length === 0) return '';
  535. // 如果设备数量超过2个,显示前两个加省略号
  536. if (deviceNames.length > 2) {
  537. return `${deviceNames[0]}, ${deviceNames[1]}...`;
  538. }
  539. // 设备数量不超过2个,正常显示所有
  540. return deviceNames.join(', ');
  541. };
  542. const tableData = ref([
  543. {
  544. id: 1,
  545. deptId:'',
  546. wellName: '',
  547. wellType: '',
  548. wellCategory: '',
  549. location: '',
  550. technique: '',
  551. workloadDesign: '',
  552. deptIds: [],
  553. deviceIds: [],
  554. deviceCodes: '',
  555. remark:'',
  556. editData: {},
  557. editing:true,
  558. originalData: {},
  559. errors: {},
  560. projectId: ''
  561. },
  562. ]);
  563. // 打开责任人选择对话框
  564. const openResponsiblePersonDialog = async (row) => {
  565. if (!formData.value.deptId) {
  566. ElMessage.warning('请先选择合同以确定项目部门');
  567. return;
  568. }
  569. currentEditingRowForResponsible.value = row;
  570. selectedResponsiblePersonIds.value = [...(row.editData.responsiblePerson || [])];
  571. try {
  572. const params = {
  573. deptIds: [formData.value.deptId] // 使用当前项目所属部门的deptId
  574. };
  575. const response = await UserApi.companyDeptsEmployee(params);
  576. responsiblePersonList.value = response;
  577. responsiblePersonDialogVisible.value = true;
  578. } catch (error) {
  579. ElMessage.error('获取责任人列表失败');
  580. console.error('获取责任人列表失败:', error);
  581. }
  582. };
  583. // 确认责任人选择
  584. const confirmResponsiblePersonSelection = () => {
  585. if (currentEditingRowForResponsible.value) {
  586. // 更新责任人ID
  587. currentEditingRowForResponsible.value.editData.responsiblePerson = [...selectedResponsiblePersonIds.value];
  588. // 更新责任人名称显示
  589. const selectedPersons = responsiblePersonList.value.filter(person =>
  590. selectedResponsiblePersonIds.value.includes(person.id)
  591. );
  592. currentEditingRowForResponsible.value.editData.responsiblePersonNames = selectedPersons
  593. .map(person => person.nickname)
  594. .join(', ');
  595. }
  596. responsiblePersonDialogVisible.value = false;
  597. };
  598. // 关闭责任人选择对话框
  599. const handleResponsiblePersonDialogClose = () => {
  600. responsiblePersonDialogVisible.value = false;
  601. };
  602. // 根据责任人ID获取责任人名称
  603. const getResponsiblePersonNames = (responsiblePersonIds: number[]) => {
  604. if (!responsiblePersonIds || responsiblePersonIds.length === 0) return '';
  605. // 获取所有有效责任人名称
  606. const personNames = responsiblePersonIds
  607. .map(id => {
  608. const person = responsiblePersonList.value.find(p => p.id === id);
  609. return person ? person.nickname : '';
  610. })
  611. .filter(name => name !== undefined && name !== '');
  612. if (personNames.length === 0) return '';
  613. // 如果责任人数量超过2个,显示前两个加省略号
  614. if (personNames.length > 2) {
  615. return `${personNames[0]}, ${personNames[1]}...`;
  616. }
  617. // 责任人数量不超过2个,正常显示所有
  618. return personNames.join(', ');
  619. };
  620. // 获取所有责任人名称(用于tooltip提示)
  621. const getAllResponsiblePersonNames = (responsiblePersonIds: number[]) => {
  622. if (!responsiblePersonIds || responsiblePersonIds.length === 0) return '无责任人';
  623. const personNames = responsiblePersonIds
  624. .map(id => {
  625. const person = responsiblePersonList.value.find(p => p.id === id);
  626. return person ? person.nickname : '未知人员';
  627. })
  628. .filter(name => name !== '未知人员');
  629. return personNames.join(', ') || '无有效责任人';
  630. };
  631. // 设备选择相关变量
  632. const deviceDialogVisible = ref(false);
  633. const currentEditingRow = ref(null);
  634. const selectedDeviceIds = ref([]);
  635. const filteredDeviceList = ref([]);
  636. // 打开设备选择对话框
  637. const openDeviceDialog = async (row) => {
  638. if (!row.editData.deptIds || row.editData.deptIds.length === 0) {
  639. ElMessage.warning('请先选择施工队伍');
  640. return;
  641. }
  642. currentEditingRow.value = row;
  643. selectedDeviceIds.value = [...(row.editData.deviceIds || [])];
  644. try {
  645. const params = {
  646. deptIds: row.editData.deptIds
  647. };
  648. const data = await IotDeviceApi.getDevicesByDepts(params);
  649. filteredDeviceList.value = data;
  650. deviceDialogVisible.value = true;
  651. } catch (error) {
  652. ElMessage.error('获取设备列表失败');
  653. console.error('获取设备列表失败:', error);
  654. }
  655. };
  656. // 处理穿梭框变化
  657. const handleTransferChange = (value, direction, movedKeys) => {
  658. // 可以添加额外的处理逻辑
  659. };
  660. // 确认设备选择
  661. const confirmDeviceSelection = () => {
  662. if (currentEditingRow.value) {
  663. selectedDeviceIds.value.forEach(id => {
  664. const device = filteredDeviceList.value.find(d => d.id === id);
  665. if (device && !deviceMap.value[id]) {
  666. deviceMap.value[id] = device;
  667. }
  668. });
  669. // 更新设备ID
  670. currentEditingRow.value.editData.deviceIds = [...selectedDeviceIds.value];
  671. // 更新设备代码显示
  672. const selectedDevices = filteredDeviceList.value.filter(device =>
  673. selectedDeviceIds.value.includes(device.id)
  674. );
  675. currentEditingRow.value.editData.deviceCodes = selectedDevices
  676. .map(device => device.deviceCode)
  677. .join(', ');
  678. }
  679. deviceDialogVisible.value = false;
  680. };
  681. // 关闭设备选择对话框
  682. const handleDeviceDialogClose = () => {
  683. deviceDialogVisible.value = false;
  684. };
  685. // 获取井型标签的方法
  686. const getWellTypeLabel = (value) => {
  687. const options = getStrDictOptions(DICT_TYPE.PMS_PROJECT_WELL_TYPE)
  688. const option = options.find(opt => opt.value === value)
  689. return option ? option.label : value
  690. }
  691. // 获取井别标签的方法
  692. const getWellCategoryLabel = (value) => {
  693. const options = getStrDictOptions(DICT_TYPE.PMS_PROJECT_WELL_CATEGORY)
  694. const option = options.find(opt => opt.value === value)
  695. return option ? option.label : value
  696. }
  697. // 获取施工工艺标签的方法
  698. const getTechniqueLabel = (value) => {
  699. const options = getStrDictOptions(DICT_TYPE.PMS_PROJECT_TECHNOLOGY)
  700. const option = options.find(opt => opt.value === value)
  701. return option ? option.label : value
  702. }
  703. /** 打开弹窗 */
  704. const open = async () => {
  705. resetForm()
  706. // 修改时,设置数据
  707. if (id) {
  708. formType.value = 'update';
  709. formLoading.value = true
  710. try {
  711. queryParams.id = id;
  712. const data = await IotProjectTaskApi.getIotProjectTaskPage(queryParams);
  713. tableData.value = data.list
  714. // 1. 收集所有设备ID
  715. const allDeviceIds = new Set<number>();
  716. // 收集所有责任人ID
  717. const allResponsiblePersonIds = new Set<number>();
  718. data.list.forEach(item => {
  719. if (item.deviceIds?.length) {
  720. item.deviceIds.forEach(id => allDeviceIds.add(id));
  721. }
  722. if (item.responsiblePerson?.length) {
  723. item.responsiblePerson.forEach(id => allResponsiblePersonIds.add(id));
  724. }
  725. });
  726. // 2. 批量获取设备信息
  727. if (allDeviceIds.size > 0) {
  728. const deviceIdsArray = Array.from(allDeviceIds);
  729. const devices = await IotDeviceApi.getDevicesByDepts({
  730. deviceIds: deviceIdsArray
  731. });
  732. // 3. 更新设备列表和映射表
  733. deviceList.value = devices;
  734. deviceMap.value = {};
  735. devices.forEach(device => {
  736. deviceMap.value[device.id] = device;
  737. });
  738. }
  739. // 批量获取责任人信息
  740. if (allResponsiblePersonIds.size > 0) {
  741. const personIdsArray = Array.from(allResponsiblePersonIds);
  742. const params = {
  743. userIds: personIdsArray
  744. };
  745. const persons = await UserApi.companyDeptsEmployee(params);
  746. // 更新责任人列表
  747. responsiblePersonList.value = persons;
  748. }
  749. tableData.value.forEach(item=>{
  750. item.editData = {
  751. ...item,
  752. deviceIds: item.deviceIds ? [...item.deviceIds] : []
  753. };
  754. item.originalData={};
  755. item.errors={};
  756. })
  757. } finally {
  758. formLoading.value = false
  759. }
  760. } else {
  761. formType.value = 'create';
  762. }
  763. // 如果有projectId参数,自动选中对应的合同
  764. if (projectId.value) {
  765. autoSelectContract(projectId.value);
  766. // 自动新增一行空记录
  767. if (tableData.value.length === 0) {
  768. addNewRow();
  769. }
  770. }
  771. }
  772. defineExpose({ open }) // 提供 open 方法,用于打开弹窗
  773. /** 自动选择合同 */
  774. const autoSelectContract = async (projectId: string) => {
  775. // 等待项目列表加载完成
  776. // await waitForProjectList();
  777. console.log('项目id:' + projectId);
  778. // 查找匹配的合同
  779. const project = projectList.value.find(item => item.id === Number(projectId));
  780. if (project) {
  781. // 设置选中值
  782. formData.value.contractId = project.id;
  783. // 触发合同信息加载
  784. getProjectInfo(project.id);
  785. } else {
  786. ElMessage.warning('未找到指定的合同信息');
  787. }
  788. }
  789. /** 等待项目列表加载完成 */
  790. /* const waitForProjectList = () => {
  791. return new Promise(resolve => {
  792. const checkInterval = setInterval(() => {
  793. if (projectList.value.length > 0) {
  794. clearInterval(checkInterval);
  795. resolve(true);
  796. }
  797. }, 100);
  798. });
  799. } */
  800. /** 校验所有行数据 */
  801. const validateAllRows = (): boolean => {
  802. // 新增检查:判断任务列表是否为空
  803. if (!tableData.value || tableData.value.length === 0) {
  804. ElMessage.error('没有任务数据,无法保存');
  805. return false;
  806. }
  807. let allValid = true;
  808. tableData.value.forEach(row => {
  809. // 对每一行进行校验
  810. const rowValid = validateRow(row);
  811. if (!rowValid) {
  812. allValid = false;
  813. }
  814. // 如果是编辑中的行,需要额外检查编辑数据
  815. if (row.editing) {
  816. if (!row.editData.wellName || row.editData.wellName.trim() === '') {
  817. row.errors.wellName = '井号不能为空';
  818. allValid = false;
  819. }
  820. if (!row.editData.wellType || row.editData.wellType.trim() === '') {
  821. row.errors.wellType = '井型不能为空';
  822. allValid = false;
  823. }
  824. }
  825. });
  826. return allValid;
  827. };
  828. /** 提交表单 */
  829. const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
  830. const submitForm = async () => {
  831. // 校验表单
  832. await formRef.value.validate()
  833. // 校验所有行数据
  834. if (!validateAllRows()) {
  835. // ElMessage.error('请检查表格中的数据,部分必填项未填写或格式不正确');
  836. return;
  837. }
  838. // 提交请求
  839. formLoading.value = true
  840. try {
  841. tableData.value.forEach(item=>{
  842. item.projectId = formData.value.id;
  843. // item.deptId = useUserStore().getUser.deptId;
  844. })
  845. const data = {
  846. taskList: tableData.value
  847. }
  848. if (formType.value === 'create') {
  849. await IotProjectTaskApi.createIotProjectTask(data)
  850. message.success(t('common.createSuccess'))
  851. } else {
  852. await IotProjectTaskApi.updateIotProjectTask(data)
  853. message.success(t('common.updateSuccess'))
  854. }
  855. // 发送操作成功的事件
  856. emit('success')
  857. close()
  858. } finally {
  859. formLoading.value = false
  860. }
  861. }
  862. /** 重置表单 */
  863. const resetForm = () => {
  864. formData.value = {
  865. id: undefined,
  866. contractId: undefined,
  867. contractName: undefined,
  868. contractCode: undefined,
  869. workloadTotal: undefined,
  870. workloadFinish: undefined,
  871. startTime: undefined,
  872. endTime: undefined,
  873. location: undefined,
  874. technique: undefined,
  875. payment: undefined,
  876. remark: undefined,
  877. manufactureName: undefined,
  878. manufacturerId: undefined,
  879. userName: undefined,
  880. userId: undefined,
  881. }
  882. formRef.value?.resetFields()
  883. }
  884. // 监听项目列表变化,确保列表加载完成后才执行自动选择
  885. watch(projectList, (newVal) => {
  886. if (newVal && newVal.length > 0) {
  887. // 如果是编辑模式,使用任务数据中的合同ID
  888. if (id && tableData.value.length > 0 && tableData.value[0].projectId) {
  889. autoSelectContract(tableData.value[0].projectId.toString());
  890. }
  891. // 如果是新增模式且有传递的projectId,使用传递的projectId
  892. else if (!id && projectId.value) {
  893. console.log('watch-新增模式且有传递的projectId' + projectId.value + ' projectId not found');
  894. autoSelectContract(projectId.value);
  895. }
  896. }
  897. });
  898. onMounted(async () => {
  899. // deptList.value = handleTree(await DeptApi.getSimpleDeptList())
  900. // 查询当前登录人所属公司下的所有部门
  901. deptList.value = handleTree(await DeptApi.companyLevelChildrenDepts())
  902. let deptId = useUserStore().getUser.deptId
  903. projectList.value = await IotProjectInfoApi.getIotProjectInfoUser(deptId);
  904. // 查询当前任务已经选中的设备信息
  905. open();
  906. // 如果有项目 ID 但合同未选中,尝试再次选择
  907. /* if (projectId && !formData.value.contractId) {
  908. setTimeout(() => autoSelectContract(projectId), 500);
  909. } */
  910. })
  911. // 计算正在编辑的行数
  912. const editingRowsCount = computed(() => {
  913. return tableData.value.filter(row => row.editing).length;
  914. });
  915. // 格式化日期
  916. const formatDate = (timestamp) => {
  917. const date = new Date(timestamp);
  918. return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
  919. };
  920. // 为编辑中的行添加特殊样式
  921. const rowClassName = ({ row }) => {
  922. return row.editing ? 'editable-row' : '';
  923. };
  924. // 添加新行
  925. const addNewRow = () => {
  926. const newId = tableData.value.length > 0
  927. ? Math.max(...tableData.value.map(item => item.id)) + 1
  928. : 1;
  929. const newRow = {
  930. id: newId,
  931. wellName: '',
  932. wellType: '',
  933. location: '',
  934. technique: '',
  935. workloadDesign: '',
  936. deptIds: [],
  937. deviceIds: [],
  938. deviceCodes: '',
  939. responsiblePerson: [], // 新增责任人字段
  940. responsiblePersonNames: '', // 新增责任人名称字段
  941. remark:'',
  942. editing: true,
  943. editData: {
  944. wellName: '',
  945. wellType: '',
  946. wellCategory: '',
  947. location: '',
  948. technique: '',
  949. workloadDesign: '',
  950. deptIds: [],
  951. deviceIds: [],
  952. deviceCodes: '',
  953. responsiblePerson: [], // 新增责任人字段
  954. responsiblePersonNames: '', // 新增责任人名称字段
  955. remark:'',
  956. },
  957. originalData: {},
  958. errors: {}
  959. };
  960. tableData.value.unshift(newRow);
  961. };
  962. // 编辑行
  963. const editRow = (row) => {
  964. // 保存原始数据用于取消编辑时恢复
  965. row.originalData = {...row};
  966. // 设置编辑数据
  967. row.editData = {
  968. id: row.id,
  969. wellName: row.wellName,
  970. wellType: row.wellType,
  971. wellCategory: row.wellCategory,
  972. location: row.location,
  973. technique: row.technique,
  974. workloadDesign: row.workloadDesign,
  975. deptIds: row.deptIds,
  976. deviceIds: row.deviceIds,
  977. deviceCodes: row.deviceCodes,
  978. responsiblePerson: row.responsiblePerson ? [...row.responsiblePerson] : [], // 复制责任人数组
  979. responsiblePersonNames: row.responsiblePersonNames, // 复制责任人名称
  980. remark: row.remark,
  981. };
  982. // 进入编辑状态
  983. row.editing = true;
  984. // 清空错误信息
  985. row.errors = {};
  986. };
  987. // 验证行数据
  988. const validateRow = (row) => {
  989. row.errors = {};
  990. let valid = true;
  991. if (!row.editData.wellName || row.editData.wellName.trim() === '') {
  992. row.errors.wellName = '井号不能为空';
  993. valid = false;
  994. }
  995. if (!row.editData.wellType || row.editData.wellType.trim() === '') {
  996. row.errors.wellType = '井型不能为空';
  997. valid = false;
  998. }
  999. // 施工地点
  1000. if (!row.editData.location || row.editData.location.trim() === '') {
  1001. row.errors.location = '施工地点不能为空';
  1002. valid = false;
  1003. }
  1004. if (!row.editData.technique || row.editData.technique.trim() === '') {
  1005. row.errors.technique = '施工工艺不能为空';
  1006. valid = false;
  1007. }
  1008. // 施工队伍 (deptIds 是数组)
  1009. if (!row.editData.deptIds || row.editData.deptIds.length === 0) {
  1010. row.errors.deptIds = '施工队伍不能为空';
  1011. valid = false;
  1012. }
  1013. // 施工设备 (deviceIds 是数组)
  1014. if (!row.editData.deviceIds || row.editData.deviceIds.length === 0) {
  1015. row.errors.deviceIds = '施工设备不能为空';
  1016. valid = false;
  1017. }
  1018. // 责任人 (responsiblePerson 是数组)
  1019. if (!row.editData.responsiblePerson || row.editData.responsiblePerson.length === 0) {
  1020. row.errors.responsiblePerson = '责任人不能为空';
  1021. valid = false;
  1022. }
  1023. return valid;
  1024. };
  1025. // 获取所有设备名称(用于tooltip提示)
  1026. const getAllDeviceNames = (deviceIds: number[]) => {
  1027. if (!deviceIds || deviceIds.length === 0) return '无设备';
  1028. const deviceNames = deviceIds
  1029. .map(id => deviceMap.value[id]?.deviceCode || '未知设备')
  1030. .filter(name => name !== '未知设备');
  1031. return deviceNames.join(', ') || '无有效设备';
  1032. };
  1033. // 保存行
  1034. const saveRow = (row) => {
  1035. if (!validateRow(row)) return;
  1036. // 将编辑数据应用到行
  1037. row.id = row.editData.id;
  1038. row.wellName = row.editData.wellName;
  1039. row.wellType = row.editData.wellType;
  1040. row.wellCategory = row.editData.wellCategory;
  1041. row.location = row.editData.location;
  1042. row.technique = row.editData.technique;
  1043. row.workloadDesign = row.editData.workloadDesign;
  1044. row.deptIds = row.editData.deptIds;
  1045. row.deviceIds = row.editData.deviceIds;
  1046. // row.deviceCodes = row.editData.deviceCodes;
  1047. // 关键修改:根据设备ID列表生成设备代码
  1048. row.responsiblePerson = row.editData.responsiblePerson; // 保存责任人ID
  1049. row.responsiblePersonNames = row.editData.responsiblePersonNames; // 保存责任人名称
  1050. if (currentEditingRow.value) {
  1051. // 更新设备ID
  1052. currentEditingRow.value.editData.deviceIds = [...selectedDeviceIds.value];
  1053. // 更新设备代码显示
  1054. const selectedDevices = filteredDeviceList.value.filter(device =>
  1055. selectedDeviceIds.value.includes(device.id)
  1056. );
  1057. currentEditingRow.value.editData.deviceCodes = selectedDevices
  1058. .map(device => device.deviceCode)
  1059. .join(', ');
  1060. row.deviceCodes = currentEditingRow.value.editData.deviceCodes;
  1061. }
  1062. row.remark = row.editData.remark;
  1063. // 退出编辑状态
  1064. row.editing = false;
  1065. };
  1066. // 保存所有更改
  1067. const saveAll = () => {
  1068. let allValid = true;
  1069. // 验证所有编辑中的行
  1070. tableData.value.forEach(row => {
  1071. if (row.editing && !validateRow(row)) {
  1072. allValid = false;
  1073. }
  1074. });
  1075. if (!allValid) {
  1076. ElMessage.error('部分数据验证失败,请检查输入');
  1077. return;
  1078. }
  1079. // 保存所有编辑中的行
  1080. tableData.value.forEach(row => {
  1081. if (row.editing) {
  1082. row.id = row.editData.id;
  1083. row.wellName = row.editData.wellName;
  1084. row.wellType = row.editData.wellType;
  1085. row.location = row.editData.location;
  1086. row.technique = row.editData.technique;
  1087. row.workloadDesign = row.editData.workloadDesign;
  1088. row.deptIds = row.editData.deptIds;
  1089. row.deviceIds = row.editData.deviceIds;
  1090. row.deviceCodes = row.editData.deviceCodes;
  1091. row.responsiblePerson = row.editData.responsiblePerson; // 保存责任人ID
  1092. row.responsiblePersonNames = row.editData.responsiblePersonNames; // 保存责任人名称
  1093. row.remark = row.editData.remark;
  1094. }
  1095. });
  1096. ElMessage.success('所有更改已保存');
  1097. };
  1098. // 取消编辑
  1099. const cancelEdit = (row) => {
  1100. // 恢复原始数据
  1101. if (row.originalData.id) {
  1102. row.id = row.originalData.id;
  1103. row.wellName = row.originalData.wellName;
  1104. row.wellType = row.originalData.wellType;
  1105. row.wellCategory = row.originalData.wellCategory;
  1106. row.location = row.originalData.location;
  1107. row.technique = row.originalData.technique;
  1108. row.workloadDesign = row.originalData.workloadDesign;
  1109. row.deptIds = row.originalData.deptIds;
  1110. row.deviceIds = row.originalData.deviceIds;
  1111. row.deviceCodes = row.originalData.deviceCodes;
  1112. row.responsiblePerson = row.originalData.responsiblePerson; // 恢复责任人ID
  1113. row.responsiblePersonNames = row.originalData.responsiblePersonNames; // 恢复责任人名称
  1114. row.remark = row.originalData.remark;
  1115. } else {
  1116. // 如果是新增行,则删除
  1117. const index = tableData.value.indexOf(row);
  1118. if (index !== -1) {
  1119. tableData.value.splice(index, 1);
  1120. }
  1121. }
  1122. row.editing = false;
  1123. };
  1124. // 删除行
  1125. const deleteRow = (index) => {
  1126. tableData.value.splice(index, 1);
  1127. ElMessage.success('行已删除');
  1128. };
  1129. </script>
  1130. <style scoped>
  1131. .edit-input {
  1132. margin-right: 10px;
  1133. }
  1134. .error-message {
  1135. color: #f56c6c;
  1136. font-size: 12px;
  1137. margin-top: 5px;
  1138. }
  1139. .action-cell {
  1140. display: flex;
  1141. gap: 8px;
  1142. }
  1143. /* 1. 穿梭框父容器:居中 + 内边距 */
  1144. .transfer-container {
  1145. text-align: center;
  1146. padding: 0px 0; /* 上下内边距,避免紧贴对话框边缘 */
  1147. }
  1148. /* 2. 穿梭框组件:控制宽度,避免过窄或过宽 */
  1149. .transfer-component {
  1150. width: 100%; /* 占对话框90%宽度,兼顾美观和内容显示 */
  1151. min-width: 600px;
  1152. }
  1153. /* 3. 直接调整 el-transfer 左右窗口的宽度 */
  1154. :deep(.el-transfer-panel) {
  1155. width: 40% !important; /* 强制设置左右面板宽度,确保两侧面板对等 */
  1156. }
  1157. /* 3. 深度选择器:修改el-transfer选项样式(解决内容省略问题) */
  1158. :deep(.el-transfer-panel__item) {
  1159. white-space: nowrap; /* 文本不换行,保证一行显示 */
  1160. overflow: hidden; /* 超出部分隐藏 */
  1161. text-overflow: ellipsis; /* 超出显示省略号 */
  1162. max-width: 100%; /* 确保文本不超出选项容器宽度 */
  1163. padding: 6px 6px; /* 适当增加内边距,优化点击体验 */
  1164. }
  1165. /* 4. 选项文本:确保继承父容器宽度,省略号正常生效 */
  1166. .transfer-option-text {
  1167. display: inline-block;
  1168. max-width: 100%;
  1169. }
  1170. /* 设备名称显示区域样式 */
  1171. .device-names {
  1172. white-space: nowrap;
  1173. overflow: hidden;
  1174. text-overflow: ellipsis;
  1175. max-width: 200px; /* 根据实际列宽调整 */
  1176. }
  1177. :deep(.el-transfer-panel__list) {
  1178. width: 100% !important; /* 使面板内部内容宽度占满 */
  1179. }
  1180. /* 责任人名称显示区域样式 */
  1181. .responsible-names {
  1182. white-space: nowrap;
  1183. overflow: hidden;
  1184. text-overflow: ellipsis;
  1185. max-width: 200px; /* 根据实际列宽调整 */
  1186. }
  1187. /* 添加部门名称的样式 */
  1188. .dept-names {
  1189. white-space: nowrap;
  1190. overflow: hidden;
  1191. text-overflow: ellipsis;
  1192. max-width: 200px; /* 根据实际列宽调整 */
  1193. display: inline-block;
  1194. vertical-align: bottom;
  1195. }
  1196. </style>