feat: Form nodes support file upload and multi-line text (#3879)
Some checks are pending
sync2gitee / repo-sync (push) Waiting to run
Typos Check / Spell Check with Typos (push) Waiting to run

This commit is contained in:
shaohuzhang1 2025-08-18 19:08:00 +08:00 committed by GitHub
parent 31d71d137a
commit d26883581c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 736 additions and 50 deletions

View File

@ -69,9 +69,11 @@ class BaseFormNode(IFormNode):
reset_field = ['field', 'label', 'default_value']
for f in reset_field:
_value = field[f]
if _value is None:
continue
if isinstance(_value, str):
field[f] = self.workflow_manage.generate_prompt(_value)
else:
elif f == 'label':
_label_value = _value.get('label')
_value['label'] = self.workflow_manage.generate_prompt(_label_value)
tooltip = _value.get('attrs').get('tooltip')

View File

@ -1,44 +1,92 @@
<template>
<div ref="aiChatRef" class="ai-chat" :class="type" :style="{
height: firsUserInput ? '100%' : undefined,
paddingBottom: applicationDetails.disclaimer ? '20px' : 0,
}">
<div v-show="showUserInputContent" :class="firsUserInput ? 'firstUserInput' : 'popperUserInput'">
<UserForm v-model:api_form_data="api_form_data" v-model:form_data="form_data" :application="applicationDetails"
:type="type" :first="firsUserInput" @confirm="UserFormConfirm" @cancel="UserFormCancel" ref="userFormRef">
<div
ref="aiChatRef"
class="ai-chat"
:class="type"
:style="{
height: firsUserInput ? '100%' : undefined,
paddingBottom: applicationDetails.disclaimer ? '20px' : 0,
}"
>
<div
v-show="showUserInputContent"
:class="firsUserInput ? 'firstUserInput' : 'popperUserInput'"
>
<UserForm
v-model:api_form_data="api_form_data"
v-model:form_data="form_data"
:application="applicationDetails"
:type="type"
:first="firsUserInput"
@confirm="UserFormConfirm"
@cancel="UserFormCancel"
ref="userFormRef"
>
</UserForm>
</div>
<template v-if="!(isUserInput || isAPIInput) || !firsUserInput || type === 'log'">
<el-scrollbar ref="scrollDiv" @scroll="handleScrollTop">
<div ref="dialogScrollbar" class="ai-chat__content p-16">
<PrologueContent :type="type" :application="applicationDetails" :available="available"
:send-message="sendMessage"></PrologueContent>
<PrologueContent
:type="type"
:application="applicationDetails"
:available="available"
:send-message="sendMessage"
></PrologueContent>
<template v-for="(item, index) in chatList" :key="index">
<!-- 问题 -->
<QuestionContent :type="type" :application="applicationDetails" :chat-record="item"></QuestionContent>
<QuestionContent
:type="type"
:application="applicationDetails"
:chat-record="item"
></QuestionContent>
<!-- 回答 -->
<AnswerContent :application="applicationDetails" :loading="loading" v-model:chat-record="chatList[index]"
:type="type" :send-message="sendMessage" :chat-management="ChatManagement"
<AnswerContent
:application="applicationDetails"
:loading="loading"
v-model:chat-record="chatList[index]"
:type="type"
:send-message="sendMessage"
:chat-management="ChatManagement"
:executionIsRightPanel="props.executionIsRightPanel"
@open-execution-detail="emit('openExecutionDetail', chatList[index])"
@openParagraph="emit('openParagraph', chatList[index])"
@openParagraphDocument="
(val: any) => emit('openParagraphDocument', chatList[index], val)
"></AnswerContent>
"
></AnswerContent>
</template>
<TransitionContent v-if="transcribing" :text="t('chat.transcribing')" :type="type"
:application="applicationDetails">
<TransitionContent
v-if="transcribing"
:text="t('chat.transcribing')"
:type="type"
:application="applicationDetails"
>
</TransitionContent>
</div>
</el-scrollbar>
<ChatInputOperate :app-id="appId" :application-details="applicationDetails" :is-mobile="isMobile" :type="type"
:send-message="sendMessage" :open-chat-id="openChatId" :validate="validate" :chat-management="ChatManagement"
v-model:chat-id="chartOpenId" v-model:loading="loading" v-model:show-user-input="showUserInput"
v-if="type !== 'log'">
<ChatInputOperate
:app-id="appId"
:application-details="applicationDetails"
:is-mobile="isMobile"
:type="type"
:send-message="sendMessage"
:open-chat-id="openChatId"
:validate="validate"
:chat-management="ChatManagement"
v-model:chat-id="chartOpenId"
v-model:loading="loading"
v-model:show-user-input="showUserInput"
v-if="type !== 'log'"
>
<template #userInput>
<el-button v-if="isUserInput || isAPIInput" class="user-input-button mb-8" @click="toggleUserInput">
<el-button
v-if="isUserInput || isAPIInput"
class="user-input-button mb-8"
@click="toggleUserInput"
>
<AppIcon iconName="app-edit" :size="16" class="mr-4"></AppIcon>
<span class="ellipsis">
{{ userInputTitle || $t('chat.userInput') }}
@ -52,11 +100,21 @@
</div>
</template>
<script setup lang="ts">
import { type Ref, ref, nextTick, computed, watch, reactive, onMounted, onBeforeUnmount } from 'vue'
import {
type Ref,
ref,
nextTick,
computed,
watch,
reactive,
onMounted,
onBeforeUnmount,
provide,
} from 'vue'
import { useRoute } from 'vue-router'
import applicationApi from '@/api/application/application'
import chatAPI from '@/api/chat/chat'
import SystemResourceManagementApplicationAPI from "@/api/system-resource-management/application.ts"
import SystemResourceManagementApplicationAPI from '@/api/system-resource-management/application.ts'
import syetrmResourceManagementChatLogApi from '@/api/system-resource-management/chat-log'
import chatLogApi from '@/api/application/chat-log'
import { ChatManagement, type chatType } from '@/api/type/application'
@ -72,6 +130,11 @@ import UserForm from '@/components/ai-chat/component/user-form/index.vue'
import Control from '@/components/ai-chat/component/control/index.vue'
import { t } from '@/locales'
import bus from '@/bus'
provide('upload', (file: any, loading?: Ref<boolean>) => {
return props.type === 'debug-ai-chat'
? applicationApi.postUploadFile(file, 'TEMPORARY_120_MINUTE', 'TEMPORARY_120_MINUTE', loading)
: chatAPI.postUploadFile(file, chartOpenId.value, 'CHAT', loading)
})
const transcribing = ref<boolean>(false)
defineOptions({ name: 'AiChat' })
const route = useRoute()
@ -301,17 +364,25 @@ const getChatRecordDetailsAPI = (row: any) => {
if (row.record_id) {
if (props.type === 'debug-ai-chat') {
if (route.path.includes('resource-management')) {
return syetrmResourceManagementChatLogApi
.getChatRecordDetails(id || props.appId, row.chat_id, row.record_id, loading)
return syetrmResourceManagementChatLogApi.getChatRecordDetails(
id || props.appId,
row.chat_id,
row.record_id,
loading,
)
} else {
return chatLogApi
.getChatRecordDetails(id || props.appId, row.chat_id, row.record_id, loading)
return chatLogApi.getChatRecordDetails(
id || props.appId,
row.chat_id,
row.record_id,
loading,
)
}
} else {
return chatAPI.getChatRecord(row.chat_id, row.record_id, loading)
}
}
return Promise.reject("404")
return Promise.reject('404')
}
/**
* 获取对话详情
@ -326,7 +397,6 @@ function getSourceDetail(row: any) {
}
})
})
}
/**
* 对话

View File

@ -2,43 +2,51 @@ import { t } from '@/locales'
const input_type_list = [
{
label: t('dynamicsForm.input_type_list.TextInput'),
value: 'TextInput'
value: 'TextInput',
},
{
label: t('dynamicsForm.input_type_list.PasswordInput'),
value: 'PasswordInput'
value: 'PasswordInput',
},
{
label: t('dynamicsForm.input_type_list.Slider'),
value: 'Slider'
value: 'Slider',
},
{
label: t('dynamicsForm.input_type_list.SwitchInput'),
value: 'SwitchInput'
value: 'SwitchInput',
},
{
label: t('dynamicsForm.input_type_list.SingleSelect'),
value: 'SingleSelect'
value: 'SingleSelect',
},
{
label: t('dynamicsForm.input_type_list.MultiSelect'),
value: 'MultiSelect'
value: 'MultiSelect',
},
{
label: t('dynamicsForm.input_type_list.DatePicker'),
value: 'DatePicker'
value: 'DatePicker',
},
{
label: t('dynamicsForm.input_type_list.JsonInput'),
value: 'JsonInput'
value: 'JsonInput',
},
{
label: t('dynamicsForm.input_type_list.RadioCard'),
value: 'RadioCard'
value: 'RadioCard',
},
{
label: t('dynamicsForm.input_type_list.RadioRow'),
value: 'RadioRow'
}
value: 'RadioRow',
},
{
label: t('dynamicsForm.input_type_list.UploadInput'),
value: 'UploadInput',
},
{
label: t('dynamicsForm.input_type_list.TextareaInput'),
value: 'TextareaInput',
},
]
export { input_type_list }

View File

@ -8,9 +8,32 @@
<el-row style="width: 100%" :gutter="10">
<el-radio-group v-model="formValue.assignment_method">
<el-radio :value="item.value" size="large" v-for="item in assignment_method_option_list">{{
item.label
}}</el-radio>
<el-radio :value="item.value" size="large" v-for="item in assignment_method_option_list"
>{{ item.label }}
<el-popover
width="300px"
v-if="item.value == 'ref_variables'"
class="box-item"
placement="top-start"
>
{{ $t('dynamicsForm.AssignmentMethod.ref_variables.popover') }}:<br />
[<br />
{<br />
"label": "xx",<br />
"value": "xx",<br />
"default": false<br />
}<br />
]<br />
label: {{ $t('dynamicsForm.AssignmentMethod.ref_variables.popover_label') }}
{{ $t('common.required') }}<br />
value: {{ $t('dynamicsForm.AssignmentMethod.ref_variables.popover_value') }}
{{ $t('common.required') }}<br />
default: {{ $t('dynamicsForm.AssignmentMethod.ref_variables.popover_default') }}
<template #reference>
<el-icon><InfoFilled /></el-icon>
</template>
</el-popover>
</el-radio>
</el-radio-group>
</el-row>
</el-form-item>

View File

@ -8,9 +8,32 @@
<el-row style="width: 100%" :gutter="10">
<el-radio-group v-model="formValue.assignment_method">
<el-radio :value="item.value" size="large" v-for="item in assignment_method_option_list">{{
item.label
}}</el-radio>
<el-radio :value="item.value" size="large" v-for="item in assignment_method_option_list"
>{{ item.label }}
<el-popover
width="300px"
v-if="item.value == 'ref_variables'"
class="box-item"
placement="top-start"
>
{{ $t('dynamicsForm.AssignmentMethod.ref_variables.popover') }}:<br />
[<br />
{<br />
"label": "xx",<br />
"value": "xx",<br />
"default": false<br />
}<br />
]<br />
label: {{ $t('dynamicsForm.AssignmentMethod.ref_variables.popover_label') }}
{{ $t('common.required') }}<br />
value: {{ $t('dynamicsForm.AssignmentMethod.ref_variables.popover_value') }}
{{ $t('common.required') }}<br />
default:{{ $t('dynamicsForm.AssignmentMethod.ref_variables.popover_default') }}
<template #reference>
<el-icon><InfoFilled /></el-icon>
</template>
</el-popover>
</el-radio>
</el-radio-group>
</el-row>
</el-form-item>

View File

@ -8,9 +8,31 @@
<el-row style="width: 100%" :gutter="10">
<el-radio-group v-model="formValue.assignment_method">
<el-radio :value="item.value" size="large" v-for="item in assignment_method_option_list">{{
item.label
}}</el-radio>
<el-radio :value="item.value" size="large" v-for="item in assignment_method_option_list"
>{{ item.label }}
<el-popover
width="300px"
v-if="item.value == 'ref_variables'"
class="box-item"
placement="top-start"
>
{{ $t('dynamicsForm.AssignmentMethod.ref_variables.popover') }}:<br />
[<br />
{<br />
"label": "xx",<br />
"value": "xx",<br />
"default": false<br />
}<br />
]<br />
label: {{ $t('dynamicsForm.AssignmentMethod.ref_variables.popover_label') }}
{{ $t('common.required') }}<br />
value: {{ $t('dynamicsForm.AssignmentMethod.ref_variables.popover_value') }}
{{ $t('common.required') }}<br />
default: {{ $t('dynamicsForm.AssignmentMethod.ref_variables.popover_default') }}
<template #reference>
<el-icon><InfoFilled /></el-icon>
</template> </el-popover
></el-radio>
</el-radio-group>
</el-row>
</el-form-item>

View File

@ -0,0 +1,195 @@
<template>
<el-form-item :label="$t('dynamicsForm.TextInput.length.label')" required>
<el-row class="w-full">
<el-col :span="11">
<el-form-item
:rules="[
{
required: true,
message: $t('dynamicsForm.TextInput.length.minRequired'),
trigger: 'change',
},
]"
prop="minlength"
>
<el-input-number
style="width: 100%"
:min="1"
:step="1"
step-strictly
v-model="formValue.minlength"
controls-position="right"
/>
</el-form-item>
</el-col>
<el-col :span="2" class="text-center">
<span>-</span>
</el-col>
<el-col :span="11">
<el-form-item
:rules="[
{
required: true,
message: $t('dynamicsForm.TextInput.length.maxRequired'),
trigger: 'change',
},
]"
prop="maxlength"
>
<el-input-number
style="width: 100%"
:min="formValue.minlength > formValue.maxlength ? formValue.minlength : 1"
step-strictly
:step="1"
v-model="formValue.maxlength"
controls-position="right"
/></el-form-item>
</el-col>
</el-row>
</el-form-item>
<el-form-item
class="defaultValueItem"
:required="formValue.required"
prop="default_value"
:label="$t('dynamicsForm.default.label')"
:rules="
formValue.required
? [
{
required: true,
message: `${$t('dynamicsForm.default.label')}${$t('dynamicsForm.default.requiredMessage')}`,
},
...rules,
]
: rules
"
>
<div class="defaultValueCheckbox">
<el-checkbox
v-model="formValue.show_default_value"
:label="$t('dynamicsForm.default.show')"
/>
</div>
<el-input
v-model="formValue.default_value"
:maxlength="formValue.maxlength"
:minlength="formValue.minlength"
:placeholder="$t('dynamicsForm.default.placeholder')"
show-word-limit
:autosize="{ minRows: 3, maxRows: 3 }"
type="textarea"
/>
</el-form-item>
</template>
<script setup lang="ts">
import { computed, onMounted, watch } from 'vue'
import { t } from '@/locales'
const props = defineProps<{
modelValue: any
}>()
const emit = defineEmits(['update:modelValue'])
const formValue = computed({
set: (item) => {
emit('update:modelValue', item)
},
get: () => {
return props.modelValue
},
})
watch(
() => formValue.value.minlength,
() => {
if (formValue.value.minlength > formValue.value.maxlength) {
formValue.value.maxlength = formValue.value.minlength
}
},
)
const getData = () => {
return {
input_type: 'TextareaInput',
attrs: {
maxlength: formValue.value.maxlength,
minlength: formValue.value.minlength,
'show-word-limit': true,
autosize: { minRows: 3, maxRows: 3 },
},
default_value: formValue.value.default_value,
show_default_value: formValue.value.show_default_value,
props_info: {
rules: formValue.value.required
? [
{
required: true,
message: `${formValue.value.label} ${t('dynamicsForm.default.requiredMessage')}`,
},
{
min: formValue.value.minlength,
max: formValue.value.maxlength,
message: `${formValue.value.label}${t('dynamicsForm.TextInput.length.requiredMessage1')} ${formValue.value.minlength} ${t('dynamicsForm.TextInput.length.requiredMessage2')} ${formValue.value.maxlength} ${t('dynamicsForm.TextInput.length.requiredMessage3')}`,
trigger: 'blur',
},
]
: [
{
min: formValue.value.minlength,
max: formValue.value.maxlength,
message: `${formValue.value.label}${t('dynamicsForm.TextInput.length.requiredMessage1')} ${formValue.value.minlength} ${t('dynamicsForm.TextInput.length.requiredMessage2')} ${formValue.value.maxlength} ${t('dynamicsForm.TextInput.length.requiredMessage3')}`,
trigger: 'blur',
},
],
},
}
}
const rander = (form_data: any) => {
const attrs = form_data.attrs || {}
formValue.value.minlength = attrs.minlength
formValue.value.maxlength = attrs.maxlength
formValue.value.default_value = form_data.default_value
formValue.value.show_default_value = form_data.show_default_value
}
const rangeRules = [
{
required: true,
validator: (rule: any, value: any, callback: any) => {
if (!formValue.value.minlength) {
callback(new Error(t('dynamicsForm.TextInput.length.requiredMessage4')))
}
if (!formValue.value.maxlength) {
callback(new Error(t('dynamicsForm.TextInput.length.requiredMessage4')))
}
return true
},
message: `${formValue.value.label} ${t('dynamicsForm.default.requiredMessage')}`,
},
]
const rules = computed(() => [
{
min: formValue.value.minlength,
max: formValue.value.maxlength,
message: `${t('dynamicsForm.TextInput.length.requiredMessage1')} ${formValue.value.minlength} ${t('dynamicsForm.TextInput.length.requiredMessage2')} ${formValue.value.maxlength} ${t('dynamicsForm.TextInput.length.requiredMessage3')}`,
trigger: 'blur',
},
])
defineExpose({ getData, rander })
onMounted(() => {
formValue.value.minlength = 0
formValue.value.maxlength = 200
formValue.value.default_value = ''
if (formValue.value.show_default_value === undefined) {
formValue.value.show_default_value = true
}
})
</script>
<style lang="scss" scoped>
.defaultValueItem {
position: relative;
.defaultValueCheckbox {
position: absolute;
right: 0;
top: -35px;
}
}
</style>

View File

@ -0,0 +1,153 @@
<template>
<el-form-item :label="$t('dynamicsForm.UploadInput.limit.label')" required prop="limit">
<el-input-number
style="width: 100%"
v-model="formValue.limit"
:rules="[
{
required: true,
message: $t('dynamicsForm.UploadInput.limit.required'),
trigger: 'change',
},
]"
:min="0"
controls-position="right"
/>
</el-form-item>
<el-form-item
:label="$t('dynamicsForm.UploadInput.max_file_size.label')"
required
prop="max_file_size"
:rules="[
{
required: true,
message: $t('dynamicsForm.UploadInput.max_file_size.required'),
trigger: 'change',
},
]"
>
<el-input-number
style="width: 100%"
v-model="formValue.max_file_size"
:min="0"
controls-position="right"
/>
</el-form-item>
<el-form-item
:label="$t('dynamicsForm.UploadInput.accept.label')"
required
prop="accept"
:rules="[
{
required: true,
message: $t('dynamicsForm.UploadInput.accept.required'),
trigger: 'change',
},
]"
>
<div class="gap-2" style="display: flex">
<el-tag
v-for="tag in formValue.accept"
:key="tag"
closable
:disable-transitions="false"
@close="handleClose(tag)"
>
{{ tag }}
</el-tag>
<el-input
v-if="inputVisible"
ref="InputRef"
v-model="inputValue"
class="w-20"
size="small"
@keyup.enter="handleInputConfirm"
@blur="handleInputConfirm"
/>
<el-button v-else class="button-new-tag" size="small" @click="showInput">
+ {{ $t('common.fileUpload.addExtensions') }}
</el-button>
</div>
</el-form-item>
</template>
<script setup lang="ts">
import { computed, onMounted, ref, nextTick } from 'vue'
import { ElMessage, type InputInstance } from 'element-plus'
import { t } from '@/locales'
const props = defineProps<{
modelValue: any
}>()
const inputValue = ref('')
const inputVisible = ref(false)
const InputRef = ref<InputInstance>()
const handleClose = (tag: string) => {
formValue.value.accept.splice(formValue.value.accept.indexOf(tag), 1)
}
const showInput = () => {
inputVisible.value = true
nextTick(() => {
InputRef.value!.input!.focus()
})
}
const handleInputConfirm = () => {
if (formValue.value.accept.find((item: string) => item === inputValue.value)) {
ElMessage.warning(t('common.fileUpload.existingExtensionsTip'))
return
}
if (inputValue.value) {
formValue.value.accept.push(inputValue.value)
}
inputVisible.value = false
inputValue.value = ''
}
const emit = defineEmits(['update:modelValue'])
const formValue = computed({
set: (item) => {
emit('update:modelValue', item)
},
get: () => {
return props.modelValue
},
})
const rander = (form_data: any) => {
formValue.value.default_value = []
formValue.value.limit = form_data.attrs.limit || 3
formValue.value.max_file_size = form_data.max_file_size || 10
formValue.value.accept = form_data.attrs.accept
? form_data.attrs.accept.split(',').map((item: string) => item.substring(1))
: ['jpg']
}
const getData = () => {
return {
input_type: 'UploadInput',
attrs: {
accept: formValue.value.accept.map((item: any) => '.' + item).join(','),
limit: formValue.value.limit,
},
max_file_size: formValue.value.max_file_size,
default_value: [],
show_default_value: formValue.value.show_default_value,
}
}
defineExpose({ getData, rander })
onMounted(() => {
formValue.value.default_value = []
formValue.value.limit = 3
formValue.value.max_file_size = 10
formValue.value.accept = ['jpg']
if (formValue.value.show_default_value === undefined) {
formValue.value.show_default_value = true
}
})
</script>
<style lang="scss" scoped>
.gap-2 {
gap: 0.5rem;
}
</style>

View File

@ -0,0 +1,5 @@
<template>
<el-input v-bind="$attrs" type="textarea"></el-input>
</template>
<script setup lang="ts"></script>
<style lang="scss"></style>

View File

@ -0,0 +1,123 @@
<template>
<el-upload
style="width: 80%"
v-loading="loading"
action="#"
v-bind="$attrs"
:auto-upload="false"
:on-change="(file: any, fileList: any) => uploadFile(file, fileList)"
v-model:file-list="model_value"
multiple
>
<el-button type="primary">{{ $t('chat.uploadFile.label') }}</el-button>
<template #file="{ file, index }"
><el-card style="--el-card-padding: 0" shadow="never">
<div
:class="[inputDisabled ? 'is-disabled' : '']"
style="
padding: 0 8px 0 8px;
display: flex;
justify-content: space-between;
align-items: center;
align-content: center;
"
>
<el-tooltip class="box-item" effect="dark" :content="file.name" placement="top-start">
<div style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 40%">
{{ file.name }}
</div></el-tooltip
>
<div>{{ formatSize(file.size) }}</div>
<el-icon @click="deleteFile(file)" style="cursor: pointer"><DeleteFilled /></el-icon>
</div>
</el-card>
</template>
</el-upload>
</template>
<script setup lang="ts">
import { computed, inject, ref, useAttrs } from 'vue'
import { ElMessage } from 'element-plus'
import type { FormField } from '@/components/dynamics-form/type'
import { t } from '@/locales'
import { useFormDisabled } from 'element-plus'
const inputDisabled = useFormDisabled()
const attrs = useAttrs() as any
const upload = inject('upload') as any
const props = withDefaults(defineProps<{ modelValue?: any; formField: FormField }>(), {
modelValue: () => [],
})
const emit = defineEmits(['update:modelValue'])
function formatSize(sizeInBytes: number) {
const units = ['B', 'KB', 'MB', 'GB', 'TB']
let size = sizeInBytes
let unitIndex = 0
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024
unitIndex++
}
return size.toFixed(2) + ' ' + units[unitIndex]
}
const deleteFile = (file: any) => {
if (inputDisabled) {
return
}
fileArray.value = fileArray.value.filter((f: any) => f.uid != file.uid)
emit('update:modelValue', fileArray.value)
}
const model_value = computed({
get: () => {
if (!model_value) {
emit('update:modelValue', [])
}
return props.modelValue
},
set: (v: Array<any>) => {
emit('update:modelValue', v)
},
})
const fileArray = ref<any>([])
const loading = ref<boolean>(false)
const uploadFile = async (file: any, fileList: Array<any>) => {
fileList.splice(fileList.indexOf(file), 1)
if (fileArray.value.find((f: any) => f.name === file.name)) {
ElMessage.warning(t('chat.uploadFile.fileRepeat'))
return
}
const max_file_size = (props.formField as any).max_file_size
if (file.size / 1024 / 1024 > max_file_size) {
ElMessage.warning(t('chat.uploadFile.sizeLimit') + max_file_size + 'MB')
return
}
if (fileList.length > attrs.limit) {
ElMessage.warning(
t('chat.uploadFile.limitMessage1') + attrs.limit + t('chat.uploadFile.limitMessage2'),
)
return
}
upload(file.raw, loading).then((ok: any) => {
const split_path = ok.data.split('/')
const file_id = split_path[split_path.length - 1]
fileArray.value?.push({ name: file.name, file_id, size: file.size })
emit('update:modelValue', fileArray.value)
})
}
</script>
<style lang="scss">
.is-disabled {
background-color: var(--el-fill-color-light);
color: var(--el-text-color-placeholder);
cursor: not-allowed;
&:hover {
cursor: not-allowed;
}
}
</style>

View File

@ -10,6 +10,8 @@ export default {
JsonInput: 'JSON',
RadioCard: 'Radio Card',
RadioRow: 'Radio Row',
UploadInput: 'File upload',
TextareaInput: 'Multiline Input',
},
default: {
label: 'Default',
@ -99,6 +101,20 @@ export default {
requiredMessage4: 'Text length is a required parameter',
},
},
UploadInput: {
limit: {
label: 'Maximum number of files per upload',
required: 'Maximum number of files is required',
},
max_file_size: {
label: 'Maximum file size (MB)',
required: 'Maximum file size is required',
},
accept: {
label: 'File type',
required: 'File type is required',
},
},
AssignmentMethod: {
label: 'Assignment Method',
custom: {
@ -106,6 +122,10 @@ export default {
},
ref_variables: {
label: 'Reference Variables',
popover: 'Variable values must comply with',
popover_label: 'Label',
popover_value: 'Value',
popover_default: 'Is Default',
},
},
}

View File

@ -71,6 +71,7 @@ export default {
imageMessage: '请解析图片内容',
fileMessage: '请解析文件内容',
errorMessage: '上传失败',
fileRepeat: '文件已存在',
},
executionDetails: {
title: '执行详情',

View File

@ -10,6 +10,8 @@ export default {
JsonInput: 'JSON文本框',
RadioCard: '选项卡',
RadioRow: '单行选项卡',
UploadInput: '文件上传',
TextareaInput: '多行文本框',
},
default: {
label: '默认值',
@ -99,13 +101,32 @@ export default {
requiredMessage4: '文本长度为必填参数',
},
},
UploadInput: {
limit: {
label: '单次上传最多文件数',
required: '单次上传最多文件数必填',
},
max_file_size: {
label: '每个文件最大(MB)',
required: '每个文件最大(MB)必填',
},
accept: {
label: '文件类型',
required: '文件类型必填',
},
},
AssignmentMethod: {
label: '赋值方式',
custom: {
label: '自定义',
},
ref_variables: {
label: '引用变量 ',
popover: '变量的值必须符合',
popover_label: '标签',
popover_value: '值',
popover_default: '是否为默认值',
},
},
}

View File

@ -10,6 +10,8 @@ export default {
JsonInput: 'JSON文字框',
RadioCard: '選項卡',
RadioRow: '單行選項卡',
UploadInput: '文件上傳',
TextareaInput: '多行文字方塊',
},
default: {
label: '預設值',
@ -99,13 +101,31 @@ export default {
requiredMessage4: '文字長度為必填參數',
},
},
UploadInput: {
limit: {
label: '單次上傳最多文件數',
required: '單次上傳最多文件數必填',
},
max_file_size: {
label: '每個文件最大(MB)',
required: '每個文件最大必填',
},
accept: {
label: '文件類型',
required: '文件類型必填',
},
},
AssignmentMethod: {
label: '賦值方式',
custom: {
label: '自定義',
},
ref_variables: {
label: '引用變量',
label: '參考變量',
popover: '變量的值必須符合',
popover_label: '標籤',
popover_value: '值',
popover_default: '是否為預設值',
},
},
}