WorkflowDetailsCard.vue 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. <template>
  2. <div class="px-4 flex items-start mb-2 mt-1">
  3. <ui-popover class="mr-2 h-8">
  4. <template #trigger>
  5. <span
  6. :title="t('workflow.sidebar.workflowIcon')"
  7. class="cursor-pointer inline-block h-full"
  8. >
  9. <ui-img
  10. v-if="workflow.icon.startsWith('http')"
  11. :src="workflow.icon"
  12. class="w-8 h-8"
  13. />
  14. <v-remixicon v-else :name="workflow.icon" size="26" class="mt-1" />
  15. </span>
  16. </template>
  17. <div class="w-56">
  18. <p class="mb-2">{{ t('workflow.sidebar.workflowIcon') }}</p>
  19. <div class="grid grid-cols-5 mb-2 gap-1">
  20. <span
  21. v-for="icon in icons"
  22. :key="icon"
  23. class="
  24. cursor-pointer
  25. rounded-lg
  26. inline-block
  27. text-center
  28. p-2
  29. hoverable
  30. "
  31. @click="$emit('update', { icon })"
  32. >
  33. <v-remixicon :name="icon" />
  34. </span>
  35. </div>
  36. <ui-input
  37. :model-value="workflow.icon.startsWith('ri') ? '' : workflow.icon"
  38. type="url"
  39. placeholder="http://example.com/img.png"
  40. label="Icon URL"
  41. @change="updateWorkflowIcon"
  42. />
  43. </div>
  44. </ui-popover>
  45. <div class="flex-1 overflow-hidden">
  46. <p class="font-semibold mt-1 text-overflow text-lg leading-tight">
  47. {{ workflow.name }}
  48. </p>
  49. <p class="line-clamp leading-tight">
  50. {{ workflow.description }}
  51. </p>
  52. </div>
  53. </div>
  54. <ui-input
  55. v-model="query"
  56. :placeholder="`${t('common.search')}...`"
  57. prepend-icon="riSearch2Line"
  58. class="px-4 mt-4 mb-2"
  59. />
  60. <div class="scroll bg-scroll overflow-auto px-4 flex-1 overflow-auto">
  61. <template v-for="(items, catId) in blocks" :key="catId">
  62. <div class="flex items-center top-0 space-x-2 mb-2">
  63. <span
  64. :class="categories[catId].color"
  65. class="h-3 w-3 rounded-full"
  66. ></span>
  67. <p class="capitalize text-gray-600">{{ categories[catId].name }}</p>
  68. </div>
  69. <div class="grid grid-cols-2 gap-2 mb-4">
  70. <div
  71. v-for="block in items"
  72. :key="block.id"
  73. :title="
  74. t(
  75. `workflow.blocks.${block.id}.${
  76. block.description ? 'description' : 'name'
  77. }`
  78. )
  79. "
  80. draggable="true"
  81. class="
  82. transform
  83. select-none
  84. cursor-move
  85. relative
  86. p-4
  87. rounded-lg
  88. bg-input
  89. transition
  90. "
  91. @dragstart="
  92. $event.dataTransfer.setData('block', JSON.stringify(block))
  93. "
  94. >
  95. <a
  96. v-if="block.docs"
  97. :href="`https://github.com/Kholid060/automa/wiki/Blocks#${block.id}`"
  98. target="_blank"
  99. :title="t('common.docs')"
  100. rel="noopener"
  101. class="absolute top-px right-2"
  102. >
  103. &#128712;
  104. </a>
  105. <v-remixicon :name="block.icon" size="24" class="mb-2" />
  106. <p class="leading-tight text-overflow">
  107. {{ block.name }}
  108. </p>
  109. </div>
  110. </div>
  111. </template>
  112. </div>
  113. </template>
  114. <script setup>
  115. import { computed, ref } from 'vue';
  116. import { useI18n } from 'vue-i18n';
  117. import { tasks, categories } from '@/utils/shared';
  118. defineProps({
  119. workflow: {
  120. type: Object,
  121. default: () => ({}),
  122. },
  123. dataChanged: {
  124. type: Boolean,
  125. default: false,
  126. },
  127. });
  128. const emit = defineEmits(['update']);
  129. const { t } = useI18n();
  130. const icons = [
  131. 'riGlobalLine',
  132. 'riFileTextLine',
  133. 'riEqualizerLine',
  134. 'riTimerLine',
  135. 'riCalendarLine',
  136. 'riFlashlightLine',
  137. 'riLightbulbFlashLine',
  138. 'riDatabase2Line',
  139. 'riWindowLine',
  140. 'riCursorLine',
  141. 'riDownloadLine',
  142. 'riCommandLine',
  143. ];
  144. const blocksArr = Object.entries(tasks).map(([key, block]) => ({
  145. ...block,
  146. id: key,
  147. name: t(`workflow.blocks.${key}.name`),
  148. }));
  149. const query = ref('');
  150. const blocks = computed(() =>
  151. blocksArr.reduce((arr, block) => {
  152. if (
  153. block.name.toLocaleLowerCase().includes(query.value.toLocaleLowerCase())
  154. ) {
  155. (arr[block.category] = arr[block.category] || []).push(block);
  156. }
  157. return arr;
  158. }, {})
  159. );
  160. function updateWorkflowIcon(value) {
  161. if (!value.startsWith('http')) return;
  162. const iconUrl = value.slice(0, 1024);
  163. emit('update', { icon: iconUrl });
  164. }
  165. </script>