PageTaskExecution.vue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <template>
  2. <div class="safety-platform-container">
  3. <header class="safety-platform-container__header">
  4. <span class="breadcrumb-title">灾害预防检查任务</span>
  5. </header>
  6. <main class="safety-platform-container__main">
  7. <div class="search-table-container">
  8. <header class="disaster-precaution__header">
  9. <BasicSearch
  10. :searchConfig="TASK_EXECUTION_SEARCH_CONFIG"
  11. :searchData="searchData"
  12. @update:searchData="handleSearch"
  13. />
  14. </header>
  15. <BasicTable
  16. :table-config="tableConfig"
  17. :table-data="tableData"
  18. @update:pageSize="handleSizeChange"
  19. @update:pageNumber="handleCurrentChange"
  20. >
  21. <template #taskName="scope">
  22. <div class="task-name--div">
  23. <el-tooltip
  24. :content="scope.row.name"
  25. placement="top"
  26. effect="light"
  27. v-if="isToolTip(scope.row.overdue, scope.row.name)"
  28. >
  29. <span>{{ scope.row.name }}</span>
  30. </el-tooltip>
  31. <span v-else>{{ scope.row.name }}</span>
  32. <div class="overdue-icon">
  33. <img :src="OverdueIcon" alt="overdue" v-if="scope.row.overdue" />
  34. </div>
  35. </div>
  36. </template>
  37. <template #inspectType="scope">
  38. <span>{{ INSPECT_TYPE_MAP[scope.row.inspectType] }}</span>
  39. </template>
  40. <template #taskStage="scope">
  41. <span>{{ TASK_STAGE_MAP[scope.row.taskState] }}</span>
  42. </template>
  43. <!-- action button 不仅分任务情况 还要分 人物权限 -->
  44. <template #action="scope">
  45. <!-- 检查任务操作入口 -->
  46. <div class="action-container--div" v-if="scope.row.taskState === TASK_STAGE.PENDING_CHECK">
  47. <ActionButton text="去检查" @click="handleCheckItem(scope.row.id, 'check')" />
  48. <!-- 仅检查责任人可以看到 -->
  49. <ActionButton
  50. text="添加检查人"
  51. @click="handleAddCheckUser(scope.row.id)"
  52. v-if="scope.row.userTypeList.includes(USER_TYPE.CHECK_RESPONSIBLE)"
  53. />
  54. </div>
  55. <!-- 审批任务操作入口 -->
  56. <div class="action-container--div" v-else-if="scope.row.taskState === TASK_STAGE.PENDING_APPROVAL">
  57. <!-- 审批人员可以看到 -->
  58. <ActionButton
  59. text="去审批"
  60. v-if="scope.row.userTypeList.includes(USER_TYPE.APPROVER)"
  61. @click="handleCheckItem(scope.row.id, 'approve')"
  62. />
  63. <!-- 检查责任人、检查执行人员可以看到 -->
  64. <ActionButton
  65. v-if="
  66. [USER_TYPE.CHECK_RESPONSIBLE, USER_TYPE.CHECK_PERSON].some((type) =>
  67. scope.row.userTypeList.includes(type),
  68. )
  69. "
  70. text="撤回"
  71. :popconfirm="{
  72. title: '确定撤回吗?',
  73. }"
  74. @confirm="handleWithdrawTask(scope.row.id, scope.row.taskState)"
  75. />
  76. </div>
  77. <!-- 完成任务操作入口 -->
  78. <div class="action-container--div" v-else-if="scope.row.taskState === TASK_STAGE.COMPLETED">
  79. <ActionButton text="查看" @click="handleCheckItem(scope.row.id, 'view')" />
  80. <!-- 仅审批人员可以看到 -->
  81. <ActionButton
  82. v-if="scope.row.userTypeList.includes(USER_TYPE.APPROVER) && !isOverdue24Hours(scope.row.updatedAt)"
  83. text="撤回"
  84. :popconfirm="{
  85. title: '确定撤回吗?',
  86. }"
  87. @confirm="handleWithdrawTask(scope.row.id, scope.row.taskState)"
  88. />
  89. </div>
  90. </template>
  91. </BasicTable>
  92. </div>
  93. </main>
  94. <el-dialog v-model="userInfo" title="添加检查人" destroy-on-close>
  95. <InspectorSelect
  96. :customUserList="currentTaskInspectorList"
  97. @cancel="userInfo = false"
  98. @submit="handleSubmitCheckUser"
  99. />
  100. </el-dialog>
  101. </div>
  102. </template>
  103. <script setup lang="ts">
  104. import { useRouter } from 'vue-router';
  105. import { ref, onMounted, reactive, onUnmounted } from 'vue';
  106. import { ElMessage } from 'element-plus';
  107. import BasicTable from '@/components/BasicTable.vue';
  108. import ActionButton from '@/components/ActionButton.vue';
  109. import BasicSearch from '@/components/BasicSearch.vue';
  110. import InspectorSelect from '@/views/disaster/components/InspectorSelect.vue';
  111. import useTableConfig from '@/hooks/useTableConfigHook';
  112. import { isToolTip } from './src/utils';
  113. import type { TaskExecutionListResponse, TaskExecutionListQuery } from '@/types/disaster-precaution';
  114. import type { QueryPageRequest } from '@/types/disaster';
  115. import type { PersonGroupItem } from '@/types/person-group/type';
  116. import {
  117. getTaskExecutionList,
  118. getTaskInspectorList,
  119. withdrawTaskInspect,
  120. withdrawTaskApproval,
  121. updateTaskInspector,
  122. } from '@/api/disaster-precaution';
  123. import { TABLE_OPTIONS_EXECUTION, TASK_EXECUTION_TABLE_COLUMNS, TASK_EXECUTION_SEARCH_CONFIG } from './src/config';
  124. import { INSPECT_TYPE_MAP, TASK_STAGE_MAP, TASK_STAGE, USER_TYPE } from './src/constants/task-execution';
  125. import OverdueIcon from '@/assets/svg/overdue.svg';
  126. const router = useRouter();
  127. const userInfo = ref(false);
  128. const searchData = reactive({
  129. inspectType: '',
  130. taskState: '',
  131. });
  132. const tableData = ref<TaskExecutionListResponse[]>([]);
  133. const { tableConfig, pagination } = useTableConfig(TASK_EXECUTION_TABLE_COLUMNS, TABLE_OPTIONS_EXECUTION);
  134. // 添加刷新时间变量,用于触发视图更新
  135. const refreshTime = ref(Date.now());
  136. let refreshTimer: number | null = null;
  137. let taskManagementListQuery: QueryPageRequest<TaskExecutionListQuery> = {
  138. pageNumber: pagination.pageNumber,
  139. pageSize: pagination.pageSize,
  140. queryParam: {},
  141. };
  142. const handleSearch = () => {
  143. taskManagementListQuery.queryParam = {};
  144. if (searchData.inspectType !== '') {
  145. taskManagementListQuery.queryParam.inspectType = searchData.inspectType;
  146. }
  147. if (searchData.taskState !== '') {
  148. taskManagementListQuery.queryParam.taskState = searchData.taskState;
  149. }
  150. getTableData();
  151. };
  152. const handleSizeChange = (value: number) => {
  153. pagination.pageSize = value;
  154. taskManagementListQuery.pageSize = value;
  155. getTableData();
  156. };
  157. const handleCurrentChange = (value: number) => {
  158. pagination.pageNumber = value;
  159. taskManagementListQuery.pageNumber = value;
  160. getTableData();
  161. };
  162. const currentTaskId = ref<number>();
  163. const currentTaskInspectorList = ref<PersonGroupItem[]>([]);
  164. const handleAddCheckUser = async (id: number) => {
  165. currentTaskId.value = id;
  166. const res = await getTaskInspectorList(currentTaskId.value);
  167. currentTaskInspectorList.value = res.map((item) => {
  168. return {
  169. ...item,
  170. checked: true,
  171. };
  172. });
  173. userInfo.value = true;
  174. };
  175. const defaultRouterName = 'disaster-precaution-task-execution-detail';
  176. const handleCheckItem = (id: number, operationType: 'check' | 'approve' | 'view') => {
  177. router.push({
  178. name: defaultRouterName,
  179. params: {
  180. id,
  181. },
  182. query: {
  183. operationType,
  184. },
  185. });
  186. };
  187. const handleWithdrawTask = async (id: number, state: number) => {
  188. let message;
  189. if (state === TASK_STAGE.PENDING_APPROVAL) {
  190. await withdrawTaskInspect(id);
  191. message = '已成功撤回检查';
  192. } else if (state === TASK_STAGE.COMPLETED) {
  193. await withdrawTaskApproval(id);
  194. message = '已成功撤回审批';
  195. }
  196. ElMessage.success(message);
  197. getTableData();
  198. };
  199. const getTableData = async () => {
  200. tableConfig.loading = true;
  201. const res = await getTaskExecutionList(taskManagementListQuery);
  202. tableData.value = res.records;
  203. pagination.total = res.totalRow;
  204. tableConfig.loading = false;
  205. };
  206. const handleSubmitCheckUser = async (ids: number[]) => {
  207. if (!currentTaskId.value) return;
  208. await updateTaskInspector({
  209. id: currentTaskId.value,
  210. inspectorIdList: ids,
  211. });
  212. ElMessage.success('添加检查人员成功!');
  213. userInfo.value = false;
  214. getTableData();
  215. };
  216. // 判断是否超过应完成时间24小时
  217. const isOverdue24Hours = (updatedAt: string) => {
  218. if (!updatedAt) return false;
  219. const dueTime = new Date(updatedAt).getTime();
  220. const currentTime = refreshTime.value;
  221. // 计算24小时的毫秒数: 24 * 60 * 60 * 1000 = 86400000
  222. return currentTime > dueTime + 86400000;
  223. };
  224. onMounted(() => {
  225. getTableData();
  226. // 设置定时器,每秒更新一次时间
  227. refreshTimer = window.setInterval(() => {
  228. refreshTime.value = Date.now();
  229. }, 1000);
  230. });
  231. // 组件销毁时清除定时器
  232. onUnmounted(() => {
  233. if (refreshTimer !== null) {
  234. clearInterval(refreshTimer);
  235. refreshTimer = null;
  236. }
  237. });
  238. </script>
  239. <style scoped lang="scss">
  240. @use '@/styles/page-details-layout.scss' as *;
  241. @use '@/styles/page-main-layout.scss' as *;
  242. @use './src/style/task-execution.scss' as *;
  243. @use '@/views/disaster/style/disaster.scss' as *;
  244. </style>