mirror of
https://github.com/Fabio286/antares.git
synced 2025-02-18 20:50:48 +01:00
feat: option to insert table rows
This commit is contained in:
parent
128a6cd9e8
commit
2f1dfdc654
@ -44,4 +44,14 @@ export default (connections) => {
|
|||||||
return { status: 'error', response: err.toString() };
|
return { status: 'error', response: err.toString() };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('insertTableRows', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await Tables.insertTableRows(connections[params.uid], params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -32,7 +32,7 @@ export class AntaresConnector {
|
|||||||
limit: [],
|
limit: [],
|
||||||
join: [],
|
join: [],
|
||||||
update: [],
|
update: [],
|
||||||
insert: [],
|
insert: {},
|
||||||
delete: false
|
delete: false
|
||||||
};
|
};
|
||||||
this._query = Object.assign({}, this._queryDefaults);
|
this._query = Object.assign({}, this._queryDefaults);
|
||||||
@ -108,6 +108,11 @@ export class AntaresConnector {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
into (table) {
|
||||||
|
this._query.from = table;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
delete (table) {
|
delete (table) {
|
||||||
this._query.delete = true;
|
this._query.delete = true;
|
||||||
this.from(table);
|
this.from(table);
|
||||||
@ -162,6 +167,16 @@ export class AntaresConnector {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} obj field: value
|
||||||
|
* @returns
|
||||||
|
* @memberof AntaresConnector
|
||||||
|
*/
|
||||||
|
insert (obj) {
|
||||||
|
this._query.insert = { ...this._query.insert, ...obj };
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {string} SQL string
|
* @returns {string} SQL string
|
||||||
* @memberof AntaresConnector
|
* @memberof AntaresConnector
|
||||||
@ -188,8 +203,10 @@ export class AntaresConnector {
|
|||||||
|
|
||||||
// FROM
|
// FROM
|
||||||
let fromRaw = '';
|
let fromRaw = '';
|
||||||
if (!this._query.update.length && !!this._query.from)
|
if (!this._query.update.length && !Object.keys(this._query.insert).length && !!this._query.from)
|
||||||
fromRaw = 'FROM';
|
fromRaw = 'FROM';
|
||||||
|
else if (Object.keys(this._query.insert).length)
|
||||||
|
fromRaw = 'INTO';
|
||||||
|
|
||||||
switch (this._client) {
|
switch (this._client) {
|
||||||
case 'maria':
|
case 'maria':
|
||||||
@ -209,6 +226,21 @@ export class AntaresConnector {
|
|||||||
const updateArray = this._query.update.reduce(this._reducer, []);
|
const updateArray = this._query.update.reduce(this._reducer, []);
|
||||||
const updateRaw = updateArray.length ? `SET ${updateArray.join(', ')} ` : '';
|
const updateRaw = updateArray.length ? `SET ${updateArray.join(', ')} ` : '';
|
||||||
|
|
||||||
|
let insertRaw = '';
|
||||||
|
if (Object.keys(this._query.insert).length) {
|
||||||
|
const fieldsList = [];
|
||||||
|
const valueList = [];
|
||||||
|
const fields = this._query.insert;
|
||||||
|
|
||||||
|
for (const key in fields) {
|
||||||
|
if (fields[key] === null) continue;
|
||||||
|
fieldsList.push(key);
|
||||||
|
valueList.push(typeof fields[key] === 'number' ? fields[key] : `"${fields[key]}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
insertRaw = ` (${fieldsList.join(',')}) VALUES (${valueList.join(',')}) `;
|
||||||
|
}
|
||||||
|
|
||||||
const groupByArray = this._query.groupBy.reduce(this._reducer, []);
|
const groupByArray = this._query.groupBy.reduce(this._reducer, []);
|
||||||
const groupByRaw = groupByArray.length ? `GROUP BY ${groupByArray.join(', ')} ` : '';
|
const groupByRaw = groupByArray.length ? `GROUP BY ${groupByArray.join(', ')} ` : '';
|
||||||
|
|
||||||
@ -229,7 +261,7 @@ export class AntaresConnector {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${selectRaw}${updateRaw ? 'UPDATE' : ''}${this._query.delete ? 'DELETE ' : ''}${fromRaw}${updateRaw}${whereRaw}${groupByRaw}${orderByRaw}${limitRaw}`;
|
return `${selectRaw}${updateRaw ? 'UPDATE' : ''}${insertRaw ? 'INSERT ' : ''}${this._query.delete ? 'DELETE ' : ''}${fromRaw}${updateRaw}${whereRaw}${groupByRaw}${orderByRaw}${limitRaw}${insertRaw}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +31,10 @@ export default class {
|
|||||||
datePrecision: field.DATETIME_PRECISION,
|
datePrecision: field.DATETIME_PRECISION,
|
||||||
charLength: field.CHARACTER_MAXIMUM_LENGTH,
|
charLength: field.CHARACTER_MAXIMUM_LENGTH,
|
||||||
isNullable: field.IS_NULLABLE,
|
isNullable: field.IS_NULLABLE,
|
||||||
default: field.COLUMN_DEFAULT
|
default: field.COLUMN_DEFAULT,
|
||||||
|
charset: field.CHARACTER_SET_NAME,
|
||||||
|
collation: field.COLLATION_NAME,
|
||||||
|
autoIncrement: field.EXTRA.includes('auto_increment')
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -50,4 +50,14 @@ export default class {
|
|||||||
.where({ [params.primary]: `IN (${params.rows.join(',')})` })
|
.where({ [params.primary]: `IN (${params.rows.join(',')})` })
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async insertTableRows (connection, params) { // Prepare every field like updateTableCell method
|
||||||
|
for (let i = 0; i < params.repeat; i++) {
|
||||||
|
await connection
|
||||||
|
.schema(params.schema)
|
||||||
|
.into(params.table)
|
||||||
|
.insert(params.row)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
278
src/renderer/components/ModalNewTableRow.vue
Normal file
278
src/renderer/components/ModalNewTableRow.vue
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
<template>
|
||||||
|
<div class="modal active">
|
||||||
|
<a class="modal-overlay" @click.stop="closeModal" />
|
||||||
|
<div class="modal-container p-0">
|
||||||
|
<div class="modal-header pl-2">
|
||||||
|
<div class="modal-title h6">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-playlist-plus mr-1" /> {{ $t('message.addNewRow') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="content">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<fieldset :disabled="isInserting">
|
||||||
|
<div
|
||||||
|
v-for="(field, key) in fields"
|
||||||
|
:key="field.name"
|
||||||
|
class="form-group"
|
||||||
|
>
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label" :title="field.name">{{ field.name }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="input-group col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-if="inputProps(field).mask"
|
||||||
|
v-model="localRow[field.name]"
|
||||||
|
v-mask="inputProps(field).mask"
|
||||||
|
class="form-input"
|
||||||
|
:type="inputProps(field).type"
|
||||||
|
:disabled="fieldsToExclude.includes(field.name)"
|
||||||
|
:tabindex="key+1"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
v-else
|
||||||
|
v-model="localRow[field.name]"
|
||||||
|
class="form-input"
|
||||||
|
:type="inputProps(field).type"
|
||||||
|
:disabled="fieldsToExclude.includes(field.name)"
|
||||||
|
:tabindex="key+1"
|
||||||
|
>
|
||||||
|
<span class="input-group-addon" :class="`type-${field.type}`">
|
||||||
|
{{ field.type }} {{ fieldLength(field) | wrapNumber }}
|
||||||
|
</span>
|
||||||
|
<label class="form-checkbox ml-3" :title="$t('word.insert')">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
:checked="!field.autoIncrement"
|
||||||
|
@change.prevent="toggleFields($event, field)"
|
||||||
|
><i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer text-light">
|
||||||
|
<div class="input-group col-3 tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')">
|
||||||
|
<input
|
||||||
|
v-model="nInserts"
|
||||||
|
type="number"
|
||||||
|
class="form-input"
|
||||||
|
min="1"
|
||||||
|
:disabled="isInserting"
|
||||||
|
>
|
||||||
|
<span class="input-group-addon">
|
||||||
|
<i class="mdi mdi-24px mdi-repeat" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
class="btn btn-primary mr-2"
|
||||||
|
:class="{'loading': isInserting}"
|
||||||
|
@click.stop="insertRows"
|
||||||
|
>
|
||||||
|
{{ $t('word.insert') }}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-link" @click.stop="closeModal">
|
||||||
|
{{ $t('word.close') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import moment from 'moment';
|
||||||
|
import { TEXT, LONG_TEXT, NUMBER, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
|
||||||
|
import { mask } from 'vue-the-mask';
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import Tables from '@/ipc-api/Tables';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalNewTableRow',
|
||||||
|
directives: {
|
||||||
|
mask
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
wrapNumber (num) {
|
||||||
|
if (!num) return '';
|
||||||
|
return `(${num})`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
fields: Array,
|
||||||
|
connection: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
localRow: {},
|
||||||
|
fieldsToExclude: [],
|
||||||
|
nInserts: 1,
|
||||||
|
isInserting: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.connection.uid);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
nInserts (val) {
|
||||||
|
if (!val || val < 1)
|
||||||
|
this.nInserts = 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
const rowObj = {};
|
||||||
|
|
||||||
|
for (const field of this.fields) {
|
||||||
|
let fieldDefault;
|
||||||
|
|
||||||
|
if (field.default === 'NULL') fieldDefault = null;
|
||||||
|
else {
|
||||||
|
if (NUMBER.includes(field.type))
|
||||||
|
fieldDefault = +field.default;
|
||||||
|
|
||||||
|
if ([...TEXT, ...LONG_TEXT].includes(field.type))
|
||||||
|
fieldDefault = field.default ? field.default.substring(1, field.default.length - 1) : '';
|
||||||
|
|
||||||
|
if ([...TIME, ...DATE].includes(field.type))
|
||||||
|
fieldDefault = field.default;
|
||||||
|
|
||||||
|
if (DATETIME.includes(field.type)) {
|
||||||
|
if (field.default && field.default.includes('current_timestamp')) {
|
||||||
|
let datePrecision = '';
|
||||||
|
for (let i = 0; i < field.datePrecision; i++)
|
||||||
|
datePrecision += i === 0 ? '.S' : 'S';
|
||||||
|
fieldDefault = moment().format(`YYYY-MM-DD HH:mm:ss${datePrecision}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rowObj[field.name] = fieldDefault;
|
||||||
|
|
||||||
|
if (field.autoIncrement)// Disable by default auto increment fields
|
||||||
|
this.fieldsToExclude = [...this.fieldsToExclude, field.name];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.localRow = { ...rowObj };
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification'
|
||||||
|
}),
|
||||||
|
async insertRows () {
|
||||||
|
this.isInserting = true;
|
||||||
|
const rowToInsert = this.localRow;
|
||||||
|
Object.keys(rowToInsert).forEach(key => {
|
||||||
|
if (this.fieldsToExclude.includes(key))
|
||||||
|
delete rowToInsert[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Tables.insertTableRows({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.workspace.breadcrumbs.schema,
|
||||||
|
table: this.workspace.breadcrumbs.table,
|
||||||
|
row: rowToInsert,
|
||||||
|
repeat: this.nInserts
|
||||||
|
});
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
this.closeModal();
|
||||||
|
this.$emit('reload');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isInserting = false;
|
||||||
|
},
|
||||||
|
closeModal () {
|
||||||
|
this.$emit('hide');
|
||||||
|
},
|
||||||
|
fieldLength (field) {
|
||||||
|
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
||||||
|
return field.numPrecision || field.datePrecision || field.charLength || 0;
|
||||||
|
},
|
||||||
|
inputProps (field) {
|
||||||
|
if ([...TEXT, ...LONG_TEXT].includes(field.type))
|
||||||
|
return { type: 'text', mask: false };
|
||||||
|
|
||||||
|
if (NUMBER.includes(field.type))
|
||||||
|
return { type: 'number', mask: false };
|
||||||
|
|
||||||
|
if (TIME.includes(field.type)) {
|
||||||
|
let timeMask = '##:##:##';
|
||||||
|
const precision = this.fieldLength(field);
|
||||||
|
|
||||||
|
for (let i = 0; i < precision; i++)
|
||||||
|
timeMask += i === 0 ? '.#' : '#';
|
||||||
|
|
||||||
|
return { type: 'text', mask: timeMask };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DATE.includes(field.type))
|
||||||
|
return { type: 'text', mask: '####-##-##' };
|
||||||
|
|
||||||
|
if (DATETIME.includes(field.type)) {
|
||||||
|
let datetimeMask = '####-##-## ##:##:##';
|
||||||
|
const precision = this.fieldLength(field);
|
||||||
|
|
||||||
|
for (let i = 0; i < precision; i++)
|
||||||
|
datetimeMask += i === 0 ? '.#' : '#';
|
||||||
|
|
||||||
|
return { type: 'text', mask: datetimeMask };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BLOB.includes(field.type))
|
||||||
|
return { type: 'file', mask: false };
|
||||||
|
|
||||||
|
if (BIT.includes(field.type))
|
||||||
|
return { type: 'text', mask: false };
|
||||||
|
|
||||||
|
return { type: 'text', mask: false };
|
||||||
|
},
|
||||||
|
toggleFields (event, field) {
|
||||||
|
if (event.target.checked)
|
||||||
|
this.fieldsToExclude = this.fieldsToExclude.filter(f => f !== field.name);
|
||||||
|
else
|
||||||
|
this.fieldsToExclude = [...this.fieldsToExclude, field.name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.modal-container {
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: normal;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group-addon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
@ -40,19 +40,28 @@
|
|||||||
@deleteSelected="deleteSelected"
|
@deleteSelected="deleteSelected"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<ModalNewTableRow
|
||||||
|
v-if="isAddModal"
|
||||||
|
:fields="fields"
|
||||||
|
:connection="connection"
|
||||||
|
@hide="hideAddModal"
|
||||||
|
@reload="reloadTable"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Tables from '@/ipc-api/Tables';
|
import Tables from '@/ipc-api/Tables';
|
||||||
import WorkspaceQueryTable from '@/components/WorkspaceQueryTable';
|
import WorkspaceQueryTable from '@/components/WorkspaceQueryTable';
|
||||||
|
import ModalNewTableRow from '@/components/ModalNewTableRow';
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import tableTabs from '@/mixins/tableTabs';
|
import tableTabs from '@/mixins/tableTabs';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WorkspaceTableTab',
|
name: 'WorkspaceTableTab',
|
||||||
components: {
|
components: {
|
||||||
WorkspaceQueryTable
|
WorkspaceQueryTable,
|
||||||
|
ModalNewTableRow
|
||||||
},
|
},
|
||||||
mixins: [tableTabs],
|
mixins: [tableTabs],
|
||||||
props: {
|
props: {
|
||||||
|
@ -37,7 +37,8 @@ module.exports = {
|
|||||||
download: 'Download',
|
download: 'Download',
|
||||||
add: 'Add',
|
add: 'Add',
|
||||||
data: 'Data',
|
data: 'Data',
|
||||||
properties: 'Properties'
|
properties: 'Properties',
|
||||||
|
insert: 'Insert'
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
appWelcome: 'Welcome to Antares SQL Client!',
|
appWelcome: 'Welcome to Antares SQL Client!',
|
||||||
@ -65,7 +66,9 @@ module.exports = {
|
|||||||
deleteRows: 'Delete row | Delete {count} rows',
|
deleteRows: 'Delete row | Delete {count} rows',
|
||||||
confirmToDeleteRows: 'Do you confirm to delete one row? | Do you confirm to delete {count} rows?',
|
confirmToDeleteRows: 'Do you confirm to delete one row? | Do you confirm to delete {count} rows?',
|
||||||
notificationsTimeout: 'Notifications timeout',
|
notificationsTimeout: 'Notifications timeout',
|
||||||
uploadFile: 'Upload file'
|
uploadFile: 'Upload file',
|
||||||
|
addNewRow: 'Add new row',
|
||||||
|
numberOfInserts: 'Number of inserts'
|
||||||
},
|
},
|
||||||
// Date and Time
|
// Date and Time
|
||||||
short: {
|
short: {
|
||||||
|
@ -17,4 +17,8 @@ export default class {
|
|||||||
static deleteTableRows (params) {
|
static deleteTableRows (params) {
|
||||||
return ipcRenderer.invoke('deleteTableRows', params);
|
return ipcRenderer.invoke('deleteTableRows', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static insertTableRows (params) {
|
||||||
|
return ipcRenderer.invoke('insertTableRows', params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user