@aspire-ui/element-component-pro 1.0.14 → 1.0.16

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,9 @@ 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: import('./types').GetFieldOptions;
1718
+ isFieldLoading: (field: string) => boolean;
1715
1719
  }> & import('vue/types/v3-component-options').ExtractComputedReturns<{}> & import('vue').ComponentCustomProperties & Readonly<import('vue').ExtractPropTypes<{
1716
1720
  disabled: {
1717
1721
  type: import('vue').PropType<boolean>;
@@ -1945,6 +1949,7 @@ declare const _default: {
1945
1949
  type: import('vue').PropType<import('./types').FormListeners>;
1946
1950
  };
1947
1951
  }>>, {
1952
+ registerFieldInstance: (field: string, instance: Record<string, unknown> | null) => void;
1948
1953
  getFieldsValue: () => Record<string, unknown>;
1949
1954
  setFieldsValue: (values: Record<string, unknown>) => Promise<void>;
1950
1955
  resetFields: () => Promise<void>;
@@ -1957,6 +1962,9 @@ declare const _default: {
1957
1962
  appendSchemaByField: (schema: import('./types').ProFormSchema, prefixField?: string, first?: boolean) => Promise<void>;
1958
1963
  removeSchemaByField: (field: string | string[]) => Promise<void>;
1959
1964
  setProps: (props: Partial<import('./types').ProFormProps>) => Promise<void>;
1965
+ getComponentInstance: (field: string) => import('vue').ComponentPublicInstance | null;
1966
+ getFieldOptions: import('./types').GetFieldOptions;
1967
+ isFieldLoading: (field: string) => boolean;
1960
1968
  }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
1961
1969
  submit: (values: Record<string, unknown>) => void;
1962
1970
  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-80bac8be]{padding:16px;position:relative}.ecp-pro-form__advance[data-v-80bac8be]{margin-bottom:16px}.ecp-pro-form_col[data-v-80bac8be]{position:relative;float:right}.el-icon-d-arrow-left.up[data-v-80bac8be]{transform:rotate(90deg)}.el-icon-d-arrow-left.down[data-v-80bac8be]{transform:rotate(-90deg)}.ecp-form-actions__advance[data-v-80bac8be]{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,22 @@ export interface ScrollToFieldOptions {
28
28
  /** 行内对齐 */
29
29
  inline?: ScrollLogicalPosition;
30
30
  }
31
+ export interface ProFormFieldOption {
32
+ label: string;
33
+ value: unknown;
34
+ }
35
+ export interface GetFieldOptions {
36
+ (field: string): ProFormFieldOption[];
37
+ (field: string, raw: false): ProFormFieldOption[];
38
+ (field: string, raw: true): unknown[];
39
+ }
40
+ /** ApiSelect 暴露的实例类型 */
41
+ export interface ApiSelectInstance {
42
+ options: ProFormFieldOption[];
43
+ rawOptions: unknown[];
44
+ loading: boolean;
45
+ fetchOptions: () => Promise<void>;
46
+ }
31
47
  /** 表单操作类型(参考 Vben Admin FormActionType) */
32
48
  export interface FormActionType {
33
49
  getFieldsValue: () => Record<string, unknown>;
@@ -42,6 +58,12 @@ export interface FormActionType {
42
58
  appendSchemaByField: (schema: ProFormSchema, prefixField?: string, first?: boolean) => Promise<void>;
43
59
  removeSchemaByField: (field: string | string[]) => Promise<void>;
44
60
  setProps: (props: Partial<ProFormProps>) => Promise<void>;
61
+ /** 获取指定字段的组件实例 */
62
+ getComponentInstance: (field: string) => ComponentPublicInstance | null;
63
+ /** 获取 api-select 字段的 options,raw=true 时返回接口原始列表数据 */
64
+ getFieldOptions: GetFieldOptions;
65
+ /** 获取 api-select 字段的加载状态 */
66
+ isFieldLoading: (field: string) => boolean;
45
67
  }
46
68
  /** 时间字段映射 [表单字段, [开始字段, 结束字段], 格式?] */
47
69
  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.16",
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,20 @@ 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 rawOptions = ref<unknown[]>([])
38
+ const cachedParams = ref<string>('')
38
39
 
39
40
  const onVisibleChange = (visible: boolean) => {
40
- if (props.lazy && visible) fetchOptions()
41
+ if (props.lazy && visible) {
42
+ const paramsKey = JSON.stringify(props.params ?? {})
43
+ if (paramsKey !== cachedParams.value || options.value.length === 0) {
44
+ fetchOptions()
45
+ }
46
+ }
41
47
  }
42
48
 
43
49
  const fetchOptions = async () => {
@@ -50,6 +56,7 @@ const fetchOptions = async () => {
50
56
  : ((res as Record<string, unknown>)?.list as unknown[]) ??
51
57
  ((res as Record<string, unknown>)?.data as unknown[]) ??
52
58
  []
59
+ rawOptions.value = raw
53
60
  const labelKey = props.labelField ?? 'label'
54
61
  const valueKey = props.valueField ?? 'value'
55
62
  options.value = raw.map((item: unknown) => {
@@ -59,11 +66,19 @@ const fetchOptions = async () => {
59
66
  value: o[valueKey] ?? o.value,
60
67
  }
61
68
  })
69
+ cachedParams.value = JSON.stringify(props.params ?? {})
62
70
  } finally {
63
71
  loading.value = false
64
72
  }
65
73
  }
66
74
 
75
+ defineExpose({
76
+ options,
77
+ rawOptions,
78
+ loading,
79
+ fetchOptions,
80
+ })
81
+
67
82
  onMounted(() => {
68
83
  if (!props.lazy) fetchOptions()
69
84
  })
@@ -72,12 +87,14 @@ watch(() => props.api, () => {
72
87
  fetchOptions()
73
88
  } else {
74
89
  options.value = []
90
+ rawOptions.value = []
75
91
  }
76
92
  }, { deep: true }
77
93
  )
78
94
  watch(() => props.params, () => {
79
95
  if (props.lazy) {
80
96
  options.value = []
97
+ rawOptions.value = []
81
98
  } else {
82
99
  fetchOptions()
83
100
  }
@@ -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,42 @@ 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,raw=true 时返回接口原始数据 */
426
+ function getFieldOptions(field: string): Array<{ label: string; value: unknown }>
427
+ function getFieldOptions(field: string, raw: false): Array<{ label: string; value: unknown }>
428
+ function getFieldOptions(field: string, raw: true): unknown[]
429
+ function getFieldOptions(field: string, raw = false): Array<{ label: string; value: unknown }> | unknown[] {
430
+ const instance = fieldInstanceMap.value.get(field)
431
+ if (!instance) return []
432
+ const apiSelectInstance = instance as unknown as ApiSelectInstance
433
+ if (raw) {
434
+ return apiSelectInstance?.rawOptions ?? []
435
+ }
436
+ return apiSelectInstance?.options ?? []
437
+ }
438
+
439
+ /** 获取 api-select 字段的加载状态 */
440
+ const isFieldLoading = (field: string): boolean => {
441
+ const instance = fieldInstanceMap.value.get(field)
442
+ if (!instance) return false
443
+ const apiSelectInstance = instance as unknown as ApiSelectInstance
444
+ return apiSelectInstance?.loading ?? false
445
+ }
446
+
407
447
  const formActionRef: FormActionType = {
408
448
  getFieldsValue,
409
449
  setFieldsValue,
@@ -417,9 +457,15 @@ const formActionRef: FormActionType = {
417
457
  appendSchemaByField,
418
458
  removeSchemaByField,
419
459
  setProps,
460
+ getComponentInstance,
461
+ getFieldOptions,
462
+ isFieldLoading,
420
463
  }
421
464
 
422
- defineExpose(formActionRef)
465
+ defineExpose({
466
+ ...formActionRef,
467
+ registerFieldInstance,
468
+ })
423
469
 
424
470
  const syncSchemas = () => {
425
471
  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>(() => ({
@@ -24,6 +24,9 @@ export interface UseFormReturn {
24
24
  appendSchemaByField: (schema: ProFormSchema, prefixField?: string, first?: boolean) => Promise<void>
25
25
  removeSchemaByField: (field: string | string[]) => Promise<void>
26
26
  setProps: (props: Partial<ProFormProps>) => Promise<void>
27
+ getComponentInstance: FormActionType['getComponentInstance']
28
+ getFieldOptions: FormActionType['getFieldOptions']
29
+ isFieldLoading: FormActionType['isFieldLoading']
27
30
  }
28
31
 
29
32
  export function useForm(props?: UseFormPropsReactive): [UseFormReturn['register'], Omit<UseFormReturn, 'register'>] {
@@ -92,6 +95,21 @@ export function useForm(props?: UseFormPropsReactive): [UseFormReturn['register'
92
95
  await formAction.value?.setProps(formProps)
93
96
  }
94
97
 
98
+ const getComponentInstance: FormActionType['getComponentInstance'] = (field) =>
99
+ formAction.value?.getComponentInstance(field) ?? null
100
+
101
+ function getFieldOptions(field: string): Array<{ label: string; value: unknown }>
102
+ function getFieldOptions(field: string, raw: false): Array<{ label: string; value: unknown }>
103
+ function getFieldOptions(field: string, raw: true): unknown[]
104
+ function getFieldOptions(field: string, raw = false): Array<{ label: string; value: unknown }> | unknown[] {
105
+ if (!formAction.value) return []
106
+ if (raw) return formAction.value.getFieldOptions(field, true)
107
+ return formAction.value.getFieldOptions(field)
108
+ }
109
+
110
+ const isFieldLoading: FormActionType['isFieldLoading'] = (field) =>
111
+ formAction.value?.isFieldLoading(field) ?? false
112
+
95
113
  const result: UseFormReturn = {
96
114
  register,
97
115
  formAction,
@@ -107,6 +125,9 @@ export function useForm(props?: UseFormPropsReactive): [UseFormReturn['register'
107
125
  appendSchemaByField,
108
126
  removeSchemaByField,
109
127
  setProps,
128
+ getComponentInstance,
129
+ getFieldOptions,
130
+ isFieldLoading,
110
131
  }
111
132
 
112
133
  return [register, result]
@@ -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,25 @@ export interface ScrollToFieldOptions {
31
32
  inline?: ScrollLogicalPosition
32
33
  }
33
34
 
35
+ export interface ProFormFieldOption {
36
+ label: string
37
+ value: unknown
38
+ }
39
+
40
+ export interface GetFieldOptions {
41
+ (field: string): ProFormFieldOption[]
42
+ (field: string, raw: false): ProFormFieldOption[]
43
+ (field: string, raw: true): unknown[]
44
+ }
45
+
46
+ /** ApiSelect 暴露的实例类型 */
47
+ export interface ApiSelectInstance {
48
+ options: ProFormFieldOption[]
49
+ rawOptions: unknown[]
50
+ loading: boolean
51
+ fetchOptions: () => Promise<void>
52
+ }
53
+
34
54
  /** 表单操作类型(参考 Vben Admin FormActionType) */
35
55
  export interface FormActionType {
36
56
  getFieldsValue: () => Record<string, unknown>
@@ -45,6 +65,12 @@ export interface FormActionType {
45
65
  appendSchemaByField: (schema: ProFormSchema, prefixField?: string, first?: boolean) => Promise<void>
46
66
  removeSchemaByField: (field: string | string[]) => Promise<void>
47
67
  setProps: (props: Partial<ProFormProps>) => Promise<void>
68
+ /** 获取指定字段的组件实例 */
69
+ getComponentInstance: (field: string) => ComponentPublicInstance | null
70
+ /** 获取 api-select 字段的 options,raw=true 时返回接口原始列表数据 */
71
+ getFieldOptions: GetFieldOptions
72
+ /** 获取 api-select 字段的加载状态 */
73
+ isFieldLoading: (field: string) => boolean
48
74
  }
49
75
 
50
76
  /** 时间字段映射 [表单字段, [开始字段, 结束字段], 格式?] */