1. 表单
    hook

import type { ValidateErrorEntity } from 'ant-design-vue/es/form/interface';

export function useFormFun(data: any) {
  /**表单model */
  const formState = <any>ref(data);
  /**表单校验提示 */
  const validateMessages = { required: '${label}不能为空' };
  /**表单对象ref */
  const formRef = ref<any>();

  /**
   * @method 设置表单数据
   * @param data 需要设置的数据
   * @returns  void
   */
  function setFormStateData(data: Record<string, any>) {
    Object.assign(formState.value, data);
    console.log(Object.assign(formState.value, data));
  }

  /**
   * @method 表单校验
   * @returns  Promise<Record<string, any> | ValidateErrorEntity>
   */
  async function formValidateFields(): Promise<
    Record<string, any> | ValidateErrorEntity
  > {
    return new Promise<Record<string, any> | ValidateErrorEntity>(
      async (resolve, reject) => {
        try {
          const res = await formRef.value?.validateFields();
          resolve(formState.value);
        } catch (error: any) {
          // alertError(error);
          reject(error);
        }
      }
    );
  }

  /**
   * @method 移除表单项的校验结果。传入待移除的表单项的 name 属性或者 name 组成的数组,如不传则移除整个表单的校验结果
   * @param nameList 表单对应的name字段组成的数组
   * @returns void
   */
  function clearValidate(nameList?: string | (string | number)[]) {
    if (!nameList) {
      nextTick(() => {
        formRef.value?.clearValidate();
        return;
      });
    }
    if (nameList?.length) {
      if (!Array.isArray(nameList)) {
        throw new Error('移除表单校验的name必须为一个数组');
      } else {
        formRef.value?.clearValidate(nameList);
      }
    }
  }

  /**
   * @method 对整个表单进行重置,将所有字段值重置为初始值并移除校验结果
   * @param nameList 表单对应的name字段组成的数组
   * @returns void
   */
  function resetFields(nameList?: string | (string | number)[]) {
    if (!nameList) {
      formRef.value?.resetFields();
      return;
    }
    if (nameList?.length) {
      if (!Array.isArray(nameList)) {
        throw new Error('重置的name必须为一个数组');
      } else {
        formRef.value?.resetFields(nameList);
      }
    }
  }

  return {
    formRef,
    formState,
    resetFields,
    clearValidate,
    validateMessages,
    setFormStateData,
    formValidateFields,
  };
}

配置文件:

export const editItem: SchemaItem[] = [
  {
    label: '工单类型',
    name: 'orderType',
    component: 'Select',
    componentProps: {
      allowClear: true,
      placeholder: '工单类型',
      options: [
        { value: '1', label: '装维单' },
        { value: '2', label: '勘察单' },
      ],
    },
    formItemProps: {
      rules: [
        {
          required: true,
          message: '请选择工单类型',
          trigger: 'blur',
        },
      ],
    },
  },
  {
    label: '需求区域',
    name: 'demandName',
    component: 'slot',
    formItemProps: {
      rules: [
        {
          required: true,
          message: '请选择需求区域',
          trigger: 'blur',
        },
      ],
    },
  },
  {
    label: '下发对象',
    name: 'issuingType',
    component: 'RadioGroup',
    componentProps: {
      allowClear: true,
      placeholder: '下发对象',
      options: [
        { label: '云中台接口人', value: '1' },
        { label: '交付队伍', value: '2' },
      ],
    },
    formItemProps: {
      rules: [
        {
          required: true,
          message: '请选择下发对象',
          trigger: 'blur',
        },
      ],
    },
  },
  {
    label: '云中台接口人',
    // name: 'cloudInterfacePerson',
    name: 'issuingObj',
    component: 'Select',
    componentProps: {
      allowClear: true,
      placeholder: '接口人/交付队伍',
      options: [],
    },
    formItemProps: {
      rules: [
        {
          required: true,
          message: '请选择云中台接口人/交付队伍',
          trigger: 'blur',
        },
      ],
    },
  },
  {
    label: '备注',
    name: 'remark',
    component: 'Textarea',
    componentProps: {
      showCount: true,
      maxlength: 100,
    },
  },
];

组件封装:

import {
  Input,
  Textarea,
  InputNumber,
  Select,
  RadioGroup,
  CheckboxGroup,
  DatePicker,
  TimePicker,
} from 'ant-design-vue';

export const componentsMap = <any>{
  Input,
  Textarea,
  InputNumber,
  Select,
  RadioGroup,
  CheckboxGroup,
  DatePicker,
  TimePicker,
};
<template>
  <a-form :model="formState" ref="formRef" v-bind="$attrs">
    <a-row class="row">
      <a-col :span="col" v-for="item in props.schema" :key="item.name">
        <a-form-item :name="item.name" :label="item.label"  v-bind="item.formItemProps">
          <template v-if="componentsMap[item.component]">
            <component :is="componentsMap[item.component]" v-bind="item.componentProps"
              v-model:value="formState[item.name]" @change="(value: any, option: any) =>
                  onItemChange(value, option, item.name, item.component)
                ">
              <template v-if="item.componentProps?.prefixOBj" #prefix>
                <component v-if="item.componentProps.prefixOBj.icon" :is="item.componentProps.prefixOBj.icon" />
                <span v-else v-html="item.componentProps?.prefixOBj.html"></span>
              </template>
              <template v-if="item.componentProps?.suffixOBj" #suffix>
                <component v-if="item.componentProps.suffixOBj.icon" :is="item.componentProps.suffixOBj.icon" />
                <span v-else v-html="item.componentProps?.suffixOBj.html"></span>
              </template>
            </component>
          </template>
          <template v-else>
            <slot :name="item.name"></slot>
          </template>
        </a-form-item>
      </a-col>
      <a-col :span="btnCol">
        <!-- 页脚 -->
        <a-form-item v-if="footer">
          <a-button type="primary" :icon="h(SearchOutlined)" @click="search">
            搜索
          </a-button>
          <a-button type="default" :icon="h(ReloadOutlined)" style="margin-left: 10px" @click="reset()">
            重置
          </a-button>
        </a-form-item>
      </a-col>
    </a-row>
  </a-form>
</template>

<script setup lang="ts">
import { h } from 'vue';
import { useFormFun } from '@/hooks/useFormFun';
import { componentsMap } from './config.js';
import { SearchOutlined, ReloadOutlined } from '@ant-design/icons-vue';

const props = defineProps({
  footer: {
    type: Boolean,
    default: true,
  },
  schemaData: {
    type: Object,
    default: () => ({}),
  },
  // 表单项配置
  schema: {
    type: Array as PropType<SchemaItem[]>,
    default: () => [],
  },
  col: {
    type: Number,
    default: 24,
  },
  btnCol: {
    type: Number,
    default: 24,
  },

});

const emit = defineEmits(['search', 'reset', 'change']);
const { formRef, formState, formValidateFields, resetFields } = useFormFun(
  props.schemaData
);
// 验证结果
const validForm = async () => {
  return await formValidateFields();
};

const reset = (nameList?: string | (string | number)[]) => {
  emit('reset', formRef.value?.resetFields(nameList));
};

const search = () => {
  emit('search', formState);
};
const onItemChange = (
  value: any,
  name: string,
  com: string
) => {
  console.log(value, name, com);
  emit('change', { value: formState.value[name], name });
};

使用:

<DynamicForm
    :schema="schema"
    :schemaData="formData"
    :layout="'inline'"
    :col="4"
    :btnCol="5"
    @search="search"
    @reset="reset"
  >
    <template #demandCode>
      // 插槽
    </template>
  </DynamicForm>

配置文件:

export const editItem: SchemaItem[] = [
  {
    label: '工单类型',
    name: 'orderType',
    component: 'Select',
    componentProps: {
      allowClear: true,
      placeholder: '工单类型',
      options: [
        { value: '1', label: '装维单' },
        { value: '2', label: '勘察单' },
      ],
    },
    formItemProps: {
      rules: [
        {
          required: true,
          message: '请选择工单类型',
          trigger: 'blur',
        },
      ],
    },
  },
  {
    label: '需求区域',
    name: 'demandName',
    component: 'slot',
    formItemProps: {
      rules: [
        {
          required: true,
          message: '请选择需求区域',
          trigger: 'blur',
        },
      ],
    },
  },
  {
    label: '下发对象',
    name: 'issuingType',
    component: 'RadioGroup',
    componentProps: {
      allowClear: true,
      placeholder: '下发对象',
      options: [
        { label: '云中台接口人', value: '1' },
        { label: '交付队伍', value: '2' },
      ],
    },
    formItemProps: {
      rules: [
        {
          required: true,
          message: '请选择下发对象',
          trigger: 'blur',
        },
      ],
    },
  },
  {
    label: '云中台接口人',
    // name: 'cloudInterfacePerson',
    name: 'issuingObj',
    component: 'Select',
    componentProps: {
      allowClear: true,
      placeholder: '接口人/交付队伍',
      options: [],
    },
    formItemProps: {
      rules: [
        {
          required: true,
          message: '请选择云中台接口人/交付队伍',
          trigger: 'blur',
        },
      ],
    },
  },
  {
    label: '备注',
    name: 'remark',
    component: 'Textarea',
    componentProps: {
      showCount: true,
      maxlength: 100,
    },
  },
];
  1. 表格hook
/**
 * @param fetchFunction - 获取数据的方法
 * @param pageSize - 默认每页显示的条数
 * @param defaultCurrent - 默认当前页码
 */
export function useTablePage(
  fetchFunction: any,
  innitParams: any,
  pageSize = 10,
  defaultCurrent = 1
) {
  const currentPage = ref(defaultCurrent);
  const loading = ref(false);
  const data = ref([]);
  const total = ref(0);
  const size = ref(pageSize);
  const selectedRowKeys = ref([]);

  const pagination = ref({
    current: currentPage.value,
    pageSize: size.value,
    total,
    showTotal: () => `共 ${total.value} 条`,
    onChange: (page: number) => {
      currentPage.value = page;
      getTableData(innitParams);
    },
  });

  const getTableData = async (innitParams: any) => {
    loading.value = true;
    try {
      const result = await fetchFunction({
        pageNow: currentPage.value,
        pageSize: size.value,
        ...innitParams,
      });
      data.value = result.data || [];
      total.value = result.total || 0;
    } catch (error) {
      console.error('Error:', error);
    } finally {
      loading.value = false;
    }
  };
  getTableData(innitParams);

  const onSelectChange = (keys: Array<string>, data: []) => {
    console.log(keys, data);
  };
  // watchEffect(() => {
  //   getTableData(currentPage.value, pageSize);
  // });

  return {
    size,
    pagination,
    currentPage,
    loading,
    data,
    total,
    selectedRowKeys,
    getTableData,
    onSelectChange,
  };
}

配置文件:

export const tableCol: tableColumn[] = [
  {
    title: '工单类型',
    dataIndex: 'orderType',
    key: 'orderType',
    align: 'center',
    customRender: function ({ text, record, index, column }: any) {
      return text === 1 ? '勘察单' : '装维单';
    },
  },
  {
    title: '需求区域',
    dataIndex: 'province',
    key: 'province',
    align: 'center',
    customRender: function ({ text, record, index, column }: any) {
      return (
        [record.province, record.city ?? '', record.district ?? '']
          .filter((s) => s)
          .join('/') || text
      );
    },
  },
  {
    title: '包含下级',
    dataIndex: 'isInclude',
    key: 'isInclude',
    align: 'center',
    customRender: function ({ text, record, index, column }: any) {
      if (text) {
        return '是';
      } else {
        return record.district !== null ? '否' : '-';
      }
    },
  },
  {
    title: '下发对象',
    dataIndex: 'issuingType',
    key: 'issuingType',
    align: 'center',
    customRender: function ({ text, record, index, column }: any) {
      return text === '1' ? '云中台接口人' : '交付队伍';
    },
  },
  {
    title: '接口人/队伍',
    dataIndex: 'issuingObj',
    key: 'issuingObj',
    align: 'center',
    // customRender: function ({ text, record, index, column }: any) {
    //   return text === null ? record.deliveryTeam : text || '-';
    // },
  },
  {
    title: '备注',
    dataIndex: 'remark',
    key: 'remark',
    align: 'center',
    ellipsis: true,
  },
  {
    title: '操作',
    dataIndex: 'action',
    key: 'action',
    align: 'center',
  },
];

使用:

<a-table
    class="m-t-10 table"
    :dataSource="data"
    :columns="columns"
    :pagination="pagination"
    row-class-name="rowClass"
    :loading="loading"
  >
    <template #bodyCell="{ column }">
      <template v-if="column.key === 'action'">
        <a-button type="link" block>修改</a-button>
        <a-button type="link" danger>删除</a-button>
      </template>
    </template>
  </a-table>

<script>
import { useTablePage } from '@/hooks/useTablePage.ts';
const getList = async (params: any) => {
    console.log('请求', params);
    // const res = await createApiAction(apiParams).dispatch.listCreate(params);
    // console.log(res);
    const mockData = [
      {
        id: 100,
        orderType: '1',
        province: '山西省',
        provinceCode: '17',
        city: '桃园县',
        cityCode: '91',
        district: null,
        districtCode: null,
        isInclude: true,
        issuingType: '1',
        issuingObj: 'culpa labore',
        remark:
          'adipisicing non occaecat culpaadipisicing non occaecat culpaadipisicing non occaecat culpaadipisicing non occaecat culpaadipisicing non occaecat culpaadipisicing non occaecat culpa',
      },
      {
        id: 101,
        orderType: '2',
        province: '山西省',
        provinceCode: '17',
        city: '桃园县',
        cityCode: '91',
        district: '沙河口区',
        districtCode: '54',
        isInclude: false,
        issuingType: '2',
        issuingObj: 'culpa labore',
        remark: 'adipisicing non occaecat culpa',
      },
      {
        id: 101,
        orderType: '1',
        province: '山西省',
        provinceCode: '17',
        city: null,
        cityCode: null,
        district: null,
        districtCode: null,
        isInclude: true,
        issuingType: '1',
        issuingObj: 'culpa labore',
        remark: 'adipisicing non occaecat culpa',
      },
    ];
    return {
      data: mockData,
      total: 12,
    };
  };
  const { getTableData, pagination, data, total, loading } = useTablePage(
    getList,
    formData.value
  );
</script>

CcChan
53 声望5 粉丝