mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-28 23:32:48 +00:00
feat: Form nodes support file upload and multi-line text (#3879)
This commit is contained in:
parent
31d71d137a
commit
d26883581c
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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) {
|
|||
}
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
/**
|
||||
* 对话
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<el-input v-bind="$attrs" type="textarea"></el-input>
|
||||
</template>
|
||||
<script setup lang="ts"></script>
|
||||
<style lang="scss"></style>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ export default {
|
|||
imageMessage: '请解析图片内容',
|
||||
fileMessage: '请解析文件内容',
|
||||
errorMessage: '上传失败',
|
||||
fileRepeat: '文件已存在',
|
||||
},
|
||||
executionDetails: {
|
||||
title: '执行详情',
|
||||
|
|
|
|||
|
|
@ -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: '是否为默认值',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: '是否為預設值',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue