feat: Create workflow applications that support template selection

This commit is contained in:
wangdan-fit2cloud 2025-02-24 17:29:54 +08:00 committed by GitHub
parent 6b72611b72
commit 4367b1c650
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 99 additions and 28 deletions

View File

@ -46,6 +46,10 @@ export default {
simplePlaceholder: 'Suitable for beginners to create assistant.', simplePlaceholder: 'Suitable for beginners to create assistant.',
workflowPlaceholder: 'Suitable for advanced users to customize the workflow of assistant' workflowPlaceholder: 'Suitable for advanced users to customize the workflow of assistant'
}, },
appTemplate: {
blankApp: 'Blank APP',
assistantApp: 'Knowledge Assistant'
},
aiModel: { aiModel: {
label: 'AI Model', label: 'AI Model',
placeholder: 'Please select an AI model' placeholder: 'Please select an AI model'
@ -110,7 +114,7 @@ export default {
reasoningContent: { reasoningContent: {
label: 'Output Thinking', label: 'Output Thinking',
tooltip: tooltip:
'Please set the thinking label based on the model\'s return, and the content in the middle of the label will be recognized as the thinking process.', "Please set the thinking label based on the model's return, and the content in the middle of the label will be recognized as the thinking process.",
start: 'Start', start: 'Start',
end: 'End' end: 'End'
} }
@ -211,7 +215,7 @@ export default {
slackSetting: { slackSetting: {
title: 'Slack Configuration', title: 'Slack Configuration',
signingSecretPlaceholder: 'Please enter signing secret', signingSecretPlaceholder: 'Please enter signing secret',
botUserTokenPlaceholder: 'Please enter bot user token', botUserTokenPlaceholder: 'Please enter bot user token'
}, },
copyUrl: 'Copy the link and fill it in' copyUrl: 'Copy the link and fill it in'
}, },

View File

@ -41,6 +41,10 @@ export default {
simplePlaceholder: '适合新手创建小助手', simplePlaceholder: '适合新手创建小助手',
workflowPlaceholder: '适合高级用户自定义小助手的工作流' workflowPlaceholder: '适合高级用户自定义小助手的工作流'
}, },
appTemplate: {
blankApp: '空白应用',
assistantApp: '知识库问答助手'
},
aiModel: { aiModel: {
label: 'AI 模型', label: 'AI 模型',
placeholder: '请选择 AI 模型' placeholder: '请选择 AI 模型'
@ -198,7 +202,7 @@ export default {
slackSetting: { slackSetting: {
title: 'Slack 应用配置', title: 'Slack 应用配置',
signingSecretPlaceholder: '请输入 Signing Secret', signingSecretPlaceholder: '请输入 Signing Secret',
botUserTokenPlaceholder: '请输入 Bot User Token', botUserTokenPlaceholder: '请输入 Bot User Token'
}, },
copyUrl: '复制链接填入到' copyUrl: '复制链接填入到'
}, },

View File

@ -41,6 +41,10 @@ export default {
simplePlaceholder: '適合新手建立小助手', simplePlaceholder: '適合新手建立小助手',
workflowPlaceholder: '適合高階用戶自訂小助手的工作流程' workflowPlaceholder: '適合高階用戶自訂小助手的工作流程'
}, },
appTemplate: {
blankApp: '空白應用',
assistantApp: '知識庫問答助手'
},
aiModel: { aiModel: {
label: 'AI 模型', label: 'AI 模型',
placeholder: '請選擇 AI 模型' placeholder: '請選擇 AI 模型'

View File

@ -38,11 +38,7 @@
<el-radio-group v-model="applicationForm.type" class="card__radio"> <el-radio-group v-model="applicationForm.type" class="card__radio">
<el-row :gutter="16"> <el-row :gutter="16">
<el-col :span="12"> <el-col :span="12">
<el-card <el-card shadow="never" :class="applicationForm.type === 'SIMPLE' ? 'active' : ''">
shadow="never"
class="mb-16"
:class="applicationForm.type === 'SIMPLE' ? 'active' : ''"
>
<el-radio value="SIMPLE" size="large"> <el-radio value="SIMPLE" size="large">
<p class="mb-4">{{ $t('views.application.simple') }}</p> <p class="mb-4">{{ $t('views.application.simple') }}</p>
<el-text type="info">{{ <el-text type="info">{{
@ -52,11 +48,7 @@
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-card <el-card shadow="never" :class="isWorkFlow(applicationForm.type) ? 'active' : ''">
shadow="never"
class="mb-16"
:class="isWorkFlow(applicationForm.type) ? 'active' : ''"
>
<el-radio value="WORK_FLOW" size="large"> <el-radio value="WORK_FLOW" size="large">
<p class="mb-4">{{ $t('views.application.workflow') }}</p> <p class="mb-4">{{ $t('views.application.workflow') }}</p>
<el-text type="info">{{ <el-text type="info">{{
@ -68,6 +60,35 @@
</el-row> </el-row>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item
:label="$t('views.document.upload.template')"
v-if="applicationForm.type === 'WORK_FLOW'"
>
<div class="w-full">
<el-row :gutter="16">
<el-col :span="12">
<el-card
class="radio-card cursor"
shadow="never"
@click="selectedType('blank')"
:class="appTemplate === 'blank' ? 'active' : ''"
>
{{ $t('views.application.applicationForm.form.appTemplate.blankApp') }}
</el-card>
</el-col>
<el-col :span="12">
<el-card
class="radio-card cursor"
shadow="never"
:class="appTemplate === 'assistant' ? 'active' : ''"
@click="selectedType('assistant')"
>
{{ $t('views.application.applicationForm.form.appTemplate.assistantApp') }}
</el-card>
</el-col>
</el-row>
</div>
</el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
@ -89,6 +110,7 @@ import type { FormInstance, FormRules } from 'element-plus'
import applicationApi from '@/api/application' import applicationApi from '@/api/application'
import { MsgSuccess, MsgAlert } from '@/utils/message' import { MsgSuccess, MsgAlert } from '@/utils/message'
import { isWorkFlow } from '@/utils/application' import { isWorkFlow } from '@/utils/application'
import { baseNodes } from '@/workflow/common/data'
import { t } from '@/locales' import { t } from '@/locales'
const router = useRouter() const router = useRouter()
const emit = defineEmits(['refresh']) const emit = defineEmits(['refresh'])
@ -106,6 +128,12 @@ const optimizationPrompt =
'<data></data>' + '<data></data>' +
t('views.application.applicationForm.dialog.defaultPrompt2') t('views.application.applicationForm.dialog.defaultPrompt2')
const workflowDefault = ref<any>({
edges: [],
nodes: baseNodes
})
const appTemplate = ref('blank')
const applicationFormRef = ref() const applicationFormRef = ref()
const loading = ref(false) const loading = ref(false)
@ -207,6 +235,11 @@ const submitHandle = async (formEl: FormInstance | undefined) => {
if (!formEl) return if (!formEl) return
await formEl.validate((valid) => { await formEl.validate((valid) => {
if (valid) { if (valid) {
if (isWorkFlow(applicationForm.value.type) && appTemplate.value === 'blank') {
workflowDefault.value.nodes[0].properties.node_data.desc = applicationForm.value.desc
workflowDefault.value.nodes[0].properties.node_data.name = applicationForm.value.name
applicationForm.value['work_flow'] = workflowDefault.value
}
applicationApi.postApplication(applicationForm.value, loading).then((res) => { applicationApi.postApplication(applicationForm.value, loading).then((res) => {
MsgSuccess(t('common.createSuccess')) MsgSuccess(t('common.createSuccess'))
if (isWorkFlow(applicationForm.value.type)) { if (isWorkFlow(applicationForm.value.type)) {
@ -220,6 +253,18 @@ const submitHandle = async (formEl: FormInstance | undefined) => {
}) })
} }
function selectedType(type: string) {
appTemplate.value = type
}
defineExpose({ open }) defineExpose({ open })
</script> </script>
<style lang="scss" scope></style> <style lang="scss" scope>
.radio-card {
line-height: 22px;
&.active {
border-color: var(--el-color-primary);
color: var(--el-color-primary);
}
}
</style>

View File

@ -4,10 +4,10 @@ import { t } from '@/locales'
export const startNode = { export const startNode = {
id: WorkflowType.Start, id: WorkflowType.Start,
type: WorkflowType.Start, type: WorkflowType.Start,
x: 180, x: 480,
y: 720, y: 3340,
properties: { properties: {
height: 200, height: 364,
stepName: t('views.applicationWorkflow.nodes.startNode.label'), stepName: t('views.applicationWorkflow.nodes.startNode.label'),
config: { config: {
fields: [ fields: [
@ -20,29 +20,45 @@ export const startNode = {
{ {
value: 'time', value: 'time',
label: t('views.applicationWorkflow.nodes.startNode.currentTime') label: t('views.applicationWorkflow.nodes.startNode.currentTime')
},
{
value: 'history_context',
label: t('views.application.applicationForm.form.historyRecord.label')
},
{
value: 'chat_id',
label: t('chat.chatId')
} }
] ]
} },
fields: [{ label: t('views.applicationWorkflow.nodes.startNode.question'), value: 'question' }],
globalFields: [
{ label: t('views.applicationWorkflow.nodes.startNode.currentTime'), value: 'time' }
],
showNode: true
} }
} }
export const baseNode = { export const baseNode = {
id: WorkflowType.Base, id: WorkflowType.Base,
type: WorkflowType.Base, type: WorkflowType.Base,
x: 200, x: 360,
y: 270, y: 2761.3875,
text: '', text: '',
properties: { properties: {
width: 420, height: 728.375,
height: 200,
stepName: t('views.applicationWorkflow.nodes.baseNode.label'), stepName: t('views.applicationWorkflow.nodes.baseNode.label'),
input_field_list: [], input_field_list: [],
node_data: { node_data: {
name: '', name: '',
desc: '', desc: '',
// @ts-ignore // @ts-ignore
prologue: t('views.application.applicationForm.form.defaultPrologue') prologue: t('views.application.applicationForm.form.defaultPrologue'),
tts_type: 'BROWSER'
}, },
config: {} config: {},
showNode: true,
user_input_config: { title: t('chat.userInput') },
user_input_field_list: []
} }
} }
/** /**
@ -246,9 +262,7 @@ export const variableAssignNode = {
height: 252, height: 252,
properties: { properties: {
stepName: t('views.applicationWorkflow.nodes.variableAssignNode.label'), stepName: t('views.applicationWorkflow.nodes.variableAssignNode.label'),
config: { config: {}
}
} }
} }
@ -415,7 +429,7 @@ export const nodeDict: any = {
[WorkflowType.TextToSpeechNode]: textToSpeechNode, [WorkflowType.TextToSpeechNode]: textToSpeechNode,
[WorkflowType.SpeechToTextNode]: speechToTextNode, [WorkflowType.SpeechToTextNode]: speechToTextNode,
[WorkflowType.ImageGenerateNode]: imageGenerateNode, [WorkflowType.ImageGenerateNode]: imageGenerateNode,
[WorkflowType.VariableAssignNode]: variableAssignNode, [WorkflowType.VariableAssignNode]: variableAssignNode
} }
export function isWorkFlow(type: string | undefined) { export function isWorkFlow(type: string | undefined) {
return type === 'WORK_FLOW' return type === 'WORK_FLOW'