@aspire-ui/element-component-pro 1.0.14 → 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/index.d.ts CHANGED
@@ -1700,6 +1700,7 @@ declare const _default: {
1700
1700
  type: import('vue').PropType<import('./types').FormListeners>;
1701
1701
  };
1702
1702
  }>>> & import('vue').ShallowUnwrapRef<{
1703
+ registerFieldInstance: (field: string, instance: Record<string, unknown> | null) => void;
1703
1704
  getFieldsValue: () => Record<string, unknown>;
1704
1705
  setFieldsValue: (values: Record<string, unknown>) => Promise<void>;
1705
1706
  resetFields: () => Promise<void>;
@@ -1712,6 +1713,12 @@ declare const _default: {
1712
1713
  appendSchemaByField: (schema: import('./types').ProFormSchema, prefixField?: string, first?: boolean) => Promise<void>;
1713
1714
  removeSchemaByField: (field: string | string[]) => Promise<void>;
1714
1715
  setProps: (props: Partial<import('./types').ProFormProps>) => Promise<void>;
1716
+ getComponentInstance: (field: string) => import('vue').ComponentPublicInstance | null;
1717
+ getFieldOptions: (field: string) => Array<{
1718
+ label: string;
1719
+ value: unknown;
1720
+ }>;
1721
+ isFieldLoading: (field: string) => boolean;
1715
1722
  }> & import('vue/types/v3-component-options').ExtractComputedReturns<{}> & import('vue').ComponentCustomProperties & Readonly<import('vue').ExtractPropTypes<{
1716
1723
  disabled: {
1717
1724
  type: import('vue').PropType<boolean>;
@@ -1945,6 +1952,7 @@ declare const _default: {
1945
1952
  type: import('vue').PropType<import('./types').FormListeners>;
1946
1953
  };
1947
1954
  }>>, {
1955
+ registerFieldInstance: (field: string, instance: Record<string, unknown> | null) => void;
1948
1956
  getFieldsValue: () => Record<string, unknown>;
1949
1957
  setFieldsValue: (values: Record<string, unknown>) => Promise<void>;
1950
1958
  resetFields: () => Promise<void>;
@@ -1957,6 +1965,12 @@ declare const _default: {
1957
1965
  appendSchemaByField: (schema: import('./types').ProFormSchema, prefixField?: string, first?: boolean) => Promise<void>;
1958
1966
  removeSchemaByField: (field: string | string[]) => Promise<void>;
1959
1967
  setProps: (props: Partial<import('./types').ProFormProps>) => Promise<void>;
1968
+ getComponentInstance: (field: string) => import('vue').ComponentPublicInstance | null;
1969
+ getFieldOptions: (field: string) => Array<{
1970
+ label: string;
1971
+ value: unknown;
1972
+ }>;
1973
+ isFieldLoading: (field: string) => boolean;
1960
1974
  }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
1961
1975
  submit: (values: Record<string, unknown>) => void;
1962
1976
  reset: () => void;
package/dist/style.css CHANGED
@@ -1 +1 @@
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-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-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
+ .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?];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aspire-ui/element-component-pro",
3
- "version": "1.0.14",
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",
@@ -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>(() => ({
@@ -1,4 +1,5 @@
1
1
  import type { VNode } from 'vue'
2
+ import type { ComponentPublicInstance } from 'vue'
2
3
 
3
4
  /** 栅格配置 */
4
5
  export interface ColEx {
@@ -31,6 +32,13 @@ export interface ScrollToFieldOptions {
31
32
  inline?: ScrollLogicalPosition
32
33
  }
33
34
 
35
+ /** ApiSelect 暴露的实例类型 */
36
+ export interface ApiSelectInstance {
37
+ options: Array<{ label: string; value: unknown }>
38
+ loading: boolean
39
+ fetchOptions: () => Promise<void>
40
+ }
41
+
34
42
  /** 表单操作类型(参考 Vben Admin FormActionType) */
35
43
  export interface FormActionType {
36
44
  getFieldsValue: () => Record<string, unknown>
@@ -45,6 +53,12 @@ export interface FormActionType {
45
53
  appendSchemaByField: (schema: ProFormSchema, prefixField?: string, first?: boolean) => Promise<void>
46
54
  removeSchemaByField: (field: string | string[]) => Promise<void>
47
55
  setProps: (props: Partial<ProFormProps>) => Promise<void>
56
+ /** 获取指定字段的组件实例 */
57
+ getComponentInstance: (field: string) => ComponentPublicInstance | null
58
+ /** 获取 api-select 字段的 options */
59
+ getFieldOptions: (field: string) => Array<{ label: string; value: unknown }>
60
+ /** 获取 api-select 字段的加载状态 */
61
+ isFieldLoading: (field: string) => boolean
48
62
  }
49
63
 
50
64
  /** 时间字段映射 [表单字段, [开始字段, 结束字段], 格式?] */