Ben Lin
2024-07-04 d582f081e148c5df7c1b334a94fa1df1cd2655a9
src/views/demo/form/index.vue
@@ -10,7 +10,7 @@
        @reset="handleReset"
      >
        <template #selectA="{ model, field }">
          <a-select
          <Select
            :options="optionsA"
            mode="multiple"
            v-model:value="model[field]"
@@ -19,7 +19,7 @@
          />
        </template>
        <template #selectB="{ model, field }">
          <a-select
          <Select
            :options="optionsB"
            mode="multiple"
            v-model:value="model[field]"
@@ -48,42 +48,44 @@
            labelField="name"
            valueField="id"
            :params="searchParams"
            @search="onSearch"
            @search="debounceOptionsFn"
          />
        </template>
      </BasicForm>
    </CollapseContainer>
  </PageWrapper>
</template>
<script lang="ts">
  import { computed, defineComponent, unref, ref } from 'vue';
  import { BasicForm, FormSchema, ApiSelect } from '/@/components/Form/index';
  import { CollapseContainer } from '/@/components/Container';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { PageWrapper } from '/@/components/Page';
<script lang="ts" setup>
  import { type Recordable } from '@vben/types';
  import { computed, unref, ref } from 'vue';
  import { BasicForm, ApiSelect, FormSchema } from '@/components/Form';
  import { CollapseContainer } from '@/components/Container';
  import { useMessage } from '@/hooks/web/useMessage';
  import { PageWrapper } from '@/components/Page';
  import { optionsListApi } from '/@/api/demo/select';
  import { optionsListApi } from '@/api/demo/select';
  import { useDebounceFn } from '@vueuse/core';
  import { treeOptionsListApi } from '/@/api/demo/tree';
  import { Select } from 'ant-design-vue';
  import { treeOptionsListApi } from '@/api/demo/tree';
  import { Select, type SelectProps } from 'ant-design-vue';
  import { cloneDeep } from 'lodash-es';
  import { areaRecord } from '/@/api/demo/cascader';
  import { uploadApi } from '/@/api/sys/upload';
  import { areaRecord } from '@/api/demo/cascader';
  import { uploadApi } from '@/api/sys/upload';
  let debounceOptionsFn = useDebounceFn(onSearch, 300);
  const valueSelectA = ref<string[]>([]);
  const valueSelectB = ref<string[]>([]);
  const options = ref<Recordable[]>([]);
  const options = ref<Required<SelectProps>['options']>([]);
  for (let i = 1; i < 10; i++) options.value.push({ label: '选项' + i, value: `${i}` });
  const optionsA = computed(() => {
    return cloneDeep(unref(options)).map((op) => {
      op.disabled = unref(valueSelectB).indexOf(op.value) !== -1;
      op.disabled = unref(valueSelectB).indexOf(op.value as string) !== -1;
      return op;
    });
  });
  const optionsB = computed(() => {
    return cloneDeep(unref(options)).map((op) => {
      op.disabled = unref(valueSelectA).indexOf(op.value) !== -1;
      op.disabled = unref(valueSelectA).indexOf(op.value as string) !== -1;
      return op;
    });
  });
@@ -151,12 +153,13 @@
    {
      field: 'field1',
      component: 'Input',
      label: '字段1',
      label: ({ model }) => {
        return `字段1${model.field3 ? model.field3 : ''}`;
      },
      colProps: {
        span: 8,
      },
      // componentProps:{},
      // can func
      componentProps: ({ schema, formModel }) => {
        console.log('form:', schema);
@@ -307,8 +310,8 @@
            value: '2',
          },
        ],
        onChange: (e, v) => {
          console.log('RadioButtonGroup====>:', e, v);
        onChange: (e) => {
          console.log(e);
        },
      },
    },
@@ -354,6 +357,39 @@
            ],
          },
        ],
      },
    },
    {
      field: 'field12',
      component: 'BasicTitle',
      label: '标题区分',
      componentProps: {
        // line: true,
        span: true,
      },
      colProps: {
        span: 24,
      },
    },
    {
      field: 'field13',
      component: 'CropperAvatar',
      label: '头像上传',
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field14',
      component: 'Transfer',
      label: '穿梭框',
      colProps: {
        span: 8,
      },
      componentProps: {
        render: (item) => item.label,
        dataSource: citiesOptionsData.guangdong,
        targetKeys: ['1'],
      },
    },
    {
@@ -407,7 +443,7 @@
      componentProps: {
        api: areaRecord,
        apiParamKey: 'parentCode',
        dataField: 'data',
        // dataField: 'data',
        labelField: 'name',
        valueField: 'code',
        initFetchParams: {
@@ -423,7 +459,6 @@
    },
    {
      field: 'field31',
      component: 'Input',
      label: '下拉本地搜索',
      helpMessage: ['ApiSelect组件', '远程数据源本地搜索', '只发起一次请求获取所有选项'],
      required: true,
@@ -432,10 +467,12 @@
        span: 8,
      },
      defaultValue: '0',
      componentProps: {
        onOptionsChange() {},
      },
    },
    {
      field: 'field32',
      component: 'Input',
      label: '下拉远程搜索',
      helpMessage: ['ApiSelect组件', '将关键词发送到接口进行远程搜索'],
      required: true,
@@ -456,6 +493,49 @@
        resultField: 'list',
        onChange: (e, v) => {
          console.log('ApiTreeSelect====>:', e, v);
        },
      },
      colProps: {
        span: 8,
      },
    },
    {
      field: 'field33',
      component: 'ApiTreeSelect',
      label: '远程懒加载下拉树',
      helpMessage: ['ApiTreeSelect组件', '使用接口提供的数据生成选项'],
      required: true,
      componentProps: {
        api: () => {
          return new Promise((resolve) => {
            resolve([
              {
                title: 'Parent Node',
                value: '0-0',
              },
            ]);
          });
        },
        async: true,
        onChange: (e, v) => {
          console.log('ApiTreeSelect====>:', e, v);
        },
        onLoadData: ({ treeData, resolve, treeNode }) => {
          console.log('treeNode====>:', treeNode);
          setTimeout(() => {
            const children: Recordable[] = [
              { title: `Child Node ${treeNode.eventKey}-0`, value: `${treeNode.eventKey}-0` },
              { title: `Child Node ${treeNode.eventKey}-1`, value: `${treeNode.eventKey}-1` },
            ];
            children.forEach((item) => {
              item.isLeaf = false;
              item.children = [];
            });
            treeNode.dataRef.children = children;
            treeData.value = [...treeData.value];
            resolve();
            return;
          }, 300);
        },
      },
      colProps: {
@@ -501,35 +581,48 @@
        // use id as value
        valueField: 'id',
        isBtn: true,
        onChange: (e, v) => {
          console.log('ApiRadioGroup====>:', e, v);
        onChange: (e) => {
          console.log('ApiRadioGroup====>:', e);
        },
      },
      colProps: {
        span: 8,
      },
    },
    // {
    //   field: 'field36',
    //   component: 'ApiTree',
    //   label: '远程Tree',
    //   helpMessage: ['ApiTree组件', '使用接口提供的数据生成选项'],
    //   required: true,
    //   componentProps: {
    //     api: treeOptionsListApi,
    //     params: {
    //       count: 2,
    //     },
    //     afterFetch: (v) => {
    //       //do something
    //       return v;
    //     },
    //     resultField: 'list',
    //   },
    //   colProps: {
    //     span: 8,
    //   },
    // },
    {
      field: 'field36',
      component: 'ApiTree',
      label: '远程Tree',
      helpMessage: ['ApiTree组件', '使用接口提供的数据生成选项'],
      required: true,
      componentProps: {
        api: treeOptionsListApi,
        params: {
          count: 2,
        },
        afterFetch: (v) => {
          //do something
          return v;
        },
        resultField: 'list',
      },
      colProps: {
        span: 8,
      },
    },
    {
      label: '远程穿梭框',
      field: 'field37',
      component: 'ApiTransfer',
      componentProps: {
        render: (item) => item.label,
        api: async () => {
          return Promise.resolve(citiesOptionsData.guangdong);
        },
      },
      defaultValue: ['1'],
      required: true,
    },
    {
      field: 'divider-linked',
      component: 'Divider',
@@ -594,7 +687,6 @@
    },
    {
      field: 'selectA',
      component: 'Select',
      label: '互斥SelectA',
      slot: 'selectA',
      defaultValue: [],
@@ -604,7 +696,6 @@
    },
    {
      field: 'selectB',
      component: 'Select',
      label: '互斥SelectB',
      slot: 'selectB',
      defaultValue: [],
@@ -624,10 +715,28 @@
    {
      field: '[startTime, endTime]',
      label: '时间范围',
      component: 'TimeRangePicker',
      componentProps: {
        format: 'HH:mm:ss',
        placeholder: ['开始时间', '结束时间'],
      },
    },
    {
      field: '[startDate, endDate]',
      label: '日期范围',
      component: 'RangePicker',
      componentProps: {
        format: 'YYYY-MM-DD',
        placeholder: ['开始日期', '结束日期'],
      },
    },
    {
      field: '[startDateTime, endDateTime]',
      label: '日期时间范围',
      component: 'RangePicker',
      componentProps: {
        format: 'YYYY-MM-DD HH:mm:ss',
        placeholder: ['开始时间', '结束时间'],
        placeholder: ['开始日期、时间', '结束日期、时间'],
        showTime: { format: 'HH:mm:ss' },
      },
    },
@@ -678,39 +787,52 @@
        allowHalf: true,
      },
    },
    {
      field: 'field23',
      component: 'ImageUpload',
      label: '上传图片',
      required: true,
      defaultValue: [
        'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
      ],
      componentProps: {
        api: uploadApi,
        accept: ['png', 'jpeg', 'jpg'],
        maxSize: 2,
        maxNumber: 1,
      },
      // rules: [
      //   {
      //     required: true,
      //     trigger: 'change',
      //     validator(_, value) {
      //       if (isArray(value) && value.length > 0) {
      //         return Promise.resolve();
      //       } else {
      //         return Promise.reject('请选择上传图片');
      //       }
      //     },
      //   },
      // ],
    },
  ];
  export default defineComponent({
    components: { BasicForm, CollapseContainer, PageWrapper, ApiSelect, ASelect: Select },
    setup() {
      const check = ref(null);
      const { createMessage } = useMessage();
      const keyword = ref<string>('');
      const searchParams = computed<Recordable>(() => {
        return { keyword: unref(keyword) };
      });
      function onSearch(value: string) {
        keyword.value = value;
      }
      return {
        schemas,
        optionsListApi,
        optionsA,
        optionsB,
        valueSelectA,
        valueSelectB,
        onSearch: useDebounceFn(onSearch, 300),
        searchParams,
        handleReset: () => {
          keyword.value = '';
        },
        handleSubmit: (values: any) => {
          console.log('values', values);
          createMessage.success('click search,values:' + JSON.stringify(values));
        },
        check,
      };
    },
  const { createMessage } = useMessage();
  const keyword = ref<string>('');
  const searchParams = computed<Recordable<string>>(() => {
    return { keyword: unref(keyword) };
  });
  function onSearch(value: string) {
    keyword.value = value;
  }
  function handleReset() {
    keyword.value = '';
  }
  function handleSubmit(values: any) {
    console.log('values', values);
    createMessage.success('click search,values:' + JSON.stringify(values));
  }
</script>