list.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. <template>
  2. <div class="safety-platform-container">
  3. <header class="safety-platform-container__header">
  4. <div class="breadcrumb-title"> 危险作业审批管理 </div>
  5. </header>
  6. <main class="safety-platform-container__main">
  7. <div style="margin-bottom:20px">
  8. <el-button type="primary" @click="$router.push({ name: 'hazardApprovalManageAdd' })">添加 </el-button>
  9. </div>
  10. <div class="search-form">
  11. <el-form :inline="true">
  12. <el-form-item label="申请单位名称">
  13. <el-input
  14. v-model="queryParams.queryParam.applicationUnitName"
  15. placeholder="申请单位名称"
  16. style="width: 170px"
  17. />
  18. </el-form-item>
  19. <el-form-item label="状态">
  20. <el-select v-model="queryParams.queryParam.status" clearable placeholder="状态" style="width: 170px">
  21. <el-option value="" label="全部" />
  22. <el-option :value="1" label="待提交" />
  23. <el-option :value="2" label="待审批" />
  24. <el-option :value="3" label="已完成" />
  25. </el-select>
  26. </el-form-item>
  27. <el-form-item label="申请人名称">
  28. <el-input v-model="queryParams.queryParam.applicantName" placeholder="搜索申请人" style="width: 170px" />
  29. </el-form-item>
  30. <el-form-item label="申请部门">
  31. <el-cascader
  32. v-model="queryParams.queryParam.applicationDepartmentId"
  33. style="width: 170px"
  34. ref="cascaderRef"
  35. :options="firstLevelDepts"
  36. :props="cascaderProp"
  37. :show-all-levels="false"
  38. placeholder="部门名称"
  39. filterable
  40. @change="handleChangeDept"
  41. />
  42. </el-form-item>
  43. </el-form>
  44. <div>
  45. <el-button type="primary" @click="queryTableList">查询</el-button>
  46. <el-button @click="handleRestParams">重置</el-button>
  47. <el-button plain @click="handleDownload">
  48. 导出
  49. </el-button>
  50. </div>
  51. </div>
  52. <div class="table-content">
  53. <el-table :data="tableData.data">
  54. <el-table-column type="index" label="序号" width="80" />
  55. <el-table-column label="申请单号" width="150" prop="code" />
  56. <el-table-column label="申请单位" width="180" prop="applicationUnitName" />
  57. <el-table-column label="申请人" width="180" prop="applicantName" />
  58. <el-table-column label="申请部门" width="180" prop="applicationDepartment" />
  59. <el-table-column label="申请人电话" width="180" prop="applicantPhone" />
  60. <el-table-column label="作业类别" width="180" prop="hazardOperationType" />
  61. <el-table-column label="申请状态" width="120" prop="statusName" />
  62. <el-table-column label="申请时间" width="180" prop="createdAt" />
  63. <el-table-column label="备注" width="180" prop="remark" />
  64. <el-table-column label="当前节点名称" width="180" prop="nodeDescription" />
  65. <el-table-column fixed="right" min-width="240" label="操作">
  66. <template #default="scope">
  67. <el-button v-if="scope.row.status === 1" type="primary" link @click="handleSendApproval(scope)"
  68. >提交</el-button
  69. >
  70. <el-button
  71. type="primary"
  72. link
  73. v-if="scope.row.status === 1"
  74. @click="$router.push({ name: 'hazardApprovalManageEdit', query: { id: scope.row.id } })"
  75. >编辑</el-button
  76. >
  77. <el-button
  78. type="primary"
  79. link
  80. @click="$router.push({ name: 'hazardApprovalManageView', query: { id: scope.row.id } })"
  81. >查看</el-button
  82. >
  83. <el-popconfirm title="确定要删除吗?" @confirm="handleConfirmDeleteRow(scope)" v-if="scope.row.status === 1">
  84. <template #reference>
  85. <el-button type="primary" link>删除</el-button>
  86. </template>
  87. </el-popconfirm>
  88. <!-- <el-button type="primary" link>审批</el-button> -->
  89. <el-button
  90. v-if="scope.row.status === 3"
  91. type="primary"
  92. link
  93. @click="$router.push({ name: 'hazardApprovalManageMonitor', query: { id: scope.row.id } })"
  94. >视频监控</el-button
  95. >
  96. </template>
  97. </el-table-column>
  98. </el-table>
  99. </div>
  100. <div class="pagination-container" v-if="tableData.total > 0">
  101. <el-pagination
  102. background
  103. :current-page="queryParams.pageNumber"
  104. :page-size="queryParams.pageSize"
  105. :total="tableData.total"
  106. @size-change="handleSizeChange"
  107. @current-change="handleCurrentChange"
  108. />
  109. </div>
  110. </main>
  111. </div>
  112. <BasicDialog
  113. v-if="approvalVisible"
  114. v-model="approvalVisible"
  115. ref="basicDialogRef"
  116. title="提交审批"
  117. @refresh="closeApprovalDialog"
  118. >
  119. <template #form>
  120. <div class="form">
  121. <el-form ref="approvalFormRef" :model="approvalForm">
  122. <el-form-item label="审批描述:" label-position="top">
  123. <el-input v-model="approvalForm.description" placeholder="请输入审批描述" type="textarea" />
  124. </el-form-item>
  125. <div class="form-item">
  126. <span>审批流程:</span>
  127. <template v-for="item in approvalNodeList" :key="item.id">
  128. <el-form-item
  129. :label="`第${item.approvalOrder}步:${item.nodeDescription}(${APPROVAL_TYPE_MAP[item.approvalType]})`"
  130. label-position="top"
  131. :prop="item.approverType !== APPROVER_TYPE.FIX ? `approvers.${item.id}` : ''"
  132. :rules="{ required: true, message: '请选择审批人员', trigger: 'change' }"
  133. >
  134. <el-input
  135. v-if="item.approverType === APPROVER_TYPE.FIX"
  136. :model-value="item.approverInfoList.map((info) => info.approverName).join(',')"
  137. disabled
  138. />
  139. <el-select
  140. v-else
  141. v-model="approvalForm.approvers[item.id]"
  142. placeholder="请选择审批人员"
  143. value-key="id"
  144. filterable
  145. remote
  146. collapse-tags
  147. collapse-tags-tooltip
  148. :max-collapse-tags="2"
  149. :remote-method="remoteMethod"
  150. :loading="loading"
  151. multiple
  152. >
  153. <el-option
  154. v-for="option in userOptions"
  155. :key="option.id"
  156. :label="`${option.realname}(${option.username})${option.deptName}`"
  157. :value="option.id"
  158. />
  159. </el-select>
  160. </el-form-item>
  161. </template>
  162. </div>
  163. </el-form>
  164. </div>
  165. </template>
  166. <template #footer>
  167. <el-button type="primary" @click="handleSubmitApproval">提交</el-button>
  168. <el-button @click="approvalVisible = false">取消</el-button>
  169. </template>
  170. </BasicDialog>
  171. </template>
  172. <script lang="ts" setup>
  173. import { onMounted, reactive, ref } from 'vue';
  174. import { ElMessage, ElTableColumn } from 'element-plus';
  175. import { useRouter } from 'vue-router';
  176. import {
  177. dangerWorkSubmit,
  178. dangerWorkQueryPage,
  179. dangerWorkDeleteDangerWork,
  180. exportHazardApprovalList,
  181. } from '@/api/production-safety/responsibility-implementation';
  182. import BasicDialog from '@/components/BasicDialog.vue';
  183. import { getApprovalNodeInstanceList } from '@/api/approval/approval';
  184. import { submitReceiptRecordApprovalProcess } from '@/api/receiptRecord';
  185. import { omit } from 'lodash-es';
  186. import { useUserInfoHook } from '@/hooks/useUserInfoHook';
  187. import type { ApprovalNodeInstanceType } from '@/views/system/approval/types';
  188. import { downloadByData } from '@/utils/file/download';
  189. import { formatDeptTree } from '@/views/disaster/utils/formatDeptTree';
  190. import { getAllDepartments } from '@/api/auth/dept';
  191. import { APPROVAL_TYPE_MAP, APPROVER_TYPE } from '@/views/emergency/emergency-plan/src/constant';
  192. import { useEmergencySuppliesHook } from '@/views/emergency/emergency-supplies/src/hook';
  193. const router = useRouter();
  194. const { id } = useUserInfoHook();
  195. const firstLevelDepts = ref<any[]>([]);
  196. const approvalVisible = ref(false);
  197. const cascaderProp = {
  198. expandTrigger: 'click',
  199. checkStrictly: true,
  200. // emitPath: false,
  201. value: 'id',
  202. label: 'deptName',
  203. };
  204. const queryParams = reactive<any>({
  205. pageNumber: 1,
  206. pageSize: 10,
  207. queryParam: {
  208. status: '',
  209. applicationUnitName: '',
  210. applicantName: '',
  211. applicationDepartmentId: [],
  212. },
  213. });
  214. const cascaderRef = ref();
  215. const tableData = reactive({
  216. data: [],
  217. total: 0,
  218. });
  219. const handleSizeChange = (value) => {};
  220. const handleCurrentChange = (value) => {
  221. queryParams.pageNumber = value;
  222. queryTableList();
  223. };
  224. const getDeptData = () => {
  225. getAllDepartments().then((res) => {
  226. firstLevelDepts.value = formatDeptTree(res);
  227. });
  228. };
  229. const handleChangeDept = () => {};
  230. const handleConfirmDeleteRow = (scope) => {
  231. dangerWorkDeleteDangerWork(scope.row.id).then(() => {
  232. ElMessage.success('删除成功!');
  233. queryTableList();
  234. });
  235. };
  236. const queryTableList = () => {
  237. dangerWorkQueryPage({
  238. ...queryParams,
  239. queryParam: {
  240. ...omit(queryParams.queryParam, 'responsibleDepartmentId'),
  241. status: queryParams.queryParam.status !== '' ? queryParams.queryParam.status : undefined,
  242. applicationDepartmentId: JSON.stringify(queryParams.queryParam.applicationDepartmentId),
  243. },
  244. }).then((res) => {
  245. tableData.data = res.records;
  246. tableData.total = res.totalRow;
  247. });
  248. };
  249. const handleRestParams = () => {
  250. Object.assign(queryParams, {
  251. pageNumber: 1,
  252. pageSize: 10,
  253. queryParam: {
  254. ...queryParams.queryParam,
  255. status: '',
  256. applicationUnitName: '',
  257. applicantName: '',
  258. applicationDepartmentId: [],
  259. },
  260. });
  261. queryTableList();
  262. };
  263. const basicDialogRef = ref<InstanceType<typeof BasicDialog>>();
  264. const approvalFormRef = ref();
  265. const approvalForm = reactive({
  266. description: '',
  267. approvers: {} as Record<number, any[]>,
  268. });
  269. const approvalNodeList = ref<ApprovalNodeInstanceType[]>([]);
  270. const receiptRecordId = ref<number>();
  271. const { userOptions, loading, remoteMethod } = useEmergencySuppliesHook();
  272. const getApprovalNode = async (id: number) => {
  273. const res = await getApprovalNodeInstanceList(id);
  274. approvalNodeList.value = res.approvalNodeInfoList || [];
  275. };
  276. const handleSendApproval = async (scope) => {
  277. await getApprovalNode(scope.row.templateId);
  278. approvalVisible.value = true;
  279. receiptRecordId.value = scope.row.id;
  280. };
  281. const resetApprovalForm = () => {
  282. approvalFormRef.value?.resetFields();
  283. approvalForm.description = '';
  284. };
  285. const closeApprovalDialog = () => {
  286. resetApprovalForm();
  287. basicDialogRef.value?.closeDialog();
  288. };
  289. const handleSubmitApproval = () => {
  290. approvalFormRef.value?.validate(async (valid: boolean) => {
  291. if (!valid) return;
  292. if (!receiptRecordId?.value) {
  293. ElMessage.error('缺少施工作业记录ID,无法提交审批');
  294. return;
  295. }
  296. const approvalData: any = {
  297. id: receiptRecordId.value,
  298. approvalDescription: approvalForm.description,
  299. approvalInfoList: approvalNodeList.value.map((node) => {
  300. let approverIdList: number[] = [];
  301. if (node.approverType === APPROVER_TYPE.FIX) {
  302. approverIdList = node.approverInfoList.map((info) => info.approverId);
  303. } else if (approvalForm.approvers[node.id]) {
  304. approverIdList = approvalForm.approvers[node.id];
  305. }
  306. return {
  307. approvalOrder: node.approvalOrder,
  308. approverIdList,
  309. };
  310. }),
  311. };
  312. try {
  313. await dangerWorkSubmit(approvalData);
  314. ElMessage.success('提交成功');
  315. approvalVisible.value = false;
  316. queryTableList();
  317. } catch (e) {
  318. ElMessage.error('提交审批失败,请重试');
  319. }
  320. });
  321. };
  322. const handleDownload = async () => {
  323. try {
  324. const response = await exportHazardApprovalList();
  325. if (response) {
  326. const fileName = `危险作业审批管理_${new Date().toISOString().split('T')[0]}.xlsx`;
  327. downloadByData(response, fileName);
  328. ElMessage.success('导出成功');
  329. }
  330. } catch (e) {
  331. console.error('导出院级文件失败:', e);
  332. ElMessage.error('导出失败,请重试');
  333. }
  334. };
  335. onMounted(async () => {
  336. await getDeptData();
  337. queryTableList();
  338. });
  339. </script>
  340. <style lang="scss" scoped>
  341. @use '@/styles/page-details-layout.scss' as *;
  342. @use '@/styles/page-main-layout.scss' as *;
  343. @use '@/styles/basic-table-action.scss' as *;
  344. main {
  345. display: flex;
  346. flex-direction: column;
  347. }
  348. :deep(.el-tabs__header) {
  349. margin: 0;
  350. }
  351. :deep(.el-tabs__item) {
  352. font-size: 14px !important;
  353. }
  354. :deep(.flexContent) {
  355. display: flex;
  356. }
  357. :deep(.breadcrumb .title) {
  358. margin-left: 0;
  359. }
  360. .search-form {
  361. min-width: 800px;
  362. display: flex;
  363. justify-content: space-between;
  364. align-items: flex-end;
  365. margin-bottom: 20px;
  366. :deep(.el-form) {
  367. flex: 1;
  368. display: flex;
  369. row-gap: 15px;
  370. flex-wrap: wrap;
  371. }
  372. :deep(.el-form-item) {
  373. margin-bottom: 0;
  374. }
  375. :deep(main) {
  376. display: flex;
  377. flex-direction: column;
  378. }
  379. }
  380. .button-content {
  381. margin-bottom: 20px;
  382. }
  383. .table-content {
  384. flex: 1;
  385. overflow: hidden;
  386. overflow-y: auto;
  387. }
  388. .page-content {
  389. display: flex;
  390. justify-content: flex-end;
  391. }
  392. </style>