index.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. <template>
  2. <ContentWrap>
  3. <!-- 搜索工作栏 -->
  4. <el-form
  5. class="-mb-15px"
  6. :model="queryParams"
  7. ref="queryFormRef"
  8. :inline="true"
  9. label-width="68px"
  10. >
  11. <el-form-item label="客户名称" prop="manufactureName">
  12. <el-input
  13. v-model="queryParams.manufactureName"
  14. placeholder="请输入客户名称"
  15. clearable
  16. @keyup.enter="handleQuery"
  17. class="!w-240px"
  18. />
  19. </el-form-item>
  20. <el-form-item label="合同名称" prop="contractName">
  21. <el-input
  22. v-model="queryParams.contractName"
  23. placeholder="请输入合同名称"
  24. clearable
  25. @keyup.enter="handleQuery"
  26. class="!w-240px"
  27. />
  28. </el-form-item>
  29. <el-form-item label="合同编号" prop="contractCode">
  30. <el-input
  31. v-model="queryParams.contractCode"
  32. placeholder="请输入合同编号"
  33. clearable
  34. @keyup.enter="handleQuery"
  35. class="!w-240px"
  36. />
  37. </el-form-item>
  38. <el-form-item label="起止日期" prop="startTime">
  39. <el-date-picker
  40. v-model="queryParams.startTime"
  41. value-format="YYYY-MM-DD HH:mm:ss"
  42. type="daterange"
  43. start-placeholder="开始日期"
  44. end-placeholder="结束日期"
  45. :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
  46. class="!w-220px"
  47. />
  48. </el-form-item>
  49. <el-form-item>
  50. <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
  51. <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
  52. <el-button
  53. type="primary"
  54. plain
  55. @click="openForm('create',undefined)"
  56. v-hasPermi="['rq:iot-project-info:create']"
  57. >
  58. <Icon icon="ep:plus" class="mr-5px" /> 新增
  59. </el-button>
  60. <el-button
  61. type="success"
  62. plain
  63. @click="handleExport"
  64. :loading="exportLoading"
  65. v-hasPermi="['rq:iot-project-info:export']"
  66. >
  67. <Icon icon="ep:download" class="mr-5px" /> 导出
  68. </el-button>
  69. </el-form-item>
  70. </el-form>
  71. </ContentWrap>
  72. <!-- 列表 -->
  73. <ContentWrap>
  74. <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
  75. <el-table-column :label="t('iotDevice.serial')" width="70" align="center">
  76. <template #default="scope">
  77. {{ scope.$index + 1 }}
  78. </template>
  79. </el-table-column>
  80. <el-table-column label="客户名称" align="center" prop="manufactureName" />
  81. <el-table-column label="合同名称" align="center" prop="contractName" >
  82. <template #default="scope">
  83. <el-link type="primary" @click="showTaskList(scope.row)">
  84. {{ scope.row.contractName }}
  85. </el-link>
  86. </template>
  87. </el-table-column>
  88. <el-table-column label="合同编号" align="center" prop="contractCode" />
  89. <el-table-column label="工作量" align="center">
  90. <el-table-column label="总数" align="center" prop="workloadTotal" />
  91. <el-table-column label="完成" align="center" prop="workloadFinish" />
  92. </el-table-column>
  93. <el-table-column label="合同起止时间" align="center">
  94. <el-table-column
  95. label="开始时间"
  96. align="center"
  97. prop="startTime"
  98. :formatter="dateFormatter2"
  99. width="180px"
  100. />
  101. <el-table-column
  102. label="完成时间"
  103. align="center"
  104. prop="endTime"
  105. :formatter="dateFormatter2"
  106. width="180px"
  107. />
  108. </el-table-column>
  109. <!-- <el-table-column label="施工地点" align="center" prop="location" />
  110. <el-table-column label="施工工艺" align="center" prop="technique" />-->
  111. <el-table-column :label="t('project.payment')" align="center" prop="payment" min-width="90">
  112. <template #default="scope">
  113. <dict-tag :type="DICT_TYPE.PMS_PROJECT_SETTLEMENT" :value="scope.row.payment" />
  114. </template>
  115. </el-table-column>
  116. <el-table-column
  117. label="创建时间"
  118. align="center"
  119. prop="createTime"
  120. :formatter="dateFormatter"
  121. width="180px"
  122. />
  123. <el-table-column label="操作" align="center" min-width="180px">
  124. <template #default="scope">
  125. <el-button
  126. link
  127. type="primary"
  128. @click="openForm('update', scope.row.id)"
  129. v-hasPermi="['rq:iot-project-info:update']"
  130. >
  131. 编辑
  132. </el-button>
  133. <el-button
  134. link
  135. type="primary"
  136. @click="assignTask(scope.row)"
  137. v-hasPermi="['rq:iot-project-task:create']"
  138. >
  139. 分配任务
  140. </el-button>
  141. <el-button
  142. link
  143. type="danger"
  144. @click="handleDelete(scope.row.id)"
  145. v-hasPermi="['rq:iot-project-info:delete']"
  146. >
  147. 删除
  148. </el-button>
  149. </template>
  150. </el-table-column>
  151. </el-table>
  152. <!-- 分页 -->
  153. <Pagination
  154. :total="total"
  155. v-model:page="queryParams.pageNo"
  156. v-model:limit="queryParams.pageSize"
  157. @pagination="getList"
  158. />
  159. </ContentWrap>
  160. <!-- 任务列表区域 -->
  161. <ContentWrap v-if="selectedProject">
  162. <el-card class="box-card">
  163. <template #header>
  164. <div class="card-header">
  165. <span>任务列表 - {{ selectedProject.contractName }}</span>
  166. <el-button link @click="closeTaskList" class="close-btn">
  167. <Icon icon="ep:close" />
  168. </el-button>
  169. </div>
  170. </template>
  171. <el-table :data="taskList" v-loading="taskLoading">
  172. <el-table-column label="井号" align="center" prop="wellName" />
  173. <el-table-column :label="t('project.wellType')" align="center" prop="wellType" min-width="70">
  174. <template #default="scope">
  175. <dict-tag :type="DICT_TYPE.PMS_PROJECT_WELL_TYPE" :value="scope.row.wellType" />
  176. </template>
  177. </el-table-column>
  178. <el-table-column :label="t('project.wellCategory')" align="center" prop="wellCategory" min-width="70">
  179. <template #default="scope">
  180. <dict-tag :type="DICT_TYPE.PMS_PROJECT_WELL_CATEGORY" :value="scope.row.wellCategory" />
  181. </template>
  182. </el-table-column>
  183. <!--
  184. <el-table-column :label="t('project.status')" align="center" prop="status" min-width="70">
  185. <template #default="scope">
  186. <dict-tag :type="DICT_TYPE.PMS_PROJECT_TASK_SCHEDULE" :value="scope.row.status" />
  187. </template>
  188. </el-table-column> -->
  189. <el-table-column :label="t('project.status')" align="center" prop="status" min-width="70">
  190. <template #default="scope">
  191. <el-link type="primary" @click="openTimelineDialog(scope.row)">
  192. <dict-tag :type="DICT_TYPE.PMS_PROJECT_TASK_SCHEDULE" :value="scope.row.status" />
  193. </el-link>
  194. </template>
  195. </el-table-column>
  196. <el-table-column label="施工地点" align="center" prop="location" />
  197. <el-table-column :label="t('project.technology')" align="center" prop="technique" min-width="70">
  198. <template #default="scope">
  199. <dict-tag :type="DICT_TYPE.PMS_PROJECT_TECHNOLOGY" :value="scope.row.technique" />
  200. </template>
  201. </el-table-column>
  202. <el-table-column label="设计工作量" align="center" prop="workloadDesign" />
  203. <el-table-column label="施工队伍" align="center">
  204. <template #default="{ row }">
  205. <el-tooltip
  206. :content="getAllDeptNames(row.deptIds)"
  207. placement="top"
  208. >
  209. <span class="dept-names">
  210. {{ getBriefDeptNames(row.deptIds) }}
  211. </span>
  212. </el-tooltip>
  213. </template>
  214. </el-table-column>
  215. <el-table-column label="施工设备" align="center">
  216. <template #default="{ row }">
  217. <el-tooltip
  218. :content="getAllDeviceNames(row.deviceIds)"
  219. placement="top"
  220. >
  221. <span class="device-names">
  222. {{ getDeviceNames(row.deviceIds) }}
  223. </span>
  224. </el-tooltip>
  225. </template>
  226. </el-table-column>
  227. <el-table-column label="责任人" align="center">
  228. <template #default="{ row }">
  229. <el-tooltip
  230. :content="getAllResponsiblePersonNames(row.responsiblePerson)"
  231. placement="top"
  232. >
  233. <span class="responsible-names">
  234. {{ getResponsiblePersonNames(row.responsiblePerson) }}
  235. </span>
  236. </el-tooltip>
  237. </template>
  238. </el-table-column>
  239. <el-table-column label="备注" align="center" prop="remark" />
  240. </el-table>
  241. </el-card>
  242. </ContentWrap>
  243. <!-- Timeline 时间线 Dialog - 已修改为 el-steps -->
  244. <el-dialog v-model="timelineDialogVisible" :title="`任务进度 - ${currentTaskRow ? currentTaskRow.wellName : ''}`" width="700px">
  245. <div v-if="stepsData.length > 0">
  246. <el-steps direction="horizontal" :active="currentStepIndex" finish-status="success">
  247. <el-step
  248. v-for="(step, index) in stepsData"
  249. :key="index"
  250. :title="step.title"
  251. :description="step.description"
  252. :status="step.status"
  253. />
  254. </el-steps>
  255. </div>
  256. <el-empty v-else description="暂无进度数据" :image-size="100" />
  257. <template #footer>
  258. <span class="dialog-footer">
  259. <el-button @click="timelineDialogVisible = false">关闭</el-button>
  260. </span>
  261. </template>
  262. </el-dialog>
  263. </template>
  264. <script setup lang="ts">
  265. import { dateFormatter,dateFormatter2 } from '@/utils/formatTime'
  266. import download from '@/utils/download'
  267. import { IotProjectInfoApi, IotProjectInfoVO } from '@/api/pms/iotprojectinfo'
  268. import {IotProjectTaskApi} from '@/api/pms/iotprojecttask'
  269. import IotProjectInfoForm from './IotProjectInfoForm.vue'
  270. import {useUserStore} from "@/store/modules/user";
  271. import {DICT_TYPE, getIntDictOptions} from "@/utils/dict";
  272. import { IotDeviceApi } from '@/api/pms/device' // 新增:引入设备API
  273. import * as UserApi from "@/api/system/user"; // 新增:引入用户API
  274. import * as DeptApi from "@/api/system/dept"; // 新增:引入部门API
  275. import { handleTree } from "@/utils/tree"; // 新增:引入树形处理工具
  276. import { IotProjectTaskScheduleApi } from '@/api/pms/iotprojecttaskschedule'
  277. import dayjs from 'dayjs' // 引入 dayjs 用于时间格式化
  278. import { ref, reactive, onMounted, computed } from 'vue'
  279. /** 项目信息 列表 */
  280. defineOptions({ name: 'iotProjectInfo' })
  281. const message = useMessage() // 消息弹窗
  282. const { t } = useI18n() // 国际化
  283. // 新增:任务列表相关状态
  284. const taskLoading = ref(false) // 任务列表的加载中
  285. const taskList = ref([]) // 任务列表的数据
  286. const selectedProject = ref(null) // 当前选中的项目
  287. const deptList = ref([]) // 部门列表
  288. const deviceMap = ref({}) // 设备映射表
  289. const responsiblePersonList = ref([]) // 责任人列表
  290. const timelineData = ref<Array<{ timestamp: string; content: string }>>([]) // 时间线数据
  291. const taskScheduleDictOptions = ref<any[]>([]) // 任务进度字典选项
  292. const timelineDialogVisible = ref(false) // 控制时间线弹窗显示
  293. const currentTaskRow = ref<any>(null) // 当前选中的任务行数据
  294. const stepsData = ref<Array<{title: string, description?: string, status?: string}>>([]) // 步骤数据
  295. const currentStepIndex = ref(0) // 当前步骤索引
  296. const loading = ref(true) // 列表的加载中
  297. const list = ref<IotProjectInfoVO[]>([]) // 列表的数据
  298. const total = ref(0) // 列表的总页数
  299. const queryParams = reactive({
  300. pageNo: 1,
  301. pageSize: 10,
  302. deptId: undefined,
  303. deptName: undefined,
  304. contractName: undefined,
  305. contractCode: undefined,
  306. workloadTotal: undefined,
  307. workloadFinish: undefined,
  308. startTime: [],
  309. endTime: [],
  310. location: undefined,
  311. technique: undefined,
  312. payment: undefined,
  313. createTime: [],
  314. userName: undefined,
  315. userId: undefined,
  316. })
  317. const queryFormRef = ref() // 搜索的表单
  318. const exportLoading = ref(false) // 导出的加载中
  319. const { push } = useRouter() // 路由跳转
  320. /** 查询列表 */
  321. const getList = async () => {
  322. loading.value = true
  323. try {
  324. queryParams.deptId = useUserStore().getUser.deptId;
  325. const data = await IotProjectInfoApi.getIotProjectInfoPage(queryParams)
  326. list.value = data.list
  327. total.value = data.total
  328. } finally {
  329. loading.value = false
  330. }
  331. }
  332. /** 搜索按钮操作 */
  333. const handleQuery = () => {
  334. queryParams.pageNo = 1
  335. getList()
  336. }
  337. /** 重置按钮操作 */
  338. const resetQuery = () => {
  339. queryFormRef.value.resetFields()
  340. handleQuery()
  341. }
  342. // 显示任务列表
  343. const showTaskList = async (project) => {
  344. selectedProject.value = project
  345. taskLoading.value = true
  346. try {
  347. // 获取任务列表
  348. const queryParams = {
  349. projectId: project.id
  350. };
  351. const taskData = await IotProjectTaskApi.getIotProjectTaskPage(queryParams);
  352. taskList.value = taskData.list;
  353. // 收集所有设备ID和责任人ID
  354. const allDeviceIds = new Set();
  355. const allResponsiblePersonIds = new Set();
  356. taskList.value.forEach(item => {
  357. if (item.deviceIds?.length) {
  358. item.deviceIds.forEach(id => allDeviceIds.add(id));
  359. }
  360. if (item.responsiblePerson?.length) {
  361. item.responsiblePerson.forEach(id => allResponsiblePersonIds.add(id));
  362. }
  363. });
  364. // 批量获取设备信息
  365. if (allDeviceIds.size > 0) {
  366. const deviceIdsArray = Array.from(allDeviceIds);
  367. const devices = await IotDeviceApi.getDevicesByDepts({
  368. deviceIds: deviceIdsArray
  369. });
  370. // 更新设备映射表
  371. devices.forEach(device => {
  372. deviceMap.value[device.id] = device;
  373. });
  374. }
  375. // 批量获取责任人信息
  376. if (allResponsiblePersonIds.size > 0) {
  377. const personIdsArray = Array.from(allResponsiblePersonIds);
  378. const persons = await UserApi.companyDeptsEmployee({
  379. userIds: personIdsArray
  380. });
  381. responsiblePersonList.value = persons;
  382. }
  383. } catch (error) {
  384. console.error('加载任务列表失败:', error);
  385. message.error('加载任务列表失败');
  386. } finally {
  387. taskLoading.value = false;
  388. }
  389. };
  390. // 新增:关闭任务列表
  391. const closeTaskList = () => {
  392. selectedProject.value = null;
  393. taskList.value = [];
  394. };
  395. // 新增:获取部门名称
  396. const getDeptNames = (deptIds) => {
  397. if (!deptIds || deptIds.length === 0) return '';
  398. const names = [];
  399. const findDept = (list, id) => {
  400. for (const item of list) {
  401. if (item.id === id) {
  402. names.push(item.name);
  403. return true;
  404. }
  405. if (item.children && findDept(item.children, id)) {
  406. return true;
  407. }
  408. }
  409. return false;
  410. };
  411. deptIds.forEach(id => findDept(deptList.value, id));
  412. return names.join(', ');
  413. };
  414. // 新增:获取设备名称
  415. const getDeviceNames = (deviceIds) => {
  416. if (!deviceIds || deviceIds.length === 0) return '';
  417. const deviceNames = deviceIds
  418. .map(id => deviceMap.value[id]?.deviceCode)
  419. .filter(name => name);
  420. if (deviceNames.length === 0) return '';
  421. if (deviceNames.length > 2) {
  422. return `${deviceNames[0]}, ${deviceNames[1]}...`;
  423. }
  424. return deviceNames.join(', ');
  425. };
  426. // 新增:获取所有设备名称
  427. const getAllDeviceNames = (deviceIds) => {
  428. if (!deviceIds || deviceIds.length === 0) return '无设备';
  429. const deviceNames = deviceIds
  430. .map(id => deviceMap.value[id]?.deviceCode || '未知设备')
  431. .filter(name => name !== '未知设备');
  432. return deviceNames.join(', ') || '无有效设备';
  433. };
  434. // 新增:获取简略部门名称(用于表格显示)
  435. const getBriefDeptNames = (deptIds) => {
  436. if (!deptIds || deptIds.length === 0) return '';
  437. const names = [];
  438. const findDept = (list, id) => {
  439. for (const item of list) {
  440. if (item.id === id) {
  441. names.push(item.name);
  442. return true;
  443. }
  444. if (item.children && findDept(item.children, id)) {
  445. return true;
  446. }
  447. }
  448. return false;
  449. };
  450. deptIds.forEach(id => findDept(deptList.value, id));
  451. if (names.length === 0) return '';
  452. if (names.length > 2) {
  453. return `${names[0]}, ${names[1]}...`;
  454. }
  455. return names.join(', ');
  456. };
  457. // 新增:获取所有部门名称(用于tooltip显示)
  458. const getAllDeptNames = (deptIds) => {
  459. if (!deptIds || deptIds.length === 0) return '无施工队伍';
  460. return getDeptNames(deptIds);
  461. };
  462. // 新增:获取责任人名称
  463. const getResponsiblePersonNames = (responsiblePersonIds) => {
  464. if (!responsiblePersonIds || responsiblePersonIds.length === 0) return '';
  465. const personNames = responsiblePersonIds
  466. .map(id => {
  467. const person = responsiblePersonList.value.find(p => p.id === id);
  468. return person ? person.nickname : '';
  469. })
  470. .filter(name => name);
  471. if (personNames.length === 0) return '';
  472. if (personNames.length > 2) {
  473. return `${personNames[0]}, ${personNames[1]}...`;
  474. }
  475. return personNames.join(', ');
  476. };
  477. // 新增:获取所有责任人名称
  478. const getAllResponsiblePersonNames = (responsiblePersonIds) => {
  479. if (!responsiblePersonIds || responsiblePersonIds.length === 0) return '无责任人';
  480. const personNames = responsiblePersonIds
  481. .map(id => {
  482. const person = responsiblePersonList.value.find(p => p.id === id);
  483. return person ? person.nickname : '未知人员';
  484. })
  485. .filter(name => name !== '未知人员');
  486. return personNames.join(', ') || '无有效责任人';
  487. };
  488. // 跳转到项目详情页面
  489. const goToDetail = (id: number) => {
  490. push({
  491. name: 'IotProjectInfoDetail',
  492. params: { id }
  493. })
  494. }
  495. /** 打开 Timeline 时间线 Dialog - 已修改为 el-steps */
  496. const openTimelineDialog = async (row: any) => {
  497. currentTaskRow.value = row
  498. timelineDialogVisible.value = true
  499. stepsData.value = [] // 清空旧数据
  500. currentStepIndex.value = 0 // 重置步骤索引
  501. try {
  502. // 获取任务进度字典
  503. if (taskScheduleDictOptions.value.length === 0) {
  504. await getTaskScheduleDictOptions()
  505. }
  506. // 获取时间线数据
  507. const params = { taskId: row.id } // 假设根据 taskId 获取
  508. const response = await IotProjectTaskScheduleApi.getIotProjectTaskSchedules(params)
  509. if (response && response.length > 0) {
  510. // 处理数据:转换时间戳、匹配字典label
  511. const sortedSchedules = response.sort((a, b) => a.status - b.status);
  512. // 生成步骤数据
  513. stepsData.value = sortedSchedules.map((item: any) => {
  514. // 格式化时间戳 (假设 startTime 是毫秒时间戳)
  515. const formattedTimestamp = item.startTime ? dayjs(item.startTime).format('YYYY-MM-DD HH:mm:ss') : '时间未设置'
  516. // 查找 status 对应的字典 label
  517. const dictItem = taskScheduleDictOptions.value.find(dict => dict.value === item.status)
  518. const statusLabel = dictItem ? dictItem.label : `未知状态 (${item.status})`
  519. // 核心修改:判断当前计划任务状态是否与当前行任务状态匹配
  520. const isCurrentStep = item.status === row.status;
  521. return {
  522. title: `${formattedTimestamp} ${statusLabel}`,
  523. // 如果匹配,description 设置为“当前进度”,否则设为空字符串
  524. description: isCurrentStep ? '当前进度' : '',
  525. // 可以根据需要设置 status,例如当前节点高亮
  526. status: isCurrentStep ? 'process' : undefined
  527. }
  528. })
  529. // 计算当前步骤索引
  530. const currentStatus = row.status;
  531. let activeIndex = -1;
  532. // 找到第一个状态大于当前任务状态的计划,当前步骤就是前一个
  533. for (let i = 0; i < sortedSchedules.length; i++) {
  534. if (currentStatus < sortedSchedules[i].status) {
  535. activeIndex = i - 0.5; // 中间状态
  536. break;
  537. } else if (currentStatus === sortedSchedules[i].status) {
  538. activeIndex = i; // 正好在这个节点上
  539. break;
  540. }
  541. }
  542. // 如果当前状态大于所有计划进度状态,则显示最后一步已完成
  543. if (activeIndex === -1 && sortedSchedules.length > 0) {
  544. if (currentStatus > sortedSchedules[sortedSchedules.length - 1].status) {
  545. activeIndex = sortedSchedules.length;
  546. } else {
  547. activeIndex = sortedSchedules.length - 1;
  548. }
  549. }
  550. currentStepIndex.value = Math.max(0, activeIndex);
  551. } else {
  552. stepsData.value = []
  553. }
  554. } catch (error) {
  555. console.error('获取任务进度时间线失败:', error)
  556. message.error('获取任务进度失败')
  557. stepsData.value = []
  558. }
  559. }
  560. /** 获取任务进度字典数据 */
  561. const getTaskScheduleDictOptions = async () => {
  562. try {
  563. taskScheduleDictOptions.value = getIntDictOptions(DICT_TYPE.PMS_PROJECT_TASK_SCHEDULE)
  564. } catch (error) {
  565. console.error('获取任务进度字典失败:', error)
  566. taskScheduleDictOptions.value = []
  567. }
  568. }
  569. /** 添加/修改操作 */
  570. const formRef = ref()
  571. const openForm = (type: string, id?: number) => {
  572. if(id===undefined){
  573. push({ name: 'IotProjectInfo', params: { type} })
  574. }else{
  575. push({ name: 'IotProjectInfo', params: { type,id} })
  576. }
  577. }
  578. /** 分配任务操作 */
  579. const assignTask = (row: IotProjectInfoVO) => {
  580. push({
  581. name: 'IotProjectTaskInfo',
  582. query: {
  583. projectId: row.id,
  584. contractName: row.contractName
  585. },
  586. params: {type: 'create'}
  587. })
  588. }
  589. /** 删除按钮操作 */
  590. const handleDelete = async (id: number) => {
  591. try {
  592. queryParams.deptId = useUserStore().getUser.deptId;
  593. const data = await IotProjectTaskApi.getIotProjectTaskList(queryParams)
  594. if(data.length===0){
  595. // 删除的二次确认
  596. await message.delConfirm()
  597. // 发起删除
  598. await IotProjectInfoApi.deleteIotProjectInfo(id)
  599. message.success(t('common.delSuccess'))
  600. }else {
  601. message.error(t('form.relatedProject'))
  602. }
  603. // 刷新列表
  604. await getList()
  605. } catch {}
  606. }
  607. /** 导出按钮操作 */
  608. const handleExport = async () => {
  609. try {
  610. // 导出的二次确认
  611. await message.exportConfirm()
  612. // 发起导出
  613. exportLoading.value = true
  614. const data = await IotProjectInfoApi.exportIotProjectInfo(queryParams)
  615. download.excel(data, '项目信息.xls')
  616. } catch {
  617. } finally {
  618. exportLoading.value = false
  619. }
  620. }
  621. /** 初始化 **/
  622. onMounted(async () => {
  623. deptList.value = handleTree(await DeptApi.companyLevelChildrenDepts());
  624. getList()
  625. // 预加载任务进度字典
  626. getTaskScheduleDictOptions()
  627. })
  628. </script>
  629. <style scoped>
  630. /* 新增:任务列表相关样式 */
  631. .card-header {
  632. display: flex;
  633. justify-content: space-between;
  634. align-items: center;
  635. }
  636. .close-btn {
  637. padding: 0;
  638. min-height: auto;
  639. }
  640. .dept-names {
  641. white-space: nowrap;
  642. overflow: hidden;
  643. text-overflow: ellipsis;
  644. max-width: 120px;
  645. display: inline-block;
  646. }
  647. .device-names {
  648. white-space: nowrap;
  649. overflow: hidden;
  650. text-overflow: ellipsis;
  651. max-width: 120px;
  652. display: inline-block;
  653. }
  654. .responsible-names {
  655. white-space: nowrap;
  656. overflow: hidden;
  657. text-overflow: ellipsis;
  658. max-width: 120px;
  659. display: inline-block;
  660. }
  661. :deep(.el-step__description) {
  662. color: red !important;
  663. }
  664. </style>