feat: 添加 PDatePicker 组件
Some checks failed
/ test (push) Successful in 22s
/ surge (push) Failing after 2m22s

This commit is contained in:
严浩
2024-12-23 16:57:02 +08:00
parent c1c8e470fd
commit 70a9aa5b79
8 changed files with 995 additions and 576 deletions

View File

@ -14,6 +14,7 @@ import { /* defaultConfig, */ bindings, plugin /* defaultConfig */ } from '@form
import type { App } from 'vue'; import type { App } from 'vue';
import { addAsteriskPlugin } from './formkit.config.plugin.addAsteriskPlugin'; import { addAsteriskPlugin } from './formkit.config.plugin.addAsteriskPlugin';
import { debugPlugin } from './formkit.config.plugin.debug'; import { debugPlugin } from './formkit.config.plugin.debug';
import { PDatePicker } from '@/__fk-inputs__/inputs/p-date-picker';
const plugins: FormKitPlugin[] = [ const plugins: FormKitPlugin[] = [
// createLibraryPlugin(fkLibrary), // createLibraryPlugin(fkLibrary),
@ -26,6 +27,7 @@ const plugins: FormKitPlugin[] = [
PInputText, PInputText,
PInputPassword, PInputPassword,
PSelect, PSelect,
PDatePicker,
}), }),
// createLibraryPlugin( // createLibraryPlugin(
// { // {

View File

@ -14,31 +14,33 @@
"@formkit/addons": "^1.6.9", "@formkit/addons": "^1.6.9",
"@formkit/core": "^1.6.9", "@formkit/core": "^1.6.9",
"@formkit/icons": "^1.6.9", "@formkit/icons": "^1.6.9",
"@formkit/pro": "^0.127.8", "@formkit/pro": "^0.127.15",
"@formkit/themes": "^1.6.9", "@formkit/themes": "^1.6.9",
"@formkit/vue": "^1.6.9", "@formkit/vue": "^1.6.9",
"@formkit/zod": "^1.6.9", "@formkit/zod": "^1.6.9",
"@headlessui/vue": "^1.7.23", "@headlessui/vue": "^1.7.23",
"@primevue/forms": "^4.2.4", "@primevue/forms": "^4.2.5",
"@primevue/themes": "^4.2.4", "@primevue/themes": "^4.2.5",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"i18next": "^24.0.5", "dayjs": "^1.11.13",
"i18next": "^24.2.0",
"postcss": "^8.4.49", "postcss": "^8.4.49",
"primeicons": "^7.0.0", "primeicons": "^7.0.0",
"primevue": "^4.2.4", "primelocale": "^1.2.2",
"sweetalert2": "^11.14.5", "primevue": "^4.2.5",
"tailwindcss": "^3.4.15", "sweetalert2": "^11.15.3",
"tailwindcss": "^3.4.17",
"vue": "^3.5.13", "vue": "^3.5.13",
"zod-i18n-map": "^2.27.0" "zod-i18n-map": "^2.27.0"
}, },
"devDependencies": { "devDependencies": {
"@primevue/auto-import-resolver": "^4.2.4", "@primevue/auto-import-resolver": "^4.2.5",
"@vitejs/plugin-vue": "^5.2.1", "@vitejs/plugin-vue": "^5.2.1",
"@vitejs/plugin-vue-jsx": "^4.1.1", "@vitejs/plugin-vue-jsx": "^4.1.1",
"typescript": "~5.6.0", "typescript": "~5.7.2",
"unocss": "^0.65.0", "unocss": "^0.65.2",
"unplugin-vue-components": "^0.27.5", "unplugin-vue-components": "^0.28.0",
"vite": "^6.0.2", "vite": "^6.0.5",
"vue-tsc": "^2.1.10" "vue-tsc": "^2.1.10"
} }
} }

1277
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -4,27 +4,12 @@ import AllCustom from './all-custom/all-custom.vue';
import TutorialForm from './tutorial-form/index.vue'; import TutorialForm from './tutorial-form/index.vue';
import ZodForm from './zod-form/index.vue'; import ZodForm from './zod-form/index.vue';
const selectedCity = ref('NY'); const datetime12h = ref();
const loading = ref(false); const datetime24h = ref();
const cities = ref([ const time = ref();
{ name: 'New York', code: 'NY' }, const date = ref();
{ name: 'Rome', code: 'RM' }, const dates = ref();
{ name: 'London', code: 'LDN' },
{ name: 'Istanbul', code: 'IST' },
{ name: 'Paris', code: 'PRS' }
]);
const loadSelectedCity = () => {
loading.value = true;
setTimeout(() => {
loading.value = false;
}, 2000);
};
const handleE = (eventName: string) => {
return (...args: any[]) => {
console.log('eventName :>> ', eventName);
console.log('args :>> ', args);
};
};
</script> </script>
@ -40,31 +25,62 @@ const handleE = (eventName: string) => {
<!-- </div> --> <!-- </div> -->
<div class="p-4 w-full bg-white rounded-lg shadow-md dark:bg-gray-800 dark:text-white mt-4"> <div class="p-4 w-full bg-white rounded-lg shadow-md dark:bg-gray-800 dark:text-white mt-4">
<Select
v-model="selectedCity"
:placeholder="loading ? 'Loading' : 'Select a City'" <div class="card flex flex-wrap gap-4 items-start">
:loading="loading" <DatePicker
class="w-full md:w-56" v-model="date"
:options="cities" view="month"
optionLabel="name" dateFormat="mm/yy"
optionValue="code" />
@input="(e: any) => handleE('input')(e)"
@value-change="(e) => handleE('value-change')(e)" <DatePicker
@change="(e) => handleE('change')(e)" v-model="dates"
@blur="(e) => handleE('blur')(e)" selectionMode="range"
></Select> :manualInput="false"
<Select />
:options="cities"
class="w-full md:w-56"
> <div class="flex-auto">
<template #value="slotProps">{{ slotProps.value }}</template> <label
</Select> for="datepicker-12h"
<Button class="font-bold block mb-2"
id="load-selected-city" > 12h Format </label>
label="Load Selected City" <DatePicker
@click="loadSelectedCity" id="datepicker-12h"
/> v-model="datetime12h"
{{ { selectedCity } }} showTime
hourFormat="12"
fluid
/>
</div>
<div class="flex-auto">
<label
for="datepicker-24h"
class="font-bold block mb-2"
> 24h Format </label>
<DatePicker
id="datepicker-24h"
v-model="datetime24h"
showTime
hourFormat="24"
fluid
/>
</div>
<div class="flex-auto">
<label
for="datepicker-timeonly"
class="font-bold block mb-2"
> Time Only </label>
<DatePicker
id="datepicker-timeonly"
v-model="time"
timeOnly
fluid
/>
</div>
</div>
</div> </div>
</div> </div>
</template> </template>

View File

@ -0,0 +1,105 @@
import type { FormKitFrameworkContext, FormKitTypeDefinition } from '@formkit/core';
import type { FormKitInputs } from '@formkit/inputs';
import { createSection, label, outer } from '@formkit/inputs';
import type { DatePickerEmitsOptions, DatePickerProps } from 'primevue/datepicker';
import PrimevueDatepicker from 'primevue/datepicker';
import { defineComponent, markRaw } from 'vue';
import { floatLabel } from '../sections/floatLabel';
import { messages } from '../sections/messages';
// :plugins="[castNumber]"
type PickerValue = Date | Array<Date> | Array<Date | null> | undefined | null;
type PropValueToDate = (value: unknown) => PickerValue;
type PropDateToValue = (date?: PickerValue) => unknown;
type PrimevueDatepickerListeners = {
'onUpdate:modelValue': DatePickerEmitsOptions['update:modelValue'];
'onBlur': DatePickerEmitsOptions['blur'];
};
const SchemaComponent = defineComponent(
(vueProps: { context: FormKitFrameworkContext & { valueToDate: PropValueToDate; dateToValue: PropDateToValue } }) => {
const formkitContext = vueProps.context;
const { valueToDate, dateToValue } = formkitContext;
const listeners: PrimevueDatepickerListeners = {
'onUpdate:modelValue': value => {
let newValue = value as unknown;
try {
newValue = dateToValue(value);
} catch (e) {
console.error(e);
}
formkitContext.node.input(newValue);
},
'onBlur': async e => {
setTimeout(
() => formkitContext.handlers.blur.call(undefined, e as never),
166, // 因为会触发两次。所以让blur事件延迟一点可以考虑优化。
);
},
};
return () => {
let value = formkitContext._value;
try {
value = valueToDate(formkitContext._value);
} catch (e) {
console.error(e);
}
return (
<PrimevueDatepicker
inputId={formkitContext.id}
fluid
invalid={formkitContext.state.invalid}
disabled={!!formkitContext.disabled}
modelValue={value}
dateFormat={formkitContext.dateFormat as never}
manualInput={formkitContext.manualInput as never}
{...listeners}
/>
);
};
},
{
props: ['context'],
},
);
const input = createSection('input', () => ({
$cmp: markRaw(SchemaComponent) as never,
bind: '$attrs',
props: {
context: '$node.context',
},
}));
export const PDatePicker: FormKitTypeDefinition = {
type: 'input',
schema: outer(
floatLabel(
input(), //
label('$label'),
),
messages(),
),
props: ['valueToDate', 'dateToValue', 'dateFormat'],
schemaMemoKey: 'q2dkascustq', // Math.random().toString(36).substring(2, 15)
};
declare module '@formkit/inputs' {
// https://formkit.com/essentials/custom-inputs#typescript-support
interface FormKitInputProps<Props extends FormKitInputs<Props>> {
PDatePicker: {
type: 'PDatePicker';
valueToDate?: PropValueToDate;
dateToValue?: PropDateToValue;
value?: unknown;
view?: DatePickerProps['view'];
/**
* https://primevue.org/datepicker/#format
*/
dateFormat?: DatePickerProps['dateFormat'];
manualInput?: DatePickerProps['manualInput'];
};
}
}

View File

@ -15,10 +15,10 @@ type PrimevueSelectListeners = {
'onBlur': SelectEmitsOptions['blur']; 'onBlur': SelectEmitsOptions['blur'];
}; };
const PSelectComp = defineComponent( const SchemaComponent = defineComponent(
(vueProps: { context: FormKitFrameworkContext }) => { (vueProps: { context: FormKitFrameworkContext }) => {
const formkitContext = vueProps.context; const formkitContext = vueProps.context;
const primevueSelectInstance = ref<Record<string, unknown> | undefined>(); const primevueSelectInstance = ref<{ label: string } | undefined>();
const listeners: PrimevueSelectListeners = { const listeners: PrimevueSelectListeners = {
'onUpdate:modelValue': (value: unknown) => { 'onUpdate:modelValue': (value: unknown) => {
formkitContext.node.input(value); formkitContext.node.input(value);
@ -98,7 +98,7 @@ const PSelectComp = defineComponent(
); );
const input = createSection('input', () => ({ const input = createSection('input', () => ({
$cmp: markRaw(PSelectComp) as never, $cmp: markRaw(SchemaComponent) as never,
bind: '$attrs', bind: '$attrs',
props: { props: {
context: '$node.context', context: '$node.context',

View File

@ -2,6 +2,7 @@
import { FormKitNode } from '@formkit/core'; import { FormKitNode } from '@formkit/core';
import { text } from '@formkit/inputs'; import { text } from '@formkit/inputs';
import Swal from 'sweetalert2'; import Swal from 'sweetalert2';
import dayjs from 'dayjs';
async function submit(formData: Record<string, any>, formNode: FormKitNode) { async function submit(formData: Record<string, any>, formNode: FormKitNode) {
console.group('submit'); console.group('submit');
@ -98,6 +99,18 @@ const promiseOptions = new Promise<typeof K_OPTIONS>(resolve => {
optionLabel="label" optionLabel="label"
optionValue="value" optionValue="value"
/> />
<FormKit
type="PDatePicker"
name="PDatePicker"
:manualInput="false"
dateFormat="yy年mm月"
:dateToValue="(date) => dayjs(date as Date).format('YYYY-MM')"
:valueToDate="(value) => dayjs(value as string).toDate()"
view="month"
value="2022-01"
label="选日期"
validation="required"
/>
<div class="flex flex-wrap gap-4"> <div class="flex flex-wrap gap-4">
<FormKit <FormKit

View File

@ -1,9 +1,10 @@
import Aura from "@primevue/themes/aura"; import Aura from '@primevue/themes/aura';
import PrimeVue from "primevue/config"; import zhCN from 'primelocale/zh-CN.json';
import ToastService from "primevue/toastservice"; import PrimeVue from 'primevue/config';
import { createApp } from "vue"; import ToastService from 'primevue/toastservice';
import { setupFormKit } from "../formkit.config"; import { createApp } from 'vue';
import App from "./App.vue"; import { setupFormKit } from '../formkit.config';
import App from './App.vue';
// import '@unocss/reset/normalize.css' // import '@unocss/reset/normalize.css'
// import '@unocss/reset/sanitize/sanitize.css' // import '@unocss/reset/sanitize/sanitize.css'
@ -12,15 +13,28 @@ import App from "./App.vue";
// import '@unocss/reset/tailwind-compat.css' // import '@unocss/reset/tailwind-compat.css'
// import '@unocss/reset/tailwind.css' // import '@unocss/reset/tailwind.css'
import "primeicons/primeicons.css"; import 'primeicons/primeicons.css';
import "virtual:uno.css"; import 'virtual:uno.css';
const app = createApp(App); const app = createApp(App);
app.use(PrimeVue, { theme: { preset: Aura } }); app.use(PrimeVue, {
locale: {
...zhCN['zh-CN'],
noFileChosenMessage: '未选择文件',
pending: '待上传',
completed: '已上传',
}, // usePrimeVue().config.locale
theme: { preset: Aura },
options: {
prefix: 'p',
darkModeSelector: 'system',
cssLayer: false,
},
});
app.use(ToastService); app.use(ToastService);
// https://github.dev/formkit/auto-animate/blob/master/docs/src/examples/formkit/ActualFormKit.vue // https://github.dev/formkit/auto-animate/blob/master/docs/src/examples/formkit/ActualFormKit.vue
setupFormKit(app); setupFormKit(app);
app.mount("#app"); app.mount('#app');