@robot-admin/naive-ui-components 0.22.0 → 0.23.0
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/CHANGELOG.md +18 -0
- package/README.md +9 -5
- package/README_EN.md +9 -5
- package/dist/C_ActionBar.d.cts +2 -1
- package/dist/C_ActionBar.d.ts +2 -1
- package/dist/C_AntV2.cjs +3 -3
- package/dist/C_AntV2.js +3 -3
- package/dist/C_BatchResult.cjs +1 -0
- package/dist/C_BatchResult.css +32 -0
- package/dist/C_BatchResult.d.cts +2 -0
- package/dist/C_BatchResult.d.ts +2 -0
- package/dist/C_BatchResult.js +1 -0
- package/dist/C_BatchResult2.cjs +1 -0
- package/dist/C_BatchResult2.js +1 -0
- package/dist/C_Breadcrumb.d.cts +2 -2
- package/dist/C_Breadcrumb.d.ts +1 -1
- package/dist/C_Captcha.d.ts +2 -0
- package/dist/C_Cascade.d.cts +1 -1
- package/dist/C_Cascade.d.ts +1 -1
- package/dist/C_Chat.d.cts +1 -1
- package/dist/C_Chat.d.ts +1 -1
- package/dist/C_City.d.cts +1 -1
- package/dist/C_City.d.ts +1 -1
- package/dist/C_Code.d.cts +1 -1
- package/dist/C_Code.d.ts +1 -1
- package/dist/C_ContextMenu.d.cts +1 -1
- package/dist/C_ContextMenu.d.ts +1 -1
- package/dist/C_DataPicker.cjs +1 -0
- package/dist/C_DataPicker.css +54 -0
- package/dist/C_DataPicker.d.cts +2 -0
- package/dist/C_DataPicker.d.ts +5 -0
- package/dist/C_DataPicker.js +1 -0
- package/dist/C_DataPicker2.cjs +1 -0
- package/dist/C_DataPicker2.js +1 -0
- package/dist/C_Date.d.cts +1 -1
- package/dist/C_Date.d.ts +1 -1
- package/dist/C_Density.cjs +1 -0
- package/dist/C_Density.css +1 -0
- package/dist/C_Density.d.cts +3 -0
- package/dist/C_Density.d.ts +3 -0
- package/dist/C_Density.js +1 -0
- package/dist/C_Density2.cjs +1 -0
- package/dist/C_Density2.js +1 -0
- package/dist/C_Editor.d.cts +1 -1
- package/dist/C_Editor.d.ts +1 -1
- package/dist/C_ErrorSummary.cjs +1 -0
- package/dist/C_ErrorSummary.css +29 -0
- package/dist/C_ErrorSummary.d.cts +3 -0
- package/dist/C_ErrorSummary.d.ts +3 -0
- package/dist/C_ErrorSummary.js +1 -0
- package/dist/C_ErrorSummary2.cjs +1 -0
- package/dist/C_ErrorSummary2.js +1 -0
- package/dist/C_Form.base.css +150 -148
- package/dist/C_Form.cjs +1 -1
- package/dist/C_Form.css +151 -253
- package/dist/C_Form.d.cts +3 -1
- package/dist/C_Form.d.ts +19 -1
- package/dist/C_Form.js +1 -1
- package/dist/C_FormModal.css +11 -12
- package/dist/C_FormModal.d.cts +1 -1
- package/dist/C_FormModal.d.ts +18 -2
- package/dist/C_FormModal2.cjs +1 -1
- package/dist/C_FormModal2.js +1 -1
- package/dist/C_FormSearch.cjs +1 -1
- package/dist/C_FormSearch.css +128 -56
- package/dist/C_FormSearch.d.cts +3 -3
- package/dist/C_FormSearch.d.ts +5 -3
- package/dist/C_FormSearch.js +1 -1
- package/dist/C_FormulaEditor.d.cts +1 -1
- package/dist/C_FormulaEditor.d.ts +1 -1
- package/dist/C_Guide.d.cts +1 -1
- package/dist/C_Guide.d.ts +1 -1
- package/dist/C_Icon.d.cts +1 -1
- package/dist/C_Icon.d.ts +1 -1
- package/dist/C_Language.d.cts +1 -1
- package/dist/C_Language.d.ts +1 -1
- package/dist/C_Loading.d.cts +1 -1
- package/dist/C_Loading.d.ts +1 -1
- package/dist/C_Login.d.cts +1 -1
- package/dist/C_Login.d.ts +4 -1
- package/dist/C_Markdown.d.cts +1 -1
- package/dist/C_Markdown.d.ts +1 -1
- package/dist/C_Menu.d.cts +2 -2
- package/dist/C_Menu.d.ts +1 -1
- package/dist/C_OrgChart.d.cts +1 -1
- package/dist/C_OrgChart.d.ts +1 -1
- package/dist/C_Progress.d.cts +1 -1
- package/dist/C_Progress.d.ts +1 -1
- package/dist/C_SectionNav.cjs +1 -0
- package/dist/C_SectionNav.css +46 -0
- package/dist/C_SectionNav.d.cts +2 -0
- package/dist/C_SectionNav.d.ts +2 -0
- package/dist/C_SectionNav.js +1 -0
- package/dist/C_SectionNav2.cjs +1 -0
- package/dist/C_SectionNav2.js +1 -0
- package/dist/C_Skeleton.d.cts +1 -1
- package/dist/C_Skeleton.d.ts +1 -1
- package/dist/C_Steps.d.cts +1 -1
- package/dist/C_Steps.d.ts +1 -1
- package/dist/C_Table.base.css +258 -256
- package/dist/C_Table.cjs +1 -1
- package/dist/C_Table.css +109 -1283
- package/dist/C_Table.d.cts +2 -1
- package/dist/C_Table.d.ts +11 -7
- package/dist/C_Table.js +1 -1
- package/dist/C_Table2.cjs +3 -0
- package/dist/C_Table2.js +3 -0
- package/dist/C_Tabs.cjs +1 -1
- package/dist/C_Tabs.d.cts +1 -1
- package/dist/C_Tabs.d.ts +1 -1
- package/dist/C_Tabs.js +1 -1
- package/dist/C_TagsView.d.cts +1 -1
- package/dist/C_Theme.d.cts +1 -1
- package/dist/C_Theme.d.ts +1 -1
- package/dist/C_Timeline.d.cts +1 -1
- package/dist/C_Timeline.d.ts +1 -1
- package/dist/C_Transfer.d.cts +1 -1
- package/dist/C_Transfer.d.ts +1 -1
- package/dist/C_Tree.d.cts +1 -1
- package/dist/C_Tree.d.ts +10 -6
- package/dist/C_Upload.d.cts +1 -1
- package/dist/C_Upload.d.ts +1 -1
- package/dist/C_WorkFlow2.cjs +1 -1
- package/dist/C_WorkFlow2.js +1 -1
- package/dist/Card.cjs +1 -1
- package/dist/Card.js +1 -1
- package/dist/Steps.cjs +1 -1
- package/dist/Steps.js +1 -1
- package/dist/Tabs.cjs +1 -1
- package/dist/Tabs.js +1 -1
- package/dist/batchPatch.cjs +1 -0
- package/dist/batchPatch.d.cts +26 -0
- package/dist/batchPatch.d.ts +26 -0
- package/dist/batchPatch.js +1 -0
- package/dist/config.cjs +1 -1
- package/dist/config.d.cts +7 -1
- package/dist/config.d.ts +7 -1
- package/dist/config.js +1 -1
- package/dist/config2.d.ts +14 -0
- package/dist/constants.d.cts +3 -3
- package/dist/constants.d.ts +3 -3
- package/dist/constants2.d.cts +402 -173
- package/dist/constants2.d.ts +402 -173
- package/dist/constants3.d.cts +3 -3
- package/dist/constants3.d.ts +3 -3
- package/dist/constants4.d.cts +173 -402
- package/dist/constants4.d.ts +173 -402
- package/dist/constants5.d.cts +2 -2
- package/dist/constants5.d.ts +2 -2
- package/dist/data.cjs +1 -1
- package/dist/data.d.cts +5 -108
- package/dist/data.d.ts +5 -108
- package/dist/data.js +1 -1
- package/dist/data2.cjs +1 -1
- package/dist/data2.d.cts +108 -0
- package/dist/data2.d.ts +108 -0
- package/dist/data2.js +1 -1
- package/dist/data3.cjs +1 -0
- package/dist/data3.js +1 -0
- package/dist/dateRange.d.cts +108 -13
- package/dist/dateRange.d.ts +108 -13
- package/dist/density.cjs +1 -1
- package/dist/density.js +1 -1
- package/dist/dictionary.cjs +1 -0
- package/dist/dictionary.d.cts +38 -0
- package/dist/dictionary.d.ts +38 -0
- package/dist/dictionary.js +1 -0
- package/dist/helpers.cjs +1 -1
- package/dist/helpers.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +65 -53
- package/dist/index.d.ts +73 -57
- package/dist/index.js +1 -1
- package/dist/index.vue.d.cts +1 -1
- package/dist/index.vue.d.ts +1 -1
- package/dist/index10.vue.d.cts +37 -80
- package/dist/index10.vue.d.ts +77 -53
- package/dist/index11.vue.d.cts +77 -53
- package/dist/index11.vue.d.ts +1117 -45
- package/dist/index12.vue.d.cts +1117 -45
- package/dist/index12.vue.d.ts +17 -128
- package/dist/index13.vue.d.cts +17 -128
- package/dist/index13.vue.d.ts +57 -53
- package/dist/index14.vue.d.cts +57 -53
- package/dist/index14.vue.d.ts +16 -25
- package/dist/index15.vue.d.cts +16 -25
- package/dist/index15.vue.d.ts +52 -12
- package/dist/index16.vue.d.cts +52 -12
- package/dist/index16.vue.d.ts +13 -129
- package/dist/index17.vue.d.cts +13 -129
- package/dist/index17.vue.d.ts +123 -74
- package/dist/index18.vue.d.cts +123 -74
- package/dist/index18.vue.d.ts +48 -71
- package/dist/index19.vue.d.cts +40 -91
- package/dist/index19.vue.d.ts +36 -96
- package/dist/index2.vue.d.cts +2 -2
- package/dist/index2.vue.d.ts +2 -2
- package/dist/index20.vue.d.cts +38 -47
- package/dist/index20.vue.d.ts +13 -48
- package/dist/index21.vue.d.cts +12 -96
- package/dist/index21.vue.d.ts +25 -95
- package/dist/index22.vue.d.cts +20 -32
- package/dist/index22.vue.d.ts +75 -37
- package/dist/index23.vue.d.cts +77 -13
- package/dist/index23.vue.d.ts +71 -13
- package/dist/index24.vue.d.cts +2 -2
- package/dist/index24.vue.d.ts +1 -1
- package/dist/index25.vue.d.cts +116 -90
- package/dist/index25.vue.d.ts +116 -90
- package/dist/index26.vue.d.cts +94 -59
- package/dist/index26.vue.d.ts +94 -59
- package/dist/index27.vue.d.cts +75 -0
- package/dist/index27.vue.d.ts +75 -0
- package/dist/index28.vue.d.cts +103 -0
- package/dist/index28.vue.d.ts +103 -0
- package/dist/index29.vue.d.cts +105 -0
- package/dist/index29.vue.d.ts +105 -0
- package/dist/index3.vue.d.cts +4 -4
- package/dist/index3.vue.d.ts +4 -4
- package/dist/index30.vue.d.cts +28 -0
- package/dist/index30.vue.d.ts +28 -0
- package/dist/index4.vue.d.cts +19 -128
- package/dist/index4.vue.d.ts +17 -34
- package/dist/index5.vue.d.cts +127 -35
- package/dist/index5.vue.d.ts +30 -19
- package/dist/index6.vue.d.cts +30 -19
- package/dist/index6.vue.d.ts +22 -118
- package/dist/index7.vue.d.cts +22 -118
- package/dist/index7.vue.d.ts +112 -18
- package/dist/index8.vue.d.cts +112 -18
- package/dist/index8.vue.d.ts +24 -35
- package/dist/index9.vue.d.cts +24 -35
- package/dist/index9.vue.d.ts +37 -80
- package/dist/mapUtils.d.cts +4 -4
- package/dist/mapUtils.d.ts +4 -4
- package/dist/numberCodec.cjs +1 -0
- package/dist/numberCodec.d.cts +25 -0
- package/dist/numberCodec.d.ts +25 -0
- package/dist/numberCodec.js +1 -0
- package/dist/observer.cjs +1 -0
- package/dist/observer.js +1 -0
- package/dist/presets.d.cts +3 -66
- package/dist/presets.d.ts +3 -66
- package/dist/resolver.cjs +1 -1
- package/dist/resolver.d.cts +1 -1
- package/dist/resolver.d.ts +1 -1
- package/dist/resolver.js +1 -1
- package/dist/serializeSearch.cjs +1 -0
- package/dist/serializeSearch.js +1 -0
- package/dist/style.css +561 -322
- package/dist/types.d.cts +60 -103
- package/dist/types.d.ts +62 -52
- package/dist/types2.d.cts +104 -51
- package/dist/types2.d.ts +56 -0
- package/dist/types3.d.cts +56 -0
- package/dist/useCalendarEvents.d.cts +1 -1
- package/dist/useCalendarEvents.d.ts +1 -1
- package/dist/useCollapsePanel.d.cts +6 -6
- package/dist/useCollapsePanel.d.ts +6 -6
- package/dist/useComponentLocale.cjs +1 -1
- package/dist/useComponentLocale.js +1 -1
- package/dist/useCropperCore.d.cts +13 -13
- package/dist/useCropperCore.d.ts +13 -13
- package/dist/useDataResource.cjs +1 -0
- package/dist/useDataResource.js +1 -0
- package/dist/useDictionary.cjs +1 -0
- package/dist/useDictionary.js +1 -0
- package/dist/useDynamicFormState.d.cts +959 -582
- package/dist/useDynamicFormState.d.ts +951 -584
- package/dist/useEdgeInteraction.d.cts +3 -3
- package/dist/useEdgeInteraction.d.ts +3 -3
- package/dist/useFormArray.cjs +1 -0
- package/dist/useFormArray.d.cts +69 -0
- package/dist/useFormArray.d.ts +69 -0
- package/dist/useFormArray.js +1 -0
- package/dist/useFormConfig.cjs +1 -0
- package/dist/useFormConfig.d.cts +590 -0
- package/dist/useFormConfig.d.ts +590 -0
- package/dist/useFormConfig.js +1 -0
- package/dist/useFullscreen.d.cts +3 -3
- package/dist/useFullscreen.d.ts +3 -3
- package/dist/useGridWidth.cjs +1 -0
- package/dist/useGridWidth.js +1 -0
- package/dist/useInfiniteScroll.d.cts +2 -2
- package/dist/useInfiniteScroll.d.ts +2 -2
- package/dist/useQRCode.d.cts +3 -3
- package/dist/useQRCode.d.ts +3 -3
- package/dist/useSignatureHistory.d.cts +4 -4
- package/dist/useSignatureHistory.d.ts +4 -4
- package/dist/useSplitResize.d.cts +5 -5
- package/dist/useSplitResize.d.ts +5 -5
- package/dist/useTableGlobalConfig.cjs +1 -0
- package/dist/useTableGlobalConfig.js +1 -0
- package/dist/useTableQuery.cjs +1 -3
- package/dist/useTableQuery.js +1 -3
- package/dist/useWorkflowValidation.d.cts +6 -6
- package/dist/useWorkflowValidation.d.ts +6 -6
- package/dist/validation.cjs +1 -0
- package/dist/validation.d.cts +18 -0
- package/dist/validation.d.ts +18 -0
- package/dist/validation.js +1 -0
- package/dist/verification.d.cts +4 -4
- package/dist/verification.d.ts +4 -4
- package/dist/workflows.cjs +1 -0
- package/dist/workflows.d.cts +2 -0
- package/dist/workflows.d.ts +2 -0
- package/dist/workflows.js +1 -0
- package/docs/components.en.md +13 -1
- package/docs/components.md +13 -1
- package/docs/guides/batch-results.md +47 -0
- package/docs/guides/data-picker.md +65 -0
- package/docs/guides/data-workflows.md +123 -0
- package/docs/guides/form-density.en.md +56 -3
- package/docs/guides/form-density.md +56 -3
- package/docs/guides/search-workflows.md +164 -0
- package/docs/guides/usage.en.md +1 -1
- package/package.json +21 -1
- package/dist/C_FormSearch2.cjs +0 -1
- package/dist/C_FormSearch2.js +0 -1
- package/dist/useTableQuery.d.cts +0 -1957
- package/dist/useTableQuery.d.ts +0 -1957
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# 业务数据与多区域工作台
|
|
2
|
+
|
|
3
|
+
搜索、表单和表格共享值语义,应用保留接口、权限、缓存和业务事务。新增能力均按需接入;未配置时沿用既有行为。
|
|
4
|
+
|
|
5
|
+
## 一个字典数据源
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import Components from '@robot-admin/naive-ui-components'
|
|
9
|
+
import type { DictionarySource } from '@robot-admin/naive-ui-components/config'
|
|
10
|
+
|
|
11
|
+
const planState: DictionarySource = {
|
|
12
|
+
getOptions: () => [
|
|
13
|
+
{ value: 'released', label: '已下达' },
|
|
14
|
+
{ value: 'running', label: '执行中' },
|
|
15
|
+
],
|
|
16
|
+
}
|
|
17
|
+
app.use(Components, {
|
|
18
|
+
dictionaries: {
|
|
19
|
+
getDictionary: name => (name === 'planState' ? planState : undefined),
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
字段只写 `dictionary: 'planState'`。C_Form 的 `children`、`asyncOptions`,C_FormSearch 的显式 `list/attrs.options`,C_Table 的 `render/formatter/editProps.options` 仍按各自既有优先级生效,未使用的字典不会被加载。
|
|
25
|
+
|
|
26
|
+
按需安装可通过 `/config` 导出的 `DICTIONARY_PROVIDER_KEY` 注入同一个 Provider。不要因为使用表单而导入 MachTable 根包。
|
|
27
|
+
|
|
28
|
+
`DictionarySource` 支持:
|
|
29
|
+
|
|
30
|
+
- `getOptions()`:同步快照,编码保留 string/number/boolean 类型。
|
|
31
|
+
- `load({ signal })`:可选的异步准备;视图会忽略已失效的请求。
|
|
32
|
+
- `subscribe(listener)`:可选的更新通知,返回取消订阅函数。
|
|
33
|
+
|
|
34
|
+
应用已有 store/resolver 时直接桥接,不叠加第二套 TTL 缓存。源负责组织、租户、语言和业务参数隔离:请求开始时捕获作用域,参数切换后不得把旧响应写进新作用域。可由 `getDictionary` 返回该作用域的稳定源,或发布当前快照变更通知。组件仅管理可销毁的选项视图及派生索引。
|
|
35
|
+
|
|
36
|
+
选项必须是有效且唯一的原始编码、字符串标签及可选 boolean disabled。空数组是已提供的空选项,不回退到旧数据。未知编码原样回显;停用历史编码可以保留在源中供展示。重复标签在反向解析时报告歧义,不自动选择第一项。高频展示使用 `createDictionaryLabelResolver(options)`,快照变化时重建一次索引。
|
|
37
|
+
|
|
38
|
+
## 数值:存储、显示和校验分别定义
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { createNumberCodec } from '@robot-admin/naive-ui-components/data'
|
|
42
|
+
import { numeric } from '@robot-admin/form-validate'
|
|
43
|
+
|
|
44
|
+
const weight = createNumberCodec({
|
|
45
|
+
storage: 'decimal-string',
|
|
46
|
+
fractionDigits: 3,
|
|
47
|
+
suffix: ' t',
|
|
48
|
+
})
|
|
49
|
+
const weightRules = [numeric({ totalDigits: 11, fractionDigits: 3, min: 0 })]
|
|
50
|
+
|
|
51
|
+
weight.parse('12345678.005') // '12345678.005'
|
|
52
|
+
weight.format('12.5004') // '12.500 t',只舍入显示,不改原值
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
精确小数用文本控件保留编辑草稿,C_Form 直接消费 `weightRules`,提交前再调用 `parse`。不要在每次输入时解析半成品文本,也不要把 `precision` 当作提交校验。清空得到 null,0 仍为有效数值。默认 number 存储会拒绝不安全整数和不能十进制往返的转换;已经被上游 number 损坏的精度不能恢复,精确接口应传原始字符串。
|
|
56
|
+
|
|
57
|
+
`fractionDigits: 0` 有效;HALF_UP 处理负数中点。parse 接收普通十进制原始值,format 可加千分位、前后缀;带单位/货币符号的显示文本不是接口值。合计、精确排序、汇率和税费按业务数值策略处理,不拿格式化文本计算。
|
|
58
|
+
|
|
59
|
+
MachTable 使用侧可在全局列类型中一次接入 `valueParser` 和 `valueFormatter`;精确字符串字段提供相应业务 comparator。其 `createDictionaryColumn` 可显式设置各输出 target 的编码/标签/码名策略,`summarizeRows` 可注入已有精确运算策略。Naive 库不会依赖或代替网格内核。
|
|
60
|
+
|
|
61
|
+
## 主从资源:本地空值与迟到响应
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { useDataResource } from '@robot-admin/naive-ui-components/workflows'
|
|
65
|
+
|
|
66
|
+
const details = useDataResource({
|
|
67
|
+
key: () => activeOrderId.value,
|
|
68
|
+
load: ({ key, signal }) => orderApi.details(key, { signal }),
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
更换 key 会取消旧请求、清除旧主记录数据;即使传输忽略 signal,旧结果也不能覆盖新记录。同 key 刷新失败保留原展示,`status: 'error'` 与 `stale` 明确表达旧快照,可通过 `reload()` 重试。reload 返回成功与否的 boolean,可直接交给 Search 的 `config.onSearch`。
|
|
73
|
+
|
|
74
|
+
可选 `source` 表示本地受控数据:undefined 才是未提供,`[]` 和 null 都是明确的本地源。本地源生效时不请求远端。组件卸载会清理 watcher/请求,KeepAlive 停用期间不开始新请求;恢复后按最新 key 继续。普通 effectScope 外使用时调用 `dispose()`。每个 panel 独立实例,互不共享请求状态;不缓存所有访问过的订单。
|
|
75
|
+
|
|
76
|
+
## 多区域校验和导航
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { useValidationScope } from '@robot-admin/naive-ui-components/workflows'
|
|
80
|
+
|
|
81
|
+
const validation = useValidationScope()
|
|
82
|
+
const unregister = validation.register({
|
|
83
|
+
id: 'header',
|
|
84
|
+
dirty: () => formRef.value?.isDirty ?? false,
|
|
85
|
+
validate: () => formRef.value!.validate(),
|
|
86
|
+
reveal: issue =>
|
|
87
|
+
formRef.value?.revealField(issue.target?.field ?? '') ?? false,
|
|
88
|
+
})
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
原生 `validate(): Promise<void>` 无需转换器,拒绝的字段错误会被标准化。表格校验可返回 `{ key, source, message, target: { section, tab, rowKey, field } }[]`;行地址使用稳定业务 key。注册标识必须唯一;动态区域移除时调用 unregister,旧验证结果不会回填。`dirty` 仅聚合已注册区域,不深遍历整张业务单据。
|
|
92
|
+
|
|
93
|
+
`C_ErrorSummary` 接收 `validation.issues`,`@navigate="validation.reveal"` 定位问题,默认每页仅挂载8项。`C_SectionNav` 的 items 包含 key/label/errorCount/disabled,通过 `@select` 接入现有分区或折叠面板;它不查找全局 DOM、不监听全局滚动。
|
|
94
|
+
|
|
95
|
+
Form 的 `revealField` 保留 Tab/Step 原有导航和校验约束,禁用或只读目标不强行聚焦,受阻返回 false。自定义控件通过 `focusTarget` 准备并返回真实元素;迟到的异步定位不会抢走当前焦点。
|
|
96
|
+
|
|
97
|
+
校验协调不等于数据库事务。保存使用业务接口的版本、幂等和分项结果;结果未确认时保留原操作及请求快照,避免自动重复提交。
|
|
98
|
+
|
|
99
|
+
## 密度和重复行
|
|
100
|
+
|
|
101
|
+
```vue
|
|
102
|
+
<C_Density density="compact">
|
|
103
|
+
<C_FormSearch ... />
|
|
104
|
+
<C_Form ... />
|
|
105
|
+
<C_Table ... />
|
|
106
|
+
<C_Density density="comfortable">
|
|
107
|
+
<!-- 工位操作区使用较大控件 -->
|
|
108
|
+
<C_Form ... />
|
|
109
|
+
</C_Density>
|
|
110
|
+
</C_Density>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
C_Density 无额外 DOM,只提供子树默认值,不修改应用或兄弟区域。局部 config、显式 size 继续优先。MachTable 的密度由其公开 size/config 独立配置。
|
|
114
|
+
|
|
115
|
+
少量异构重复行使用 `useFormArray(initial, { maxRows })`:items 中每行有稳定 key 与独立 value,增删移动保留身份,`getValues()` 返回隔离副本。reset 使用初始快照;动态删除时同步移除该行校验参与者。大量同构明细使用虚拟表格,不挂载整页常驻输入框。
|
|
116
|
+
|
|
117
|
+
## 批改、结果和输出边界
|
|
118
|
+
|
|
119
|
+
`createBatchPatch` 把 keep/clear/set 转为明确的标量 payload:keep 不发送,clear 发送 null,set 保留空串、0、false。非标量子记录仍由领域协议定义,不自动猜测嵌套更新。
|
|
120
|
+
|
|
121
|
+
`C_BatchResult` 只呈现结果和发出可重试 key,不调用后端。重复 key 以最新结果为准,后续成功项不会因旧失败再次重试。批改范围、差异预览、服务端授权和乐观锁由应用负责。见 [批量结果](./batch-results.md)、[远程实体选择](./data-picker.md)。
|
|
122
|
+
|
|
123
|
+
显示、复制、CSV、XLSX 各 target 可以采用不同策略,但必须使用同一已执行查询、字典和精度快照。大数据导出使用后端任务,模板排版/分页打印使用报表产品。不要把当前加载页当筛选全量,也不要在同步 formatter/serializer 内发请求。
|
|
@@ -18,12 +18,14 @@ const density = ref<ComponentDensity>('compact')
|
|
|
18
18
|
| Density | Field and button size | Layout gap | Card padding | Form feedback minimum |
|
|
19
19
|
| ------------- | --------------------- | ---------- | ------------ | --------------------- |
|
|
20
20
|
| `default` | `medium` | 16px | 24px | 24px |
|
|
21
|
-
| `compact` | `small` | 8px | 12px |
|
|
21
|
+
| `compact` | `small` | 8px | 12px | 4px |
|
|
22
22
|
| `comfortable` | `large` | 20px | 28px | 28px |
|
|
23
23
|
|
|
24
24
|
Validation feedback can grow beyond its minimum height. Changing density preserves form values, search drafts, expanded filters, and card collapse state. Each instance owns its density.
|
|
25
25
|
|
|
26
|
-
Omitting `density` retains existing spacing and the default `medium` size. Explicit `C_Form` `config.size`, `C_FormSearch` `size`, field `attrs.size`, and individual layout gaps take precedence over the preset.
|
|
26
|
+
Omitting `density` retains existing field and layout spacing and the default `medium` size; action areas no longer reserve an unused validation row. Explicit `C_Form` `config.size`, `C_FormSearch` `size`, field `attrs.size`, and individual layout gaps take precedence over the preset.
|
|
27
|
+
|
|
28
|
+
Responsive grids constrain horizontal gaps to the available container width, preventing accumulated gaps across 24 tracks from overflowing narrow modals. Wide screens retain the configured gap, vertical spacing stays independent, and Naive UI continues to calculate offsets. Set `grid.responsive: false` to preserve fixed spacing.
|
|
27
29
|
|
|
28
30
|
Existing native `size` passthrough on `C_Form` retains its final-override priority and synchronizes fields and buttons. Prefer `config` for new integrations.
|
|
29
31
|
|
|
@@ -39,4 +41,55 @@ All eight form layouts support density: default, inline, grid, card, tabs, steps
|
|
|
39
41
|
|
|
40
42
|
Density presets are plain data and preserve lazy loading of layouts and fields.
|
|
41
43
|
|
|
42
|
-
`C_FormModal`
|
|
44
|
+
`C_FormModal` defaults to `compact` and inherits application form density. Compact modals use `small` fields and `tiny` footer buttons, matching compact action bars. Explicit `config.size` still controls both fields and buttons; explicit `default` / `comfortable` density uses its corresponding size. Table editor modals inherit table density as well.
|
|
45
|
+
|
|
46
|
+
## Application defaults with on-demand imports
|
|
47
|
+
|
|
48
|
+
Provide defaults once during application startup. Local `config.density` takes precedence; search inherits only density, not form layout, callbacks or size.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { FORM_GLOBAL_CONFIG_KEY } from '@robot-admin/naive-ui-components/config'
|
|
52
|
+
|
|
53
|
+
app.provide(FORM_GLOBAL_CONFIG_KEY, { density: 'compact' })
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Compact search fields default to 208px and fill the available width on narrow screens; explicit field `attrs.style.width` takes precedence. Compact numeric fields hide step buttons by default while preserving keyboard Up / Down, step, precision and limits. Set `attrs: { showButton: true }` when buttons are useful. Naive UI still owns number handling and themes; the text input avoids browser-native number spinners. Field `attrs.inputProps` can override accessible names and numeric semantics.
|
|
57
|
+
|
|
58
|
+
## Arrange fields by container width
|
|
59
|
+
|
|
60
|
+
Opt into `config.grid.minItemWidth` to budget the entire field, including its label. Default spans adapt to the actual split pane or dialog width. Explicit spans stay intact while they fit, and expand only when necessary, up to a full row. Existing `cols`, `offset`, `suffix`, and Naive UI wrapping remain in charge.
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const config = {
|
|
64
|
+
density: 'compact',
|
|
65
|
+
layout: 'grid',
|
|
66
|
+
grid: { cols: 24, minItemWidth: 300, xGap: 16, yGap: 8 },
|
|
67
|
+
} as const
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Omitting the option preserves existing spans. `responsive: false` disables automatic expansion. A container narrower than the budget uses a full row without forcing an overflowing CSS minimum width. One local `ResizeObserver` follows activation, deactivation, and disposal; no global resize listener is added.
|
|
71
|
+
|
|
72
|
+
## Reveal an invalid field
|
|
73
|
+
|
|
74
|
+
`await formRef.revealField(field)` enters the field's tab or step, or expands its card group, then scrolls to and focuses an available control. It returns `true` only after actual focus succeeds. Missing, hidden, disabled, or readonly fields, unloaded layouts, and navigation blocked by existing validation or business guards return `false`. It never clears errors, changes validation rules, or enables an editing mode.
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
const focused = await formRef.value?.revealField('customer.name')
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Lookup stays within this form's item references. Unmounting, KeepAlive deactivation, a layout change, or a newer reveal request cancels older work, so late results cannot steal focus. A custom or virtual control may supply `focusTarget` on its field. Prepare the control and return its actual element; let the form recheck access and apply focus rather than focusing inside the adapter.
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
const fields = [
|
|
84
|
+
{
|
|
85
|
+
type: 'custom-customer',
|
|
86
|
+
prop: 'customerId',
|
|
87
|
+
focusTarget: async ({ signal }) => {
|
|
88
|
+
await customerEditor.ensureMounted(signal)
|
|
89
|
+
return signal.aborted ? null : customerEditor.inputElement
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The context also provides `field`, the form item's `element` (possibly null), and the current `model`. An adapter that ignores cancellation still cannot apply an obsolete focus result. Return null when no real target exists; callers can keep the error summary visible or explain a navigation restriction.
|
|
@@ -18,14 +18,16 @@ const density = ref<ComponentDensity>('compact')
|
|
|
18
18
|
| 密度 | 字段与操作按钮 | 布局间距 | 卡片留白 | 表单反馈预留高度 |
|
|
19
19
|
| ------------- | -------------- | -------- | -------- | ---------------- |
|
|
20
20
|
| `default` | `medium` | 16px | 24px | 24px |
|
|
21
|
-
| `compact` | `small` | 8px | 12px |
|
|
21
|
+
| `compact` | `small` | 8px | 12px | 4px |
|
|
22
22
|
| `comfortable` | `large` | 20px | 28px | 28px |
|
|
23
23
|
|
|
24
24
|
反馈预留高度是最小值,校验错误可自然撑开。密度支持响应式切换,切换不会清空表单值、搜索草稿、折叠条件或分组展开状态;不同实例可以使用不同密度。
|
|
25
25
|
|
|
26
26
|
## 保留明确覆盖
|
|
27
27
|
|
|
28
|
-
已有项目未配置 `density`
|
|
28
|
+
已有项目未配置 `density` 时保留原来的字段、布局间距和默认 `medium` 尺寸;无校验职责的操作区不再保留空反馈行。显式配置的控件尺寸、字段尺寸和布局间距继续优先,不需要迁移旧用法。
|
|
29
|
+
|
|
30
|
+
响应式网格会根据容器宽度约束横向间距,避免 24 栅格的累计间距撑破窄弹窗;宽屏仍使用配置值,纵向间距独立,偏移继续交给 Naive UI 处理。`grid.responsive: false` 可保留固定的间距行为。
|
|
29
31
|
|
|
30
32
|
已有 `C_Form` 原生 `size` 透传继续按原有末尾覆盖顺序生效,并同步字段与操作按钮;新接入推荐统一使用 `config`。
|
|
31
33
|
|
|
@@ -56,4 +58,55 @@ const largeFields = { density: 'compact', size: 'large' } as const
|
|
|
56
58
|
|
|
57
59
|
卡片、网格等布局仍按需加载。密度预设仅为纯数据,不引入额外 UI 依赖。
|
|
58
60
|
|
|
59
|
-
`C_FormModal`
|
|
61
|
+
`C_FormModal` 默认使用 `compact`,也继承应用级表单密度;紧凑弹窗的字段为 `small`,页脚按钮为 `tiny`,与紧凑操作栏一致。显式 `config.size` 仍同时控制字段和按钮;显式 `default` / `comfortable` 密度使用对应尺寸。表格编辑弹窗也会继承表格密度。
|
|
62
|
+
|
|
63
|
+
## 按需设置应用默认值
|
|
64
|
+
|
|
65
|
+
在应用启动时提供一次即可,局部的 `config.density` 优先,搜索只继承密度,不继承表单布局、回调或尺寸。
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { FORM_GLOBAL_CONFIG_KEY } from '@robot-admin/naive-ui-components/config'
|
|
69
|
+
|
|
70
|
+
app.provide(FORM_GLOBAL_CONFIG_KEY, { density: 'compact' })
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
紧凑搜索的字段默认宽度为 208px,窄屏自动占满可用宽度;显式字段 `attrs.style.width` 仍可覆盖。紧凑数字字段默认隐藏增减按钮,保留键盘 ↑ / ↓、步长、精度及上下限;需要按钮的字段配置 `attrs: { showButton: true }`。控件继续使用 Naive UI 的数值处理与主题,输入为文本,不叠加浏览器原生数字箭头;`attrs.inputProps` 可覆盖可访问名称和数值语义。
|
|
74
|
+
|
|
75
|
+
## 按容器安排字段
|
|
76
|
+
|
|
77
|
+
网格可显式设置 `config.grid.minItemWidth`,预算包含标签和控件整体,不是仅输入框宽度。根据当前分栏或弹窗容器的真实宽度调整默认跨度;已有显式 `span` 在宽度足够时保留,只有容纳不了该预算时才扩展,最多占满整行。`cols`、`offset`、`suffix` 与 Naive UI 换行规则不变。
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
const config = {
|
|
81
|
+
density: 'compact',
|
|
82
|
+
layout: 'grid',
|
|
83
|
+
grid: { cols: 24, minItemWidth: 300, xGap: 16, yGap: 8 },
|
|
84
|
+
} as const
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
未设置该项时保持原有跨度行为;`responsive: false` 禁用自动扩展。容器比最小预算还窄时使用整行,不通过固定 `min-width` 撑破页面。宽度由局部 `ResizeObserver` 提供,停用、重新激活及销毁时管理自身订阅,无全局 resize 监听。
|
|
88
|
+
|
|
89
|
+
## 定位校验字段
|
|
90
|
+
|
|
91
|
+
实例方法 `await formRef.revealField(field)` 会先进入字段所在标签页、步骤或展开对应卡片分组,再滚动并聚焦真实可用控件;仅成功获得焦点才返回 `true`。找不到字段、字段隐藏/禁用/只读、布局尚未加载、切换前业务回调或已有校验阻止导航时返回 `false`。它不清除错误、不修改校验规则,也不会替业务切换编辑模式。
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
const focused = await formRef.value?.revealField('customer.name')
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
定位只使用当前表单实例的字段引用。卸载、KeepAlive 停用、布局改变或新定位请求会取消旧请求,迟到结果不会抢回焦点。自定义或虚拟控件可在字段上配置 `focusTarget`,异步准备后返回真实元素;由表单重新检查访问约束并执行 focus,适配器不要自行聚焦,也不要返回占位元素。
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
const fields = [
|
|
101
|
+
{
|
|
102
|
+
type: 'custom-customer',
|
|
103
|
+
prop: 'customerId',
|
|
104
|
+
focusTarget: async ({ signal }) => {
|
|
105
|
+
await customerEditor.ensureMounted(signal)
|
|
106
|
+
return signal.aborted ? null : customerEditor.inputElement
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
]
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`focusTarget` 上下文还提供 `field`、字段表单项的 `element`(可能为空)和当前 `model`。即使异步适配器忽略取消,库也会拒绝旧元素;没有可聚焦目标时诚实返回 `false`,由使用侧保留错误摘要或提示导航限制。
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# 密集搜索区与查询快照
|
|
2
|
+
|
|
3
|
+
`C_FormSearch` 保留已有 `formItemList`、`v-model`、`formParams`、历史记录和 `@search` 接口。普通接入仍采用 `inline` 布局;复杂业务可显式开启容器网格。组件只管理搜索条件与执行状态,不绑定分页接口、租户规则或特定表格。
|
|
4
|
+
|
|
5
|
+
## 容器布局与字段
|
|
6
|
+
|
|
7
|
+
```vue
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
import { ref } from 'vue'
|
|
10
|
+
import {
|
|
11
|
+
C_FormSearch,
|
|
12
|
+
serializeSearch,
|
|
13
|
+
type SearchFormItem,
|
|
14
|
+
type SearchNumberRangeValue,
|
|
15
|
+
type FormSearchExpose,
|
|
16
|
+
} from '@robot-admin/naive-ui-components/C_FormSearch'
|
|
17
|
+
|
|
18
|
+
interface Query {
|
|
19
|
+
organization: string
|
|
20
|
+
keyword: string
|
|
21
|
+
amount: SearchNumberRangeValue
|
|
22
|
+
month: string | null
|
|
23
|
+
enabled: boolean | null
|
|
24
|
+
customer: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const query = ref<Query>({
|
|
28
|
+
organization: '001',
|
|
29
|
+
keyword: '',
|
|
30
|
+
amount: null,
|
|
31
|
+
month: null,
|
|
32
|
+
enabled: false,
|
|
33
|
+
customer: '',
|
|
34
|
+
})
|
|
35
|
+
const search = ref<FormSearchExpose<Query>>()
|
|
36
|
+
const fields: SearchFormItem<Query>[] = [
|
|
37
|
+
{ type: 'input', prop: 'organization', label: '组织', fixed: true },
|
|
38
|
+
{ type: 'input', prop: 'keyword', label: '关键词' },
|
|
39
|
+
{
|
|
40
|
+
type: 'number-range',
|
|
41
|
+
prop: 'amount',
|
|
42
|
+
label: '金额范围',
|
|
43
|
+
span: 2,
|
|
44
|
+
query: { keys: ['minimumAmount', 'maximumAmount'] },
|
|
45
|
+
},
|
|
46
|
+
{ type: 'month', prop: 'month', label: '月份', valueMode: 'formatted' },
|
|
47
|
+
{
|
|
48
|
+
type: 'select',
|
|
49
|
+
prop: 'enabled',
|
|
50
|
+
label: '状态',
|
|
51
|
+
list: [
|
|
52
|
+
{ label: '启用', value: true },
|
|
53
|
+
{ label: '停用', value: false },
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
type: 'custom',
|
|
58
|
+
prop: 'customer',
|
|
59
|
+
label: '客户',
|
|
60
|
+
slot: 'customer',
|
|
61
|
+
defaultValue: '',
|
|
62
|
+
},
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
async function execute(params: Query) {
|
|
66
|
+
const request = serializeSearch(params, fields)
|
|
67
|
+
// await api.list(request),或交给已有的 useMachTableQuery 执行器。
|
|
68
|
+
// 必须返回真实任务的 Promise;仅启动异步任务然后返回 void 无法等待业务完成。
|
|
69
|
+
await Promise.resolve(request)
|
|
70
|
+
}
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<template>
|
|
74
|
+
<C_FormSearch
|
|
75
|
+
ref="search"
|
|
76
|
+
v-model="query"
|
|
77
|
+
:form-item-list="fields"
|
|
78
|
+
:config="{
|
|
79
|
+
density: 'compact',
|
|
80
|
+
layout: 'grid',
|
|
81
|
+
columns: 4,
|
|
82
|
+
fieldMinWidth: 208,
|
|
83
|
+
labelPlacement: 'left',
|
|
84
|
+
labelWidth: 88,
|
|
85
|
+
foldThreshold: 4,
|
|
86
|
+
onSearch: execute,
|
|
87
|
+
}"
|
|
88
|
+
>
|
|
89
|
+
<template #customer="{ value, model, disabled, updateValue }">
|
|
90
|
+
<!-- 接入已有客户 Picker;选择结果通过 updateValue 回写。 -->
|
|
91
|
+
<button
|
|
92
|
+
type="button"
|
|
93
|
+
:disabled="disabled"
|
|
94
|
+
@click="updateValue(model.customer)"
|
|
95
|
+
>
|
|
96
|
+
{{ value || '选择客户' }}
|
|
97
|
+
</button>
|
|
98
|
+
</template>
|
|
99
|
+
</C_FormSearch>
|
|
100
|
+
</template>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- `layout: 'grid'` 按**搜索区容器**宽度分栏,`columns` 是最大列数。窄分栏中即使浏览器很宽也会减少列数;不使用视口宽度判断可用空间。
|
|
104
|
+
- `fieldMinWidth` 为基础轨道预算,字段 `span` / `minWidth` 可申请更多空间;实际跨度不会超过容器当前列数。操作区自然位于最后一列。
|
|
105
|
+
- `labelPlacement: 'left' | 'top'` 与 `labelWidth` 对应 Naive UI 表单标签。省略字段 `label` 保持旧的无标签外观。
|
|
106
|
+
- `density` 与表单共用,字段显式 `attrs.size` 和组件 `size` 保持优先。
|
|
107
|
+
- 折叠只影响显示,条件和校验仍有效。隐藏/折叠且有值的字段会显示生效数量;`0`、`false` 也是有效条件。
|
|
108
|
+
|
|
109
|
+
## 控件与原始模型
|
|
110
|
+
|
|
111
|
+
支持 `input`、`select`、`number`、`number-range`、`date`、`month`、`date-range`、`custom`、`spacer`。
|
|
112
|
+
|
|
113
|
+
数字输入继续使用 Naive UI;`attrs` 可配置 `min`、`max`、`precision` 等。紧凑模式的单数字默认隐藏步进按钮,显式 `attrs.showButton` 可覆盖。范围模型为 `[number | null, number | null] | null`,允许先填写单边,两边清空时回到 `null`。范围先后、跨度上限等业务规则通过字段 `rules` 声明,组件不预设业务阈值。
|
|
114
|
+
|
|
115
|
+
数字控件只用于 `number` 模型。精确十进制字符串、超出安全范围的金额/编号应使用 `custom` 输入与明确的 codec,避免先转 `Number` 导致原值损失。搜索组件不负责计算税额或汇率。
|
|
116
|
+
|
|
117
|
+
日期/月默认使用时间戳;已有 `date-range` 时间戳模型保持兼容。字符串模型声明 `valueMode: 'formatted'`,包括初始值为 `null` 时。默认格式分别为 `yyyy-MM-dd`、`yyyy-MM`、`yyyy-MM-dd HH:mm`,可通过 `attrs.format` / `attrs.valueFormat` 显式覆盖。展示格式与回写类型分开,清空不会将字符串日期改成时间戳。
|
|
118
|
+
|
|
119
|
+
`select` 保留字符串、数字、布尔值及多选数组的业务类型。内部布尔编码仅用于适配原生选择器,提交的 `false` 不会变成字符串或 `0`。
|
|
120
|
+
|
|
121
|
+
## 字典与自定义控件
|
|
122
|
+
|
|
123
|
+
字段可配置 `dictionary: 'orderStatus'`,由应用已提供的字典 Provider 解析;也可直接传入 `DictionarySource`。搜索不另建字典缓存,使用同一来源的选项、订阅与可取消加载。
|
|
124
|
+
|
|
125
|
+
显式 `list`(包括空数组)和 `attrs.options` 优先于字典,且不会加载未使用的字典。加载失败在选择器中显示重试入口;字典标签更新不会重置输入草稿。组织切换和字典加载策略仍由应用 Provider 决定。
|
|
126
|
+
|
|
127
|
+
`custom` 可使用 `slot` 或 `render(context)`;必须声明类型正确的 `defaultValue`,使 reset 和 clear 有确定语义。上下文提供 `value`、只读 `model`、`prop`、`size`、`disabled`、`readonly` 和 `updateValue`。修改走 `updateValue`;`fixed`、`readonly`、`disabled` 字段会拦截控件回写。自定义按钮使用 `type="button"`,避免误触发表单提交。
|
|
128
|
+
|
|
129
|
+
## 草稿、执行结果和重置
|
|
130
|
+
|
|
131
|
+
- `v-model` / `formParams` 是可编辑草稿;字段配置或字典选项变化不拿旧 props 覆盖未提交输入。
|
|
132
|
+
- `@search` 是经过校验的执行通知,保留原有时机;它不表示后台查询成功。
|
|
133
|
+
- 使用 `config.onSearch` 返回任务 Promise 后,只有成功完成才更新 `appliedParams` 并发出 `@applied`。回调拒绝或显式返回 `false` 时,`searchFn()` 返回 `false`,保留上一次成功条件并允许重试。可直接返回 `useDataResource.reload()` 的 boolean 结果;接入自行收拢错误的其它控制器时,回调应检查其错误状态并返回 `false`。
|
|
134
|
+
- 仅使用旧 `@search` 的接入无法观察事件监听器异步结果,因此不会伪造 `appliedParams`;需要成功快照时使用 `onSearch`。
|
|
135
|
+
- 查询期间继续输入只改变草稿;成功快照仍是当次发送的条件。`hasUnappliedChanges` 可判断草稿与已执行条件是否不同。
|
|
136
|
+
- `cleanFn()` 只重置草稿,不主动执行查询,也不把重置值标记为成功查询。`defaultValue: 0 / false` 保留原类型;未配置字段、隐藏上下文、分页参数不被清除。
|
|
137
|
+
- `fixed: true` 的字段参与查询,但控件与 reset 不可改写。固定作用域切换时清空已执行快照,迟到请求不能在新作用域下标记成功。宿主仍应在执行器中取消失效请求,防止业务结果覆盖;`fixed` 不是后端授权机制。
|
|
138
|
+
|
|
139
|
+
原生表单提交(包括 Enter)共享搜索任务锁,验证期间与请求期间都防重;外部 `loading` 仍生效。
|
|
140
|
+
|
|
141
|
+
## 显式请求编码
|
|
142
|
+
|
|
143
|
+
`serializeSearch(params, fields, { omitEmpty: true })` 是纯辅助函数,组件不会隐式调用,也不会更改 `onSearch`、事件或 `v-model` 的类型。
|
|
144
|
+
|
|
145
|
+
字段 `query` 支持:
|
|
146
|
+
|
|
147
|
+
- `name`:仅将请求字段改名。
|
|
148
|
+
- `keys: ['start', 'end']`:将范围拆成两个请求字段,移除原范围键。
|
|
149
|
+
- `serialize(value)`:显式转换单个字段的传输格式;入参是隔离数据,不能改变草稿。
|
|
150
|
+
|
|
151
|
+
默认省略已配置字段的空值,保留 `0`、`false`、`'001'`。范围两端分别清理;`omitEmpty: false` 保留显式空值。未配置的上下文字段继续保留。重复目标键和不安全路径会报错,避免编码覆盖固定组织等条件。
|
|
152
|
+
|
|
153
|
+
表格列表、合计与导出应使用同一次成功执行的条件快照,按各自 target 选择编码或标签。页面负责把该快照交给后端导出任务;不读取用户正在修改的草稿冒充已查询范围。
|
|
154
|
+
|
|
155
|
+
## 常用条件
|
|
156
|
+
|
|
157
|
+
通过公开实例的 `applyPreset(values)` 应用“今日”“待处理”等业务预设,无需再封装一个搜索组件。它只更新已声明且可编辑的字段,固定组织、隐藏上下文、只读字段与分页参数保持原值;0/false 不会被丢弃。传入值先隔离复制,失败不会部分改写模型。
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
searchRef.value?.applyPreset({ status: 'running', amountRange: [0, null] })
|
|
161
|
+
// 用户确认执行时再调用 searchFn();应用预设本身不发请求、不更新 appliedParams。
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
动态日期和班次的定义留在业务预设中,点击时产生当次值;预设不引入全局存储、额外 watcher 或另一套查询规则。
|
package/docs/guides/usage.en.md
CHANGED
|
@@ -270,7 +270,7 @@ For create/edit flows, `C_FormModal` consumes a structural headless `editor` dir
|
|
|
270
270
|
</template>
|
|
271
271
|
```
|
|
272
272
|
|
|
273
|
-
The editor contract is structural: the component package does not depend on a request library. Any controller exposing `visible/mode/model/title/loading/setModel/close/submit` can drive it. The default uses a responsive 620px width, `small` form controls, and a right-aligned icon-based `C_ActionBar`
|
|
273
|
+
The editor contract is structural: the component package does not depend on a request library. Any controller exposing `visible/mode/model/title/loading/setModel/close/submit` can drive it. The default uses a responsive 620px width, compact density, `small` form controls, and a right-aligned icon-based `C_ActionBar` with `tiny` footer buttons. Close button, mask close, and Escape remain enabled by default and can be disabled for high-risk forms.
|
|
274
274
|
|
|
275
275
|
For remote tables, `useTableQuery` owns cancellation, latest-request-wins behavior, pagination, and loading. Destructure its `bindings` and use `<C_Table v-bind="bindings" />`:
|
|
276
276
|
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.23.0",
|
|
7
7
|
"description": "基于 Naive UI 的 Vue 3 组件库",
|
|
8
8
|
"packageManager": "bun@1.3.14",
|
|
9
9
|
"engines": {
|
|
@@ -35,6 +35,26 @@
|
|
|
35
35
|
"default": "./dist/config.cjs"
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
|
+
"./data": {
|
|
39
|
+
"import": {
|
|
40
|
+
"types": "./dist/data.d.ts",
|
|
41
|
+
"default": "./dist/data.js"
|
|
42
|
+
},
|
|
43
|
+
"require": {
|
|
44
|
+
"types": "./dist/data.d.cts",
|
|
45
|
+
"default": "./dist/data.cjs"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"./workflows": {
|
|
49
|
+
"import": {
|
|
50
|
+
"types": "./dist/workflows.d.ts",
|
|
51
|
+
"default": "./dist/workflows.js"
|
|
52
|
+
},
|
|
53
|
+
"require": {
|
|
54
|
+
"types": "./dist/workflows.d.cts",
|
|
55
|
+
"default": "./dist/workflows.cjs"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
38
58
|
"./style.css": "./dist/style.css",
|
|
39
59
|
"./resolver": {
|
|
40
60
|
"import": {
|
package/dist/C_FormSearch2.cjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
require(`./chunk.cjs`);const e=require(`./density.cjs`),t=require(`./useComponentLocale.cjs`),n=require(`./feedback.cjs`),r=require(`./export-helper.cjs`),i=require(`./useActionTask.cjs`),a=require(`./storage.cjs`),o=require(`./data.cjs`);let s=require(`vue`),c=require(`naive-ui`);function l(e,t,n={}){let r=()=>(0,s.toValue)(n),i=(0,s.computed)(()=>!!r().storageKey),o=t=>e.value.find(e=>e.prop===t),c=()=>{let{storageKey:t}=r();t&&a.i(t,Object.fromEntries(e.value.filter(e=>e.hisList?.length).map(e=>[e.prop,e.hisList])))},l=()=>{let{storageKey:e}=r();if(!e)return;let t=a.t(e);t&&(Array.isArray(t)?t.map(e=>[e.prop,e.hisList]):Object.entries(t)).forEach(([e,t])=>{let n=o(e);n&&Array.isArray(t)&&(n.hisList=[...t])})},u=t=>{e.value.forEach(e=>{e.isFocus=!!(e.hisList&&e.prop===t)})},d=e=>{l(),u(e)},f=()=>u(),p=(e,n)=>{o(n)?.type===`input`&&(t.value[n]=e,f())},m=(e,t)=>{let n=o(e);n?.hisList&&(n.hisList.splice(t,1),n.hisList.length===0&&(n.isFocus=!1),c())},h=e=>{let t=o(e);t?.hisList&&(t.hisList=[],t.isFocus=!1,c())},g=(e,t)=>{let n=e.indexOf(t);n>-1&&e.splice(n,1),e.unshift(t);let i=Math.max(1,r().maxItems??5);return e.length>i&&(e.length=i),e};return{enabled:i,handleFocus:d,closeAllPanels:f,selectHistoryItem:p,deleteHistoryItem:m,clearAllHistory:h,saveCurrentInputs:()=>{i.value&&(Object.keys(t.value).forEach(e=>{let n=t.value[e];if(n==null||n===``)return;let r=o(e);if(r?.type!==`input`||!r.hisList)return;let i=String(n).trim();i&&g(r.hisList,i)}),f(),c())},restoreFromStorage:l}}function u(e,t){return e.defaultValue===void 0?e.type===`input`?``:e.type===`select`&&e.attrs?.multiple===!0?[]:e.type===`select`&&e.attrs?.clearable===!1?o.t(t):null:o.t(e.defaultValue)}function d(e,t,n){return t??(e.type===`input`?``:e.type===`select`&&e.attrs?.multiple===!0?[]:e.type===`select`&&e.attrs?.clearable===!1?u(e,n):null)}function f(e,r){let a=(0,s.computed)(()=>(0,s.toValue)(r.config)??{}),c=n.t(()=>a.value.feedback),{t:d}=t.t(()=>a.value.locale),f=(0,s.ref)(null),p=(0,s.computed)(()=>a.value.foldThreshold??7),m=(0,s.computed)(()=>a.value.requireValue??!0),h=(0,s.ref)(o.t(r.formItemList)),g=(0,s.ref)(o.t(r.formParams)),_=(0,s.ref)(!1),v=i.t(),y=(0,s.computed)(()=>v.isRunning(`search`)),b=!0;(0,s.getCurrentScope)()&&(0,s.onScopeDispose)(()=>{b=!1});let x=l(h,g,r.historyOptions),S=(0,s.computed)(()=>h.value.filter(e=>e.show!==!1)),C=(0,s.computed)(()=>S.value.filter((e,t)=>_.value||t<p.value)),w=(0,s.computed)(()=>S.value.length>p.value),T=e=>e==null||e===``||Array.isArray(e)&&e.length===0,E=()=>{let e=g.value;h.value.forEach(t=>{t.isFocus=!1,t.show!==!1&&(t.type===`select`&&!t.placeholder&&(t.placeholder=`请选择`),t.type!==`spacer`&&e[t.prop]===void 0&&(e[t.prop]=u(t,e[t.prop])))})},D=async()=>{try{return await f.value?.validate(),!0}catch{return w.value&&(_.value=!0),!1}},O=e=>b&&!(0,s.toValue)(r.loading)&&o.a(g.value,e),k=async()=>{let t=o.t(g.value);if(!await D()||!O(t))return!1;if(m.value&&!Object.entries(t).filter(([e])=>e!==`pageNum`&&e!==`pageSize`).some(([,e])=>!T(e)))return c.warning(d(`search.valueRequired`)),!1;if(x.saveCurrentInputs(),e(`search`,o.t(t)),!O(t))return!1;if(!a.value.onSearch)return!0;try{return await a.value.onSearch(t),b}catch(e){return b&&c.error(d(`search.failed`),e),!1}},A=async()=>{let e=await v.run(`search`,k,()=>!(0,s.toValue)(r.loading));return e.executed&&e.value},j=t=>{e(`change-params`,o.t(t)),e(`update:modelValue`,o.t(t))},M=()=>{I=!0;let e=++L;(0,s.nextTick)(()=>{e===L&&(I=!1)})},N=()=>{f.value?.restoreValidation();let t=o.t(g.value),n=t;h.value.forEach(e=>{e.show===!1||e.type===`spacer`||e.prop===`pageNum`||e.prop===`pageSize`||(n[e.prop]=u(e,n[e.prop]))}),M(),g.value=t,j(t),e(`reset`,o.t(t))},P=()=>{_.value=!_.value},F=(e,t)=>{M(),h.value=o.t(e),g.value=o.t(t),E()},I=!1,L=0;return E(),(0,s.watch)(g,e=>{I||j(e)},{deep:!0}),(0,s.onMounted)(()=>{x.restoreFromStorage()}),{formRef:f,fields:h,formParams:g,expanded:_,searching:y,visibleFields:C,availableFields:S,foldThreshold:p,hasExpandButton:w,history:x,searchFn:A,resetFn:N,toggleFold:P,syncFromProps:F}}const p=e=>{if(Array.isArray(e)&&e.length===2){if(e.every(e=>typeof e==`string`))return{mode:`formatted`,value:[e[0],e[1]]};if(e.every(e=>typeof e==`number`&&Number.isFinite(e)))return{mode:`timestamp`,value:[e[0],e[1]]}}return{mode:`timestamp`,value:null}};function m(e,t){let n=e?.map(e=>e.value===void 0?e.label??e.labelDefault:e.value),r=Array.isArray(t)?t:[t],i=new Set([...n??[],...r]),a=new Map;for(let e of[!1,!0]){let t=`__c_form_search_boolean_${e}__`;for(;i.has(t);)t+=`_`;a.set(e,t),i.add(t)}let o=e=>typeof e==`boolean`?a.get(e):typeof e==`string`||typeof e==`number`?e:null,s=e=>e===a.get(!1)?!1:e===a.get(!0)?!0:e;return{options:e?.map((e,t)=>({...e,label:e.label??e.labelDefault??``,value:o(n[t])??void 0})),value:Array.isArray(t)?t.map(o).filter(e=>e!==null):o(t),decode:e=>Array.isArray(e)?e.map(s):e===null?null:s(e)}}const h=[`onClick`],g={class:`history-text`},_={key:0,class:`history-footer`},v=[`onClick`],y={class:`form-search-item-box form-search-actions`},b={class:`form-item-input`},x=(0,s.defineComponent)({name:`C_FormSearch`,__name:`index`,props:{bordered:{type:Boolean,default:!0},formItemList:{default:()=>[]},formParams:{default:()=>({})},modelValue:{},formSearchInputHistoryString:{},size:{},loading:{type:Boolean,default:!1},config:{}},emits:[`search`,`reset`,`change-params`,`update:modelValue`],setup(t,{expose:n,emit:r}){let i=t,a=r,o=(0,s.computed)(()=>e.t(i.config?.density)),l=(0,s.computed)(()=>i.size??o.value.size),u=(0,s.computed)(()=>{if(i.config?.density===void 0)return;let{gap:e,rowGap:t,padding:n}=o.value;return{"--c-search-gap":`${e}px`,"--c-search-row-gap":`${t}px`,"--c-search-padding":`${n}px`}}),{formRef:x,formParams:S,expanded:C,searching:w,availableFields:T,foldThreshold:E,hasExpandButton:D,history:O,searchFn:k,resetFn:A,toggleFold:j,syncFromProps:M}=f(a,{formItemList:i.formItemList,formParams:i.modelValue??i.formParams,config:()=>i.config,loading:()=>i.loading,historyOptions:()=>({storageKey:i.formSearchInputHistoryString,maxItems:i.config?.historyMaxItems})}),N=(0,s.computed)(()=>new Map(T.value.filter(e=>e.type===`select`).map(e=>[e.prop,m(e.list,S.value[e.prop])]))),P=e=>{let t=S.value[e];return typeof t==`string`?t:null},F=(e,t)=>{I(e,N.value.get(e)?.decode(t)??null)},I=(e,t)=>{let n=T.value.find(t=>t.prop===e),r=S.value;n&&(r[e]=d(n,t,r[e]))},L=e=>{let t=p(S.value[e]);return t.mode===`formatted`?{formattedValue:t.value,"onUpdate:formattedValue":t=>I(e,t)}:{value:t.value,"onUpdate:value":t=>I(e,t)}};return(0,s.watch)(()=>[i.formItemList,i.modelValue,i.formParams],([e,t,n])=>M(e,t??n),{deep:!0}),n({formRef:x,formParams:S,searchFn:k,cleanFn:A,changeFoldState:j}),(e,n)=>((0,s.openBlock)(),(0,s.createBlock)((0,s.unref)(c.NCard),{class:(0,s.normalizeClass)([`form-search-card custom-card`,t.config?.density&&`form-search-card--${t.config.density}`]),style:(0,s.normalizeStyle)(u.value),bordered:t.bordered},{default:(0,s.withCtx)(()=>[(0,s.createVNode)((0,s.unref)(c.NForm),{class:`form-search`,model:(0,s.unref)(S),ref_key:`formRef`,ref:x,size:l.value,onSubmit:(0,s.withModifiers)((0,s.unref)(k),[`prevent`])},{default:(0,s.withCtx)(()=>[((0,s.openBlock)(!0),(0,s.createElementBlock)(s.Fragment,null,(0,s.renderList)((0,s.unref)(T),(e,t)=>(0,s.withDirectives)(((0,s.openBlock)(),(0,s.createElementBlock)(`div`,{class:`form-search-item-box`,key:e.prop||t},[e.type===`spacer`?(0,s.createCommentVNode)(`v-if`,!0):((0,s.openBlock)(),(0,s.createBlock)((0,s.unref)(c.NFormItem),{key:0,class:`form-item-input`,path:e.prop,rule:e.rules,"show-feedback":!1,"show-label":!1},{default:(0,s.withCtx)(()=>[e.type===`input`?((0,s.openBlock)(),(0,s.createBlock)((0,s.unref)(c.NInput),(0,s.mergeProps)({key:0,clearable:``,size:l.value,value:P(e.prop),"onUpdate:value":t=>I(e.prop,t),placeholder:e.placeholder},{ref_for:!0},e.attrs,{onFocus:t=>(0,s.unref)(O).handleFocus(e.prop),onBlur:(0,s.unref)(O).closeAllPanels}),null,16,[`size`,`value`,`onUpdate:value`,`placeholder`,`onFocus`,`onBlur`])):(0,s.createCommentVNode)(`v-if`,!0),e.isFocus?((0,s.openBlock)(),(0,s.createElementBlock)(`div`,{key:1,class:`input-history`,onMousedown:n[0]||=(0,s.withModifiers)(()=>{},[`prevent`])},[((0,s.openBlock)(!0),(0,s.createElementBlock)(s.Fragment,null,(0,s.renderList)(e.hisList,(t,r)=>((0,s.openBlock)(),(0,s.createElementBlock)(`div`,{class:`history-item`,onClick:n=>(0,s.unref)(O).selectHistoryItem(t,e.prop),key:t},[(0,s.createElementVNode)(`span`,g,(0,s.toDisplayString)(t),1),(0,s.createVNode)((0,s.unref)(c.NIcon),{class:`delete-icon`,size:`14`,onClick:(0,s.withModifiers)(t=>(0,s.unref)(O).deleteHistoryItem(e.prop,r),[`stop`])},{default:(0,s.withCtx)(()=>[...n[1]||=[(0,s.createElementVNode)(`div`,{class:`i-mdi:close`},null,-1)]]),_:1},8,[`onClick`])],8,h))),128)),e.hisList&&e.hisList.length>0?((0,s.openBlock)(),(0,s.createElementBlock)(`div`,_,[(0,s.createElementVNode)(`span`,{class:`clear-all`,onClick:(0,s.withModifiers)(t=>(0,s.unref)(O).clearAllHistory(e.prop),[`stop`])},` 清空历史记录 `,8,v)])):(0,s.createCommentVNode)(`v-if`,!0)],32)):(0,s.createCommentVNode)(`v-if`,!0),e.type===`select`?((0,s.openBlock)(),(0,s.createBlock)((0,s.unref)(c.NSelect),(0,s.mergeProps)({key:2,value:N.value.get(e.prop)?.value,"onUpdate:value":t=>F(e.prop,t),placeholder:e.placeholder||`请选择`,clearable:``,size:l.value,options:N.value.get(e.prop)?.options},{ref_for:!0},e.attrs),null,16,[`value`,`onUpdate:value`,`placeholder`,`size`,`options`])):(0,s.createCommentVNode)(`v-if`,!0),e.type===`date-range`?((0,s.openBlock)(),(0,s.createBlock)((0,s.unref)(c.NDatePicker),(0,s.mergeProps)({key:3,type:`datetimerange`,size:l.value},{ref_for:!0},{...L(e.prop),...e.attrs},{format:`yyyy-MM-dd HH:mm`,"value-format":`yyyy-MM-dd HH:mm`,"start-placeholder":`开始时间`,"end-placeholder":`结束时间`,clearable:``}),null,16,[`size`])):(0,s.createCommentVNode)(`v-if`,!0)]),_:2},1032,[`path`,`rule`]))])),[[s.vShow,(0,s.unref)(C)||t<(0,s.unref)(E)]])),128)),(0,s.createElementVNode)(`div`,y,[(0,s.createElementVNode)(`div`,b,[n[6]||=(0,s.createElementVNode)(`div`,{class:`button-label-placeholder`},null,-1),(0,s.createVNode)((0,s.unref)(c.NSpace),{size:t.config?.density?o.value.gap:8},{default:(0,s.withCtx)(()=>[(0,s.createVNode)((0,s.unref)(c.NTooltip),{trigger:`hover`},{trigger:(0,s.withCtx)(()=>[(0,s.createVNode)((0,s.unref)(c.NButton),{type:`primary`,size:l.value,"aria-label":`搜索`,onClick:(0,s.unref)(k),loading:t.loading||(0,s.unref)(w)},{icon:(0,s.withCtx)(()=>[...n[2]||=[(0,s.createElementVNode)(`div`,{class:`i-mdi:search w-4 h-4`},null,-1)]]),_:1},8,[`size`,`onClick`,`loading`])]),default:(0,s.withCtx)(()=>[n[3]||=(0,s.createTextVNode)(` 搜索 `,-1)]),_:1}),(0,s.createVNode)((0,s.unref)(c.NTooltip),{trigger:`hover`},{trigger:(0,s.withCtx)(()=>[(0,s.createVNode)((0,s.unref)(c.NButton),{size:l.value,"aria-label":`重置`,onClick:(0,s.unref)(A)},{icon:(0,s.withCtx)(()=>[...n[4]||=[(0,s.createElementVNode)(`div`,{class:`i-mdi:refresh w-4 h-4`},null,-1)]]),_:1},8,[`size`,`onClick`])]),default:(0,s.withCtx)(()=>[n[5]||=(0,s.createTextVNode)(` 重置 `,-1)]),_:1}),(0,s.unref)(D)?((0,s.openBlock)(),(0,s.createBlock)((0,s.unref)(c.NTooltip),{key:0,trigger:`hover`},{trigger:(0,s.withCtx)(()=>[(0,s.createVNode)((0,s.unref)(c.NButton),{size:l.value,"aria-label":(0,s.unref)(C)?`收起搜索条件`:`展开搜索条件`,"aria-expanded":(0,s.unref)(C),onClick:(0,s.unref)(j)},{icon:(0,s.withCtx)(()=>[(0,s.createElementVNode)(`div`,{class:(0,s.normalizeClass)((0,s.unref)(C)?`i-mdi:chevron-up w-4 h-4`:`i-mdi:chevron-down w-4 h-4`)},null,2)]),_:1},8,[`size`,`aria-label`,`aria-expanded`,`onClick`])]),default:(0,s.withCtx)(()=>[(0,s.createTextVNode)(` `+(0,s.toDisplayString)((0,s.unref)(C)?`收起`:`展开`),1)]),_:1})):(0,s.createCommentVNode)(`v-if`,!0)]),_:1},8,[`size`])])])]),_:1},8,[`model`,`size`,`onSubmit`])]),_:1},8,[`class`,`style`,`bordered`]))}});var S=r.t(x,[[`__scopeId`,`data-v-da3c9bb6`]]);Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return S}});
|
package/dist/C_FormSearch2.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./density.js";import{t}from"./useComponentLocale.js";import{t as n}from"./feedback.js";import{t as r}from"./export-helper.js";import{t as i}from"./useActionTask.js";import{i as a,t as o}from"./storage.js";import{a as s,t as c}from"./data.js";import{Fragment as l,computed as u,createBlock as d,createCommentVNode as f,createElementBlock as p,createElementVNode as m,createTextVNode as h,createVNode as g,defineComponent as _,getCurrentScope as v,mergeProps as y,nextTick as b,normalizeClass as x,normalizeStyle as S,onMounted as C,onScopeDispose as w,openBlock as T,ref as E,renderList as D,toDisplayString as O,toValue as k,unref as A,vShow as j,watch as M,withCtx as N,withDirectives as P,withModifiers as F}from"vue";import{NButton as I,NCard as L,NDatePicker as R,NForm as z,NFormItem as B,NIcon as V,NInput as H,NSelect as U,NSpace as ee,NTooltip as W}from"naive-ui";function G(e,t,n={}){let r=()=>k(n),i=u(()=>!!r().storageKey),s=t=>e.value.find(e=>e.prop===t),c=()=>{let{storageKey:t}=r();t&&a(t,Object.fromEntries(e.value.filter(e=>e.hisList?.length).map(e=>[e.prop,e.hisList])))},l=()=>{let{storageKey:e}=r();if(!e)return;let t=o(e);t&&(Array.isArray(t)?t.map(e=>[e.prop,e.hisList]):Object.entries(t)).forEach(([e,t])=>{let n=s(e);n&&Array.isArray(t)&&(n.hisList=[...t])})},d=t=>{e.value.forEach(e=>{e.isFocus=!!(e.hisList&&e.prop===t)})},f=e=>{l(),d(e)},p=()=>d(),m=(e,n)=>{s(n)?.type===`input`&&(t.value[n]=e,p())},h=(e,t)=>{let n=s(e);n?.hisList&&(n.hisList.splice(t,1),n.hisList.length===0&&(n.isFocus=!1),c())},g=e=>{let t=s(e);t?.hisList&&(t.hisList=[],t.isFocus=!1,c())},_=(e,t)=>{let n=e.indexOf(t);n>-1&&e.splice(n,1),e.unshift(t);let i=Math.max(1,r().maxItems??5);return e.length>i&&(e.length=i),e};return{enabled:i,handleFocus:f,closeAllPanels:p,selectHistoryItem:m,deleteHistoryItem:h,clearAllHistory:g,saveCurrentInputs:()=>{i.value&&(Object.keys(t.value).forEach(e=>{let n=t.value[e];if(n==null||n===``)return;let r=s(e);if(r?.type!==`input`||!r.hisList)return;let i=String(n).trim();i&&_(r.hisList,i)}),p(),c())},restoreFromStorage:l}}function K(e,t){return e.defaultValue===void 0?e.type===`input`?``:e.type===`select`&&e.attrs?.multiple===!0?[]:e.type===`select`&&e.attrs?.clearable===!1?c(t):null:c(e.defaultValue)}function q(e,t,n){return t??(e.type===`input`?``:e.type===`select`&&e.attrs?.multiple===!0?[]:e.type===`select`&&e.attrs?.clearable===!1?K(e,n):null)}function J(e,r){let a=u(()=>k(r.config)??{}),o=n(()=>a.value.feedback),{t:l}=t(()=>a.value.locale),d=E(null),f=u(()=>a.value.foldThreshold??7),p=u(()=>a.value.requireValue??!0),m=E(c(r.formItemList)),h=E(c(r.formParams)),g=E(!1),_=i(),y=u(()=>_.isRunning(`search`)),x=!0;v()&&w(()=>{x=!1});let S=G(m,h,r.historyOptions),T=u(()=>m.value.filter(e=>e.show!==!1)),D=u(()=>T.value.filter((e,t)=>g.value||t<f.value)),O=u(()=>T.value.length>f.value),A=e=>e==null||e===``||Array.isArray(e)&&e.length===0,j=()=>{let e=h.value;m.value.forEach(t=>{t.isFocus=!1,t.show!==!1&&(t.type===`select`&&!t.placeholder&&(t.placeholder=`请选择`),t.type!==`spacer`&&e[t.prop]===void 0&&(e[t.prop]=K(t,e[t.prop])))})},N=async()=>{try{return await d.value?.validate(),!0}catch{return O.value&&(g.value=!0),!1}},P=e=>x&&!k(r.loading)&&s(h.value,e),F=async()=>{let t=c(h.value);if(!await N()||!P(t))return!1;if(p.value&&!Object.entries(t).filter(([e])=>e!==`pageNum`&&e!==`pageSize`).some(([,e])=>!A(e)))return o.warning(l(`search.valueRequired`)),!1;if(S.saveCurrentInputs(),e(`search`,c(t)),!P(t))return!1;if(!a.value.onSearch)return!0;try{return await a.value.onSearch(t),x}catch(e){return x&&o.error(l(`search.failed`),e),!1}},I=async()=>{let e=await _.run(`search`,F,()=>!k(r.loading));return e.executed&&e.value},L=t=>{e(`change-params`,c(t)),e(`update:modelValue`,c(t))},R=()=>{H=!0;let e=++U;b(()=>{e===U&&(H=!1)})},z=()=>{d.value?.restoreValidation();let t=c(h.value),n=t;m.value.forEach(e=>{e.show===!1||e.type===`spacer`||e.prop===`pageNum`||e.prop===`pageSize`||(n[e.prop]=K(e,n[e.prop]))}),R(),h.value=t,L(t),e(`reset`,c(t))},B=()=>{g.value=!g.value},V=(e,t)=>{R(),m.value=c(e),h.value=c(t),j()},H=!1,U=0;return j(),M(h,e=>{H||L(e)},{deep:!0}),C(()=>{S.restoreFromStorage()}),{formRef:d,fields:m,formParams:h,expanded:g,searching:y,visibleFields:D,availableFields:T,foldThreshold:f,hasExpandButton:O,history:S,searchFn:I,resetFn:z,toggleFold:B,syncFromProps:V}}const Y=e=>{if(Array.isArray(e)&&e.length===2){if(e.every(e=>typeof e==`string`))return{mode:`formatted`,value:[e[0],e[1]]};if(e.every(e=>typeof e==`number`&&Number.isFinite(e)))return{mode:`timestamp`,value:[e[0],e[1]]}}return{mode:`timestamp`,value:null}};function te(e,t){let n=e?.map(e=>e.value===void 0?e.label??e.labelDefault:e.value),r=Array.isArray(t)?t:[t],i=new Set([...n??[],...r]),a=new Map;for(let e of[!1,!0]){let t=`__c_form_search_boolean_${e}__`;for(;i.has(t);)t+=`_`;a.set(e,t),i.add(t)}let o=e=>typeof e==`boolean`?a.get(e):typeof e==`string`||typeof e==`number`?e:null,s=e=>e===a.get(!1)?!1:e===a.get(!0)?!0:e;return{options:e?.map((e,t)=>({...e,label:e.label??e.labelDefault??``,value:o(n[t])??void 0})),value:Array.isArray(t)?t.map(o).filter(e=>e!==null):o(t),decode:e=>Array.isArray(e)?e.map(s):e===null?null:s(e)}}const ne=[`onClick`],re={class:`history-text`},ie={key:0,class:`history-footer`},ae=[`onClick`],oe={class:`form-search-item-box form-search-actions`},se={class:`form-item-input`};var X=r(_({name:`C_FormSearch`,__name:`index`,props:{bordered:{type:Boolean,default:!0},formItemList:{default:()=>[]},formParams:{default:()=>({})},modelValue:{},formSearchInputHistoryString:{},size:{},loading:{type:Boolean,default:!1},config:{}},emits:[`search`,`reset`,`change-params`,`update:modelValue`],setup(t,{expose:n,emit:r}){let i=t,a=r,o=u(()=>e(i.config?.density)),s=u(()=>i.size??o.value.size),c=u(()=>{if(i.config?.density===void 0)return;let{gap:e,rowGap:t,padding:n}=o.value;return{"--c-search-gap":`${e}px`,"--c-search-row-gap":`${t}px`,"--c-search-padding":`${n}px`}}),{formRef:_,formParams:v,expanded:b,searching:C,availableFields:w,foldThreshold:E,hasExpandButton:k,history:G,searchFn:K,resetFn:X,toggleFold:Z,syncFromProps:ce}=J(a,{formItemList:i.formItemList,formParams:i.modelValue??i.formParams,config:()=>i.config,loading:()=>i.loading,historyOptions:()=>({storageKey:i.formSearchInputHistoryString,maxItems:i.config?.historyMaxItems})}),Q=u(()=>new Map(w.value.filter(e=>e.type===`select`).map(e=>[e.prop,te(e.list,v.value[e.prop])]))),le=e=>{let t=v.value[e];return typeof t==`string`?t:null},ue=(e,t)=>{$(e,Q.value.get(e)?.decode(t)??null)},$=(e,t)=>{let n=w.value.find(t=>t.prop===e),r=v.value;n&&(r[e]=q(n,t,r[e]))},de=e=>{let t=Y(v.value[e]);return t.mode===`formatted`?{formattedValue:t.value,"onUpdate:formattedValue":t=>$(e,t)}:{value:t.value,"onUpdate:value":t=>$(e,t)}};return M(()=>[i.formItemList,i.modelValue,i.formParams],([e,t,n])=>ce(e,t??n),{deep:!0}),n({formRef:_,formParams:v,searchFn:K,cleanFn:X,changeFoldState:Z}),(e,n)=>(T(),d(A(L),{class:x([`form-search-card custom-card`,t.config?.density&&`form-search-card--${t.config.density}`]),style:S(c.value),bordered:t.bordered},{default:N(()=>[g(A(z),{class:`form-search`,model:A(v),ref_key:`formRef`,ref:_,size:s.value,onSubmit:F(A(K),[`prevent`])},{default:N(()=>[(T(!0),p(l,null,D(A(w),(e,t)=>P((T(),p(`div`,{class:`form-search-item-box`,key:e.prop||t},[e.type===`spacer`?f(`v-if`,!0):(T(),d(A(B),{key:0,class:`form-item-input`,path:e.prop,rule:e.rules,"show-feedback":!1,"show-label":!1},{default:N(()=>[e.type===`input`?(T(),d(A(H),y({key:0,clearable:``,size:s.value,value:le(e.prop),"onUpdate:value":t=>$(e.prop,t),placeholder:e.placeholder},{ref_for:!0},e.attrs,{onFocus:t=>A(G).handleFocus(e.prop),onBlur:A(G).closeAllPanels}),null,16,[`size`,`value`,`onUpdate:value`,`placeholder`,`onFocus`,`onBlur`])):f(`v-if`,!0),e.isFocus?(T(),p(`div`,{key:1,class:`input-history`,onMousedown:n[0]||=F(()=>{},[`prevent`])},[(T(!0),p(l,null,D(e.hisList,(t,r)=>(T(),p(`div`,{class:`history-item`,onClick:n=>A(G).selectHistoryItem(t,e.prop),key:t},[m(`span`,re,O(t),1),g(A(V),{class:`delete-icon`,size:`14`,onClick:F(t=>A(G).deleteHistoryItem(e.prop,r),[`stop`])},{default:N(()=>[...n[1]||=[m(`div`,{class:`i-mdi:close`},null,-1)]]),_:1},8,[`onClick`])],8,ne))),128)),e.hisList&&e.hisList.length>0?(T(),p(`div`,ie,[m(`span`,{class:`clear-all`,onClick:F(t=>A(G).clearAllHistory(e.prop),[`stop`])},` 清空历史记录 `,8,ae)])):f(`v-if`,!0)],32)):f(`v-if`,!0),e.type===`select`?(T(),d(A(U),y({key:2,value:Q.value.get(e.prop)?.value,"onUpdate:value":t=>ue(e.prop,t),placeholder:e.placeholder||`请选择`,clearable:``,size:s.value,options:Q.value.get(e.prop)?.options},{ref_for:!0},e.attrs),null,16,[`value`,`onUpdate:value`,`placeholder`,`size`,`options`])):f(`v-if`,!0),e.type===`date-range`?(T(),d(A(R),y({key:3,type:`datetimerange`,size:s.value},{ref_for:!0},{...de(e.prop),...e.attrs},{format:`yyyy-MM-dd HH:mm`,"value-format":`yyyy-MM-dd HH:mm`,"start-placeholder":`开始时间`,"end-placeholder":`结束时间`,clearable:``}),null,16,[`size`])):f(`v-if`,!0)]),_:2},1032,[`path`,`rule`]))])),[[j,A(b)||t<A(E)]])),128)),m(`div`,oe,[m(`div`,se,[n[6]||=m(`div`,{class:`button-label-placeholder`},null,-1),g(A(ee),{size:t.config?.density?o.value.gap:8},{default:N(()=>[g(A(W),{trigger:`hover`},{trigger:N(()=>[g(A(I),{type:`primary`,size:s.value,"aria-label":`搜索`,onClick:A(K),loading:t.loading||A(C)},{icon:N(()=>[...n[2]||=[m(`div`,{class:`i-mdi:search w-4 h-4`},null,-1)]]),_:1},8,[`size`,`onClick`,`loading`])]),default:N(()=>[n[3]||=h(` 搜索 `,-1)]),_:1}),g(A(W),{trigger:`hover`},{trigger:N(()=>[g(A(I),{size:s.value,"aria-label":`重置`,onClick:A(X)},{icon:N(()=>[...n[4]||=[m(`div`,{class:`i-mdi:refresh w-4 h-4`},null,-1)]]),_:1},8,[`size`,`onClick`])]),default:N(()=>[n[5]||=h(` 重置 `,-1)]),_:1}),A(k)?(T(),d(A(W),{key:0,trigger:`hover`},{trigger:N(()=>[g(A(I),{size:s.value,"aria-label":A(b)?`收起搜索条件`:`展开搜索条件`,"aria-expanded":A(b),onClick:A(Z)},{icon:N(()=>[m(`div`,{class:x(A(b)?`i-mdi:chevron-up w-4 h-4`:`i-mdi:chevron-down w-4 h-4`)},null,2)]),_:1},8,[`size`,`aria-label`,`aria-expanded`,`onClick`])]),default:N(()=>[h(` `+O(A(b)?`收起`:`展开`),1)]),_:1})):f(`v-if`,!0)]),_:1},8,[`size`])])])]),_:1},8,[`model`,`size`,`onSubmit`])]),_:1},8,[`class`,`style`,`bordered`]))}}),[[`__scopeId`,`data-v-da3c9bb6`]]);export{J as n,G as r,X as t};
|