@tmagic/form 1.4.8 → 1.4.10

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.
Files changed (63) hide show
  1. package/dist/style.css +4 -0
  2. package/dist/tmagic-form.js +13 -12
  3. package/dist/tmagic-form.umd.cjs +13 -12
  4. package/package.json +5 -5
  5. package/src/Form.vue +201 -0
  6. package/src/FormBox.vue +120 -0
  7. package/src/FormDialog.vue +173 -0
  8. package/src/FormDrawer.vue +159 -0
  9. package/src/containers/Col.vue +52 -0
  10. package/src/containers/Container.vue +408 -0
  11. package/src/containers/Fieldset.vue +124 -0
  12. package/src/containers/GroupList.vue +139 -0
  13. package/src/containers/GroupListItem.vue +135 -0
  14. package/src/containers/Panel.vue +99 -0
  15. package/src/containers/Row.vue +54 -0
  16. package/src/containers/Step.vue +82 -0
  17. package/src/containers/Table.vue +648 -0
  18. package/src/containers/Tabs.vue +226 -0
  19. package/src/fields/Cascader.vue +131 -0
  20. package/src/fields/Checkbox.vue +58 -0
  21. package/src/fields/CheckboxGroup.vue +44 -0
  22. package/src/fields/ColorPicker.vue +30 -0
  23. package/src/fields/Date.vue +38 -0
  24. package/src/fields/DateTime.vue +47 -0
  25. package/src/fields/Daterange.vue +99 -0
  26. package/src/fields/Display.vue +21 -0
  27. package/src/fields/DynamicField.vue +90 -0
  28. package/src/fields/Hidden.vue +16 -0
  29. package/src/fields/Link.vue +86 -0
  30. package/src/fields/Number.vue +49 -0
  31. package/src/fields/NumberRange.vue +50 -0
  32. package/src/fields/RadioGroup.vue +28 -0
  33. package/src/fields/Select.vue +449 -0
  34. package/src/fields/Switch.vue +59 -0
  35. package/src/fields/Text.vue +170 -0
  36. package/src/fields/Textarea.vue +46 -0
  37. package/src/fields/Time.vue +34 -0
  38. package/src/fields/Timerange.vue +76 -0
  39. package/src/index.ts +139 -0
  40. package/src/schema.ts +758 -0
  41. package/src/shims-vue.d.ts +6 -0
  42. package/src/theme/container.scss +5 -0
  43. package/src/theme/date-time.scss +7 -0
  44. package/src/theme/fieldset.scss +28 -0
  45. package/src/theme/form-box.scss +13 -0
  46. package/src/theme/form-dialog.scss +13 -0
  47. package/src/theme/form-drawer.scss +11 -0
  48. package/src/theme/form.scss +43 -0
  49. package/src/theme/group-list.scss +23 -0
  50. package/src/theme/index.scss +15 -0
  51. package/src/theme/link.scss +3 -0
  52. package/src/theme/number-range.scss +8 -0
  53. package/src/theme/panel.scss +24 -0
  54. package/src/theme/select.scss +3 -0
  55. package/src/theme/table.scss +70 -0
  56. package/src/theme/tabs.scss +27 -0
  57. package/src/theme/text.scss +6 -0
  58. package/src/utils/config.ts +27 -0
  59. package/src/utils/containerProps.ts +50 -0
  60. package/src/utils/form.ts +273 -0
  61. package/src/utils/useAddField.ts +40 -0
  62. package/types/schema.d.ts +7 -5
  63. package/types/utils/form.d.ts +2 -2
package/dist/style.css CHANGED
@@ -1,4 +1,8 @@
1
1
  @charset "UTF-8";
2
+ .tmagic-design-form-inline .m-form-container {
3
+ display: inline-flex;
4
+ }
5
+
2
6
  .m-form-dialog .el-dialog__body {
3
7
  padding: 0 !important;
4
8
  }
@@ -97,18 +97,18 @@ const getDefaultValue = function(mForm, { defaultValue, type, filter, multiple }
97
97
  return "";
98
98
  };
99
99
  const filterFunction = (mForm, config, props) => {
100
- if (typeof config !== "function") {
101
- return config;
100
+ if (typeof config === "function") {
101
+ return config(mForm, {
102
+ values: mForm?.initValues || {},
103
+ model: props.model,
104
+ parent: mForm?.parentValues || {},
105
+ formValue: mForm?.values || props.model,
106
+ prop: props.prop,
107
+ config: props.config,
108
+ index: props.index
109
+ });
102
110
  }
103
- return config(mForm, {
104
- values: mForm?.initValues || {},
105
- model: props.model,
106
- parent: mForm?.parentValues || {},
107
- formValue: mForm?.values || props.model,
108
- prop: props.prop,
109
- config: props.config,
110
- index: props.index
111
- });
111
+ return config;
112
112
  };
113
113
  const display = function(mForm, config, props) {
114
114
  if (config === "expand") {
@@ -2366,7 +2366,8 @@ const _sfc_main$l = /* @__PURE__ */ defineComponent({
2366
2366
  const mForm = inject("mForm");
2367
2367
  const options = computed(() => {
2368
2368
  if (Array.isArray(props.config.options)) return props.config.options;
2369
- if (typeof props.config.options === "function") return filterFunction(mForm, props.config.options, props);
2369
+ if (typeof props.config.options === "function")
2370
+ return filterFunction(mForm, props.config.options, props) || [];
2370
2371
  return [];
2371
2372
  });
2372
2373
  return (_ctx, _cache) => {
@@ -96,18 +96,18 @@
96
96
  return "";
97
97
  };
98
98
  const filterFunction = (mForm, config, props) => {
99
- if (typeof config !== "function") {
100
- return config;
99
+ if (typeof config === "function") {
100
+ return config(mForm, {
101
+ values: mForm?.initValues || {},
102
+ model: props.model,
103
+ parent: mForm?.parentValues || {},
104
+ formValue: mForm?.values || props.model,
105
+ prop: props.prop,
106
+ config: props.config,
107
+ index: props.index
108
+ });
101
109
  }
102
- return config(mForm, {
103
- values: mForm?.initValues || {},
104
- model: props.model,
105
- parent: mForm?.parentValues || {},
106
- formValue: mForm?.values || props.model,
107
- prop: props.prop,
108
- config: props.config,
109
- index: props.index
110
- });
110
+ return config;
111
111
  };
112
112
  const display = function(mForm, config, props) {
113
113
  if (config === "expand") {
@@ -2365,7 +2365,8 @@
2365
2365
  const mForm = vue.inject("mForm");
2366
2366
  const options = vue.computed(() => {
2367
2367
  if (Array.isArray(props.config.options)) return props.config.options;
2368
- if (typeof props.config.options === "function") return filterFunction(mForm, props.config.options, props);
2368
+ if (typeof props.config.options === "function")
2369
+ return filterFunction(mForm, props.config.options, props) || [];
2369
2370
  return [];
2370
2371
  });
2371
2372
  return (_ctx, _cache) => {
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
- "version": "1.4.8",
2
+ "version": "1.4.10",
3
3
  "name": "@tmagic/form",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
- "dist/*",
7
6
  "src/theme/*"
8
7
  ],
9
8
  "main": "dist/tmagic-form.umd.cjs",
@@ -24,7 +23,8 @@
24
23
  },
25
24
  "files": [
26
25
  "dist",
27
- "types"
26
+ "types",
27
+ "src"
28
28
  ],
29
29
  "license": "Apache-2.0",
30
30
  "engines": {
@@ -54,8 +54,8 @@
54
54
  "peerDependencies": {
55
55
  "vue": "^3.4.27",
56
56
  "typescript": "*",
57
- "@tmagic/design": "1.4.8",
58
- "@tmagic/utils": "1.4.8"
57
+ "@tmagic/design": "1.4.10",
58
+ "@tmagic/utils": "1.4.10"
59
59
  },
60
60
  "peerDependenciesMeta": {
61
61
  "typescript": {
package/src/Form.vue ADDED
@@ -0,0 +1,201 @@
1
+ <template>
2
+ <TMagicForm
3
+ class="m-form"
4
+ ref="tMagicForm"
5
+ :model="values"
6
+ :label-width="labelWidth"
7
+ :style="`height: ${height}`"
8
+ :inline="inline"
9
+ :label-position="labelPosition"
10
+ >
11
+ <template v-if="initialized && Array.isArray(config)">
12
+ <Container
13
+ v-for="(item, index) in config"
14
+ :disabled="disabled"
15
+ :key="item[keyProp] ?? index"
16
+ :config="item"
17
+ :model="values"
18
+ :last-values="lastValuesProcessed"
19
+ :is-compare="isCompare"
20
+ :label-width="item.labelWidth || labelWidth"
21
+ :step-active="stepActive"
22
+ :size="size"
23
+ @change="changeHandler"
24
+ ></Container>
25
+ </template>
26
+ </TMagicForm>
27
+ </template>
28
+
29
+ <script setup lang="ts">
30
+ import { provide, reactive, ref, toRaw, watch, watchEffect } from 'vue';
31
+ import { cloneDeep, isEqual } from 'lodash-es';
32
+
33
+ import { TMagicForm } from '@tmagic/design';
34
+
35
+ import Container from './containers/Container.vue';
36
+ import { getConfig } from './utils/config';
37
+ import { initValue } from './utils/form';
38
+ import type { FormConfig, FormState, FormValue, ValidateError } from './schema';
39
+
40
+ defineOptions({
41
+ name: 'MForm',
42
+ });
43
+
44
+ const props = withDefaults(
45
+ defineProps<{
46
+ /** 表单配置 */
47
+ config: FormConfig;
48
+ /** 表单值 */
49
+ initValues: Record<string, any>;
50
+ /** 需对比的值(开启对比模式时传入) */
51
+ lastValues?: Record<string, any>;
52
+ /** 是否开启对比模式 */
53
+ isCompare?: boolean;
54
+ parentValues?: Record<string, any>;
55
+ labelWidth?: string;
56
+ disabled?: boolean;
57
+ height?: string;
58
+ stepActive?: string | number;
59
+ size?: 'small' | 'default' | 'large';
60
+ inline?: boolean;
61
+ labelPosition?: string;
62
+ keyProp?: string;
63
+ popperClass?: string;
64
+ extendState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
65
+ }>(),
66
+ {
67
+ config: () => [],
68
+ initValues: () => ({}),
69
+ lastValues: () => ({}),
70
+ isCompare: false,
71
+ parentValues: () => ({}),
72
+ labelWidth: '200px',
73
+ disabled: false,
74
+ height: 'auto',
75
+ stepActive: 1,
76
+ inline: false,
77
+ labelPosition: 'right',
78
+ keyProp: '__key',
79
+ },
80
+ );
81
+
82
+ const emit = defineEmits(['change', 'error', 'field-input', 'field-change']);
83
+
84
+ const tMagicForm = ref<InstanceType<typeof TMagicForm>>();
85
+ const initialized = ref(false);
86
+ const values = ref<FormValue>({});
87
+ const lastValuesProcessed = ref<FormValue>({});
88
+ const fields = new Map<string, any>();
89
+
90
+ const requestFuc = getConfig('request') as Function;
91
+
92
+ const formState: FormState = reactive<FormState>({
93
+ keyProp: props.keyProp,
94
+ popperClass: props.popperClass,
95
+ config: props.config,
96
+ initValues: props.initValues,
97
+ isCompare: props.isCompare,
98
+ lastValues: props.lastValues,
99
+ parentValues: props.parentValues,
100
+ values,
101
+ lastValuesProcessed,
102
+ $emit: emit as (event: string, ...args: any[]) => void,
103
+ fields,
104
+ setField: (prop: string, field: any) => fields.set(prop, field),
105
+ getField: (prop: string) => fields.get(prop),
106
+ deleteField: (prop: string) => fields.delete(prop),
107
+ post: (options: any) => {
108
+ if (requestFuc) {
109
+ return requestFuc({
110
+ ...options,
111
+ method: 'POST',
112
+ });
113
+ }
114
+ },
115
+ });
116
+
117
+ watchEffect(async () => {
118
+ formState.initValues = props.initValues;
119
+ formState.lastValues = props.lastValues;
120
+ formState.isCompare = props.isCompare;
121
+ formState.config = props.config;
122
+ formState.keyProp = props.keyProp;
123
+ formState.popperClass = props.popperClass;
124
+ formState.parentValues = props.parentValues;
125
+
126
+ if (typeof props.extendState === 'function') {
127
+ const state = (await props.extendState(formState)) || {};
128
+ Object.entries(state).forEach(([key, value]) => {
129
+ formState[key] = value;
130
+ });
131
+ }
132
+ });
133
+
134
+ provide('mForm', formState);
135
+
136
+ watch(
137
+ [() => props.config, () => props.initValues],
138
+ ([config], [preConfig]) => {
139
+ if (!isEqual(toRaw(config), toRaw(preConfig))) {
140
+ initialized.value = false;
141
+ }
142
+
143
+ initValue(formState, {
144
+ initValues: props.initValues,
145
+ config: props.config,
146
+ }).then((value) => {
147
+ values.value = value;
148
+ // 非对比模式,初始化完成
149
+ initialized.value = !props.isCompare;
150
+ });
151
+
152
+ if (props.isCompare) {
153
+ // 对比模式下初始化待对比的表单值
154
+ initValue(formState, {
155
+ initValues: props.lastValues,
156
+ config: props.config,
157
+ }).then((value) => {
158
+ lastValuesProcessed.value = value;
159
+ initialized.value = true;
160
+ });
161
+ }
162
+ },
163
+ { immediate: true },
164
+ );
165
+
166
+ const changeHandler = () => {
167
+ emit('change', values.value);
168
+ };
169
+
170
+ defineExpose({
171
+ values,
172
+ lastValuesProcessed,
173
+ formState,
174
+ initialized,
175
+
176
+ changeHandler,
177
+
178
+ resetForm: () => tMagicForm.value?.resetFields(),
179
+
180
+ submitForm: async (native?: boolean): Promise<any> => {
181
+ try {
182
+ await tMagicForm.value?.validate();
183
+ return native ? values.value : cloneDeep(toRaw(values.value));
184
+ } catch (invalidFields: any) {
185
+ emit('error', invalidFields);
186
+
187
+ const error: string[] = [];
188
+
189
+ Object.entries(invalidFields).forEach(([, ValidateError]) => {
190
+ (ValidateError as ValidateError[]).forEach(({ field, message }) => {
191
+ if (field && message) error.push(`${field} -> ${message}`);
192
+ if (field && !message) error.push(`${field} -> 出现错误`);
193
+ if (!field && message) error.push(`${message}`);
194
+ });
195
+ });
196
+
197
+ throw new Error(error.join('<br>'));
198
+ }
199
+ },
200
+ });
201
+ </script>
@@ -0,0 +1,120 @@
1
+ <template>
2
+ <div class="m-form-box" :style="style">
3
+ <div class="m-box-body" :style="bodyHeight ? { height: `${bodyHeight}px` } : {}">
4
+ <TMagicScrollbar>
5
+ <Form
6
+ ref="form"
7
+ :size="size"
8
+ :disabled="disabled"
9
+ :config="config"
10
+ :init-values="values"
11
+ :parent-values="parentValues"
12
+ :label-width="labelWidth"
13
+ :label-position="labelPosition"
14
+ :inline="inline"
15
+ @change="changeHandler"
16
+ ></Form>
17
+ <slot></slot>
18
+ </TMagicScrollbar>
19
+ </div>
20
+
21
+ <div class="dialog-footer" :style="`height: ${footerHeight}px`">
22
+ <div>
23
+ <slot name="left"></slot>
24
+ </div>
25
+ <div>
26
+ <slot name="footer">
27
+ <TMagicButton type="primary" :size="size" :disabled="disabled" :loading="saveFetch" @click="submitHandler">{{
28
+ confirmText
29
+ }}</TMagicButton>
30
+ </slot>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ </template>
35
+
36
+ <script setup lang="ts">
37
+ import { computed, ref, watchEffect } from 'vue';
38
+
39
+ import { TMagicButton, TMagicScrollbar } from '@tmagic/design';
40
+
41
+ import Form from './Form.vue';
42
+ import type { FormConfig } from './schema';
43
+
44
+ defineOptions({
45
+ name: 'MFormBox',
46
+ });
47
+
48
+ const props = withDefaults(
49
+ defineProps<{
50
+ config?: FormConfig;
51
+ values?: Object;
52
+ parentValues?: Object;
53
+ width?: number;
54
+ height?: number;
55
+ labelWidth?: string;
56
+ disabled?: boolean;
57
+ size?: 'small' | 'default' | 'large';
58
+ confirmText?: string;
59
+ inline?: boolean;
60
+ labelPosition?: string;
61
+ }>(),
62
+ {
63
+ config: () => [],
64
+ values: () => ({}),
65
+ confirmText: '确定',
66
+ },
67
+ );
68
+
69
+ const emit = defineEmits(['submit', 'change', 'error']);
70
+
71
+ const footerHeight = 60;
72
+
73
+ const style = computed(() => {
74
+ const style: { width?: string; height?: string } = {};
75
+ if (typeof props.width === 'number') {
76
+ style.width = `${props.width}px`;
77
+ }
78
+ if (typeof props.height === 'number') {
79
+ style.height = `${props.height}px`;
80
+ }
81
+ return style;
82
+ });
83
+
84
+ const form = ref<InstanceType<typeof Form>>();
85
+ const saveFetch = ref(false);
86
+
87
+ const bodyHeight = ref(0);
88
+
89
+ watchEffect(() => {
90
+ if (!props.height) {
91
+ return;
92
+ }
93
+ bodyHeight.value = props.height - footerHeight;
94
+ });
95
+
96
+ const submitHandler = async () => {
97
+ try {
98
+ const values = await form.value?.submitForm();
99
+ emit('submit', values);
100
+ } catch (e) {
101
+ emit('error', e);
102
+ }
103
+ };
104
+
105
+ const changeHandler = (value: any) => {
106
+ emit('change', value);
107
+ };
108
+
109
+ const show = () => {};
110
+
111
+ const hide = () => {};
112
+
113
+ defineExpose({
114
+ form,
115
+ saveFetch,
116
+
117
+ show,
118
+ hide,
119
+ });
120
+ </script>
@@ -0,0 +1,173 @@
1
+ <template>
2
+ <TMagicDialog
3
+ v-model="dialogVisible"
4
+ class="m-form-dialog"
5
+ top="20px"
6
+ append-to-body
7
+ :title="title"
8
+ :width="width"
9
+ :zIndex="zIndex"
10
+ :fullscreen="fullscreen"
11
+ :close-on-click-modal="false"
12
+ @close="closeHandler"
13
+ >
14
+ <div
15
+ v-if="dialogVisible"
16
+ class="m-dialog-body"
17
+ :style="`max-height: ${bodyHeight}; overflow-y: auto; overflow-x: hidden;`"
18
+ >
19
+ <Form
20
+ v-model="stepActive"
21
+ ref="form"
22
+ :size="size"
23
+ :disabled="disabled"
24
+ :config="config"
25
+ :init-values="values"
26
+ :parent-values="parentValues"
27
+ :label-width="labelWidth"
28
+ :label-position="labelPosition"
29
+ :inline="inline"
30
+ @change="changeHandler"
31
+ ></Form>
32
+ <slot></slot>
33
+ </div>
34
+
35
+ <template #footer>
36
+ <TMagicRow class="dialog-footer">
37
+ <TMagicCol :span="12" style="text-align: left">
38
+ <div style="min-height: 1px">
39
+ <slot name="left"></slot>
40
+ </div>
41
+ </TMagicCol>
42
+ <TMagicCol :span="12">
43
+ <slot name="footer">
44
+ <TMagicButton @click="cancel" size="small">取 消</TMagicButton>
45
+ <TMagicButton v-if="hasStep && stepActive > 1" type="info" size="small" @click="preStep"
46
+ >上一步</TMagicButton
47
+ >
48
+ <TMagicButton v-if="hasStep && stepCount > stepActive" type="info" size="small" @click="nextStep"
49
+ >下一步</TMagicButton
50
+ >
51
+ <TMagicButton v-else type="primary" size="small" :disabled="disabled" :loading="saveFetch" @click="save">{{
52
+ confirmText
53
+ }}</TMagicButton>
54
+ </slot>
55
+ </TMagicCol>
56
+ </TMagicRow>
57
+ </template>
58
+ </TMagicDialog>
59
+ </template>
60
+
61
+ <script setup lang="ts">
62
+ import { computed, ref } from 'vue';
63
+
64
+ import { TMagicButton, TMagicCol, TMagicDialog, TMagicRow } from '@tmagic/design';
65
+
66
+ import Form from './Form.vue';
67
+ import { FormConfig, StepConfig } from './schema';
68
+
69
+ defineOptions({
70
+ name: 'MFormDialog',
71
+ });
72
+
73
+ const props = withDefaults(
74
+ defineProps<{
75
+ config?: FormConfig;
76
+ values?: Object;
77
+ parentValues?: Object;
78
+ width?: string | number;
79
+ labelWidth?: string;
80
+ fullscreen?: boolean;
81
+ disabled?: boolean;
82
+ title?: string;
83
+ inline?: boolean;
84
+ labelPosition?: string;
85
+ zIndex?: number;
86
+ size?: 'small' | 'default' | 'large';
87
+ confirmText?: string;
88
+ }>(),
89
+ {
90
+ config: () => [],
91
+ values: () => ({}),
92
+ confirmText: '确定',
93
+ },
94
+ );
95
+
96
+ const emit = defineEmits(['close', 'submit', 'error', 'change']);
97
+
98
+ const form = ref<InstanceType<typeof Form>>();
99
+ const dialogVisible = ref(false);
100
+ const saveFetch = ref(false);
101
+ const stepActive = ref(1);
102
+ const bodyHeight = ref(`${document.body.clientHeight - 194}px`);
103
+
104
+ const stepCount = computed(() => {
105
+ const { length } = props.config;
106
+ for (let index = 0; index < length; index++) {
107
+ if (props.config[index].type === 'step') {
108
+ return (props.config[index] as StepConfig).items.length;
109
+ }
110
+ }
111
+ return 0;
112
+ });
113
+
114
+ const hasStep = computed(() => {
115
+ const { length } = props.config;
116
+ for (let index = 0; index < length; index++) {
117
+ if (props.config[index].type === 'step') {
118
+ return true;
119
+ }
120
+ }
121
+
122
+ return false;
123
+ });
124
+
125
+ const closeHandler = () => {
126
+ stepActive.value = 1;
127
+ emit('close');
128
+ };
129
+
130
+ const save = async () => {
131
+ try {
132
+ const values = await form.value?.submitForm();
133
+ emit('submit', values);
134
+ } catch (e) {
135
+ emit('error', e);
136
+ }
137
+ };
138
+
139
+ const preStep = () => {
140
+ stepActive.value -= 1;
141
+ };
142
+
143
+ const nextStep = () => {
144
+ stepActive.value += 1;
145
+ };
146
+
147
+ const changeHandler = (value: any) => {
148
+ emit('change', value);
149
+ };
150
+
151
+ const show = () => {
152
+ dialogVisible.value = true;
153
+ };
154
+
155
+ const hide = () => {
156
+ dialogVisible.value = false;
157
+ };
158
+
159
+ const cancel = () => {
160
+ hide();
161
+ };
162
+
163
+ defineExpose({
164
+ form,
165
+ saveFetch,
166
+ dialogVisible,
167
+
168
+ cancel,
169
+ save,
170
+ show,
171
+ hide,
172
+ });
173
+ </script>