| | |
| | | import { reactive } from "vue"; |
| | | /** |
| | | * 通用表单数据管理组合式函数 |
| | | * 提供表单数据的增删改查、验证、重置等功能 |
| | | */ |
| | | import { ref, reactive, computed, nextTick } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { clone } from "lodash"; |
| | | |
| | | /** |
| | | * 创建表单数据管理功能 |
| | | * @param {Object} options 配置选项 |
| | | * @param {Object} options.defaultForm 默认表单结构 |
| | | * @param {Function} options.addApi 新增API函数 |
| | | * @param {Function} options.updateApi 更新API函数 |
| | | * @param {Function} options.getDetailApi 获取详情API函数 |
| | | * @param {Object} options.rules 表单验证规则 |
| | | * @param {Function} options.beforeSubmit 提交前的数据处理函数 |
| | | * @param {Function} options.afterSubmit 提交后的回调函数 |
| | | * @param {Boolean} options.autoReset 提交成功后是否自动重置表单 |
| | | * @returns {Object} 返回表单管理相关的方法和状态 |
| | | */ |
| | | export function useFormData(options = {}) { |
| | | const { |
| | | defaultForm = {}, |
| | | addApi, |
| | | updateApi, |
| | | getDetailApi, |
| | | rules = {}, |
| | | beforeSubmit, |
| | | afterSubmit, |
| | | autoReset = true |
| | | } = options; |
| | | |
| | | export default function useFormData(initData) { |
| | | // 表单状态 |
| | | const form = ref({ ...defaultForm }); |
| | | const originalForm = ref({ ...defaultForm }); |
| | | const loading = ref(false); |
| | | const dialogVisible = ref(false); |
| | | const mode = ref('add'); // 'add', 'edit', 'view' |
| | | const title = ref(''); |
| | | const formRef = ref(null); |
| | | |
| | | // 计算属性 |
| | | const isAdd = computed(() => mode.value === 'add'); |
| | | const isEdit = computed(() => mode.value === 'edit'); |
| | | const isView = computed(() => mode.value === 'view'); |
| | | const isReadonly = computed(() => mode.value === 'view'); |
| | | const submitButtonText = computed(() => { |
| | | return isAdd.value ? '新增' : '保存'; |
| | | }); |
| | | |
| | | /** |
| | | * 打开表单对话框 |
| | | * @param {String} formMode 表单模式:'add', 'edit', 'view' |
| | | * @param {Object} data 编辑/查看时的数据 |
| | | * @param {String} customTitle 自定义标题 |
| | | */ |
| | | const openForm = async (formMode = 'add', data = null, customTitle = '') => { |
| | | mode.value = formMode; |
| | | |
| | | // 设置标题 |
| | | if (customTitle) { |
| | | title.value = customTitle; |
| | | } else { |
| | | const titleMap = { |
| | | add: '新增', |
| | | edit: '编辑', |
| | | view: '查看' |
| | | }; |
| | | title.value = titleMap[formMode] || '表单'; |
| | | } |
| | | |
| | | // 根据模式处理数据 |
| | | if (formMode === 'add') { |
| | | resetForm(); |
| | | } else if (data) { |
| | | // 编辑/查看模式,如果有详情API则获取最新数据 |
| | | if (getDetailApi && data.id) { |
| | | loading.value = true; |
| | | try { |
| | | const res = await getDetailApi(data.id); |
| | | if (res.code === 200) { |
| | | setFormData(res.data); |
| | | } else { |
| | | ElMessage.error('获取详情失败'); |
| | | setFormData(data); |
| | | } |
| | | } catch (error) { |
| | | console.error('获取详情失败:', error); |
| | | setFormData(data); |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | } else { |
| | | setFormData(data); |
| | | } |
| | | } |
| | | |
| | | dialogVisible.value = true; |
| | | }; |
| | | |
| | | /** |
| | | * 设置表单数据 |
| | | * @param {Object} data 表单数据 |
| | | */ |
| | | const setFormData = (data) => { |
| | | form.value = { ...defaultForm, ...data }; |
| | | originalForm.value = { ...form.value }; |
| | | }; |
| | | |
| | | /** |
| | | * 重置表单 |
| | | */ |
| | | const resetForm = () => { |
| | | form.value = { ...defaultForm }; |
| | | originalForm.value = { ...defaultForm }; |
| | | if (formRef.value) { |
| | | formRef.value.resetFields(); |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * 恢复表单到原始状态 |
| | | */ |
| | | const restoreForm = () => { |
| | | form.value = { ...originalForm.value }; |
| | | if (formRef.value) { |
| | | formRef.value.clearValidate(); |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * 表单验证 |
| | | * @returns {Promise<Boolean>} 验证结果 |
| | | */ |
| | | const validateForm = async () => { |
| | | if (!formRef.value) return true; |
| | | |
| | | try { |
| | | await formRef.value.validate(); |
| | | return true; |
| | | } catch (error) { |
| | | console.log('表单验证失败:', error); |
| | | return false; |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * 提交表单 |
| | | * @param {Object} customData 自定义提交数据 |
| | | * @returns {Promise<Boolean>} 提交结果 |
| | | */ |
| | | const submitForm = async (customData = null) => { |
| | | // 验证表单 |
| | | const isValid = await validateForm(); |
| | | if (!isValid) { |
| | | ElMessage.warning('请检查表单数据'); |
| | | return false; |
| | | } |
| | | |
| | | // 准备提交数据 |
| | | let submitData = customData || { ...form.value }; |
| | | |
| | | // 执行提交前处理 |
| | | if (beforeSubmit && typeof beforeSubmit === 'function') { |
| | | try { |
| | | submitData = await beforeSubmit(submitData, mode.value); |
| | | } catch (error) { |
| | | console.error('提交前处理失败:', error); |
| | | ElMessage.error('数据处理失败'); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | loading.value = true; |
| | | |
| | | try { |
| | | let res; |
| | | |
| | | if (isAdd.value && addApi) { |
| | | res = await addApi(submitData); |
| | | } else if (isEdit.value && updateApi) { |
| | | res = await updateApi(submitData); |
| | | } else { |
| | | ElMessage.error('未配置相应的API接口'); |
| | | return false; |
| | | } |
| | | |
| | | if (res.code === 200) { |
| | | const action = isAdd.value ? '新增' : '更新'; |
| | | ElMessage.success(`${action}成功`); |
| | | |
| | | // 执行提交后回调 |
| | | if (afterSubmit && typeof afterSubmit === 'function') { |
| | | await afterSubmit(res.data, mode.value, submitData); |
| | | } |
| | | |
| | | // 自动重置表单 |
| | | if (autoReset) { |
| | | closeForm(); |
| | | } |
| | | |
| | | return true; |
| | | } else { |
| | | ElMessage.error(res.msg || '操作失败'); |
| | | return false; |
| | | } |
| | | } catch (error) { |
| | | console.error('提交失败:', error); |
| | | ElMessage.error('操作失败,请稍后重试'); |
| | | return false; |
| | | } finally { |
| | | loading.value = false; |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * 关闭表单对话框 |
| | | */ |
| | | const closeForm = () => { |
| | | dialogVisible.value = false; |
| | | resetForm(); |
| | | }; |
| | | |
| | | /** |
| | | * 检查表单是否有变更 |
| | | * @returns {Boolean} 是否有变更 |
| | | */ |
| | | const hasChanges = computed(() => { |
| | | return JSON.stringify(form.value) !== JSON.stringify(originalForm.value); |
| | | }); |
| | | |
| | | /** |
| | | * 带确认的关闭表单 |
| | | */ |
| | | const closeFormWithConfirm = async () => { |
| | | if (hasChanges.value && !isView.value) { |
| | | try { |
| | | await ElMessageBox.confirm('表单数据已修改,确定要离开吗?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning', |
| | | }); |
| | | closeForm(); |
| | | } catch { |
| | | // 用户取消 |
| | | } |
| | | } else { |
| | | closeForm(); |
| | | } |
| | | }; |
| | | |
| | | /** |
| | | * 设置表单字段值 |
| | | * @param {String} field 字段名 |
| | | * @param {Any} value 字段值 |
| | | */ |
| | | const setFieldValue = (field, value) => { |
| | | form.value[field] = value; |
| | | }; |
| | | |
| | | /** |
| | | * 获取表单字段值 |
| | | * @param {String} field 字段名 |
| | | * @returns {Any} 字段值 |
| | | */ |
| | | const getFieldValue = (field) => { |
| | | return form.value[field]; |
| | | }; |
| | | |
| | | /** |
| | | * 批量设置表单字段值 |
| | | * @param {Object} values 字段值对象 |
| | | */ |
| | | const setFieldValues = (values) => { |
| | | Object.keys(values).forEach(key => { |
| | | if (form.value.hasOwnProperty(key)) { |
| | | form.value[key] = values[key]; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | /** |
| | | * 清除字段验证 |
| | | * @param {String|Array} fields 字段名或字段名数组 |
| | | */ |
| | | const clearValidate = (fields = null) => { |
| | | if (formRef.value) { |
| | | formRef.value.clearValidate(fields); |
| | | } |
| | | }; |
| | | |
| | | return { |
| | | // 状态 |
| | | form, |
| | | loading, |
| | | dialogVisible, |
| | | mode, |
| | | title, |
| | | formRef, |
| | | |
| | | // 计算属性 |
| | | isAdd, |
| | | isEdit, |
| | | isView, |
| | | isReadonly, |
| | | submitButtonText, |
| | | hasChanges, |
| | | |
| | | // 方法 |
| | | openForm, |
| | | setFormData, |
| | | resetForm, |
| | | restoreForm, |
| | | validateForm, |
| | | submitForm, |
| | | closeForm, |
| | | closeFormWithConfirm, |
| | | setFieldValue, |
| | | getFieldValue, |
| | | setFieldValues, |
| | | clearValidate |
| | | }; |
| | | } |
| | | |
| | | // 向后兼容的默认导出 |
| | | export default function useFormDataSimple(initData) { |
| | | const form = reactive(clone(initData, true)); |
| | | |
| | | function resetForm() { |