@xiaohaih/json-form-vant 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +807 -0
- package/TODO.md +36 -0
- package/components/area/index.ts +6 -0
- package/components/area/index.vue +120 -0
- package/components/area/types.ts +84 -0
- package/components/cascader/index.ts +6 -0
- package/components/cascader/index.vue +146 -0
- package/components/cascader/types.ts +75 -0
- package/components/checkbox/index.ts +6 -0
- package/components/checkbox/index.vue +59 -0
- package/components/checkbox/types.ts +54 -0
- package/components/checkbox-group/index.ts +6 -0
- package/components/checkbox-group/index.vue +67 -0
- package/components/checkbox-group/types.ts +63 -0
- package/components/component-definition/components.ts +29 -0
- package/components/component-definition/definition.ts +25 -0
- package/components/component-definition/index.ts +4 -0
- package/components/custom-render/index.ts +6 -0
- package/components/custom-render/index.vue +66 -0
- package/components/custom-render/types.ts +43 -0
- package/components/date-picker/index.ts +6 -0
- package/components/date-picker/index.vue +130 -0
- package/components/date-picker/types.ts +91 -0
- package/components/date-time-picker-group/index.ts +6 -0
- package/components/date-time-picker-group/index.vue +158 -0
- package/components/date-time-picker-group/types.ts +115 -0
- package/components/datetime-picker/index.ts +6 -0
- package/components/datetime-picker/index.vue +128 -0
- package/components/datetime-picker/types.ts +78 -0
- package/components/dynamic-group/index.ts +10 -0
- package/components/dynamic-group/index.vue +140 -0
- package/components/dynamic-group/types.ts +68 -0
- package/components/group/assist.ts +99 -0
- package/components/group/index.ts +7 -0
- package/components/group/index.vue +117 -0
- package/components/group/types.ts +57 -0
- package/components/group/virtual-group.vue +38 -0
- package/components/index.ts +10 -0
- package/components/input/index.ts +6 -0
- package/components/input/index.vue +83 -0
- package/components/input/types.ts +43 -0
- package/components/input-slot/index.ts +6 -0
- package/components/input-slot/index.vue +148 -0
- package/components/input-slot/types.ts +34 -0
- package/components/number-keyboard/index.ts +6 -0
- package/components/number-keyboard/index.vue +81 -0
- package/components/number-keyboard/types.ts +57 -0
- package/components/password-input/index.ts +6 -0
- package/components/password-input/index.vue +103 -0
- package/components/password-input/types.ts +64 -0
- package/components/picker/index.ts +6 -0
- package/components/picker/index.vue +136 -0
- package/components/picker/types.ts +94 -0
- package/components/radio/index.ts +6 -0
- package/components/radio/index.vue +68 -0
- package/components/radio/types.ts +58 -0
- package/components/radio-group/index.ts +6 -0
- package/components/radio-group/index.vue +74 -0
- package/components/radio-group/types.ts +65 -0
- package/components/rate/index.ts +6 -0
- package/components/rate/index.vue +63 -0
- package/components/rate/types.ts +47 -0
- package/components/share.ts +78 -0
- package/components/signature/index.ts +6 -0
- package/components/signature/index.vue +65 -0
- package/components/signature/instance.vue +161 -0
- package/components/signature/types.ts +79 -0
- package/components/slider/index.ts +6 -0
- package/components/slider/index.vue +63 -0
- package/components/slider/types.ts +53 -0
- package/components/stepper/index.ts +6 -0
- package/components/stepper/index.vue +62 -0
- package/components/stepper/types.ts +47 -0
- package/components/switch/index.ts +6 -0
- package/components/switch/index.vue +61 -0
- package/components/switch/types.ts +51 -0
- package/components/time-picker/index.ts +6 -0
- package/components/time-picker/index.vue +130 -0
- package/components/time-picker/types.ts +91 -0
- package/components/tree-select/index.ts +6 -0
- package/components/tree-select/index.vue +160 -0
- package/components/tree-select/types.ts +77 -0
- package/components/upload/index.ts +6 -0
- package/components/upload/index.vue +109 -0
- package/components/upload/types.ts +85 -0
- package/components/use.ts +45 -0
- package/components/utils.ts +52 -0
- package/components/wrapper/index.ts +6 -0
- package/components/wrapper/index.vue +117 -0
- package/components/wrapper/types.ts +94 -0
- package/dist/components/area/index.d.ts +5 -0
- package/dist/components/area/index.vue.d.ts +1843 -0
- package/dist/components/area/types.d.ts +1434 -0
- package/dist/components/cascader/index.d.ts +5 -0
- package/dist/components/cascader/index.vue.d.ts +2467 -0
- package/dist/components/cascader/types.d.ts +1419 -0
- package/dist/components/checkbox/index.d.ts +5 -0
- package/dist/components/checkbox/index.vue.d.ts +1550 -0
- package/dist/components/checkbox/types.d.ts +1313 -0
- package/dist/components/checkbox-group/index.d.ts +5 -0
- package/dist/components/checkbox-group/index.vue.d.ts +1643 -0
- package/dist/components/checkbox-group/types.d.ts +1372 -0
- package/dist/components/component-definition/components.d.ts +30 -0
- package/dist/components/component-definition/index.d.ts +4 -0
- package/dist/components/custom-render/index.d.ts +5 -0
- package/dist/components/custom-render/index.vue.d.ts +1473 -0
- package/dist/components/custom-render/types.d.ts +1175 -0
- package/dist/components/date-picker/index.d.ts +5 -0
- package/dist/components/date-picker/index.vue.d.ts +1888 -0
- package/dist/components/date-picker/types.d.ts +1458 -0
- package/dist/components/date-time-picker-group/index.d.ts +5 -0
- package/dist/components/date-time-picker-group/index.vue.d.ts +2181 -0
- package/dist/components/date-time-picker-group/types.d.ts +1549 -0
- package/dist/components/dynamic-group/index.d.ts +5 -0
- package/dist/components/dynamic-group/index.vue.d.ts +457 -0
- package/dist/components/dynamic-group/types.d.ts +403 -0
- package/dist/components/group/assist.d.ts +58 -0
- package/dist/components/group/index.d.ts +6 -0
- package/dist/components/group/index.vue.d.ts +139 -0
- package/dist/components/group/types.d.ts +189 -0
- package/dist/components/group/virtual-group.vue.d.ts +42 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/input/index.d.ts +5 -0
- package/dist/components/input/index.vue.d.ts +2229 -0
- package/dist/components/input/types.d.ts +1258 -0
- package/dist/components/input-slot/index.d.ts +5 -0
- package/dist/components/input-slot/index.vue.d.ts +626 -0
- package/dist/components/input-slot/types.d.ts +311 -0
- package/dist/components/number-keyboard/index.d.ts +5 -0
- package/dist/components/number-keyboard/index.vue.d.ts +1643 -0
- package/dist/components/number-keyboard/types.d.ts +1324 -0
- package/dist/components/password-input/index.d.ts +5 -0
- package/dist/components/password-input/index.vue.d.ts +1715 -0
- package/dist/components/password-input/types.d.ts +1357 -0
- package/dist/components/picker/index.d.ts +5 -0
- package/dist/components/picker/index.vue.d.ts +1868 -0
- package/dist/components/picker/types.d.ts +1466 -0
- package/dist/components/radio/index.d.ts +5 -0
- package/dist/components/radio/index.vue.d.ts +1563 -0
- package/dist/components/radio/types.d.ts +1327 -0
- package/dist/components/radio-group/index.d.ts +5 -0
- package/dist/components/radio-group/index.vue.d.ts +1617 -0
- package/dist/components/radio-group/types.d.ts +1383 -0
- package/dist/components/rate/index.d.ts +5 -0
- package/dist/components/rate/index.vue.d.ts +1557 -0
- package/dist/components/rate/types.d.ts +1281 -0
- package/dist/components/share.d.ts +679 -0
- package/dist/components/signature/index.d.ts +5 -0
- package/dist/components/signature/index.vue.d.ts +3017 -0
- package/dist/components/signature/instance.vue.d.ts +1614 -0
- package/dist/components/signature/types.d.ts +1369 -0
- package/dist/components/slider/index.d.ts +5 -0
- package/dist/components/slider/index.vue.d.ts +1563 -0
- package/dist/components/slider/types.d.ts +1302 -0
- package/dist/components/stepper/index.d.ts +5 -0
- package/dist/components/stepper/index.vue.d.ts +1620 -0
- package/dist/components/stepper/types.d.ts +1281 -0
- package/dist/components/switch/index.d.ts +5 -0
- package/dist/components/switch/index.vue.d.ts +1529 -0
- package/dist/components/switch/types.d.ts +1296 -0
- package/dist/components/time-picker/index.d.ts +5 -0
- package/dist/components/time-picker/index.vue.d.ts +1936 -0
- package/dist/components/time-picker/types.d.ts +1458 -0
- package/dist/components/tree-select/index.d.ts +5 -0
- package/dist/components/tree-select/index.vue.d.ts +1802 -0
- package/dist/components/tree-select/types.d.ts +1411 -0
- package/dist/components/upload/index.d.ts +5 -0
- package/dist/components/upload/index.vue.d.ts +1697 -0
- package/dist/components/upload/types.d.ts +1376 -0
- package/dist/components/use.d.ts +53 -0
- package/dist/components/utils.d.ts +15 -0
- package/dist/components/wrapper/index.d.ts +5 -0
- package/dist/components/wrapper/index.vue.d.ts +1085 -0
- package/dist/components/wrapper/types.d.ts +541 -0
- package/dist/docs/.vitepress/config.d.ts +3 -0
- package/dist/docs/.vitepress/theme/index.d.ts +2 -0
- package/dist/index.cjs.js +5459 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.cjs.min.js +3568 -0
- package/dist/index.cjs.min.js.map +1 -0
- package/dist/index.esm.js +5264 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.esm.min.js +3559 -0
- package/dist/index.esm.min.js.map +1 -0
- package/dist/index.umd.js +5465 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/index.umd.min.js +3573 -0
- package/dist/index.umd.min.js.map +1 -0
- package/dist/src/assist.d.ts +32 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/interface.d.ts +129 -0
- package/dist/src/utils.d.ts +9 -0
- package/dist/src/version.d.ts +2 -0
- package/docs/.vitepress/config.ts +99 -0
- package/docs/.vitepress/theme/index.ts +5 -0
- package/docs/README.md +20 -0
- package/docs/index.md +25 -0
- package/env.d.ts +8 -0
- package/package.json +71 -0
- package/scripts/generate-version.mjs +26 -0
- package/scripts/postinstall.cjs +13 -0
- package/scripts/utils.cjs +67 -0
- package/src/assist.ts +40 -0
- package/src/index.ts +5 -0
- package/src/interface.ts +293 -0
- package/src/utils.ts +22 -0
- package/src/version.ts +2 -0
- package/tsconfig.app.json +41 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +24 -0
- package/tsdown.config.ts +12 -0
- package/vite.config.ts +93 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<textarea v-if="type === 'textarea'" ref="inputRef" v-bind="inputAttrs" @focus="focusHandle" @blur="blurHandle" @keypress="keypressHandle" />
|
|
3
|
+
<input v-else ref="inputRef" v-bind="inputAttrs" @focus="focusHandle" @blur="blurHandle" @keypress="keypressHandle">
|
|
4
|
+
</template>
|
|
5
|
+
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
import { computed, defineComponent, nextTick, ref } from 'vue';
|
|
8
|
+
import type { ComponentExposed } from 'vue-component-type-helpers';
|
|
9
|
+
import { inputSlotEmits as emits, inputSlotProps as props } from './types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @file 输入框 - 该组件提供 vant 输入框的样式, 便于覆盖 Field.input 插槽
|
|
13
|
+
*/
|
|
14
|
+
export default defineComponent({
|
|
15
|
+
name: 'HInputSlot',
|
|
16
|
+
components: { },
|
|
17
|
+
inheritAttrs: false,
|
|
18
|
+
props,
|
|
19
|
+
emits,
|
|
20
|
+
setup(props, ctx) {
|
|
21
|
+
const id = '';
|
|
22
|
+
const inputRef = ref<ComponentExposed<typeof HTMLInputElement | typeof HTMLTextAreaElement>>();
|
|
23
|
+
const inputAttrs = computed(() => {
|
|
24
|
+
const { rows, label, disabled, readonly } = props;
|
|
25
|
+
return {
|
|
26
|
+
...ctx.attrs,
|
|
27
|
+
'rows': rows !== undefined ? +rows : undefined,
|
|
28
|
+
'aria-labelledby': label ? `${id}-label` : undefined,
|
|
29
|
+
'data-allow-mismatch': 'attribute',
|
|
30
|
+
'class': 'van-field__control',
|
|
31
|
+
disabled,
|
|
32
|
+
readonly,
|
|
33
|
+
// onBlur,
|
|
34
|
+
// onFocus,
|
|
35
|
+
// onKeypress,
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
function blur() {
|
|
40
|
+
inputRef.value?.blur();
|
|
41
|
+
}
|
|
42
|
+
function focus() {
|
|
43
|
+
inputRef.value?.focus();
|
|
44
|
+
}
|
|
45
|
+
function focusHandle(event: Event) {
|
|
46
|
+
nextTick(adjustTextareaSize);
|
|
47
|
+
// readonly not work in legacy mobile safari
|
|
48
|
+
props.readonly && blur();
|
|
49
|
+
};
|
|
50
|
+
function blurHandle() {
|
|
51
|
+
if (props.readonly) return;
|
|
52
|
+
nextTick(adjustTextareaSize);
|
|
53
|
+
resetScroll();
|
|
54
|
+
}
|
|
55
|
+
function keypressHandle(event: KeyboardEvent) {
|
|
56
|
+
// const ENTER_CODE = 13;
|
|
57
|
+
// if (event.keyCode === ENTER_CODE) {
|
|
58
|
+
// // trigger blur after click keyboard search button
|
|
59
|
+
// props.type === 'search' && blur();
|
|
60
|
+
// }
|
|
61
|
+
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const adjustTextareaSize = () => {
|
|
65
|
+
const input = inputRef.value;
|
|
66
|
+
if (props.type === 'textarea' && props.autosize && input) {
|
|
67
|
+
resizeTextarea(input as HTMLInputElement, props.autosize);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
function resetScroll() {
|
|
71
|
+
if (isIOS_) {
|
|
72
|
+
setRootScrollTop(getRootScrollTop());
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const inBrowser = typeof window !== 'undefined';
|
|
77
|
+
const isIOS = (): boolean =>
|
|
78
|
+
inBrowser
|
|
79
|
+
? /ios|iphone|ipad|ipod/.test(navigator.userAgent.toLowerCase())
|
|
80
|
+
: false;
|
|
81
|
+
const isIOS_ = isIOS();
|
|
82
|
+
const isObject = (val: unknown): val is Record<any, any> =>
|
|
83
|
+
val !== null && typeof val === 'object';
|
|
84
|
+
|
|
85
|
+
function resizeTextarea(
|
|
86
|
+
input: HTMLInputElement,
|
|
87
|
+
autosize: true | {
|
|
88
|
+
maxHeight?: number;
|
|
89
|
+
minHeight?: number;
|
|
90
|
+
},
|
|
91
|
+
) {
|
|
92
|
+
const scrollTop = getRootScrollTop();
|
|
93
|
+
input.style.height = 'auto';
|
|
94
|
+
|
|
95
|
+
let height = input.scrollHeight;
|
|
96
|
+
if (isObject(autosize)) {
|
|
97
|
+
const { maxHeight, minHeight } = autosize;
|
|
98
|
+
if (maxHeight !== undefined) {
|
|
99
|
+
height = Math.min(height, maxHeight);
|
|
100
|
+
}
|
|
101
|
+
if (minHeight !== undefined) {
|
|
102
|
+
height = Math.max(height, minHeight);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (height) {
|
|
107
|
+
input.style.height = `${height}px`;
|
|
108
|
+
// https://github.com/vant-ui/vant/issues/9178
|
|
109
|
+
setRootScrollTop(scrollTop);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
type ScrollElement = Element | Window;
|
|
114
|
+
function setScrollTop(el: ScrollElement, value: number) {
|
|
115
|
+
if ('scrollTop' in el) {
|
|
116
|
+
el.scrollTop = value;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
el.scrollTo(el.scrollX, value);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function getRootScrollTop(): number {
|
|
124
|
+
return (
|
|
125
|
+
window.pageYOffset
|
|
126
|
+
|| document.documentElement.scrollTop
|
|
127
|
+
|| document.body.scrollTop
|
|
128
|
+
|| 0
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
function setRootScrollTop(value: number) {
|
|
132
|
+
setScrollTop(window, value);
|
|
133
|
+
setScrollTop(document.body, value);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
inputRef,
|
|
138
|
+
inputAttrs,
|
|
139
|
+
focusHandle,
|
|
140
|
+
blurHandle,
|
|
141
|
+
keypressHandle,
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
</script>
|
|
146
|
+
|
|
147
|
+
<style lang="scss" scoped>
|
|
148
|
+
</style>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Obj2Props, usePlain } from '@xiaohaih/json-form-core';
|
|
2
|
+
import { emits2props, plainProps } from '@xiaohaih/json-form-core';
|
|
3
|
+
import type { Field as VanField } from 'vant';
|
|
4
|
+
import type { ExtractPublicPropTypes, Prop, PropType } from 'vue';
|
|
5
|
+
import type { ComponentProps } from 'vue-component-type-helpers';
|
|
6
|
+
|
|
7
|
+
type VanFieldProps = Obj2Props<ComponentProps<typeof VanField>>;
|
|
8
|
+
|
|
9
|
+
/** 组件传参 */
|
|
10
|
+
export const inputSlotProps = {
|
|
11
|
+
...{
|
|
12
|
+
// 这些属性不需要传到组件上
|
|
13
|
+
t: { type: String as PropType<string> },
|
|
14
|
+
rules: { type: [Array, Object] as PropType<any> },
|
|
15
|
+
name: { type: [String] as PropType<string> },
|
|
16
|
+
} as unknown as typeof VanField.props as unknown as VanFieldProps,
|
|
17
|
+
/** 输入框类型(input/textarea) */
|
|
18
|
+
type: { type: String } as unknown as VanFieldProps['type'],
|
|
19
|
+
/** 输入框行数 */
|
|
20
|
+
rows: { type: [Number, String] as PropType<number | string> },
|
|
21
|
+
/** 禁用 */
|
|
22
|
+
disabled: { type: [Boolean] as PropType<boolean> },
|
|
23
|
+
/** 只读 */
|
|
24
|
+
readonly: { type: [Boolean] as PropType<boolean> },
|
|
25
|
+
/** 文本信息 */
|
|
26
|
+
label: { type: [String] as PropType<string> },
|
|
27
|
+
};
|
|
28
|
+
export type InputSlotProps = ExtractPublicPropTypes<typeof inputSlotProps>;
|
|
29
|
+
|
|
30
|
+
/** 组件事件 */
|
|
31
|
+
export const inputSlotEmits = {
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type InputSlotEmits = typeof inputSlotEmits;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<VanField
|
|
3
|
+
v-if="!hide"
|
|
4
|
+
:name="field"
|
|
5
|
+
:model-value="(checked as string)"
|
|
6
|
+
:disabled="globalDisabled || disabled"
|
|
7
|
+
:readonly="true" :is-link="isLink"
|
|
8
|
+
v-bind="$attrs"
|
|
9
|
+
@click.stop="clickHandle"
|
|
10
|
+
>
|
|
11
|
+
<template #extra>
|
|
12
|
+
<VanNumberKeyboard
|
|
13
|
+
:model-value="(checked as string)"
|
|
14
|
+
:show="popupInfo.visible"
|
|
15
|
+
v-bind="numberKeyboardProps" v-on="numberKeyboardOn"
|
|
16
|
+
@update:model-value="change" @blur="popupInfo.close"
|
|
17
|
+
>
|
|
18
|
+
<template v-for="(item, slotName) of numberKeyboardSlots" :key="slotName" #[hyphenate(slotName)]="row">
|
|
19
|
+
<component :is="getNode(item)" v-bind="slotProps" v-bind.prop="row" />
|
|
20
|
+
</template>
|
|
21
|
+
</VanNumberKeyboard>
|
|
22
|
+
</template>
|
|
23
|
+
<template v-for="(item, slotName) of slots" :key="slotName" #[hyphenate(slotName)]="row">
|
|
24
|
+
<component :is="getNode(item)" v-bind="slotProps" v-bind.prop="row" />
|
|
25
|
+
</template>
|
|
26
|
+
</VanField>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<script lang="ts">
|
|
30
|
+
import { getNode, hyphenate, usePlain } from '@xiaohaih/json-form-core';
|
|
31
|
+
import { Field as VanField, NumberKeyboard as VanNumberKeyboard } from 'vant';
|
|
32
|
+
import type { SlotsType } from 'vue';
|
|
33
|
+
import { defineComponent, ref } from 'vue';
|
|
34
|
+
import { useCommonSetup } from '../use';
|
|
35
|
+
import type { NumberKeyboardSlots } from './types';
|
|
36
|
+
import { numberKeyboardEmitsPrivate as emits, numberKeyboardPropsPrivate as props } from './types';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @file 数字键盘
|
|
40
|
+
*/
|
|
41
|
+
export default defineComponent({
|
|
42
|
+
name: 'HNumberKeyboard',
|
|
43
|
+
components: { VanField, VanNumberKeyboard },
|
|
44
|
+
inheritAttrs: false,
|
|
45
|
+
props,
|
|
46
|
+
emits,
|
|
47
|
+
slots: Object as SlotsType<NumberKeyboardSlots>,
|
|
48
|
+
setup(props, ctx) {
|
|
49
|
+
const plain = usePlain(props);
|
|
50
|
+
const { slotProps } = useCommonSetup(props, ctx, plain);
|
|
51
|
+
|
|
52
|
+
/** 点击事件 */
|
|
53
|
+
function clickHandle(ev: MouseEvent) {
|
|
54
|
+
if (plain.globalDisabled.value || props.disabled || plain.globalReadonly.value || props.readonly) return;
|
|
55
|
+
props.onRowClick ? props.onRowClick(popupInfo.value, ev) : popupInfo.value.open();
|
|
56
|
+
}
|
|
57
|
+
/** 弹窗显示状态 */
|
|
58
|
+
const popupInfo = ref({
|
|
59
|
+
visible: false,
|
|
60
|
+
open() {
|
|
61
|
+
popupInfo.value.visible = true;
|
|
62
|
+
},
|
|
63
|
+
close() {
|
|
64
|
+
popupInfo.value.visible = false;
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
hyphenate,
|
|
70
|
+
getNode,
|
|
71
|
+
...plain,
|
|
72
|
+
slotProps,
|
|
73
|
+
clickHandle,
|
|
74
|
+
popupInfo,
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
</script>
|
|
79
|
+
|
|
80
|
+
<style lang="scss" scoped>
|
|
81
|
+
</style>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { emits2obj, PlainProps } from '@xiaohaih/json-form-core';
|
|
2
|
+
import { emits2props, plainProps } from '@xiaohaih/json-form-core';
|
|
3
|
+
import { Field as VanField, NumberKeyboard as VanNumberKeyboard } from 'vant';
|
|
4
|
+
import type { ExtractPublicPropTypes, PropType } from 'vue';
|
|
5
|
+
import type { ComponentProps } from 'vue-component-type-helpers';
|
|
6
|
+
import type { CommonProps, CommonSlots, CommonSlotsProps, ComponentType } from '../share';
|
|
7
|
+
import { commonProps } from '../share';
|
|
8
|
+
|
|
9
|
+
/** 组件传参 - 私有 */
|
|
10
|
+
export function numberKeyboardPropsGeneric<Query extends Record<string, any>, OptionQuery extends Record<string, any>>() {
|
|
11
|
+
return {
|
|
12
|
+
...commonProps as CommonProps<Query, OptionQuery>,
|
|
13
|
+
...plainProps as PlainProps<Query, OptionQuery>,
|
|
14
|
+
/** VanNumberKeyboard 组件的属性 */
|
|
15
|
+
numberKeyboardProps: { type: Object as PropType<Partial<ComponentProps<typeof VanNumberKeyboard>>> },
|
|
16
|
+
/** VanNumberKeyboard 组件的事件 - 兼容 vue2 版本 */
|
|
17
|
+
numberKeyboardOn: { type: Object as PropType<Partial<ReturnType<typeof emits2obj<NonNullable<typeof VanNumberKeyboard.emits>>>>>, default: () => ({}) },
|
|
18
|
+
/** VanNumberKeyboard 组件的插槽 */
|
|
19
|
+
numberKeyboardSlots: { type: Object as PropType<{
|
|
20
|
+
/** 自定义删除按键内容 */
|
|
21
|
+
delete: ComponentType<CommonSlotsProps<any, any>>;
|
|
22
|
+
/** 自定义左下角按键内容 */
|
|
23
|
+
extraKey: ComponentType<CommonSlotsProps<any, any>>;
|
|
24
|
+
/** 自定义标题栏左侧内容 */
|
|
25
|
+
titleLeft: ComponentType<CommonSlotsProps<any, any>>;
|
|
26
|
+
}> },
|
|
27
|
+
/** 是否展示右侧箭头并开启点击反馈 - Field 字段, 调整默认值 */
|
|
28
|
+
isLink: { type: Boolean, default: true },
|
|
29
|
+
/** 点击事件, 当传递了此事件时, 会忽略内部打开弹窗操作 */
|
|
30
|
+
onRowClick: { type: Function as PropType<(option: { open: () => void; close: () => void }, ev: MouseEvent) => void> },
|
|
31
|
+
} as const;
|
|
32
|
+
}
|
|
33
|
+
/** 组件传参 - 私有 */
|
|
34
|
+
export const numberKeyboardPropsPrivate = numberKeyboardPropsGeneric();
|
|
35
|
+
/** 组件传参 - 外部调用 */
|
|
36
|
+
export const numberKeyboardProps = {
|
|
37
|
+
...VanField.props as unknown as {},
|
|
38
|
+
...numberKeyboardPropsPrivate,
|
|
39
|
+
};
|
|
40
|
+
export type NumberKeyboardProps<Query extends Record<string, any>, OptionQuery extends Record<string, any>> = ExtractPublicPropTypes<ReturnType<typeof numberKeyboardPropsGeneric<Query, OptionQuery>>>;
|
|
41
|
+
|
|
42
|
+
/** 组件事件 - 私有 */
|
|
43
|
+
export function numberKeyboardEmitsGeneric<T>() {
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
/** 组件事件 - 私有 */
|
|
47
|
+
export const numberKeyboardEmitsPrivate = numberKeyboardEmitsGeneric();
|
|
48
|
+
/** 组件事件 - 外部调用 */
|
|
49
|
+
export const numberKeyboardEmits = {
|
|
50
|
+
...VanField.emits,
|
|
51
|
+
...numberKeyboardEmitsPrivate,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type NumberKeyboardEmits<T> = ReturnType<typeof numberKeyboardEmitsGeneric<T>>;
|
|
55
|
+
|
|
56
|
+
export interface NumberKeyboardSlots extends CommonSlots<any> {
|
|
57
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<VanField
|
|
3
|
+
v-if="!hide"
|
|
4
|
+
:name="field" :label="label"
|
|
5
|
+
:model-value="(checked as string)"
|
|
6
|
+
:disabled="globalDisabled || disabled"
|
|
7
|
+
:readonly="globalReadonly || readonly"
|
|
8
|
+
v-bind="$attrs"
|
|
9
|
+
@click.stop="clickHandle"
|
|
10
|
+
>
|
|
11
|
+
<template #input>
|
|
12
|
+
<VanPasswordInput
|
|
13
|
+
style="width: 100%"
|
|
14
|
+
:value="(checked as string)"
|
|
15
|
+
:focused="popupInfo.visible"
|
|
16
|
+
v-bind="passwordInputProps" v-on="passwordInputOn"
|
|
17
|
+
@focus="clickHandle"
|
|
18
|
+
>
|
|
19
|
+
<template v-for="(item, slotName) of passwordInputSlots" :key="slotName" #[hyphenate(slotName)]="row">
|
|
20
|
+
<component :is="getNode(item)" v-bind="slotProps" v-bind.prop="row" />
|
|
21
|
+
</template>
|
|
22
|
+
</VanPasswordInput>
|
|
23
|
+
</template>
|
|
24
|
+
<template #extra>
|
|
25
|
+
<VanNumberKeyboard
|
|
26
|
+
:model-value="(checked as string)"
|
|
27
|
+
:show="popupInfo.visible"
|
|
28
|
+
v-bind="numberKeyboardProps"
|
|
29
|
+
v-on="numberKeyboardOn"
|
|
30
|
+
@update:model-value="customChange"
|
|
31
|
+
@blur="popupInfo.close"
|
|
32
|
+
>
|
|
33
|
+
<template v-for="(item, slotName) of numberKeyboardSlots" :key="slotName" #[hyphenate(slotName)]="row">
|
|
34
|
+
<component :is="getNode(item)" v-bind="slotProps" v-bind.prop="row" />
|
|
35
|
+
</template>
|
|
36
|
+
</VanNumberKeyboard>
|
|
37
|
+
</template>
|
|
38
|
+
<template v-for="(item, slotName) of slots" :key="slotName" #[hyphenate(slotName)]="row">
|
|
39
|
+
<component :is="getNode(item)" v-bind="slotProps" v-bind.prop="row" />
|
|
40
|
+
</template>
|
|
41
|
+
</VanField>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<script lang="ts">
|
|
45
|
+
import { getNode, hyphenate, usePlain } from '@xiaohaih/json-form-core';
|
|
46
|
+
import { Field as VanField, NumberKeyboard as VanNumberKeyboard, PasswordInput as VanPasswordInput } from 'vant';
|
|
47
|
+
import type { SlotsType } from 'vue';
|
|
48
|
+
import { computed, defineComponent, ref } from 'vue';
|
|
49
|
+
import { useCommonSetup } from '../use';
|
|
50
|
+
import type { PasswordInputSlots } from './types';
|
|
51
|
+
import { passwordInputEmitsPrivate as emits, passwordInputPropsPrivate as props } from './types';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @file 密码输入框
|
|
55
|
+
*/
|
|
56
|
+
export default defineComponent({
|
|
57
|
+
name: 'HPasswordInput',
|
|
58
|
+
components: { VanField, VanNumberKeyboard, VanPasswordInput },
|
|
59
|
+
inheritAttrs: false,
|
|
60
|
+
props,
|
|
61
|
+
emits,
|
|
62
|
+
slots: Object as SlotsType<PasswordInputSlots>,
|
|
63
|
+
setup(props, ctx) {
|
|
64
|
+
const plain = usePlain(props);
|
|
65
|
+
const { slotProps } = useCommonSetup(props, ctx, plain);
|
|
66
|
+
|
|
67
|
+
/** 点击事件 */
|
|
68
|
+
function clickHandle(ev: MouseEvent) {
|
|
69
|
+
if (plain.globalDisabled.value || props.disabled || plain.globalReadonly.value || props.readonly) return;
|
|
70
|
+
props.onRowClick ? props.onRowClick(popupInfo.value, ev) : popupInfo.value.open();
|
|
71
|
+
}
|
|
72
|
+
/** 重写 change 事件 */
|
|
73
|
+
function customChange(value: string) {
|
|
74
|
+
const len = Number(props.passwordInputProps?.length) || 6;
|
|
75
|
+
if (value.length === len) popupInfo.value.close();
|
|
76
|
+
plain.change(value);
|
|
77
|
+
}
|
|
78
|
+
/** 弹窗显示状态 */
|
|
79
|
+
const popupInfo = ref({
|
|
80
|
+
visible: false,
|
|
81
|
+
open() {
|
|
82
|
+
popupInfo.value.visible = true;
|
|
83
|
+
},
|
|
84
|
+
close() {
|
|
85
|
+
popupInfo.value.visible = false;
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
hyphenate,
|
|
91
|
+
getNode,
|
|
92
|
+
...plain,
|
|
93
|
+
slotProps,
|
|
94
|
+
clickHandle,
|
|
95
|
+
customChange,
|
|
96
|
+
popupInfo,
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
</script>
|
|
101
|
+
|
|
102
|
+
<style lang="scss" scoped>
|
|
103
|
+
</style>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { emits2obj, PlainProps } from '@xiaohaih/json-form-core';
|
|
2
|
+
import { emits2props, plainProps } from '@xiaohaih/json-form-core';
|
|
3
|
+
import { Field as VanField, NumberKeyboard as VanNumberKeyboard, PasswordInput as VanPasswordInput } from 'vant';
|
|
4
|
+
import type { ExtractPublicPropTypes, PropType } from 'vue';
|
|
5
|
+
import type { ComponentProps } from 'vue-component-type-helpers';
|
|
6
|
+
import type { CommonProps, CommonSlots, CommonSlotsProps, ComponentType } from '../share';
|
|
7
|
+
import { commonProps } from '../share';
|
|
8
|
+
|
|
9
|
+
/** 组件传参 - 私有 */
|
|
10
|
+
export function passwordInputPropsGeneric<Query extends Record<string, any>, OptionQuery extends Record<string, any>>() {
|
|
11
|
+
return {
|
|
12
|
+
...commonProps as CommonProps<Query, OptionQuery>,
|
|
13
|
+
...plainProps as PlainProps<Query, OptionQuery>,
|
|
14
|
+
/** 输入框左侧文本 */
|
|
15
|
+
label: { type: String },
|
|
16
|
+
/** VanPasswordInput 组件的属性 */
|
|
17
|
+
passwordInputProps: { type: Object as PropType<Partial<ComponentProps<typeof VanPasswordInput>>> },
|
|
18
|
+
/** VanPasswordInput 组件的事件 - 兼容 vue2 版本 */
|
|
19
|
+
passwordInputOn: { type: Object as PropType<Partial<ReturnType<typeof emits2obj<NonNullable<typeof VanPasswordInput.emits>>>>>, default: () => ({}) },
|
|
20
|
+
/** VanPasswordInput 组件的插槽 */
|
|
21
|
+
passwordInputSlots: { type: Object as PropType<{
|
|
22
|
+
}> },
|
|
23
|
+
/** 点击事件, 当传递了此事件时, 会忽略内部打开弹窗操作 */
|
|
24
|
+
onRowClick: { type: Function as PropType<(option: { open: () => void; close: () => void }, ev: MouseEvent) => void> },
|
|
25
|
+
/** VanNumberKeyboard 组件的属性 */
|
|
26
|
+
numberKeyboardProps: { type: Object as PropType<Partial<ComponentProps<typeof VanNumberKeyboard>>> },
|
|
27
|
+
/** VanNumberKeyboard 组件的事件 - 兼容 vue2 版本 */
|
|
28
|
+
numberKeyboardOn: { type: Object as PropType<Partial<ReturnType<typeof emits2obj<NonNullable<typeof VanNumberKeyboard.emits>>>>>, default: () => ({}) },
|
|
29
|
+
/** VanNumberKeyboard 组件的插槽 */
|
|
30
|
+
numberKeyboardSlots: { type: Object as PropType<{
|
|
31
|
+
/** 自定义删除按键内容 */
|
|
32
|
+
delete: ComponentType<CommonSlotsProps<any, any>>;
|
|
33
|
+
/** 自定义左下角按键内容 */
|
|
34
|
+
extraKey: ComponentType<CommonSlotsProps<any, any>>;
|
|
35
|
+
/** 自定义标题栏左侧内容 */
|
|
36
|
+
titleLeft: ComponentType<CommonSlotsProps<any, any>>;
|
|
37
|
+
}> },
|
|
38
|
+
} as const;
|
|
39
|
+
}
|
|
40
|
+
/** 组件传参 - 私有 */
|
|
41
|
+
export const passwordInputPropsPrivate = passwordInputPropsGeneric();
|
|
42
|
+
/** 组件传参 - 外部调用 */
|
|
43
|
+
export const passwordInputProps = {
|
|
44
|
+
...VanField.props as unknown as {},
|
|
45
|
+
...passwordInputPropsPrivate,
|
|
46
|
+
};
|
|
47
|
+
export type PasswordInputProps<Query extends Record<string, any>, OptionQuery extends Record<string, any>> = ExtractPublicPropTypes<ReturnType<typeof passwordInputPropsGeneric<Query, OptionQuery>>>;
|
|
48
|
+
|
|
49
|
+
/** 组件事件 - 私有 */
|
|
50
|
+
export function passwordInputEmitsGeneric<T>() {
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
/** 组件事件 - 私有 */
|
|
54
|
+
export const passwordInputEmitsPrivate = passwordInputEmitsGeneric();
|
|
55
|
+
/** 组件事件 - 外部调用 */
|
|
56
|
+
export const passwordInputEmits = {
|
|
57
|
+
...VanField.emits,
|
|
58
|
+
...passwordInputEmitsPrivate,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export type PasswordInputEmits<T> = ReturnType<typeof passwordInputEmitsGeneric<T>>;
|
|
62
|
+
|
|
63
|
+
export interface PasswordInputSlots extends CommonSlots<any> {
|
|
64
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<VanField
|
|
3
|
+
v-if="!hide"
|
|
4
|
+
:name="field"
|
|
5
|
+
:disabled="globalDisabled || disabled"
|
|
6
|
+
:readonly="true"
|
|
7
|
+
:is-link="isLink"
|
|
8
|
+
:model-value="showText"
|
|
9
|
+
v-bind="$attrs"
|
|
10
|
+
@click="clickHandle"
|
|
11
|
+
>
|
|
12
|
+
<template #extra>
|
|
13
|
+
<VanPopup v-model:show="popupInfo.visible" teleport="body" round position="bottom" v-bind="popupProps" v-on="popupOn" @close="popupInfo.close">
|
|
14
|
+
<VanPicker
|
|
15
|
+
:model-value="checked"
|
|
16
|
+
:columns="finalOption"
|
|
17
|
+
v-bind="pickerProps" v-on="pickerOn"
|
|
18
|
+
@change="pickerHandle('change', $event)" @confirm="pickerHandle('confirm', $event)" @cancel="pickerHandle('cancel', $event)"
|
|
19
|
+
>
|
|
20
|
+
<template v-for="(item, slotName) of pickerSlots" :key="slotName" #[hyphenate(slotName)]="row">
|
|
21
|
+
<component :is="getNode(item)" v-bind="slotProps" v-bind.prop="row" />
|
|
22
|
+
</template>
|
|
23
|
+
</VanPicker>
|
|
24
|
+
</VanPopup>
|
|
25
|
+
</template>
|
|
26
|
+
<template v-for="(item, slotName) of slots" :key="slotName" #[hyphenate(slotName)]="row">
|
|
27
|
+
<component :is="getNode(item)" v-bind="slotProps" v-bind.prop="row" />
|
|
28
|
+
</template>
|
|
29
|
+
</VanField>
|
|
30
|
+
</template>
|
|
31
|
+
|
|
32
|
+
<script lang="ts">
|
|
33
|
+
import { getNode, hyphenate, usePlain } from '@xiaohaih/json-form-core';
|
|
34
|
+
import { Field as VanField, Picker as VanPicker, Popup as VanPopup } from 'vant';
|
|
35
|
+
import type { SlotsType } from 'vue';
|
|
36
|
+
import { computed, defineComponent, ref, watch } from 'vue';
|
|
37
|
+
import { useCommonSetup } from '../use';
|
|
38
|
+
import type { PickerOption, PickerSlots } from './types';
|
|
39
|
+
import { pickerEmitsPrivate as emits, pickerPropsPrivate as props } from './types';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @file 选择框
|
|
43
|
+
*/
|
|
44
|
+
export default defineComponent({
|
|
45
|
+
name: 'HPicker',
|
|
46
|
+
components: { VanPicker, VanField, VanPopup },
|
|
47
|
+
inheritAttrs: false,
|
|
48
|
+
props,
|
|
49
|
+
emits,
|
|
50
|
+
slots: Object as SlotsType<PickerSlots>,
|
|
51
|
+
setup(props, ctx) {
|
|
52
|
+
const plain = usePlain(props);
|
|
53
|
+
const { slotProps } = useCommonSetup(props, ctx, plain);
|
|
54
|
+
/** 展示在页面上的值 */
|
|
55
|
+
const showText = computed(() => {
|
|
56
|
+
const value = plain.checked.value as string[];
|
|
57
|
+
if (!value || !value.length) return '';
|
|
58
|
+
const { format, showAllLevels, separator } = props;
|
|
59
|
+
const source = (props.pickerProps?.columns || plain.finalOption.value) as Record<string, any>[];
|
|
60
|
+
const _columnsFieldNames = (props.pickerProps || {}).columnsFieldNames || {};
|
|
61
|
+
const columnsFieldNames = { text: _columnsFieldNames.text || 'text', value: _columnsFieldNames.value || 'value', children: _columnsFieldNames.children || 'children' };
|
|
62
|
+
if (format) return format({ source, value, columnsFieldNames, showAllLevels, separator });
|
|
63
|
+
|
|
64
|
+
// 判断是否为扁平多列结构(source 中的元素是数组)
|
|
65
|
+
const isMultiColumnFlat = source.length > 0 && Array.isArray(source[0]);
|
|
66
|
+
|
|
67
|
+
if (isMultiColumnFlat) {
|
|
68
|
+
// 1. 扁平的多列:每列独立查找
|
|
69
|
+
return value.map((val, i) => {
|
|
70
|
+
const column = source[i] || [];
|
|
71
|
+
const item = column.find((opt: Record<string, any>) => opt[columnsFieldNames.value] === val);
|
|
72
|
+
return item ? item[columnsFieldNames.text] : '';
|
|
73
|
+
}).filter(Boolean).join(separator);
|
|
74
|
+
}
|
|
75
|
+
// 2. 树形级联或单列:逐级查找
|
|
76
|
+
const texts: string[] = [];
|
|
77
|
+
let currentOptions = source;
|
|
78
|
+
for (const val of value) {
|
|
79
|
+
const item = currentOptions.find((opt: Record<string, any>) => opt[columnsFieldNames.value] === val);
|
|
80
|
+
if (!item) break;
|
|
81
|
+
texts.push(item[columnsFieldNames.text]);
|
|
82
|
+
currentOptions = item[columnsFieldNames.children] || [];
|
|
83
|
+
}
|
|
84
|
+
return showAllLevels ? texts.join(separator) : texts[texts.length - 1];
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
/** 点击事件 */
|
|
88
|
+
function clickHandle(ev: MouseEvent) {
|
|
89
|
+
if (plain.globalDisabled.value || props.disabled || plain.globalReadonly.value || props.readonly) return;
|
|
90
|
+
props.onRowClick ? props.onRowClick(popupInfo.value, ev) : popupInfo.value.open();
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* 日期更改事件
|
|
94
|
+
* @param {string} type 事件类型
|
|
95
|
+
* @param {string} value 日期值
|
|
96
|
+
*/
|
|
97
|
+
function pickerHandle(type: string, value: PickerOption) {
|
|
98
|
+
(props.valueTrigger === type || props.pickerProps?.showToolbar === false) && plain.change(value.selectedValues);
|
|
99
|
+
switch (type) {
|
|
100
|
+
case 'confirm':
|
|
101
|
+
case 'cancel':
|
|
102
|
+
{
|
|
103
|
+
popupInfo.value.close();
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
default: break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** 弹窗显示状态 */
|
|
111
|
+
const popupInfo = ref({
|
|
112
|
+
visible: false,
|
|
113
|
+
open() {
|
|
114
|
+
popupInfo.value.visible = true;
|
|
115
|
+
},
|
|
116
|
+
close() {
|
|
117
|
+
popupInfo.value.visible = false;
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
hyphenate,
|
|
123
|
+
getNode,
|
|
124
|
+
...plain,
|
|
125
|
+
slotProps,
|
|
126
|
+
showText,
|
|
127
|
+
clickHandle,
|
|
128
|
+
pickerHandle,
|
|
129
|
+
popupInfo,
|
|
130
|
+
};
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
</script>
|
|
134
|
+
|
|
135
|
+
<style lang="scss" scoped>
|
|
136
|
+
</style>
|