MapEditModal.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. <template>
  2. <BasicModal
  3. v-bind="$attrs"
  4. @register="registerModal"
  5. @ok="handleSubmit"
  6. title="图纸管理"
  7. width="900px"
  8. :min-height="600"
  9. :max-height="1000"
  10. scrollable
  11. centered
  12. destroyOnClose
  13. :bodyStyle="{ padding: '20px' }"
  14. >
  15. <a-form
  16. ref="formRef"
  17. :model="formData"
  18. :rules="formRules"
  19. layout="horizontal"
  20. :label-col="{ span: 4 }"
  21. :wrapper-col="{ span: 20 }"
  22. >
  23. <div class="edit-container">
  24. <div class="mine-base-info">
  25. <!-- 添加 key 强制重新渲染 -->
  26. <a-form-item
  27. v-for="schema in visibleFormSchemas"
  28. :key="schema.field + formKey"
  29. :name="schema.field"
  30. :label="schema.label"
  31. :rules="schema.rules"
  32. >
  33. <!-- Upload 组件特殊处理 -->
  34. <template v-if="schema.component === 'Upload'">
  35. <a-upload
  36. v-model:file-list="formData[schema.field]"
  37. v-bind="schema.componentProps"
  38. @change="(info) => handleUploadChange(info, schema.field)"
  39. :customRequest="handleCustomUpload"
  40. :maxCount="1"
  41. >
  42. <a-button>
  43. <template #icon><UploadOutlined /></template>
  44. 上传文件
  45. </a-button>
  46. </a-upload>
  47. </template>
  48. <!-- 其他组件动态渲染 -->
  49. <template v-else>
  50. <component
  51. :is="getComponent(schema.component)"
  52. :value="formData[schema.field]"
  53. @update:value="handleUpdateValue(schema.field, $event)"
  54. @change="(val) => handleComponentChange(schema.field, val, schema)"
  55. v-bind="schema.componentProps"
  56. :placeholder="`请输入${schema.label}`"
  57. style="width: 100%"
  58. />
  59. </template>
  60. </a-form-item>
  61. </div>
  62. </div>
  63. </a-form>
  64. </BasicModal>
  65. </template>
  66. <script setup lang="ts">
  67. import { ref, computed, nextTick } from 'vue';
  68. import { BasicModal, useModalInner } from '/@/components/Modal';
  69. import { mapEditForm } from '../cad.data';
  70. import { Select, Input, message } from 'ant-design-vue';
  71. import { UploadOutlined } from '@ant-design/icons-vue';
  72. import MineCascader from '/@/components/Form/src/jeecg/components/MineCascader/MineCascader.vue';
  73. import type { FormInstance } from 'ant-design-vue/es/form';
  74. import { uploadFile, updateMineFile, addMineFile } from '../cad.api';
  75. import type { UploadChangeParam, UploadFile } from 'ant-design-vue/es/upload/interface';
  76. import dayjs from 'dayjs';
  77. import { useMineDepartmentStore } from '/@/store/modules/mine';
  78. // 表单 key(用于强制重新渲染)
  79. const formKey = ref(0);
  80. // 标识当前是否为新增模式
  81. const isAddMode = ref(false);
  82. // 组件映射表(Upload 单独处理)
  83. const componentMap: Record<string, any> = {
  84. Input,
  85. Select,
  86. InputTextArea: Input.TextArea,
  87. MineCascader,
  88. };
  89. // 定义事件发射
  90. const emit = defineEmits(['success']);
  91. // 表单实例
  92. const formRef = ref<FormInstance>();
  93. // 表单数据
  94. const formData = ref({
  95. id: '',
  96. mineCode: '',
  97. mineName: '',
  98. fileName: '',
  99. fileType: '',
  100. fileAttach: [] as any[],
  101. remark: '',
  102. filePath: '',
  103. createTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
  104. });
  105. // 获取部门信息
  106. const mineStore = useMineDepartmentStore();
  107. // 表单规则
  108. const formRules = computed(() => {
  109. const rules: Record<string, any> = {};
  110. mapEditForm.forEach((item) => {
  111. if (item.rules) {
  112. rules[item.field] = item.rules;
  113. }
  114. });
  115. return rules;
  116. });
  117. const visibleFormSchemas = computed(() => {
  118. // 定义新增模式下允许显示的字段
  119. const allowedFieldsInAddMode = ['mineCode', 'fileName', 'fileType', 'fileAttach' , 'remark'];
  120. const allowedFieldsInEditMode = ['mineCode', 'fileName', 'fileType', 'fileAttach', 'remark'];
  121. return mapEditForm.filter(item => {
  122. // 1. 基础隐藏逻辑
  123. if (item.ifShow === false || item.show === false) {
  124. return false;
  125. }
  126. // 2. 如果是新增模式,只保留允许显示的字段
  127. if (isAddMode.value) {
  128. return allowedFieldsInAddMode.includes(item.field);
  129. }
  130. return allowedFieldsInEditMode.includes(item.field);;
  131. });
  132. });
  133. // 获取组件
  134. const getComponent = (componentName: string) => {
  135. return componentMap[componentName] || Input;
  136. };
  137. // 处理值更新
  138. const handleUpdateValue = (field: string, value: any) => {
  139. formData.value[field as keyof typeof formData.value] = value;
  140. };
  141. const handleComponentChange = (field: string, value: any, schema: any) => {
  142. console.log(`字段 ${field} 触发 change 事件,新值为:`, schema, value);
  143. if (field === 'mineCode') {
  144. if (value) {
  145. const depart = mineStore.findDepartById(value, mineStore.getDepartTree);
  146. if (depart) {
  147. // 同步更新 formData 中的 mineName
  148. formData.value.mineName = depart.departName;
  149. console.log('自动填充矿区名称:', depart.departName);
  150. } else {
  151. // 如果没找到,可能数据还没加载完,或者 ID 无效
  152. console.warn('未找到对应的矿区名称');
  153. formData.value.mineName = '';
  154. }
  155. } else {
  156. // 如果清空了选择,也清空名称
  157. formData.value.mineName = '';
  158. }
  159. }
  160. };
  161. /**
  162. * 自定义上传请求
  163. * 使用 defHttp 封装的 uploadFile 接口
  164. */
  165. const handleCustomUpload = async (options: any) => {
  166. const { file, onSuccess, onError } = options;
  167. // 构建 FormData
  168. const formDataObj = new FormData();
  169. formDataObj.append('file', file);
  170. // 添加其他参数
  171. // formDataObj.append('biz', 'cadFile');
  172. try {
  173. // 调用上传接口
  174. const res = await uploadFile(formDataObj);
  175. if (res&& res.success && res.message) {
  176. // 成功回调,将后端返回的路径挂载到 file 对象上,方便后续提交或展示
  177. formData.value.filePath = res.message;
  178. const uploadedFileItem = {
  179. uid: file.uid || Date.now(), // 唯一标识
  180. name: file.name, // 文件名
  181. status: 'done', // 状态:完成
  182. url: res.message, // 预览地址(如果有)
  183. response: res // 原始响应
  184. };
  185. // 更新表单中的 fileAttach 数组,触发视图更新
  186. formData.value.fileAttach = [uploadedFileItem];
  187. onSuccess(res, file);
  188. } else {
  189. throw new Error(`${res.message}`);
  190. }
  191. } catch (err: any) {
  192. console.error('上传错误:', err);
  193. message.error(err.message || '文件上传失败');
  194. onError(err);
  195. }
  196. };
  197. /**
  198. * 上传状态改变处理
  199. * @param info 上传信息
  200. * @param field 当前绑定的表单字段名
  201. */
  202. const handleUploadChange = async (info: UploadChangeParam, field: string) => {
  203. const { fileList } = info;
  204. // const status = info.file.status;
  205. // // 1. 更新文件列表显示
  206. // // formData.value[field as keyof typeof formData.value] = fileList;
  207. // // 2. 如果上传成功,提取 filePath 并存储
  208. // if (info.file.status === 'done') {
  209. // const response = info.file.response;
  210. // // 假设后端返回的 message 就是我们要的 filePath
  211. // const filePath = response?.message;
  212. // if (filePath) {
  213. // const currentFile = fileList[fileList.length - 1];
  214. // if(currentFile) {
  215. // currentFile.customPath = filePath;
  216. // }
  217. // message.success(`${info.file.name} 文件上传成功`);
  218. // }
  219. // } else if (info.file.status === 'error') {
  220. // message.error(`${info.file.name} 文件上传失败`);
  221. // }
  222. };
  223. // 注册模态框并初始化数据
  224. const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
  225. setModalProps({ confirmLoading: false });
  226. // 先重置 formKey 强制重新渲染
  227. formKey.value++;
  228. // 如果没有data或者id为空则为新增模式
  229. isAddMode.value = !data || !data.id;
  230. // 重置表单数据
  231. await nextTick();
  232. formData.value = {
  233. id: '',
  234. mineCode: '',
  235. mineName: '',
  236. fileName: '',
  237. fileType: '',
  238. fileAttach: [],
  239. filePath: '',
  240. remark: '',
  241. createTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
  242. };
  243. // 编辑模式时填充现有数据
  244. if (data && !isAddMode.value) {
  245. const record = data;
  246. // 处理文件回显
  247. let fileList: UploadFile[] = [];
  248. if (record.filePath) {
  249. const originalFileName = record.filePath.split('/').pop() || record.filePath.split('\\').pop() || '已上传文件';
  250. // 后端返回了 filePath,构造一个 fake file 对象用于展示
  251. fileList = [{
  252. uid: '-1',
  253. name: originalFileName,
  254. status: 'done',
  255. url: record.filePath,
  256. response: { message: record.filePath }
  257. } as UploadFile];
  258. }
  259. // 使用 nextTick 确保数据填充后视图更新
  260. await nextTick();
  261. formData.value = {
  262. id: record.id || '',
  263. mineCode: record.mineCode || '',
  264. mineName: record.mineName || '',
  265. fileName: record.fileName || '',
  266. fileType: record.fileType || '',
  267. fileAttach: fileList || [],
  268. filePath: record.filePath || '',
  269. remark: record.remark || '',
  270. createTime: record.createTime || dayjs().format('YYYY-MM-DD HH:mm:ss'),
  271. };
  272. }
  273. });
  274. // 提交表单处理
  275. async function handleSubmit() {
  276. try {
  277. if (!formRef.value) return;
  278. await formRef.value.validate();
  279. setModalProps({ confirmLoading: true });
  280. // 构造提交数据
  281. const submitData = {
  282. ...formData.value,
  283. };
  284. submitData.createTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
  285. delete submitData.fileAttach;
  286. // 调用更新接口
  287. await updateMineFile(submitData);
  288. if (isAddMode.value) {
  289. await addMineFile(submitData);
  290. } else {
  291. await updateMineFile(submitData);
  292. }
  293. message.success(isAddMode.value ? '新增成功' : '修改成功');
  294. emit('success',true);
  295. closeModal();
  296. } catch (error: any) {
  297. console.error('提交失败:', error);
  298. message.error(error.message || '操作失败,请检查必填项');
  299. } finally {
  300. setModalProps({ confirmLoading: false });
  301. }
  302. }
  303. </script>
  304. <style scoped>
  305. .mine-base-info {
  306. padding: 12px 16px;
  307. border: 1px solid #cad2e0;
  308. border-radius: 5px;
  309. background-color: #f8f9fc;
  310. }
  311. </style>