feat: Enhance dynamic form rendering (#4223)

This commit is contained in:
shaohuzhang1 2025-10-21 19:25:22 +08:00 committed by GitHub
parent da7f0edecd
commit 727c8bfa98
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 471 additions and 169 deletions

View File

@ -5,6 +5,7 @@
:model="form_data"
:render_data="damo_data"
ref="dynamicsFormRef"
:other-params="{ current_workspace_id: 'default' }"
>
<template #default="scope">
<el-form-item label="其他字段">
@ -21,7 +22,30 @@ import { ref } from 'vue'
import type { Dict } from '@/api/type/common'
const damo_data: Array<FormField> = [
{ field: 'name', input_type: 'PasswordInput', label: '用戶名', required: false },
{
field: 'name',
input_type: 'PasswordInput',
label: {
label: '用戶名',
input_type: 'SettingLabel',
field: 'name_setting',
relation_show_field_dict: {
name: {
values: ['01993837-5b09-7f20-9360-801d11d43d28'],
},
},
relation_trigger_field_dict: {
name: {
values: ['01993837-5b09-7f20-9360-801d11d43d28'],
request:
'self.children=()=>request.get(extra.renderTemplate(trigger_setting.url)).then(ok=>{return ok})',
url: '/workspace/${current_workspace_id}/model/${trigger_value}/model_params_form',
},
},
children: [],
},
required: false,
},
{ field: 'json_text', input_type: 'JsonInput', label: 'aa', required: false },
{
field: 'array_object_card_field',
@ -33,8 +57,8 @@ const damo_data: Array<FormField> = [
children: [
{ field: 'name1', input_type: 'TextInput', label: '用戶名1' },
{ field: 'name2', input_type: 'TextInput', label: '用戶名2' },
{ field: 'name3', input_type: 'TextInput', label: '用戶名3' }
]
{ field: 'name3', input_type: 'TextInput', label: '用戶名3' },
],
},
{
field: 'maxkb_tokens',
@ -46,9 +70,9 @@ const damo_data: Array<FormField> = [
step: 1,
precision: 1,
'show-input-controls': false,
'show-input': true
'show-input': true,
},
label: { label: '温度', attrs: { tooltip: 'sss' }, input_type: 'TooltipLabel' }
label: { label: '温度', attrs: { tooltip: 'sss' }, input_type: 'TooltipLabel' },
},
{
field: 'object_card_field',
@ -60,8 +84,8 @@ const damo_data: Array<FormField> = [
children: [
{ field: 'name1', input_type: 'TextInput', label: '用戶名1' },
{ field: 'name2', input_type: 'TextInput', label: '用戶名2' },
{ field: 'name3', input_type: 'TextInput', label: '用戶名3' }
]
{ field: 'name3', input_type: 'TextInput', label: '用戶名3' },
],
},
{
field: 'tab_card_field',
@ -70,39 +94,54 @@ const damo_data: Array<FormField> = [
trigger_type: 'CHILD_FORMS',
attrs: { 'label-width': '120px', 'label-suffix': ':ssss', 'label-position': 'left' },
required: false,
relation_trigger_field_dict: {
'array_object_card_field.0.name1': ['111']
},
props_info: { tabs_label: '用户' },
children: [
{ field: 'name1', input_type: 'TextInput', label: '用戶名1' },
{ field: 'name2', input_type: 'TextInput', label: '用戶名2' },
{ field: 'name3', input_type: 'TextInput', label: '用戶名3' }
]
{ field: 'name3', input_type: 'TextInput', label: '用戶名3' },
],
},
{
field: 'single_select_field',
input_type: 'SingleSelect',
label: '测试单选',
text_field: 'name',
value_field: 'id',
required: true,
attrs: { placeholder: '请选择' },
option_list: [
{
key: '测试',
value: 'test'
required_asterisk: true,
label: {
label: '测试单选',
input_type: 'SettingLabel',
field: 'name_setting',
relation_show_field_dict: {
single_select_field: {
values: [],
},
},
{
key: '测试1',
value: 'test1'
}
]
relation_trigger_field_dict: {
single_select_field: {
values: [],
request:
'self.children=()=>request.get(extra.renderTemplate(trigger_setting.url)).then(ok=>{return ok})',
url: '/workspace/${current_workspace_id}/model/${trigger_value}/model_params_form',
},
},
children: [],
},
relation_trigger_field_dict: {
name: {
values: [],
url: '/workspace/${current_workspace_id}/model_list?model_type=LLM',
change_field: 'option_list',
},
},
},
{
field: 'multi_select_field',
input_type: 'MultiSelect',
default_value: ['test1'],
relation_show_field_dict: {
'object_card_field.name1': []
'object_card_field.name1': [],
},
label: '测试多选下拉',
required: true,
@ -110,13 +149,13 @@ const damo_data: Array<FormField> = [
option_list: [
{
key: '测试',
value: 'test'
value: 'test',
},
{
key: '测试1',
value: 'test1'
}
]
value: 'test1',
},
],
},
{
field: 'radio_field',
@ -127,13 +166,13 @@ const damo_data: Array<FormField> = [
option_list: [
{
key: '测试',
value: 'test'
value: 'test',
},
{
key: '测试1',
value: 'test1'
}
]
value: 'test1',
},
],
},
{
field: 'radio_button_field',
@ -144,13 +183,13 @@ const damo_data: Array<FormField> = [
option_list: [
{
key: '测试',
value: 'test'
value: 'test',
},
{
key: '测试1',
value: 'test1'
}
]
value: 'test1',
},
],
},
{
field: 'radio_card_field',
@ -161,13 +200,13 @@ const damo_data: Array<FormField> = [
option_list: [
{
key: '测试',
value: 'test'
value: 'test',
},
{
key: '测试111111',
value: 'test1'
}
]
value: 'test1',
},
],
},
{
field: 'table_radio_field',
@ -181,7 +220,7 @@ const damo_data: Array<FormField> = [
{
property: '`${row.key}${row.number}`',
label: '名称',
type: 'eval'
type: 'eval',
},
{
property: 'ProgressTableItem',
@ -194,8 +233,8 @@ const damo_data: Array<FormField> = [
{ color: '#e6a23c', percentage: 40 },
{ color: '#5cb87a', percentage: 60 },
{ color: '#1989fa', percentage: 80 },
{ color: '#6f7ad3', percentage: 100 }
]
{ color: '#6f7ad3', percentage: 100 },
],
},
props_info: {
view_card: [
@ -203,31 +242,31 @@ const damo_data: Array<FormField> = [
type: 'eval',
title: '测试',
value_field:
'`${parseFloat(row.number).toLocaleString("zh-CN",{style: "decimal",maximumFractionDigits:1})}%&nbsp;&nbsp;&nbsp;`'
'`${parseFloat(row.number).toLocaleString("zh-CN",{style: "decimal",maximumFractionDigits:1})}%&nbsp;&nbsp;&nbsp;`',
},
{
type: 'eval',
title: '名称',
value_field: '`${row.key}&nbsp;&nbsp;&nbsp;`'
}
]
}
}
value_field: '`${row.key}&nbsp;&nbsp;&nbsp;`',
},
],
},
},
],
style: { width: '500px' }
style: { width: '500px' },
},
option_list: [
{
key: '测试',
value: 'test',
number: 10
number: 10,
},
{
key: '测试111111',
value: 'test1',
number: 100
}
]
number: 100,
},
],
},
{
field: 'table_checkbox_field',
@ -241,7 +280,7 @@ const damo_data: Array<FormField> = [
{
property: '`${row.key}${row.number}`',
label: '名称',
type: 'eval'
type: 'eval',
},
{
property: 'ProgressTableItem',
@ -254,8 +293,8 @@ const damo_data: Array<FormField> = [
{ color: '#e6a23c', percentage: 40 },
{ color: '#5cb87a', percentage: 60 },
{ color: '#1989fa', percentage: 80 },
{ color: '#6f7ad3', percentage: 100 }
]
{ color: '#6f7ad3', percentage: 100 },
],
},
props_info: {
view_card: [
@ -263,32 +302,32 @@ const damo_data: Array<FormField> = [
type: 'eval',
title: '测试',
value_field:
'`${parseFloat(row.number).toLocaleString("zh-CN",{style: "decimal",maximumFractionDigits:1})}%&nbsp;&nbsp;&nbsp;`'
'`${parseFloat(row.number).toLocaleString("zh-CN",{style: "decimal",maximumFractionDigits:1})}%&nbsp;&nbsp;&nbsp;`',
},
{
type: 'eval',
title: '名称',
value_field: '`${row.key}&nbsp;&nbsp;&nbsp;`'
}
]
}
}
value_field: '`${row.key}&nbsp;&nbsp;&nbsp;`',
},
],
},
},
],
style: { width: '500px' }
style: { width: '500px' },
},
option_list: [
{
key: '测试',
value: 'test',
number: 10
number: 10,
},
{
key: '测试111111',
value: 'test1',
number: 100
}
]
}
number: 100,
},
],
},
]
const form_data = ref<Dict<any>>({})
const dynamicsFormRef = ref<InstanceType<typeof DynamicsForm>>()

View File

@ -5,13 +5,16 @@
:prop="formfield.field"
:key="formfield.field"
:rules="rules"
:class="formfield.required_asterisk ? 'hide-asterisk' : ''"
>
<template #label v-if="formfield.label">
<FormItemLabel v-if="isString(formfield.label)" :form-field="formfield"></FormItemLabel>
<component
v-else
:is="formfield.label.input_type"
:label="formfield.label.label"
:label="formfield.label"
v-model="labelValue"
:form-value="formValue"
v-bind="label_attrs"
></component>
</template>
@ -36,6 +39,7 @@ import FormItemLabel from './FormItemLabel.vue'
import type { Dict } from '@/api/type/common'
import bus from '@/utils/bus'
import { t } from '@/locales'
import { get } from 'lodash'
const props = defineProps<{
//
modelValue: any
@ -47,7 +51,13 @@ const props = defineProps<{
//
otherParams: any
// Options
trigger: (formItem: FormField, loading: Ref<boolean>) => Promise<any>
trigger: (
trigger_field: string,
trigger_value: any,
trigger_setting: any,
self: any,
loading: Ref<boolean>,
) => void
//
initDefaultData: (formItem: FormField) => void
//
@ -60,13 +70,22 @@ const props = defineProps<{
parent_field?: string
}>()
const emit = defineEmits(['change'])
const emit = defineEmits(['change', 'changeLabel'])
const loading = ref<boolean>(false)
const isString = (value: any) => {
return typeof value === 'string'
}
const labelValue = computed({
get: () => {
return props.formValue[props.formfield.label.field]
},
set: (value: any) => {
emit('changeLabel', value)
bus.emit(props.formfield.label.field, value)
},
})
const itemValue = computed({
get: () => {
return props.modelValue
@ -147,32 +166,50 @@ const componentStyle = computed(() => {
const attrs = computed(() => {
return props.formfield.attrs ? props.formfield.attrs : {}
})
const initTrigger = (self: any, trigger_field_dict?: Dict<any>) => {
if (trigger_field_dict) {
Object.keys(trigger_field_dict).forEach((key) => {
const setting = trigger_field_dict[key]
const triggerValues = setting['values']
const value = get(props.formValue, key)
if (triggerValues && triggerValues.length > 0) {
if (triggerValues.includes(value)) {
props.trigger(key, value, setting, self, loading)
}
} else {
props.trigger(key, value, setting, self, loading)
}
})
}
}
onMounted(() => {
props.initDefaultData(props.formfield)
if (props.formfield.provider && props.formfield.method) {
props.trigger(props.formfield, loading)
}
//
const trigger_field_dict = props.formfield.relation_trigger_field_dict
initTrigger(props.formfield, props.formfield.relation_trigger_field_dict)
initTrigger(props.formfield.label, props.formfield.label?.relation_trigger_field_dict)
isString(props.formfield.label)
? undefined
: onTrigger(props.formfield.label, props.formfield.label.relation_trigger_field_dict)
onTrigger(props.formfield, props.formfield.relation_trigger_field_dict)
})
const onTrigger = (self: any, trigger_field_dict?: Dict<any>) => {
if (trigger_field_dict) {
const keys = Object.keys(trigger_field_dict)
keys.forEach((key) => {
const value = trigger_field_dict[key]
const setting = trigger_field_dict[key]
const values: Array<any> = setting.values
//
bus.on(key, (v: any) => {
if (value && value.length > 0) {
if (value.includes(v)) {
props.trigger(props.formfield, loading)
if (values && values.length > 0) {
if (values.includes(v)) {
props.trigger(key, v, setting, self, loading)
}
} else {
props.trigger(props.formfield, loading)
props.trigger(key, v, setting, self, loading)
}
})
})
}
})
}
const validate = () => {
if (props.formfield.trigger_type === 'CHILD_FORMS' && componentFormRef.value) {
return componentFormRef.value.validate()
@ -181,4 +218,10 @@ const validate = () => {
}
defineExpose({ validate })
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.hide-asterisk {
::after {
display: none;
}
}
</style>

View File

@ -2,13 +2,13 @@ import type { App } from 'vue'
import type { Dict } from '@/api/type/common'
import DynamicsForm from '@/components/dynamics-form/index.vue'
let components: Dict<any> = import.meta.glob('@/components/dynamics-form/**/**.vue', {
eager: true
eager: true,
})
components = {
...components,
...import.meta.glob('@/components/dynamics-form/**/**/**.vue', {
eager: true
})
eager: true,
}),
}
const install = (app: App) => {

View File

@ -6,6 +6,8 @@
label-suffix=":"
v-loading="loading"
v-bind="$attrs"
label-position="top"
require-asterisk-position="right"
>
<slot :form_value="formValue"></slot>
<template v-for="item in formFieldList" :key="item.field">
@ -14,6 +16,7 @@
:key="item.field"
v-if="show(item)"
@change="change(item, $event)"
@changeLabel="changeLabel(item, $event)"
v-bind:modelValue="formValue[item.field]"
:formfield="item"
:trigger="trigger"
@ -33,18 +36,27 @@
import type { Dict } from '@/api/type/common'
import FormItem from '@/components/dynamics-form/FormItem.vue'
import type { FormField } from '@/components/dynamics-form/type'
import { ref, onBeforeMount, watch, type Ref } from 'vue'
import { ref, onBeforeMount, watch, type Ref, computed } from 'vue'
import type { FormInstance } from 'element-plus'
import { get } from '@/request/index'
import type Result from '@/request/Result'
import _ from 'lodash'
import { get, post, put, del } from '@/request/index'
const request = {
get,
post,
put,
del,
}
defineOptions({ name: 'dynamicsForm' })
const props = withDefaults(
defineProps<{
//
render_data: Promise<Result<Array<FormField>>> | string | Array<FormField>
render_data:
| Promise<Result<Array<FormField>>>
| string
| Array<FormField>
| (() => Promise<Result<Array<FormField>>>)
//
otherParams?: any
//
@ -103,6 +115,15 @@ const change = (field: FormField, value: any) => {
formValue.value[field.field] = value
}
/**
* 表单字段修改
* @param field
* @param value
*/
const changeLabel = (field: FormField, value: any) => {
formValue.value[field.label.field] = value
}
watch(
formValue,
() => {
@ -110,14 +131,60 @@ watch(
},
{ deep: true },
)
function renderTemplate(template: string, data: any) {
return template.replace(/\$\{(\w+)\}/g, (match, key) => {
return data[key] !== undefined ? data[key] : match
})
}
/**
* 触发器,用户获取子表单 或者 下拉选项
* @param field
* @param loading
*/
const trigger = (field: FormField, loading: Ref<boolean>) => {
return Promise.resolve([])
const trigger = (
trigger_field: string,
trigger_value: any,
trigger_setting: any,
self: any,
loading: Ref<boolean>,
) => {
const request_call = new Function(
'self',
'trigger_setting',
'request',
'extra',
trigger_setting.request
? trigger_setting.request
: 'return request.get(extra.renderTemplate(trigger_setting.url));',
)(self, trigger_setting, request, {
renderTemplate: (url: string) =>
renderTemplate(url, {
trigger_value: trigger_value,
...props.otherParams,
}),
})
if (!trigger_setting.change && !trigger_setting.change_field) {
return
}
request_call.then((ok: any) => {
new Function(
'self',
'trigger_setting',
'response',
'extra',
trigger_setting.change
? trigger_setting.change
: `self[trigger_setting.change_field]=[
...response.data.shared_model.map((m) => {
return { ...m, type: 'share' }
}),
...response.data.model.map((m) => {
return { ...m, type: 'workspace' }
})
];`,
)(self, trigger_setting, ok, { form_data: formValue, getDefault: getFormDefaultValue })
})
}
/**
* 初始化默认数据
@ -141,7 +208,11 @@ onBeforeMount(() => {
})
const render = (
render_data: string | Array<FormField> | Promise<Result<Array<FormField>>>,
render_data:
| string
| Array<FormField>
| Promise<Result<Array<FormField>>>
| (() => Promise<Result<Array<FormField>>>),
data?: Dict<any>,
) => {
if (typeof render_data == 'string') {
@ -150,6 +221,15 @@ const render = (
})
} else if (render_data instanceof Array) {
formFieldList.value = render_data
} else if (typeof render_data === 'function') {
render_data().then((ok: any) => {
formFieldList.value = ok.data
const form_data = data ? data : {}
if (form_data) {
const value = getFormDefaultValue(formFieldList.value, form_data)
formValue.value = _.cloneDeep(value)
}
})
} else {
render_data.then((ok) => {
formFieldList.value = ok.data
@ -157,37 +237,42 @@ const render = (
}
const form_data = data ? data : {}
if (form_data) {
const value = formFieldList.value
.map((item) => {
if (form_data[item.field] !== undefined) {
if (item.value_field && item.option_list && item.option_list.length > 0) {
const value_field = item.value_field
const find = item.option_list?.find((i) => {
if (typeof form_data[item.field] === 'string') {
return i[value_field] === form_data[item.field]
} else {
return form_data[item.field].indexOf([value_field]) === -1
}
})
if (find) {
return { [item.field]: form_data[item.field] }
}
if (item.show_default_value === true || item.show_default_value === undefined) {
return { [item.field]: item.default_value }
}
} else {
return { [item.field]: form_data[item.field] }
}
}
if (item.show_default_value === true || item.show_default_value === undefined) {
return { [item.field]: item.default_value }
}
return {}
})
.reduce((x, y) => ({ ...x, ...y }), {})
const value = getFormDefaultValue(formFieldList.value, form_data)
formValue.value = _.cloneDeep(value)
}
}
const getFormDefaultValue = (fieldList: Array<any>, form_data?: any) => {
form_data = form_data ? form_data : {}
const value = fieldList
.map((item) => {
if (form_data[item.field] !== undefined) {
if (item.value_field && item.option_list && item.option_list.length > 0) {
const value_field = item.value_field
const find = item.option_list?.find((i: any) => {
if (typeof form_data[item.field] === 'string') {
return i[value_field] === form_data[item.field]
} else {
return form_data[item.field].indexOf([value_field]) === -1
}
})
if (find) {
return { [item.field]: form_data[item.field] }
}
if (item.show_default_value === true || item.show_default_value === undefined) {
return { [item.field]: item.default_value }
}
} else {
return { [item.field]: form_data[item.field] }
}
}
if (item.show_default_value === true || item.show_default_value === undefined) {
return { [item.field]: item.default_value }
}
return {}
})
.reduce((x, y) => ({ ...x, ...y }), {})
return value
}
/**
* 校验函数
*/

View File

@ -4,8 +4,6 @@
<DynamicsForm
:style="formStyle"
:view="view"
label-position="top"
require-asterisk-position="right"
ref="ceFormRef"
v-model="_data[index]"
:model="_data[index]"
@ -13,6 +11,8 @@
:render_data="render_data()"
v-bind="attr"
:parent_field="formField.field + '.' + index"
label-position="top"
require-asterisk-position="right"
></DynamicsForm>
<el-tooltip effect="dark" :content="$t('common.delete')" placement="top">
<el-button text @click.stop="deleteKnowledge(item)" class="delete-button">

View File

@ -3,14 +3,14 @@
<DynamicsForm
:read-only="view"
:style="formStyle"
label-position="top"
require-asterisk-position="right"
ref="dynamicsFormRef"
v-model="data"
:other-params="other"
:render_data="formField.children ? formField.children : []"
v-bind="$attrs"
:parent_field="formField.field"
label-position="top"
require-asterisk-position="right"
></DynamicsForm>
</el-card>
</template>
@ -38,7 +38,7 @@ const data = computed({
},
set: ($event) => {
emit('update:modelValue', $event)
}
},
})
const other = computed(() => {
@ -69,7 +69,7 @@ function validate() {
return Promise.resolve()
}
defineExpose({
validate
validate,
})
</script>
<style lang="scss" scoped></style>

View File

@ -12,8 +12,6 @@
<DynamicsForm
:style="formStyle"
:view="view"
label-position="top"
require-asterisk-position="right"
ref="ceFormRef"
v-model="_data[index]"
:model="_data[index]"
@ -21,6 +19,8 @@
:render_data="render_data()"
v-bind="attr"
:parent_field="formField.field + '.' + index"
label-position="top"
require-asterisk-position="right"
></DynamicsForm>
</el-card>
</template>
@ -66,7 +66,7 @@ const _data = computed<Array<any>>({
},
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,
})
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,97 @@
<template>
<div class="flex-between w-full my-required">
<div>
<span> {{ label.label }}<span class="color-danger">*</span></span>
</div>
<el-tooltip v-if="label.attrs?.tooltip" effect="dark" placement="right">
<template #content
><div style="max-width: 200px">{{ label.attrs.tooltip }}</div></template
>
<AppIcon iconName="app-warning" class="app-warning-icon" style="flex-shrink: 0"></AppIcon>
</el-tooltip>
<el-button v-if="show(label)" type="primary" link @click="open()">
<AppIcon iconName="app-setting"></AppIcon>
</el-button>
<el-dialog
destroy-on-close
v-model="dialogVisible"
title="Tips"
width="500"
:before-close="close"
>
<DynamicsForm
:read-only="view"
ref="dynamicsFormRef"
:render_data="label.children ? label.children : []"
label-position="top"
v-model="form_data"
require-asterisk-position="right"
:model="form_data"
></DynamicsForm>
<template #footer>
<div class="dialog-footer">
<el-button @click="close">取消</el-button>
<el-button type="primary" @click="submit"> 确定 </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import DynamicsForm from '@/components/dynamics-form/index.vue'
import { ref } from 'vue'
import { cloneDeep, get } from 'lodash'
const props = defineProps<{
label: any
modelValue?: any
formValue: any
view?: boolean
}>()
const emit = defineEmits(['update:modelValue'])
const dialogVisible = ref<boolean>(false)
const dynamicsFormRef = ref<InstanceType<typeof DynamicsForm>>()
const form_data = ref<any>(undefined)
const open = () => {
if (props.modelValue) {
form_data.value = cloneDeep(props.modelValue)
}
dialogVisible.value = true
}
const close = () => {
dialogVisible.value = false
form_data.value = undefined
}
/**
* 当前 field是否展示
* @param field
*/
const show = (field: any) => {
if (field.relation_show_field_dict) {
const keys = Object.keys(field.relation_show_field_dict)
for (const index in keys) {
const key = keys[index]
const v = get(props.formValue, key)
if (v && v !== undefined && v !== null) {
const values = field.relation_show_field_dict[key]
if (values && values.length > 0) {
return values.includes(v)
} else {
return true
}
} else {
return false
}
}
}
return true
}
const submit = () => {
dynamicsFormRef.value?.validate().then(() => {
dialogVisible.value = false
emit('update:modelValue', form_data.value)
form_data.value = undefined
})
}
</script>
<style lang="scss" scoped></style>

View File

@ -1,11 +1,11 @@
<template>
<div class="flex align-center" style="display: inline-flex">
<div class="flex-between mr-4">
<span>{{ label }}</span>
<span>{{ label.label }}</span>
</div>
<el-tooltip effect="dark" placement="right">
<el-tooltip v-if="label.attrs.tooltip" effect="dark" placement="right">
<template #content
><div style="max-width: 200px">{{ tooltip }}</div></template
><div style="max-width: 200px">{{ label.attrs.tooltip }}</div></template
>
<AppIcon iconName="app-warning" class="app-warning-icon" style="flex-shrink: 0"></AppIcon>
</el-tooltip>
@ -13,8 +13,7 @@
</template>
<script setup lang="ts">
defineProps<{
label: string
tooltip: string
label: any
}>()
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,18 @@
<template>
<el-row> </el-row>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import type { FormField } from '@/components/dynamics-form/type'
import _ from 'lodash'
const props = defineProps<{
formValue?: any
formfieldList?: Array<FormField>
field: string
otherParams: any
formField: FormField
view?: boolean
}>()
</script>
<style lang="scss" scoped></style>

View File

@ -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'

View File

@ -137,7 +137,7 @@ interface FormField {
/**
* {field:field_value_list} field有值 ,field_value_list中才
*/
relation_trigger_field_dict?: Dict<Array<any>>
relation_trigger_field_dict?: Dict<any>
/**
* OPTION_LIST请求Option_list数据 CHILD_FORMS请求子表单
*/
@ -172,5 +172,6 @@ interface FormField {
method?: string
children?: Array<FormField>
required_asterisk?: boolean
}
export type { FormField }

View File

@ -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: (

View File

@ -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<RouteRecordRaw> = [
{
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<RouteRecordRaw> = [
name: 'Chat',
component: () => import('@/views/chat/index.vue'),
},
{
path: '/demo',
name: 'demo',
component: () => import('@/views/demo/index.vue'),
},
// 对话用户登录
{

View File

@ -0,0 +1,9 @@
<template>
<div style="height: 500px">
<DemoVue />
</div>
</template>
<script setup lang="ts">
import DemoVue from '@/components/dynamics-form/Demo.vue'
</script>
<style lang="scss" scoped></style>