mirror of
				https://github.com/Fabio286/antares.git
				synced 2025-06-05 21:59:22 +02:00 
			
		
		
		
	feat: index management
This commit is contained in:
		
							
								
								
									
										14
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								package.json
									
									
									
									
									
								
							| @@ -4,7 +4,7 @@ | |||||||
|   "version": "0.0.9", |   "version": "0.0.9", | ||||||
|   "description": "A cross-platform easy to use SQL client.", |   "description": "A cross-platform easy to use SQL client.", | ||||||
|   "license": "MIT", |   "license": "MIT", | ||||||
|   "repository": "https://github.com/EStarium/antares.git", |   "repository": "https://github.com/Fabio286/antares.git", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "dev": "cross-env NODE_ENV=development electron-webpack dev", |     "dev": "cross-env NODE_ENV=development electron-webpack dev", | ||||||
|     "compile": "electron-webpack", |     "compile": "electron-webpack", | ||||||
| @@ -17,7 +17,7 @@ | |||||||
|   }, |   }, | ||||||
|   "author": "Fabio Di Stasio <fabio286@gmail.com>", |   "author": "Fabio Di Stasio <fabio286@gmail.com>", | ||||||
|   "build": { |   "build": { | ||||||
|     "appId": "com.estarium.antares", |     "appId": "com.fabio286.antares", | ||||||
|     "artifactName": "${productName}-${version}-${os}_${arch}.${ext}", |     "artifactName": "${productName}-${version}-${os}_${arch}.${ext}", | ||||||
|     "dmg": { |     "dmg": { | ||||||
|       "contents": [ |       "contents": [ | ||||||
| @@ -58,10 +58,10 @@ | |||||||
|     "pg": "^8.5.1", |     "pg": "^8.5.1", | ||||||
|     "source-map-support": "^0.5.16", |     "source-map-support": "^0.5.16", | ||||||
|     "spectre.css": "^0.5.9", |     "spectre.css": "^0.5.9", | ||||||
|     "vue-i18n": "^8.22.1", |     "vue-i18n": "^8.22.2", | ||||||
|     "vue-the-mask": "^0.11.1", |     "vue-the-mask": "^0.11.1", | ||||||
|     "vuedraggable": "^2.24.3", |     "vuedraggable": "^2.24.3", | ||||||
|     "vuex": "^3.5.1", |     "vuex": "^3.6.0", | ||||||
|     "vuex-persist": "^3.1.3" |     "vuex-persist": "^3.1.3" | ||||||
|   }, |   }, | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
| @@ -72,8 +72,8 @@ | |||||||
|     "electron-devtools-installer": "^3.1.1", |     "electron-devtools-installer": "^3.1.1", | ||||||
|     "electron-webpack": "^2.8.2", |     "electron-webpack": "^2.8.2", | ||||||
|     "electron-webpack-vue": "^2.4.0", |     "electron-webpack-vue": "^2.4.0", | ||||||
|     "eslint": "^7.13.0", |     "eslint": "^7.14.0", | ||||||
|     "eslint-config-standard": "^16.0.1", |     "eslint-config-standard": "^16.0.2", | ||||||
|     "eslint-plugin-import": "^2.22.1", |     "eslint-plugin-import": "^2.22.1", | ||||||
|     "eslint-plugin-node": "^11.1.0", |     "eslint-plugin-node": "^11.1.0", | ||||||
|     "eslint-plugin-promise": "^4.2.1", |     "eslint-plugin-promise": "^4.2.1", | ||||||
| @@ -82,7 +82,7 @@ | |||||||
|     "node-sass": "^5.0.0", |     "node-sass": "^5.0.0", | ||||||
|     "sass-loader": "^10.1.0", |     "sass-loader": "^10.1.0", | ||||||
|     "standard-version": "^9.0.0", |     "standard-version": "^9.0.0", | ||||||
|     "stylelint": "^13.7.2", |     "stylelint": "^13.8.0", | ||||||
|     "stylelint-config-standard": "^20.0.0", |     "stylelint-config-standard": "^20.0.0", | ||||||
|     "stylelint-scss": "^3.18.0", |     "stylelint-scss": "^3.18.0", | ||||||
|     "vue": "^2.6.12", |     "vue": "^2.6.12", | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								src/common/index-types/mysql.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/common/index-types/mysql.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | module.exports = [ | ||||||
|  |    'PRIMARY', | ||||||
|  |    'INDEX', | ||||||
|  |    'UNIQUE', | ||||||
|  |    'FULLTEXT' | ||||||
|  | ]; | ||||||
| @@ -325,9 +325,12 @@ export class MySQLClient extends AntaresCore { | |||||||
|          additions, |          additions, | ||||||
|          deletions, |          deletions, | ||||||
|          changes, |          changes, | ||||||
|  |          indexChanges, | ||||||
|          options |          options | ||||||
|       } = params; |       } = params; | ||||||
|  |  | ||||||
|  |       console.log(params); | ||||||
|  |  | ||||||
|       let sql = `ALTER TABLE \`${table}\` `; |       let sql = `ALTER TABLE \`${table}\` `; | ||||||
|       const alterColumns = []; |       const alterColumns = []; | ||||||
|  |  | ||||||
| @@ -337,7 +340,7 @@ export class MySQLClient extends AntaresCore { | |||||||
|       if ('autoIncrement' in options) alterColumns.push(`AUTO_INCREMENT=${+options.autoIncrement}`); |       if ('autoIncrement' in options) alterColumns.push(`AUTO_INCREMENT=${+options.autoIncrement}`); | ||||||
|       if ('collation' in options) alterColumns.push(`COLLATE='${options.collation}'`); |       if ('collation' in options) alterColumns.push(`COLLATE='${options.collation}'`); | ||||||
|  |  | ||||||
|       // ADD |       // ADD FIELDS | ||||||
|       additions.forEach(addition => { |       additions.forEach(addition => { | ||||||
|          const length = addition.numLength || addition.charLength || addition.datePrecision; |          const length = addition.numLength || addition.charLength || addition.datePrecision; | ||||||
|  |  | ||||||
| @@ -354,7 +357,22 @@ export class MySQLClient extends AntaresCore { | |||||||
|             ${addition.after ? `AFTER \`${addition.after}\`` : 'FIRST'}`); |             ${addition.after ? `AFTER \`${addition.after}\`` : 'FIRST'}`); | ||||||
|       }); |       }); | ||||||
|  |  | ||||||
|       // CHANGE |       // ADD INDEX | ||||||
|  |       indexChanges.additions.forEach(addition => { | ||||||
|  |          const fields = addition.fields.map(field => `\`${field}\``).join(','); | ||||||
|  |          let type = addition.type; | ||||||
|  |  | ||||||
|  |          if (type === 'PRIMARY') | ||||||
|  |             alterColumns.push(`ADD PRIMARY KEY (${fields})`); | ||||||
|  |          else { | ||||||
|  |             if (type === 'UNIQUE') | ||||||
|  |                type = 'UNIQUE INDEX'; | ||||||
|  |  | ||||||
|  |             alterColumns.push(`ADD ${type} \`${addition.name}\` (${fields})`); | ||||||
|  |          } | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       // CHANGE FIELDS | ||||||
|       changes.forEach(change => { |       changes.forEach(change => { | ||||||
|          const length = change.numLength || change.charLength || change.datePrecision; |          const length = change.numLength || change.charLength || change.datePrecision; | ||||||
|  |  | ||||||
| @@ -371,11 +389,39 @@ export class MySQLClient extends AntaresCore { | |||||||
|             ${change.after ? `AFTER \`${change.after}\`` : 'FIRST'}`); |             ${change.after ? `AFTER \`${change.after}\`` : 'FIRST'}`); | ||||||
|       }); |       }); | ||||||
|  |  | ||||||
|       // DROP |       // CHANGE INDEX | ||||||
|  |       indexChanges.changes.forEach(change => { | ||||||
|  |          if (change.oldType === 'PRIMARY') | ||||||
|  |             alterColumns.push('DROP PRIMARY KEY'); | ||||||
|  |          else | ||||||
|  |             alterColumns.push(`DROP INDEX \`${change.oldName}\``); | ||||||
|  |  | ||||||
|  |          const fields = change.fields.map(field => `\`${field}\``).join(','); | ||||||
|  |          let type = change.type; | ||||||
|  |  | ||||||
|  |          if (type === 'PRIMARY') | ||||||
|  |             alterColumns.push(`ADD PRIMARY KEY (${fields})`); | ||||||
|  |          else { | ||||||
|  |             if (type === 'UNIQUE') | ||||||
|  |                type = 'UNIQUE INDEX'; | ||||||
|  |  | ||||||
|  |             alterColumns.push(`ADD ${type} \`${change.name}\` (${fields})`); | ||||||
|  |          } | ||||||
|  |       }); | ||||||
|  |  | ||||||
|  |       // DROP FIELDS | ||||||
|       deletions.forEach(deletion => { |       deletions.forEach(deletion => { | ||||||
|          alterColumns.push(`DROP COLUMN \`${deletion.name}\``); |          alterColumns.push(`DROP COLUMN \`${deletion.name}\``); | ||||||
|       }); |       }); | ||||||
|  |  | ||||||
|  |       // DROP INDEX | ||||||
|  |       indexChanges.deletions.forEach(deletion => { | ||||||
|  |          if (deletion.type === 'PRIMARY') | ||||||
|  |             alterColumns.push('DROP PRIMARY KEY'); | ||||||
|  |          else | ||||||
|  |             alterColumns.push(`DROP INDEX \`${deletion.name}\``); | ||||||
|  |       }); | ||||||
|  |  | ||||||
|       sql += alterColumns.join(', '); |       sql += alterColumns.join(', '); | ||||||
|  |  | ||||||
|       // RENAME |       // RENAME | ||||||
|   | |||||||
| @@ -86,9 +86,8 @@ export default { | |||||||
|  |  | ||||||
|     .context-container { |     .context-container { | ||||||
|       min-width: 100px; |       min-width: 100px; | ||||||
|       max-width: 150px; |  | ||||||
|       z-index: 10; |       z-index: 10; | ||||||
|       box-shadow: 0 0 1px 0 #000; |       box-shadow: 0 0 2px 0 #000; | ||||||
|       padding: 0; |       padding: 0; | ||||||
|       background: #1d1d1d; |       background: #1d1d1d; | ||||||
|       border-radius: 0.1rem; |       border-radius: 0.1rem; | ||||||
| @@ -103,9 +102,28 @@ export default { | |||||||
|         padding: 0.1rem 0.3rem; |         padding: 0.1rem 0.3rem; | ||||||
|         cursor: pointer; |         cursor: pointer; | ||||||
|         justify-content: space-between; |         justify-content: space-between; | ||||||
|  |         position: relative; | ||||||
|  |  | ||||||
|  |         .context-submenu { | ||||||
|  |           opacity: 0; | ||||||
|  |           visibility: hidden; | ||||||
|  |           transition: opacity 0.2s; | ||||||
|  |           position: absolute; | ||||||
|  |           left: 100%; | ||||||
|  |           top: 0; | ||||||
|  |           background: #1d1d1d; | ||||||
|  |           box-shadow: 0 0 2px 0 #000; | ||||||
|  |           min-width: 100px; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         &:hover { |         &:hover { | ||||||
|           background: $primary-color; |           background: $primary-color; | ||||||
|  |  | ||||||
|  |           .context-submenu { | ||||||
|  |             display: block; | ||||||
|  |             visibility: visible; | ||||||
|  |             opacity: 1; | ||||||
|  |           } | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -6,6 +6,11 @@ | |||||||
|       <div class="context-element"> |       <div class="context-element"> | ||||||
|          <span class="d-flex"><i class="mdi mdi-18px mdi-plus text-light pr-1" /> {{ $t('word.add') }}</span> |          <span class="d-flex"><i class="mdi mdi-18px mdi-plus text-light pr-1" /> {{ $t('word.add') }}</span> | ||||||
|          <i class="mdi mdi-18px mdi-chevron-right text-light pl-1" /> |          <i class="mdi mdi-18px mdi-chevron-right text-light pl-1" /> | ||||||
|  |          <div class="context-submenu"> | ||||||
|  |             <div class="context-element"> | ||||||
|  |                <span class="d-flex"><i class="mdi mdi-18px mdi-table text-light pr-1" /> {{ $t('word.table') }}</span> | ||||||
|  |             </div> | ||||||
|  |          </div> | ||||||
|       </div> |       </div> | ||||||
|       <div class="context-element" @click="showEditModal"> |       <div class="context-element" @click="showEditModal"> | ||||||
|          <span class="d-flex"><i class="mdi mdi-18px mdi-pencil text-light pr-1" /> {{ $t('word.edit') }}</span> |          <span class="d-flex"><i class="mdi mdi-18px mdi-pencil text-light pr-1" /> {{ $t('word.edit') }}</span> | ||||||
|   | |||||||
							
								
								
									
										263
									
								
								src/renderer/components/WorkspacePropsIndexesModal.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								src/renderer/components/WorkspacePropsIndexesModal.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,263 @@ | |||||||
|  | <template> | ||||||
|  |    <ConfirmModal | ||||||
|  |       :confirm-text="$t('word.confirm')" | ||||||
|  |       size="medium" | ||||||
|  |       @confirm="confirmIndexesChange" | ||||||
|  |       @hide="$emit('hide')" | ||||||
|  |    > | ||||||
|  |       <template :slot="'header'"> | ||||||
|  |          <div class="d-flex"> | ||||||
|  |             <i class="mdi mdi-24px mdi-key mdi-rotate-45 mr-1" /> {{ $t('word.indexes') }} "{{ table }}" | ||||||
|  |          </div> | ||||||
|  |       </template> | ||||||
|  |       <div :slot="'body'"> | ||||||
|  |          <div class="columns col-gapless"> | ||||||
|  |             <div class="column col-5"> | ||||||
|  |                <div class="panel" :style="{ height: modalInnerHeight + 'px'}"> | ||||||
|  |                   <div class="panel-header pt-0 pl-0"> | ||||||
|  |                      <div class="d-flex"> | ||||||
|  |                         <button class="btn btn-dark btn-sm d-flex" @click="addIndex"> | ||||||
|  |                            <span>{{ $t('word.add') }}</span> | ||||||
|  |                            <i class="mdi mdi-24px mdi-key-plus ml-1" /> | ||||||
|  |                         </button> | ||||||
|  |                         <button | ||||||
|  |                            class="btn btn-dark btn-sm d-flex ml-2 mr-0" | ||||||
|  |                            :title="$t('message.clearChanges')" | ||||||
|  |                            :disabled="!isChanged" | ||||||
|  |                            @click.prevent="clearChanges" | ||||||
|  |                         > | ||||||
|  |                            <span>{{ $t('word.clear') }}</span> | ||||||
|  |                            <i class="mdi mdi-24px mdi-delete-sweep ml-1" /> | ||||||
|  |                         </button> | ||||||
|  |                      </div> | ||||||
|  |                   </div> | ||||||
|  |                   <div ref="indexesPanel" class="panel-body p-0 pr-1"> | ||||||
|  |                      <div | ||||||
|  |                         v-for="index in indexesProxy" | ||||||
|  |                         :key="index._id" | ||||||
|  |                         class="tile tile-centered c-hand mb-1 p-1" | ||||||
|  |                         :class="{'selected-index': selectedIndexID === index._id}" | ||||||
|  |                         @click="selectIndex($event, index._id)" | ||||||
|  |                      > | ||||||
|  |                         <div class="tile-icon"> | ||||||
|  |                            <div> | ||||||
|  |                               <i class="mdi mdi-key mdi-24px column-key" :class="`key-${index.type}`" /> | ||||||
|  |                            </div> | ||||||
|  |                         </div> | ||||||
|  |                         <div class="tile-content"> | ||||||
|  |                            <div class="tile-title"> | ||||||
|  |                               {{ index.name }} | ||||||
|  |                            </div> | ||||||
|  |                            <small class="tile-subtitle text-gray">{{ index.type }} · {{ index.fields.length }} {{ $tc('word.field', index.fields.length) }}</small> | ||||||
|  |                         </div> | ||||||
|  |                         <div class="tile-action"> | ||||||
|  |                            <button | ||||||
|  |                               class="btn btn-link remove-field p-0 mr-2" | ||||||
|  |                               :title="$t('word.delete')" | ||||||
|  |                               @click.prevent="removeIndex(index._id)" | ||||||
|  |                            > | ||||||
|  |                               <i class="mdi mdi-close" /> | ||||||
|  |                            </button> | ||||||
|  |                         </div> | ||||||
|  |                      </div> | ||||||
|  |                   </div> | ||||||
|  |                </div> | ||||||
|  |             </div> | ||||||
|  |  | ||||||
|  |             <div class="column col-7 pl-2 editor-col"> | ||||||
|  |                <form v-if="selectedIndexObj" :style="{ height: modalInnerHeight + 'px'}"> | ||||||
|  |                   <div class="form-group"> | ||||||
|  |                      <label class="form-label"> | ||||||
|  |                         {{ $t('word.name') }} | ||||||
|  |                      </label> | ||||||
|  |                      <input | ||||||
|  |                         v-model="selectedIndexObj.name" | ||||||
|  |                         class="form-input" | ||||||
|  |                         type="text" | ||||||
|  |                      > | ||||||
|  |                   </div> | ||||||
|  |                   <div class="form-group"> | ||||||
|  |                      <label class="form-label"> | ||||||
|  |                         {{ $t('word.type') }} | ||||||
|  |                      </label> | ||||||
|  |                      <select v-model="selectedIndexObj.type" class="form-select"> | ||||||
|  |                         <option | ||||||
|  |                            v-for="index in indexTypes" | ||||||
|  |                            :key="index" | ||||||
|  |                            :value="index" | ||||||
|  |                            :disabled="index === 'PRIMARY' && hasPrimary" | ||||||
|  |                         > | ||||||
|  |                            {{ index }} | ||||||
|  |                         </option> | ||||||
|  |                      </select> | ||||||
|  |                   </div> | ||||||
|  |                   <div class="form-group"> | ||||||
|  |                      <label class="form-label"> | ||||||
|  |                         {{ $tc('word.field', fields.length) }} | ||||||
|  |                      </label> | ||||||
|  |                      <div class="fields-list"> | ||||||
|  |                         <label | ||||||
|  |                            v-for="(field, i) in fields" | ||||||
|  |                            :key="`${field.name}-${i}`" | ||||||
|  |                            class="form-checkbox m-0" | ||||||
|  |                            @click.prevent="toggleField(field.name)" | ||||||
|  |                         > | ||||||
|  |                            <input type="checkbox" :checked="selectedIndexObj.fields.some(f => f === field.name)"> | ||||||
|  |                            <i class="form-icon" /> {{ field.name }} | ||||||
|  |                         </label> | ||||||
|  |                      </div> | ||||||
|  |                   </div> | ||||||
|  |                </form> | ||||||
|  |             </div> | ||||||
|  |          </div> | ||||||
|  |       </div> | ||||||
|  |    </ConfirmModal> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script> | ||||||
|  | import { uidGen } from 'common/libs/uidGen'; | ||||||
|  | import ConfirmModal from '@/components/BaseConfirmModal'; | ||||||
|  |  | ||||||
|  | export default { | ||||||
|  |    name: 'WorkspacePropsIndexesModal', | ||||||
|  |    components: { | ||||||
|  |       ConfirmModal | ||||||
|  |    }, | ||||||
|  |    props: { | ||||||
|  |       localIndexes: Array, | ||||||
|  |       table: String, | ||||||
|  |       fields: Array, | ||||||
|  |       workspace: Object, | ||||||
|  |       indexTypes: Array | ||||||
|  |    }, | ||||||
|  |    data () { | ||||||
|  |       return { | ||||||
|  |          indexesProxy: [], | ||||||
|  |          isOptionsChanging: false, | ||||||
|  |          selectedIndexID: '', | ||||||
|  |          modalInnerHeight: 400 | ||||||
|  |       }; | ||||||
|  |    }, | ||||||
|  |    computed: { | ||||||
|  |       selectedIndexObj () { | ||||||
|  |          return this.indexesProxy.find(index => index._id === this.selectedIndexID); | ||||||
|  |       }, | ||||||
|  |       isChanged () { | ||||||
|  |          return JSON.stringify(this.localIndexes) !== JSON.stringify(this.indexesProxy); | ||||||
|  |       }, | ||||||
|  |       hasPrimary () { | ||||||
|  |          return this.indexesProxy.some(index => index.type === 'PRIMARY'); | ||||||
|  |       } | ||||||
|  |    }, | ||||||
|  |    mounted () { | ||||||
|  |       this.indexesProxy = JSON.parse(JSON.stringify(this.localIndexes)); | ||||||
|  |  | ||||||
|  |       if (this.indexesProxy.length) | ||||||
|  |          this.resetSelectedID(); | ||||||
|  |  | ||||||
|  |       this.getModalInnerHeight(); | ||||||
|  |       window.addEventListener('resize', this.getModalInnerHeight); | ||||||
|  |    }, | ||||||
|  |    destroyed () { | ||||||
|  |       window.removeEventListener('resize', this.getModalInnerHeight); | ||||||
|  |    }, | ||||||
|  |    methods: { | ||||||
|  |       confirmIndexesChange () { | ||||||
|  |          this.$emit('indexes-update', this.indexesProxy); | ||||||
|  |       }, | ||||||
|  |       selectIndex (event, id) { | ||||||
|  |          if (this.selectedIndexID !== id && !event.target.classList.contains('remove-field')) | ||||||
|  |             this.selectedIndexID = id; | ||||||
|  |       }, | ||||||
|  |       getModalInnerHeight () { | ||||||
|  |          const modalBody = document.querySelector('.modal-body'); | ||||||
|  |          if (modalBody) | ||||||
|  |             this.modalInnerHeight = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom)); | ||||||
|  |       }, | ||||||
|  |       addIndex () { | ||||||
|  |          this.indexesProxy = [...this.indexesProxy, { | ||||||
|  |             _id: uidGen(), | ||||||
|  |             name: 'NEW_INDEX', | ||||||
|  |             fields: [], | ||||||
|  |             type: 'INDEX', | ||||||
|  |             comment: '', | ||||||
|  |             indexType: 'BTREE', | ||||||
|  |             indexComment: '', | ||||||
|  |             cardinality: 0 | ||||||
|  |          }]; | ||||||
|  |  | ||||||
|  |          if (this.indexesProxy.length === 1) | ||||||
|  |             this.resetSelectedID(); | ||||||
|  |  | ||||||
|  |          setTimeout(() => { | ||||||
|  |             this.$refs.indexesPanel.scrollTop = this.$refs.indexesPanel.scrollHeight + 60; | ||||||
|  |          }, 20); | ||||||
|  |       }, | ||||||
|  |       removeIndex (id) { | ||||||
|  |          this.indexesProxy = this.indexesProxy.filter(index => index._id !== id); | ||||||
|  |  | ||||||
|  |          if (this.selectedIndexID === id && this.indexesProxy.length) | ||||||
|  |             this.resetSelectedID(); | ||||||
|  |       }, | ||||||
|  |       clearChanges () { | ||||||
|  |          this.indexesProxy = JSON.parse(JSON.stringify(this.localIndexes)); | ||||||
|  |          if (!this.indexesProxy.some(index => index._id === this.selectedIndexID)) | ||||||
|  |             this.resetSelectedID(); | ||||||
|  |       }, | ||||||
|  |       toggleField (field) { | ||||||
|  |          this.indexesProxy = this.indexesProxy.map(index => { | ||||||
|  |             if (index._id === this.selectedIndexID) { | ||||||
|  |                if (index.fields.includes(field)) | ||||||
|  |                   index.fields = index.fields.filter(f => f !== field); | ||||||
|  |                else | ||||||
|  |                   index.fields.push(field); | ||||||
|  |             } | ||||||
|  |             return index; | ||||||
|  |          }); | ||||||
|  |       }, | ||||||
|  |       resetSelectedID () { | ||||||
|  |          this.selectedIndexID = this.indexesProxy[0]._id; | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | }; | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .tile { | ||||||
|  |   border-radius: 2px; | ||||||
|  |   opacity: 0.5; | ||||||
|  |   transition: background 0.2s; | ||||||
|  |   transition: opacity 0.2s; | ||||||
|  |  | ||||||
|  |   .tile-action { | ||||||
|  |     opacity: 0; | ||||||
|  |     transition: opacity 0.2s; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &:hover { | ||||||
|  |     background: $bg-color-light; | ||||||
|  |  | ||||||
|  |     .tile-action { | ||||||
|  |       opacity: 1; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &.selected-index { | ||||||
|  |     background: $bg-color-light; | ||||||
|  |     opacity: 1; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .editor-col { | ||||||
|  |   border-left: 2px solid $bg-color-light; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .fields-list { | ||||||
|  |   max-height: 200px; | ||||||
|  |   overflow: auto; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .remove-field .mdi { | ||||||
|  |   pointer-events: none; | ||||||
|  | } | ||||||
|  | </style> | ||||||
| @@ -96,7 +96,6 @@ export default { | |||||||
|    }, |    }, | ||||||
|    props: { |    props: { | ||||||
|       localOptions: Object, |       localOptions: Object, | ||||||
|       tableOptions: Object, |  | ||||||
|       table: String, |       table: String, | ||||||
|       workspace: Object |       workspace: Object | ||||||
|    }, |    }, | ||||||
|   | |||||||
| @@ -32,7 +32,11 @@ | |||||||
|                   <span>{{ $t('word.add') }}</span> |                   <span>{{ $t('word.add') }}</span> | ||||||
|                   <i class="mdi mdi-24px mdi-playlist-plus ml-1" /> |                   <i class="mdi mdi-24px mdi-playlist-plus ml-1" /> | ||||||
|                </button> |                </button> | ||||||
|                <button class="btn btn-dark btn-sm" :title="$t('message.manageIndexes')"> |                <button | ||||||
|  |                   class="btn btn-dark btn-sm" | ||||||
|  |                   :title="$t('message.manageIndexes')" | ||||||
|  |                   @click="showIntdexesModal" | ||||||
|  |                > | ||||||
|                   <span>{{ $t('word.indexes') }}</span> |                   <span>{{ $t('word.indexes') }}</span> | ||||||
|                   <i class="mdi mdi-24px mdi-key mdi-rotate-45 ml-1" /> |                   <i class="mdi mdi-24px mdi-key mdi-rotate-45 ml-1" /> | ||||||
|                </button> |                </button> | ||||||
| @@ -50,26 +54,38 @@ | |||||||
|       <div class="workspace-query-results column col-12"> |       <div class="workspace-query-results column col-12"> | ||||||
|          <WorkspacePropsTable |          <WorkspacePropsTable | ||||||
|             v-if="localFields" |             v-if="localFields" | ||||||
|             ref="queryTable" |             ref="indexTable" | ||||||
|             :fields="localFields" |             :fields="localFields" | ||||||
|             :indexes="localIndexes" |             :indexes="localIndexes" | ||||||
|             :tab-uid="tabUid" |             :tab-uid="tabUid" | ||||||
|             :conn-uid="connection.uid" |             :conn-uid="connection.uid" | ||||||
|  |             :index-types="workspace.indexTypes" | ||||||
|             :table="table" |             :table="table" | ||||||
|             :schema="schema" |             :schema="schema" | ||||||
|             mode="table" |             mode="table" | ||||||
|             @remove-field="removeField" |             @remove-field="removeField" | ||||||
|  |             @add-new-index="addNewIndex" | ||||||
|  |             @add-to-index="addToIndex" | ||||||
|          /> |          /> | ||||||
|       </div> |       </div> | ||||||
|       <WorkspacePropsOptionsModal |       <WorkspacePropsOptionsModal | ||||||
|          v-if="isOptionsModal" |          v-if="isOptionsModal" | ||||||
|          :local-options="localOptions" |          :local-options="localOptions" | ||||||
|          :table-options="tableOptions" |  | ||||||
|          :table="table" |          :table="table" | ||||||
|          :workspace="workspace" |          :workspace="workspace" | ||||||
|          @hide="hideOptionsModal" |          @hide="hideOptionsModal" | ||||||
|          @options-update="optionsUpdate" |          @options-update="optionsUpdate" | ||||||
|       /> |       /> | ||||||
|  |       <WorkspacePropsIndexesModal | ||||||
|  |          v-if="isIndexesModal" | ||||||
|  |          :local-indexes="localIndexes" | ||||||
|  |          :table="table" | ||||||
|  |          :fields="localFields" | ||||||
|  |          :index-types="workspace.indexTypes" | ||||||
|  |          :workspace="workspace" | ||||||
|  |          @hide="hideIndexesModal" | ||||||
|  |          @indexes-update="indexesUpdate" | ||||||
|  |       /> | ||||||
|    </div> |    </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| @@ -79,12 +95,14 @@ import { uidGen } from 'common/libs/uidGen'; | |||||||
| import Tables from '@/ipc-api/Tables'; | import Tables from '@/ipc-api/Tables'; | ||||||
| import WorkspacePropsTable from '@/components/WorkspacePropsTable'; | import WorkspacePropsTable from '@/components/WorkspacePropsTable'; | ||||||
| import WorkspacePropsOptionsModal from '@/components/WorkspacePropsOptionsModal'; | import WorkspacePropsOptionsModal from '@/components/WorkspacePropsOptionsModal'; | ||||||
|  | import WorkspacePropsIndexesModal from '@/components/WorkspacePropsIndexesModal'; | ||||||
|  |  | ||||||
| export default { | export default { | ||||||
|    name: 'WorkspacePropsTab', |    name: 'WorkspacePropsTab', | ||||||
|    components: { |    components: { | ||||||
|       WorkspacePropsTable, |       WorkspacePropsTable, | ||||||
|       WorkspacePropsOptionsModal |       WorkspacePropsOptionsModal, | ||||||
|  |       WorkspacePropsIndexesModal | ||||||
|    }, |    }, | ||||||
|    props: { |    props: { | ||||||
|       connection: Object, |       connection: Object, | ||||||
| @@ -95,8 +113,8 @@ export default { | |||||||
|          tabUid: 'prop', |          tabUid: 'prop', | ||||||
|          isQuering: false, |          isQuering: false, | ||||||
|          isSaving: false, |          isSaving: false, | ||||||
|          isAddModal: false, |  | ||||||
|          isOptionsModal: false, |          isOptionsModal: false, | ||||||
|  |          isIndexesModal: false, | ||||||
|          isOptionsChanging: false, |          isOptionsChanging: false, | ||||||
|          originalFields: [], |          originalFields: [], | ||||||
|          localFields: [], |          localFields: [], | ||||||
| @@ -105,7 +123,8 @@ export default { | |||||||
|          originalIndexes: [], |          originalIndexes: [], | ||||||
|          localIndexes: [], |          localIndexes: [], | ||||||
|          localOptions: {}, |          localOptions: {}, | ||||||
|          lastTable: null |          lastTable: null, | ||||||
|  |          newFieldsCounter: 0 | ||||||
|       }; |       }; | ||||||
|    }, |    }, | ||||||
|    computed: { |    computed: { | ||||||
| @@ -132,6 +151,7 @@ export default { | |||||||
|       isChanged () { |       isChanged () { | ||||||
|          return JSON.stringify(this.originalFields) !== JSON.stringify(this.localFields) || |          return JSON.stringify(this.originalFields) !== JSON.stringify(this.localFields) || | ||||||
|             JSON.stringify(this.originalKeyUsage) !== JSON.stringify(this.localKeyUsage) || |             JSON.stringify(this.originalKeyUsage) !== JSON.stringify(this.localKeyUsage) || | ||||||
|  |             JSON.stringify(this.originalIndexes) !== JSON.stringify(this.localIndexes) || | ||||||
|             JSON.stringify(this.tableOptions) !== JSON.stringify(this.localOptions); |             JSON.stringify(this.tableOptions) !== JSON.stringify(this.localOptions); | ||||||
|       } |       } | ||||||
|    }, |    }, | ||||||
| @@ -156,6 +176,7 @@ export default { | |||||||
|       }), |       }), | ||||||
|       async getFieldsData () { |       async getFieldsData () { | ||||||
|          if (!this.table) return; |          if (!this.table) return; | ||||||
|  |          this.newFieldsCounter = 0; | ||||||
|          this.isQuering = true; |          this.isQuering = true; | ||||||
|          this.localOptions = JSON.parse(JSON.stringify(this.tableOptions)); |          this.localOptions = JSON.parse(JSON.stringify(this.tableOptions)); | ||||||
|  |  | ||||||
| @@ -184,8 +205,26 @@ export default { | |||||||
|             const { status, response } = await Tables.getTableIndexes(params); |             const { status, response } = await Tables.getTableIndexes(params); | ||||||
|  |  | ||||||
|             if (status === 'success') { |             if (status === 'success') { | ||||||
|                this.originalIndexes = response; |                const indexesObj = response.reduce((acc, curr) => { | ||||||
|                this.localIndexes = JSON.parse(JSON.stringify(response)); |                   acc[curr.name] = acc[curr.name] || []; | ||||||
|  |                   acc[curr.name].push(curr); | ||||||
|  |                   return acc; | ||||||
|  |                }, {}); | ||||||
|  |  | ||||||
|  |                this.originalIndexes = Object.keys(indexesObj).map(index => { | ||||||
|  |                   return { | ||||||
|  |                      _id: uidGen(), | ||||||
|  |                      name: index, | ||||||
|  |                      fields: indexesObj[index].map(field => field.column), | ||||||
|  |                      type: indexesObj[index][0].type, | ||||||
|  |                      comment: indexesObj[index][0].comment, | ||||||
|  |                      indexType: indexesObj[index][0].indexType, | ||||||
|  |                      indexComment: indexesObj[index][0].indexComment, | ||||||
|  |                      cardinality: indexesObj[index][0].cardinality | ||||||
|  |                   }; | ||||||
|  |                }); | ||||||
|  |  | ||||||
|  |                this.localIndexes = JSON.parse(JSON.stringify(this.originalIndexes)); | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
|                this.addNotification({ status: 'error', message: response }); |                this.addNotification({ status: 'error', message: response }); | ||||||
| @@ -214,20 +253,21 @@ export default { | |||||||
|          if (this.isSaving) return; |          if (this.isSaving) return; | ||||||
|          this.isSaving = true; |          this.isSaving = true; | ||||||
|  |  | ||||||
|  |          // FIELDS | ||||||
|          const originalIDs = this.originalFields.reduce((acc, curr) => [...acc, curr._id], []); |          const originalIDs = this.originalFields.reduce((acc, curr) => [...acc, curr._id], []); | ||||||
|          const localIDs = this.localFields.reduce((acc, curr) => [...acc, curr._id], []); |          const localIDs = this.localFields.reduce((acc, curr) => [...acc, curr._id], []); | ||||||
|  |  | ||||||
|          // Additions |          // Fields Additions | ||||||
|          const additions = this.localFields.filter((field, i) => !originalIDs.includes(field._id)).map(field => { |          const additions = this.localFields.filter((field, i) => !originalIDs.includes(field._id)).map(field => { | ||||||
|             const lI = this.localFields.findIndex(localField => localField._id === field._id); |             const lI = this.localFields.findIndex(localField => localField._id === field._id); | ||||||
|             const after = lI > 0 ? this.localFields[lI - 1].name : false; |             const after = lI > 0 ? this.localFields[lI - 1].name : false; | ||||||
|             return { ...field, after }; |             return { ...field, after }; | ||||||
|          }); |          }); | ||||||
|  |  | ||||||
|          // Deletions |          // Fields Deletions | ||||||
|          const deletions = this.originalFields.filter(field => !localIDs.includes(field._id)); |          const deletions = this.originalFields.filter(field => !localIDs.includes(field._id)); | ||||||
|  |  | ||||||
|          // Changes |          // Fields Changes | ||||||
|          const changes = []; |          const changes = []; | ||||||
|          this.originalFields.forEach((originalField, oI) => { |          this.originalFields.forEach((originalField, oI) => { | ||||||
|             const lI = this.localFields.findIndex(localField => localField._id === originalField._id); |             const lI = this.localFields.findIndex(localField => localField._id === originalField._id); | ||||||
| @@ -247,6 +287,33 @@ export default { | |||||||
|             return acc; |             return acc; | ||||||
|          }, {}); |          }, {}); | ||||||
|  |  | ||||||
|  |          // INDEXES | ||||||
|  |          const indexChanges = { | ||||||
|  |             additions: [], | ||||||
|  |             changes: [], | ||||||
|  |             deletions: [] | ||||||
|  |          }; | ||||||
|  |          const originalIndexIDs = this.originalIndexes.reduce((acc, curr) => [...acc, curr._id], []); | ||||||
|  |          const localIndexIDs = this.localIndexes.reduce((acc, curr) => [...acc, curr._id], []); | ||||||
|  |  | ||||||
|  |          // Index Additions | ||||||
|  |          indexChanges.additions = this.localIndexes.filter(index => !originalIndexIDs.includes(index._id)); | ||||||
|  |  | ||||||
|  |          // Index Changes | ||||||
|  |          this.originalIndexes.forEach(originalIndex => { | ||||||
|  |             const lI = this.localIndexes.findIndex(localIndex => localIndex._id === originalIndex._id); | ||||||
|  |             if (JSON.stringify(originalIndex) !== JSON.stringify(this.localIndexes[lI])) { | ||||||
|  |                indexChanges.changes.push({ | ||||||
|  |                   ...this.localIndexes[lI], | ||||||
|  |                   oldName: originalIndex.name, | ||||||
|  |                   oldType: originalIndex.type | ||||||
|  |                }); | ||||||
|  |             } | ||||||
|  |          }); | ||||||
|  |  | ||||||
|  |          // Index Deletions | ||||||
|  |          indexChanges.deletions = this.originalIndexes.filter(index => !localIndexIDs.includes(index._id)); | ||||||
|  |  | ||||||
|          const params = { |          const params = { | ||||||
|             uid: this.connection.uid, |             uid: this.connection.uid, | ||||||
|             schema: this.schema, |             schema: this.schema, | ||||||
| @@ -254,6 +321,7 @@ export default { | |||||||
|             additions, |             additions, | ||||||
|             changes, |             changes, | ||||||
|             deletions, |             deletions, | ||||||
|  |             indexChanges, | ||||||
|             options |             options | ||||||
|          }; |          }; | ||||||
|  |  | ||||||
| @@ -272,16 +340,19 @@ export default { | |||||||
|          } |          } | ||||||
|  |  | ||||||
|          this.isSaving = false; |          this.isSaving = false; | ||||||
|  |          this.newFieldsCounter = 0; | ||||||
|       }, |       }, | ||||||
|       clearChanges () { |       clearChanges () { | ||||||
|          this.localFields = JSON.parse(JSON.stringify(this.originalFields)); |          this.localFields = JSON.parse(JSON.stringify(this.originalFields)); | ||||||
|  |          this.localIndexes = JSON.parse(JSON.stringify(this.originalIndexes)); | ||||||
|          this.localKeyUsage = JSON.parse(JSON.stringify(this.originalKeyUsage)); |          this.localKeyUsage = JSON.parse(JSON.stringify(this.originalKeyUsage)); | ||||||
|          this.localOptions = JSON.parse(JSON.stringify(this.tableOptions)); |          this.localOptions = JSON.parse(JSON.stringify(this.tableOptions)); | ||||||
|  |          this.newFieldsCounter = 0; | ||||||
|       }, |       }, | ||||||
|       addField () { |       addField () { | ||||||
|          this.localFields.push({ |          this.localFields.push({ | ||||||
|             _id: uidGen(), |             _id: uidGen(), | ||||||
|             name: '', |             name: `${this.$tc('word.field', 1)}_${++this.newFieldsCounter}`, | ||||||
|             key: '', |             key: '', | ||||||
|             type: 'int', |             type: 'int', | ||||||
|             schema: this.schema, |             schema: this.schema, | ||||||
| @@ -301,10 +372,33 @@ export default { | |||||||
|             onUpdate: '', |             onUpdate: '', | ||||||
|             comment: '' |             comment: '' | ||||||
|          }); |          }); | ||||||
|  |  | ||||||
|  |          setTimeout(() => { | ||||||
|  |             const scrollable = this.$refs.indexTable.$refs.tableWrapper; | ||||||
|  |             scrollable.scrollTop = scrollable.scrollHeight + 30; | ||||||
|  |          }, 20); | ||||||
|       }, |       }, | ||||||
|       removeField (uid) { |       removeField (uid) { | ||||||
|          this.localFields = this.localFields.filter(field => field._id !== uid); |          this.localFields = this.localFields.filter(field => field._id !== uid); | ||||||
|       }, |       }, | ||||||
|  |       addNewIndex (payload) { | ||||||
|  |          this.localIndexes = [...this.localIndexes, { | ||||||
|  |             _id: uidGen(), | ||||||
|  |             name: payload.index === 'PRIMARY' ? 'PRIMARY' : payload.field, | ||||||
|  |             fields: [payload.field], | ||||||
|  |             type: payload.index, | ||||||
|  |             comment: '', | ||||||
|  |             indexType: 'BTREE', | ||||||
|  |             indexComment: '', | ||||||
|  |             cardinality: 0 | ||||||
|  |          }]; | ||||||
|  |       }, | ||||||
|  |       addToIndex (payload) { | ||||||
|  |          this.localIndexes = this.localIndexes.map(index => { | ||||||
|  |             if (index._id === payload.index) index.fields.push(payload.field); | ||||||
|  |             return index; | ||||||
|  |          }); | ||||||
|  |       }, | ||||||
|       showOptionsModal () { |       showOptionsModal () { | ||||||
|          this.isOptionsModal = true; |          this.isOptionsModal = true; | ||||||
|       }, |       }, | ||||||
| @@ -313,6 +407,15 @@ export default { | |||||||
|       }, |       }, | ||||||
|       optionsUpdate (options) { |       optionsUpdate (options) { | ||||||
|          this.localOptions = options; |          this.localOptions = options; | ||||||
|  |       }, | ||||||
|  |       showIntdexesModal () { | ||||||
|  |          this.isIndexesModal = true; | ||||||
|  |       }, | ||||||
|  |       hideIndexesModal () { | ||||||
|  |          this.isIndexesModal = false; | ||||||
|  |       }, | ||||||
|  |       indexesUpdate (indexes) { | ||||||
|  |          this.localIndexes = indexes; | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -8,8 +8,12 @@ | |||||||
|          v-if="isContext" |          v-if="isContext" | ||||||
|          :context-event="contextEvent" |          :context-event="contextEvent" | ||||||
|          :selected-field="selectedField" |          :selected-field="selectedField" | ||||||
|  |          :index-types="indexTypes" | ||||||
|  |          :indexes="indexes" | ||||||
|          @delete-selected="removeField" |          @delete-selected="removeField" | ||||||
|          @close-context="isContext = false" |          @close-context="isContext = false" | ||||||
|  |          @add-new-index="$emit('add-new-index', $event)" | ||||||
|  |          @add-to-index="$emit('add-to-index', $event)" | ||||||
|       /> |       /> | ||||||
|       <div ref="propTable" class="table table-hover"> |       <div ref="propTable" class="table table-hover"> | ||||||
|          <div class="thead"> |          <div class="thead"> | ||||||
| @@ -124,6 +128,7 @@ export default { | |||||||
|    props: { |    props: { | ||||||
|       fields: Array, |       fields: Array, | ||||||
|       indexes: Array, |       indexes: Array, | ||||||
|  |       indexTypes: Array, | ||||||
|       tabUid: [String, Number], |       tabUid: [String, Number], | ||||||
|       connUid: String, |       connUid: String, | ||||||
|       table: String, |       table: String, | ||||||
| @@ -133,7 +138,6 @@ export default { | |||||||
|    data () { |    data () { | ||||||
|       return { |       return { | ||||||
|          resultsSize: 1000, |          resultsSize: 1000, | ||||||
|          localResults: [], |  | ||||||
|          isContext: false, |          isContext: false, | ||||||
|          contextEvent: null, |          contextEvent: null, | ||||||
|          selectedField: null, |          selectedField: null, | ||||||
| @@ -156,6 +160,14 @@ export default { | |||||||
|       }, |       }, | ||||||
|       tabProperties () { |       tabProperties () { | ||||||
|          return this.getWorkspaceTab(this.tabUid); |          return this.getWorkspaceTab(this.tabUid); | ||||||
|  |       }, | ||||||
|  |       fieldsLength () { | ||||||
|  |          return this.fields.length; | ||||||
|  |       } | ||||||
|  |    }, | ||||||
|  |    watch: { | ||||||
|  |       fieldsLength () { | ||||||
|  |          this.refreshScroller(); | ||||||
|       } |       } | ||||||
|    }, |    }, | ||||||
|    updated () { |    updated () { | ||||||
| @@ -184,22 +196,24 @@ export default { | |||||||
|                const size = window.innerHeight - el.getBoundingClientRect().top - footer.offsetHeight; |                const size = window.innerHeight - el.getBoundingClientRect().top - footer.offsetHeight; | ||||||
|                this.resultsSize = size; |                this.resultsSize = size; | ||||||
|             } |             } | ||||||
|             // this.$refs.resultTable.updateWindow(); |  | ||||||
|          } |          } | ||||||
|       }, |       }, | ||||||
|       refreshScroller () { |       refreshScroller () { | ||||||
|          this.resizeResults(); |          this.resizeResults(); | ||||||
|       }, |       }, | ||||||
|       contextMenu (event, uid) { |       contextMenu (event, uid) { | ||||||
|          this.selectedField = uid; |          this.selectedField = this.fields.find(field => field._id === uid); | ||||||
|          this.contextEvent = event; |          this.contextEvent = event; | ||||||
|          this.isContext = true; |          this.isContext = true; | ||||||
|       }, |       }, | ||||||
|       removeField () { |       removeField () { | ||||||
|          this.$emit('remove-field', this.selectedField); |          this.$emit('remove-field', this.selectedField._id); | ||||||
|       }, |       }, | ||||||
|       getIndexes (field) { |       getIndexes (field) { | ||||||
|          return this.indexes.filter(index => index.column === field); |          return this.indexes.reduce((acc, curr) => { | ||||||
|  |             acc.push(...curr.fields.map(f => ({ name: f, type: curr.type }))); | ||||||
|  |             return acc; | ||||||
|  |          }, []).filter(f => f.name === field); | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
| }; | }; | ||||||
| @@ -213,4 +227,8 @@ export default { | |||||||
|     overflow: hidden; |     overflow: hidden; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | .vscroll { | ||||||
|  |   overflow: auto; | ||||||
|  | } | ||||||
| </style> | </style> | ||||||
|   | |||||||
| @@ -3,6 +3,36 @@ | |||||||
|       :context-event="contextEvent" |       :context-event="contextEvent" | ||||||
|       @close-context="closeContext" |       @close-context="closeContext" | ||||||
|    > |    > | ||||||
|  |       <div class="context-element"> | ||||||
|  |          <span class="d-flex"><i class="mdi mdi-18px mdi-key-plus text-light pr-1" /> {{ $t('message.createNewIndex') }}</span> | ||||||
|  |          <i class="mdi mdi-18px mdi-chevron-right text-light pl-1" /> | ||||||
|  |          <div class="context-submenu"> | ||||||
|  |             <div | ||||||
|  |                v-for="index in indexTypes" | ||||||
|  |                :key="index" | ||||||
|  |                class="context-element" | ||||||
|  |                :class="{'disabled': index === 'PRIMARY' && hasPrimary}" | ||||||
|  |                @click="addNewIndex(index)" | ||||||
|  |             > | ||||||
|  |                <span class="d-flex"><i class="mdi mdi-18px mdi-key column-key pr-1" :class="`key-${index}`" /> {{ index }}</span> | ||||||
|  |             </div> | ||||||
|  |          </div> | ||||||
|  |       </div> | ||||||
|  |       <div class="context-element"> | ||||||
|  |          <span class="d-flex"><i class="mdi mdi-18px mdi-key-arrow-right text-light pr-1" /> {{ $t('message.addToIndex') }}</span> | ||||||
|  |          <i class="mdi mdi-18px mdi-chevron-right text-light pl-1" /> | ||||||
|  |          <div class="context-submenu"> | ||||||
|  |             <div | ||||||
|  |                v-for="index in indexes" | ||||||
|  |                :key="index.name" | ||||||
|  |                class="context-element" | ||||||
|  |                :class="{'disabled': index.fields.includes(selectedField.name)}" | ||||||
|  |                @click="addToIndex(index._id)" | ||||||
|  |             > | ||||||
|  |                <span class="d-flex"><i class="mdi mdi-18px mdi-key column-key pr-1" :class="`key-${index.type}`" /> {{ index.name }}</span> | ||||||
|  |             </div> | ||||||
|  |          </div> | ||||||
|  |       </div> | ||||||
|       <div class="context-element" @click="deleteField"> |       <div class="context-element" @click="deleteField"> | ||||||
|          <span class="d-flex"><i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $t('message.deleteField') }}</span> |          <span class="d-flex"><i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $t('message.deleteField') }}</span> | ||||||
|       </div> |       </div> | ||||||
| @@ -19,14 +49,14 @@ export default { | |||||||
|    }, |    }, | ||||||
|    props: { |    props: { | ||||||
|       contextEvent: MouseEvent, |       contextEvent: MouseEvent, | ||||||
|       selectedField: String |       indexes: Array, | ||||||
|    }, |       indexTypes: Array, | ||||||
|    data () { |       selectedField: Object | ||||||
|       return { |  | ||||||
|          isConfirmModal: false |  | ||||||
|       }; |  | ||||||
|    }, |    }, | ||||||
|    computed: { |    computed: { | ||||||
|  |       hasPrimary () { | ||||||
|  |          return this.indexes.some(index => index.type === 'PRIMARY'); | ||||||
|  |       } | ||||||
|    }, |    }, | ||||||
|    methods: { |    methods: { | ||||||
|       closeContext () { |       closeContext () { | ||||||
| @@ -35,7 +65,23 @@ export default { | |||||||
|       deleteField () { |       deleteField () { | ||||||
|          this.$emit('delete-selected'); |          this.$emit('delete-selected'); | ||||||
|          this.closeContext(); |          this.closeContext(); | ||||||
|  |       }, | ||||||
|  |       addNewIndex (index) { | ||||||
|  |          this.$emit('add-new-index', { field: this.selectedField.name, index }); | ||||||
|  |          this.closeContext(); | ||||||
|  |       }, | ||||||
|  |       addToIndex (index) { | ||||||
|  |          this.$emit('add-to-index', { field: this.selectedField.name, index }); | ||||||
|  |          this.closeContext(); | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
| }; | }; | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .disabled { | ||||||
|  |   pointer-events: none; | ||||||
|  |   filter: grayscale(100%); | ||||||
|  |   opacity: 0.5; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|   | |||||||
| @@ -9,8 +9,8 @@ | |||||||
|       <div class="td" tabindex="0"> |       <div class="td" tabindex="0"> | ||||||
|          <div class="text-center"> |          <div class="text-center"> | ||||||
|             <i |             <i | ||||||
|                v-for="index in indexes" |                v-for="(index, i) in indexes" | ||||||
|                :key="index.name" |                :key="`${index.name}-${i}`" | ||||||
|                :title="index.type" |                :title="index.type" | ||||||
|                class="d-inline-block mdi mdi-key column-key c-help" |                class="d-inline-block mdi mdi-key column-key c-help" | ||||||
|                :class="`key-${index.type}`" |                :class="`key-${index.type}`" | ||||||
|   | |||||||
| @@ -44,7 +44,7 @@ | |||||||
|                <div v-if="results.length && results[0].rows"> |                <div v-if="results.length && results[0].rows"> | ||||||
|                   {{ $t('word.results') }}: <b>{{ results[0].rows.length.toLocaleString() }}</b> |                   {{ $t('word.results') }}: <b>{{ results[0].rows.length.toLocaleString() }}</b> | ||||||
|                </div> |                </div> | ||||||
|                <div v-if="results.length && results[0].rows && results[0].rows.length < tableInfo.rows"> |                <div v-if="results.length && results[0].rows && tableInfo && results[0].rows.length < tableInfo.rows"> | ||||||
|                   {{ $t('word.total') }}: <b>{{ tableInfo.rows.toLocaleString() }}</b> <small>({{ $t('word.approximately') }})</small> |                   {{ $t('word.total') }}: <b>{{ tableInfo.rows.toLocaleString() }}</b> <small>({{ $t('word.approximately') }})</small> | ||||||
|                </div> |                </div> | ||||||
|                <div v-if="workspace.breadcrumbs.database"> |                <div v-if="workspace.breadcrumbs.database"> | ||||||
|   | |||||||
| @@ -58,7 +58,8 @@ module.exports = { | |||||||
|       engine: 'Engine', |       engine: 'Engine', | ||||||
|       field: 'Field | Fields', |       field: 'Field | Fields', | ||||||
|       approximately: 'Approximately', |       approximately: 'Approximately', | ||||||
|       total: 'Total' |       total: 'Total', | ||||||
|  |       table: 'Table' | ||||||
|    }, |    }, | ||||||
|    message: { |    message: { | ||||||
|       appWelcome: 'Welcome to Antares SQL Client!', |       appWelcome: 'Welcome to Antares SQL Client!', | ||||||
|   | |||||||
| @@ -41,12 +41,13 @@ export default { | |||||||
|       SELECT_WORKSPACE (state, uid) { |       SELECT_WORKSPACE (state, uid) { | ||||||
|          state.selected_workspace = uid; |          state.selected_workspace = uid; | ||||||
|       }, |       }, | ||||||
|       ADD_CONNECTED (state, { uid, client, dataTypes, structure }) { |       ADD_CONNECTED (state, { uid, client, dataTypes, indexTypes, structure }) { | ||||||
|          state.workspaces = state.workspaces.map(workspace => workspace.uid === uid |          state.workspaces = state.workspaces.map(workspace => workspace.uid === uid | ||||||
|             ? { |             ? { | ||||||
|                ...workspace, |                ...workspace, | ||||||
|                client, |                client, | ||||||
|                dataTypes, |                dataTypes, | ||||||
|  |                indexTypes, | ||||||
|                structure, |                structure, | ||||||
|                connected: true |                connected: true | ||||||
|             } |             } | ||||||
| @@ -187,17 +188,20 @@ export default { | |||||||
|                dispatch('notifications/addNotification', { status, message: response }, { root: true }); |                dispatch('notifications/addNotification', { status, message: response }, { root: true }); | ||||||
|             else { |             else { | ||||||
|                let dataTypes = []; |                let dataTypes = []; | ||||||
|  |                let indexTypes = []; | ||||||
|  |  | ||||||
|                switch (connection.client) { |                switch (connection.client) { | ||||||
|                   case 'mysql': |                   case 'mysql': | ||||||
|                   case 'maria': |                   case 'maria': | ||||||
|                      dataTypes = require('common/data-types/mysql'); |                      dataTypes = require('common/data-types/mysql'); | ||||||
|  |                      indexTypes = require('common/index-types/mysql'); | ||||||
|                      break; |                      break; | ||||||
|                } |                } | ||||||
|                commit('ADD_CONNECTED', { |                commit('ADD_CONNECTED', { | ||||||
|                   uid: connection.uid, |                   uid: connection.uid, | ||||||
|                   client: connection.client, |                   client: connection.client, | ||||||
|                   dataTypes, |                   dataTypes, | ||||||
|  |                   indexTypes, | ||||||
|                   structure: response |                   structure: response | ||||||
|                }); |                }); | ||||||
|                dispatch('refreshCollations', connection.uid); |                dispatch('refreshCollations', connection.uid); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user