mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
feat: Enhance dynamic form rendering (#4223)
This commit is contained in:
parent
da7f0edecd
commit
727c8bfa98
|
|
@ -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})}% `'
|
||||
'`${parseFloat(row.number).toLocaleString("zh-CN",{style: "decimal",maximumFractionDigits:1})}% `',
|
||||
},
|
||||
{
|
||||
type: 'eval',
|
||||
title: '名称',
|
||||
value_field: '`${row.key} `'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
value_field: '`${row.key} `',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
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})}% `'
|
||||
'`${parseFloat(row.number).toLocaleString("zh-CN",{style: "decimal",maximumFractionDigits:1})}% `',
|
||||
},
|
||||
{
|
||||
type: 'eval',
|
||||
title: '名称',
|
||||
value_field: '`${row.key} `'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
value_field: '`${row.key} `',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
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>>()
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
/**
|
||||
* 校验函数
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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: (
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
},
|
||||
|
||||
// 对话用户登录
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
Loading…
Reference in New Issue