accidentCaseManagementDetail.vue 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. <template>
  2. <main class="safety-platform-container__main">
  3. <BasicForm ref="basicFormRef" :formData="ruleFormData" :formRules="isViewMode ? undefined : formRules"
  4. :formConfig="computedFormConfig">
  5. <template #fileUrl>
  6. <!-- <UploadFiles label="上传文件" :maxCount="1" :file-list="ruleFormData.attachmentUrl" :disabled="isViewMode"
  7. :allow-all-file-types="true" @uploadSuccess="handleUploadSuccess" /> -->
  8. <UploadFiles
  9. v-if="!isViewMode"
  10. label="上传文件"
  11. :maxCount="1"
  12. :file-list="ruleFormData.attachmentUrl"
  13. :disabled="isViewMode"
  14. :allow-all-file-types="true"
  15. @uploadSuccess="(list: FileItem[]) => handleUploadSuccess(list)"
  16. />
  17. <div class="file-list" v-else>
  18. <div class="file-item" v-for="file in ruleFormData.attachmentUrl" :key="file.fileId">
  19. <span class="file-item--name">{{ file.fileName }}</span>
  20. <div class="file-item--footer">
  21. <el-button link type="primary" @click="previewOnline(file.fileUrl, file.fileType)"
  22. >预览</el-button
  23. >
  24. <!-- <el-button link type="primary" @click.stop="downloadFile(file.fileUrl, file.fileName)"
  25. >下载</el-button
  26. > -->
  27. </div>
  28. </div>
  29. </div>
  30. </template>
  31. <template #content>
  32. <div class="editor-container">
  33. <Toolbar style="border-bottom: 1px solid #dcdfe6" :editor="editorRef" />
  34. <Editor style="height: 400px; overflow-y: auto" v-model="ruleFormData.description" mode="default"
  35. :defaultConfig="editorConfig" @on-created="handleEditorCreated" @on-change="handleEditorChange" />
  36. </div>
  37. </template>
  38. <template #status>
  39. <el-radio-group v-model="ruleFormData.status" :disabled="isViewMode">
  40. <el-radio :value="1">启用</el-radio>
  41. <el-radio :value="0">禁用</el-radio>
  42. </el-radio-group>
  43. </template>
  44. </BasicForm>
  45. <PreviewOnline ref="previewOnlineRef" />
  46. </main>
  47. <footer class="safety-platform-container__footer">
  48. <el-button @click="router.back()">返回</el-button>
  49. <el-button v-if="!isViewMode" type="primary" @click="handleSubmit">
  50. {{ isCreateMode ? '提交' : '保存' }}
  51. </el-button>
  52. </footer>
  53. </template>
  54. <script setup lang="ts">
  55. import { computed, onMounted, ref, shallowRef, onBeforeUnmount } from 'vue';
  56. import { useRoute, useRouter } from 'vue-router';
  57. import { ElMessage } from 'element-plus';
  58. import BasicForm from '@/components/BasicForm.vue';
  59. import UploadFiles from '@/components/UploadFiles/UploadFiles.vue';
  60. import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
  61. import '@wangeditor/editor/dist/css/style.css';
  62. import { useFormConfigHook } from '@/hooks/useFormConfigHook';
  63. import { ACADEMY_FILE_FORM_CONFIG, ACADEMY_FILE_FORM_DATA, ACADEMY_FILE_FORM_RULES } from '../configs/form';
  64. import {
  65. queryAccidentCaseDetail,
  66. saveAccidentCase,
  67. updateAccidentCase,
  68. type newAccidentCases,
  69. } from '@/api/safety-culture';
  70. import type { FileItem } from '@/components/UploadFiles/types';
  71. import { formatAttachmentList } from '@/components/UploadFiles/utils';
  72. import PreviewOnline from '@/views/disaster/components/PreviewOnline.vue';
  73. const router = useRouter();
  74. const route = useRoute();
  75. const operate = computed(() => (route.query.operate as string) || 'safety-culture-material-create');
  76. const currentId = computed(() => Number(route.query.id));
  77. const isCreateMode = computed(() => operate.value === 'safety-culture-material-create');
  78. const isEditMode = computed(() => operate.value === 'safety-culture-material-edit');
  79. const isViewMode = computed(() => operate.value === 'safety-culture-material-view');
  80. const previewOnlineRef = ref<InstanceType<typeof PreviewOnline>>();
  81. const { ruleFormData, formRules, ruleFormConfig, cloneRuleFormData, beforeRouteLeave } =
  82. useFormConfigHook(ACADEMY_FILE_FORM_CONFIG, ACADEMY_FILE_FORM_DATA, ACADEMY_FILE_FORM_RULES);
  83. const viewFormConfig = ref(
  84. ACADEMY_FILE_FORM_CONFIG.map((item) => ({
  85. ...item,
  86. componentProps: {
  87. ...item.componentProps,
  88. disabled: true,
  89. },
  90. })),
  91. );
  92. const computedFormConfig = computed(() => {
  93. if (isViewMode.value) {
  94. return viewFormConfig.value;
  95. }
  96. return ruleFormConfig.value;
  97. });
  98. const basicFormRef = ref<InstanceType<typeof BasicForm>>();
  99. const editorRef = shallowRef();
  100. const editorConfig = computed(() => ({
  101. placeholder: '请输入文档内容',
  102. MENU_CONF: {},
  103. }));
  104. const handleEditorCreated = (editor: any) => {
  105. editorRef.value = editor;
  106. if (isViewMode.value) {
  107. editor.disable();
  108. }
  109. };
  110. const handleEditorChange = () => {
  111. };
  112. const handleUploadSuccess = (files) => {
  113. ruleFormData.attachmentUrl = files;
  114. };
  115. // 将逗号分隔的URL字符串转换为FileItem数组
  116. const convertFileUrlToFileItems = (fileUrl: string): FileItem[] => {
  117. if (!fileUrl || !fileUrl.trim()) {
  118. return [];
  119. }
  120. // 按逗号分割URL
  121. const urls = fileUrl.split(',').map(url => url.trim()).filter(url => url);
  122. return urls.map((url, index) => {
  123. // 从URL中提取文件名
  124. const urlParts = url.split('/');
  125. const fileName = urlParts[urlParts.length - 1] || `附件${index + 1}`;
  126. // 根据文件扩展名判断文件类型
  127. const extension = fileName.split('.').pop()?.toLowerCase() || '';
  128. let fileType = 'pdf';
  129. if (extension === 'doc' || extension === 'docx') {
  130. fileType = 'word';
  131. } else if (extension === 'xls' || extension === 'xlsx') {
  132. fileType = 'excel';
  133. } else if (extension === 'ppt' || extension === 'pptx') {
  134. fileType = 'ppt';
  135. }
  136. return {
  137. fileId: Date.now() + index,
  138. fileName,
  139. fileType,
  140. fileSize: '0',
  141. fileUrl: url,
  142. };
  143. });
  144. };
  145. const handleValidate = async () => {
  146. if (!basicFormRef.value) return;
  147. const res = await basicFormRef.value.validateForm();
  148. return res;
  149. };
  150. const getDetail = async () => {
  151. if (!currentId.value) return;
  152. try {
  153. const res = await queryAccidentCaseDetail(currentId.value);
  154. if (res) {
  155. ruleFormData.caseName = res.caseName || '';
  156. ruleFormData.categoryName = res.categoryName || '';
  157. ruleFormData.status = res.status ?? 1;
  158. ruleFormData.description = res.description || '';
  159. ruleFormData.attachmentUrl = JSON.parse(res.attachmentUrl || res.fileUrl || '');
  160. }
  161. cloneRuleFormData();
  162. } catch (e) {
  163. console.error('获取事故案例详情失败:', e);
  164. ElMessage.error('获取详情失败');
  165. }
  166. };
  167. const handleSubmit = async () => {
  168. const res = await handleValidate();
  169. if (!res) return;
  170. if (!ruleFormData.attachmentUrl || ruleFormData.attachmentUrl.length === 0) {
  171. ElMessage.warning('请上传文件');
  172. return;
  173. }
  174. console.log('ruleFormData', ruleFormData);
  175. try {
  176. const uploadedFileList = await formatAttachmentList(ruleFormData.attachmentUrl);
  177. const basePayload: newAccidentCases = {
  178. caseName: ruleFormData.caseName,
  179. categoryName: ruleFormData.categoryName,
  180. description: ruleFormData.description,
  181. attachmentUrl:JSON.stringify(uploadedFileList),
  182. status: ruleFormData.status,
  183. };
  184. if (isCreateMode.value) {
  185. await saveAccidentCase(basePayload);
  186. ElMessage.success('创建成功');
  187. } else if (isEditMode.value && currentId.value) {
  188. await updateAccidentCase({
  189. id: currentId.value,
  190. ...basePayload,
  191. });
  192. ElMessage.success('保存成功');
  193. }
  194. router.back();
  195. } catch (e) {
  196. console.error('保存事故案例失败:', e);
  197. ElMessage.error('保存失败,请重试');
  198. }
  199. };
  200. const previewOnline = (url: string | undefined, type) => {
  201. if (url) {
  202. previewOnlineRef.value?.open(url, type);
  203. }
  204. };
  205. onMounted(() => {
  206. cloneRuleFormData();
  207. // beforeRouteLeave();
  208. if (isEditMode.value || isViewMode.value) {
  209. getDetail();
  210. }
  211. });
  212. onBeforeUnmount(() => {
  213. const editor = editorRef.value;
  214. if (editor == null) return;
  215. editor.destroy();
  216. });
  217. </script>
  218. <style scoped lang="scss">
  219. @use '@/styles/page-details-layout.scss' as *;
  220. .editor-container {
  221. width: 100%;
  222. border: 1px solid #dcdfe6;
  223. border-radius: 4px;
  224. overflow: hidden;
  225. }
  226. .content-display {
  227. min-height: 200px;
  228. padding: 12px;
  229. border: 1px solid #dcdfe6;
  230. border-radius: 4px;
  231. background-color: #f5f7fa;
  232. }
  233. .file-display {
  234. .file-link {
  235. color: #409eff;
  236. text-decoration: none;
  237. &:hover {
  238. text-decoration: underline;
  239. }
  240. }
  241. }
  242. .no-file {
  243. color: rgba(0, 0, 0, 0.65);
  244. }
  245. .image-uploader {
  246. :deep(.el-upload--picture-card) {
  247. width: 80px !important;
  248. height: 80px !important;
  249. line-height: 80px;
  250. }
  251. :deep(.el-upload-list--picture-card .el-upload-list__item) {
  252. width: 80px !important;
  253. height: 80px !important;
  254. }
  255. }
  256. </style>
  257. <style lang="scss">
  258. .w-e-full-screen-container {
  259. inset: 0 !important;
  260. z-index: 3000 !important;
  261. }
  262. .w-e-full-screen-container .w-e-text-container {
  263. height: calc(100vh - 42px) !important;
  264. }
  265. </style>