From 727c8bfa98e12b5057bd1d3267b3073711067cc9 Mon Sep 17 00:00:00 2001 From: shaohuzhang1 <80892890+shaohuzhang1@users.noreply.github.com> Date: Tue, 21 Oct 2025 19:25:22 +0800 Subject: [PATCH] feat: Enhance dynamic form rendering (#4223) --- ui/src/components/dynamics-form/Demo.vue | 175 +++++++++++------- ui/src/components/dynamics-form/FormItem.vue | 77 ++++++-- ui/src/components/dynamics-form/index.ts | 6 +- ui/src/components/dynamics-form/index.vue | 157 ++++++++++++---- .../items/complex/ArrayObjectCard.vue | 4 +- .../items/complex/ObjectCard.vue | 8 +- .../dynamics-form/items/complex/TabCard.vue | 8 +- .../items/label/SettingLabel.vue | 97 ++++++++++ .../items/label/TooltipLabel.vue | 9 +- .../dynamics-form/items/layout/RowLayout.vue | 18 ++ .../items/select/SingleSelect.vue | 2 +- ui/src/components/dynamics-form/type.ts | 3 +- ui/src/request/index.ts | 56 +++--- ui/src/router/routes.ts | 11 +- ui/src/views/demo/index.vue | 9 + 15 files changed, 471 insertions(+), 169 deletions(-) create mode 100644 ui/src/components/dynamics-form/items/label/SettingLabel.vue create mode 100644 ui/src/components/dynamics-form/items/layout/RowLayout.vue create mode 100644 ui/src/views/demo/index.vue diff --git a/ui/src/components/dynamics-form/Demo.vue b/ui/src/components/dynamics-form/Demo.vue index 9e179c3ce..0075f9f01 100644 --- a/ui/src/components/dynamics-form/Demo.vue +++ b/ui/src/components/dynamics-form/Demo.vue @@ -5,6 +5,7 @@ :model="form_data" :render_data="damo_data" ref="dynamicsFormRef" + :other-params="{ current_workspace_id: 'default' }" > @@ -66,7 +66,7 @@ const _data = computed>({ }, set(value) { emit('update:modelValue', value) - } + }, }) const props_info = computed(() => { @@ -117,7 +117,7 @@ const handleTabsEdit = (targetName: TabPaneName | undefined, action: 'remove' | defineExpose({ validate, - field: props.field + field: props.field, }) diff --git a/ui/src/components/dynamics-form/items/label/SettingLabel.vue b/ui/src/components/dynamics-form/items/label/SettingLabel.vue new file mode 100644 index 000000000..980c0dcc1 --- /dev/null +++ b/ui/src/components/dynamics-form/items/label/SettingLabel.vue @@ -0,0 +1,97 @@ + + + diff --git a/ui/src/components/dynamics-form/items/label/TooltipLabel.vue b/ui/src/components/dynamics-form/items/label/TooltipLabel.vue index 035b6bd6f..baff107f4 100644 --- a/ui/src/components/dynamics-form/items/label/TooltipLabel.vue +++ b/ui/src/components/dynamics-form/items/label/TooltipLabel.vue @@ -1,11 +1,11 @@ @@ -13,8 +13,7 @@ diff --git a/ui/src/components/dynamics-form/items/layout/RowLayout.vue b/ui/src/components/dynamics-form/items/layout/RowLayout.vue new file mode 100644 index 000000000..40001347c --- /dev/null +++ b/ui/src/components/dynamics-form/items/layout/RowLayout.vue @@ -0,0 +1,18 @@ + + + diff --git a/ui/src/components/dynamics-form/items/select/SingleSelect.vue b/ui/src/components/dynamics-form/items/select/SingleSelect.vue index a4d765676..602d21674 100644 --- a/ui/src/components/dynamics-form/items/select/SingleSelect.vue +++ b/ui/src/components/dynamics-form/items/select/SingleSelect.vue @@ -42,7 +42,7 @@ const _modelValue = computed({ set(value) { emit('update:modelValue', value) emit('change', props.formField) - } + }, }) const textField = computed(() => { return props.formField.text_field ? props.formField.text_field : 'key' diff --git a/ui/src/components/dynamics-form/type.ts b/ui/src/components/dynamics-form/type.ts index e0da49be2..88a3af373 100644 --- a/ui/src/components/dynamics-form/type.ts +++ b/ui/src/components/dynamics-form/type.ts @@ -137,7 +137,7 @@ interface FormField { /** * {field:field_value_list} 表示在 field有值 ,并且值在field_value_list中才 执行函数获取 数据 */ - relation_trigger_field_dict?: Dict> + relation_trigger_field_dict?: Dict /** * 执行器类型 OPTION_LIST请求Option_list数据 CHILD_FORMS请求子表单 */ @@ -172,5 +172,6 @@ interface FormField { method?: string children?: Array + required_asterisk?: boolean } export type { FormField } diff --git a/ui/src/request/index.ts b/ui/src/request/index.ts index 4e3ee5477..4bb6744c3 100644 --- a/ui/src/request/index.ts +++ b/ui/src/request/index.ts @@ -23,6 +23,9 @@ instance.interceptors.request.use( if (config.headers === undefined) { config.headers = new AxiosHeaders() } + if (config.url && config.url.startsWith('http')) { + return config + } const { user, login } = useStore() const token = login.getToken() const language = user.getLanguage() @@ -250,32 +253,33 @@ export const exportExcel: ( } function extractFilename(contentDisposition: string) { - if (!contentDisposition) return null; + if (!contentDisposition) return null // 处理 URL 编码的文件名 - const urlEncodedMatch = contentDisposition.match(/filename=([^;]*)/i) || - contentDisposition.match(/filename\*=UTF-8''([^;]*)/i); + const urlEncodedMatch = + contentDisposition.match(/filename=([^;]*)/i) || + contentDisposition.match(/filename\*=UTF-8''([^;]*)/i) if (urlEncodedMatch && urlEncodedMatch[1]) { try { - return decodeURIComponent(urlEncodedMatch[1].replace(/"/g, '')); + return decodeURIComponent(urlEncodedMatch[1].replace(/"/g, '')) } catch (e) { - console.error("解码URL编码文件名失败:", e); + console.error('解码URL编码文件名失败:', e) } } // 处理 Base64 编码的文件名 - const base64Part = contentDisposition.match(/=\?utf-8\?b\?(.*?)\?=/i)?.[1]; + const base64Part = contentDisposition.match(/=\?utf-8\?b\?(.*?)\?=/i)?.[1] if (base64Part) { try { - const decoded = decodeURIComponent(escape(atob(base64Part))); - const filenameMatch = decoded.match(/filename="(.*?)"/i); - return filenameMatch ? filenameMatch[1] : null; + const decoded = decodeURIComponent(escape(atob(base64Part))) + const filenameMatch = decoded.match(/filename="(.*?)"/i) + return filenameMatch ? filenameMatch[1] : null } catch (e) { - console.error("解码Base64文件名失败:", e); + console.error('解码Base64文件名失败:', e) } } - return null; + return null } export const exportFile: ( @@ -320,20 +324,22 @@ export const exportFile: ( ], }), loading, - ).then((res: any) => { - if (res) { - const blob = new Blob([res], { - type: 'application/octet-stream', - }) - const link = document.createElement('a') - link.href = window.URL.createObjectURL(blob) - link.download = fileName - link.click() - //释放内存 - window.URL.revokeObjectURL(link.href) - } - return true - }).catch(()=>{}) + ) + .then((res: any) => { + if (res) { + const blob = new Blob([res], { + type: 'application/octet-stream', + }) + const link = document.createElement('a') + link.href = window.URL.createObjectURL(blob) + link.download = fileName + link.click() + //释放内存 + window.URL.revokeObjectURL(link.href) + } + return true + }) + .catch(() => {}) } export const exportExcelPost: ( diff --git a/ui/src/router/routes.ts b/ui/src/router/routes.ts index e18753816..5f9cc420b 100644 --- a/ui/src/router/routes.ts +++ b/ui/src/router/routes.ts @@ -1,6 +1,6 @@ -import type {RouteRecordRaw} from 'vue-router' +import type { RouteRecordRaw } from 'vue-router' -const modules: any = import.meta.glob('./modules/*.ts', {eager: true}) +const modules: any = import.meta.glob('./modules/*.ts', { eager: true }) const rolesRoutes: RouteRecordRaw[] = [...Object.keys(modules).map((key) => modules[key].default)] @@ -33,7 +33,7 @@ export const routes: Array = [ { path: '/application/:from/:id/workflow', name: 'ApplicationWorkflow', - meta: {activeMenu: '/application'}, + meta: { activeMenu: '/application' }, component: () => import('@/views/application-workflow/index.vue'), }, // 对话 @@ -42,6 +42,11 @@ export const routes: Array = [ name: 'Chat', component: () => import('@/views/chat/index.vue'), }, + { + path: '/demo', + name: 'demo', + component: () => import('@/views/demo/index.vue'), + }, // 对话用户登录 { diff --git a/ui/src/views/demo/index.vue b/ui/src/views/demo/index.vue new file mode 100644 index 000000000..650e6c443 --- /dev/null +++ b/ui/src/views/demo/index.vue @@ -0,0 +1,9 @@ + + +