| | |
| | | @reset="handleReset" |
| | | > |
| | | <template #selectA="{ model, field }"> |
| | | <a-select |
| | | <Select |
| | | :options="optionsA" |
| | | mode="multiple" |
| | | v-model:value="model[field]" |
| | |
| | | /> |
| | | </template> |
| | | <template #selectB="{ model, field }"> |
| | | <a-select |
| | | <Select |
| | | :options="optionsB" |
| | | mode="multiple" |
| | | v-model:value="model[field]" |
| | |
| | | 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; |
| | | }); |
| | | }); |
| | |
| | | { |
| | | 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); |
| | |
| | | value: '2', |
| | | }, |
| | | ], |
| | | onChange: (e, v) => { |
| | | console.log('RadioButtonGroup====>:', e, v); |
| | | onChange: (e) => { |
| | | console.log(e); |
| | | }, |
| | | }, |
| | | }, |
| | |
| | | ], |
| | | }, |
| | | ], |
| | | }, |
| | | }, |
| | | { |
| | | 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'], |
| | | }, |
| | | }, |
| | | { |
| | |
| | | componentProps: { |
| | | api: areaRecord, |
| | | apiParamKey: 'parentCode', |
| | | dataField: 'data', |
| | | // dataField: 'data', |
| | | labelField: 'name', |
| | | valueField: 'code', |
| | | initFetchParams: { |
| | |
| | | }, |
| | | { |
| | | field: 'field31', |
| | | component: 'Input', |
| | | label: '下拉本地搜索', |
| | | helpMessage: ['ApiSelect组件', '远程数据源本地搜索', '只发起一次请求获取所有选项'], |
| | | required: true, |
| | |
| | | span: 8, |
| | | }, |
| | | defaultValue: '0', |
| | | componentProps: { |
| | | onOptionsChange() {}, |
| | | }, |
| | | }, |
| | | { |
| | | field: 'field32', |
| | | component: 'Input', |
| | | label: '下拉远程搜索', |
| | | helpMessage: ['ApiSelect组件', '将关键词发送到接口进行远程搜索'], |
| | | required: true, |
| | |
| | | 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: { |
| | |
| | | // 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', |
| | |
| | | }, |
| | | { |
| | | field: 'selectA', |
| | | component: 'Select', |
| | | label: '互斥SelectA', |
| | | slot: 'selectA', |
| | | defaultValue: [], |
| | |
| | | }, |
| | | { |
| | | field: 'selectB', |
| | | component: 'Select', |
| | | label: '互斥SelectB', |
| | | slot: 'selectB', |
| | | defaultValue: [], |
| | |
| | | { |
| | | 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' }, |
| | | }, |
| | | }, |
| | |
| | | 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> |