hiddenTroubleReviewManagementDetail.vue 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. <template>
  2. <main class="safety-platform-container__main">
  3. <BasicForm
  4. ref="basicFormRef"
  5. :formData="ruleFormData"
  6. :formRules="isViewMode ? undefined : formRules"
  7. :formConfig="computedFormConfig"
  8. >
  9. <template #fileFormat>
  10. <el-radio-group v-model="ruleFormData.fileFormat" :disabled="isViewMode">
  11. <el-radio value="PDF">PDF</el-radio>
  12. <el-radio value="WORD">WORD</el-radio>
  13. </el-radio-group>
  14. </template>
  15. <template #fileUrl>
  16. <UploadFiles
  17. v-if="!isViewMode"
  18. label="上传文件"
  19. :maxCount="1"
  20. :fileList="uploadFileList"
  21. @uploadSuccess="handleUploadSuccess"
  22. />
  23. <div v-else-if="ruleFormData.fileUrl" class="file-display">
  24. <a :href="ruleFormData.fileUrl" target="_blank" class="file-link">{{ getFileName(ruleFormData.fileUrl) }}</a>
  25. </div>
  26. <span v-else class="no-file">暂无文件</span>
  27. </template>
  28. <template #content>
  29. <div v-if="!isViewMode" class="editor-container">
  30. <Toolbar style="border-bottom: 1px solid #dcdfe6" :editor="editorRef" />
  31. <Editor
  32. style="height: 400px; overflow-y: auto"
  33. v-model="ruleFormData.content"
  34. mode="default"
  35. :defaultConfig="editorConfig"
  36. @on-created="handleEditorCreated"
  37. @on-change="handleEditorChange"
  38. />
  39. </div>
  40. <div v-else class="content-display" v-html="ruleFormData.content || '暂无内容'"></div>
  41. </template>
  42. <template #status>
  43. <el-radio-group v-model="ruleFormData.status" :disabled="isViewMode">
  44. <el-radio :value="1">启用</el-radio>
  45. <el-radio :value="0">禁用</el-radio>
  46. </el-radio-group>
  47. </template>
  48. </BasicForm>
  49. </main>
  50. <footer class="safety-platform-container__footer">
  51. <el-button @click="router.back()">返回</el-button>
  52. <el-button v-if="!isViewMode" type="primary" @click="handleSubmit">
  53. {{ isCreateMode ? '提交' : '保存' }}
  54. </el-button>
  55. </footer>
  56. </template>
  57. <script setup lang="ts">
  58. import { computed, onMounted, ref, shallowRef, onBeforeUnmount } from 'vue';
  59. import { useRoute, useRouter } from 'vue-router';
  60. import { ElMessage } from 'element-plus';
  61. import BasicForm from '@/components/BasicForm.vue';
  62. import UploadFiles from '@/components/UploadFiles/UploadFiles.vue';
  63. import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
  64. import '@wangeditor/editor/dist/css/style.css';
  65. import { useFormConfigHook } from '@/hooks/useFormConfigHook';
  66. import { ACADEMY_FILE_FORM_CONFIG, ACADEMY_FILE_FORM_DATA, ACADEMY_FILE_FORM_RULES } from '../configs/form';
  67. import {
  68. queryAcademyFileById,
  69. saveAcademyFile,
  70. updateAcademyFile,
  71. type ProductionSafetyFile,
  72. } from '@/api/production-safety-system';
  73. import type { FileItem } from '@/components/UploadFiles/types';
  74. const router = useRouter();
  75. const route = useRoute();
  76. const operate = computed(() => (route.query.operate as string) || 'hidden-trouble-review-create');
  77. const currentId = computed(() => Number(route.query.id));
  78. const isCreateMode = computed(() => operate.value === 'hidden-trouble-review-create');
  79. const isEditMode = computed(() => operate.value === 'hidden-trouble-review-edit');
  80. const isViewMode = computed(() => operate.value === 'hidden-trouble-review-view');
  81. const { ruleFormData, formRules, ruleFormConfig, cloneRuleFormData, beforeRouteLeave } =
  82. useFormConfigHook(ACADEMY_FILE_FORM_CONFIG, ACADEMY_FILE_FORM_DATA, ACADEMY_FILE_FORM_RULES);
  83. // 查看模式下,所有字段设为只读
  84. const viewFormConfig = ref(
  85. ACADEMY_FILE_FORM_CONFIG.map((item) => ({
  86. ...item,
  87. componentProps: {
  88. ...item.componentProps,
  89. disabled: true,
  90. },
  91. })),
  92. );
  93. const computedFormConfig = computed(() => {
  94. if (isViewMode.value) {
  95. return viewFormConfig.value;
  96. }
  97. return ruleFormConfig.value;
  98. });
  99. const basicFormRef = ref<InstanceType<typeof BasicForm>>();
  100. // 富文本编辑器
  101. const editorRef = shallowRef();
  102. const editorConfig = {
  103. placeholder: '请输入文档内容',
  104. MENU_CONF: {},
  105. };
  106. const handleEditorCreated = (editor: any) => {
  107. editorRef.value = editor;
  108. };
  109. const handleEditorChange = () => {
  110. // 编辑器内容变化时的处理
  111. };
  112. // 文件上传
  113. const uploadFileList = ref<FileItem[]>([]);
  114. const handleUploadSuccess = (files: FileItem[]) => {
  115. uploadFileList.value = files;
  116. if (files.length > 0 && files[0].file) {
  117. // 这里需要实际上传文件到服务器,获取 fileUrl
  118. // 暂时使用文件对象,实际应该调用上传接口
  119. ruleFormData.fileUrl = files[0].file.name; // 临时处理,需要替换为实际上传后的URL
  120. }
  121. };
  122. const getFileName = (url: string) => {
  123. if (!url) return '';
  124. const parts = url.split('/');
  125. return parts[parts.length - 1];
  126. };
  127. const handleValidate = async () => {
  128. if (!basicFormRef.value) return;
  129. const res = await basicFormRef.value.validateForm();
  130. return res;
  131. };
  132. const getDetail = async () => {
  133. if (!currentId.value) return;
  134. try {
  135. const res = await queryAcademyFileById(currentId.value);
  136. if (res) {
  137. // 映射接口字段到表单字段
  138. ruleFormData.fileName = res.fileName || '';
  139. ruleFormData.classifyName = res.classifyName || '';
  140. ruleFormData.fileCode = res.fileCode || '';
  141. ruleFormData.fileVersion = res.fileVersion || '';
  142. ruleFormData.fileFormat = res.fileFormat || '';
  143. ruleFormData.releaseDate = res.releaseDate || '';
  144. ruleFormData.fileUrl = res.fileUrl || '';
  145. ruleFormData.content = res.content || '';
  146. ruleFormData.status = res.status ?? 1;
  147. // 如果有文件URL,转换为FileItem格式
  148. if (res.fileUrl) {
  149. uploadFileList.value = [
  150. {
  151. fileId: Date.now(),
  152. fileName: getFileName(res.fileUrl),
  153. fileType: res.fileFormat?.toLowerCase() === 'pdf' ? 'pdf' : 'word',
  154. fileSize: '0KB',
  155. },
  156. ];
  157. }
  158. }
  159. cloneRuleFormData();
  160. } catch (e) {
  161. console.error('获取院级文件详情失败:', e);
  162. ElMessage.error(e?.message || e?.data || '获取详情失败');
  163. }
  164. };
  165. const handleSubmit = async () => {
  166. const res = await handleValidate();
  167. if (!res) return;
  168. try {
  169. const basePayload: ProductionSafetyFile = {
  170. fileName: ruleFormData.fileName,
  171. classifyName: ruleFormData.classifyName,
  172. fileCode: ruleFormData.fileCode,
  173. fileVersion: ruleFormData.fileVersion,
  174. fileFormat: ruleFormData.fileFormat,
  175. releaseDate: ruleFormData.releaseDate,
  176. fileUrl: ruleFormData.fileUrl || undefined,
  177. content: ruleFormData.content || undefined,
  178. status: ruleFormData.status ?? 1,
  179. };
  180. if (isCreateMode.value) {
  181. await saveAcademyFile(basePayload);
  182. ElMessage.success('创建成功');
  183. } else if (isEditMode.value && currentId.value) {
  184. await updateAcademyFile({
  185. id: currentId.value,
  186. ...basePayload,
  187. });
  188. ElMessage.success('保存成功');
  189. }
  190. router.back();
  191. } catch (e) {
  192. console.error('保存院级文件失败:', e);
  193. ElMessage.error(e?.message || e?.data || '保存失败,请重试');
  194. }
  195. };
  196. onMounted(() => {
  197. cloneRuleFormData();
  198. beforeRouteLeave();
  199. if (isEditMode.value || isViewMode.value) {
  200. getDetail();
  201. }
  202. });
  203. onBeforeUnmount(() => {
  204. const editor = editorRef.value;
  205. if (editor == null) return;
  206. editor.destroy();
  207. });
  208. </script>
  209. <style scoped lang="scss">
  210. @use '@/styles/page-details-layout.scss' as *;
  211. .editor-container {
  212. width: 100%;
  213. border: 1px solid #dcdfe6;
  214. border-radius: 4px;
  215. overflow: hidden;
  216. }
  217. .content-display {
  218. min-height: 200px;
  219. padding: 12px;
  220. border: 1px solid #dcdfe6;
  221. border-radius: 4px;
  222. background-color: #f5f7fa;
  223. }
  224. .file-display {
  225. .file-link {
  226. color: #409eff;
  227. text-decoration: none;
  228. &:hover {
  229. text-decoration: underline;
  230. }
  231. }
  232. }
  233. .no-file {
  234. color: rgba(0, 0, 0, 0.65);
  235. }
  236. </style>