mirror of
				https://github.com/Fabio286/antares.git
				synced 2025-06-05 21:59:22 +02:00 
			
		
		
		
	feat: views edit
This commit is contained in:
		| @@ -20,4 +20,14 @@ export default (connections) => { | ||||
|          return { status: 'error', response: err.toString() }; | ||||
|       } | ||||
|    }); | ||||
|  | ||||
|    ipcMain.handle('alter-view', async (event, params) => { | ||||
|       try { | ||||
|          await connections[params.uid].alterView(params); | ||||
|          return { status: 'success' }; | ||||
|       } | ||||
|       catch (err) { | ||||
|          return { status: 'error', response: err.toString() }; | ||||
|       } | ||||
|    }); | ||||
| }; | ||||
|   | ||||
| @@ -295,6 +295,18 @@ export class MySQLClient extends AntaresCore { | ||||
|       return await this.raw(sql); | ||||
|    } | ||||
|  | ||||
|    /** | ||||
|     * ALTER VIEW | ||||
|     * | ||||
|     * @returns {Array.<Object>} parameters | ||||
|     * @memberof MySQLClient | ||||
|     */ | ||||
|    async alterView (params) { | ||||
|       const { view } = params; | ||||
|       const sql = `ALTER ALGORITHM = ${view.algorithm} DEFINER=${view.definer} SQL SECURITY ${view.security} VIEW \`${view.name}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}`; | ||||
|       return await this.raw(sql); | ||||
|    } | ||||
|  | ||||
|    /** | ||||
|     * SHOW COLLATION | ||||
|     * | ||||
|   | ||||
| @@ -182,8 +182,13 @@ export default { | ||||
|       if (this.autoFocus) { | ||||
|          setTimeout(() => { | ||||
|             this.editor.focus(); | ||||
|             this.editor.resize(); | ||||
|          }, 20); | ||||
|       } | ||||
|  | ||||
|       setTimeout(() => { | ||||
|          this.editor.resize(); | ||||
|       }, 20); | ||||
|    } | ||||
| }; | ||||
| </script> | ||||
|   | ||||
| @@ -13,25 +13,25 @@ | ||||
|                </a> | ||||
|             </li> | ||||
|             <li | ||||
|                v-if="workspace.breadcrumbs.table" | ||||
|                v-if="schemaChild" | ||||
|                class="tab-item" | ||||
|                :class="{'active': selectedTab === 'prop'}" | ||||
|                @click="selectTab({uid: workspace.uid, tab: 'prop'})" | ||||
|             > | ||||
|                <a class="tab-link"> | ||||
|                   <i class="mdi mdi-18px mdi-tune mr-1" /> | ||||
|                   <span :title="workspace.breadcrumbs.table">{{ $t('word.properties').toUpperCase() }}: {{ workspace.breadcrumbs.table }}</span> | ||||
|                   <span :title="schemaChild">{{ $t('word.properties').toUpperCase() }}: {{ schemaChild }}</span> | ||||
|                </a> | ||||
|             </li> | ||||
|             <li | ||||
|                v-if="workspace.breadcrumbs.table" | ||||
|                v-if="workspace.breadcrumbs.table || workspace.breadcrumbs.view" | ||||
|                class="tab-item" | ||||
|                :class="{'active': selectedTab === 'data'}" | ||||
|                @click="selectTab({uid: workspace.uid, tab: 'data'})" | ||||
|             > | ||||
|                <a class="tab-link"> | ||||
|                   <i class="mdi mdi-18px mdi-table mr-1" /> | ||||
|                   <span :title="workspace.breadcrumbs.table">{{ $t('word.data').toUpperCase() }}: {{ workspace.breadcrumbs.table }}</span> | ||||
|                   <i class="mdi mdi-18px mr-1" :class="workspace.breadcrumbs.table ? 'mdi-table' : 'mdi-table-eye'" /> | ||||
|                   <span :title="schemaChild">{{ $t('word.data').toUpperCase() }}: {{ schemaChild }}</span> | ||||
|                </a> | ||||
|             </li> | ||||
|             <li | ||||
| @@ -66,15 +66,21 @@ | ||||
|             </li> | ||||
|          </ul> | ||||
|          <WorkspacePropsTab | ||||
|             v-show="selectedTab === 'prop'" | ||||
|             v-show="selectedTab === 'prop' && workspace.breadcrumbs.table" | ||||
|             :is-selected="selectedTab === 'prop'" | ||||
|             :connection="connection" | ||||
|             :table="workspace.breadcrumbs.table" | ||||
|          /> | ||||
|          <WorkspacePropsTabView | ||||
|             v-show="selectedTab === 'prop' && workspace.breadcrumbs.view" | ||||
|             :is-selected="selectedTab === 'prop'" | ||||
|             :connection="connection" | ||||
|             :view="workspace.breadcrumbs.view" | ||||
|          /> | ||||
|          <WorkspaceTableTab | ||||
|             v-show="selectedTab === 'data'" | ||||
|             :connection="connection" | ||||
|             :table="workspace.breadcrumbs.table" | ||||
|             :table="workspace.breadcrumbs.table || workspace.breadcrumbs.view" | ||||
|          /> | ||||
|          <WorkspaceQueryTab | ||||
|             v-for="tab of queryTabs" | ||||
| @@ -94,6 +100,7 @@ import WorkspaceExploreBar from '@/components/WorkspaceExploreBar'; | ||||
| import WorkspaceQueryTab from '@/components/WorkspaceQueryTab'; | ||||
| import WorkspaceTableTab from '@/components/WorkspaceTableTab'; | ||||
| import WorkspacePropsTab from '@/components/WorkspacePropsTab'; | ||||
| import WorkspacePropsTabView from '@/components/WorkspacePropsTabView'; | ||||
|  | ||||
| export default { | ||||
|    name: 'Workspace', | ||||
| @@ -101,7 +108,8 @@ export default { | ||||
|       WorkspaceExploreBar, | ||||
|       WorkspaceQueryTab, | ||||
|       WorkspaceTableTab, | ||||
|       WorkspacePropsTab | ||||
|       WorkspacePropsTab, | ||||
|       WorkspacePropsTabView | ||||
|    }, | ||||
|    props: { | ||||
|       connection: Object | ||||
| @@ -123,7 +131,7 @@ export default { | ||||
|          return this.selectedWorkspace === this.connection.uid; | ||||
|       }, | ||||
|       selectedTab () { | ||||
|          if (this.workspace.breadcrumbs.table === null && ['data', 'prop'].includes(this.workspace.selected_tab)) | ||||
|          if (this.workspace.breadcrumbs.table === null && this.workspace.breadcrumbs.view === null && ['data', 'prop'].includes(this.workspace.selected_tab)) | ||||
|             return this.queryTabs[0].uid; | ||||
|  | ||||
|          return this.queryTabs.find(tab => tab.uid === this.workspace.selected_tab) || | ||||
| @@ -133,6 +141,13 @@ export default { | ||||
|       }, | ||||
|       queryTabs () { | ||||
|          return this.workspace.tabs.filter(tab => tab.type === 'query'); | ||||
|       }, | ||||
|       schemaChild () { | ||||
|          for (const key in this.workspace.breadcrumbs) { | ||||
|             if (key === 'schema') continue; | ||||
|             if (this.workspace.breadcrumbs[key]) return this.workspace.breadcrumbs[key]; | ||||
|          } | ||||
|          return false; | ||||
|       } | ||||
|    }, | ||||
|    async created () { | ||||
|   | ||||
| @@ -10,6 +10,18 @@ | ||||
|             <div class="context-element" @click="showCreateTableModal"> | ||||
|                <span class="d-flex"><i class="mdi mdi-18px mdi-table text-light pr-1" /> {{ $t('word.table') }}</span> | ||||
|             </div> | ||||
|             <div class="context-element" @click="showCreateTableModal"> | ||||
|                <span class="d-flex"><i class="mdi mdi-18px mdi-table-eye text-light pr-1" /> {{ $t('word.view') }}</span> | ||||
|             </div> | ||||
|             <div class="context-element d-none" @click="false"> | ||||
|                <span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ $t('word.trigger') }}</span> | ||||
|             </div> | ||||
|             <div class="context-element d-none" @click="false"> | ||||
|                <span class="d-flex"><i class="mdi mdi-18px mdi-cog-box pr-1" /> {{ $t('word.storedRoutine') }}</span> | ||||
|             </div> | ||||
|             <div class="context-element d-none" @click="false"> | ||||
|                <span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ $t('word.scheduler') }}</span> | ||||
|             </div> | ||||
|          </div> | ||||
|       </div> | ||||
|       <div class="context-element" @click="showEditModal"> | ||||
| @@ -124,3 +136,8 @@ export default { | ||||
|    } | ||||
| }; | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .context-submenu { | ||||
|   min-width: 150px !important; | ||||
| } | ||||
| </style> | ||||
|   | ||||
							
								
								
									
										296
									
								
								src/renderer/components/WorkspacePropsTabView.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										296
									
								
								src/renderer/components/WorkspacePropsTabView.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,296 @@ | ||||
| <template> | ||||
|    <div class="workspace-query-tab column col-12 columns col-gapless"> | ||||
|       <div class="workspace-query-runner column col-12"> | ||||
|          <div class="workspace-query-runner-footer"> | ||||
|             <div class="workspace-query-buttons"> | ||||
|                <button | ||||
|                   class="btn btn-primary btn-sm" | ||||
|                   :disabled="!isChanged" | ||||
|                   :class="{'loading':isSaving}" | ||||
|                   @click="saveChanges" | ||||
|                > | ||||
|                   <span>{{ $t('word.save') }}</span> | ||||
|                   <i class="mdi mdi-24px mdi-content-save ml-1" /> | ||||
|                </button> | ||||
|                <button | ||||
|                   :disabled="!isChanged" | ||||
|                   class="btn btn-link btn-sm mr-0" | ||||
|                   :title="$t('message.clearChanges')" | ||||
|                   @click="clearChanges" | ||||
|                > | ||||
|                   <span>{{ $t('word.clear') }}</span> | ||||
|                   <i class="mdi mdi-24px mdi-delete-sweep ml-1" /> | ||||
|                </button> | ||||
|             </div> | ||||
|          </div> | ||||
|       </div> | ||||
|       <div class="container"> | ||||
|          <div class="columns mb-4"> | ||||
|             <div class="column col-3"> | ||||
|                <div class="form-group"> | ||||
|                   <label class="form-label">{{ $t('word.name') }}</label> | ||||
|                   <input | ||||
|                      v-model="localView.name" | ||||
|                      class="form-input" | ||||
|                      type="text" | ||||
|                   > | ||||
|                </div> | ||||
|             </div> | ||||
|             <div class="column col-3"> | ||||
|                <div class="form-group"> | ||||
|                   <label class="form-label">{{ $t('word.definer') }}</label> | ||||
|                   <input | ||||
|                      v-model="localView.definer" | ||||
|                      class="form-input" | ||||
|                      type="text" | ||||
|                      readonly | ||||
|                   > | ||||
|                </div> | ||||
|             </div> | ||||
|          </div> | ||||
|          <div class="columns"> | ||||
|             <div class="column col-2"> | ||||
|                <div class="form-group"> | ||||
|                   <label class="form-label">{{ $t('message.sqlSecurity') }}</label> | ||||
|                   <label class="form-radio"> | ||||
|                      <input | ||||
|                         v-model="localView.security" | ||||
|                         type="radio" | ||||
|                         name="security" | ||||
|                         value="DEFINER" | ||||
|                      > | ||||
|                      <i class="form-icon" /> DEFINER | ||||
|                   </label> | ||||
|                   <label class="form-radio"> | ||||
|                      <input | ||||
|                         v-model="localView.security" | ||||
|                         type="radio" | ||||
|                         name="security" | ||||
|                         value="INVOKER" | ||||
|                      > | ||||
|                      <i class="form-icon" /> INVOKER | ||||
|                   </label> | ||||
|                </div> | ||||
|             </div> | ||||
|             <div class="column col-2"> | ||||
|                <div class="form-group"> | ||||
|                   <label class="form-label">{{ $t('word.algorithm') }}</label> | ||||
|                   <label class="form-radio"> | ||||
|                      <input | ||||
|                         v-model="localView.algorithm" | ||||
|                         type="radio" | ||||
|                         name="algorithm" | ||||
|                         value="UNDEFINED" | ||||
|                      > | ||||
|                      <i class="form-icon" /> UNDEFINED | ||||
|                   </label> | ||||
|                   <label class="form-radio"> | ||||
|                      <input | ||||
|                         v-model="localView.algorithm" | ||||
|                         type="radio" | ||||
|                         value="MERGE" | ||||
|                         name="algorithm" | ||||
|                      > | ||||
|                      <i class="form-icon" /> MERGE | ||||
|                   </label> | ||||
|                   <label class="form-radio"> | ||||
|                      <input | ||||
|                         v-model="localView.algorithm" | ||||
|                         type="radio" | ||||
|                         value="TEMPTABLE" | ||||
|                         name="algorithm" | ||||
|                      > | ||||
|                      <i class="form-icon" /> TEMPTABLE | ||||
|                   </label> | ||||
|                </div> | ||||
|             </div> | ||||
|             <div class="column col-2"> | ||||
|                <div class="form-group"> | ||||
|                   <label class="form-label">{{ $t('message.updateOption') }}</label> | ||||
|                   <label class="form-radio"> | ||||
|                      <input | ||||
|                         v-model="localView.updateOption" | ||||
|                         type="radio" | ||||
|                         name="update" | ||||
|                         value="" | ||||
|                      > | ||||
|                      <i class="form-icon" /> None | ||||
|                   </label> | ||||
|                   <label class="form-radio"> | ||||
|                      <input | ||||
|                         v-model="localView.updateOption" | ||||
|                         type="radio" | ||||
|                         name="update" | ||||
|                         value="CASCADED" | ||||
|                      > | ||||
|                      <i class="form-icon" /> CASCADED | ||||
|                   </label> | ||||
|                   <label class="form-radio"> | ||||
|                      <input | ||||
|                         v-model="localView.updateOption" | ||||
|                         type="radio" | ||||
|                         name="update" | ||||
|                         value="LOCAL" | ||||
|                      > | ||||
|                      <i class="form-icon" /> LOCAL | ||||
|                   </label> | ||||
|                </div> | ||||
|             </div> | ||||
|          </div> | ||||
|       </div> | ||||
|       <div class="workspace-query-results column col-12 mt-2"> | ||||
|          <label class="form-label ml-2">{{ $t('message.selectStatement') }}</label> | ||||
|          <QueryEditor | ||||
|             v-if="isSelected" | ||||
|             ref="queryEditor" | ||||
|             :value.sync="localView.sql" | ||||
|             :workspace="workspace" | ||||
|             :schema="schema" | ||||
|             :height="editorHeight" | ||||
|          /> | ||||
|       </div> | ||||
|    </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { mapGetters, mapActions } from 'vuex'; | ||||
| import QueryEditor from '@/components/QueryEditor'; | ||||
| import Views from '@/ipc-api/Views'; | ||||
|  | ||||
| export default { | ||||
|    name: 'WorkspacePropsTabView', | ||||
|    components: { | ||||
|       QueryEditor | ||||
|    }, | ||||
|    props: { | ||||
|       connection: Object, | ||||
|       view: String | ||||
|    }, | ||||
|    data () { | ||||
|       return { | ||||
|          tabUid: 'prop', | ||||
|          isQuering: false, | ||||
|          isSaving: false, | ||||
|          originalView: null, | ||||
|          localView: { sql: '' }, | ||||
|          lastView: null, | ||||
|          sqlProxy: '', | ||||
|          editorHeight: 300 | ||||
|       }; | ||||
|    }, | ||||
|    computed: { | ||||
|       ...mapGetters({ | ||||
|          getWorkspace: 'workspaces/getWorkspace' | ||||
|       }), | ||||
|       workspace () { | ||||
|          return this.getWorkspace(this.connection.uid); | ||||
|       }, | ||||
|       isSelected () { | ||||
|          return this.workspace.selected_tab === 'prop'; | ||||
|       }, | ||||
|       schema () { | ||||
|          return this.workspace.breadcrumbs.schema; | ||||
|       }, | ||||
|       isChanged () { | ||||
|          return JSON.stringify(this.originalView) !== JSON.stringify(this.localView); | ||||
|       } | ||||
|    }, | ||||
|    watch: { | ||||
|       async view () { | ||||
|          if (this.isSelected) { | ||||
|             await this.getViewData(); | ||||
|             this.$refs.queryEditor.editor.session.setValue(this.localView.sql); | ||||
|             this.lastView = this.view; | ||||
|          } | ||||
|       }, | ||||
|       async isSelected (val) { | ||||
|          if (val && this.lastView !== this.view) { | ||||
|             await this.getViewData(); | ||||
|             this.$refs.queryEditor.editor.session.setValue(this.localView.sql); | ||||
|             this.lastView = this.view; | ||||
|          } | ||||
|       }, | ||||
|       isChanged (val) { | ||||
|          if (this.isSelected && this.lastView === this.view && this.view !== null) | ||||
|             this.setUnsavedChanges(val); | ||||
|       } | ||||
|    }, | ||||
|    mounted () { | ||||
|       window.addEventListener('resize', this.resizeQueryEditor); | ||||
|    }, | ||||
|    destroyed () { | ||||
|       window.removeEventListener('resize', this.resizeQueryEditor); | ||||
|    }, | ||||
|    methods: { | ||||
|       ...mapActions({ | ||||
|          addNotification: 'notifications/addNotification', | ||||
|          refreshStructure: 'workspaces/refreshStructure', | ||||
|          setUnsavedChanges: 'workspaces/setUnsavedChanges' | ||||
|       }), | ||||
|       async getViewData () { | ||||
|          if (!this.view) return; | ||||
|          this.isQuering = true; | ||||
|  | ||||
|          const params = { | ||||
|             uid: this.connection.uid, | ||||
|             schema: this.schema, | ||||
|             view: this.workspace.breadcrumbs.view | ||||
|          }; | ||||
|  | ||||
|          try { | ||||
|             const { status, response } = await Views.getViewInformations(params); | ||||
|             if (status === 'success') { | ||||
|                this.originalView = response; | ||||
|                this.localView = JSON.parse(JSON.stringify(this.originalView)); | ||||
|                this.sqlProxy = this.localView.sql; | ||||
|             } | ||||
|             else | ||||
|                this.addNotification({ status: 'error', message: response }); | ||||
|          } | ||||
|          catch (err) { | ||||
|             this.addNotification({ status: 'error', message: err.stack }); | ||||
|          } | ||||
|  | ||||
|          this.resizeQueryEditor(); | ||||
|          this.isQuering = false; | ||||
|       }, | ||||
|       async saveChanges () { | ||||
|          if (this.isSaving) return; | ||||
|          this.isSaving = true; | ||||
|          const params = { | ||||
|             uid: this.connection.uid, | ||||
|             schema: this.schema, | ||||
|             view: this.localView | ||||
|          }; | ||||
|  | ||||
|          try { | ||||
|             const { status, response } = await Views.alterView(params); | ||||
|  | ||||
|             if (status === 'success') { | ||||
|                await this.refreshStructure(this.connection.uid); | ||||
|                this.getViewData(); | ||||
|             } | ||||
|             else | ||||
|                this.addNotification({ status: 'error', message: response }); | ||||
|          } | ||||
|          catch (err) { | ||||
|             this.addNotification({ status: 'error', message: err.stack }); | ||||
|          } | ||||
|  | ||||
|          this.isSaving = false; | ||||
|       }, | ||||
|       clearChanges () { | ||||
|          this.localView = JSON.parse(JSON.stringify(this.originalView)); | ||||
|          this.$refs.queryEditor.editor.session.setValue(this.localView.sql); | ||||
|       }, | ||||
|       resizeQueryEditor () { | ||||
|          if (this.$refs.queryEditor) { | ||||
|             const footer = document.getElementById('footer'); | ||||
|             const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight; | ||||
|             this.editorHeight = size; | ||||
|             this.$refs.queryEditor.editor.resize(); | ||||
|          } | ||||
|       } | ||||
|    } | ||||
| }; | ||||
| </script> | ||||
| @@ -9,4 +9,8 @@ export default class { | ||||
|    static dropView (params) { | ||||
|       return ipcRenderer.invoke('drop-view', params); | ||||
|    } | ||||
|  | ||||
|    static alterView (params) { | ||||
|       return ipcRenderer.invoke('alter-view', params); | ||||
|    } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user