mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-07-10 06:52:05 +08:00
feat: enhance theme customizer with shadow options and update styles
- Added shadow customization options to the theme customizer, allowing users to select from 'none', 'low', 'medium', and 'high'. - Updated the theme customizer settings interface and default values to include shadow settings. - Enhanced the CSS variables for shadows in common.scss to support different shadow levels based on user selection. - Modified the VirtualSlideView component styles to improve layout and scrolling behavior. - Updated localization files for English, Simplified Chinese, and Traditional Chinese to include new shadow-related terms. - Adjusted various components to ensure consistent application of shadow styles across the application.
This commit is contained in:
@@ -38,54 +38,22 @@ onConnect((connection: Connection) => {
|
||||
// 当前选中的流程边ID
|
||||
const selectedEdgeId = ref<string | null>(null)
|
||||
|
||||
// 当前选中的动作节点ID
|
||||
const selectedNodeId = ref<string | null>(null)
|
||||
|
||||
// 流程边配置表单
|
||||
const edgeForm = ref({
|
||||
condition: '',
|
||||
join_policy: '',
|
||||
branch_policy: '',
|
||||
})
|
||||
|
||||
// 动作节点执行配置表单
|
||||
const nodeForm = ref({
|
||||
inputs_text: '',
|
||||
outputs_text: '',
|
||||
join_policy: '',
|
||||
fail_policy: '',
|
||||
branch_policy: '',
|
||||
concurrency_key: '',
|
||||
timeout: null as number | null,
|
||||
retry_max_attempts: null as number | null,
|
||||
retry_interval: null as number | null,
|
||||
retry_backoff: null as number | null,
|
||||
// 后端动作固定契约,供条件构造器读取上一节点输出
|
||||
const actionDefinitions = ref<any[]>([])
|
||||
|
||||
// 动作类型到契约的映射
|
||||
const actionContractMap = computed(() => {
|
||||
return actionDefinitions.value.reduce((result, action) => {
|
||||
result[action.type] = action.contract || {}
|
||||
return result
|
||||
}, {} as Record<string, any>)
|
||||
})
|
||||
|
||||
// 汇合策略选项
|
||||
const joinPolicyOptions = computed(() => [
|
||||
{ title: t('dialog.workflowActions.joinPolicyDefault'), value: '' },
|
||||
{ title: t('dialog.workflowActions.joinPolicyAllSuccess'), value: 'all_success' },
|
||||
{ title: t('dialog.workflowActions.joinPolicyAnySuccess'), value: 'any_success' },
|
||||
{ title: t('dialog.workflowActions.joinPolicyAllDone'), value: 'all_done' },
|
||||
{ title: t('dialog.workflowActions.joinPolicyFailFast'), value: 'fail_fast' },
|
||||
])
|
||||
|
||||
// 分支策略选项
|
||||
const branchPolicyOptions = computed(() => [
|
||||
{ title: t('dialog.workflowActions.branchPolicyDefault'), value: '' },
|
||||
{ title: t('dialog.workflowActions.branchPolicyParallel'), value: 'parallel' },
|
||||
{ title: t('dialog.workflowActions.branchPolicyExclusive'), value: 'exclusive' },
|
||||
])
|
||||
|
||||
// 失败策略选项
|
||||
const failPolicyOptions = computed(() => [
|
||||
{ title: t('dialog.workflowActions.failPolicyDefault'), value: '' },
|
||||
{ title: t('dialog.workflowActions.failPolicyStop'), value: 'stop' },
|
||||
{ title: t('dialog.workflowActions.failPolicyContinue'), value: 'continue' },
|
||||
{ title: t('dialog.workflowActions.failPolicyIgnore'), value: 'ignore' },
|
||||
])
|
||||
|
||||
// 获取指定节点端口的类型(输入/输出)
|
||||
const getPortType = (node: GraphNode, handleId: string) => {
|
||||
// 检查是否是输入端口(对应 handleBounds.target)
|
||||
@@ -122,30 +90,30 @@ const getEdgeConfigValue = (edge: any, key: string) => {
|
||||
return edge?.[key] ?? edge?.data?.[key] ?? ''
|
||||
}
|
||||
|
||||
// 统一流程边数据结构,确保条件和汇合策略能被后端读取
|
||||
// 复制对象并移除不再由前端编辑的高级配置
|
||||
const omitConfigKeys = (value: any, keys: string[]) => {
|
||||
const result = { ...(value || {}) }
|
||||
keys.forEach(key => delete result[key])
|
||||
return result
|
||||
}
|
||||
|
||||
// 统一流程边数据结构,前端只编辑边条件,汇合和分支策略由执行器默认处理
|
||||
const normalizeWorkflowEdge = (edge: any) => {
|
||||
const condition = String(getEdgeConfigValue(edge, 'condition') || '').trim()
|
||||
const joinPolicy = String(getEdgeConfigValue(edge, 'join_policy') || '').trim()
|
||||
const branchPolicy = String(getEdgeConfigValue(edge, 'branch_policy') || '').trim()
|
||||
const edgeClass = String(edge?.class || '')
|
||||
.replace(/\bworkflow-conditional-edge\b/g, '')
|
||||
.trim()
|
||||
const data = {
|
||||
...(edge?.data || {}),
|
||||
condition: condition || undefined,
|
||||
join_policy: joinPolicy || undefined,
|
||||
branch_policy: branchPolicy || undefined,
|
||||
}
|
||||
const data = omitConfigKeys(edge?.data, ['join_policy', 'branch_policy'])
|
||||
data.condition = condition || undefined
|
||||
const edgePayload = omitConfigKeys(edge, ['join_policy', 'branch_policy'])
|
||||
|
||||
return {
|
||||
...edge,
|
||||
...edgePayload,
|
||||
animated: edge?.animated ?? true,
|
||||
type: edge?.type || 'animation',
|
||||
label: condition ? t('dialog.workflowActions.edgeConditionalLabel') : undefined,
|
||||
class: [edgeClass, condition ? 'workflow-conditional-edge' : ''].filter(Boolean).join(' ') || undefined,
|
||||
condition: condition || undefined,
|
||||
join_policy: joinPolicy || undefined,
|
||||
branch_policy: branchPolicy || undefined,
|
||||
data,
|
||||
}
|
||||
}
|
||||
@@ -155,129 +123,25 @@ const normalizeWorkflowEdges = () => {
|
||||
edges.value = (edges.value || []).map(edge => normalizeWorkflowEdge(edge))
|
||||
}
|
||||
|
||||
// 判断扩展配置是否为空,避免旧 data 中的空值覆盖顶层字段
|
||||
const isEmptyConfigValue = (value: any) => {
|
||||
if (value === undefined || value === null || value === '') return true
|
||||
if (Array.isArray(value)) return value.length === 0
|
||||
if (typeof value === 'object') return Object.keys(value).length === 0
|
||||
return false
|
||||
}
|
||||
|
||||
// 读取动作节点扩展配置,兼容顶层字段与 data 字段
|
||||
const getNodeConfigValue = (node: any, key: string) => {
|
||||
const nodeValue = node?.[key]
|
||||
if (!isEmptyConfigValue(nodeValue)) return nodeValue
|
||||
const dataValue = node?.data?.[key]
|
||||
return isEmptyConfigValue(dataValue) ? undefined : dataValue
|
||||
}
|
||||
|
||||
// 将输入声明统一为路径数组
|
||||
const normalizeInputPaths = (value: any) => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(item => String(item).trim()).filter(Boolean)
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return value
|
||||
.split(/[\n,]+/)
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
// 解析 JSON 形式的结构化配置
|
||||
const parseStructuredConfig = (value: string, label: string) => {
|
||||
const text = String(value || '').trim()
|
||||
if (!text) return undefined
|
||||
try {
|
||||
const parsed = JSON.parse(text)
|
||||
if (parsed && (Array.isArray(parsed) || typeof parsed === 'object')) {
|
||||
return parsed
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
throw new Error(t('dialog.workflowActions.invalidJsonConfig', { label }))
|
||||
}
|
||||
|
||||
// 尝试把存量结构化配置标准化为对象或数组
|
||||
const normalizeStructuredConfig = (value: any) => {
|
||||
if (isEmptyConfigValue(value)) return undefined
|
||||
if (Array.isArray(value) || typeof value === 'object') return value
|
||||
if (typeof value !== 'string') return undefined
|
||||
try {
|
||||
const parsed = JSON.parse(value)
|
||||
return parsed && (Array.isArray(parsed) || typeof parsed === 'object') ? parsed : undefined
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
// 将结构化配置格式化为面板中的 JSON 文本
|
||||
const stringifyStructuredConfig = (value: any) => {
|
||||
const normalizedValue = normalizeStructuredConfig(value)
|
||||
return normalizedValue ? JSON.stringify(normalizedValue, null, 2) : ''
|
||||
}
|
||||
|
||||
// 数值字段统一清洗,空值会在保存时被移除
|
||||
const normalizeNumber = (value: any, minValue = 0, integer = false) => {
|
||||
if (value === undefined || value === null || value === '') return undefined
|
||||
const numberValue = Number(value)
|
||||
if (!Number.isFinite(numberValue) || numberValue < minValue) return undefined
|
||||
return integer ? Math.floor(numberValue) : numberValue
|
||||
}
|
||||
|
||||
// 读取节点重试策略
|
||||
const normalizeRetryConfig = (value: any) => {
|
||||
const retryConfig = normalizeStructuredConfig(value)
|
||||
return retryConfig && !Array.isArray(retryConfig) ? retryConfig : {}
|
||||
}
|
||||
|
||||
// 根据面板表单构造重试策略
|
||||
const buildRetryConfigFromForm = () => {
|
||||
const retryConfig: Record<string, number> = {}
|
||||
const maxAttempts = normalizeNumber(nodeForm.value.retry_max_attempts, 1, true)
|
||||
const interval = normalizeNumber(nodeForm.value.retry_interval, 0)
|
||||
const backoff = normalizeNumber(nodeForm.value.retry_backoff, 1)
|
||||
if (maxAttempts !== undefined) retryConfig.max_attempts = maxAttempts
|
||||
if (interval !== undefined) retryConfig.interval = interval
|
||||
if (backoff !== undefined) retryConfig.backoff = backoff
|
||||
return Object.keys(retryConfig).length ? retryConfig : undefined
|
||||
}
|
||||
|
||||
// 统一动作节点数据结构,确保执行策略能被后端读取
|
||||
// 统一动作节点数据结构,高级运行配置由后端默认值和动作契约接管
|
||||
const normalizeWorkflowNode = (node: any) => {
|
||||
const inputPaths = normalizeInputPaths(getNodeConfigValue(node, 'inputs'))
|
||||
const outputs = normalizeStructuredConfig(getNodeConfigValue(node, 'outputs'))
|
||||
const joinPolicy = String(getNodeConfigValue(node, 'join_policy') || '').trim()
|
||||
const failPolicy = String(getNodeConfigValue(node, 'fail_policy') || '').trim()
|
||||
const branchPolicy = String(getNodeConfigValue(node, 'branch_policy') || '').trim()
|
||||
const concurrencyKey = String(getNodeConfigValue(node, 'concurrency_key') || '').trim()
|
||||
const timeout = normalizeNumber(getNodeConfigValue(node, 'timeout'), 1, true)
|
||||
const retryConfig = normalizeRetryConfig(getNodeConfigValue(node, 'retry'))
|
||||
const retry = Object.keys(retryConfig).length ? retryConfig : undefined
|
||||
const data = {
|
||||
...(node?.data || {}),
|
||||
inputs: inputPaths.length ? inputPaths : undefined,
|
||||
outputs: outputs || undefined,
|
||||
join_policy: joinPolicy || undefined,
|
||||
fail_policy: failPolicy || undefined,
|
||||
branch_policy: branchPolicy || undefined,
|
||||
concurrency_key: concurrencyKey || undefined,
|
||||
timeout: timeout || undefined,
|
||||
retry,
|
||||
}
|
||||
const hiddenConfigKeys = [
|
||||
'inputs',
|
||||
'outputs',
|
||||
'join_policy',
|
||||
'fail_policy',
|
||||
'branch_policy',
|
||||
'concurrency_key',
|
||||
'timeout',
|
||||
'retry',
|
||||
'contract',
|
||||
'_contract',
|
||||
]
|
||||
const data = omitConfigKeys(node?.data, hiddenConfigKeys)
|
||||
const nodePayload = omitConfigKeys(node, hiddenConfigKeys)
|
||||
|
||||
return {
|
||||
...node,
|
||||
inputs: inputPaths.length ? inputPaths : undefined,
|
||||
outputs: outputs || undefined,
|
||||
join_policy: joinPolicy || undefined,
|
||||
fail_policy: failPolicy || undefined,
|
||||
branch_policy: branchPolicy || undefined,
|
||||
concurrency_key: concurrencyKey || undefined,
|
||||
timeout: timeout || undefined,
|
||||
retry,
|
||||
...nodePayload,
|
||||
data,
|
||||
}
|
||||
}
|
||||
@@ -293,16 +157,39 @@ const getNodeName = (nodeId?: string) => {
|
||||
return (node as any)?.name || node?.data?.label || nodeId || ''
|
||||
}
|
||||
|
||||
// 获取流程边源节点可用于条件判断的输出字段
|
||||
const getEdgeConditionFields = (edge: any) => {
|
||||
const sourceNode = edge
|
||||
? nodes.value.find(node => node.id === edge.source)
|
||||
: null
|
||||
const contract = sourceNode ? actionContractMap.value[sourceNode.type] || {} : {}
|
||||
const fields = contract.condition_fields || contract.outputs || []
|
||||
return Array.isArray(fields)
|
||||
? fields.filter((field: any) => field?.name || field)
|
||||
: []
|
||||
}
|
||||
|
||||
// 判断流程边是否存在可编辑条件
|
||||
const canConfigureEdge = (edge: any) => {
|
||||
const condition = String(getEdgeConfigValue(edge, 'condition') || '').trim()
|
||||
return Boolean(condition || getEdgeConditionFields(edge).length)
|
||||
}
|
||||
|
||||
// 选中流程边时打开设置面板
|
||||
function handleEdgeClick(params: any) {
|
||||
async function handleEdgeClick(params: any) {
|
||||
const edge = params?.edge
|
||||
if (!edge) return
|
||||
selectedNodeId.value = null
|
||||
if (!actionDefinitions.value.length) {
|
||||
await loadActionDefinitions()
|
||||
}
|
||||
if (!canConfigureEdge(edge)) {
|
||||
closeEdgeSettings()
|
||||
$toast.info(t('dialog.workflowActions.edgeNoConditionFields'))
|
||||
return
|
||||
}
|
||||
selectedEdgeId.value = edge.id
|
||||
edgeForm.value = {
|
||||
condition: String(getEdgeConfigValue(edge, 'condition') || ''),
|
||||
join_policy: String(getEdgeConfigValue(edge, 'join_policy') || ''),
|
||||
branch_policy: String(getEdgeConfigValue(edge, 'branch_policy') || ''),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,8 +198,6 @@ function closeEdgeSettings() {
|
||||
selectedEdgeId.value = null
|
||||
edgeForm.value = {
|
||||
condition: '',
|
||||
join_policy: '',
|
||||
branch_policy: '',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,14 +209,10 @@ function saveEdgeSettings() {
|
||||
return normalizeWorkflowEdge({
|
||||
...edge,
|
||||
condition: edgeForm.value.condition,
|
||||
join_policy: edgeForm.value.join_policy,
|
||||
data: {
|
||||
...(edge.data || {}),
|
||||
condition: edgeForm.value.condition,
|
||||
join_policy: edgeForm.value.join_policy,
|
||||
branch_policy: edgeForm.value.branch_policy,
|
||||
},
|
||||
branch_policy: edgeForm.value.branch_policy,
|
||||
})
|
||||
})
|
||||
$toast.success(t('dialog.workflowActions.edgeSaveSuccess'))
|
||||
@@ -350,106 +231,49 @@ const selectedEdge = computed(() => {
|
||||
return edges.value.find(edge => edge.id === selectedEdgeId.value) || null
|
||||
})
|
||||
|
||||
// 当前选中的动作节点
|
||||
const selectedNode = computed(() => {
|
||||
if (!selectedNodeId.value) return null
|
||||
return nodes.value.find(node => node.id === selectedNodeId.value) || null
|
||||
})
|
||||
// 当前边可用于条件判断的输出字段
|
||||
const selectedEdgeConditionFields = computed(() => (
|
||||
selectedEdge.value ? getEdgeConditionFields(selectedEdge.value) : []
|
||||
))
|
||||
|
||||
// 将节点数据填入右侧执行配置面板
|
||||
function fillNodeForm(node: any) {
|
||||
const retryConfig = normalizeRetryConfig(getNodeConfigValue(node, 'retry'))
|
||||
nodeForm.value = {
|
||||
inputs_text: normalizeInputPaths(getNodeConfigValue(node, 'inputs')).join('\n'),
|
||||
outputs_text: stringifyStructuredConfig(getNodeConfigValue(node, 'outputs')),
|
||||
join_policy: String(getNodeConfigValue(node, 'join_policy') || ''),
|
||||
fail_policy: String(getNodeConfigValue(node, 'fail_policy') || ''),
|
||||
branch_policy: String(getNodeConfigValue(node, 'branch_policy') || ''),
|
||||
concurrency_key: String(getNodeConfigValue(node, 'concurrency_key') || ''),
|
||||
timeout: normalizeNumber(getNodeConfigValue(node, 'timeout'), 1, true) ?? null,
|
||||
retry_max_attempts: normalizeNumber(retryConfig.max_attempts, 1, true) ?? null,
|
||||
retry_interval: normalizeNumber(retryConfig.interval, 0) ?? null,
|
||||
retry_backoff: normalizeNumber(retryConfig.backoff, 1) ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
// 选中动作节点时打开执行配置面板
|
||||
function handleNodeClick(params: any) {
|
||||
const node = params?.node
|
||||
if (!node) return
|
||||
if (node.name == '备注') return
|
||||
selectedEdgeId.value = null
|
||||
selectedNodeId.value = node.id
|
||||
fillNodeForm(node)
|
||||
}
|
||||
|
||||
// 关闭动作节点执行配置面板
|
||||
function closeNodeSettings() {
|
||||
selectedNodeId.value = null
|
||||
nodeForm.value = {
|
||||
inputs_text: '',
|
||||
outputs_text: '',
|
||||
join_policy: '',
|
||||
fail_policy: '',
|
||||
branch_policy: '',
|
||||
concurrency_key: '',
|
||||
timeout: null,
|
||||
retry_max_attempts: null,
|
||||
retry_interval: null,
|
||||
retry_backoff: null,
|
||||
}
|
||||
}
|
||||
|
||||
// 根据面板表单构造动作节点执行配置
|
||||
function buildNodeConfigFromForm() {
|
||||
return {
|
||||
inputs: normalizeInputPaths(nodeForm.value.inputs_text),
|
||||
outputs: parseStructuredConfig(nodeForm.value.outputs_text, t('dialog.workflowActions.nodeOutputsLabel')),
|
||||
join_policy: nodeForm.value.join_policy,
|
||||
fail_policy: nodeForm.value.fail_policy,
|
||||
branch_policy: nodeForm.value.branch_policy,
|
||||
concurrency_key: nodeForm.value.concurrency_key,
|
||||
timeout: normalizeNumber(nodeForm.value.timeout, 1, true),
|
||||
retry: buildRetryConfigFromForm(),
|
||||
}
|
||||
}
|
||||
|
||||
// 保存动作节点执行配置
|
||||
function saveNodeSettings() {
|
||||
if (!selectedNodeId.value) return
|
||||
let nodeConfig: any
|
||||
try {
|
||||
nodeConfig = buildNodeConfigFromForm()
|
||||
} catch (error: any) {
|
||||
$toast.error(error.message)
|
||||
return
|
||||
}
|
||||
nodes.value = nodes.value.map(node => {
|
||||
if (node.id !== selectedNodeId.value) return node
|
||||
return normalizeWorkflowNode({
|
||||
...node,
|
||||
inputs: nodeConfig.inputs,
|
||||
outputs: nodeConfig.outputs,
|
||||
join_policy: nodeConfig.join_policy,
|
||||
fail_policy: nodeConfig.fail_policy,
|
||||
branch_policy: nodeConfig.branch_policy,
|
||||
concurrency_key: nodeConfig.concurrency_key,
|
||||
timeout: nodeConfig.timeout,
|
||||
retry: nodeConfig.retry,
|
||||
data: {
|
||||
...(node.data || {}),
|
||||
inputs: nodeConfig.inputs,
|
||||
outputs: nodeConfig.outputs,
|
||||
join_policy: nodeConfig.join_policy,
|
||||
fail_policy: nodeConfig.fail_policy,
|
||||
branch_policy: nodeConfig.branch_policy,
|
||||
concurrency_key: nodeConfig.concurrency_key,
|
||||
timeout: nodeConfig.timeout,
|
||||
retry: nodeConfig.retry,
|
||||
},
|
||||
// 当前边的条件下拉选项,按源节点固定输出自动生成
|
||||
const edgeConditionOptions = computed(() => {
|
||||
const sourceNode = selectedEdge.value
|
||||
? nodes.value.find(node => node.id === selectedEdge.value?.source)
|
||||
: null
|
||||
const options = [{ title: t('dialog.workflowActions.conditionAlways'), value: '' }]
|
||||
selectedEdgeConditionFields.value.forEach((field: any) => {
|
||||
const fieldName = field.name || field
|
||||
if (!fieldName) return
|
||||
const fieldLabel = field.label || fieldName
|
||||
if (field.kind === 'list') {
|
||||
options.push({
|
||||
title: t('dialog.workflowActions.conditionHasOutput', { field: fieldLabel }),
|
||||
value: `outputs.${sourceNode?.id}.${fieldName}.count > 0`,
|
||||
})
|
||||
options.push({
|
||||
title: t('dialog.workflowActions.conditionNoOutput', { field: fieldLabel }),
|
||||
value: `outputs.${sourceNode?.id}.${fieldName}.count == 0`,
|
||||
})
|
||||
return
|
||||
}
|
||||
options.push({
|
||||
title: t('dialog.workflowActions.conditionHasValue', { field: fieldLabel }),
|
||||
value: `outputs.${sourceNode?.id}.${fieldName} != None`,
|
||||
})
|
||||
})
|
||||
$toast.success(t('dialog.workflowActions.nodeSaveSuccess'))
|
||||
if (edgeForm.value.condition && !options.some(item => item.value === edgeForm.value.condition)) {
|
||||
options.push({
|
||||
title: t('dialog.workflowActions.conditionCustom'),
|
||||
value: edgeForm.value.condition,
|
||||
})
|
||||
}
|
||||
return options
|
||||
})
|
||||
|
||||
// 选中动作节点时关闭可能打开的边条件面板,不再提供节点运行设置
|
||||
function handleNodeClick() {
|
||||
closeEdgeSettings()
|
||||
}
|
||||
|
||||
// 自定义节点类型
|
||||
@@ -478,6 +302,17 @@ for (const path in components) {
|
||||
})
|
||||
}
|
||||
|
||||
// 加载动作契约,供边条件构造器使用
|
||||
async function loadActionDefinitions() {
|
||||
try {
|
||||
const actionList = await api.get('workflow/actions')
|
||||
actionDefinitions.value = Array.isArray(actionList) ? actionList : []
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
actionDefinitions.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// 定义输入参数
|
||||
const props = defineProps({
|
||||
workflow: Object as PropType<Workflow>,
|
||||
@@ -590,6 +425,7 @@ function shareWorkflow() {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadActionDefinitions()
|
||||
if (props.workflow) {
|
||||
nodes.value = props.workflow.actions ?? []
|
||||
edges.value = props.workflow.flows ?? []
|
||||
@@ -611,8 +447,8 @@ watch(
|
||||
watch(
|
||||
nodes,
|
||||
() => {
|
||||
if (selectedNodeId.value && !selectedNode.value) {
|
||||
closeNodeSettings()
|
||||
if (selectedEdge.value && !canConfigureEdge(selectedEdge.value)) {
|
||||
closeEdgeSettings()
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
@@ -691,33 +527,11 @@ const isMacOS = computed(() => {
|
||||
<span>{{ getNodeName(selectedEdge.target) }}</span>
|
||||
</div>
|
||||
|
||||
<VTextarea
|
||||
<VSelect
|
||||
v-model="edgeForm.condition"
|
||||
:items="edgeConditionOptions"
|
||||
:label="t('dialog.workflowActions.edgeConditionLabel')"
|
||||
:placeholder="t('dialog.workflowActions.edgeConditionPlaceholder')"
|
||||
rows="3"
|
||||
auto-grow
|
||||
clearable
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
hide-details="auto"
|
||||
/>
|
||||
|
||||
<VSelect
|
||||
v-model="edgeForm.join_policy"
|
||||
:items="joinPolicyOptions"
|
||||
:label="t('dialog.workflowActions.edgeJoinPolicyLabel')"
|
||||
item-title="title"
|
||||
item-value="value"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
hide-details="auto"
|
||||
/>
|
||||
|
||||
<VSelect
|
||||
v-model="edgeForm.branch_policy"
|
||||
:items="branchPolicyOptions"
|
||||
:label="t('dialog.workflowActions.edgeBranchPolicyLabel')"
|
||||
item-title="title"
|
||||
item-value="value"
|
||||
variant="outlined"
|
||||
@@ -739,121 +553,6 @@ const isMacOS = computed(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="selectedNode" class="workflow-edge-panel workflow-node-panel">
|
||||
<div class="edge-panel-header">
|
||||
<div class="edge-panel-title">
|
||||
<VIcon icon="mdi-tune-variant" size="20" />
|
||||
<span>{{ t('dialog.workflowActions.nodeSettingsTitle') }}</span>
|
||||
</div>
|
||||
<VBtn icon variant="text" size="small" @click="closeNodeSettings">
|
||||
<VIcon icon="mdi-close" />
|
||||
</VBtn>
|
||||
</div>
|
||||
|
||||
<div class="edge-route">
|
||||
<VIcon icon="mdi-checkbox-blank-circle-outline" size="16" />
|
||||
<span>{{ getNodeName(selectedNode.id) }}</span>
|
||||
</div>
|
||||
|
||||
<VSelect
|
||||
v-model="nodeForm.join_policy"
|
||||
:items="joinPolicyOptions"
|
||||
:label="t('dialog.workflowActions.nodeJoinPolicyLabel')"
|
||||
item-title="title"
|
||||
item-value="value"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
hide-details="auto"
|
||||
/>
|
||||
|
||||
<VSelect
|
||||
v-model="nodeForm.fail_policy"
|
||||
:items="failPolicyOptions"
|
||||
:label="t('dialog.workflowActions.nodeFailPolicyLabel')"
|
||||
item-title="title"
|
||||
item-value="value"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
hide-details="auto"
|
||||
/>
|
||||
|
||||
<VSelect
|
||||
v-model="nodeForm.branch_policy"
|
||||
:items="branchPolicyOptions"
|
||||
:label="t('dialog.workflowActions.nodeBranchPolicyLabel')"
|
||||
item-title="title"
|
||||
item-value="value"
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
hide-details="auto"
|
||||
/>
|
||||
|
||||
<VTextField
|
||||
v-model="nodeForm.concurrency_key"
|
||||
:label="t('dialog.workflowActions.nodeConcurrencyKeyLabel')"
|
||||
:placeholder="t('dialog.workflowActions.nodeConcurrencyKeyPlaceholder')"
|
||||
clearable
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
hide-details="auto"
|
||||
/>
|
||||
|
||||
<div class="workflow-number-grid">
|
||||
<VTextField
|
||||
v-model.number="nodeForm.timeout"
|
||||
type="number"
|
||||
min="1"
|
||||
:label="t('dialog.workflowActions.nodeTimeoutLabel')"
|
||||
clearable
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
hide-details="auto"
|
||||
/>
|
||||
<VTextField
|
||||
v-model.number="nodeForm.retry_max_attempts"
|
||||
type="number"
|
||||
min="1"
|
||||
:label="t('dialog.workflowActions.nodeRetryAttemptsLabel')"
|
||||
clearable
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
hide-details="auto"
|
||||
/>
|
||||
<VTextField
|
||||
v-model.number="nodeForm.retry_interval"
|
||||
type="number"
|
||||
min="0"
|
||||
step="0.1"
|
||||
:label="t('dialog.workflowActions.nodeRetryIntervalLabel')"
|
||||
clearable
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
hide-details="auto"
|
||||
/>
|
||||
<VTextField
|
||||
v-model.number="nodeForm.retry_backoff"
|
||||
type="number"
|
||||
min="1"
|
||||
step="0.1"
|
||||
:label="t('dialog.workflowActions.nodeRetryBackoffLabel')"
|
||||
clearable
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
hide-details="auto"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="edge-panel-actions">
|
||||
<VSpacer />
|
||||
<VBtn variant="text" @click="closeNodeSettings">
|
||||
{{ t('dialog.workflowActions.edgeCancel') }}
|
||||
</VBtn>
|
||||
<VBtn color="primary" @click="saveNodeSettings">
|
||||
{{ t('dialog.workflowActions.edgeSave') }}
|
||||
</VBtn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<WorkflowSidebar @component-click="handleComponentClick" />
|
||||
</div>
|
||||
</VCardText>
|
||||
@@ -914,10 +613,6 @@ const isMacOS = computed(() => {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.workflow-node-panel {
|
||||
inline-size: min(420px, calc(100vw - 32px));
|
||||
}
|
||||
|
||||
.edge-panel-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -958,12 +653,6 @@ const isMacOS = computed(() => {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.workflow-number-grid {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.vue-flow__minimap {
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
|
||||
@@ -1042,8 +731,5 @@ const isMacOS = computed(() => {
|
||||
max-block-size: min(72vh, calc(100% - 112px));
|
||||
}
|
||||
|
||||
.workflow-number-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user