WorkflowDataTable.vue 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. <template>
  2. <template v-if="!workflow.connectedTable">
  3. <ui-popover class="mb-4">
  4. <template #trigger>
  5. <ui-button> Connect to a storage table </ui-button>
  6. </template>
  7. <p>Select a table</p>
  8. <ui-list class="mt-2 space-y-1 max-h-80 overflow-auto w-64">
  9. <p v-if="state.tableList.length === 0">
  10. {{ t('message.noData') }}
  11. </p>
  12. <ui-list-item
  13. v-for="item in state.tableList"
  14. :key="item.id"
  15. class="text-overflow cursor-pointer"
  16. @click="connectTable(item)"
  17. >
  18. {{ item.name }}
  19. </ui-list-item>
  20. </ui-list>
  21. </ui-popover>
  22. <div class="flex mb-4">
  23. <ui-input
  24. v-model="state.query"
  25. autofocus
  26. :placeholder="t('workflow.table.placeholder')"
  27. class="mr-2 flex-1"
  28. @keyup.enter="addColumn"
  29. @keyup.esc="$emit('close')"
  30. />
  31. <ui-button variant="accent" @click="addColumn">
  32. {{ t('common.add') }}
  33. </ui-button>
  34. </div>
  35. </template>
  36. <div
  37. v-else-if="state.connectedTable"
  38. class="py-2 px-4 rounded-md bg-green-200 dark:bg-green-300 flex items-center mb-4"
  39. >
  40. <p class="mr-1 text-black">
  41. This workflow is connected to the
  42. <router-link
  43. :to="`/storage/tables/${state.connectedTable.id}`"
  44. class="underline"
  45. >
  46. {{ state.connectedTable.name }}
  47. </router-link>
  48. table
  49. </p>
  50. <v-remixicon
  51. name="riLinkUnlinkM"
  52. title="Disconnect table"
  53. class="cursor-pointer"
  54. @click="disconnectTable"
  55. />
  56. </div>
  57. <div
  58. class="overflow-y-auto scroll"
  59. style="max-height: 600px; height: calc(100vh - 13rem)"
  60. >
  61. <p v-if="columns.length === 0" class="text-center mt-4">
  62. {{ t('message.noData') }}
  63. </p>
  64. <ul v-else class="space-y-2 py-1">
  65. <li
  66. v-for="(column, index) in columns"
  67. :key="column.id"
  68. class="flex items-center space-x-2"
  69. >
  70. <ui-input
  71. :disabled="Boolean(workflow.connectedTable)"
  72. :model-value="columns[index].name"
  73. :placeholder="t('workflow.table.column.name')"
  74. class="flex-1"
  75. @blur="updateColumnName(index, $event.target)"
  76. />
  77. <ui-select
  78. v-model="columns[index].type"
  79. :disabled="Boolean(workflow.connectedTable)"
  80. :placeholder="t('workflow.table.column.type')"
  81. class="flex-1"
  82. >
  83. <option v-for="type in dataTypes" :key="type.id" :value="type.id">
  84. {{ type.name }}
  85. </option>
  86. </ui-select>
  87. <button
  88. v-if="!Boolean(workflow.connectedTable)"
  89. @click="state.columns.splice(index, 1)"
  90. >
  91. <v-remixicon name="riDeleteBin7Line" />
  92. </button>
  93. </li>
  94. </ul>
  95. </div>
  96. </template>
  97. <script setup>
  98. import { computed, onMounted, watch, reactive } from 'vue';
  99. import { nanoid } from 'nanoid';
  100. import { useI18n } from 'vue-i18n';
  101. import dbStorage from '@/db/storage';
  102. import { debounce } from '@/utils/helper';
  103. import { dataTypes } from '@/utils/constants/table';
  104. import { useWorkflowStore } from '@/stores/workflow';
  105. const props = defineProps({
  106. workflow: {
  107. type: Object,
  108. default: () => ({}),
  109. },
  110. });
  111. const emit = defineEmits([
  112. 'update',
  113. 'close',
  114. 'change',
  115. 'connect',
  116. 'disconnect',
  117. ]);
  118. const { t } = useI18n();
  119. const workflowStore = useWorkflowStore();
  120. const state = reactive({
  121. query: '',
  122. columns: [],
  123. tableList: [],
  124. connectedTable: null,
  125. });
  126. const columns = computed(() => {
  127. if (state.connectedTable) return state.connectedTable.columns;
  128. return state.columns.filter(({ name }) => name.includes(state.query));
  129. });
  130. function getColumnName(name) {
  131. const columnName = name.replace(/[\s@[\]]/g, '');
  132. const isColumnExists = state.columns.some(
  133. (column) => column.name === columnName
  134. );
  135. if (isColumnExists || columnName.trim() === '') return '';
  136. return columnName;
  137. }
  138. function updateColumnName(index, target) {
  139. const columnName = getColumnName(target.value);
  140. if (!columnName) {
  141. target.value = state.columns[index].name;
  142. return;
  143. }
  144. state.columns[index].name = columnName;
  145. }
  146. function addColumn() {
  147. const columnName = getColumnName(state.query);
  148. if (!columnName) return;
  149. state.columns.push({ id: nanoid(5), name: columnName, type: 'string' });
  150. state.query = '';
  151. }
  152. function connectTable(table) {
  153. workflowStore
  154. .update({
  155. id: props.workflow.id,
  156. data: { connectedTable: table.id },
  157. })
  158. .then(() => {
  159. emit('connect');
  160. state.query = '';
  161. state.connectedTable = table;
  162. });
  163. }
  164. function disconnectTable() {
  165. workflowStore
  166. .update({
  167. id: props.workflow.id,
  168. data: { connectedTable: null },
  169. })
  170. .then(() => {
  171. state.columns = props.workflow.table;
  172. state.connectedTable = null;
  173. emit('disconnect');
  174. });
  175. }
  176. watch(
  177. () => state.columns,
  178. debounce((newValue) => {
  179. if (props.workflow.connectedTable) return;
  180. const data = { table: newValue };
  181. emit('update', data);
  182. emit('change', data);
  183. }, 250),
  184. { deep: true }
  185. );
  186. onMounted(async () => {
  187. state.tableList = await dbStorage.tablesItems.toArray();
  188. if (props.workflow.connectedTable) {
  189. const findTable = state.tableList.find(
  190. (table) => table.id === props.workflow.connectedTable
  191. );
  192. if (findTable) {
  193. state.connectedTable = findTable;
  194. return;
  195. }
  196. emit('change', { connectedTable: null });
  197. emit('update', { connectedTable: null });
  198. }
  199. let isChanged = false;
  200. state.columns =
  201. props.workflow.table?.map((column) => {
  202. if (!column.id) {
  203. isChanged = true;
  204. column.id = column.name;
  205. }
  206. return column;
  207. }) || [];
  208. if (isChanged) {
  209. const data = { table: state.columns };
  210. emit('change', data);
  211. emit('update', data);
  212. }
  213. });
  214. </script>