PageDisposalRectificationItem.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. <template>
  2. <div class="disaster-precaution-container">
  3. <header class="disaster-precaution-container__header">
  4. <img :src="BackIcon" alt="back" class="back-icon" @click="router.back()" />
  5. <span class="disaster-precaution-container__title">整改处理</span>
  6. </header>
  7. <main class="disaster-precaution-container__main">
  8. <section class="disaster-information">
  9. <div class="disaster-information__title">
  10. <div class="disaster-information--line"></div>
  11. <span>受灾信息</span>
  12. </div>
  13. <div class="disaster-information__content">
  14. <section class="disaster-information__content--loss" v-if="LossReportItemFormData?.isLoss">
  15. <el-row :gutter="20">
  16. <el-col :span="8">
  17. <div class="disaster-information__content--item">
  18. <span class="label">受灾物品:</span>
  19. <span class="value">{{ LossReportItemFormData?.affectedItems }}</span>
  20. </div>
  21. </el-col>
  22. <el-col :span="8">
  23. <div class="disaster-information__content--item">
  24. <span class="label">地点:</span>
  25. <span class="value">
  26. {{ LossReportItemFormData?.buildingNo }}
  27. {{ LossReportItemFormData?.floorNo }}
  28. {{ LossReportItemFormData?.roomNo }}
  29. </span>
  30. </div>
  31. </el-col>
  32. <el-col :span="8">
  33. <div class="disaster-information__content--item">
  34. <span class="label">上报时间:</span>
  35. <span class="value">{{ LossReportItemFormData?.updatedAt }}</span>
  36. </div>
  37. </el-col>
  38. </el-row>
  39. <el-row :gutter="20">
  40. <el-col :span="8">
  41. <div class="disaster-information__content--item">
  42. <span class="label">影响安全评估:</span>
  43. <span class="value">{{ getSafetyLevel(LossReportItemFormData?.safetyLevel) }}</span>
  44. </div>
  45. </el-col>
  46. <el-col :span="8">
  47. <div class="disaster-information__content--item">
  48. <span class="label">是否影响正常工作:</span>
  49. <span class="value">
  50. {{ LossReportItemFormData?.isAffectWork === 1 ? '是' : '否' }}
  51. </span>
  52. </div>
  53. </el-col>
  54. <el-col :span="8">
  55. <div class="disaster-information__content--item" v-if="LossReportItemFormData?.estimatedLoss">
  56. <span class="label">损失金额评估:</span>
  57. <span class="value">{{ LossReportItemFormData?.estimatedLoss }}元</span>
  58. </div>
  59. </el-col>
  60. </el-row>
  61. <div class="disaster-information__content--item">
  62. <span class="label">损失描述:</span>
  63. <span class="value">{{ LossReportItemFormData?.description }}</span>
  64. </div>
  65. <div class="disaster-information__content--item" v-if="JSON.parse(LossReportItemFormData?.images).length">
  66. <span class="label">受灾图片:</span>
  67. <div class="image-container">
  68. <el-image
  69. v-for="(image, index) in JSON.parse(LossReportItemFormData?.images)"
  70. :key="index"
  71. :src="image"
  72. :zoom-rate="1.2"
  73. :max-scale="7"
  74. :min-scale="0.2"
  75. :preview-src-list="JSON.parse(LossReportItemFormData?.images)"
  76. show-progress
  77. :initial-index="index"
  78. fit="contain"
  79. />
  80. </div>
  81. </div>
  82. <div class="disaster-information__content--item" v-if="LossReportItemFormData?.remark">
  83. <span class="label">备注:</span>
  84. <span class="value">{{ LossReportItemFormData?.remark }}</span>
  85. </div>
  86. </section>
  87. </div>
  88. </section>
  89. <section class="disaster-information">
  90. <div class="disaster-information__title">
  91. <div class="disaster-information--line"></div>
  92. <span>整改处理</span>
  93. </div>
  94. <BasicForm ref="basicFormRef" :formData="ruleFormData" :formRules="formRules" :formConfig="ruleFormConfig">
  95. <template #fixImages>
  96. <UploadImages ref="uploadImagesRef" @uploadSuccess="handleUploadImageSuccess" />
  97. </template>
  98. <template #fixMaterials>
  99. <UploadFiles ref="uploadFilesRef" label="上传材料" @uploadSuccess="handleUploadFileSuccess" />
  100. </template>
  101. </BasicForm>
  102. </section>
  103. </main>
  104. <footer class="disaster-precaution-container__footer">
  105. <el-button @click="router.back()">取消</el-button>
  106. <el-button type="primary" v-if="isShowNextSubmit" @click="handleNextSubmit">提交,并处置下一条</el-button>
  107. <el-button type="primary" @click="handleSubmit">提交</el-button>
  108. </footer>
  109. <UploadLoading :form-loading="formLoading" v-if="formLoading" />
  110. </div>
  111. </template>
  112. <script lang="ts" setup>
  113. import BackIcon from 'assets/svg/back.svg';
  114. import { useRoute, useRouter } from 'vue-router';
  115. import { ref, onMounted, watch, computed, onUnmounted } from 'vue';
  116. import { ElMessage } from 'element-plus';
  117. import UploadLoading from '@/components/UploadLoading.vue';
  118. import BasicForm from '@/components/BasicForm.vue';
  119. import UploadImages from './src/components/UploadImages.vue';
  120. import UploadFiles from '@/views/disaster/components/UploadFiles.vue';
  121. import { useDisasterControlHook } from './src/hook';
  122. import { useFormConfigHook } from '@/hooks/useFormConfigHook';
  123. import { useUserInfoHook } from '@/views/disaster/hooks';
  124. import type { FileItem } from '@/views/disaster/types';
  125. import type { disasterReportRecordDetailListResponse, DisposalRectificationFormData } from '@/types/disaster-control';
  126. import { createDisposalRectification } from '@/api/disaster-control';
  127. import { uploadFileApi, UPLOAD_BIZ_TYPE } from '@/api/minio';
  128. import {
  129. DISPOSAL_RECTIFICATION_FORM_CONFIG,
  130. DISPOSAL_RECTIFICATION_FORM_DATA,
  131. DISPOSAL_RECTIFICATION_FORM_RULES,
  132. } from './src/config';
  133. const basicFormRef = ref<InstanceType<typeof BasicForm>>();
  134. const uploadImagesRef = ref<InstanceType<typeof UploadImages>>();
  135. const uploadFilesRef = ref<InstanceType<typeof UploadFiles>>();
  136. const LossReportItemFormData = ref<disasterReportRecordDetailListResponse>();
  137. const { getLossReportItem, getSafetyLevel, getSafetyLevelDict } = useDisasterControlHook();
  138. const { realname, id: userId } = useUserInfoHook();
  139. const router = useRouter();
  140. const route = useRoute();
  141. const id = ref<number>(Number(route.params.id));
  142. const handleTaskId = Number(route.query.handleTaskId);
  143. const fixTaskId = Number(route.query.fixTaskId);
  144. const formLoading = ref(false);
  145. const getLossReportItemData = async () => {
  146. const res = await getLossReportItem(handleTaskId, id.value);
  147. LossReportItemFormData.value = res;
  148. };
  149. const { ruleFormConfig, ruleFormData, formRules, cloneRuleFormData, beforeRouteLeave } =
  150. useFormConfigHook<DisposalRectificationFormData>(
  151. DISPOSAL_RECTIFICATION_FORM_CONFIG,
  152. DISPOSAL_RECTIFICATION_FORM_DATA,
  153. DISPOSAL_RECTIFICATION_FORM_RULES,
  154. );
  155. const rectificationIds = ref<number[]>([]);
  156. const isShowNextSubmit = computed(() => {
  157. return rectificationIds.value.length > 1 && rectificationIds.value.includes(id.value);
  158. });
  159. const handleUploadImageSuccess = (files: File[]) => {
  160. ruleFormData.uploadImages = files;
  161. };
  162. const handleUploadFileSuccess = (files: FileItem[]) => {
  163. ruleFormData.uploadFiles = files;
  164. };
  165. const formatImageList = async (file: File) => {
  166. if (!file) return file;
  167. const fileName = file.name;
  168. const res = await uploadFileApi({ bizType: UPLOAD_BIZ_TYPE.ATTACHMENT, fileName, file });
  169. return res.url;
  170. };
  171. const formatFileList = async (file: FileItem) => {
  172. if (!file.file) return file;
  173. const fileName = file.fileName;
  174. const res = await uploadFileApi({ bizType: UPLOAD_BIZ_TYPE.ATTACHMENT, fileName, file: file.file });
  175. const fileType = file.fileType;
  176. const fileSize = file.fileSize;
  177. const fileId = file.fileId;
  178. const fileUrl = res.url;
  179. return {
  180. fileName,
  181. fileType,
  182. fileSize,
  183. fileUrl,
  184. fileId,
  185. };
  186. };
  187. const submitDisposalRectification = async () => {
  188. const imagesListRes: string[] = await Promise.all(
  189. (ruleFormData.uploadImages || []).map((item) => formatImageList(item)),
  190. );
  191. const fileListRes: FileItem[] = await Promise.all(
  192. (ruleFormData.uploadFiles || []).map((item) => formatFileList(item)),
  193. );
  194. const imagesListString = JSON.stringify(imagesListRes);
  195. const fileListString = JSON.stringify(fileListRes);
  196. ruleFormData.fixImages = imagesListString;
  197. ruleFormData.fixMaterials = fileListString;
  198. await createDisposalRectification({
  199. fixTaskId,
  200. ...ruleFormData,
  201. });
  202. ElMessage.success('提交成功');
  203. cloneRuleFormData();
  204. };
  205. const handleSubmit = async () => {
  206. if (!basicFormRef.value) return;
  207. const validateResult = await basicFormRef.value.validateForm();
  208. if (!validateResult) return;
  209. formLoading.value = true;
  210. try {
  211. await submitDisposalRectification();
  212. router.back();
  213. } finally {
  214. formLoading.value = false;
  215. }
  216. };
  217. const defaultRouterName = 'disaster-control-disposal-rectification-item';
  218. const handleNextSubmit = async () => {
  219. if (!basicFormRef.value) return;
  220. const validateResult = await basicFormRef.value.validateForm();
  221. if (!validateResult) return;
  222. try {
  223. await submitDisposalRectification();
  224. } finally {
  225. formLoading.value = false;
  226. }
  227. // 查找当前id在数组中的索引
  228. const currentIndex = rectificationIds.value.indexOf(id.value);
  229. if (currentIndex !== -1) {
  230. // 从数组中移除当前id
  231. rectificationIds.value.splice(currentIndex, 1);
  232. // 确定下一个要处理的id
  233. let nextId;
  234. if (currentIndex < rectificationIds.value.length) {
  235. // 如果不是最后一个元素,取当前索引位置的元素(移除当前id后,下一个id会自动前移)
  236. nextId = rectificationIds.value[currentIndex];
  237. } else {
  238. // 如果是最后一个元素,取第一个元素
  239. nextId = rectificationIds.value[0];
  240. }
  241. // 保存更新后的数组到sessionStorage
  242. sessionStorage.setItem('rectificationIds', JSON.stringify(rectificationIds.value));
  243. router.replace({
  244. name: defaultRouterName,
  245. params: {
  246. id: nextId,
  247. },
  248. query: {
  249. handleTaskId,
  250. fixTaskId,
  251. },
  252. });
  253. }
  254. const mainElement = document.querySelector('.disaster-precaution-container__main');
  255. if (!mainElement) return;
  256. mainElement.scrollTop = 0;
  257. };
  258. const initRuleFormCreatedBy = () => {
  259. ruleFormData.createdBy = userId;
  260. ruleFormData.createdByName = realname;
  261. };
  262. onMounted(() => {
  263. initRuleFormCreatedBy();
  264. cloneRuleFormData();
  265. beforeRouteLeave();
  266. getLossReportItemData();
  267. getSafetyLevelDict();
  268. const sessionRectificationIds = sessionStorage.getItem('rectificationIds') || '[]';
  269. rectificationIds.value = JSON.parse(sessionRectificationIds);
  270. });
  271. onUnmounted(() => {
  272. sessionStorage.removeItem('rectificationIds');
  273. });
  274. watch(
  275. () => route.params.id,
  276. (newId) => {
  277. id.value = Number(newId);
  278. getLossReportItemData();
  279. basicFormRef.value?.clearValidate();
  280. initRuleFormCreatedBy();
  281. uploadImagesRef.value?.removeAllImages();
  282. uploadFilesRef.value?.removeAllFiles();
  283. cloneRuleFormData();
  284. },
  285. );
  286. </script>
  287. <style lang="scss" scoped>
  288. @use '../style/disaster.scss' as *;
  289. @use './src/style/common.scss' as *;
  290. @use './src//style/view-item.scss' as *;
  291. .disaster-precaution-container__main {
  292. display: flex !important;
  293. flex-direction: column !important;
  294. gap: 30px !important;
  295. }
  296. </style>