@aspire-ui/element-component-pro 1.0.13 → 1.0.15

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/dist/style.css CHANGED
@@ -1 +1 @@
1
- .ecp-pro-table[data-v-c5638c20]{padding:16px;background:#fff;width:100%;box-sizing:border-box}.ecp-pro-table[data-v-c5638c20] .el-table{width:100%!important}.ecp-pro-table__header[data-v-c5638c20]{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px}.ecp-pro-table__title-wrapper[data-v-c5638c20]{display:flex;align-items:center;gap:4px}.ecp-pro-table__title[data-v-c5638c20]{font-size:16px;font-weight:600}.ecp-pro-table__help[data-v-c5638c20]{color:#909399;cursor:help}.ecp-pro-table__toolbar[data-v-c5638c20]{display:flex;align-items:center;gap:8px}.ecp-pro-table__body[data-v-c5638c20]{width:100%}.ecp-pro-table__pagination[data-v-c5638c20]{margin-top:16px;display:flex;justify-content:flex-end}.ecp-pro-table__col-help[data-v-c5638c20]{margin-left:4px;color:#909399;cursor:help}.ecp-table-action[data-v-45a58e7c],.ecp-table-action__item[data-v-45a58e7c]{display:inline-flex;align-items:center;gap:4px}.ecp-table-action__icon[data-v-45a58e7c]{margin-right:4px}.ecp-table-action__more[data-v-45a58e7c]{display:inline-flex;align-items:center}.ecp-table-action__dropdown-item[data-v-45a58e7c]{display:inline-flex;align-items:center;gap:4px}.ecp-tree-select[data-v-f30bba11]{position:relative;width:100%}.ecp-tree-select__filter-inner[data-v-f30bba11]{margin-bottom:8px}.ecp-tree-select__dropdown[data-v-f30bba11]{position:absolute;top:100%;left:0;right:0;max-height:280px;overflow:auto;background:#fff;border:1px solid #dcdfe6;border-radius:4px;margin-top:4px;z-index:1000;padding:8px}.ecp-tree-select__loading[data-v-f30bba11]{padding:24px;text-align:center;color:#909399;font-size:14px}.ecp-pro-form-item__colon[data-v-d3466c67]{margin-right:2px}.ecp-pro-form-item__help-icon[data-v-d3466c67]{margin-left:4px;color:#909399;cursor:help;font-size:14px}.ecp-pro-form-item__help-icon[data-v-d3466c67]:hover{color:#409eff}.ecp-pro-form-item__help-item[data-v-d3466c67]{margin-bottom:4px}.ecp-pro-form-item__help-item[data-v-d3466c67]:last-child{margin-bottom:0}.ecp-form-actions[data-v-489c88d2]{text-align:right}.ecp-form-actions__advance[data-v-489c88d2]{margin-right:8px}.el-icon-d-arrow-left.up[data-v-489c88d2]{transform:rotate(90deg)}.el-icon-d-arrow-left.down[data-v-489c88d2]{transform:rotate(-90deg)}.ecp-pro-form[data-v-bf70afca]{padding:16px;position:relative}.ecp-pro-form__advance[data-v-bf70afca]{margin-bottom:16px}.ecp-pro-form_col[data-v-bf70afca]{position:relative;float:right}.el-icon-d-arrow-left.up[data-v-bf70afca]{transform:rotate(90deg)}.el-icon-d-arrow-left.down[data-v-bf70afca]{transform:rotate(-90deg)}.ecp-form-actions__advance[data-v-bf70afca]{position:absolute;bottom:0;left:50%;transform:translate(-50%,-50%)}.ecp-pro-descriptions[data-v-7d6cd376]{width:100%;box-sizing:border-box}.ecp-pro-descriptions__header[data-v-7d6cd376]{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;gap:12px}.ecp-pro-descriptions__title-wrap[data-v-7d6cd376]{display:flex;align-items:center;gap:6px}.ecp-pro-descriptions__title[data-v-7d6cd376]{font-size:16px;font-weight:600;color:#303133}.ecp-pro-descriptions__help[data-v-7d6cd376],.ecp-pro-descriptions__toggle[data-v-7d6cd376]{color:#909399}.ecp-pro-descriptions__toggle .el-icon-arrow-down[data-v-7d6cd376]{margin-left:4px;transition:transform .2s ease}.ecp-pro-descriptions__toggle .el-icon-arrow-down.is-expanded[data-v-7d6cd376]{transform:rotate(180deg)}.ecp-pro-descriptions__body[data-v-7d6cd376]{display:grid;border-top:1px solid #ebeef5;border-left:1px solid #ebeef5;overflow:hidden}.ecp-pro-descriptions__body.is-collapsed[data-v-7d6cd376]{overflow:hidden}.ecp-pro-descriptions__body[data-v-7d6cd376]:not(.is-bordered){border-top:0;border-left:0;gap:12px 16px}.ecp-pro-descriptions__item[data-v-7d6cd376]{display:flex;min-width:0;border-right:1px solid #ebeef5;border-bottom:1px solid #ebeef5}.ecp-pro-descriptions__body:not(.is-bordered) .ecp-pro-descriptions__item[data-v-7d6cd376]{border-right:0;border-bottom:0}.ecp-pro-descriptions__label[data-v-7d6cd376],.ecp-pro-descriptions__content[data-v-7d6cd376]{min-width:0;box-sizing:border-box;word-break:break-word}.ecp-pro-descriptions__label[data-v-7d6cd376]{flex:0 0 120px;padding:12px 16px;color:#606266;background:#fafafa}.ecp-pro-descriptions__content[data-v-7d6cd376]{flex:1;padding:12px 16px;color:#303133;background:#fff}.ecp-pro-descriptions__body:not(.is-bordered) .ecp-pro-descriptions__label[data-v-7d6cd376]{flex-basis:auto;padding:0;margin-right:8px;background:transparent;font-weight:500}.ecp-pro-descriptions__body:not(.is-bordered) .ecp-pro-descriptions__content[data-v-7d6cd376]{padding:0;background:transparent}.ecp-pro-descriptions__body.is-small .ecp-pro-descriptions__label[data-v-7d6cd376],.ecp-pro-descriptions__body.is-small .ecp-pro-descriptions__content[data-v-7d6cd376]{padding-top:8px;padding-bottom:8px;font-size:13px}@media (max-width: 767px){.ecp-pro-descriptions__item[data-v-7d6cd376]{flex-direction:column}.ecp-pro-descriptions__label[data-v-7d6cd376]{flex-basis:auto}}
1
+ .ecp-pro-table[data-v-f3d27813]{padding:16px;background:#fff;width:100%;box-sizing:border-box}.ecp-pro-table[data-v-f3d27813] .el-table{width:100%!important}.ecp-pro-table__header[data-v-f3d27813]{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px}.ecp-pro-table__title-wrapper[data-v-f3d27813]{display:flex;align-items:center;gap:4px}.ecp-pro-table__title[data-v-f3d27813]{font-size:16px;font-weight:600}.ecp-pro-table__help[data-v-f3d27813]{color:#909399;cursor:help}.ecp-pro-table__toolbar[data-v-f3d27813]{display:flex;align-items:center;gap:8px}.ecp-pro-table__body[data-v-f3d27813]{width:100%}.ecp-pro-table__pagination[data-v-f3d27813]{margin-top:16px;display:flex;justify-content:flex-end}.ecp-pro-table__col-help[data-v-f3d27813]{margin-left:4px;color:#909399;cursor:help}.ecp-table-action[data-v-45a58e7c],.ecp-table-action__item[data-v-45a58e7c]{display:inline-flex;align-items:center;gap:4px}.ecp-table-action__icon[data-v-45a58e7c]{margin-right:4px}.ecp-table-action__more[data-v-45a58e7c]{display:inline-flex;align-items:center}.ecp-table-action__dropdown-item[data-v-45a58e7c]{display:inline-flex;align-items:center;gap:4px}.ecp-tree-select[data-v-f30bba11]{position:relative;width:100%}.ecp-tree-select__filter-inner[data-v-f30bba11]{margin-bottom:8px}.ecp-tree-select__dropdown[data-v-f30bba11]{position:absolute;top:100%;left:0;right:0;max-height:280px;overflow:auto;background:#fff;border:1px solid #dcdfe6;border-radius:4px;margin-top:4px;z-index:1000;padding:8px}.ecp-tree-select__loading[data-v-f30bba11]{padding:24px;text-align:center;color:#909399;font-size:14px}.ecp-pro-form-item__colon[data-v-48d7960b]{margin-right:2px}.ecp-pro-form-item__help-icon[data-v-48d7960b]{margin-left:4px;color:#909399;cursor:help;font-size:14px}.ecp-pro-form-item__help-icon[data-v-48d7960b]:hover{color:#409eff}.ecp-pro-form-item__help-item[data-v-48d7960b]{margin-bottom:4px}.ecp-pro-form-item__help-item[data-v-48d7960b]:last-child{margin-bottom:0}.ecp-form-actions[data-v-489c88d2]{text-align:right}.ecp-form-actions__advance[data-v-489c88d2]{margin-right:8px}.el-icon-d-arrow-left.up[data-v-489c88d2]{transform:rotate(90deg)}.el-icon-d-arrow-left.down[data-v-489c88d2]{transform:rotate(-90deg)}.ecp-pro-form[data-v-7ee469dc]{padding:16px;position:relative}.ecp-pro-form__advance[data-v-7ee469dc]{margin-bottom:16px}.ecp-pro-form_col[data-v-7ee469dc]{position:relative;float:right}.el-icon-d-arrow-left.up[data-v-7ee469dc]{transform:rotate(90deg)}.el-icon-d-arrow-left.down[data-v-7ee469dc]{transform:rotate(-90deg)}.ecp-form-actions__advance[data-v-7ee469dc]{position:absolute;bottom:0;left:50%;transform:translate(-50%,-50%)}.ecp-pro-descriptions[data-v-656036f6]{width:100%;box-sizing:border-box}.ecp-pro-descriptions__header[data-v-656036f6]{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px;gap:12px}.ecp-pro-descriptions__title-wrap[data-v-656036f6]{display:flex;align-items:center;gap:6px}.ecp-pro-descriptions__title[data-v-656036f6]{font-size:16px;font-weight:600;color:#303133}.ecp-pro-descriptions__help[data-v-656036f6],.ecp-pro-descriptions__toggle[data-v-656036f6]{color:#909399}.ecp-pro-descriptions__toggle .el-icon-arrow-down[data-v-656036f6]{margin-left:4px;transition:transform .2s ease}.ecp-pro-descriptions__toggle .el-icon-arrow-down.is-expanded[data-v-656036f6]{transform:rotate(180deg)}.ecp-pro-descriptions__body[data-v-656036f6]{display:grid;border-top:1px solid #ebeef5;border-left:1px solid #ebeef5;overflow:hidden}.ecp-pro-descriptions__body.is-collapsed[data-v-656036f6]{overflow:hidden}.ecp-pro-descriptions__body[data-v-656036f6]:not(.is-bordered){border-top:0;border-left:0;gap:12px 16px}.ecp-pro-descriptions__item[data-v-656036f6]{display:flex;min-width:0;border-right:1px solid #ebeef5;border-bottom:1px solid #ebeef5}.ecp-pro-descriptions__body:not(.is-bordered) .ecp-pro-descriptions__item[data-v-656036f6]{border-right:0;border-bottom:0}.ecp-pro-descriptions__label[data-v-656036f6],.ecp-pro-descriptions__content[data-v-656036f6]{min-width:0;box-sizing:border-box;word-break:break-word}.ecp-pro-descriptions__label[data-v-656036f6]{flex:0 0 120px;padding:12px 16px;color:#606266;background:#fafafa}.ecp-pro-descriptions__content[data-v-656036f6]{flex:1;padding:12px 16px;color:#303133;background:#fff}.ecp-pro-descriptions__body:not(.is-bordered) .ecp-pro-descriptions__label[data-v-656036f6]{flex-basis:auto;padding:0;margin-right:8px;background:transparent;font-weight:500}.ecp-pro-descriptions__body:not(.is-bordered) .ecp-pro-descriptions__content[data-v-656036f6]{padding:0;background:transparent}.ecp-pro-descriptions__body.is-small .ecp-pro-descriptions__label[data-v-656036f6],.ecp-pro-descriptions__body.is-small .ecp-pro-descriptions__content[data-v-656036f6]{padding-top:8px;padding-bottom:8px;font-size:13px}@media (max-width: 767px){.ecp-pro-descriptions__item[data-v-656036f6]{flex-direction:column}.ecp-pro-descriptions__label[data-v-656036f6]{flex-basis:auto}}.ecp-pro-table-form__form[data-v-44aa7592] .el-form-item{margin-bottom:0}.ecp-pro-table-form__cell-item[data-v-44aa7592]{width:100%}.ecp-pro-table-form__cell-item[data-v-44aa7592] .el-form-item__content{margin-left:0!important;line-height:normal}.ecp-pro-table-form__fixed-label[data-v-44aa7592]{color:#303133;font-size:14px}.ecp-pro-table-form__req[data-v-44aa7592]{color:#f56c6c;margin-right:2px}.ecp-pro-table-form__th-text[data-v-44aa7592]{font-weight:500;color:#606266}.ecp-pro-table-form__action-title[data-v-44aa7592]{margin-right:8px;font-size:13px;color:#606266}.ecp-pro-table-form__add-btn[data-v-44aa7592]{padding:0;font-size:14px}.ecp-pro-table-form__del-btn[data-v-44aa7592]{padding:0;color:#909399}.ecp-pro-table-form__del-btn[data-v-44aa7592]:not(:disabled){color:#409eff}.ecp-pro-table-form .ecp-pro-table-form__header-cell{background:#f5f7fa!important}
@@ -1,4 +1,4 @@
1
- import { VNode } from 'vue';
1
+ import { VNode, ComponentPublicInstance } from 'vue';
2
2
 
3
3
  /** 栅格配置 */
4
4
  export interface ColEx {
@@ -28,6 +28,15 @@ export interface ScrollToFieldOptions {
28
28
  /** 行内对齐 */
29
29
  inline?: ScrollLogicalPosition;
30
30
  }
31
+ /** ApiSelect 暴露的实例类型 */
32
+ export interface ApiSelectInstance {
33
+ options: Array<{
34
+ label: string;
35
+ value: unknown;
36
+ }>;
37
+ loading: boolean;
38
+ fetchOptions: () => Promise<void>;
39
+ }
31
40
  /** 表单操作类型(参考 Vben Admin FormActionType) */
32
41
  export interface FormActionType {
33
42
  getFieldsValue: () => Record<string, unknown>;
@@ -42,6 +51,15 @@ export interface FormActionType {
42
51
  appendSchemaByField: (schema: ProFormSchema, prefixField?: string, first?: boolean) => Promise<void>;
43
52
  removeSchemaByField: (field: string | string[]) => Promise<void>;
44
53
  setProps: (props: Partial<ProFormProps>) => Promise<void>;
54
+ /** 获取指定字段的组件实例 */
55
+ getComponentInstance: (field: string) => ComponentPublicInstance | null;
56
+ /** 获取 api-select 字段的 options */
57
+ getFieldOptions: (field: string) => Array<{
58
+ label: string;
59
+ value: unknown;
60
+ }>;
61
+ /** 获取 api-select 字段的加载状态 */
62
+ isFieldLoading: (field: string) => boolean;
45
63
  }
46
64
  /** 时间字段映射 [表单字段, [开始字段, 结束字段], 格式?] */
47
65
  export type FieldMapToTime = [string, [string, string], string?];
@@ -188,6 +206,7 @@ export interface DescriptionSchema {
188
206
  }
189
207
  /** Description Props */
190
208
  export interface DescriptionProps {
209
+ [key: string]: unknown;
191
210
  title?: string;
192
211
  helpMessage?: string | string[];
193
212
  size?: 'medium' | 'small';
@@ -1,12 +1,17 @@
1
1
  export interface UseComponentSettingReturn {
2
2
  /** 获取组件默认配置;不传参时返回全部组件的配置 */
3
- getSetting: (componentName?: string) => Record<string, unknown>;
4
- /** 设置组件默认配置(与已有配置浅合并) */
3
+ getSetting: <T extends Record<string, unknown> = Record<string, unknown>>(componentName?: string) => T;
4
+ /** 设置组件默认配置(与已有配置深度合并) */
5
5
  setSetting: (componentName: string, config: Record<string, unknown>) => void;
6
+ /** 合并组件配置:全局配置与组件 props 合并,返回合并后的结果 */
7
+ mergeSettings: <T extends Record<string, unknown> = Record<string, unknown>>(componentName: string, props: Record<string, unknown>) => T;
6
8
  }
7
9
  /**
8
10
  * 组件默认配置:供所有组件统一获取/设置默认配置
9
- * - getSetting:获取组件默认配置,用于初始化或合并 props
10
- * - setSetting:设置组件默认配置,可在应用入口或按需调用
11
+ * - getSetting:获取组件默认配置
12
+ * - setSetting:设置组件默认配置(深度合并),可在应用入口或按需调用
13
+ * - mergeSettings:合并全局配置与组件 props,props 优先级更高
14
+ *
15
+ * 合并优先级:组件默认值 < 全局配置 < 组件 props
11
16
  */
12
17
  export declare function useComponentSetting(): UseComponentSettingReturn;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aspire-ui/element-component-pro",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "Element UI 二次封装组件库,基于 Vue 2.7 + TypeScript + setup 语法糖,实现 VbenAdmin 风格的 Pro 组件",
5
5
  "type": "module",
6
6
  "main": "./dist/element-component-pro.umd.js",
@@ -181,8 +181,8 @@ const normalizeTooltip = (schema: DescriptionSchema, value: unknown, record: Rec
181
181
  return normalizeTooltipConfig(resolved, value)
182
182
  }
183
183
 
184
- const { getSetting } = useComponentSetting()
185
- const effectiveProps = computed(() => ({ ...getSetting('ProDescriptions'), ...props, ...innerProps.value }))
184
+ const { mergeSettings } = useComponentSetting()
185
+ const effectiveProps = computed(() => mergeSettings<DescriptionProps>('ProDescriptions', { ...props, ...innerProps.value }))
186
186
 
187
187
  const breakpoints = { xxl: 1920, xl: 1200, lg: 992, md: 768, sm: 576 }
188
188
 
@@ -1,7 +1,6 @@
1
1
  <template>
2
- <el-select :value="value" :placeholder="placeholder" :disabled="disabled" :loading="loading" :clearable="clearable"
3
- :filterable="filterable" :multiple="multiple" v-bind="$attrs" @input="$emit('input', $event)"
4
- @visible-change="onVisibleChange">
2
+ <el-select v-bind="{ ...$attrs, value: value, placeholder, disabled, loading, clearable, filterable, multiple }"
3
+ @input="$emit('input', $event)" @change="$emit('change', $event)" @visible-change="onVisibleChange">
5
4
  <el-option v-for="opt in options" :key="String(opt.value)" :label="opt.label" :value="opt.value" />
6
5
  </el-select>
7
6
  </template>
@@ -31,13 +30,19 @@ const props = withDefaults(
31
30
  { labelField: 'label', valueField: 'value', lazy: false }
32
31
  )
33
32
 
34
- defineEmits<{ (e: 'input', value: unknown): void }>()
33
+ defineEmits<{ (e: 'input', value: unknown): void; (e: 'change', value: unknown): void }>()
35
34
 
36
35
  const loading = ref(false)
37
36
  const options = ref<Array<{ label: string; value: unknown }>>([])
37
+ const cachedParams = ref<string>('')
38
38
 
39
39
  const onVisibleChange = (visible: boolean) => {
40
- if (props.lazy && visible) fetchOptions()
40
+ if (props.lazy && visible) {
41
+ const paramsKey = JSON.stringify(props.params ?? {})
42
+ if (paramsKey !== cachedParams.value || options.value.length === 0) {
43
+ fetchOptions()
44
+ }
45
+ }
41
46
  }
42
47
 
43
48
  const fetchOptions = async () => {
@@ -59,11 +64,18 @@ const fetchOptions = async () => {
59
64
  value: o[valueKey] ?? o.value,
60
65
  }
61
66
  })
67
+ cachedParams.value = JSON.stringify(props.params ?? {})
62
68
  } finally {
63
69
  loading.value = false
64
70
  }
65
71
  }
66
72
 
73
+ defineExpose({
74
+ options,
75
+ loading,
76
+ fetchOptions,
77
+ })
78
+
67
79
  onMounted(() => {
68
80
  if (!props.lazy) fetchOptions()
69
81
  })
@@ -10,7 +10,8 @@
10
10
  :offset="schema.colProps?.offset ?? effectiveProps.baseColProps?.offset ?? 0" :data-field="schema.field">
11
11
  <ProFormItem :schema="schema" :form-model="currentFormModel" :form-disabled="effectiveProps.disabled"
12
12
  :auto-placeholder="effectiveProps.autoSetPlaceholder" :form-action-type="formActionRef" :colon="effectiveProps.colon"
13
- :custom-components="formCustomComponents" :on-field-change="handleFieldChange">
13
+ :custom-components="formCustomComponents" :on-field-change="handleFieldChange"
14
+ :register-field-instance="registerFieldInstance">
14
15
  <template v-if="slots[getSlotName(schema)]">
15
16
  <slot :name="getSlotName(schema)" :model="currentFormModel" :schema="schema" :field="schema.field"
16
17
  :values="currentFormModel" />
@@ -57,10 +58,11 @@
57
58
 
58
59
  <script setup lang="ts">
59
60
  import { ref, computed, watch, useSlots, onMounted, onUnmounted } from 'vue'
61
+ import type { ComponentPublicInstance } from 'vue'
60
62
  import { useComponentSetting } from '../useComponentSetting'
61
63
  import ProFormItem from './ProFormItem.vue'
62
64
  import FormActions from './FormActions.vue'
63
- import type { ProFormSchema, ProFormProps, FormActionType, ColEx, ScrollToFieldOptions, FormListeners } from '../types'
65
+ import type { ProFormSchema, ProFormProps, FormActionType, ColEx, ScrollToFieldOptions, FormListeners, ApiSelectInstance } from '../types'
64
66
 
65
67
  const props = withDefaults(
66
68
  defineProps<{
@@ -103,7 +105,7 @@ const props = withDefaults(
103
105
  autoSetPlaceholder: true,
104
106
  showSubmitButton: true,
105
107
  showResetButton: true,
106
- submitButtonText: '提交',
108
+ submitButtonText: '查询',
107
109
  resetButtonText: '重置',
108
110
  submitButtonIcon: 'el-icon-search',
109
111
  resetButtonIcon: 'el-icon-refresh-left',
@@ -132,6 +134,8 @@ const formModel = ref<Record<string, unknown>>({})
132
134
  const formRules = ref<Record<string, unknown[]>>({})
133
135
  const innerSchemas = ref<ProFormSchema[]>([])
134
136
  const innerProps = ref<Partial<ProFormProps>>({})
137
+ /** 字段名 -> 组件实例映射(用于获取 api-select 等组件的内部状态) */
138
+ const fieldInstanceMap = ref<Map<string, ComponentPublicInstance>>(new Map())
135
139
 
136
140
  /** Element UI 栅格断点 (px) */
137
141
  const BREAKPOINTS = { xl: 1920, lg: 1200, md: 992, sm: 768 }
@@ -404,6 +408,39 @@ const setProps = async (formProps: Partial<ProFormProps>) => {
404
408
  }
405
409
  }
406
410
 
411
+ /** 注册组件实例(供 ProFormItem 调用) */
412
+ const registerFieldInstance = (field: string, instance: Record<string, unknown> | null) => {
413
+ if (instance) {
414
+ fieldInstanceMap.value.set(field, instance as unknown as ComponentPublicInstance)
415
+ } else {
416
+ fieldInstanceMap.value.delete(field)
417
+ }
418
+ }
419
+
420
+ /** 获取指定字段的组件实例 */
421
+ const getComponentInstance = (field: string): ComponentPublicInstance | null => {
422
+ return fieldInstanceMap.value.get(field) ?? null
423
+ }
424
+
425
+ /** 获取 api-select 字段的 options */
426
+ const getFieldOptions = (field: string): Array<{ label: string; value: unknown }> => {
427
+ const instance = fieldInstanceMap.value.get(field)
428
+ if (!instance) return []
429
+ const apiSelectInstance = instance as unknown as ApiSelectInstance
430
+ if (apiSelectInstance?.options) {
431
+ return apiSelectInstance.options
432
+ }
433
+ return []
434
+ }
435
+
436
+ /** 获取 api-select 字段的加载状态 */
437
+ const isFieldLoading = (field: string): boolean => {
438
+ const instance = fieldInstanceMap.value.get(field)
439
+ if (!instance) return false
440
+ const apiSelectInstance = instance as unknown as ApiSelectInstance
441
+ return apiSelectInstance?.loading ?? false
442
+ }
443
+
407
444
  const formActionRef: FormActionType = {
408
445
  getFieldsValue,
409
446
  setFieldsValue,
@@ -417,9 +454,15 @@ const formActionRef: FormActionType = {
417
454
  appendSchemaByField,
418
455
  removeSchemaByField,
419
456
  setProps,
457
+ getComponentInstance,
458
+ getFieldOptions,
459
+ isFieldLoading,
420
460
  }
421
461
 
422
- defineExpose(formActionRef)
462
+ defineExpose({
463
+ ...formActionRef,
464
+ registerFieldInstance,
465
+ })
423
466
 
424
467
  const syncSchemas = () => {
425
468
  innerSchemas.value = [...(props.schemas ?? [])]
@@ -91,6 +91,7 @@
91
91
  </el-select>
92
92
  <ApiSelect
93
93
  v-else-if="schema.component === 'api-select'"
94
+ ref="apiSelectRef"
94
95
  :value="formModel[schema.field]"
95
96
  :placeholder="schema.placeholder || (autoPlaceholder ? `请选择${schema.label}` : undefined)"
96
97
  :disabled="effectiveDisabled"
@@ -178,7 +179,7 @@
178
179
  </template>
179
180
 
180
181
  <script setup lang="ts">
181
- import { computed, useSlots, h } from 'vue'
182
+ import { computed, useSlots, h, ref, onMounted, onUnmounted } from 'vue'
182
183
  import ApiSelect from './ApiSelect.vue'
183
184
  import FormattedNumberInput from './FormattedNumberInput.vue'
184
185
  import TreeSelect from './TreeSelect.vue'
@@ -201,8 +202,25 @@ const props = defineProps<{
201
202
  onFieldChange?: (field: string, value: unknown) => void
202
203
  /** 自定义组件映射(由 ProForm 传入) */
203
204
  customComponents?: Record<string, unknown>
205
+ /** 注册字段实例(由 ProForm 传入) */
206
+ registerFieldInstance?: (field: string, instance: Record<string, unknown> | null) => void
204
207
  }>()
205
208
 
209
+ /** ApiSelect 组件实例 ref */
210
+ const apiSelectRef = ref<Record<string, unknown> | null>(null)
211
+
212
+ onMounted(() => {
213
+ if (props.schema.component === 'api-select') {
214
+ props.registerFieldInstance?.(props.schema.field, apiSelectRef.value)
215
+ }
216
+ })
217
+
218
+ onUnmounted(() => {
219
+ if (props.schema.component === 'api-select') {
220
+ props.registerFieldInstance?.(props.schema.field, null)
221
+ }
222
+ })
223
+
206
224
  const slots = useSlots()
207
225
 
208
226
  const renderParams = computed<RenderCallbackParams>(() => ({
@@ -208,6 +208,8 @@
208
208
  :page-sizes="pagination.pageSizes"
209
209
  :page-size="pagination.pageSize"
210
210
  :total="pagination.total"
211
+ :small="paginationSmall"
212
+ :background="paginationBackground"
211
213
  :layout="(effectiveProps.pagination && typeof effectiveProps.pagination === 'object' ? effectiveProps.pagination.layout : null) || 'total, sizes, prev, pager, next, jumper'"
212
214
  v-bind="(effectiveProps.pagination && typeof effectiveProps.pagination === 'object' && effectiveProps.pagination.props) || {}"
213
215
  @size-change="handleSizeChange"
@@ -301,7 +303,7 @@ const props = withDefaults(
301
303
  actionColumn?: Partial<ProColumn>
302
304
  rowSelection?: { type?: 'checkbox' | 'radio'; width?: number; fixed?: 'left' | 'right'; getCheckboxProps?: (r: Record<string, unknown>) => { disabled?: boolean }; getRadioProps?: (r: Record<string, unknown>) => { disabled?: boolean } }
303
305
  clearSelectOnPageChange?: boolean
304
- pagination?: false | { pageSize?: number; pageSizes?: number[]; layout?: string; props?: Record<string, unknown> } | Record<string, unknown>
306
+ pagination?: false | { pageSize?: number; pageSizes?: number[]; layout?: string; small?: boolean; background?: boolean; props?: Record<string, unknown> } | Record<string, unknown>
305
307
  tableSetting?: { redo?: boolean; size?: boolean; setting?: boolean; fullScreen?: boolean }
306
308
  fetchSetting?: FetchSetting
307
309
  beforeFetch?: (params: Record<string, unknown>) => Record<string, unknown>
@@ -362,21 +364,50 @@ const innerProps = ref<Partial<ProTableProps>>({})
362
364
  const selectedRows = ref<Record<string, unknown>[]>([])
363
365
  const showPaginationRef = ref<boolean | null>(null)
364
366
 
367
+ const { mergeSettings } = useComponentSetting()
368
+ const effectiveProps = computed(() => mergeSettings<ProTableProps>('ProTable', { ...props, ...innerProps.value }))
369
+
370
+ /** 从 effectiveProps.pagination 读取分页默认值(已通过深度合并) */
371
+ const defaultPagination = computed(() => {
372
+ const paginationConfig = effectiveProps.value.pagination
373
+ if (paginationConfig && typeof paginationConfig === 'object') {
374
+ return {
375
+ pageSize: (paginationConfig as Record<string, unknown>).pageSize ?? 10,
376
+ pageSizes: ((paginationConfig as Record<string, unknown>).pageSizes as number[]) ?? [10, 20, 50, 100],
377
+ }
378
+ }
379
+ return { pageSize: 10, pageSizes: [10, 20, 50, 100] as number[] }
380
+ })
381
+
365
382
  const pagination = ref({
366
383
  page: 1,
367
- pageSize: (props.pagination && typeof props.pagination === 'object') ? (props.pagination.pageSize ?? 10) : 10,
368
- pageSizes: (props.pagination && typeof props.pagination === 'object') ? (props.pagination.pageSizes ?? [10, 20, 50, 100]) : [10, 20, 50, 100],
384
+ pageSize: defaultPagination.value.pageSize,
385
+ pageSizes: defaultPagination.value.pageSizes,
369
386
  total: 0,
370
387
  })
371
388
 
372
- const { getSetting: getComponentSetting } = useComponentSetting()
373
- const effectiveProps = computed(() => ({ ...getComponentSetting('ProTable'), ...props, ...innerProps.value }))
374
389
  const showTitleBar = computed(() => !!effectiveProps.value.title || !!slots.tableTitle || !!slots.toolbar)
375
390
  const showPagination = computed(() => {
376
391
  if (showPaginationRef.value !== null) return showPaginationRef.value
377
392
  return !!props.pagination && typeof props.pagination === 'object'
378
393
  })
379
394
 
395
+ /** 分页 small 属性:从 pagination 配置或全局设置中读取 */
396
+ const paginationSmall = computed(() => {
397
+ if (effectiveProps.value.pagination && typeof effectiveProps.value.pagination === 'object') {
398
+ return !!(effectiveProps.value.pagination as Record<string, unknown>).small
399
+ }
400
+ return false
401
+ })
402
+
403
+ /** 分页 background 属性:从 pagination 配置或全局设置中读取 */
404
+ const paginationBackground = computed(() => {
405
+ if (effectiveProps.value.pagination && typeof effectiveProps.value.pagination === 'object') {
406
+ return !!(effectiveProps.value.pagination as Record<string, unknown>).background
407
+ }
408
+ return false
409
+ })
410
+
380
411
  const rowKeyField = computed(() => effectiveProps.value.rowKey || 'id')
381
412
 
382
413
  /** 选中行 key 集合(用于快速判断) */
@@ -82,6 +82,7 @@ export interface TreeProps {
82
82
 
83
83
  /** ProTable Props */
84
84
  export interface ProTableProps {
85
+ [key: string]: unknown
85
86
  columns?: ProColumn[]
86
87
  dataSource?: Record<string, unknown>[]
87
88
  api?: (params: Record<string, unknown>) => Promise<unknown>