@tmagic/form 1.8.0-beta.13 → 1.8.0-beta.14

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 (72) hide show
  1. package/dist/es/Form.vue_vue_type_script_setup_true_lang.js +51 -16
  2. package/dist/es/FormDialog.vue_vue_type_script_setup_true_lang.js +12 -5
  3. package/dist/es/FormDrawer.vue_vue_type_script_setup_true_lang.js +38 -12
  4. package/dist/es/containers/Container.vue_vue_type_script_setup_true_lang.js +23 -17
  5. package/dist/es/containers/GroupList.vue_vue_type_script_setup_true_lang.js +6 -5
  6. package/dist/es/containers/GroupListItem.vue_vue_type_script_setup_true_lang.js +41 -31
  7. package/dist/es/containers/Panel.vue_vue_type_script_setup_true_lang.js +16 -10
  8. package/dist/es/containers/table/ActionsColumn.vue_vue_type_script_setup_true_lang.js +13 -6
  9. package/dist/es/containers/table/Table.vue_vue_type_script_setup_true_lang.js +4 -2
  10. package/dist/es/containers/table/useTableColumns.js +5 -1
  11. package/dist/es/containers/table-group-list/TableGroupList.vue_vue_type_script_setup_true_lang.js +2 -1
  12. package/dist/es/fields/Cascader.vue_vue_type_script_setup_true_lang.js +5 -2
  13. package/dist/es/fields/Checkbox.vue_vue_type_script_setup_true_lang.js +4 -1
  14. package/dist/es/fields/CheckboxGroup.vue_vue_type_script_setup_true_lang.js +5 -2
  15. package/dist/es/fields/ColorPicker.vue_vue_type_script_setup_true_lang.js +4 -1
  16. package/dist/es/fields/Date.vue_vue_type_script_setup_true_lang.js +5 -2
  17. package/dist/es/fields/DateTime.vue_vue_type_script_setup_true_lang.js +5 -2
  18. package/dist/es/fields/Daterange.vue_vue_type_script_setup_true_lang.js +5 -2
  19. package/dist/es/fields/Display.vue_vue_type_script_setup_true_lang.js +5 -2
  20. package/dist/es/fields/DynamicField.vue_vue_type_script_setup_true_lang.js +4 -1
  21. package/dist/es/fields/Link.vue_vue_type_script_setup_true_lang.js +29 -7
  22. package/dist/es/fields/Number.vue_vue_type_script_setup_true_lang.js +7 -2
  23. package/dist/es/fields/NumberRange.vue_vue_type_script_setup_true_lang.js +4 -1
  24. package/dist/es/fields/RadioGroup.vue_vue_type_script_setup_true_lang.js +19 -9
  25. package/dist/es/fields/Select.vue_vue_type_script_setup_true_lang.js +4 -1
  26. package/dist/es/fields/Switch.vue_vue_type_script_setup_true_lang.js +4 -1
  27. package/dist/es/fields/Text.vue_vue_type_script_setup_true_lang.js +112 -77
  28. package/dist/es/fields/Textarea.vue_vue_type_script_setup_true_lang.js +4 -1
  29. package/dist/es/fields/Time.vue_vue_type_script_setup_true_lang.js +4 -1
  30. package/dist/es/fields/Timerange.vue_vue_type_script_setup_true_lang.js +6 -3
  31. package/dist/es/index.js +3 -3
  32. package/dist/es/plugin.js +3 -1
  33. package/dist/es/style.css +122 -8
  34. package/dist/es/submitForm.js +2 -8
  35. package/dist/es/utils/config.js +4 -1
  36. package/dist/es/utils/form.js +33 -3
  37. package/dist/es/utils/typeMatch.js +1 -1
  38. package/dist/style.css +122 -8
  39. package/dist/themes/magic-admin.css +499 -0
  40. package/dist/tmagic-form.umd.cjs +541 -296
  41. package/package.json +4 -4
  42. package/src/Form.vue +71 -18
  43. package/src/FormDialog.vue +14 -3
  44. package/src/FormDrawer.vue +40 -12
  45. package/src/containers/Container.vue +16 -5
  46. package/src/containers/GroupList.vue +1 -1
  47. package/src/containers/GroupListItem.vue +28 -22
  48. package/src/containers/Panel.vue +10 -3
  49. package/src/containers/table/ActionsColumn.vue +10 -4
  50. package/src/containers/table/Table.vue +1 -0
  51. package/src/containers/table/useTableColumns.ts +7 -3
  52. package/src/containers/table-group-list/TableGroupList.vue +7 -1
  53. package/src/fields/Link.vue +16 -3
  54. package/src/fields/Number.vue +2 -1
  55. package/src/fields/RadioGroup.vue +21 -6
  56. package/src/fields/Text.vue +43 -19
  57. package/src/plugin.ts +3 -0
  58. package/src/submitForm.ts +5 -11
  59. package/src/theme/container.scss +3 -0
  60. package/src/theme/fieldset.scss +51 -0
  61. package/src/theme/form.scss +6 -0
  62. package/src/theme/group-list.scss +9 -7
  63. package/src/theme/index.scss +1 -0
  64. package/src/theme/link.scss +17 -0
  65. package/src/theme/panel.scss +1 -1
  66. package/src/theme/radio-group.scss +23 -0
  67. package/src/theme/tabs.scss +2 -2
  68. package/src/theme/text.scss +38 -0
  69. package/src/theme/themes/magic-admin/index.scss +95 -0
  70. package/src/utils/config.ts +7 -1
  71. package/src/utils/form.ts +37 -0
  72. package/types/index.d.ts +64 -3
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.8.0-beta.13",
2
+ "version": "1.8.0-beta.14",
3
3
  "name": "@tmagic/form",
4
4
  "type": "module",
5
5
  "sideEffects": [
@@ -52,9 +52,9 @@
52
52
  "peerDependencies": {
53
53
  "vue": "^3.5.40",
54
54
  "typescript": "^6.0.3",
55
- "@tmagic/form-schema": "1.8.0-beta.13",
56
- "@tmagic/utils": "1.8.0-beta.13",
57
- "@tmagic/design": "1.8.0-beta.13"
55
+ "@tmagic/utils": "1.8.0-beta.14",
56
+ "@tmagic/design": "1.8.0-beta.14",
57
+ "@tmagic/form-schema": "1.8.0-beta.14"
58
58
  },
59
59
  "peerDependenciesMeta": {
60
60
  "typescript": {
package/src/Form.vue CHANGED
@@ -1,6 +1,10 @@
1
1
  <template>
2
2
  <TMagicForm
3
- class="m-form"
3
+ :class="[
4
+ 'm-form',
5
+ effectiveTheme ? `m-form--${effectiveTheme}` : '',
6
+ effectiveTheme ? `m-theme--${effectiveTheme}` : '',
7
+ ]"
4
8
  ref="tMagicForm"
5
9
  :model="values"
6
10
  :label-width="labelWidth"
@@ -32,15 +36,27 @@
32
36
  </template>
33
37
 
34
38
  <script setup lang="ts">
35
- import { computed, nextTick, provide, reactive, ref, shallowRef, toRaw, useTemplateRef, watch, watchEffect } from 'vue';
39
+ import {
40
+ computed,
41
+ inject,
42
+ nextTick,
43
+ provide,
44
+ reactive,
45
+ ref,
46
+ shallowRef,
47
+ toRaw,
48
+ useTemplateRef,
49
+ watch,
50
+ watchEffect,
51
+ } from 'vue';
36
52
  import { cloneDeep, isEqual } from 'lodash-es';
37
53
 
38
- import { TMagicForm, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
54
+ import { M_THEME_KEY, TMagicForm, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
39
55
  import { setValueByKeyPath } from '@tmagic/utils';
40
56
 
41
57
  import Container from './containers/Container.vue';
42
58
  import { getConfig } from './utils/config';
43
- import { initValue } from './utils/form';
59
+ import { applyExtendState, initValue } from './utils/form';
44
60
  import type {
45
61
  ChangeRecord,
46
62
  ContainerChangeEventData,
@@ -116,6 +132,15 @@ const props = withDefaults(
116
132
  * 通过 provide 下发,对整棵表单的所有层级 Container 生效,只需在 MForm 这一层传一次。
117
133
  */
118
134
  selfDiffFieldTypes?: string[] | ((_defaultTypes: string[]) => string[]);
135
+ /**
136
+ * 主题名称:对应 `packages/form/src/theme/themes/<theme>/index.scss` 的目录名。
137
+ *
138
+ * 设置后会在表单根元素上追加 `m-form--<theme>` 修饰类,配合按需引入
139
+ * `@tmagic/form/dist/themes/<theme>.css` 即可启用主题样式。
140
+ *
141
+ * 例如:`<MForm theme="magic-admin" />` + `import '@tmagic/form/dist/themes/magic-admin.css'`。
142
+ */
143
+ theme?: string;
119
144
  }>(),
120
145
  {
121
146
  config: () => [],
@@ -149,6 +174,27 @@ const fields = new Map<string, any>();
149
174
 
150
175
  const requestFuc = getConfig('request') as Function;
151
176
 
177
+ /**
178
+ * 当前表单生效的主题名称:
179
+ * - 优先用本组件自己的 `props.theme`;
180
+ * - 没设置时回退到最近祖先 `<MEditor>` / `<MForm>` provide 的主题,便于内嵌于编辑器
181
+ * 时自动跟随外层主题,无需在每个 `MForm` 上重复传 `theme`。
182
+ *
183
+ * 同时把合并后的值再 provide 出去(见下方 `provide(M_THEME_KEY, ...)`),让 form 子树
184
+ * 里再嵌套的 portal 组件(`TMagicPopover` 等)依然能拿到非空主题。
185
+ */
186
+ const ancestorTheme = inject(M_THEME_KEY, null);
187
+ const effectiveTheme = computed(() => props.theme || ancestorTheme?.value || '');
188
+
189
+ /**
190
+ * 拼到 `formState.popperClass` 上的主题修饰类(仅 `m-theme--<theme>`,
191
+ * 不带 `m-form` / `m-editor` 前缀,因为 Element Plus 弹层节点本身既不是 form 也不是 editor)。
192
+ *
193
+ * 这条类会随所有读 `mForm.popperClass` 的字段(Select / DateTime / Cascader 等)下发到
194
+ * Element Plus 的 `popper-class`,让 portal 节点也命中 `m-theme--<theme>` 上的 CSS 变量。
195
+ */
196
+ const themeClass = computed(() => (effectiveTheme.value ? `m-theme--${effectiveTheme.value}` : ''));
197
+
152
198
  /**
153
199
  * formState 实现说明:
154
200
  *
@@ -165,13 +211,21 @@ const requestFuc = getConfig('request') as Function;
165
211
  * 把最新值刷进 formState。
166
212
  * - accessor 描述符(`{ get stage() { return ... } }`)按原样写入,调用方可以控制
167
213
  * 读时求值,每次读取都会重新执行 getter。
214
+ *
215
+ * 4. `popperClass` 会自动拼接 `themeClass`:调用方传入的 `popperClass` + 当前主题
216
+ * 修饰类(含祖先 `<MEditor>` provide 的主题)。这样所有透传到 Element Plus 弹层
217
+ * `popper-class` 的字段(Select / DateTime / Cascader 等)能自带主题作用域。
168
218
  */
169
219
  const formState: FormState = reactive<FormState>({
170
220
  get keyProp() {
171
221
  return props.keyProp;
172
222
  },
173
223
  get popperClass() {
174
- return props.popperClass;
224
+ const userClass = props.popperClass ?? '';
225
+ const tc = themeClass.value;
226
+ if (!userClass) return tc;
227
+ if (!tc) return userClass;
228
+ return `${userClass} ${tc}`;
175
229
  },
176
230
  get config() {
177
231
  return props.config;
@@ -222,11 +276,10 @@ const formState: FormState = reactive<FormState>({
222
276
  * 这里不再重复同步;因此 `props.initValues` 这类高频变化也不会再触发
223
277
  * `extendState` 重跑(旧版的性能问题修复点)。
224
278
  *
225
- * 实现细节:
226
- * - data 描述符:通过 `formState[key] = value` 走 reactive proxy 的 set
227
- * 触发依赖通知;与旧版「逐项赋值」语义完全等价。
228
- * - accessor 描述符(`{ get stage() {...} }`)按原样写入 formState,调用方
229
- * 可以自行控制读时求值;强制 `configurable: true` 以便下一次重跑可再 define。
279
+ * 实现细节:合并逻辑统一收口在 `applyExtendState`(utils/form)——
280
+ * data 描述符走 reactive proxy 的 set 触发依赖通知(与旧版「逐项赋值」语义等价),
281
+ * accessor 描述符按原样 defineProperty 支持读时求值;
282
+ * props 派生的只读 getter 字段(keyProp 等)以普通字段形式返回时会被跳过并告警。
230
283
  */
231
284
  watchEffect(async (onCleanup) => {
232
285
  const { extendState } = props;
@@ -246,18 +299,18 @@ watchEffect(async (onCleanup) => {
246
299
  }
247
300
  if (stale) return;
248
301
 
249
- for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(state))) {
250
- if ('value' in descriptor) {
251
- (formState as any)[key] = (state as any)[key];
252
- } else {
253
- descriptor.configurable = true;
254
- Object.defineProperty(formState, key, descriptor);
255
- }
256
- }
302
+ applyExtendState(formState, state);
257
303
  });
258
304
 
259
305
  provide('mForm', formState);
260
306
 
307
+ /**
308
+ * 把生效主题(自身或祖先)再 provide 出去,供 form 子树内含 `Teleport` 的组件
309
+ * (如 `TMagicPopover`)在传送目标上挂 `m-theme--<theme>` 类。
310
+ * 详见 `@tmagic/design/theme.ts`。
311
+ */
312
+ provide(M_THEME_KEY, effectiveTheme);
313
+ provide('formInline', props.inline);
261
314
  // 对比相关配置单独通过 provide 下发,所有层级的 Container 通过 inject 获取,无需逐层透传 prop。
262
315
  // 用 getter 对象保证读取时回到最新的 props 值,维持响应式。
263
316
  provide(FORM_DIFF_CONFIG_KEY, {
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <TMagicDialog
3
3
  v-model="dialogVisible"
4
- class="m-form-dialog"
4
+ :class="['m-form-dialog', effectiveTheme ? `m-theme--${effectiveTheme}` : '']"
5
5
  top="20px"
6
6
  append-to-body
7
7
  :title="title"
@@ -34,6 +34,7 @@
34
34
  :use-field-text-in-error="useFieldTextInError"
35
35
  :type-match-valid="typeMatchValid"
36
36
  :extend-state="extendState"
37
+ :theme="effectiveTheme"
37
38
  @change="changeHandler"
38
39
  ></Form>
39
40
  <slot></slot>
@@ -66,9 +67,9 @@
66
67
  </template>
67
68
 
68
69
  <script setup lang="ts">
69
- import { computed, ref } from 'vue';
70
+ import { computed, inject, provide, ref } from 'vue';
70
71
 
71
- import { TMagicButton, TMagicCol, TMagicDialog, TMagicRow } from '@tmagic/design';
72
+ import { M_THEME_KEY, TMagicButton, TMagicCol, TMagicDialog, TMagicRow } from '@tmagic/design';
72
73
 
73
74
  import Form from './Form.vue';
74
75
  import { ContainerChangeEventData, FormConfig, FormState, FormValue, StepConfig } from './schema';
@@ -104,6 +105,12 @@ const props = withDefaults(
104
105
  useFieldTextInError?: boolean;
105
106
  /** 透传给内部 `MForm`,用于扩展 `formState`(如注入 `$message` / `$store` 等) */
106
107
  extendState?: (_state: FormState) => Record<string, any> | Promise<Record<string, any>>;
108
+ /**
109
+ * 主题名。优先级:传入 `theme` prop > 祖先 `provide(M_THEME_KEY)` > 空串。
110
+ * 计算结果会再次 `provide` 出去,使得 Dialog 被 Teleport 到 body 后,内部子树
111
+ * (包括 `MForm` 以及更深的 popover / dropdown)仍能拿到主题。
112
+ */
113
+ theme?: string;
107
114
  }>(),
108
115
  {
109
116
  config: () => [],
@@ -118,6 +125,10 @@ const props = withDefaults(
118
125
  },
119
126
  );
120
127
 
128
+ const ancestorTheme = inject(M_THEME_KEY, null);
129
+ const effectiveTheme = computed(() => props.theme || ancestorTheme?.value || '');
130
+ provide(M_THEME_KEY, effectiveTheme);
131
+
121
132
  const emit = defineEmits(['close', 'submit', 'error', 'change']);
122
133
 
123
134
  const form = ref<InstanceType<typeof Form>>();
@@ -1,8 +1,8 @@
1
1
  <template>
2
2
  <TMagicDrawer
3
- class="m-form-drawer"
3
+ :class="['m-form-drawer', effectiveTheme ? `m-theme--${effectiveTheme}` : '']"
4
4
  ref="drawer"
5
- v-model="visible"
5
+ v-model="dialogVisible"
6
6
  :title="title"
7
7
  :close-on-press-escape="closeOnPressEscape"
8
8
  :append-to-body="true"
@@ -16,7 +16,7 @@
16
16
  @close="closeHandler"
17
17
  @closed="closedHandler"
18
18
  >
19
- <div v-if="visible" ref="drawerBody" class="m-drawer-body">
19
+ <div v-if="dialogVisible" ref="drawerBody" class="m-drawer-body">
20
20
  <Form
21
21
  ref="form"
22
22
  :size="size"
@@ -31,6 +31,7 @@
31
31
  :use-field-text-in-error="useFieldTextInError"
32
32
  :type-match-valid="typeMatchValid"
33
33
  :extend-state="extendState"
34
+ :theme="effectiveTheme"
34
35
  @change="changeHandler"
35
36
  ></Form>
36
37
  <slot></slot>
@@ -57,18 +58,18 @@
57
58
  </template>
58
59
 
59
60
  <script setup lang="ts">
60
- import { ref, watchEffect } from 'vue';
61
+ import { computed, inject, provide, ref, watchEffect } from 'vue';
61
62
 
62
- import { TMagicButton, TMagicCol, TMagicDrawer, TMagicRow } from '@tmagic/design';
63
+ import { M_THEME_KEY, TMagicButton, TMagicCol, TMagicDrawer, TMagicRow } from '@tmagic/design';
63
64
 
64
65
  import Form from './Form.vue';
65
66
  import type { ContainerChangeEventData, FormConfig, FormState, FormValue } from './schema';
66
67
 
67
68
  defineOptions({
68
- name: 'MFormDialog',
69
+ name: 'MFormDrawer',
69
70
  });
70
71
 
71
- withDefaults(
72
+ const props = withDefaults(
72
73
  defineProps<{
73
74
  config?: FormConfig;
74
75
  values?: Object;
@@ -92,6 +93,12 @@ withDefaults(
92
93
  beforeClose?: (_done: (_cancel?: boolean) => void) => void;
93
94
  /** 透传给内部 `MForm`,用于扩展 `formState`(如注入 `$message` / `$store` 等) */
94
95
  extendState?: (_state: FormState) => Record<string, any> | Promise<Record<string, any>>;
96
+ /**
97
+ * 主题名。优先级:传入 `theme` prop > 祖先 `provide(M_THEME_KEY)` > 空串。
98
+ * 计算结果会再次 `provide` 出去,使得 Drawer 被 Teleport 到 body 后,内部子树
99
+ * (包括 `MForm` 以及更深的 popover / dropdown)仍能拿到主题。
100
+ */
101
+ theme?: string;
95
102
  }>(),
96
103
  {
97
104
  closeOnPressEscape: true,
@@ -102,12 +109,16 @@ withDefaults(
102
109
  },
103
110
  );
104
111
 
112
+ const ancestorTheme = inject(M_THEME_KEY, null);
113
+ const effectiveTheme = computed(() => props.theme || ancestorTheme?.value || '');
114
+ provide(M_THEME_KEY, effectiveTheme);
115
+
105
116
  const emit = defineEmits(['close', 'closed', 'submit', 'error', 'change', 'open', 'opened']);
106
117
 
107
118
  const drawer = ref<InstanceType<typeof TMagicDrawer>>();
108
119
  const form = ref<InstanceType<typeof Form>>();
109
120
  const drawerBody = ref<HTMLDivElement>();
110
- const visible = ref(false);
121
+ const dialogVisible = ref(false);
111
122
  const saveFetch = ref(false);
112
123
  const bodyHeight = ref(0);
113
124
 
@@ -148,25 +159,42 @@ const closedHandler = () => {
148
159
  };
149
160
 
150
161
  const show = () => {
151
- visible.value = true;
162
+ dialogVisible.value = true;
152
163
  };
153
164
 
154
165
  const hide = () => {
155
- visible.value = false;
166
+ dialogVisible.value = false;
167
+ };
168
+ const close = () => {
169
+ dialogVisible.value = false;
170
+ };
171
+ const cancel = () => {
172
+ hide();
156
173
  };
157
-
158
174
  /** 用于关闭 Drawer, 该方法会调用传入的 before-close 方法 */
159
175
  const handleClose = () => {
160
176
  drawer.value?.handleClose();
161
177
  };
162
178
 
179
+ const save = async () => {
180
+ try {
181
+ const changeRecords = [...(form.value?.changeRecords || [])];
182
+ const values = await form.value?.submitForm();
183
+ emit('submit', values, { changeRecords });
184
+ } catch (e) {
185
+ emit('error', e);
186
+ }
187
+ };
188
+
163
189
  defineExpose({
164
190
  form,
165
191
  saveFetch,
166
192
  bodyHeight,
167
-
193
+ close,
168
194
  show,
169
195
  hide,
196
+ save,
197
+ cancel,
170
198
  handleClose,
171
199
  });
172
200
  </script>
@@ -35,6 +35,9 @@
35
35
  :label-title="config.labelTitle"
36
36
  :text="text"
37
37
  ></FormLabel>
38
+ <span class="m-form-tip m-form-title-extra" v-if="config.titleExtra && config.labelPosition === 'top'">
39
+ {{ config.titleExtra }}</span
40
+ >
38
41
  </slot>
39
42
  </template>
40
43
 
@@ -222,17 +225,23 @@
222
225
  </template>
223
226
  </template>
224
227
 
225
- <div style="text-align: center" v-if="config.expand && type !== 'fieldset'">
226
- <TMagicButton type="primary" size="small" :disabled="false" link @click="expandHandler">{{
227
- expand ? '收起配置' : '展开更多配置'
228
- }}</TMagicButton>
228
+ <div class="m-form-container-expand" v-if="config.expand && type !== 'fieldset'">
229
+ <TMagicButton
230
+ type="primary"
231
+ size="small"
232
+ :disabled="false"
233
+ link
234
+ @click="expandHandler"
235
+ :icon="expand ? ArrowUp : ArrowDown"
236
+ >{{ expand ? '收起配置' : '展开更多配置' }}</TMagicButton
237
+ >
229
238
  </div>
230
239
  </div>
231
240
  </template>
232
241
 
233
242
  <script setup lang="ts">
234
243
  import { computed, inject, readonly, ref, toRaw, watch, watchEffect } from 'vue';
235
- import { WarningFilled } from '@element-plus/icons-vue';
244
+ import { ArrowDown, ArrowUp, WarningFilled } from '@element-plus/icons-vue';
236
245
  import { isEqual } from 'lodash-es';
237
246
 
238
247
  import { TMagicButton, TMagicFormItem, TMagicIcon, TMagicTooltip } from '@tmagic/design';
@@ -440,8 +449,10 @@ const formItemProps = computed(() => ({
440
449
  prop: itemProp.value,
441
450
  labelWidth: itemLabelWidth.value,
442
451
  labelPosition: props.config.labelPosition,
452
+
443
453
  rules: rule.value,
444
454
  extra: filterFunction(mForm, props.config.extra, props),
455
+ extraTips: props.config.extraTips,
445
456
  }));
446
457
 
447
458
  const itemLabelWidth = computed(() => props.config.labelWidth ?? props.labelWidth);
@@ -2,7 +2,7 @@
2
2
  <div class="m-fields-group-list">
3
3
  <div v-if="config.extra" v-html="config.extra" style="color: rgba(0, 0, 0, 0.45)"></div>
4
4
  <div v-if="!model[name] || !model[name].length" class="el-table__empty-block">
5
- <span class="el-table__empty-text t-table__empty">暂无数据</span>
5
+ <span class="el-table__empty-text t-table__empty">暂无{{ config.titlePrefix || '' }}数据</span>
6
6
  </div>
7
7
 
8
8
  <MFieldsGroupListItem
@@ -1,26 +1,29 @@
1
1
  <template>
2
- <TMagicCard class="m-fields-group-list-item" :body-style="{ display: expand ? 'block' : 'none' }">
2
+ <TMagicCard class="m-fields-group-list-item" :flat="config.flat" :body-style="{ display: expand ? 'block' : 'none' }">
3
3
  <template #header>
4
- <div>
5
- <TMagicButton link :disabled="disabled" @click="expandHandler">
6
- <TMagicIcon><CaretBottom v-if="expand" /><CaretRight v-else /></TMagicIcon><span v-html="title"></span>
4
+ <div class="m-fields-group-list-item-header">
5
+ <TMagicButton link :disabled="disabled" @click="expandHandler" class="expand-button">
6
+ <TMagicIcon><ArrowDown v-if="expand" /><ArrowRight v-else /></TMagicIcon>
7
7
  </TMagicButton>
8
8
 
9
- <TMagicButton
10
- v-if="!isCompare"
11
- v-show="showDelete"
12
- type="danger"
13
- size="small"
14
- link
15
- :icon="Delete"
16
- :disabled="disabled"
17
- @click="removeHandler"
18
- ></TMagicButton>
9
+ <span v-html="title"></span>
10
+ <TMagicTooltip :content="`删除 ${title}`">
11
+ <TMagicButton
12
+ v-if="!isCompare"
13
+ v-show="showDelete"
14
+ size="default"
15
+ link
16
+ class="delete-button"
17
+ :icon="Delete"
18
+ :disabled="disabled"
19
+ @click="removeHandler"
20
+ ></TMagicButton>
21
+ </TMagicTooltip>
19
22
 
20
23
  <TMagicButton
21
24
  v-if="copyable && !isCompare"
22
25
  link
23
- size="small"
26
+ size="default"
24
27
  type="primary"
25
28
  :icon="DocumentCopy"
26
29
  :disabled="disabled"
@@ -32,18 +35,18 @@
32
35
  <TMagicButton
33
36
  v-show="index !== 0"
34
37
  link
35
- size="small"
38
+ size="default"
36
39
  :disabled="disabled"
37
- :icon="CaretTop"
40
+ :icon="Top"
38
41
  @click="changeOrder(-1)"
39
42
  >上移</TMagicButton
40
43
  >
41
44
  <TMagicButton
42
45
  v-show="index !== length - 1"
43
46
  link
44
- size="small"
47
+ size="default"
45
48
  :disabled="disabled"
46
- :icon="CaretBottom"
49
+ :icon="Bottom"
47
50
  @click="changeOrder(1)"
48
51
  >下移</TMagicButton
49
52
  >
@@ -107,9 +110,9 @@
107
110
 
108
111
  <script setup lang="ts">
109
112
  import { computed, inject, ref } from 'vue';
110
- import { CaretBottom, CaretRight, CaretTop, Delete, DocumentCopy, Position } from '@element-plus/icons-vue';
113
+ import { ArrowDown, ArrowRight, Bottom, Delete, DocumentCopy, Position, Top } from '@element-plus/icons-vue';
111
114
 
112
- import { TMagicButton, TMagicCard, TMagicIcon, TMagicInputNumber, TMagicPopover } from '@tmagic/design';
115
+ import { TMagicButton, TMagicCard, TMagicIcon, TMagicInputNumber, TMagicPopover, TMagicTooltip } from '@tmagic/design';
113
116
 
114
117
  import type { ContainerChangeEventData, FormState, GroupListConfig } from '../schema';
115
118
  import { filterFunction } from '../utils/form';
@@ -152,7 +155,10 @@ const rowConfig = computed(() => ({
152
155
 
153
156
  const title = computed(() => {
154
157
  if (props.config.titleKey && props.model[props.config.titleKey]) {
155
- return props.model[props.config.titleKey];
158
+ const { titlePrefix } = props.config;
159
+ return titlePrefix
160
+ ? `${titlePrefix} ${String(props.index + 1)}: ${props.model[props.config.titleKey]}`
161
+ : `${props.model[props.config.titleKey]}`;
156
162
  }
157
163
 
158
164
  if (props.config.title) {
@@ -3,10 +3,17 @@
3
3
  v-if="items && items.length"
4
4
  class="box-card m-form-panel"
5
5
  :body-style="{ display: expand ? 'block' : 'none' }"
6
+ :flat="config.flat"
6
7
  >
7
8
  <template #header>
8
9
  <div style="width: 100%; display: flex; align-items: center">
9
- <TMagicButton style="padding: 0" link :icon="expand ? CaretBottom : CaretRight" @click="expand = !expand">
10
+ <TMagicButton
11
+ style="padding: 0; margin-right: 10px"
12
+ link
13
+ :icon="expand ? ArrowDown : ArrowRight"
14
+ @click="expand = !expand"
15
+ v-if="!hideExpand"
16
+ >
10
17
  </TMagicButton>
11
18
  <slot name="header">
12
19
  <span style="cursor: pointer" @click="expand = !expand">{{ filter(config.title) }}</span>
@@ -62,7 +69,7 @@
62
69
 
63
70
  <script setup lang="ts">
64
71
  import { computed, inject, ref } from 'vue';
65
- import { CaretBottom, CaretRight } from '@element-plus/icons-vue';
72
+ import { ArrowDown, ArrowRight } from '@element-plus/icons-vue';
66
73
 
67
74
  import { TMagicButton, TMagicCard } from '@tmagic/design';
68
75
 
@@ -85,6 +92,7 @@ const props = defineProps<{
85
92
  prop?: string;
86
93
  size?: string;
87
94
  disabled?: boolean;
95
+ hideExpand?: boolean;
88
96
  }>();
89
97
 
90
98
  const emit = defineEmits<{
@@ -107,7 +115,6 @@ const onAddDiffCount = () => emit('addDiffCount');
107
115
 
108
116
  defineExpose({
109
117
  getExpand: () => expand.value,
110
-
111
118
  setExpand: (v: boolean) => {
112
119
  expand.value = v;
113
120
  },
@@ -9,15 +9,18 @@
9
9
  >
10
10
  </TMagicButton>
11
11
  </TMagicTooltip>
12
+
12
13
  <TMagicButton
13
14
  v-show="showDelete(index + 1 + currentPage * pageSize - 1)"
14
15
  size="small"
15
16
  type="danger"
16
17
  link
17
18
  title="删除"
18
- :icon="config.deleteActionButtonIcon || Delete"
19
+ :icon="flat ? undefined : config.deleteActionButtonIcon || Delete"
19
20
  @click="removeHandler(index + 1 + currentPage * pageSize - 1)"
20
- ></TMagicButton>
21
+ >
22
+ <template v-if="flat">删除</template>
23
+ </TMagicButton>
21
24
 
22
25
  <TMagicButton
23
26
  v-if="copyable(index + 1 + currentPage * pageSize - 1)"
@@ -25,10 +28,12 @@
25
28
  size="small"
26
29
  type="primary"
27
30
  title="复制"
28
- :icon="config.copyActionButtonIcon || DocumentCopy"
31
+ :icon="flat ? undefined : config.copyActionButtonIcon || DocumentCopy"
29
32
  :disabled="disabled"
30
33
  @click="copyHandler(index + 1 + currentPage * pageSize - 1)"
31
- ></TMagicButton>
34
+ >
35
+ <template v-if="flat">复制</template>
36
+ </TMagicButton>
32
37
  </template>
33
38
 
34
39
  <script setup lang="ts">
@@ -53,6 +58,7 @@ const props = defineProps<{
53
58
  row: any;
54
59
  prop?: string;
55
60
  sortKey?: string;
61
+ flat?: boolean;
56
62
  }>();
57
63
 
58
64
  const mForm = inject<FormState | undefined>('mForm');
@@ -27,6 +27,7 @@
27
27
  :key="updateKey"
28
28
  @select="selectHandle"
29
29
  @sort-change="sortChangeHandler"
30
+ :empty-text="`暂无${config.titlePrefix || ''}数据`"
30
31
  ></TMagicTable>
31
32
  </TMagicTooltip>
32
33
 
@@ -6,6 +6,7 @@ import { type TableColumnOptions, TMagicIcon, TMagicTooltip } from '@tmagic/desi
6
6
  import type { FormItemConfig, FormState, TableColumnConfig } from '@tmagic/form-schema';
7
7
 
8
8
  import type { ContainerChangeEventData } from '../../schema';
9
+ import { isGlobalFlat } from '../../utils/config';
9
10
  import { display as displayFunc, getDataByPage, sortArray } from '../../utils/form';
10
11
  import Container from '../Container.vue';
11
12
 
@@ -111,23 +112,26 @@ export const useTableColumns = (
111
112
  },
112
113
  });
113
114
  }
114
-
115
- let actionFixed: 'left' | 'right' | undefined = props.config.fixed === false ? undefined : 'left';
116
-
115
+ const defaultFixed: 'left' | 'right' = isGlobalFlat.value ? 'right' : 'left';
116
+ let actionFixed: 'left' | 'right' | undefined = props.config.fixed === false ? undefined : defaultFixed;
117
117
  if (typeof props.config.fixed === 'string' && ['left', 'right'].includes(props.config.fixed)) {
118
118
  actionFixed = props.config.fixed;
119
119
  }
120
120
 
121
+ const actionFlat = props.config.flat === undefined && isGlobalFlat.value ? true : props.config.flat;
122
+
121
123
  const actionColumn = {
122
124
  props: {
123
125
  label: '操作',
124
126
  fixed: actionFixed,
127
+
125
128
  width: props.config.operateColWidth ?? (props.config.dropSortHandle && props.config.dropSort ? 132 : 112),
126
129
  align: 'center',
127
130
  },
128
131
  cell: ({ row, $index }: any) =>
129
132
  h(ActionsColumn, {
130
133
  row,
134
+ flat: actionFlat,
131
135
  index: $index,
132
136
  model: props.model,
133
137
  config: props.config,
@@ -37,11 +37,17 @@
37
37
  :size="addButtonSize"
38
38
  :plain="displayMode === 'table'"
39
39
  :icon="Plus"
40
+ text
40
41
  :disabled="disabled"
41
42
  v-bind="currentConfig.addButtonConfig?.props || { type: 'primary' }"
42
43
  @click="newHandler"
43
44
  >
44
- {{ currentConfig.addButtonConfig?.text || (displayMode === 'table' ? '新增一行' : '新增') }}
45
+ {{
46
+ currentConfig.addButtonConfig?.text ||
47
+ (displayMode === 'table'
48
+ ? `新增一行${groupListConfig.titlePrefix || ''}`
49
+ : `新增${groupListConfig.titlePrefix || ''}`)
50
+ }}
45
51
  </TMagicButton>
46
52
  </template>
47
53
  </component>