zhang_12370
5 天以前 1c4e88c1cb0639663f77a33dbab26c3cac71ad93
src/hooks/useFormData.js
@@ -1,8 +1,330 @@
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() {