mirror of https://github.com/Fabio286/antares.git
feat: foreign key support in add/edit row
This commit is contained in:
parent
dca625fe5a
commit
0b6a188d19
|
@ -1,6 +1,6 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
github: [fabio286]
|
||||
patreon: fabio286
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
import { app, BrowserWindow, nativeImage } from 'electron';
|
||||
import * as path from 'path';
|
||||
import { format as formatUrl } from 'url';
|
||||
|
||||
import ipcHandlers from './ipc-handlers';
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||
|
|
|
@ -64,4 +64,14 @@ export default (connections) => {
|
|||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-foreign-list', async (event, params) => {
|
||||
try {
|
||||
const results = await Tables.getForeignList(connections[params.uid], params);
|
||||
return { status: 'success', response: results };
|
||||
}
|
||||
catch (err) {
|
||||
return { status: 'error', response: err.toString() };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -40,8 +40,6 @@ export default class {
|
|||
}
|
||||
|
||||
static async getKeyUsage (connection, schema, table) {
|
||||
// SELECT * FROM information_schema.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA='fep-gprs' AND TABLE_NAME='struttura_macchine' AND REFERENCED_TABLE_NAME IS NOT NULL;
|
||||
|
||||
const { rows } = await connection
|
||||
.select('*')
|
||||
.schema('information_schema')
|
||||
|
|
|
@ -54,12 +54,13 @@ export default class {
|
|||
|
||||
static async insertTableRows (connection, params) {
|
||||
const insertObj = {};
|
||||
console.log(params);
|
||||
for (const key in params.row) {
|
||||
const type = params.fields[key];
|
||||
let escapedParam;
|
||||
|
||||
if (NUMBER.includes(type))
|
||||
if (params.row[key] === null)
|
||||
escapedParam = 'NULL';
|
||||
else if (NUMBER.includes(type))
|
||||
escapedParam = params.row[key];
|
||||
else if ([...TEXT, ...LONG_TEXT].includes(type))
|
||||
escapedParam = `"${sqlEscaper(params.row[key])}"`;
|
||||
|
@ -85,4 +86,17 @@ export default class {
|
|||
.run();
|
||||
}
|
||||
}
|
||||
|
||||
static async getForeignList (connection, params) {
|
||||
const query = connection
|
||||
.select(`${params.column} AS foreignColumn`)
|
||||
.schema(params.schema)
|
||||
.from(params.table)
|
||||
.orderBy('foreignColumn ASC');
|
||||
|
||||
if (params.description)
|
||||
query.select(`LEFT(${params.description}, 20) AS foreignDescription`);
|
||||
|
||||
return query.run();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
<template>
|
||||
<select
|
||||
ref="editField"
|
||||
class="px-1"
|
||||
@change="onChange"
|
||||
@blur="$emit('blur')"
|
||||
>
|
||||
<option
|
||||
v-for="row in foreignList"
|
||||
:key="row.foreignColumn"
|
||||
:value="row.foreignColumn"
|
||||
:selected="row.foreignColumn === value"
|
||||
>
|
||||
{{ row.foreignColumn }} {{ 'foreignDescription' in row ? ` - ${row.foreignDescription}` : '' | cutText }}
|
||||
</option>
|
||||
</select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Tables from '@/ipc-api/Tables';
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import { TEXT, LONG_TEXT } from 'common/fieldTypes';
|
||||
export default {
|
||||
name: 'ForeignKeySelect',
|
||||
filters: {
|
||||
cutText (val) {
|
||||
if (typeof val !== 'string') return val;
|
||||
return val.length > 15 ? `${val.substring(0, 15)}...` : val;
|
||||
}
|
||||
},
|
||||
props: {
|
||||
value: [String, Number],
|
||||
keyUsage: Object
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
foreignList: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
selectedWorkspace: 'workspaces/getSelected'
|
||||
})
|
||||
},
|
||||
async created () {
|
||||
let firstTextField;
|
||||
const params = {
|
||||
uid: this.selectedWorkspace,
|
||||
schema: this.keyUsage.refSchema,
|
||||
table: this.keyUsage.refTable
|
||||
};
|
||||
|
||||
try { // Field data
|
||||
const { status, response } = await Tables.getTableColumns(params);
|
||||
if (status === 'success')
|
||||
firstTextField = response.find(field => [...TEXT, ...LONG_TEXT].includes(field.type)).name || false;
|
||||
else
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
}
|
||||
catch (err) {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
|
||||
try { // Foregn list
|
||||
const { status, response } = await Tables.getForeignList({
|
||||
...params,
|
||||
column: this.keyUsage.refColumn,
|
||||
description: firstTextField
|
||||
});
|
||||
|
||||
if (status === 'success')
|
||||
this.foreignList = response.rows;
|
||||
else
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
}
|
||||
catch (err) {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
addNotification: 'notifications/addNotification'
|
||||
}),
|
||||
onChange () {
|
||||
this.$emit('update:value', this.$refs.editField.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -23,8 +23,15 @@
|
|||
<label class="form-label" :title="field.name">{{ field.name }}</label>
|
||||
</div>
|
||||
<div class="input-group col-8 col-sm-12">
|
||||
<ForeignKeySelect
|
||||
v-if="foreignKeys.includes(field.name)"
|
||||
class="form-select"
|
||||
:value.sync="localRow[field.name]"
|
||||
:key-usage="getKeyUsage(field.name)"
|
||||
:disabled="fieldsToExclude.includes(field.name)"
|
||||
/>
|
||||
<input
|
||||
v-if="inputProps(field).mask"
|
||||
v-else-if="inputProps(field).mask"
|
||||
v-model="localRow[field.name]"
|
||||
v-mask="inputProps(field).mask"
|
||||
class="form-input"
|
||||
|
@ -100,9 +107,13 @@ import { TEXT, LONG_TEXT, NUMBER, DATE, TIME, DATETIME, BLOB, BIT } from 'common
|
|||
import { mask } from 'vue-the-mask';
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import Tables from '@/ipc-api/Tables';
|
||||
import ForeignKeySelect from '@/components/ForeignKeySelect';
|
||||
|
||||
export default {
|
||||
name: 'ModalNewTableRow',
|
||||
components: {
|
||||
ForeignKeySelect
|
||||
},
|
||||
directives: {
|
||||
mask
|
||||
},
|
||||
|
@ -113,8 +124,8 @@ export default {
|
|||
}
|
||||
},
|
||||
props: {
|
||||
fields: Array,
|
||||
connection: Object
|
||||
connection: Object,
|
||||
tabUid: [String, Number]
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
|
@ -126,10 +137,21 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
getWorkspace: 'workspaces/getWorkspace'
|
||||
selectedWorkspace: 'workspaces/getSelected',
|
||||
getWorkspace: 'workspaces/getWorkspace',
|
||||
getWorkspaceTab: 'workspaces/getWorkspaceTab'
|
||||
}),
|
||||
workspace () {
|
||||
return this.getWorkspace(this.connection.uid);
|
||||
return this.getWorkspace(this.selectedWorkspace);
|
||||
},
|
||||
foreignKeys () {
|
||||
return this.keyUsage.map(key => key.column);
|
||||
},
|
||||
fields () {
|
||||
return this.getWorkspaceTab(this.tabUid) ? this.getWorkspaceTab(this.tabUid).fields : [];
|
||||
},
|
||||
keyUsage () {
|
||||
return this.getWorkspaceTab(this.tabUid) ? this.getWorkspaceTab(this.tabUid).keyUsage : [];
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -194,7 +216,7 @@ export default {
|
|||
|
||||
try {
|
||||
const { status, response } = await Tables.insertTableRows({
|
||||
uid: this.connection.uid,
|
||||
uid: this.selectedWorkspace,
|
||||
schema: this.workspace.breadcrumbs.schema,
|
||||
table: this.workspace.breadcrumbs.table,
|
||||
row: rowToInsert,
|
||||
|
@ -271,6 +293,10 @@ export default {
|
|||
if (!files.length) return;
|
||||
|
||||
this.localRow[field] = files[0].path;
|
||||
},
|
||||
|
||||
getKeyUsage (keyName) {
|
||||
return this.keyUsage.find(key => key.column === keyName);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -16,6 +16,13 @@
|
|||
:class="`${isNull(col)} type-${fieldType(cKey)}`"
|
||||
@dblclick="editON($event, col, cKey)"
|
||||
>{{ col | typeFormat(fieldType(cKey), fieldPrecision(cKey)) | cutText }}</span>
|
||||
<ForeignKeySelect
|
||||
v-else-if="foreignKeys.includes(cKey)"
|
||||
class="editable-field"
|
||||
:value.sync="editingContent"
|
||||
:key-usage="getKeyUsage(cKey)"
|
||||
@blur="editOFF"
|
||||
/>
|
||||
<template v-else>
|
||||
<input
|
||||
v-if="inputProps.mask"
|
||||
|
@ -131,11 +138,13 @@ import hexToBinary from 'common/libs/hexToBinary';
|
|||
import { TEXT, LONG_TEXT, NUMBER, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
|
||||
import { mask } from 'vue-the-mask';
|
||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||
import ForeignKeySelect from '@/components/ForeignKeySelect';
|
||||
|
||||
export default {
|
||||
name: 'WorkspaceQueryTableRow',
|
||||
components: {
|
||||
ConfirmModal
|
||||
ConfirmModal,
|
||||
ForeignKeySelect
|
||||
},
|
||||
directives: {
|
||||
mask
|
||||
|
@ -240,6 +249,9 @@ export default {
|
|||
},
|
||||
isImage () {
|
||||
return ['gif', 'jpg', 'png', 'bmp', 'ico', 'tif'].includes(this.contentInfo.ext);
|
||||
},
|
||||
foreignKeys () {
|
||||
return this.keyUsage.map(key => key.column);
|
||||
}
|
||||
},
|
||||
created () {
|
||||
|
@ -381,6 +393,9 @@ export default {
|
|||
},
|
||||
selectRow (event, row) {
|
||||
this.$emit('selectRow', event, row);
|
||||
},
|
||||
getKeyUsage (keyName) {
|
||||
return this.keyUsage.find(key => key.column === keyName);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
<div class="workspace-query-results column col-12">
|
||||
<WorkspaceQueryTable
|
||||
v-if="results"
|
||||
v-show="!isQuering"
|
||||
ref="queryTable"
|
||||
:results="results"
|
||||
:tab-uid="tabUid"
|
||||
|
@ -43,8 +42,7 @@
|
|||
</div>
|
||||
<ModalNewTableRow
|
||||
v-if="isAddModal"
|
||||
:fields="fields"
|
||||
:connection="connection"
|
||||
:tab-uid="tabUid"
|
||||
@hide="hideAddModal"
|
||||
@reload="reloadTable"
|
||||
/>
|
||||
|
@ -57,7 +55,6 @@ import WorkspaceQueryTable from '@/components/WorkspaceQueryTable';
|
|||
import ModalNewTableRow from '@/components/ModalNewTableRow';
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import tableTabs from '@/mixins/tableTabs';
|
||||
// import { TEXT, LONG_TEXT } from 'common/fieldTypes';
|
||||
|
||||
export default {
|
||||
name: 'WorkspaceTableTab',
|
||||
|
@ -90,13 +87,6 @@ export default {
|
|||
},
|
||||
isSelected () {
|
||||
return this.workspace.selected_tab === 1;
|
||||
},
|
||||
firstTextField () { // TODO: move inside new row modal and row components
|
||||
if (this.fields.length) {
|
||||
const textField = this.fields.find(field => [...TEXT, ...LONG_TEXT].includes(field.type));
|
||||
return textField ? textField.name : '';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
|
@ -25,4 +25,8 @@ export default class {
|
|||
static insertTableRows (params) {
|
||||
return ipcRenderer.invoke('insertTableRows', params);
|
||||
}
|
||||
|
||||
static getForeignList (params) {
|
||||
return ipcRenderer.invoke('get-foreign-list', params);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue