| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- <!--
- * @Author: liuJie
- * @Date: 2026-01-25 22:13:06
- * @LastEditors: liuJie
- * @LastEditTime: 2026-01-27 10:01:23
- * @Describe: file describe
- -->
- <script lang="ts" setup>
- import { computed, provide, watch, ref } from 'vue'
- import { Icon, Input } from '@repo/ui'
- import { useDebounceFn } from '@vueuse/core'
- import NodeLog from './NodeLog.vue'
- import { agent } from '@repo/api-service'
- import { nodeMap } from '@/nodes'
- import type { IWorkflowNode, IWorkflow } from '@repo/workflow'
- import type { NodeVar } from '@/types/var'
- interface Props {
- id: string
- workflow: IWorkflow
- visible: boolean
- }
- const props = withDefaults(defineProps<Props>(), {
- visible: false,
- id: ''
- })
- const emit = defineEmits<{
- 'update:visible': [value: boolean]
- // 更新节点数据
- 'update:node:data': [data: IWorkflowNode]
- // 运行节点
- 'run-node': [id: string]
- }>()
- const node = computed<IWorkflowNode>(
- () => props.workflow.nodes.find((node) => node.id === props.id)!
- )
- const setter = computed(() => {
- return (
- node.value?.data?.nodeType && nodeMap[node.value.data.nodeType as keyof typeof nodeMap]?.Setter
- )
- })
- const nodeInfo = computed(() => {
- return node.value?.data?.nodeType
- ? nodeMap[node.value.data.nodeType as keyof typeof nodeMap]
- : undefined
- })
- const closeDrawer = () => {
- emit('update:visible', false)
- }
- /**
- * 更新节点配置数据 node.data
- */
- const onUpdateData = useDebounceFn((data: Record<string, unknown>) => {
- emit('update:node:data', {
- ...node.value,
- data: {
- ...node.value?.data,
- ...data
- }
- })
- }, 1000)
- const name = ref(node.value?.name || '')
- const remark = ref(node.value?.remark || '')
- const nodeVars = ref<NodeVar[]>([])
- const onUpdateName = () => {
- if (name.value !== node.value?.name && name.value.trim() !== '') {
- emit('update:node:data', { ...node.value, name: name.value })
- }
- }
- const onUpdateRemark = () => {
- emit('update:node:data', { ...node.value, remark: remark.value })
- }
- watch(
- () => [props.id, props.visible],
- async () => {
- name.value = node.value?.name || ''
- remark.value = node.value?.remark || ''
- if (props.id && props.visible) {
- const response = await agent.postAgentGetPrevNodeOutVariableList({
- node_id: props.id,
- varTypeList: []
- })
- nodeVars.value = (response.result as NodeVar[]) || []
- }
- }
- )
- provide('nodeVars', nodeVars)
- </script>
- <template>
- <div class="setter">
- <div class="drawer shadow-2xl" :class="{ 'drawer--open': props.visible && setter }">
- <header class="text-gray-800">
- <div class="w-full flex items-center justify-between">
- <h4 class="flex items-center">
- <span
- v-if="nodeInfo?.icon"
- class="h-22px w-22px flex items-center justify-center rounded-lg shrink-0"
- :style="{ background: nodeInfo?.iconColor }"
- >
- <Icon :icon="nodeInfo?.icon" color="#fff" :size="14" />
- </span>
- <Input
- v-model="name"
- placeholder="添加标题..."
- variant="borderless"
- @blur="onUpdateName"
- />
- </h4>
- <div class="flex items-center">
- <el-tooltip content="运行节点" placement="top">
- <Icon
- icon="lucide:play"
- width="20"
- height="20"
- class="text-gray-400 p-2 hover:cursor-pointer hover:bg-gray-200"
- @click="emit('run-node', id)"
- />
- </el-tooltip>
- <el-divider direction="vertical" />
- <Icon
- icon="lucide:x"
- height="24"
- width="24"
- @click="closeDrawer"
- class="cursor-pointer"
- ></Icon>
- </div>
- </div>
- <Input
- v-model="remark"
- placeholder="添加描述..."
- variant="borderless"
- @blur="onUpdateRemark"
- />
- </header>
- <div class="content">
- <el-tabs>
- <el-tab-pane label="设置">
- <div class="tab-pane tab-pane--fill">
- <component
- :is="setter"
- :key="node?.id"
- :id="node?.id"
- :data="node?.data"
- @update="onUpdateData"
- ></component>
- </div>
- </el-tab-pane>
- <el-tab-pane label="上次运行">
- <div class="tab-pane tab-pane--scroll">
- <NodeLog :node="node" />
- </div>
- </el-tab-pane>
- </el-tabs>
- </div>
- </div>
- </div>
- </template>
- <style lang="less" scoped>
- .setter {
- /* Drawer 主体 */
- .drawer {
- position: fixed;
- top: 60px;
- right: 5px;
- bottom: 10px;
- width: 420px;
- background: #fff;
- z-index: 1000;
- border-radius: 8px;
- display: flex;
- flex-direction: column;
- border: 1px solid #e4e4e4;
- /* 初始隐藏状态 */
- transform: translateX(110%);
- transition: transform 0.25s ease;
- }
- /* 显示状态 */
- .drawer--open {
- transform: translateX(0);
- }
- /* Header */
- .drawer header {
- height: 66px;
- padding: 16px;
- padding-bottom: 0;
- // border-bottom: 1px solid #eee;
- h4 {
- margin: 0;
- }
- }
- /* 内容区 */
- .drawer .content {
- flex: 1;
- min-height: 0;
- overflow: hidden;
- }
- :deep(.el-collapse-item__header) {
- box-sizing: border-box;
- padding: 0 8px;
- }
- :deep(.el-collapse-item__content) {
- box-sizing: border-box;
- padding: 0 8px 12px 8px;
- }
- :deep(.el-tabs__nav-scroll) {
- padding-left: 20px;
- }
- :deep(.el-tabs) {
- height: 100%;
- display: flex;
- flex-direction: column;
- }
- :deep(.el-tabs__header) {
- flex-shrink: 0;
- margin-bottom: 0;
- }
- :deep(.el-tabs__content) {
- flex: 1;
- min-height: 0;
- overflow: hidden;
- }
- :deep(.el-tab-pane) {
- height: 100%;
- }
- .tab-pane {
- height: 100%;
- min-height: 0;
- }
- .tab-pane--fill {
- display: flex;
- flex-direction: column;
- overflow-x: hidden;
- overflow-y: auto;
- }
- :deep(.tab-pane--fill > .el-scrollbar) {
- height: 100%;
- }
- .tab-pane--scroll {
- overflow-y: auto;
- }
- }
- </style>
|