From 11b3e7c41f03e20dce166969718bfab33f6b32ec Mon Sep 17 00:00:00 2001 From: shaohuzhang1 Date: Tue, 25 Aug 2026 18:43:03 +0800 Subject: [PATCH] feat: dynamics-form --- ui/package.json | 1 + ui/src/api/types/common.ts | 3 + ui/src/components/mk-dynamics-form/Demo.vue | 357 ++++++++++++++ .../mk-dynamics-form/DemoConstructor.vue | 217 +++++++++ .../components/mk-dynamics-form/FormItem.vue | 240 +++++++++ .../mk-dynamics-form/FormItemLabel.vue | 11 + .../mk-dynamics-form/constructor/data.ts | 20 + .../mk-dynamics-form/constructor/index.vue | 291 +++++++++++ .../items/DatePickerConstructor.vue | 144 ++++++ .../items/JsonInputConstructor.vue | 171 +++++++ .../items/KnowledgeConstructor.vue | 163 +++++++ .../constructor/items/ModelConstructor.vue | 288 +++++++++++ .../constructor/items/MultiRowConstructor.vue | 238 +++++++++ .../items/MultiSelectConstructor.vue | 246 ++++++++++ .../items/PasswordInputConstructor.vue | 193 ++++++++ .../items/RadioCardConstructor.vue | 238 +++++++++ .../constructor/items/RadioRowConstructor.vue | 238 +++++++++ .../items/SingleSelectConstructor.vue | 242 +++++++++ .../constructor/items/SliderConstructor.vue | 168 +++++++ .../items/SwitchInputConstructor.vue | 54 +++ .../items/TextInputConstructor.vue | 181 +++++++ .../items/TextareaInputConstructor.vue | 194 ++++++++ .../items/TreeSelectConstructor.vue | 458 ++++++++++++++++++ .../items/UploadInputConstructor.vue | 153 ++++++ .../mk-dynamics-form/constructor/type.ts | 15 + ui/src/components/mk-dynamics-form/index.ts | 25 + ui/src/components/mk-dynamics-form/index.vue | 325 +++++++++++++ .../mk-dynamics-form/items/DatePicker.vue | 5 + .../mk-dynamics-form/items/JsonInput.vue | 142 ++++++ .../mk-dynamics-form/items/MultiRow.vue | 112 +++++ .../mk-dynamics-form/items/PasswordInput.vue | 5 + .../mk-dynamics-form/items/TextInput.vue | 5 + .../mk-dynamics-form/items/TextareaInput.vue | 5 + .../items/common/SelectHeader.vue | 30 ++ .../items/complex/ArrayObjectCard.vue | 160 ++++++ .../items/complex/ObjectCard.vue | 75 +++ .../items/complex/TabCard.vue | 127 +++++ .../items/knowledge/Knowledge.vue | 71 +++ .../items/label/SettingLabel.vue | 97 ++++ .../items/label/TooltipLabel.vue | 19 + .../items/layout/RowLayout.vue | 18 + .../mk-dynamics-form/items/model/Model.vue | 147 ++++++ .../items/model/provider-data.ts | 112 +++++ .../mk-dynamics-form/items/radio/Radio.vue | 38 ++ .../items/radio/RadioButton.vue | 38 ++ .../items/radio/RadioCard.vue | 105 ++++ .../mk-dynamics-form/items/radio/RadioRow.vue | 92 ++++ .../items/select/MultiSelect.vue | 71 +++ .../items/select/SingleSelect.vue | 81 ++++ .../mk-dynamics-form/items/slider/Slider.vue | 6 + .../items/switch/SwitchInput.vue | 7 + .../items/table/ProgressTableItem.vue | 76 +++ .../items/table/TableCheckbox.vue | 214 ++++++++ .../items/table/TableColumn.vue | 22 + .../items/table/TableRadio.vue | 202 ++++++++ .../mk-dynamics-form/items/tree/Tree.vue | 221 +++++++++ .../items/tree/TreeSelect.vue | 11 + .../items/upload/LocalFileUpload.vue | 317 ++++++++++++ .../items/upload/UploadInput.vue | 288 +++++++++++ ui/src/components/mk-dynamics-form/type.ts | 205 ++++++++ .../visibility/ConditionRow.vue | 132 +++++ .../visibility/Constructor.vue | 164 +++++++ .../visibility/FieldSelector.vue | 73 +++ .../mk-dynamics-form/visibility/field-type.ts | 66 +++ .../mk-dynamics-form/visibility/index.ts | 188 +++++++ ui/src/utils/bus.ts | 9 + ui/src/utils/common.ts | 26 + 67 files changed, 8656 insertions(+) create mode 100644 ui/src/components/mk-dynamics-form/Demo.vue create mode 100644 ui/src/components/mk-dynamics-form/DemoConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/FormItem.vue create mode 100644 ui/src/components/mk-dynamics-form/FormItemLabel.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/data.ts create mode 100644 ui/src/components/mk-dynamics-form/constructor/index.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/items/DatePickerConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/items/JsonInputConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/items/KnowledgeConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/items/ModelConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/items/MultiRowConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/items/MultiSelectConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/items/PasswordInputConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/items/RadioCardConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/items/RadioRowConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/items/SingleSelectConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/items/SliderConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/items/SwitchInputConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/items/TextInputConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/items/TextareaInputConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/items/TreeSelectConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/items/UploadInputConstructor.vue create mode 100644 ui/src/components/mk-dynamics-form/constructor/type.ts create mode 100644 ui/src/components/mk-dynamics-form/index.ts create mode 100644 ui/src/components/mk-dynamics-form/index.vue create mode 100644 ui/src/components/mk-dynamics-form/items/DatePicker.vue create mode 100644 ui/src/components/mk-dynamics-form/items/JsonInput.vue create mode 100644 ui/src/components/mk-dynamics-form/items/MultiRow.vue create mode 100644 ui/src/components/mk-dynamics-form/items/PasswordInput.vue create mode 100644 ui/src/components/mk-dynamics-form/items/TextInput.vue create mode 100644 ui/src/components/mk-dynamics-form/items/TextareaInput.vue create mode 100644 ui/src/components/mk-dynamics-form/items/common/SelectHeader.vue create mode 100644 ui/src/components/mk-dynamics-form/items/complex/ArrayObjectCard.vue create mode 100644 ui/src/components/mk-dynamics-form/items/complex/ObjectCard.vue create mode 100644 ui/src/components/mk-dynamics-form/items/complex/TabCard.vue create mode 100644 ui/src/components/mk-dynamics-form/items/knowledge/Knowledge.vue create mode 100644 ui/src/components/mk-dynamics-form/items/label/SettingLabel.vue create mode 100644 ui/src/components/mk-dynamics-form/items/label/TooltipLabel.vue create mode 100644 ui/src/components/mk-dynamics-form/items/layout/RowLayout.vue create mode 100644 ui/src/components/mk-dynamics-form/items/model/Model.vue create mode 100644 ui/src/components/mk-dynamics-form/items/model/provider-data.ts create mode 100644 ui/src/components/mk-dynamics-form/items/radio/Radio.vue create mode 100644 ui/src/components/mk-dynamics-form/items/radio/RadioButton.vue create mode 100644 ui/src/components/mk-dynamics-form/items/radio/RadioCard.vue create mode 100644 ui/src/components/mk-dynamics-form/items/radio/RadioRow.vue create mode 100644 ui/src/components/mk-dynamics-form/items/select/MultiSelect.vue create mode 100644 ui/src/components/mk-dynamics-form/items/select/SingleSelect.vue create mode 100644 ui/src/components/mk-dynamics-form/items/slider/Slider.vue create mode 100644 ui/src/components/mk-dynamics-form/items/switch/SwitchInput.vue create mode 100644 ui/src/components/mk-dynamics-form/items/table/ProgressTableItem.vue create mode 100644 ui/src/components/mk-dynamics-form/items/table/TableCheckbox.vue create mode 100644 ui/src/components/mk-dynamics-form/items/table/TableColumn.vue create mode 100644 ui/src/components/mk-dynamics-form/items/table/TableRadio.vue create mode 100644 ui/src/components/mk-dynamics-form/items/tree/Tree.vue create mode 100644 ui/src/components/mk-dynamics-form/items/tree/TreeSelect.vue create mode 100644 ui/src/components/mk-dynamics-form/items/upload/LocalFileUpload.vue create mode 100644 ui/src/components/mk-dynamics-form/items/upload/UploadInput.vue create mode 100644 ui/src/components/mk-dynamics-form/type.ts create mode 100644 ui/src/components/mk-dynamics-form/visibility/ConditionRow.vue create mode 100644 ui/src/components/mk-dynamics-form/visibility/Constructor.vue create mode 100644 ui/src/components/mk-dynamics-form/visibility/FieldSelector.vue create mode 100644 ui/src/components/mk-dynamics-form/visibility/field-type.ts create mode 100644 ui/src/components/mk-dynamics-form/visibility/index.ts create mode 100644 ui/src/utils/bus.ts diff --git a/ui/package.json b/ui/package.json index e8df7319bbd..b00ec03117a 100644 --- a/ui/package.json +++ b/ui/package.json @@ -17,6 +17,7 @@ }, "dependencies": { "@antv/layout": "^2.0.0", + "@codemirror/lang-json": "^6.0.2", "@codemirror/lang-python": "^6.2.1", "@codemirror/lint": "^6.9.7", "@codemirror/state": "^6.7.1", diff --git a/ui/src/api/types/common.ts b/ui/src/api/types/common.ts index ee58c403725..cac0afdad41 100644 --- a/ui/src/api/types/common.ts +++ b/ui/src/api/types/common.ts @@ -1,6 +1,9 @@ /** 多个公共组件共同使用的通用选项类型。 */ export type RequestParams = Record +/** 键为字符串的通用字典类型。 */ +export type Dict = Record + /** 通用的 ID + 名称选项,用于下拉列表、标签等场景。 */ export interface ListItem { id: string diff --git a/ui/src/components/mk-dynamics-form/Demo.vue b/ui/src/components/mk-dynamics-form/Demo.vue new file mode 100644 index 00000000000..ac30f9893d6 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/Demo.vue @@ -0,0 +1,357 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/DemoConstructor.vue b/ui/src/components/mk-dynamics-form/DemoConstructor.vue new file mode 100644 index 00000000000..abeeb631954 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/DemoConstructor.vue @@ -0,0 +1,217 @@ + + + + + diff --git a/ui/src/components/mk-dynamics-form/FormItem.vue b/ui/src/components/mk-dynamics-form/FormItem.vue new file mode 100644 index 00000000000..67d4ed129b0 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/FormItem.vue @@ -0,0 +1,240 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/FormItemLabel.vue b/ui/src/components/mk-dynamics-form/FormItemLabel.vue new file mode 100644 index 00000000000..c244fe8ff06 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/FormItemLabel.vue @@ -0,0 +1,11 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/data.ts b/ui/src/components/mk-dynamics-form/constructor/data.ts new file mode 100644 index 00000000000..322d5437792 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/data.ts @@ -0,0 +1,20 @@ +const input_type_list = [ + { label: '文本框', value: 'TextInput' }, + { label: '多行文本框', value: 'TextareaInput' }, + { label: 'JSON 文本框', value: 'JsonInput' }, + { label: '密码框', value: 'PasswordInput' }, + { label: '单选框', value: 'SingleSelect' }, + { label: '多选框', value: 'MultiSelect' }, + { label: '选项卡', value: 'RadioCard' }, + { label: '单行选项卡', value: 'RadioRow' }, + { label: '单行多选卡', value: 'MultiRow' }, + { label: '滑块', value: 'Slider' }, + { label: '开关', value: 'SwitchInput' }, + { label: '日期', value: 'DatePicker' }, + { label: '文件上传', value: 'UploadInput' }, + { label: '模型', value: 'Model' }, + { label: '知识库', value: 'Knowledge' }, + { label: '树形选择器', value: 'TreeSelect' }, +] + +export { input_type_list } diff --git a/ui/src/components/mk-dynamics-form/constructor/index.vue b/ui/src/components/mk-dynamics-form/constructor/index.vue new file mode 100644 index 00000000000..66f1c30cbc2 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/index.vue @@ -0,0 +1,291 @@ + + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/items/DatePickerConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/DatePickerConstructor.vue new file mode 100644 index 00000000000..70306485440 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/items/DatePickerConstructor.vue @@ -0,0 +1,144 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/items/JsonInputConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/JsonInputConstructor.vue new file mode 100644 index 00000000000..3bbc36e0746 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/items/JsonInputConstructor.vue @@ -0,0 +1,171 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/items/KnowledgeConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/KnowledgeConstructor.vue new file mode 100644 index 00000000000..31a05f9c43c --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/items/KnowledgeConstructor.vue @@ -0,0 +1,163 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/items/ModelConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/ModelConstructor.vue new file mode 100644 index 00000000000..c9bfec90688 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/items/ModelConstructor.vue @@ -0,0 +1,288 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/items/MultiRowConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/MultiRowConstructor.vue new file mode 100644 index 00000000000..d41c566d987 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/items/MultiRowConstructor.vue @@ -0,0 +1,238 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/items/MultiSelectConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/MultiSelectConstructor.vue new file mode 100644 index 00000000000..44301d1e8ea --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/items/MultiSelectConstructor.vue @@ -0,0 +1,246 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/items/PasswordInputConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/PasswordInputConstructor.vue new file mode 100644 index 00000000000..9f54040f079 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/items/PasswordInputConstructor.vue @@ -0,0 +1,193 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/items/RadioCardConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/RadioCardConstructor.vue new file mode 100644 index 00000000000..ac76bc4441c --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/items/RadioCardConstructor.vue @@ -0,0 +1,238 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/items/RadioRowConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/RadioRowConstructor.vue new file mode 100644 index 00000000000..e918cd3485f --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/items/RadioRowConstructor.vue @@ -0,0 +1,238 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/items/SingleSelectConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/SingleSelectConstructor.vue new file mode 100644 index 00000000000..37b7f372c0b --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/items/SingleSelectConstructor.vue @@ -0,0 +1,242 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/items/SliderConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/SliderConstructor.vue new file mode 100644 index 00000000000..54e389807e0 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/items/SliderConstructor.vue @@ -0,0 +1,168 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/items/SwitchInputConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/SwitchInputConstructor.vue new file mode 100644 index 00000000000..63e44d9d102 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/items/SwitchInputConstructor.vue @@ -0,0 +1,54 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/items/TextInputConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/TextInputConstructor.vue new file mode 100644 index 00000000000..197b448ea45 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/items/TextInputConstructor.vue @@ -0,0 +1,181 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/items/TextareaInputConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/TextareaInputConstructor.vue new file mode 100644 index 00000000000..68f0248929e --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/items/TextareaInputConstructor.vue @@ -0,0 +1,194 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/items/TreeSelectConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/TreeSelectConstructor.vue new file mode 100644 index 00000000000..790b0ddb142 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/items/TreeSelectConstructor.vue @@ -0,0 +1,458 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/items/UploadInputConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/UploadInputConstructor.vue new file mode 100644 index 00000000000..ff99dcdb4d0 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/items/UploadInputConstructor.vue @@ -0,0 +1,153 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/constructor/type.ts b/ui/src/components/mk-dynamics-form/constructor/type.ts new file mode 100644 index 00000000000..e1d445d33d0 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/constructor/type.ts @@ -0,0 +1,15 @@ +interface LeftOptions { + label: string + value: string + icon?: any + type?: string + self?: boolean + children?: Array + /** + * 叶子字段配置,供 visibility 推断运算符与值编辑器 + */ + input_type?: string + option_list?: Array + attrs?: Record +} +export { type LeftOptions } diff --git a/ui/src/components/mk-dynamics-form/index.ts b/ui/src/components/mk-dynamics-form/index.ts new file mode 100644 index 00000000000..415a3004963 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/index.ts @@ -0,0 +1,25 @@ +import type { App } from 'vue' +import type { Dict } from '@/api/types/common' +import DynamicsForm from '@/components/mk-dynamics-form/index.vue' +let components: Dict = import.meta.glob('@/components/mk-dynamics-form/**/**.vue', { + eager: true, +}) +components = { + ...components, + ...import.meta.glob('@/components/mk-dynamics-form/**/**/**.vue', { + eager: true, + }), +} + +const install = (app: App) => { + Object.keys(components).forEach((key: string) => { + const commentName: string = key + .substring(key.lastIndexOf('/') + 1, key.length) + .replace('.vue', '') + if (key !== '/src/components/mk-dynamics-form/constructor/index.vue') { + app.component(commentName, components[key].default) + } + }) + app.component('DynamicsForm', DynamicsForm) +} +export default { install } diff --git a/ui/src/components/mk-dynamics-form/index.vue b/ui/src/components/mk-dynamics-form/index.vue new file mode 100644 index 00000000000..5226c883ae7 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/index.vue @@ -0,0 +1,325 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/DatePicker.vue b/ui/src/components/mk-dynamics-form/items/DatePicker.vue new file mode 100644 index 00000000000..291978c9c52 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/DatePicker.vue @@ -0,0 +1,5 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/JsonInput.vue b/ui/src/components/mk-dynamics-form/items/JsonInput.vue new file mode 100644 index 00000000000..59502cf5770 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/JsonInput.vue @@ -0,0 +1,142 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/MultiRow.vue b/ui/src/components/mk-dynamics-form/items/MultiRow.vue new file mode 100644 index 00000000000..2012d36b731 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/MultiRow.vue @@ -0,0 +1,112 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/PasswordInput.vue b/ui/src/components/mk-dynamics-form/items/PasswordInput.vue new file mode 100644 index 00000000000..2111d246152 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/PasswordInput.vue @@ -0,0 +1,5 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/TextInput.vue b/ui/src/components/mk-dynamics-form/items/TextInput.vue new file mode 100644 index 00000000000..46ca9b4e953 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/TextInput.vue @@ -0,0 +1,5 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/TextareaInput.vue b/ui/src/components/mk-dynamics-form/items/TextareaInput.vue new file mode 100644 index 00000000000..c477c96ec10 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/TextareaInput.vue @@ -0,0 +1,5 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/common/SelectHeader.vue b/ui/src/components/mk-dynamics-form/items/common/SelectHeader.vue new file mode 100644 index 00000000000..019593c3827 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/common/SelectHeader.vue @@ -0,0 +1,30 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/complex/ArrayObjectCard.vue b/ui/src/components/mk-dynamics-form/items/complex/ArrayObjectCard.vue new file mode 100644 index 00000000000..696a82c01e2 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/complex/ArrayObjectCard.vue @@ -0,0 +1,160 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/complex/ObjectCard.vue b/ui/src/components/mk-dynamics-form/items/complex/ObjectCard.vue new file mode 100644 index 00000000000..ea285b6fc84 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/complex/ObjectCard.vue @@ -0,0 +1,75 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/complex/TabCard.vue b/ui/src/components/mk-dynamics-form/items/complex/TabCard.vue new file mode 100644 index 00000000000..dcef4ba4018 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/complex/TabCard.vue @@ -0,0 +1,127 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/knowledge/Knowledge.vue b/ui/src/components/mk-dynamics-form/items/knowledge/Knowledge.vue new file mode 100644 index 00000000000..fe7b3b06393 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/knowledge/Knowledge.vue @@ -0,0 +1,71 @@ + + + + diff --git a/ui/src/components/mk-dynamics-form/items/label/SettingLabel.vue b/ui/src/components/mk-dynamics-form/items/label/SettingLabel.vue new file mode 100644 index 00000000000..2369117ca0c --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/label/SettingLabel.vue @@ -0,0 +1,97 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/label/TooltipLabel.vue b/ui/src/components/mk-dynamics-form/items/label/TooltipLabel.vue new file mode 100644 index 00000000000..baff107f45b --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/label/TooltipLabel.vue @@ -0,0 +1,19 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/layout/RowLayout.vue b/ui/src/components/mk-dynamics-form/items/layout/RowLayout.vue new file mode 100644 index 00000000000..95f64faa0d3 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/layout/RowLayout.vue @@ -0,0 +1,18 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/model/Model.vue b/ui/src/components/mk-dynamics-form/items/model/Model.vue new file mode 100644 index 00000000000..f0d6c259e46 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/model/Model.vue @@ -0,0 +1,147 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/model/provider-data.ts b/ui/src/components/mk-dynamics-form/items/model/provider-data.ts new file mode 100644 index 00000000000..5e7b39b82c6 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/model/provider-data.ts @@ -0,0 +1,112 @@ +export const providerList = [ + { + "provider": "model_azure_provider", + "name": "Azure OpenAI", + "icon": "" + }, + { + "provider": "model_wenxin_provider", + "name": "千帆大模型", + "icon": "\n\n\n\n" + }, + { + "provider": "model_ollama_provider", + "name": "Ollama", + "icon": " \n\n" + }, + { + "provider": "model_openai_provider", + "name": "OpenAI", + "icon": "" + }, + { + "provider": "model_docker_ai_provider", + "name": "Docker AI", + "icon": "\n\n\n" + }, + { + "provider": "model_kimi_provider", + "name": "Kimi", + "icon": "" + }, + { + "provider": "model_zhipu_provider", + "name": "智谱 AI", + "icon": "" + }, + { + "provider": "model_xf_provider", + "name": "讯飞星火", + "icon": "" + }, + { + "provider": "model_deepseek_provider", + "name": "DeepSeek", + "icon": "\n\t\n" + }, + { + "provider": "model_gemini_provider", + "name": "Gemini", + "icon": "" + }, + { + "provider": "model_volcanic_engine_provider", + "name": "火山引擎", + "icon": "\n\n \n\n" + }, + { + "provider": "model_tencent_provider", + "name": "腾讯混元", + "icon": "\n\n \n\n" + }, + { + "provider": "model_tencent_cloud_provider", + "name": "腾讯云", + "icon": "\n\n\n \n \n \n" + }, + { + "provider": "model_aws_bedrock_provider", + "name": "Amazon Bedrock", + "icon": "" + }, + { + "provider": "model_local_provider", + "name": "本地模型", + "icon": "\n\n\n" + }, + { + "provider": "model_xinference_provider", + "name": "Xorbits Inference", + "icon": "\n\n \n\n" + }, + { + "provider": "model_vllm_provider", + "name": "vLLM", + "icon": "\n\n \n\n" + }, + { + "provider": "aliyun_bai_lian_model_provider", + "name": "阿里云百炼", + "icon": "【icon】阿里百炼大模型" + }, + { + "provider": "model_anthropic_provider", + "name": "Anthropic", + "icon": "" + }, + { + "provider": "model_siliconCloud_provider", + "name": "SILICONFLOW", + "icon": "\n\n\n\n\n" + }, + { + "provider": "model_regolo_provider", + "name": "Regolo", + "icon": "\n\n \n \n \n .cls-1 {\n fill: #303030;\n }\n\n .cls-2 {\n fill: #59e389;\n }\n \n \n \n \n \n \n \n \n \n\n" + }, + { + "provider": "model_minimax_provider", + "name": "MiniMax", + "icon": "" + } +] diff --git a/ui/src/components/mk-dynamics-form/items/radio/Radio.vue b/ui/src/components/mk-dynamics-form/items/radio/Radio.vue new file mode 100644 index 00000000000..a801755a9cf --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/radio/Radio.vue @@ -0,0 +1,38 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/radio/RadioButton.vue b/ui/src/components/mk-dynamics-form/items/radio/RadioButton.vue new file mode 100644 index 00000000000..ba45ec4ef21 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/radio/RadioButton.vue @@ -0,0 +1,38 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/radio/RadioCard.vue b/ui/src/components/mk-dynamics-form/items/radio/RadioCard.vue new file mode 100644 index 00000000000..56e6220f995 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/radio/RadioCard.vue @@ -0,0 +1,105 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/radio/RadioRow.vue b/ui/src/components/mk-dynamics-form/items/radio/RadioRow.vue new file mode 100644 index 00000000000..a1d28ba4e43 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/radio/RadioRow.vue @@ -0,0 +1,92 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/select/MultiSelect.vue b/ui/src/components/mk-dynamics-form/items/select/MultiSelect.vue new file mode 100644 index 00000000000..4f6443d48a7 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/select/MultiSelect.vue @@ -0,0 +1,71 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/select/SingleSelect.vue b/ui/src/components/mk-dynamics-form/items/select/SingleSelect.vue new file mode 100644 index 00000000000..fae256aea30 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/select/SingleSelect.vue @@ -0,0 +1,81 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/slider/Slider.vue b/ui/src/components/mk-dynamics-form/items/slider/Slider.vue new file mode 100644 index 00000000000..000245a72d1 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/slider/Slider.vue @@ -0,0 +1,6 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/switch/SwitchInput.vue b/ui/src/components/mk-dynamics-form/items/switch/SwitchInput.vue new file mode 100644 index 00000000000..c787945f35a --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/switch/SwitchInput.vue @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/ui/src/components/mk-dynamics-form/items/table/ProgressTableItem.vue b/ui/src/components/mk-dynamics-form/items/table/ProgressTableItem.vue new file mode 100644 index 00000000000..0575c78f9fd --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/table/ProgressTableItem.vue @@ -0,0 +1,76 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/table/TableCheckbox.vue b/ui/src/components/mk-dynamics-form/items/table/TableCheckbox.vue new file mode 100644 index 00000000000..fee082cfde5 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/table/TableCheckbox.vue @@ -0,0 +1,214 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/table/TableColumn.vue b/ui/src/components/mk-dynamics-form/items/table/TableColumn.vue new file mode 100644 index 00000000000..9b6989e1f8a --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/table/TableColumn.vue @@ -0,0 +1,22 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/table/TableRadio.vue b/ui/src/components/mk-dynamics-form/items/table/TableRadio.vue new file mode 100644 index 00000000000..3bfc3b8644b --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/table/TableRadio.vue @@ -0,0 +1,202 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/tree/Tree.vue b/ui/src/components/mk-dynamics-form/items/tree/Tree.vue new file mode 100644 index 00000000000..a5a0b2d9db6 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/tree/Tree.vue @@ -0,0 +1,221 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/tree/TreeSelect.vue b/ui/src/components/mk-dynamics-form/items/tree/TreeSelect.vue new file mode 100644 index 00000000000..70671f28226 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/tree/TreeSelect.vue @@ -0,0 +1,11 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/upload/LocalFileUpload.vue b/ui/src/components/mk-dynamics-form/items/upload/LocalFileUpload.vue new file mode 100644 index 00000000000..a2c98aa223b --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/upload/LocalFileUpload.vue @@ -0,0 +1,317 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/items/upload/UploadInput.vue b/ui/src/components/mk-dynamics-form/items/upload/UploadInput.vue new file mode 100644 index 00000000000..330c4a704f3 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/items/upload/UploadInput.vue @@ -0,0 +1,288 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/type.ts b/ui/src/components/mk-dynamics-form/type.ts new file mode 100644 index 00000000000..49e3723c09a --- /dev/null +++ b/ui/src/components/mk-dynamics-form/type.ts @@ -0,0 +1,205 @@ +import type { Dict } from '@/api/types/common' + +interface ViewCardItem { + /** + * 类型 + */ + type: 'eval' | 'default' + /** + * 标题 + */ + title: string + /** + * 值 根据类型不一样 取值也不一样 default= row[value_field] eval `${parseFloat(row.number).toLocaleString("zh-CN",{style: "decimal",maximumFractionDigits:1})}%   ` + */ + value_field: string +} + +interface TableColumn { + /** + * 字段|组件名称|可计算的模板字符串 + */ + property: string + /** + *表头 + */ + label: string + /** + * 表数据字段 + */ + value_field?: string + + attrs?: Attrs + /** + * 类型 + */ + type: 'eval' | 'component' | 'default' + + props_info?: PropsInfo +} +interface ColorItem { + /** + * 颜色#f56c6c + */ + color: string + /** + * 进度 + */ + percentage: number +} +interface Attrs { + /** + * 提示语 + */ + placeholder?: string + /** + * 标签的长度,例如 '50px'。 作为 Form 直接子元素的 form-item 会继承该值。 可以使用 auto。 + */ + labelWidth?: string + /** + * 表单域标签的后缀 + */ + labelSuffix?: string + /** + * 星号的位置。 + */ + requireAsteriskPosition?: 'left' | 'right' + + color?: Array + + [propName: string]: any +} +interface PropsInfo { + /** + * 表格选择的card + */ + view_card?: Array + /** + * 表格选择 + */ + table_columns?: Array + /** + * 选中 message + */ + active_msg?: string + + /** + * 组件样式 + */ + style?: Dict + + /** + * el-form-item 样式 + */ + item_style?: Dict + /** + * 表单校验 这个和element校验一样 + */ + rules?: Dict + /** + * 默认 不为空校验提示 + */ + err_msg?: string + /** + *tabs的时候使用 + */ + tabs_label?: string + + [propName: string]: any +} +export type CompareOptions = + | 'eq' + | 'not_eq' + | 'contain' + | 'not_contain' + | 'is_true' + | 'is_not_true' + | 'gt' + | 'ge' + | 'lt' + | 'le' + +export interface VisibilityCondition { + id: string + field: [string, string] + self?: boolean + compare: CompareOptions | '' + value: any + leftValue?: any +} + +interface VisibilityRule { + action: 'show' | 'hide' + condition: 'and' | 'or' + conditions: VisibilityCondition[] +} + +interface FormField { + field: string + /** + * 输入框类型 + */ + input_type: string + /** + * 提示 + */ + label?: string | any + /** + * 是否 必填 + */ + required?: boolean + /** + * 默认值 + */ + default_value?: any + /** + * 是否显示默认值 + */ + show_default_value?: boolean + + /** + * 显隐设置 + */ + visibility_rules?: Array + /** + * {field:field_value_list} 表示在 field有值 ,并且值在field_value_list中才 执行函数获取 数据 + */ + relation_trigger_field_dict?: Dict + /** + * 执行器类型 OPTION_LIST请求Option_list数据 CHILD_FORMS请求子表单 + */ + trigger_type?: 'OPTION_LIST' | 'CHILD_FORMS' + /** + * 前端attr数据 + */ + attrs?: Attrs + /** + * 其他额外信息 + */ + props_info?: PropsInfo + /** + * 下拉选字段field + */ + text_field?: string + /** + * 下拉选 value + */ + value_field?: string + /** + * 下拉选数据 + */ + option_list?: Array + /** + * 供应商 + */ + provider?: string + /** + * 执行函数 + */ + method?: string + + children?: Array + required_asterisk?: boolean + [propName: string]: any +} +export type { FormField } diff --git a/ui/src/components/mk-dynamics-form/visibility/ConditionRow.vue b/ui/src/components/mk-dynamics-form/visibility/ConditionRow.vue new file mode 100644 index 00000000000..d00dbeec08c --- /dev/null +++ b/ui/src/components/mk-dynamics-form/visibility/ConditionRow.vue @@ -0,0 +1,132 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/visibility/Constructor.vue b/ui/src/components/mk-dynamics-form/visibility/Constructor.vue new file mode 100644 index 00000000000..7bb9641cf38 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/visibility/Constructor.vue @@ -0,0 +1,164 @@ + + + diff --git a/ui/src/components/mk-dynamics-form/visibility/FieldSelector.vue b/ui/src/components/mk-dynamics-form/visibility/FieldSelector.vue new file mode 100644 index 00000000000..12c0b13c6fd --- /dev/null +++ b/ui/src/components/mk-dynamics-form/visibility/FieldSelector.vue @@ -0,0 +1,73 @@ + + + + diff --git a/ui/src/components/mk-dynamics-form/visibility/field-type.ts b/ui/src/components/mk-dynamics-form/visibility/field-type.ts new file mode 100644 index 00000000000..69c7e9a2988 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/visibility/field-type.ts @@ -0,0 +1,66 @@ +import type { LeftOptions } from '../constructor/type' + +export type InferredFieldType = string | undefined + +export function inferFieldType( + fieldPath: [string, string] | Array, + leftOptions?: Array, +): InferredFieldType { + return getFieldConfig(fieldPath, leftOptions)?.input_type +} + +// input_type → 允许的运算符(按设计文档表格) +const TYPE_OP_MAP: Record> = { + SwitchInput: ['is_true', 'is_not_true'], + + SingleSelect: ['eq', 'not_eq'], + RadioCard: ['eq', 'not_eq'], + RadioRow: ['eq', 'not_eq'], + TreeSelect: ['eq', 'not_eq'], + Model: ['eq', 'not_eq'], + Knowledge: ['eq', 'not_eq'], + DatePicker: ['eq', 'not_eq'], + + MultiSelect: ['contain', 'not_contain'], + MultiRow: ['contain', 'not_contain'], + + TextInput: ['eq', 'not_eq', 'contain', 'not_contain'], + TextareaInput: ['eq', 'not_eq', 'contain', 'not_contain'], + PasswordInput: ['eq', 'not_eq', 'contain', 'not_contain'], + JsonInput: ['eq', 'not_eq', 'contain', 'not_contain'], + + Slider: ['eq', 'not_eq', 'gt', 'ge', 'lt', 'le'], +} + +const ALL_VISIBILITY_OPS = [ + 'eq', + 'not_eq', + 'contain', + 'not_contain', + 'is_true', + 'is_not_true', + 'gt', + 'ge', + 'lt', + 'le', +] + +export function getAllowedOps(inputType: string | undefined): Array { + if (!inputType) return ALL_VISIBILITY_OPS + return TYPE_OP_MAP[inputType] ?? ALL_VISIBILITY_OPS +} + +/** + * 根据 [scope_value, field_value] 在 leftOptions 树里取回叶子字段配置。 + * 推不出 → 返回 undefined + */ +export function getFieldConfig( + fieldPath: [string, string] | Array, + leftOptions?: Array, +): LeftOptions | undefined { + if (!fieldPath || fieldPath.length < 2) return undefined + const [scopeValue, fieldValue] = fieldPath + + const scope = (leftOptions ?? []).find((item) => item.value === scopeValue) + return scope?.children?.find((child) => child.value === fieldValue) +} diff --git a/ui/src/components/mk-dynamics-form/visibility/index.ts b/ui/src/components/mk-dynamics-form/visibility/index.ts new file mode 100644 index 00000000000..92bf02fb7f1 --- /dev/null +++ b/ui/src/components/mk-dynamics-form/visibility/index.ts @@ -0,0 +1,188 @@ +export type CompareOptions = + | 'eq' + | 'not_eq' + | 'contain' + | 'not_contain' + | 'is_true' + | 'is_not_true' + | 'gt' + | 'ge' + | 'lt' + | 'le' + +export interface VisibilityCondition { + id: string + field: [string, string] // [scope_or_node_id, field_name] + compare: CompareOptions | '' + value: any + _left?: any // cross node exist +} + +export interface VisibilityRules { + action: 'show' | 'hide' + condition: 'and' | 'or' + node_id?: string + node_name?: string + conditions: VisibilityCondition[] +} + +export interface VisibilityCtx { + formValue: Record + currentNodeId: string // field 同节点判读 node_id + currentNodeName: string // current node display name, {{currentNodeName.result}}, same form value. reference +} + +/** + * 解析 匹配值 残留的 {{}} + * + * 前端只处理 同 node 表单 引用 + * ex: 当前节点叫「表单收集」,{{表单收集.region}} → formValue.region + * + * 跨节点 {{开始.question}} / {{全局变量.x}} / {{chat.x}} 已由后端 form-node + * reset_field 阶段(过滤掉本节点的 field_list 后)通过 generate_prompt + * 预渲染为字面量,前端不会再看到这些形态。 + */ +export function resolveValue(raw: string, ctx: VisibilityCtx): string { + return raw.replace(/\{\{([^.\s}]+)\.([^.\s}]+)\}\}/g, (match, nodeName, fieldName) => { + if (nodeName !== ctx.currentNodeName) { + return match // 非同表单,前置node 引用 + } + const v = ctx.formValue?.[fieldName] + return v == null ? match : String(v) + }) +} + +export function lookupLeft(cond: VisibilityCondition, ctx: VisibilityCtx): any { + const scope = cond.field[0] === 'global' ? 'base-node' : cond.field[0] + if (scope === ctx.currentNodeId) { + return ctx.formValue?.[cond.field[1]] // 同节点:实时从 formValue 取 + } + return (cond as any)._left // 跨节点:后端 返回 +} + +type CmpFn = (left: any, right: any) => boolean + +const compareHandlers: Record = { + eq: (l, r) => String(l) === String(r), + not_eq: (l, r) => String(l) !== String(r), + contain: (l, r) => containImpl(l, r), + not_contain: (l, r) => !containImpl(l, r), + is_true: (l) => l === true, + is_not_true: (l) => l !== true, + gt: (l, r) => + numOrStrCmp( + l, + r, + (a, b) => a > b, + (a, b) => a > b, + ), + ge: (l, r) => + numOrStrCmp( + l, + r, + (a, b) => a >= b, + (a, b) => a >= b, + ), + lt: (l, r) => + numOrStrCmp( + l, + r, + (a, b) => a < b, + (a, b) => a < b, + ), + le: (l, r) => + numOrStrCmp( + l, + r, + (a, b) => a <= b, + (a, b) => a <= b, + ), +} + +export function compareByOp(left: any, op: CompareOptions, right: any): boolean { + const fn = compareHandlers[op] + if (!fn) throw new Error(`Unknown compare op: ${op}`) + return fn(left, right) +} + +function containImpl(source: any, target: any): boolean { + if (Array.isArray(target)) { + return target.every((t) => containImpl(source, t)) + } + const t = String(target) + if (typeof source === 'string') return source.includes(t) + if (Array.isArray(source)) return source.some((item) => String(item) === t) + return String(source).includes(t) +} + +function numOrStrCmp( + left: any, + right: any, + numFn: (a: number, b: number) => boolean, + strFn: (a: string, b: string) => boolean, +): boolean { + const a = Number(left) + const b = Number(right) + if (!Number.isNaN(a) && !Number.isNaN(b)) return numFn(a, b) + try { + return strFn(String(left), String(right)) + } catch { + return false + } +} + +export function evaluateVisibility( + rules: VisibilityRules | null | undefined, + ctx: VisibilityCtx, +): boolean { + if (!rules || !rules.conditions || rules.conditions.length === 0) { + return true + } + + const results = rules.conditions.map((cond) => { + const left = lookupLeft(cond, ctx) + + if (left == null && cond.compare !== 'is_true' && cond.compare !== 'is_not_true') { + return false + } + + const right = typeof cond.value === 'string' ? resolveValue(cond.value, ctx) : cond.value + return compareByOp(left, cond.compare as CompareOptions, right) + }) + + const matched = rules.condition === 'or' ? results.some(Boolean) : results.every(Boolean) + + return rules.action === 'show' ? matched : !matched +} + +/** + * 单向扫描计算整个字段列表的显隐表。 + * @param fields + * @param formValue + * @returns { 字段名: 是否可见 } 的 map + */ +export function computeVisibilityMap( + fields: Array<{ field: string; visibility_rules?: VisibilityRules }>, + formValue: Record, +): Record { + const copy: Record = { ...formValue } + const map: Record = {} + + for (const f of fields) { + if (!f.visibility_rules?.node_id) { + map[f.field] = true + continue + } + + const visible = evaluateVisibility(f.visibility_rules, { + formValue: copy, + currentNodeId: f.visibility_rules.node_id, + currentNodeName: f.visibility_rules.node_name || '', + }) + map[f.field] = visible + if (!visible) { + copy[f.field] = null + } + } + return map +} diff --git a/ui/src/utils/bus.ts b/ui/src/utils/bus.ts new file mode 100644 index 00000000000..cd7c6f9207a --- /dev/null +++ b/ui/src/utils/bus.ts @@ -0,0 +1,9 @@ +import mitt, { type Emitter } from 'mitt' + +/** + * 全局事件总线。 + * mk-dynamics-form 用它在字段之间传递「值变化 / 联动触发」事件。 + */ +const bus: Emitter> = mitt>() + +export default bus diff --git a/ui/src/utils/common.ts b/ui/src/utils/common.ts index 4e6cc5ffc28..b34ce9496ad 100644 --- a/ui/src/utils/common.ts +++ b/ui/src/utils/common.ts @@ -16,3 +16,29 @@ export const resetUrl = (url?: string | null, useDefault?: boolean) => { } return sourceUrl } +export const relatedObject = (list: Array>, val: unknown, attr: string) => { + const filterData = list.find((item) => item[attr] === val) + return filterData || null +} +export const getAttrsArray = (array: Array>, attr: string) => { + return array.map((item) => { + return item[attr] + }) +} + +export const downloadByURL = (url: string, name: string) => { + const a = document.createElement('a') + a.setAttribute('href', url) + a.setAttribute('target', '_blank') + a.setAttribute('download', name) + document.body.appendChild(a) + a.click() + document.body.removeChild(a) +} + +export const getFileUrl = (fileId?: string) => { + if (fileId) { + return `./oss/file/${fileId}` + } + return '' +}