mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
feat: knowledge workflow
This commit is contained in:
parent
c632a09f1f
commit
45b32bf405
|
|
@ -6,6 +6,7 @@ export enum SearchMode {
|
|||
|
||||
export enum WorkflowType {
|
||||
Base = 'base-node',
|
||||
KnowledgeBase = 'knowledge-base-node',
|
||||
Start = 'start-node',
|
||||
AiChat = 'ai-chat-node',
|
||||
SearchKnowledge = 'search-knowledge-node',
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@
|
|||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onBeforeUnmount, computed, nextTick, provide } from 'vue'
|
||||
import { ref, onBeforeMount, onBeforeUnmount, computed, nextTick, provide } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import type { Action } from 'element-plus'
|
||||
import Workflow from '@/workflow/index.vue'
|
||||
|
|
@ -159,6 +159,7 @@ import { EditionConst, PermissionConst, RoleConst } from '@/utils/permission/dat
|
|||
import permissionMap from '@/permission'
|
||||
import { WorkflowMode } from '@/enums/application'
|
||||
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
|
||||
import { knowledgeBaseNode } from '@/workflow/common/data'
|
||||
provide('getResourceDetail', () => detail)
|
||||
provide('workflowMode', WorkflowMode.Knowledge)
|
||||
provide('loopWorkflowMode', WorkflowMode.KnowledgeLoop)
|
||||
|
|
@ -423,18 +424,16 @@ function getDetail() {
|
|||
loadSharedApi({ type: 'knowledge', systemType: apiType.value })
|
||||
.getKnowledgeDetail(id)
|
||||
.then((res: any) => {
|
||||
let workspace = res.data?.work_flow
|
||||
if (!workspace) {
|
||||
workspace = {}
|
||||
}
|
||||
|
||||
detail.value = res.data
|
||||
detail.value.stt_model_id = res.data.stt_model
|
||||
detail.value.tts_model_id = res.data.tts_model
|
||||
detail.value.tts_type = res.data.tts_type
|
||||
saveTime.value = res.data?.update_time
|
||||
if (!detail.value.work_flow || !('nodes' in detail.value.work_flow)) {
|
||||
detail.value.work_flow = { nodes: [knowledgeBaseNode] }
|
||||
}
|
||||
detail.value.work_flow?.nodes
|
||||
?.filter((v: any) => v.id === 'base-node')
|
||||
?.filter((v: any) => v.id === 'knowledge-base-node')
|
||||
.map((v: any) => {
|
||||
apiInputParams.value = v.properties.api_input_field_list
|
||||
? v.properties.api_input_field_list.map((v: any) => {
|
||||
|
|
@ -526,7 +525,7 @@ const closeInterval = () => {
|
|||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
onBeforeMount(() => {
|
||||
getDetail()
|
||||
const workflowAutoSave = localStorage.getItem('workflowAutoSave')
|
||||
isSave.value = workflowAutoSave === 'true' ? true : false
|
||||
|
|
|
|||
|
|
@ -345,9 +345,12 @@ const nodeFields = computed(() => {
|
|||
})
|
||||
|
||||
function showOperate(type: string) {
|
||||
return ![WorkflowType.Start, WorkflowType.Base, WorkflowType.LoopStartNode.toString()].includes(
|
||||
type,
|
||||
)
|
||||
return ![
|
||||
WorkflowType.Start,
|
||||
WorkflowType.Base,
|
||||
WorkflowType.KnowledgeBase,
|
||||
WorkflowType.LoopStartNode.toString(),
|
||||
].includes(type)
|
||||
}
|
||||
const openNodeMenu = (anchorValue: any) => {
|
||||
showAnchor.value = true
|
||||
|
|
|
|||
|
|
@ -107,10 +107,11 @@ class AppNode extends HtmlResize.view {
|
|||
(pre, next) => [...pre, ...next],
|
||||
[],
|
||||
)
|
||||
const start_node_field_list = (
|
||||
this.props.graphModel.getNodeModelById('start-node') ||
|
||||
this.props.graphModel.getNodeModelById('loop-start-node')
|
||||
).get_node_field_list()
|
||||
const start_node_field_list =
|
||||
(
|
||||
this.props.graphModel.getNodeModelById('start-node') ||
|
||||
this.props.graphModel.getNodeModelById('loop-start-node')
|
||||
)?.get_node_field_list() || []
|
||||
return [...start_node_field_list, ...result]
|
||||
}
|
||||
|
||||
|
|
@ -414,7 +415,7 @@ class AppNodeModel extends HtmlResize.model {
|
|||
const showNode = this.properties.showNode === undefined ? true : this.properties.showNode
|
||||
const anchors: any = []
|
||||
|
||||
if (this.type !== WorkflowType.Base) {
|
||||
if (![WorkflowType.Base as string, WorkflowType.KnowledgeBase as string].includes(this.type)) {
|
||||
if (![WorkflowType.Start, WorkflowType.LoopStartNode.toString()].includes(this.type)) {
|
||||
anchors.push({
|
||||
x: x - width / 2 + 10,
|
||||
|
|
|
|||
|
|
@ -57,6 +57,28 @@ export const baseNode = {
|
|||
user_input_field_list: [],
|
||||
},
|
||||
}
|
||||
export const knowledgeBaseNode = {
|
||||
id: WorkflowType.KnowledgeBase,
|
||||
type: WorkflowType.KnowledgeBase,
|
||||
x: 360,
|
||||
y: 2761.3875,
|
||||
text: '',
|
||||
properties: {
|
||||
height: 728.375,
|
||||
stepName: t('views.applicationWorkflow.nodes.baseNode.label'),
|
||||
input_field_list: [],
|
||||
node_data: {
|
||||
name: '',
|
||||
desc: '',
|
||||
prologue: t('views.application.form.defaultPrologue'),
|
||||
tts_type: 'BROWSER',
|
||||
},
|
||||
config: {},
|
||||
showNode: true,
|
||||
user_input_config: { title: t('chat.userInput') },
|
||||
user_input_field_list: [],
|
||||
},
|
||||
}
|
||||
/**
|
||||
* 说明
|
||||
* type 与 nodes 文件对应
|
||||
|
|
@ -880,6 +902,7 @@ export const nodeDict: any = {
|
|||
[WorkflowType.VideoUnderstandNode]: videoUnderstandNode,
|
||||
[WorkflowType.ParameterExtractionNode]: parameterExtractionNode,
|
||||
[WorkflowType.VariableAggregationNode]: variableAggregationNode,
|
||||
[WorkflowType.KnowledgeBase]: knowledgeBaseNode,
|
||||
}
|
||||
|
||||
export function isWorkFlow(type: string | undefined) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
<template>
|
||||
<el-avatar shape="square" style="background: #ff8800">
|
||||
<img src="@/assets/workflow/icon_hi.svg" style="width: 75%" alt="" />
|
||||
</el-avatar>
|
||||
</template>
|
||||
<script setup lang="ts"></script>
|
||||
|
|
@ -78,7 +78,6 @@ const renderGraphData = (data?: any) => {
|
|||
strokeWidth: 1,
|
||||
},
|
||||
})
|
||||
lf.value.graphModel.get = 'sdasdaad'
|
||||
lf.value.on('graph:rendered', () => {
|
||||
flowId.value = lf.value.graphModel.flowId
|
||||
})
|
||||
|
|
|
|||
|
|
@ -15,8 +15,9 @@ class BaseModel extends AppNodeModel {
|
|||
return 600
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
type: 'base-node',
|
||||
type: 'knowledge-base-node',
|
||||
model: BaseModel,
|
||||
view: BaseNode
|
||||
view: BaseNode,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,168 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="
|
||||
isEdit
|
||||
? $t('common.param.editParam')
|
||||
: $t('common.param.addParam')
|
||||
"
|
||||
v-model="dialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
:before-close="close"
|
||||
append-to-body
|
||||
>
|
||||
<DynamicsFormConstructor
|
||||
v-model="currentRow"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
:input_type_list="inputTypeList"
|
||||
ref="DynamicsFormConstructorRef"
|
||||
></DynamicsFormConstructor>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="close"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="submit()" :loading="loading">
|
||||
{{ isEdit ? $t('common.save') : $t('common.add') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import DynamicsFormConstructor from '@/components/dynamics-form/constructor/index.vue'
|
||||
import type { FormField } from '@/components/dynamics-form/type'
|
||||
import _ from 'lodash'
|
||||
import { t } from '@/locales'
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const DynamicsFormConstructorRef = ref()
|
||||
const loading = ref<boolean>(false)
|
||||
const isEdit = ref(false)
|
||||
const currentItem = ref<FormField | any>()
|
||||
const check_field = (field_list: Array<string>, obj: any) => {
|
||||
return field_list.every((field) => _.get(obj, field, undefined) !== undefined)
|
||||
}
|
||||
const currentRow = computed(() => {
|
||||
if (currentItem.value) {
|
||||
const row = currentItem.value
|
||||
switch (row.type) {
|
||||
case 'input':
|
||||
if (check_field(['field', 'input_type', 'label', 'required', 'attrs'], currentItem.value)) {
|
||||
return currentItem.value
|
||||
}
|
||||
return {
|
||||
attrs: row.attrs || { maxlength: 200, minlength: 0 },
|
||||
field: row.field || row.variable,
|
||||
input_type: 'TextInput',
|
||||
label: row.label || row.name,
|
||||
default_value: row.default_value,
|
||||
required: row.required != undefined ? row.required : row.is_required
|
||||
}
|
||||
case 'select':
|
||||
if (
|
||||
check_field(
|
||||
['field', 'input_type', 'label', 'required', 'option_list'],
|
||||
currentItem.value
|
||||
)
|
||||
) {
|
||||
return currentItem.value
|
||||
}
|
||||
return {
|
||||
attrs: row.attrs || {},
|
||||
field: row.field || row.variable,
|
||||
input_type: 'SingleSelect',
|
||||
label: row.label || row.name,
|
||||
default_value: row.default_value,
|
||||
required: row.required != undefined ? row.required : row.is_required,
|
||||
option_list: row.option_list
|
||||
? row.option_list
|
||||
: row.optionList.map((o: any) => {
|
||||
return { key: o, value: o }
|
||||
})
|
||||
}
|
||||
|
||||
case 'date':
|
||||
if (
|
||||
check_field(
|
||||
[
|
||||
'field',
|
||||
'input_type',
|
||||
'label',
|
||||
'required',
|
||||
'attrs.format',
|
||||
'attrs.value-format',
|
||||
'attrs.type'
|
||||
],
|
||||
currentItem.value
|
||||
)
|
||||
) {
|
||||
return currentItem.value
|
||||
}
|
||||
return {
|
||||
field: row.field || row.variable,
|
||||
input_type: 'DatePicker',
|
||||
label: row.label || row.name,
|
||||
default_value: row.default_value,
|
||||
required: row.required != undefined ? row.required : row.is_required,
|
||||
attrs: {
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
'value-format': 'YYYY-MM-DD HH:mm:ss',
|
||||
type: 'datetime'
|
||||
}
|
||||
}
|
||||
default:
|
||||
return currentItem.value
|
||||
}
|
||||
} else {
|
||||
return { input_type: 'TextInput', required: false, attrs: { maxlength: 200, minlength: 0 }, show_default_value: true }
|
||||
}
|
||||
})
|
||||
const currentIndex = ref(null)
|
||||
const inputTypeList = ref([
|
||||
{ label: t('dynamicsForm.input_type_list.TextInput'), value: 'TextInputConstructor' },
|
||||
{ label: t('dynamicsForm.input_type_list.PasswordInput'), value: 'PasswordInputConstructor' },
|
||||
{ label: t('dynamicsForm.input_type_list.SingleSelect'), value: 'SingleSelectConstructor' },
|
||||
{ label: t('dynamicsForm.input_type_list.MultiSelect'), value: 'MultiSelectConstructor' },
|
||||
{ label: t('dynamicsForm.input_type_list.RadioCard'), value: 'RadioCardConstructor' },
|
||||
{ label: t('dynamicsForm.input_type_list.DatePicker'), value: 'DatePickerConstructor' },
|
||||
{ label: t('dynamicsForm.input_type_list.SwitchInput'), value: 'SwitchInputConstructor' },
|
||||
])
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
||||
const open = (row: any, index: any) => {
|
||||
dialogVisible.value = true
|
||||
|
||||
if (row) {
|
||||
isEdit.value = true
|
||||
currentItem.value = cloneDeep(row)
|
||||
currentIndex.value = index
|
||||
} else {
|
||||
currentItem.value = null
|
||||
}
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
dialogVisible.value = false
|
||||
isEdit.value = false
|
||||
currentIndex.value = null
|
||||
currentItem.value = null as any
|
||||
}
|
||||
|
||||
const submit = async () => {
|
||||
const formEl = DynamicsFormConstructorRef.value
|
||||
if (!formEl) return
|
||||
await formEl.validate().then(() => {
|
||||
emit('refresh', formEl?.getData(), currentIndex.value)
|
||||
isEdit.value = false
|
||||
currentItem.value = null as any
|
||||
currentIndex.value = null
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open, close })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,222 @@
|
|||
<template>
|
||||
<div class="flex-between mb-16">
|
||||
<h5 class="break-all ellipsis lighter" style="max-width: 80%" :title="inputFieldConfig.title">
|
||||
{{ inputFieldConfig.title }}
|
||||
</h5>
|
||||
<div>
|
||||
<el-button type="primary" link @click="openChangeTitleDialog">
|
||||
<AppIcon iconName="app-setting"></AppIcon>
|
||||
</el-button>
|
||||
<span class="ml-4">
|
||||
<el-button link type="primary" @click="openAddDialog()">
|
||||
<AppIcon iconName="app-add-outlined" class="mr-4"></AppIcon>
|
||||
{{ $t('common.add') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<el-table
|
||||
v-if="props.nodeModel.properties.user_input_field_list?.length > 0"
|
||||
:data="props.nodeModel.properties.user_input_field_list"
|
||||
class="mb-16"
|
||||
ref="tableRef"
|
||||
row-key="field"
|
||||
>
|
||||
<el-table-column prop="field" :label="$t('dynamicsForm.paramForm.field.label')" width="95">
|
||||
<template #default="{ row }">
|
||||
<span :title="row.field" class="ellipsis-1">{{ row.field }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="label" :label="$t('dynamicsForm.paramForm.name.label')">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.label && row.label.input_type === 'TooltipLabel'">
|
||||
<span :title="row.label.label" class="ellipsis-1">
|
||||
{{ row.label.label }}
|
||||
</span>
|
||||
</span>
|
||||
<span v-else>
|
||||
<span :title="row.label" class="ellipsis-1">
|
||||
{{ row.label }}
|
||||
</span></span
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('dynamicsForm.paramForm.input_type.label')" width="95">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="info" class="info-tag" v-if="row.input_type === 'TextInput'">{{
|
||||
$t('dynamicsForm.input_type_list.TextInput')
|
||||
}}</el-tag>
|
||||
<el-tag type="info" class="info-tag" v-if="row.input_type === 'PasswordInput'">{{
|
||||
$t('dynamicsForm.input_type_list.PasswordInput')
|
||||
}}</el-tag>
|
||||
<el-tag type="info" class="info-tag" v-if="row.input_type === 'Slider'">{{
|
||||
$t('dynamicsForm.input_type_list.Slider')
|
||||
}}</el-tag>
|
||||
<el-tag type="info" class="info-tag" v-if="row.input_type === 'SwitchInput'">{{
|
||||
$t('dynamicsForm.input_type_list.SwitchInput')
|
||||
}}</el-tag>
|
||||
<el-tag type="info" class="info-tag" v-if="row.input_type === 'SingleSelect'">{{
|
||||
$t('dynamicsForm.input_type_list.SingleSelect')
|
||||
}}</el-tag>
|
||||
<el-tag type="info" class="info-tag" v-if="row.input_type === 'MultiSelect'">{{
|
||||
$t('dynamicsForm.input_type_list.MultiSelect')
|
||||
}}</el-tag>
|
||||
<el-tag type="info" class="info-tag" v-if="row.input_type === 'RadioCard'">{{
|
||||
$t('dynamicsForm.input_type_list.RadioCard')
|
||||
}}</el-tag>
|
||||
<el-tag type="info" class="info-tag" v-if="row.input_type === 'DatePicker'">{{
|
||||
$t('dynamicsForm.input_type_list.DatePicker')
|
||||
}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="default_value" :label="$t('dynamicsForm.default.label')">
|
||||
<template #default="{ row }">
|
||||
<span :title="row.default_value" class="ellipsis-1">{{ getDefaultValue(row) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('common.required')">
|
||||
<template #default="{ row }">
|
||||
<div @click.stop>
|
||||
<el-switch disabled size="small" v-model="row.required" />
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('common.operation')" align="left" width="90">
|
||||
<template #default="{ row, $index }">
|
||||
<span class="mr-4">
|
||||
<el-tooltip effect="dark" :content="$t('common.modify')" placement="top">
|
||||
<el-button type="primary" text @click.stop="openAddDialog(row, $index)">
|
||||
<AppIcon iconName="app-edit"></AppIcon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-tooltip effect="dark" :content="$t('common.delete')" placement="top">
|
||||
<el-button type="primary" text @click="deleteField($index)">
|
||||
<AppIcon iconName="app-delete"></AppIcon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<UserFieldFormDialog ref="UserFieldFormDialogRef" @refresh="refreshFieldList" />
|
||||
<UserInputTitleDialog ref="UserInputTitleDialogRef" @refresh="refreshFieldTitle" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { set, cloneDeep } from 'lodash'
|
||||
import Sortable from 'sortablejs'
|
||||
import UserFieldFormDialog from './UserFieldFormDialog.vue'
|
||||
import { MsgError } from '@/utils/message'
|
||||
import { t } from '@/locales'
|
||||
import UserInputTitleDialog from '@/workflow/nodes/base-node/component/UserInputTitleDialog.vue'
|
||||
const props = defineProps<{ nodeModel: any }>()
|
||||
|
||||
const tableRef = ref()
|
||||
const UserFieldFormDialogRef = ref()
|
||||
const UserInputTitleDialogRef = ref()
|
||||
const inputFieldList = ref<any[]>([])
|
||||
const inputFieldConfig = ref({ title: t('chat.userInput') })
|
||||
|
||||
function openAddDialog(data?: any, index?: any) {
|
||||
UserFieldFormDialogRef.value.open(data, index)
|
||||
}
|
||||
|
||||
function openChangeTitleDialog() {
|
||||
UserInputTitleDialogRef.value.open(inputFieldConfig.value)
|
||||
}
|
||||
|
||||
function deleteField(index: any) {
|
||||
inputFieldList.value.splice(index, 1)
|
||||
props.nodeModel.graphModel.eventCenter.emit('refreshFieldList')
|
||||
const fields = inputFieldList.value.map((item) => ({
|
||||
label: item.label.label,
|
||||
value: item.field,
|
||||
}))
|
||||
|
||||
set(props.nodeModel.properties.config, 'fields', fields)
|
||||
onDragHandle()
|
||||
}
|
||||
|
||||
function refreshFieldList(data: any, index: any) {
|
||||
for (let i = 0; i < inputFieldList.value.length; i++) {
|
||||
if (inputFieldList.value[i].field === data.field && index !== i) {
|
||||
MsgError(t('views.applicationWorkflow.tip.paramErrorMessage') + data.field)
|
||||
return
|
||||
}
|
||||
}
|
||||
if (index !== null) {
|
||||
inputFieldList.value.splice(index, 1, data)
|
||||
} else {
|
||||
inputFieldList.value.push(data)
|
||||
}
|
||||
UserFieldFormDialogRef.value.close()
|
||||
const fields = inputFieldList.value.map((item) => ({
|
||||
label: item.label.label,
|
||||
value: item.field,
|
||||
}))
|
||||
|
||||
set(props.nodeModel.properties.config, 'fields', fields)
|
||||
onDragHandle()
|
||||
}
|
||||
|
||||
function refreshFieldTitle(data: any) {
|
||||
inputFieldConfig.value = data
|
||||
UserInputTitleDialogRef.value.close()
|
||||
}
|
||||
|
||||
const getDefaultValue = (row: any) => {
|
||||
if (row.input_type === 'PasswordInput') {
|
||||
return '******'
|
||||
}
|
||||
if (row.default_value) {
|
||||
const default_value = row.option_list
|
||||
?.filter((v: any) => row.default_value.indexOf(v.value) > -1)
|
||||
.map((v: any) => v.label)
|
||||
.join(',')
|
||||
if (default_value) {
|
||||
return default_value
|
||||
}
|
||||
return row.default_value
|
||||
}
|
||||
if (row.default_value !== undefined) {
|
||||
return row.default_value
|
||||
}
|
||||
}
|
||||
|
||||
function onDragHandle() {
|
||||
if (!tableRef.value) return
|
||||
|
||||
// 获取表格的 tbody DOM 元素
|
||||
const wrapper = tableRef.value.$el as HTMLElement
|
||||
const tbody = wrapper.querySelector('.el-table__body-wrapper tbody')
|
||||
if (!tbody) return
|
||||
// 初始化 Sortable
|
||||
Sortable.create(tbody as HTMLElement, {
|
||||
animation: 150,
|
||||
ghostClass: 'ghost-row',
|
||||
onEnd: (evt) => {
|
||||
if (evt.oldIndex === undefined || evt.newIndex === undefined) return
|
||||
// 更新数据顺序
|
||||
const items = cloneDeep([...inputFieldList.value])
|
||||
const [movedItem] = items.splice(evt.oldIndex, 1)
|
||||
items.splice(evt.newIndex, 0, movedItem)
|
||||
inputFieldList.value = items
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
set(props.nodeModel.properties, 'user_input_field_list', inputFieldList)
|
||||
if (props.nodeModel.properties.config) {
|
||||
inputFieldConfig.value = props.nodeModel.properties.user_input_config
|
||||
}
|
||||
set(props.nodeModel.properties, 'user_input_config', inputFieldConfig)
|
||||
onDragHandle()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('common.setting')"
|
||||
v-model="dialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
:destroy-on-close="true"
|
||||
:before-close="close"
|
||||
append-to-body
|
||||
>
|
||||
<el-form
|
||||
label-position="top"
|
||||
ref="fieldFormRef"
|
||||
:rules="rules"
|
||||
:model="form"
|
||||
require-asterisk-position="right"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item :label="$t('common.title')" prop="title">
|
||||
<el-input
|
||||
v-model="form.title"
|
||||
maxlength="64"
|
||||
show-word-limit
|
||||
@blur="form.title = form.title.trim()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false"> {{ $t('common.cancel') }} </el-button>
|
||||
<el-button type="primary" @click="submit(fieldFormRef)" :loading="loading">
|
||||
{{ $t('common.save') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { t } from '@/locales'
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const fieldFormRef = ref()
|
||||
const loading = ref<boolean>(false)
|
||||
|
||||
const form = ref<any>({
|
||||
title: t('chat.userInput'),
|
||||
})
|
||||
|
||||
const rules = reactive({
|
||||
title: [
|
||||
{ required: true, message: t('dynamicsForm.paramForm.name.requiredMessage'), trigger: 'blur' },
|
||||
],
|
||||
})
|
||||
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
|
||||
const open = (row: any) => {
|
||||
if (row) {
|
||||
form.value = cloneDeep(row)
|
||||
}
|
||||
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
emit('refresh', form.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
defineExpose({ open, close })
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import BaseNodeVue from './index.vue'
|
||||
import { AppNode, AppNodeModel } from '@/workflow/common/app-node'
|
||||
|
||||
class BaseNode extends AppNode {
|
||||
constructor(props: any) {
|
||||
super(props, BaseNodeVue)
|
||||
}
|
||||
}
|
||||
|
||||
class BaseModel extends AppNodeModel {
|
||||
constructor(data: any, graphModel: any) {
|
||||
super(data, graphModel)
|
||||
}
|
||||
get_width() {
|
||||
return 600
|
||||
}
|
||||
}
|
||||
export default {
|
||||
type: 'knowledge-base-node',
|
||||
model: BaseModel,
|
||||
view: BaseNode,
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<template>
|
||||
<NodeContainer :nodeModel="nodeModel">
|
||||
<UserInputFieldTable ref="UserInputFieldTableFef" :node-model="nodeModel" />
|
||||
</NodeContainer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { set } from 'lodash'
|
||||
import NodeContainer from '@/workflow/common/NodeContainer.vue'
|
||||
import type { FormInstance } from 'element-plus'
|
||||
import { ref, computed, onMounted, inject } from 'vue'
|
||||
import { t } from '@/locales'
|
||||
import UserInputFieldTable from './component/UserInputFieldTable.vue'
|
||||
|
||||
const getResourceDetail = inject('getResourceDetail') as any
|
||||
|
||||
const props = defineProps<{ nodeModel: any }>()
|
||||
|
||||
const UserInputFieldTableFef = ref()
|
||||
|
||||
const baseNodeFormRef = ref<FormInstance>()
|
||||
|
||||
const resource = getResourceDetail()
|
||||
|
||||
onMounted(() => {})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-form-item__label) {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue