diff --git a/packages/pro-field/src/components/DatePicker/index.tsx b/packages/pro-field/src/components/DatePicker/index.tsx new file mode 100644 index 00000000..1631a75a --- /dev/null +++ b/packages/pro-field/src/components/DatePicker/index.tsx @@ -0,0 +1,77 @@ +import { defineComponent, type App, DefineComponent, Plugin } from 'vue'; +import Dayjs from 'dayjs'; +import { fieldDatePickerProps, FieldDatePickerProps } from './types'; +import { DatePicker } from 'ant-design-vue'; +import { getSlot } from '@ant-design-vue/pro-utils'; +import type { VueNode } from 'ant-design-vue/lib/_util/type'; + +const formatDate = (text: any, format: any) => { + if (!text) { + return '-'; + } + if (typeof format === 'function') { + return format(Dayjs(text)); + } else { + return Dayjs(text).format(format || 'YYYY-MM-DD'); + } +}; + +export const slots = ['suffixIcon', 'prevIcon', 'nextIcon', 'superPrevIcon', 'superNextIcon']; + +const FieldDatePicker = defineComponent({ + name: 'FieldDatePicker', + inheritAttrs: false, + props: fieldDatePickerProps, + slots, + setup(props, { slots }) { + const suffixIcon = getSlot<() => VueNode>(slots, props.fieldProps as Record, 'suffixIcon'); + const prevIcon = getSlot<() => VueNode>(slots, props.fieldProps as Record, 'prevIcon'); + const nextIcon = getSlot<() => VueNode>(slots, props.fieldProps as Record, 'nextIcon'); + const superPrevIcon = getSlot<() => VueNode>(slots, props.fieldProps as Record, 'superPrevIcon'); + const superNextIcon = getSlot<() => VueNode>(slots, props.fieldProps as Record, 'superNextIcon'); + + const render = getSlot(slots, props.fieldProps as Record, 'render') as any; + const renderFormItem = getSlot(slots, props.fieldProps as Record, 'renderFormItem') as any; + + return () => { + const { mode, text, dateFormat, fieldProps } = props; + const { placeholder, format } = fieldProps || {}; + + if (mode === 'read') { + const dom = formatDate(text, format || dateFormat); + if (render) { + return render(text, { mode, ...fieldProps }, <>{dom}); + } + return <>{dom}; + } + if (mode === 'edit' || mode === 'update') { + const dom = ( + + ); + if (renderFormItem) { + return renderFormItem(text, { mode, ...fieldProps }, dom); + } + return dom; + } + return null; + }; + }, +}); + +FieldDatePicker.install = (app: App) => { + app.component(FieldDatePicker.name, FieldDatePicker); + return app; +}; + +export default FieldDatePicker as DefineComponent & Plugin; diff --git a/packages/pro-field/src/components/DatePicker/types.ts b/packages/pro-field/src/components/DatePicker/types.ts new file mode 100644 index 00000000..ceb5e959 --- /dev/null +++ b/packages/pro-field/src/components/DatePicker/types.ts @@ -0,0 +1,16 @@ +import type { ExtractPropTypes, PropType } from 'vue'; +import type { CommonProps, DatePickerProps } from 'ant-design-vue/es/date-picker/generatePicker/props'; +import { proFieldFC } from '../typings'; + +export const fieldDatePickerProps = { + ...proFieldFC, + /** 日期格式化 */ + dateFormat: { + type: String, + }, + fieldProps: { + type: Object as PropType & DatePickerProps>, + }, +}; + +export type FieldDatePickerProps = Partial>; diff --git a/packages/pro-field/src/components/Password/index.tsx b/packages/pro-field/src/components/Password/index.tsx index ceebb87f..4bbf750a 100644 --- a/packages/pro-field/src/components/Password/index.tsx +++ b/packages/pro-field/src/components/Password/index.tsx @@ -11,6 +11,7 @@ const FieldPassword = defineComponent({ setup(props, { slots }) { return () => { const { mode, text, fieldProps } = props; + const placeholder = fieldProps.placeholder || '请输入'; const render = props.render ?? slots.render; const renderFormItem = props.renderFormItem ?? slots?.renderFormItem; @@ -34,7 +35,7 @@ const FieldPassword = defineComponent({ return dom; } if (mode === 'edit' || mode === 'update') { - const renderDom = ; + const renderDom = ; if (renderFormItem) { return renderFormItem(text, { mode, fieldProps }, renderDom); } diff --git a/packages/pro-field/src/components/Select/SearchSelect/index.tsx b/packages/pro-field/src/components/Select/SearchSelect/index.tsx index ab7526e0..23314208 100644 --- a/packages/pro-field/src/components/Select/SearchSelect/index.tsx +++ b/packages/pro-field/src/components/Select/SearchSelect/index.tsx @@ -3,20 +3,7 @@ import { Select } from 'ant-design-vue'; import { searchSelectProps } from './types'; import { getSlot } from '@ant-design-vue/pro-utils'; -export const slots = [ - 'notFoundContent', - 'suffixIcon', - 'itemIcon', - 'removeIcon', - 'clearIcon', - 'dropdownRender', - 'option', - 'placeholder', - 'tagRender', - 'maxTagPlaceholder', - 'optionLabel', - 'default', -]; +export const slots = ['default']; const SearchSelect = defineComponent({ props: searchSelectProps, @@ -24,17 +11,6 @@ const SearchSelect = defineComponent({ setup(props, { slots }) { const searchValue = ref(props.searchValue); - const notFoundContent = getSlot(slots, props, 'notFoundContent'); - const suffixIcon = getSlot(slots, props, 'suffixIcon'); - const itemIcon = getSlot(slots, props, 'itemIcon'); - const removeIcon = getSlot(slots, props, 'removeIcon'); - const clearIcon = getSlot(slots, props, 'clearIcon'); - const dropdownRender = getSlot(slots, props, 'dropdownRender'); - const option = getSlot(slots, props, 'option'); - const placeholder = getSlot(slots, props, 'placeholder'); - const tagRender = getSlot(slots, props, 'tagRender'); - const maxTagPlaceholder = getSlot(slots, props, 'maxTagPlaceholder'); - const optionLabel = getSlot(slots, props, 'optionLabel'); const children = getSlot(slots, props, 'default'); return () => { @@ -43,6 +19,7 @@ const SearchSelect = defineComponent({ labelInValue, autoClearSearchValue = true, showSearch, + placeholder, onSearch, onClear, fetchData, @@ -52,22 +29,12 @@ const SearchSelect = defineComponent({ return ( ; + const renderDom = ; if (renderFormItem) { return renderFormItem(text, { mode, fieldProps }, renderDom); } @@ -43,4 +44,4 @@ FieldText.install = (app: App) => { return app; }; -export default FieldText as DefineComponent & Plugin; +export default FieldText as DefineComponent & Plugin; diff --git a/packages/pro-field/src/components/Text/types.ts b/packages/pro-field/src/components/Text/types.ts index 7796adf0..c5dc5122 100644 --- a/packages/pro-field/src/components/Text/types.ts +++ b/packages/pro-field/src/components/Text/types.ts @@ -3,7 +3,7 @@ import type { InputProps } from 'ant-design-vue/es/input/inputProps'; import type { VueNode } from '@ant-design-vue/pro-utils'; import { proFieldFC } from '../typings'; -export const textFieldPorps = { +export const textFieldProps = { ...proFieldFC, // 这里预留一个原生的input type属性 type: { @@ -15,4 +15,4 @@ export const textFieldPorps = { }, }; -export type TextFieldPorps = Partial>; +export type TextFieldProps = Partial>; diff --git a/packages/pro-field/src/index.tsx b/packages/pro-field/src/index.tsx index 3d389f23..1f2f0a53 100644 --- a/packages/pro-field/src/index.tsx +++ b/packages/pro-field/src/index.tsx @@ -47,9 +47,9 @@ export { }; // pro-field -import { textFieldPorps, type TextFieldPorps } from './components/Text/types'; +import { textFieldProps, type TextFieldProps } from './components/Text/types'; import FieldText from './components/Text'; -export { FieldText, textFieldPorps, type TextFieldPorps }; +export { FieldText, textFieldProps, type TextFieldProps }; import { passwordTextProps, type PasswordTextProps } from './components/Password/types'; import FieldPassword from './components/Password'; @@ -57,8 +57,11 @@ export { FieldPassword, passwordTextProps, type PasswordTextProps }; import { searchSelectProps, type SearchSelectProps } from './components/Select/SearchSelect/types'; import FieldSelect from './components/Select'; -import { slots as searchSelectSlots } from './components/Select/SearchSelect'; -export { FieldSelect, searchSelectProps, searchSelectSlots, type SearchSelectProps }; +export { FieldSelect, searchSelectProps, type SearchSelectProps }; + +import { fieldDatePickerProps, type FieldDatePickerProps } from './components/DatePicker/types'; +import FieldDatePicker, { slots as fieldDatePickerSlots } from './components/DatePicker'; +export { FieldDatePicker, fieldDatePickerProps, fieldDatePickerSlots, FieldDatePickerProps }; // style import './default.less'; @@ -100,6 +103,21 @@ const defaultRenderText = ( props: RenderProps // valueTypeMap: Record ): VueNode => { + if (valueType === 'date') { + const { fieldProps } = props; + return ( + + ); + } if (valueType === 'select') { let text = ''; if (dataValue instanceof Array) { @@ -143,7 +161,7 @@ const ProField = defineComponent({ const omitFieldProps = omitUndefined({ ...fieldProps, value: unref(inputValue), - 'onUpdate:value'(value: string) { + 'onUpdate:value'(value: any) { inputValue.value = value; fieldProps?.['onUpdate:value']?.(value); }, diff --git a/packages/pro-form/examples/views/ProForm.vue b/packages/pro-form/examples/views/ProForm.vue index 184cb257..59ca6fe4 100644 --- a/packages/pro-form/examples/views/ProForm.vue +++ b/packages/pro-form/examples/views/ProForm.vue @@ -139,25 +139,40 @@ name="lang" label="语言" :field-props="{ - placeholder: '请选择', + placeholder: '请选择语言', }" > {{ lang.icon }}  {{ lang.label }} + + > + + + + + @@ -166,7 +181,7 @@ import { reactive, ref, FunctionalComponent } from 'vue'; import { PlusOutlined } from '@ant-design/icons-vue'; import { RadioGroup, RadioButton, Switch, Divider, SelectOption, type SelectProps } from 'ant-design-vue'; import type { FormLayout } from 'ant-design-vue/es/form/Form'; -import { ProForm, ProFormText, ProFormPassword, ProFormSelect } from '@ant-design-vue/pro-form'; +import { ProForm, ProFormText, ProFormPassword, ProFormSelect, ProFormDatePicker } from '@ant-design-vue/pro-form'; let lastFetchId = 0; @@ -201,6 +216,7 @@ const formModel = reactive({ girlName: undefined, lang: undefined, country: undefined, + expirationTime: undefined, }); const sex = ref([ diff --git a/packages/pro-form/src/BaseForm/hooks/useFormMethods.ts b/packages/pro-form/src/BaseForm/hooks/useFormMethods.ts index d9280d7c..5acdd2ac 100644 --- a/packages/pro-form/src/BaseForm/hooks/useFormMethods.ts +++ b/packages/pro-form/src/BaseForm/hooks/useFormMethods.ts @@ -1,4 +1,3 @@ -import { toRaw, isProxy } from 'vue'; import { proxyToRaw } from '@ant-design-vue/pro-utils'; import type { Recordable } from '../../typings'; diff --git a/packages/pro-form/src/components/DatePicker/DatePicker.tsx b/packages/pro-form/src/components/DatePicker/DatePicker.tsx new file mode 100644 index 00000000..30843357 --- /dev/null +++ b/packages/pro-form/src/components/DatePicker/DatePicker.tsx @@ -0,0 +1,63 @@ +import { defineComponent, type PropType, ExtractPropTypes, App, DefineComponent, Plugin } from 'vue'; +import type { CommonProps, DatePickerProps } from 'ant-design-vue/es/date-picker/generatePicker/props'; +import { getSlot } from '@ant-design-vue/pro-utils'; +import { pick } from 'lodash-es'; +import { fieldDatePickerSlots } from '@ant-design-vue/pro-field'; +import ProFormField, { proFormFieldProps } from '../Field'; +import { proFormItemProps } from '../FormItem'; +import type { VueNode } from 'ant-design-vue/lib/_util/type'; + +const props = { + ...proFormFieldProps, + fieldProps: { + type: Object as PropType & DatePickerProps, 'value'>>, + }, +}; + +export type ProFormDatePickerProps = Partial>; + +export const ProFormDatePicker = defineComponent({ + name: 'ProFormDatePicker', + inheritAttrs: false, + props, + slots: fieldDatePickerSlots, + setup(props, { slots }) { + const formItemProps = { + ...props.formItemProps, + ...pick(props, Object.keys(proFormItemProps)), + }; + const suffixIcon = getSlot<() => VueNode>(slots, props, 'suffixIcon'); + const prevIcon = getSlot<() => VueNode>(slots, props, 'prevIcon'); + const nextIcon = getSlot<() => VueNode>(slots, props, 'nextIcon'); + const superPrevIcon = getSlot<() => VueNode>(slots, props, 'superPrevIcon'); + const superNextIcon = getSlot<() => VueNode>(slots, props, 'superNextIcon'); + + return () => { + const { fieldProps, colProps } = props; + return ( + + ); + }; + }, +}); + +ProFormDatePicker.install = (app: App) => { + app.component(ProFormDatePicker.name, ProFormDatePicker); + return app; +}; + +export default ProFormDatePicker as DefineComponent & Plugin; diff --git a/packages/pro-form/src/components/Select/index.tsx b/packages/pro-form/src/components/Select/index.tsx index 4c0f52c3..86e5fcc1 100644 --- a/packages/pro-form/src/components/Select/index.tsx +++ b/packages/pro-form/src/components/Select/index.tsx @@ -1,5 +1,5 @@ import { defineComponent, computed, unref, type App, DefineComponent, Plugin, PropType, ExtractPropTypes } from 'vue'; -import { searchSelectSlots } from '@ant-design-vue/pro-field'; +// import { searchSelectSlots } from '@ant-design-vue/pro-field'; import { getSlot, type VueNode } from '@ant-design-vue/pro-utils'; import { pick } from 'lodash-es'; import type { SelectProps, DefaultOptionType } from 'ant-design-vue/es/select'; @@ -28,24 +28,22 @@ export const ProFormSelect = defineComponent({ name: 'ProFormSelect', inheritAttrs: false, props, - slots: searchSelectSlots, + slots: ['notFoundContent', 'suffixIcon'], setup(props, { slots }) { const formItemProps = { ...props.formItemProps, ...pick(props, Object.keys(proFormItemProps)), }; - const notFoundContent = getSlot(slots, props, 'notFoundContent'); - const suffixIcon = getSlot(slots, props, 'suffixIcon'); - const itemIcon = getSlot(slots, props, 'itemIcon'); - const removeIcon = getSlot(slots, props, 'removeIcon'); - const clearIcon = getSlot(slots, props, 'clearIcon'); + const notFoundContent = getSlot<() => VueNode>(slots, props, 'notFoundContent'); + const suffixIcon = getSlot<() => VueNode>(slots, props, 'suffixIcon'); + const itemIcon = getSlot<() => VueNode>(slots, props, 'itemIcon'); + const removeIcon = getSlot<() => VueNode>(slots, props, 'removeIcon') as any; + const clearIcon = getSlot<() => VueNode>(slots, props, 'clearIcon'); const dropdownRender = getSlot(slots, props, 'dropdownRender'); - const option = getSlot(slots, props, 'option'); - const placeholder = getSlot(slots, props, 'placeholder'); + const placeholder = getSlot<() => VueNode>(slots, props, 'placeholder') as any; const tagRender = getSlot(slots, props, 'tagRender'); const maxTagPlaceholder = getSlot(slots, props, 'maxTagPlaceholder'); - const optionLabel = getSlot(slots, props, 'optionLabel'); const children = getSlot(slots, props, 'default'); const options = computed(() => { @@ -53,12 +51,11 @@ export const ProFormSelect = defineComponent({ }); return () => { - const { request, params, colProps } = props; + const { request, params, colProps, fieldProps } = props; return ( { @@ -6,7 +7,7 @@ export const proxyToRaw = (proxy: Recordable) => { for (const key in proxy) { let value = proxy[key]; if (isProxy(value)) { - value = toRaw(value); + value = cloneDeep(toRaw(value)); } target[key] = value; }