PublishBtn.vue 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. <template>
  2. <el-popover
  3. ref="popoverRef"
  4. width="300px"
  5. trigger="click"
  6. placement="bottom-end"
  7. :disabled="isPublishing"
  8. >
  9. <div class="publish-selector">
  10. <div class="publish-selector__title">{{ t('pages.editor.selectPublishNode') }}</div>
  11. <div class="flex items-center gap-2">
  12. <el-input v-model="search" placeholder="搜索" />
  13. </div>
  14. <el-divider class="my-0" />
  15. <el-scrollbar height="280px">
  16. <button
  17. v-for="node in publishNodes"
  18. :key="node.id"
  19. type="button"
  20. class="publish-selector__item"
  21. @click="handleSelectPublishNode(node.id)"
  22. >
  23. <span class="flex items-center gap-1">
  24. <Icon
  25. :icon="nodeMap[node.nodeType]?.icon!"
  26. :color="nodeMap[node.nodeType]?.iconColor"
  27. :size="18"
  28. />
  29. <span class="publish-selector__name">{{ node.name }}</span>
  30. </span>
  31. <span class="publish-selector__type">{{ nodeMap[node.nodeType]?.displayName }}</span>
  32. </button>
  33. <el-empty v-if="!publishNodes.length" :image-size="120" description="无节点数据" />
  34. </el-scrollbar>
  35. </div>
  36. <template #reference>
  37. <el-button
  38. :type="isPublished ? 'success' : 'default'"
  39. size="small"
  40. :disabled="!publishNodes.length"
  41. :loading="isPublishing"
  42. @click="handlePublishClick"
  43. >
  44. <Icon v-if="isPublished" icon="ix:success" class="mr-1" />
  45. {{
  46. isPublished ? t('pages.editor.status.published') : t('pages.editor.status.unpublished')
  47. }}
  48. </el-button>
  49. </template>
  50. </el-popover>
  51. </template>
  52. <script setup lang="ts">
  53. import { computed, ref } from 'vue'
  54. import { ElMessage, type PopoverInstance } from 'element-plus'
  55. import { agent } from '@repo/api-service'
  56. import { Icon } from '@repo/ui'
  57. import type { IWorkflow } from '@repo/workflow'
  58. import { useI18n } from '@/composables/useI18n'
  59. import { nodeMap } from '@/nodes'
  60. const props = defineProps<{
  61. workflow: IWorkflow
  62. }>()
  63. const emit = defineEmits<{
  64. (e: 'published'): void
  65. }>()
  66. const { t } = useI18n()
  67. const popoverRef = ref<PopoverInstance>()
  68. const isPublishing = ref(false)
  69. const search = ref('')
  70. // const PUBLISHABLE_NODE_TYPES = ['start', 'trigger-schedule', 'trigger-webhook']
  71. const isPublished = computed(() => props.workflow?.status === 'published')
  72. const publishNodes = computed(() => {
  73. return (
  74. (props.workflow?.nodes || [])
  75. // .filter((node) => {
  76. // const nodeType = (node as any)?.nodeType || (node as any)?.data?.nodeType
  77. // return PUBLISHABLE_NODE_TYPES.includes(nodeType || '')
  78. // })
  79. .filter((node) => node.name?.includes(search.value))
  80. .map((node) => {
  81. const nodeType = ((node as any)?.nodeType || (node as any)?.data?.nodeType || '') as string
  82. return {
  83. id: node.id,
  84. name: node.name || nodeType,
  85. nodeType
  86. }
  87. })
  88. )
  89. })
  90. const publishAgent = async (nodeId?: string) => {
  91. if (!nodeId) {
  92. ElMessage.warning(t('pages.editor.messages.missingPublishNode'))
  93. return
  94. }
  95. if (isPublishing.value) {
  96. return
  97. }
  98. isPublishing.value = true
  99. try {
  100. const response = await agent.postAgentDoAgentPublish({
  101. start_node_id: nodeId,
  102. appAgentId: props.workflow.id
  103. })
  104. if (response?.isSuccess) {
  105. props.workflow.status = 'published'
  106. popoverRef.value?.hide()
  107. ElMessage.success(t('pages.editor.messages.publishSuccess'))
  108. emit('published')
  109. return
  110. }
  111. if ((response as any)?.error) {
  112. ElMessage.error((response as any).error)
  113. return
  114. }
  115. ElMessage.error(t('pages.editor.messages.publishFailed'))
  116. } catch (error) {
  117. console.error('postAgentDoAgentPublish error', error)
  118. ElMessage.error(t('pages.editor.messages.publishFailed'))
  119. } finally {
  120. isPublishing.value = false
  121. }
  122. }
  123. const handlePublishClick = () => {
  124. if (publishNodes.value.length <= 1) {
  125. publishAgent(publishNodes.value[0]?.id)
  126. }
  127. }
  128. const handleSelectPublishNode = (id: string) => {
  129. publishAgent(id)
  130. }
  131. </script>
  132. <style scoped>
  133. .publish-selector {
  134. display: flex;
  135. flex-direction: column;
  136. gap: 8px;
  137. }
  138. .publish-selector__title {
  139. font-size: 13px;
  140. font-weight: 600;
  141. color: #344054;
  142. }
  143. .publish-selector__item {
  144. display: flex;
  145. align-items: center;
  146. justify-content: space-between;
  147. gap: 12px;
  148. width: 100%;
  149. padding: 10px 12px;
  150. border: 1px solid #e4e7ec;
  151. border-radius: 10px;
  152. background: #fff;
  153. cursor: pointer;
  154. text-align: left;
  155. margin-bottom: 8px;
  156. }
  157. .publish-selector__item:hover {
  158. border-color: #2563eb;
  159. background: #f8fbff;
  160. }
  161. .publish-selector__name {
  162. font-size: 13px;
  163. font-weight: 500;
  164. color: #344054;
  165. }
  166. .publish-selector__type {
  167. font-size: 12px;
  168. color: #667085;
  169. }
  170. </style>