1
1
mirror of https://github.com/Fabio286/antares.git synced 2025-06-05 21:59:22 +02:00

Merge pull request #87 from Fabio286/new-tab-system

New tab system
This commit is contained in:
2021-07-22 10:57:43 +02:00
committed by GitHub
47 changed files with 2136 additions and 1787 deletions

View File

@@ -127,7 +127,7 @@
"standard-version": "^9.3.0", "standard-version": "^9.3.0",
"stylelint": "^13.13.1", "stylelint": "^13.13.1",
"stylelint-config-standard": "^22.0.0", "stylelint-config-standard": "^22.0.0",
"stylelint-scss": "^3.19.0", "stylelint-scss": "^3.20.1",
"vue": "^2.6.14", "vue": "^2.6.14",
"vue-template-compiler": "^2.6.14", "vue-template-compiler": "^2.6.14",
"webpack": "^4.46.0" "webpack": "^4.46.0"

View File

@@ -128,7 +128,12 @@ export default connections => {
if (!query) return; if (!query) return;
try { try {
const result = await connections[uid].raw(query, { nest: true, details: true, schema }); const result = await connections[uid].raw(query, {
nest: true,
details: true,
schema,
comments: false
});
return { status: 'success', response: result }; return { status: 'success', response: result };
} }

View File

@@ -29,7 +29,7 @@ export default (connections) => {
if (sortParams && sortParams.field && sortParams.dir) if (sortParams && sortParams.field && sortParams.dir)
query.orderBy({ [sortParams.field]: sortParams.dir.toUpperCase() }); query.orderBy({ [sortParams.field]: sortParams.dir.toUpperCase() });
const result = await query.run({ details: true }); const result = await query.run({ details: true, schema });
return { status: 'success', response: result }; return { status: 'success', response: result };
} }

View File

@@ -335,11 +335,11 @@ export class MySQLClient extends AntaresCore {
.select('*') .select('*')
.schema('information_schema') .schema('information_schema')
.from('COLUMNS') .from('COLUMNS')
.where({ TABLE_SCHEMA: `= '${this._schema || schema}'`, TABLE_NAME: `= '${table}'` }) .where({ TABLE_SCHEMA: `= '${schema}'`, TABLE_NAME: `= '${table}'` })
.orderBy({ ORDINAL_POSITION: 'ASC' }) .orderBy({ ORDINAL_POSITION: 'ASC' })
.run(); .run();
const { rows: fields } = await this.raw(`SHOW CREATE TABLE \`${this._schema || schema}\`.\`${table}\``); const { rows: fields } = await this.raw(`SHOW CREATE TABLE \`${schema}\`.\`${table}\``);
const remappedFields = fields.map(row => { const remappedFields = fields.map(row => {
if (!row['Create Table']) return false; if (!row['Create Table']) return false;
@@ -363,15 +363,14 @@ export class MySQLClient extends AntaresCore {
const details = fieldArr.slice(2).join(' '); const details = fieldArr.slice(2).join(' ');
let defaultValue = null; let defaultValue = null;
if (details.includes('DEFAULT')) { if (details.includes('DEFAULT'))
defaultValue = details.match(/(?<=DEFAULT ).*?$/gs)[0].split(' COMMENT')[0]; defaultValue = details.match(/(?<=DEFAULT ).*?$/gs)[0].split(' COMMENT')[0];
const defaultValueArr = defaultValue.split(''); // const defaultValueArr = defaultValue.split('');
if (defaultValueArr[0] === '\'') { // if (defaultValueArr[0] === '\'') {
defaultValueArr.shift(); // defaultValueArr.shift();
defaultValueArr.pop(); // defaultValueArr.pop();
defaultValue = defaultValueArr.join(''); // defaultValue = defaultValueArr.join('');
} // }
}
const typeAndLength = nameAndType[1].replace(')', '').split('('); const typeAndLength = nameAndType[1].replace(')', '').split('(');
@@ -574,7 +573,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async dropView (params) { async dropView (params) {
const sql = `DROP VIEW \`${this._schema}\`.\`${params.view}\``; const sql = `DROP VIEW \`${params.schema}\`.\`${params.view}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -586,10 +585,10 @@ export class MySQLClient extends AntaresCore {
*/ */
async alterView (params) { async alterView (params) {
const { view } = params; const { view } = params;
let sql = `ALTER ALGORITHM = ${view.algorithm}${view.definer ? ` DEFINER=${view.definer}` : ''} SQL SECURITY ${view.security} VIEW \`${this._schema}\`.\`${view.oldName}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}`; let sql = `ALTER ALGORITHM = ${view.algorithm}${view.definer ? ` DEFINER=${view.definer}` : ''} SQL SECURITY ${view.security} VIEW \`${view.schema}\`.\`${view.oldName}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}`;
if (view.name !== view.oldName) if (view.name !== view.oldName)
sql += `; RENAME TABLE \`${this._schema}\`.\`${view.oldName}\` TO \`${this._schema}\`.\`${view.name}\``; sql += `; RENAME TABLE \`${view.schema}\`.\`${view.oldName}\` TO \`${view.schema}\`.\`${view.name}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -600,8 +599,8 @@ export class MySQLClient extends AntaresCore {
* @returns {Array.<Object>} parameters * @returns {Array.<Object>} parameters
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async createView (view) { async createView (params) {
const sql = `CREATE ALGORITHM = ${view.algorithm} ${view.definer ? `DEFINER=${view.definer} ` : ''}SQL SECURITY ${view.security} VIEW \`${this._schema}\`.\`${view.name}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}`; const sql = `CREATE ALGORITHM = ${params.algorithm} ${params.definer ? `DEFINER=${params.definer} ` : ''}SQL SECURITY ${params.security} VIEW \`${params.schema}\`.\`${params.name}\` AS ${params.sql} ${params.updateOption ? `WITH ${params.updateOption} CHECK OPTION` : ''}`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -634,7 +633,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async dropTrigger (params) { async dropTrigger (params) {
const sql = `DROP TRIGGER \`${this._schema}\`.\`${params.trigger}\``; const sql = `DROP TRIGGER \`${params.schema}\`.\`${params.trigger}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -651,8 +650,8 @@ export class MySQLClient extends AntaresCore {
try { try {
await this.createTrigger(tempTrigger); await this.createTrigger(tempTrigger);
await this.dropTrigger({ trigger: tempTrigger.name }); await this.dropTrigger({ schema: trigger.schema, trigger: tempTrigger.name });
await this.dropTrigger({ trigger: trigger.oldName }); await this.dropTrigger({ schema: trigger.schema, trigger: trigger.oldName });
await this.createTrigger(trigger); await this.createTrigger(trigger);
} }
catch (err) { catch (err) {
@@ -666,8 +665,8 @@ export class MySQLClient extends AntaresCore {
* @returns {Array.<Object>} parameters * @returns {Array.<Object>} parameters
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async createTrigger (trigger) { async createTrigger (params) {
const sql = `CREATE ${trigger.definer ? `DEFINER=${trigger.definer} ` : ''}TRIGGER \`${this._schema}\`.\`${trigger.name}\` ${trigger.activation} ${trigger.event} ON \`${trigger.table}\` FOR EACH ROW ${trigger.sql}`; const sql = `CREATE ${params.definer ? `DEFINER=${params.definer} ` : ''}TRIGGER \`${params.schema}\`.\`${params.name}\` ${params.activation} ${params.event} ON \`${params.table}\` FOR EACH ROW ${params.sql}`;
return await this.raw(sql, { split: false }); return await this.raw(sql, { split: false });
} }
@@ -741,7 +740,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async dropRoutine (params) { async dropRoutine (params) {
const sql = `DROP PROCEDURE \`${this._schema}\`.\`${params.routine}\``; const sql = `DROP PROCEDURE \`${params.schema}\`.\`${params.routine}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -758,8 +757,8 @@ export class MySQLClient extends AntaresCore {
try { try {
await this.createRoutine(tempProcedure); await this.createRoutine(tempProcedure);
await this.dropRoutine({ routine: tempProcedure.name }); await this.dropRoutine({ schema: routine.schema, routine: tempProcedure.name });
await this.dropRoutine({ routine: routine.oldName }); await this.dropRoutine({ schema: routine.schema, routine: routine.oldName });
await this.createRoutine(routine); await this.createRoutine(routine);
} }
catch (err) { catch (err) {
@@ -773,21 +772,21 @@ export class MySQLClient extends AntaresCore {
* @returns {Array.<Object>} parameters * @returns {Array.<Object>} parameters
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async createRoutine (routine) { async createRoutine (params) {
const parameters = 'parameters' in routine const parameters = 'parameters' in params
? routine.parameters.reduce((acc, curr) => { ? params.parameters.reduce((acc, curr) => {
acc.push(`${curr.context} \`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`); acc.push(`${curr.context} \`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
return acc; return acc;
}, []).join(',') }, []).join(',')
: ''; : '';
const sql = `CREATE ${routine.definer ? `DEFINER=${routine.definer} ` : ''}PROCEDURE \`${this._schema}\`.\`${routine.name}\`(${parameters}) const sql = `CREATE ${params.definer ? `DEFINER=${params.definer} ` : ''}PROCEDURE \`${params.schema}\`.\`${params.name}\`(${parameters})
LANGUAGE SQL LANGUAGE SQL
${routine.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'} ${params.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
${routine.dataAccess} ${params.dataAccess}
SQL SECURITY ${routine.security} SQL SECURITY ${params.security}
COMMENT '${routine.comment}' COMMENT '${params.comment}'
${routine.sql}`; ${params.sql}`;
return await this.raw(sql, { split: false }); return await this.raw(sql, { split: false });
} }
@@ -868,7 +867,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async dropFunction (params) { async dropFunction (params) {
const sql = `DROP FUNCTION \`${this._schema}\`.\`${params.func}\``; const sql = `DROP FUNCTION \`${params.schema}\`.\`${params.func}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -885,8 +884,8 @@ export class MySQLClient extends AntaresCore {
try { try {
await this.createFunction(tempProcedure); await this.createFunction(tempProcedure);
await this.dropFunction({ func: tempProcedure.name }); await this.dropFunction({ schema: func.schema, func: tempProcedure.name });
await this.dropFunction({ func: func.oldName }); await this.dropFunction({ schema: func.schema, func: func.oldName });
await this.createFunction(func); await this.createFunction(func);
} }
catch (err) { catch (err) {
@@ -900,20 +899,20 @@ export class MySQLClient extends AntaresCore {
* @returns {Array.<Object>} parameters * @returns {Array.<Object>} parameters
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async createFunction (func) { async createFunction (params) {
const parameters = func.parameters.reduce((acc, curr) => { const parameters = params.parameters.reduce((acc, curr) => {
acc.push(`\`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`); acc.push(`\`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
return acc; return acc;
}, []).join(','); }, []).join(',');
const body = func.returns ? func.sql : 'BEGIN\n RETURN 0;\nEND'; const body = params.returns ? params.sql : 'BEGIN\n RETURN 0;\nEND';
const sql = `CREATE ${func.definer ? `DEFINER=${func.definer} ` : ''}FUNCTION \`${this._schema}\`.\`${func.name}\`(${parameters}) RETURNS ${func.returns || 'SMALLINT'}${func.returnsLength ? `(${func.returnsLength})` : ''} const sql = `CREATE ${params.definer ? `DEFINER=${params.definer} ` : ''}FUNCTION \`${params.schema}\`.\`${params.name}\`(${parameters}) RETURNS ${params.returns || 'SMALLINT'}${params.returnsLength ? `(${params.returnsLength})` : ''}
LANGUAGE SQL LANGUAGE SQL
${func.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'} ${params.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
${func.dataAccess} ${params.dataAccess}
SQL SECURITY ${func.security} SQL SECURITY ${params.security}
COMMENT '${func.comment}' COMMENT '${params.comment}'
${body}`; ${body}`;
return await this.raw(sql, { split: false }); return await this.raw(sql, { split: false });
@@ -960,7 +959,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async dropEvent (params) { async dropEvent (params) {
const sql = `DROP EVENT \`${this._schema}\`.\`${params.scheduler}\``; const sql = `DROP EVENT \`${params.schema}\`.\`${params.scheduler}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -976,13 +975,13 @@ export class MySQLClient extends AntaresCore {
if (scheduler.execution === 'EVERY' && scheduler.every[0].includes('-')) if (scheduler.execution === 'EVERY' && scheduler.every[0].includes('-'))
scheduler.every[0] = `'${scheduler.every[0]}'`; scheduler.every[0] = `'${scheduler.every[0]}'`;
const sql = `ALTER ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${this._schema}\`.\`${scheduler.oldName}\` const sql = `ALTER ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${scheduler.schema}\`.\`${scheduler.oldName}\`
ON SCHEDULE ON SCHEDULE
${scheduler.execution === 'EVERY' ${scheduler.execution === 'EVERY'
? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}` ? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}`
: `AT '${scheduler.at}'`} : `AT '${scheduler.at}'`}
ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE
${scheduler.name !== scheduler.oldName ? `RENAME TO \`${this._schema}\`.\`${scheduler.name}\`` : ''} ${scheduler.name !== scheduler.oldName ? `RENAME TO \`${scheduler.schema}\`.\`${scheduler.name}\`` : ''}
${scheduler.state} ${scheduler.state}
COMMENT '${scheduler.comment}' COMMENT '${scheduler.comment}'
DO ${scheduler.sql}`; DO ${scheduler.sql}`;
@@ -996,16 +995,16 @@ export class MySQLClient extends AntaresCore {
* @returns {Array.<Object>} parameters * @returns {Array.<Object>} parameters
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async createEvent (scheduler) { async createEvent (params) {
const sql = `CREATE ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${this._schema}\`.\`${scheduler.name}\` const sql = `CREATE ${params.definer ? ` DEFINER=${params.definer}` : ''} EVENT \`${params.schema}\`.\`${params.name}\`
ON SCHEDULE ON SCHEDULE
${scheduler.execution === 'EVERY' ${params.execution === 'EVERY'
? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}` ? `EVERY ${params.every.join(' ')}${params.starts ? ` STARTS '${params.starts}'` : ''}${params.ends ? ` ENDS '${params.ends}'` : ''}`
: `AT '${scheduler.at}'`} : `AT '${params.at}'`}
ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE ON COMPLETION${!params.preserve ? ' NOT' : ''} PRESERVE
${scheduler.state} ${params.state}
COMMENT '${scheduler.comment}' COMMENT '${params.comment}'
DO ${scheduler.sql}`; DO ${params.sql}`;
return await this.raw(sql, { split: false }); return await this.raw(sql, { split: false });
} }
@@ -1127,14 +1126,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async createTable (params) { async createTable (params) {
const { const sql = `CREATE TABLE \`${params.schema}\`.\`${params.name}\` (\`${params.name}_ID\` INT NULL) COMMENT='${params.comment}', COLLATE='${params.collation}', ENGINE=${params.engine}`;
name,
collation,
comment,
engine
} = params;
const sql = `CREATE TABLE \`${this._schema}\`.\`${name}\` (\`${name}_ID\` INT NULL) COMMENT='${comment}', COLLATE='${collation}', ENGINE=${engine}`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1148,6 +1140,7 @@ export class MySQLClient extends AntaresCore {
async alterTable (params) { async alterTable (params) {
const { const {
table, table,
schema,
additions, additions,
deletions, deletions,
changes, changes,
@@ -1156,7 +1149,7 @@ export class MySQLClient extends AntaresCore {
options options
} = params; } = params;
let sql = `ALTER TABLE \`${this._schema || params.options.schema}\`.\`${table}\` `; let sql = `ALTER TABLE \`${schema}\`.\`${table}\` `;
const alterColumns = []; const alterColumns = [];
// OPTIONS // OPTIONS
@@ -1268,7 +1261,7 @@ export class MySQLClient extends AntaresCore {
sql += alterColumns.join(', '); sql += alterColumns.join(', ');
// RENAME // RENAME
if (options.name) sql += `; RENAME TABLE \`${this._schema}\`.\`${table}\` TO \`${this._schema}\`.\`${options.name}\``; if (options.name) sql += `; RENAME TABLE \`${schema}\`.\`${table}\` TO \`${schema}\`.\`${options.name}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1280,7 +1273,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async duplicateTable (params) { async duplicateTable (params) {
const sql = `CREATE TABLE \`${this._schema}\`.\`${params.table}_copy\` LIKE \`${this._schema}\`.\`${params.table}\``; const sql = `CREATE TABLE \`${params.schema}\`.\`${params.table}_copy\` LIKE \`${params.schema}\`.\`${params.table}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1291,7 +1284,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async truncateTable (params) { async truncateTable (params) {
const sql = `TRUNCATE TABLE \`${this._schema}\`.\`${params.table}\``; const sql = `TRUNCATE TABLE \`${params.schema}\`.\`${params.table}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1302,7 +1295,7 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async dropTable (params) { async dropTable (params) {
const sql = `DROP TABLE \`${this._schema}\`.\`${params.table}\``; const sql = `DROP TABLE \`${params.schema}\`.\`${params.table}\``;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1373,16 +1366,19 @@ export class MySQLClient extends AntaresCore {
* @memberof MySQLClient * @memberof MySQLClient
*/ */
async raw (sql, args) { async raw (sql, args) {
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
args = { args = {
nest: false, nest: false,
details: false, details: false,
split: true, split: true,
comments: true,
...args ...args
}; };
if (!args.comments)
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments
const nestTables = args.nest ? '.' : false; const nestTables = args.nest ? '.' : false;
const resultsArr = []; const resultsArr = [];
let paramsArr = []; let paramsArr = [];
@@ -1419,7 +1415,7 @@ export class MySQLClient extends AntaresCore {
name: field.orgName, name: field.orgName,
alias: field.name, alias: field.name,
orgName: field.orgName, orgName: field.orgName,
schema: field.schema, schema: args.schema || field.schema,
table: field.table, table: field.table,
tableAlias: field.table, tableAlias: field.table,
orgTable: field.orgTable, orgTable: field.orgTable,

View File

@@ -483,7 +483,7 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async dropView (params) { async dropView (params) {
const sql = `DROP VIEW ${this._schema}.${params.view}`; const sql = `DROP VIEW ${params.schema}.${params.view}`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -495,10 +495,10 @@ export class PostgreSQLClient extends AntaresCore {
*/ */
async alterView (params) { async alterView (params) {
const { view } = params; const { view } = params;
let sql = `CREATE OR REPLACE VIEW ${this._schema}.${view.oldName} AS ${view.sql}`; let sql = `CREATE OR REPLACE VIEW ${view.schema}.${view.oldName} AS ${view.sql}`;
if (view.name !== view.oldName) if (view.name !== view.oldName)
sql += `; ALTER VIEW ${this._schema}.${view.oldName} RENAME TO ${view.name}`; sql += `; ALTER VIEW ${view.schema}.${view.oldName} RENAME TO ${view.name}`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -509,8 +509,8 @@ export class PostgreSQLClient extends AntaresCore {
* @returns {Array.<Object>} parameters * @returns {Array.<Object>} parameters
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async createView (view) { async createView (params) {
const sql = `CREATE VIEW ${this._schema}.${view.name} AS ${view.sql}`; const sql = `CREATE VIEW ${params.schema}.${params.name} AS ${params.sql}`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -560,7 +560,7 @@ export class PostgreSQLClient extends AntaresCore {
*/ */
async dropTrigger (params) { async dropTrigger (params) {
const triggerParts = params.trigger.split('.'); const triggerParts = params.trigger.split('.');
const sql = `DROP TRIGGER "${triggerParts[1]}" ON "${triggerParts[0]}"`; const sql = `DROP TRIGGER "${triggerParts[1]}" ON "${params.schema}"."${triggerParts[0]}"`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -577,8 +577,8 @@ export class PostgreSQLClient extends AntaresCore {
try { try {
await this.createTrigger(tempTrigger); await this.createTrigger(tempTrigger);
await this.dropTrigger({ trigger: `${tempTrigger.table}.${tempTrigger.name}` }); await this.dropTrigger({ schema: trigger.schema, trigger: `${tempTrigger.table}.${tempTrigger.name}` });
await this.dropTrigger({ trigger: `${trigger.table}.${trigger.oldName}` }); await this.dropTrigger({ schema: trigger.schema, trigger: `${trigger.table}.${trigger.oldName}` });
await this.createTrigger(trigger); await this.createTrigger(trigger);
} }
catch (err) { catch (err) {
@@ -592,9 +592,9 @@ export class PostgreSQLClient extends AntaresCore {
* @returns {Array.<Object>} parameters * @returns {Array.<Object>} parameters
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async createTrigger (trigger) { async createTrigger (params) {
const eventsString = Array.isArray(trigger.event) ? trigger.event.join(' OR ') : trigger.event; const eventsString = Array.isArray(params.event) ? params.event.join(' OR ') : params.event;
const sql = `CREATE TRIGGER "${trigger.name}" ${trigger.activation} ${eventsString} ON "${trigger.table}" FOR EACH ROW ${trigger.sql}`; const sql = `CREATE TRIGGER "${params.name}" ${params.activation} ${eventsString} ON "${params.schema}"."${params.table}" FOR EACH ROW ${params.sql}`;
return await this.raw(sql, { split: false }); return await this.raw(sql, { split: false });
} }
@@ -637,6 +637,7 @@ export class PostgreSQLClient extends AntaresCore {
AND proc.routine_type = 'PROCEDURE' AND proc.routine_type = 'PROCEDURE'
AND proc.routine_name = '${routine}' AND proc.routine_name = '${routine}'
AND proc.specific_schema = '${schema}' AND proc.specific_schema = '${schema}'
AND args.data_type != NULL
ORDER BY procedure_schema, ORDER BY procedure_schema,
specific_name, specific_name,
procedure_name, procedure_name,
@@ -675,7 +676,7 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async dropRoutine (params) { async dropRoutine (params) {
const sql = `DROP PROCEDURE ${this._schema}.${params.routine}`; const sql = `DROP PROCEDURE "${params.schema}"."${params.routine}"`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -692,8 +693,8 @@ export class PostgreSQLClient extends AntaresCore {
try { try {
await this.createRoutine(tempProcedure); await this.createRoutine(tempProcedure);
await this.dropRoutine({ routine: tempProcedure.name }); await this.dropRoutine({ schema: routine.schema, routine: tempProcedure.name });
await this.dropRoutine({ routine: routine.oldName }); await this.dropRoutine({ schema: routine.schema, routine: routine.oldName });
await this.createRoutine(routine); await this.createRoutine(routine);
} }
catch (err) { catch (err) {
@@ -715,10 +716,10 @@ export class PostgreSQLClient extends AntaresCore {
}, []).join(',') }, []).join(',')
: ''; : '';
if (this._schema !== 'public') if (routine.schema !== 'public')
await this.use(this._schema); await this.use(routine.schema);
const sql = `CREATE PROCEDURE ${this._schema}.${routine.name}(${parameters}) const sql = `CREATE PROCEDURE "${routine.schema}"."${routine.name}"(${parameters})
LANGUAGE ${routine.language} LANGUAGE ${routine.language}
SECURITY ${routine.security} SECURITY ${routine.security}
AS ${routine.sql}`; AS ${routine.sql}`;
@@ -804,7 +805,7 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async dropFunction (params) { async dropFunction (params) {
const sql = `DROP FUNCTION ${this._schema}.${params.func}`; const sql = `DROP FUNCTION "${params.schema}"."${params.func}"`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -821,8 +822,8 @@ export class PostgreSQLClient extends AntaresCore {
try { try {
await this.createFunction(tempProcedure); await this.createFunction(tempProcedure);
await this.dropFunction({ func: tempProcedure.name }); await this.dropFunction({ schema: func.schema, func: tempProcedure.name });
await this.dropFunction({ func: func.oldName }); await this.dropFunction({ schema: func.schema, func: func.oldName });
await this.createFunction(func); await this.createFunction(func);
} }
catch (err) { catch (err) {
@@ -839,17 +840,17 @@ export class PostgreSQLClient extends AntaresCore {
async createFunction (func) { async createFunction (func) {
const parameters = 'parameters' in func const parameters = 'parameters' in func
? func.parameters.reduce((acc, curr) => { ? func.parameters.reduce((acc, curr) => {
acc.push(`${curr.context} ${curr.name} ${curr.type}${curr.length ? `(${curr.length})` : ''}`); acc.push(`${curr.context} ${curr.name || ''} ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
return acc; return acc;
}, []).join(',') }, []).join(',')
: ''; : '';
if (this._schema !== 'public') if (func.schema !== 'public')
await this.use(this._schema); await this.use(func.schema);
const body = func.returns ? func.sql : '$function$\n$function$'; const body = func.returns ? func.sql : '$function$\n$function$';
const sql = `CREATE FUNCTION ${this._schema}.${func.name}(${parameters}) const sql = `CREATE FUNCTION "${func.schema}"."${func.name}" (${parameters})
RETURNS ${func.returns || 'void'} RETURNS ${func.returns || 'void'}
LANGUAGE ${func.language} LANGUAGE ${func.language}
SECURITY ${func.security} SECURITY ${func.security}
@@ -867,12 +868,12 @@ export class PostgreSQLClient extends AntaresCore {
async alterTriggerFunction (params) { async alterTriggerFunction (params) {
const { func } = params; const { func } = params;
if (this._schema !== 'public') if (func.schema !== 'public')
await this.use(this._schema); await this.use(func.schema);
const body = func.returns ? func.sql : '$function$\n$function$'; const body = func.returns ? func.sql : '$function$\n$function$';
const sql = `CREATE OR REPLACE FUNCTION ${this._schema}.${func.name}() const sql = `CREATE OR REPLACE FUNCTION "${func.schema}"."${func.name}" ()
RETURNS TRIGGER RETURNS TRIGGER
LANGUAGE ${func.language} LANGUAGE ${func.language}
AS ${body}`; AS ${body}`;
@@ -887,12 +888,12 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async createTriggerFunction (func) { async createTriggerFunction (func) {
if (this._schema !== 'public') if (func.schema !== 'public')
await this.use(this._schema); await this.use(func.schema);
const body = func.returns ? func.sql : '$function$\r\nBEGIN\r\n\r\nEND\r\n$function$'; const body = func.returns ? func.sql : '$function$\r\nBEGIN\r\n\r\nEND\r\n$function$';
const sql = `CREATE FUNCTION ${this._schema}.${func.name}() const sql = `CREATE FUNCTION "${func.schema}"."${func.name}" ()
RETURNS TRIGGER RETURNS TRIGGER
LANGUAGE ${func.language} LANGUAGE ${func.language}
AS ${body}`; AS ${body}`;
@@ -988,11 +989,7 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async createTable (params) { async createTable (params) {
const { const sql = `CREATE TABLE "${params.schema}"."${params.name}" (${params.name}_id INTEGER NULL); ALTER TABLE "${params.schema}"."${params.name}" DROP COLUMN ${params.name}_id`;
name
} = params;
const sql = `CREATE TABLE ${this._schema}.${name} (${name}_id INTEGER NULL); ALTER TABLE ${this._schema}.${name} DROP COLUMN ${name}_id`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1006,6 +1003,7 @@ export class PostgreSQLClient extends AntaresCore {
async alterTable (params) { async alterTable (params) {
const { const {
table, table,
schema,
additions, additions,
deletions, deletions,
changes, changes,
@@ -1014,8 +1012,8 @@ export class PostgreSQLClient extends AntaresCore {
options options
} = params; } = params;
if (this._schema !== 'public') if (schema !== 'public')
await this.use(this._schema); await this.use(schema);
let sql = ''; let sql = '';
const alterColumns = []; const alterColumns = [];
@@ -1056,7 +1054,7 @@ export class PostgreSQLClient extends AntaresCore {
else if (type === 'UNIQUE') else if (type === 'UNIQUE')
alterColumns.push(`ADD CONSTRAINT ${addition.name} UNIQUE (${fields})`); alterColumns.push(`ADD CONSTRAINT ${addition.name} UNIQUE (${fields})`);
else else
manageIndexes.push(`CREATE INDEX ${addition.name} ON ${this._schema}.${table}(${fields})`); manageIndexes.push(`CREATE INDEX ${addition.name} ON "${schema}"."${table}" (${fields})`);
}); });
// ADD FOREIGN KEYS // ADD FOREIGN KEYS
@@ -1094,7 +1092,7 @@ export class PostgreSQLClient extends AntaresCore {
} }
if (change.orgName !== change.name) if (change.orgName !== change.name)
renameColumns.push(`ALTER TABLE "${this._schema}"."${table}" RENAME COLUMN "${change.orgName}" TO "${change.name}"`); renameColumns.push(`ALTER TABLE "${schema}"."${table}" RENAME COLUMN "${change.orgName}" TO "${change.name}"`);
}); });
// CHANGE INDEX // CHANGE INDEX
@@ -1112,7 +1110,7 @@ export class PostgreSQLClient extends AntaresCore {
else if (type === 'UNIQUE') else if (type === 'UNIQUE')
alterColumns.push(`ADD CONSTRAINT ${change.name} UNIQUE (${fields})`); alterColumns.push(`ADD CONSTRAINT ${change.name} UNIQUE (${fields})`);
else else
manageIndexes.push(`CREATE INDEX ${change.name} ON ${this._schema}.${table}(${fields})`); manageIndexes.push(`CREATE INDEX ${change.name} ON "${schema}"."${table}" (${fields})`);
}); });
// CHANGE FOREIGN KEYS // CHANGE FOREIGN KEYS
@@ -1139,10 +1137,10 @@ export class PostgreSQLClient extends AntaresCore {
alterColumns.push(`DROP CONSTRAINT ${deletion.constraintName}`); alterColumns.push(`DROP CONSTRAINT ${deletion.constraintName}`);
}); });
if (alterColumns.length) sql += `ALTER TABLE "${this._schema}"."${table}" ${alterColumns.join(', ')}; `; if (alterColumns.length) sql += `ALTER TABLE "${schema}"."${table}" ${alterColumns.join(', ')}; `;
if (createSequences.length) sql = `${createSequences.join(';')}; ${sql}`; if (createSequences.length) sql = `${createSequences.join(';')}; ${sql}`;
if (manageIndexes.length) sql = `${manageIndexes.join(';')}; ${sql}`; if (manageIndexes.length) sql = `${manageIndexes.join(';')}; ${sql}`;
if (options.name) sql += `ALTER TABLE "${this._schema}"."${table}" RENAME TO "${options.name}"; `; if (options.name) sql += `ALTER TABLE "${schema}"."${table}" RENAME TO "${options.name}"; `;
// RENAME // RENAME
if (renameColumns.length) sql = `${renameColumns.join(';')}; ${sql}`; if (renameColumns.length) sql = `${renameColumns.join(';')}; ${sql}`;
@@ -1157,7 +1155,7 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async duplicateTable (params) { async duplicateTable (params) {
const sql = `CREATE TABLE ${this._schema}.${params.table}_copy (LIKE ${this._schema}.${params.table} INCLUDING ALL)`; const sql = `CREATE TABLE ${params.schema}.${params.table}_copy (LIKE ${params.schema}.${params.table} INCLUDING ALL)`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1168,7 +1166,7 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async truncateTable (params) { async truncateTable (params) {
const sql = `TRUNCATE TABLE ${this._schema}.${params.table}`; const sql = `TRUNCATE TABLE ${params.schema}.${params.table}`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1179,7 +1177,7 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async dropTable (params) { async dropTable (params) {
const sql = `DROP TABLE ${this._schema}.${params.table}`; const sql = `DROP TABLE ${params.schema}.${params.table}`;
return await this.raw(sql); return await this.raw(sql);
} }
@@ -1250,18 +1248,20 @@ export class PostgreSQLClient extends AntaresCore {
* @memberof PostgreSQLClient * @memberof PostgreSQLClient
*/ */
async raw (sql, args) { async raw (sql, args) {
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');
args = { args = {
nest: false, nest: false,
details: false, details: false,
split: true, split: true,
comments: true,
...args ...args
}; };
if (args.schema && args.schema !== 'public') if (args.schema && args.schema !== 'public')
await this.use(args.schema); await this.use(args.schema);
if (!args.comments)
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments
const resultsArr = []; const resultsArr = [];
let paramsArr = []; let paramsArr = [];
const queries = args.split const queries = args.split

View File

@@ -16,7 +16,6 @@
<TheNotificationsBoard /> <TheNotificationsBoard />
<TheScratchpad v-if="isScratchpad" /> <TheScratchpad v-if="isScratchpad" />
<ModalSettings v-if="isSettingModal" /> <ModalSettings v-if="isSettingModal" />
<ModalDiscardChanges v-if="isUnsavedDiscardModal" />
<BaseTextEditor class="d-none" value="" /> <BaseTextEditor class="d-none" value="" />
</div> </div>
</div> </div>
@@ -39,7 +38,6 @@ export default {
WorkspaceAddConnectionPanel: () => import(/* webpackChunkName: "WorkspaceAddConnectionPanel" */'@/components/WorkspaceAddConnectionPanel'), WorkspaceAddConnectionPanel: () => import(/* webpackChunkName: "WorkspaceAddConnectionPanel" */'@/components/WorkspaceAddConnectionPanel'),
ModalSettings: () => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings'), ModalSettings: () => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings'),
TheScratchpad: () => import(/* webpackChunkName: "TheScratchpad" */'@/components/TheScratchpad'), TheScratchpad: () => import(/* webpackChunkName: "TheScratchpad" */'@/components/TheScratchpad'),
ModalDiscardChanges: () => import(/* webpackChunkName: "ModalDiscardChanges" */'@/components/ModalDiscardChanges'),
BaseTextEditor: () => import(/* webpackChunkName: "BaseTextEditor" */'@/components/BaseTextEditor') BaseTextEditor: () => import(/* webpackChunkName: "BaseTextEditor" */'@/components/BaseTextEditor')
}, },
data () { data () {

View File

@@ -41,7 +41,7 @@
</form> </form>
</div> </div>
</div> </div>
<div class="modal-footer text-light"> <div class="modal-footer">
<button class="btn btn-primary mr-2" @click.stop="sendCredentials"> <button class="btn btn-primary mr-2" @click.stop="sendCredentials">
{{ $t('word.send') }} {{ $t('word.send') }}
</button> </button>

View File

@@ -2,8 +2,8 @@
<ConfirmModal <ConfirmModal
:confirm-text="$t('word.discard')" :confirm-text="$t('word.discard')"
:cancel-text="$t('word.stay')" :cancel-text="$t('word.stay')"
@confirm="discardUnsavedChanges" @confirm="$emit('confirm')"
@hide="closeUnsavedChangesModal" @hide="$emit('close')"
> >
<template slot="header"> <template slot="header">
<div class="d-flex"> <div class="d-flex">
@@ -19,7 +19,6 @@
</template> </template>
<script> <script>
import { mapActions } from 'vuex';
import ConfirmModal from '@/components/BaseConfirmModal'; import ConfirmModal from '@/components/BaseConfirmModal';
export default { export default {
@@ -34,13 +33,6 @@ export default {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
discardUnsavedChanges: 'workspaces/discardUnsavedChanges',
closeUnsavedChangesModal: 'workspaces/closeUnsavedChangesModal'
}),
closeModal () {
this.$emit('close');
},
onKey (e) { onKey (e) {
e.stopPropagation(); e.stopPropagation();
if (e.key === 'Escape') if (e.key === 'Escape')

View File

@@ -53,7 +53,7 @@
</form> </form>
</div> </div>
</div> </div>
<div class="modal-footer text-light"> <div class="modal-footer">
<button class="btn btn-primary mr-2" @click.stop="updateSchema"> <button class="btn btn-primary mr-2" @click.stop="updateSchema">
{{ $t('word.update') }} {{ $t('word.update') }}
</button> </button>
@@ -72,7 +72,7 @@ import Schema from '@/ipc-api/Schema';
export default { export default {
name: 'ModalEditSchema', name: 'ModalEditSchema',
props: { props: {
selectedDatabase: String selectedSchema: String
}, },
data () { data () {
return { return {
@@ -99,7 +99,7 @@ export default {
async created () { async created () {
let actualCollation; let actualCollation;
try { try {
const { status, response } = await Schema.getDatabaseCollation({ uid: this.selectedWorkspace, database: this.selectedDatabase }); const { status, response } = await Schema.getDatabaseCollation({ uid: this.selectedWorkspace, database: this.selectedSchema });
if (status === 'success') if (status === 'success')
actualCollation = response; actualCollation = response;
@@ -112,8 +112,8 @@ export default {
} }
this.database = { this.database = {
name: this.selectedDatabase, name: this.selectedSchema,
prevName: this.selectedDatabase, prevName: this.selectedSchema,
collation: actualCollation || this.defaultCollation, collation: actualCollation || this.defaultCollation,
prevCollation: actualCollation || this.defaultCollation prevCollation: actualCollation || this.defaultCollation
}; };

View File

@@ -52,7 +52,7 @@
</form> </form>
</div> </div>
</div> </div>
<div class="modal-footer text-light columns"> <div class="modal-footer columns">
<div class="column d-flex" :class="hasFakes ? 'col-4' : 'col-2'"> <div class="column d-flex" :class="hasFakes ? 'col-4' : 'col-2'">
<div class="input-group tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')"> <div class="input-group tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')">
<input <input

View File

@@ -49,7 +49,7 @@
</form> </form>
</div> </div>
</div> </div>
<div class="modal-footer text-light"> <div class="modal-footer">
<button <button
class="btn btn-primary mr-2" class="btn btn-primary mr-2"
:class="{'loading': isLoading}" :class="{'loading': isLoading}"

View File

@@ -86,7 +86,7 @@
</form> </form>
</div> </div>
</div> </div>
<div class="modal-footer text-light"> <div class="modal-footer">
<div class="input-group col-3 tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')"> <div class="input-group col-3 tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')">
<input <input
v-model="nInserts" v-model="nInserts"

View File

@@ -104,6 +104,19 @@
</select> </select>
</div> </div>
</div> </div>
<div class="form-group">
<div class="col-6 col-sm-12">
<label class="form-label">
{{ $t('message.restorePreviourSession') }}
</label>
</div>
<div class="col-6 col-sm-12">
<label class="form-switch d-inline-block" @click.prevent="toggleRestoreSession">
<input type="checkbox" :checked="restoreTabs">
<i class="form-icon" />
</label>
</div>
</div>
<div class="form-group"> <div class="form-group">
<div class="col-6 col-sm-12"> <div class="col-6 col-sm-12">
<label class="form-label"> <label class="form-label">
@@ -369,6 +382,7 @@ export default {
selectedAutoComplete: 'settings/getAutoComplete', selectedAutoComplete: 'settings/getAutoComplete',
selectedLineWrap: 'settings/getLineWrap', selectedLineWrap: 'settings/getLineWrap',
notificationsTimeout: 'settings/getNotificationsTimeout', notificationsTimeout: 'settings/getNotificationsTimeout',
restoreTabs: 'settings/getRestoreTabs',
applicationTheme: 'settings/getApplicationTheme', applicationTheme: 'settings/getApplicationTheme',
editorTheme: 'settings/getEditorTheme', editorTheme: 'settings/getEditorTheme',
editorFontSize: 'settings/getEditorFontSize', editorFontSize: 'settings/getEditorFontSize',
@@ -423,6 +437,7 @@ ORDER BY
closeModal: 'application/hideSettingModal', closeModal: 'application/hideSettingModal',
changeLocale: 'settings/changeLocale', changeLocale: 'settings/changeLocale',
changePageSize: 'settings/changePageSize', changePageSize: 'settings/changePageSize',
changeRestoreTabs: 'settings/changeRestoreTabs',
changeAutoComplete: 'settings/changeAutoComplete', changeAutoComplete: 'settings/changeAutoComplete',
changeLineWrap: 'settings/changeLineWrap', changeLineWrap: 'settings/changeLineWrap',
changeApplicationTheme: 'settings/changeApplicationTheme', changeApplicationTheme: 'settings/changeApplicationTheme',
@@ -447,6 +462,9 @@ ORDER BY
if (e.key === 'Escape') if (e.key === 'Escape')
this.closeModal(); this.closeModal();
}, },
toggleRestoreSession () {
this.changeRestoreTabs(!this.restoreTabs);
},
toggleAutoComplete () { toggleAutoComplete () {
this.changeAutoComplete(!this.selectedAutoComplete); this.changeAutoComplete(!this.selectedAutoComplete);
}, },

View File

@@ -8,21 +8,25 @@
@close-context="isContext = false" @close-context="isContext = false"
/> />
<ul class="settingbar-elements"> <ul class="settingbar-elements">
<draggable v-model="connections"> <Draggable
v-model="connections"
@start="isDragging = true"
@end="dragStop"
>
<li <li
v-for="connection in connections" v-for="connection in connections"
:key="connection.uid" :key="connection.uid"
draggable="true" draggable="true"
class="settingbar-element btn btn-link ex-tooltip" class="settingbar-element btn btn-link ex-tooltip"
:class="{'selected': connection.uid === selectedWorkspace}" :class="{'selected': connection.uid === selectedWorkspace}"
@click="selectWorkspace(connection.uid)" @click.stop="selectWorkspace(connection.uid)"
@contextmenu.prevent="contextMenu($event, connection)" @contextmenu.prevent="contextMenu($event, connection)"
@mouseover.self="tooltipPosition" @mouseover.self="tooltipPosition"
> >
<i class="settingbar-element-icon dbi" :class="`dbi-${connection.client} ${getStatusBadge(connection.uid)}`" /> <i class="settingbar-element-icon dbi" :class="`dbi-${connection.client} ${getStatusBadge(connection.uid)}`" />
<span class="ex-tooltip-content">{{ getConnectionName(connection.uid) }}</span> <span v-if="!isDragging" class="ex-tooltip-content">{{ getConnectionName(connection.uid) }}</span>
</li> </li>
</draggable> </Draggable>
<li <li
class="settingbar-element btn btn-link ex-tooltip" class="settingbar-element btn btn-link ex-tooltip"
:class="{'selected': 'NEW' === selectedWorkspace}" :class="{'selected': 'NEW' === selectedWorkspace}"
@@ -52,19 +56,20 @@
<script> <script>
import { mapActions, mapGetters } from 'vuex'; import { mapActions, mapGetters } from 'vuex';
import draggable from 'vuedraggable'; import Draggable from 'vuedraggable';
import SettingBarContext from '@/components/SettingBarContext'; import SettingBarContext from '@/components/SettingBarContext';
export default { export default {
name: 'TheSettingBar', name: 'TheSettingBar',
components: { components: {
draggable, Draggable,
SettingBarContext SettingBarContext
}, },
data () { data () {
return { return {
dragElement: null, dragElement: null,
isContext: false, isContext: false,
isDragging: false,
contextEvent: null, contextEvent: null,
contextConnection: {}, contextConnection: {},
scale: 0 scale: 0
@@ -106,7 +111,7 @@ export default {
return connection.ask ? '' : `${connection.user + '@'}${connection.host}:${connection.port}`; return connection.ask ? '' : `${connection.user + '@'}${connection.host}:${connection.port}`;
}, },
tooltipPosition (e) { tooltipPosition (e) {
const el = e.target; const el = e.target ? e.target : e;
const fromTop = window.pageYOffset + el.getBoundingClientRect().top - (el.offsetHeight / 4); const fromTop = window.pageYOffset + el.getBoundingClientRect().top - (el.offsetHeight / 4);
el.querySelector('.ex-tooltip-content').style.top = `${fromTop}px`; el.querySelector('.ex-tooltip-content').style.top = `${fromTop}px`;
}, },
@@ -125,6 +130,13 @@ export default {
return ''; return '';
} }
} }
},
dragStop (e) {
this.isDragging = false;
setTimeout(() => {
this.tooltipPosition(e.originalEvent.target.parentNode);
}, 200);
} }
} }
}; };
@@ -235,7 +247,13 @@ export default {
transition: opacity 0.2s; transition: opacity 0.2s;
} }
&:hover .ex-tooltip-content { &.sortable-chosen {
.ex-tooltip-content {
opacity: 0 !important;
}
}
&:hover:not(.selected) .ex-tooltip-content {
visibility: visible; visibility: visible;
opacity: 1; opacity: 1;
} }

View File

@@ -6,12 +6,163 @@
:is-selected="isSelected" :is-selected="isSelected"
/> />
<div v-if="workspace.connection_status === 'connected'" class="workspace-tabs column columns col-gapless"> <div v-if="workspace.connection_status === 'connected'" class="workspace-tabs column columns col-gapless">
<ul <Draggable
id="tabWrap"
ref="tabWrap" ref="tabWrap"
v-model="draggableTabs"
tag="ul"
group="tabs"
class="tab tab-block column col-12" class="tab tab-block column col-12"
draggable=".tab-draggable"
@mouseover.native="addWheelEvent"
> >
<li class="tab-item dropdown tools-dropdown"> <li
v-for="(tab, i) of draggableTabs"
:key="i"
class="tab-item tab-draggable"
draggable="true"
:class="{'active': selectedTab === tab.uid}"
@click="selectTab({uid: workspace.uid, tab: tab.uid})"
@mouseup.middle="closeTab(tab)"
>
<a v-if="tab.type === 'query'" class="tab-link">
<i class="mdi mdi-18px mdi-code-tags mr-1" />
<span>
Query #{{ tab.index }}
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a>
<a
v-else-if="tab.type === 'temp-data'"
class="tab-link"
@dblclick="openAsPermanentTab(tab)"
>
<i class="mdi mdi-18px mr-1" :class="tab.elementType === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
<span :title="`${$t('word.data').toUpperCase()}: ${tab.elementType}`">
<span class=" text-italic">{{ tab.elementName }}</span>
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a>
<a v-else-if="tab.type === 'data'" class="tab-link">
<i class="mdi mdi-18px mr-1" :class="tab.elementType === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
<span :title="`${$t('word.data').toUpperCase()}: ${tab.elementType}`">
{{ tab.elementName }}
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a>
<a
v-else-if="tab.type === 'table-props'"
class="tab-link"
:class="{'badge': tab.isChanged}"
>
<i class="mdi mdi-tune-vertical-variant mdi-18px mr-1" />
<span :title="`${$t('word.settings').toUpperCase()}: ${tab.elementType}`">
{{ tab.elementName }}
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a>
<a
v-else-if="tab.type === 'view-props'"
class="tab-link"
:class="{'badge': tab.isChanged}"
>
<i class="mdi mdi-tune-vertical-variant mdi-18px mr-1" />
<span :title="`${$t('word.settings').toUpperCase()}: ${tab.elementType}`">
{{ tab.elementName }}
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a>
<a
v-else-if="tab.type.includes('temp-')"
class="tab-link"
:class="{'badge': tab.isChanged}"
@dblclick="openAsPermanentTab(tab)"
>
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
<span :title="`${$t('word.settings').toUpperCase()}: ${tab.elementType}`">
<span class=" text-italic">{{ tab.elementName }}</span>
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a>
<a
v-else
class="tab-link"
:class="{'badge': tab.isChanged}"
>
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
<span :title="`${$t('word.settings').toUpperCase()}: ${tab.elementType}`">
{{ tab.elementName }}
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a>
<!-- <a
v-else-if="tab.type === 'temp-trigger-function-props'"
class="tab-link"
:class="{'badge': tab.isChanged}"
@dblclick="openAsPermanentTab(tab)"
>
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
<span :title="`${$t('word.settings').toUpperCase()}: ${tab.elementType}`">
<span class=" text-italic">{{ tab.elementName }}</span>
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a>
<a
v-else-if="tab.type === 'trigger-function-props'"
class="tab-link"
:class="{'badge': tab.isChanged}"
>
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
<span :title="`${$t('word.settings').toUpperCase()}: ${tab.elementType}`">
{{ tab.elementName }}
<span
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab)"
/>
</span>
</a> -->
</li>
<li slot="header" class="tab-item dropdown tools-dropdown">
<a <a
class="tab-link workspace-tools-link dropdown-toggle" class="tab-link workspace-tools-link dropdown-toggle"
tabindex="0" tabindex="0"
@@ -48,113 +199,97 @@
</li> </li>
</ul> </ul>
</li> </li>
<li <li slot="footer" class="tab-item">
v-if="schemaChild && isSettingSupported"
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-vertical-variant mr-1" />
<span :title="schemaChild">{{ $t('word.settings').toUpperCase() }}: {{ schemaChild }}</span>
</a>
</li>
<li
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 mr-1" :class="workspace.breadcrumbs.table ? 'mdi-table' : 'mdi-table-eye'" />
<span :title="schemaChild">{{ $t('word.data').toUpperCase() }}: {{ schemaChild }}</span>
</a>
</li>
<li
v-for="tab of queryTabs"
:key="tab.uid"
class="tab-item"
:class="{'active': selectedTab === tab.uid}"
@click="selectTab({uid: workspace.uid, tab: tab.uid})"
@mouseup.middle="closeTab(tab.uid)"
>
<a class="tab-link">
<i class="mdi mdi-18px mdi-code-tags mr-1" />
<span>
Query #{{ tab.index }}
<span
v-if="queryTabs.length > 1"
class="btn btn-clear"
:title="$t('word.close')"
@click.stop="closeTab(tab.uid)"
/>
</span>
</a>
</li>
<li class="tab-item">
<a <a
class="tab-add" class="tab-add"
:title="$t('message.openNewTab')" :title="$t('message.openNewTab')"
@click="addTab" @click="addQueryTab"
> >
<i class="mdi mdi-24px mdi-plus" /> <i class="mdi mdi-24px mdi-plus" />
</a> </a>
</li> </li>
</ul> </Draggable>
<WorkspacePropsTab <!--<WorkspacePropsTabScheduler
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"
/>
<WorkspacePropsTabTrigger
v-show="selectedTab === 'prop' && workspace.breadcrumbs.trigger"
:is-selected="selectedTab === 'prop'"
:connection="connection"
:trigger="workspace.breadcrumbs.trigger"
/>
<WorkspacePropsTabRoutine
v-show="selectedTab === 'prop' && workspace.breadcrumbs.procedure"
:is-selected="selectedTab === 'prop'"
:connection="connection"
:routine="workspace.breadcrumbs.procedure"
/>
<WorkspacePropsTabFunction
v-show="selectedTab === 'prop' && workspace.breadcrumbs.function"
:is-selected="selectedTab === 'prop'"
:connection="connection"
:function="workspace.breadcrumbs.function"
/>
<WorkspacePropsTabTriggerFunction
v-show="selectedTab === 'prop' && workspace.breadcrumbs.triggerFunction"
:is-selected="selectedTab === 'prop'"
:connection="connection"
:function="workspace.breadcrumbs.triggerFunction"
/>
<WorkspacePropsTabScheduler
v-show="selectedTab === 'prop' && workspace.breadcrumbs.scheduler" v-show="selectedTab === 'prop' && workspace.breadcrumbs.scheduler"
:is-selected="selectedTab === 'prop'" :is-selected="selectedTab === 'prop'"
:connection="connection" :connection="connection"
:scheduler="workspace.breadcrumbs.scheduler" :scheduler="workspace.breadcrumbs.scheduler"
/> /> -->
<WorkspaceTableTab <WorkspaceEmptyState v-if="!workspace.tabs.length" @new-tab="addQueryTab" />
v-show="selectedTab === 'data'" <template v-for="tab of workspace.tabs">
:connection="connection"
:table="workspace.breadcrumbs.table || workspace.breadcrumbs.view"
/>
<WorkspaceQueryTab <WorkspaceQueryTab
v-for="tab of queryTabs" v-if="tab.type==='query'"
:key="tab.uid" :key="tab.uid"
:tab="tab" :tab="tab"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
:connection="connection" :connection="connection"
/> />
<WorkspaceTableTab
v-else-if="['temp-data', 'data'].includes(tab.type)"
:key="tab.uid"
:connection="connection"
:is-selected="selectedTab === tab.uid"
:table="tab.elementName"
:schema="tab.schema"
:element-type="tab.elementType"
/>
<WorkspacePropsTab
v-else-if="tab.type === 'table-props'"
:key="tab.uid"
:connection="connection"
:is-selected="selectedTab === tab.uid"
:table="tab.elementName"
:schema="tab.schema"
/>
<WorkspacePropsTabView
v-else-if="tab.type === 'view-props'"
:key="tab.uid"
:is-selected="selectedTab === tab.uid"
:connection="connection"
:view="tab.elementName"
:schema="tab.schema"
/>
<WorkspacePropsTabTrigger
v-else-if="['temp-trigger-props', 'trigger-props'].includes(tab.type)"
:key="tab.uid"
:connection="connection"
:is-selected="selectedTab === tab.uid"
:trigger="tab.elementName"
:schema="tab.schema"
/>
<WorkspacePropsTabTriggerFunction
v-else-if="['temp-trigger-function-props', 'trigger-function-props'].includes(tab.type)"
:key="tab.uid"
:connection="connection"
:is-selected="selectedTab === tab.uid"
:function="tab.elementName"
:schema="tab.schema"
/>
<WorkspacePropsTabRoutine
v-else-if="['temp-routine-props', 'routine-props'].includes(tab.type)"
:key="tab.uid"
:connection="connection"
:is-selected="selectedTab === tab.uid"
:routine="tab.elementName"
:schema="tab.schema"
/>
<WorkspacePropsTabFunction
v-else-if="['temp-function-props', 'function-props'].includes(tab.type)"
:key="tab.uid"
:connection="connection"
:is-selected="selectedTab === tab.uid"
:function="tab.elementName"
:schema="tab.schema"
/>
<WorkspacePropsTabScheduler
v-else-if="['temp-scheduler-props', 'scheduler-props'].includes(tab.type)"
:key="tab.uid"
:connection="connection"
:is-selected="selectedTab === tab.uid"
:scheduler="tab.elementName"
:schema="tab.schema"
/>
</template>
</div> </div>
<WorkspaceEditConnectionPanel v-else :connection="connection" /> <WorkspaceEditConnectionPanel v-else :connection="connection" />
<ModalProcessesList <ModalProcessesList
@@ -162,12 +297,20 @@
:connection="connection" :connection="connection"
@close="hideProcessesModal" @close="hideProcessesModal"
/> />
<ModalDiscardChanges
v-if="unsavedTab"
@confirm="closeTab(unsavedTab, true)"
@close="unsavedTab = null"
/>
</div> </div>
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { mapGetters, mapActions } from 'vuex';
import Draggable from 'vuedraggable';
import Connection from '@/ipc-api/Connection'; import Connection from '@/ipc-api/Connection';
import WorkspaceEmptyState from '@/components/WorkspaceEmptyState';
import WorkspaceExploreBar from '@/components/WorkspaceExploreBar'; import WorkspaceExploreBar from '@/components/WorkspaceExploreBar';
import WorkspaceEditConnectionPanel from '@/components/WorkspaceEditConnectionPanel'; import WorkspaceEditConnectionPanel from '@/components/WorkspaceEditConnectionPanel';
import WorkspaceQueryTab from '@/components/WorkspaceQueryTab'; import WorkspaceQueryTab from '@/components/WorkspaceQueryTab';
@@ -175,15 +318,18 @@ import WorkspaceTableTab from '@/components/WorkspaceTableTab';
import WorkspacePropsTab from '@/components/WorkspacePropsTab'; import WorkspacePropsTab from '@/components/WorkspacePropsTab';
import WorkspacePropsTabView from '@/components/WorkspacePropsTabView'; import WorkspacePropsTabView from '@/components/WorkspacePropsTabView';
import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger'; import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger';
import WorkspacePropsTabTriggerFunction from '@/components/WorkspacePropsTabTriggerFunction';
import WorkspacePropsTabRoutine from '@/components/WorkspacePropsTabRoutine'; import WorkspacePropsTabRoutine from '@/components/WorkspacePropsTabRoutine';
import WorkspacePropsTabFunction from '@/components/WorkspacePropsTabFunction'; import WorkspacePropsTabFunction from '@/components/WorkspacePropsTabFunction';
import WorkspacePropsTabTriggerFunction from '@/components/WorkspacePropsTabTriggerFunction';
import WorkspacePropsTabScheduler from '@/components/WorkspacePropsTabScheduler'; import WorkspacePropsTabScheduler from '@/components/WorkspacePropsTabScheduler';
import ModalProcessesList from '@/components/ModalProcessesList'; import ModalProcessesList from '@/components/ModalProcessesList';
import ModalDiscardChanges from '@/components/ModalDiscardChanges';
export default { export default {
name: 'Workspace', name: 'Workspace',
components: { components: {
Draggable,
WorkspaceEmptyState,
WorkspaceExploreBar, WorkspaceExploreBar,
WorkspaceEditConnectionPanel, WorkspaceEditConnectionPanel,
WorkspaceQueryTab, WorkspaceQueryTab,
@@ -191,11 +337,12 @@ export default {
WorkspacePropsTab, WorkspacePropsTab,
WorkspacePropsTabView, WorkspacePropsTabView,
WorkspacePropsTabTrigger, WorkspacePropsTabTrigger,
WorkspacePropsTabTriggerFunction,
WorkspacePropsTabRoutine, WorkspacePropsTabRoutine,
WorkspacePropsTabFunction, WorkspacePropsTabFunction,
WorkspacePropsTabTriggerFunction,
WorkspacePropsTabScheduler, WorkspacePropsTabScheduler,
ModalProcessesList ModalProcessesList,
ModalDiscardChanges
}, },
props: { props: {
connection: Object connection: Object
@@ -203,7 +350,8 @@ export default {
data () { data () {
return { return {
hasWheelEvent: false, hasWheelEvent: false,
isProcessesModal: false isProcessesModal: false,
unsavedTab: null
}; };
}, },
computed: { computed: {
@@ -214,6 +362,14 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
draggableTabs: {
get () {
return this.workspace.tabs;
},
set (val) {
this.updateTabs({ uid: this.connection.uid, tabs: val });
}
},
isSelected () { isSelected () {
return this.selectedWorkspace === this.connection.uid; return this.selectedWorkspace === this.connection.uid;
}, },
@@ -228,29 +384,7 @@ export default {
return false; return false;
}, },
selectedTab () { selectedTab () {
if ( return this.workspace.selected_tab;
(
this.workspace.breadcrumbs.table === null &&
this.workspace.breadcrumbs.view === null &&
this.workspace.breadcrumbs.trigger === null &&
this.workspace.breadcrumbs.procedure === null &&
this.workspace.breadcrumbs.function === null &&
this.workspace.breadcrumbs.triggerFunction === null &&
this.workspace.breadcrumbs.scheduler === null &&
['data', 'prop'].includes(this.workspace.selected_tab)
) ||
(
this.workspace.breadcrumbs.table === null &&
this.workspace.breadcrumbs.view === null &&
this.workspace.selected_tab === 'data'
)
)
return this.queryTabs[0].uid;
return this.queryTabs.find(tab => tab.uid === this.workspace.selected_tab) ||
['data', 'prop'].includes(this.workspace.selected_tab)
? this.workspace.selected_tab
: this.queryTabs[0].uid;
}, },
queryTabs () { queryTabs () {
return this.workspace.tabs.filter(tab => tab.type === 'query'); return this.workspace.tabs.filter(tab => tab.type === 'query');
@@ -269,14 +403,6 @@ export default {
if (isInitiated) if (isInitiated)
this.connectWorkspace(this.connection); this.connectWorkspace(this.connection);
}, },
mounted () {
if (this.$refs.tabWrap) {
this.$refs.tabWrap.addEventListener('wheel', e => {
if (e.deltaY > 0) this.$refs.tabWrap.scrollLeft += 50;
else this.$refs.tabWrap.scrollLeft -= 50;
});
}
},
methods: { methods: {
...mapActions({ ...mapActions({
addWorkspace: 'workspaces/addWorkspace', addWorkspace: 'workspaces/addWorkspace',
@@ -284,28 +410,55 @@ export default {
removeConnected: 'workspaces/removeConnected', removeConnected: 'workspaces/removeConnected',
selectTab: 'workspaces/selectTab', selectTab: 'workspaces/selectTab',
newTab: 'workspaces/newTab', newTab: 'workspaces/newTab',
removeTab: 'workspaces/removeTab' removeTab: 'workspaces/removeTab',
updateTabs: 'workspaces/updateTabs'
}), }),
addTab () { addQueryTab () {
this.newTab({ uid: this.connection.uid }); this.newTab({ uid: this.connection.uid, type: 'query' });
if (!this.hasWheelEvent) {
this.$refs.tabWrap.addEventListener('wheel', e => {
if (e.deltaY > 0) this.$refs.tabWrap.scrollLeft += 50;
else this.$refs.tabWrap.scrollLeft -= 50;
});
this.hasWheelEvent = true;
}
}, },
closeTab (tUid) { openAsPermanentTab (tab) {
if (this.queryTabs.length === 1) return; const permanentTabs = {
this.removeTab({ uid: this.connection.uid, tab: tUid }); table: 'data',
view: 'data',
trigger: 'trigger-props',
triggerFunction: 'trigger-function-props',
function: 'function-props',
routine: 'routine-props',
scheduler: 'scheduler-props'
};
this.newTab({
uid: this.connection.uid,
schema: tab.schema,
elementName: tab.elementName,
type: permanentTabs[tab.elementType],
elementType: tab.elementType
});
},
closeTab (tab, force) {
this.unsavedTab = null;
// if (tab.type === 'query' && this.queryTabs.length === 1) return;
if (!force && tab.isChanged) {
this.unsavedTab = tab;
return;
}
this.removeTab({ uid: this.connection.uid, tab: tab.uid });
}, },
showProcessesModal () { showProcessesModal () {
this.isProcessesModal = true; this.isProcessesModal = true;
}, },
hideProcessesModal () { hideProcessesModal () {
this.isProcessesModal = false; this.isProcessesModal = false;
},
addWheelEvent () {
if (!this.hasWheelEvent) {
this.$refs.tabWrap.$el.addEventListener('wheel', e => {
if (e.deltaY > 0) this.$refs.tabWrap.$el.scrollLeft += 50;
else this.$refs.tabWrap.$el.scrollLeft -= 50;
});
this.hasWheelEvent = true;
}
} }
} }
}; };
@@ -334,20 +487,35 @@ export default {
} }
.tab-item { .tab-item {
max-width: 12rem;
width: fit-content; width: fit-content;
flex: initial; flex: initial;
> a { > a {
padding: 0.2rem 0.8rem; padding: 0.2rem 0.6rem;
cursor: pointer; cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
opacity: 0.7; opacity: 0.7;
transition: opacity 0.2s; transition: opacity 0.2s;
&.badge::after {
position: absolute;
right: 35px;
top: 25px;
}
.btn-clear {
margin-left: 0.5rem;
opacity: 0;
transition: opacity 0.2s;
}
&:hover { &:hover {
opacity: 1; opacity: 1;
.btn-clear {
opacity: 1;
}
} }
&.tab-add { &.tab-add {
@@ -366,9 +534,15 @@ export default {
&.active a { &.active a {
opacity: 1; opacity: 1;
.btn-clear {
opacity: 1;
}
} }
&.tools-dropdown { &.tools-dropdown {
height: 34px;
.tab-link:focus { .tab-link:focus {
opacity: 1; opacity: 1;
outline: 0; outline: 0;

View File

@@ -0,0 +1,55 @@
<template>
<div class="column col-12 empty">
<div class="empty-icon">
<img :src="require(`@/images/logo-${applicationTheme}.svg`).default" width="200">
</div>
<p class="h6 empty-subtitle">
{{ $t('message.noOpenTabs') }}
</p>
<div class="empty-action">
<button class="btn btn-gray d-flex" @click="$emit('new-tab')">
<i class="mdi mdi-24px mdi-tab-plus mr-2" />
{{ $t('message.openNewTab') }}
</button>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
name: 'WorkspaceEmptyState',
computed: {
...mapGetters({
applicationTheme: 'settings/getApplicationTheme',
getWorkspace: 'workspaces/getWorkspace',
selectedWorkspace: 'workspaces/getSelected'
}),
workspace () {
return this.getWorkspace(this.selectedWorkspace);
}
},
created () {
this.changeBreadcrumbs({ schema: this.workspace.breadcrumbs.schema });
},
methods: {
...mapActions({
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
})
}
};
</script>
<style scoped>
.empty {
height: 100%;
border-radius: 0;
background: transparent;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-left: auto;
margin-right: auto;
}
</style>

View File

@@ -21,7 +21,7 @@
@click="refresh" @click="refresh"
/> />
<i <i
class="mdi mdi-18px mdi-power-plug-off c-hand" class="mdi mdi-18px mdi-power c-hand"
:title="$t('word.disconnect')" :title="$t('word.disconnect')"
@click="disconnectWorkspace(connection.uid)" @click="disconnectWorkspace(connection.uid)"
/> />
@@ -100,7 +100,7 @@
/> />
<DatabaseContext <DatabaseContext
v-if="isDatabaseContext" v-if="isDatabaseContext"
:selected-database="selectedDatabase" :selected-schema="selectedSchema"
:context-event="databaseContextEvent" :context-event="databaseContextEvent"
@close-context="closeDatabaseContext" @close-context="closeDatabaseContext"
@show-create-table-modal="showCreateTableModal" @show-create-table-modal="showCreateTableModal"
@@ -114,6 +114,7 @@
/> />
<TableContext <TableContext
v-if="isTableContext" v-if="isTableContext"
:selected-schema="selectedSchema"
:selected-table="selectedTable" :selected-table="selectedTable"
:context-event="tableContextEvent" :context-event="tableContextEvent"
@close-context="closeTableContext" @close-context="closeTableContext"
@@ -122,6 +123,7 @@
<MiscContext <MiscContext
v-if="isMiscContext" v-if="isMiscContext"
:selected-misc="selectedMisc" :selected-misc="selectedMisc"
:selected-schema="selectedSchema"
:context-event="miscContextEvent" :context-event="miscContextEvent"
@close-context="closeMiscContext" @close-context="closeMiscContext"
@reload="refresh" @reload="refresh"
@@ -129,6 +131,7 @@
<MiscFolderContext <MiscFolderContext
v-if="isMiscFolderContext" v-if="isMiscFolderContext"
:selected-misc="selectedMisc" :selected-misc="selectedMisc"
:selected-schema="selectedSchema"
:context-event="miscContextEvent" :context-event="miscContextEvent"
@show-create-trigger-modal="showCreateTriggerModal" @show-create-trigger-modal="showCreateTriggerModal"
@show-create-routine-modal="showCreateRoutineModal" @show-create-routine-modal="showCreateRoutineModal"
@@ -211,7 +214,7 @@ export default {
tableContextEvent: null, tableContextEvent: null,
miscContextEvent: null, miscContextEvent: null,
selectedDatabase: '', selectedSchema: '',
selectedTable: null, selectedTable: null,
selectedMisc: null, selectedMisc: null,
searchTerm: '' searchTerm: ''
@@ -271,6 +274,7 @@ export default {
refreshStructure: 'workspaces/refreshStructure', refreshStructure: 'workspaces/refreshStructure',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs', changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
selectTab: 'workspaces/selectTab', selectTab: 'workspaces/selectTab',
newTab: 'workspaces/newTab',
setSearchTerm: 'workspaces/setSearchTerm', setSearchTerm: 'workspaces/setSearchTerm',
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
changeExplorebarSize: 'settings/changeExplorebarSize' changeExplorebarSize: 'settings/changeExplorebarSize'
@@ -308,6 +312,7 @@ export default {
async openCreateTableEditor (payload) { async openCreateTableEditor (payload) {
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.selectedSchema,
...payload ...payload
}; };
@@ -315,14 +320,19 @@ export default {
if (status === 'success') { if (status === 'success') {
await this.refresh(); await this.refresh();
this.changeBreadcrumbs({ schema: this.selectedDatabase, table: payload.name }); this.newTab({
this.selectTab({ uid: this.workspace.uid, tab: 'prop' }); uid: this.workspace.uid,
schema: this.selectedSchema,
elementName: payload.name,
elementType: 'table',
type: 'table-props'
});
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
}, },
openSchemaContext (payload) { openSchemaContext (payload) {
this.selectedDatabase = payload.schema; this.selectedSchema = payload.schema;
this.databaseContextEvent = payload.event; this.databaseContextEvent = payload.event;
this.isDatabaseContext = true; this.isDatabaseContext = true;
}, },
@@ -331,6 +341,7 @@ export default {
}, },
openTableContext (payload) { openTableContext (payload) {
this.selectedTable = payload.table; this.selectedTable = payload.table;
this.selectedSchema = payload.schema;
this.tableContextEvent = payload.event; this.tableContextEvent = payload.event;
this.isTableContext = true; this.isTableContext = true;
}, },
@@ -339,11 +350,13 @@ export default {
}, },
openMiscContext (payload) { openMiscContext (payload) {
this.selectedMisc = payload.misc; this.selectedMisc = payload.misc;
this.selectedSchema = payload.schema;
this.miscContextEvent = payload.event; this.miscContextEvent = payload.event;
this.isMiscContext = true; this.isMiscContext = true;
}, },
openMiscFolderContext (payload) { openMiscFolderContext (payload) {
this.selectedMisc = payload.type; this.selectedMisc = payload.type;
this.selectedSchema = payload.schema;
this.miscContextEvent = payload.event; this.miscContextEvent = payload.event;
this.isMiscFolderContext = true; this.isMiscFolderContext = true;
}, },
@@ -364,6 +377,7 @@ export default {
async openCreateViewEditor (payload) { async openCreateViewEditor (payload) {
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.selectedSchema,
...payload ...payload
}; };
@@ -371,8 +385,15 @@ export default {
if (status === 'success') { if (status === 'success') {
await this.refresh(); await this.refresh();
this.changeBreadcrumbs({ schema: this.selectedDatabase, view: payload.name }); this.changeBreadcrumbs({ schema: this.selectedSchema, view: payload.name });
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
this.newTab({
uid: this.workspace.uid,
schema: this.selectedSchema,
elementName: payload.name,
elementType: 'view',
type: 'view-props'
});
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@@ -388,6 +409,7 @@ export default {
async openCreateTriggerEditor (payload) { async openCreateTriggerEditor (payload) {
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.selectedSchema,
...payload ...payload
}; };
@@ -396,8 +418,15 @@ export default {
if (status === 'success') { if (status === 'success') {
await this.refresh(); await this.refresh();
const triggerName = this.customizations.triggerTableInName ? `${payload.table}.${payload.name}` : payload.name; const triggerName = this.customizations.triggerTableInName ? `${payload.table}.${payload.name}` : payload.name;
this.changeBreadcrumbs({ schema: this.selectedDatabase, trigger: triggerName }); this.changeBreadcrumbs({ schema: this.selectedSchema, trigger: triggerName });
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
this.newTab({
uid: this.workspace.uid,
schema: this.selectedSchema,
elementName: triggerName,
elementType: 'trigger',
type: 'trigger-props'
});
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@@ -413,6 +442,7 @@ export default {
async openCreateRoutineEditor (payload) { async openCreateRoutineEditor (payload) {
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.selectedSchema,
...payload ...payload
}; };
@@ -420,8 +450,15 @@ export default {
if (status === 'success') { if (status === 'success') {
await this.refresh(); await this.refresh();
this.changeBreadcrumbs({ schema: this.selectedDatabase, procedure: payload.name }); this.changeBreadcrumbs({ schema: this.selectedSchema, routine: payload.name });
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
this.newTab({
uid: this.workspace.uid,
schema: this.selectedSchema,
elementName: payload.name,
elementType: 'routine',
type: 'routine-props'
});
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@@ -453,6 +490,7 @@ export default {
async openCreateFunctionEditor (payload) { async openCreateFunctionEditor (payload) {
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.selectedSchema,
...payload ...payload
}; };
@@ -460,8 +498,15 @@ export default {
if (status === 'success') { if (status === 'success') {
await this.refresh(); await this.refresh();
this.changeBreadcrumbs({ schema: this.selectedDatabase, function: payload.name }); this.changeBreadcrumbs({ schema: this.selectedSchema, function: payload.name });
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
this.newTab({
uid: this.workspace.uid,
schema: this.selectedSchema,
elementName: payload.name,
elementType: 'function',
type: 'function-props'
});
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@@ -469,6 +514,7 @@ export default {
async openCreateTriggerFunctionEditor (payload) { async openCreateTriggerFunctionEditor (payload) {
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.selectedSchema,
...payload ...payload
}; };
@@ -476,8 +522,15 @@ export default {
if (status === 'success') { if (status === 'success') {
await this.refresh(); await this.refresh();
this.changeBreadcrumbs({ schema: this.selectedDatabase, triggerFunction: payload.name }); this.changeBreadcrumbs({ schema: this.selectedSchema, triggerFunction: payload.name });
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
this.newTab({
uid: this.workspace.uid,
schema: this.selectedSchema,
elementName: payload.name,
elementType: 'triggerFunction',
type: 'trigger-function-props'
});
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@@ -485,6 +538,7 @@ export default {
async openCreateSchedulerEditor (payload) { async openCreateSchedulerEditor (payload) {
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.selectedSchema,
...payload ...payload
}; };
@@ -492,8 +546,15 @@ export default {
if (status === 'success') { if (status === 'success') {
await this.refresh(); await this.refresh();
this.changeBreadcrumbs({ schema: this.selectedDatabase, scheduler: payload.name }); this.changeBreadcrumbs({ schema: this.selectedSchema, scheduler: payload.name });
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
this.newTab({
uid: this.workspace.uid,
schema: this.selectedSchema,
elementName: payload.name,
elementType: 'scheduler',
type: 'scheduler-props'
});
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });

View File

@@ -59,7 +59,8 @@ export default {
}, },
props: { props: {
contextEvent: MouseEvent, contextEvent: MouseEvent,
selectedMisc: Object selectedMisc: Object,
selectedSchema: String
}, },
data () { data () {
return { return {
@@ -97,6 +98,7 @@ export default {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs', changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
removeTabs: 'workspaces/removeTabs',
newTab: 'workspaces/newTab' newTab: 'workspaces/newTab'
}), }),
showCreateTableModal () { showCreateTableModal () {
@@ -126,12 +128,14 @@ export default {
case 'trigger': case 'trigger':
res = await Triggers.dropTrigger({ res = await Triggers.dropTrigger({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
schema: this.selectedSchema,
trigger: this.selectedMisc.name trigger: this.selectedMisc.name
}); });
break; break;
case 'procedure': case 'procedure':
res = await Routines.dropRoutine({ res = await Routines.dropRoutine({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
schema: this.selectedSchema,
routine: this.selectedMisc.name routine: this.selectedMisc.name
}); });
break; break;
@@ -139,12 +143,14 @@ export default {
case 'triggerFunction': case 'triggerFunction':
res = await Functions.dropFunction({ res = await Functions.dropFunction({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
schema: this.selectedSchema,
func: this.selectedMisc.name func: this.selectedMisc.name
}); });
break; break;
case 'scheduler': case 'scheduler':
res = await Schedulers.dropScheduler({ res = await Schedulers.dropScheduler({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
schema: this.selectedSchema,
scheduler: this.selectedMisc.name scheduler: this.selectedMisc.name
}); });
break; break;
@@ -153,7 +159,12 @@ export default {
const { status, response } = res; const { status, response } = res;
if (status === 'success') { if (status === 'success') {
this.changeBreadcrumbs({ [this.selectedMisc.type]: null }); this.removeTabs({
uid: this.selectedWorkspace,
elementName: this.selectedMisc.name,
elementType: this.selectedMisc.type,
schema: this.selectedSchema
});
this.closeContext(); this.closeContext();
this.$emit('reload'); this.$emit('reload');
@@ -180,8 +191,8 @@ export default {
async runRoutineCheck () { async runRoutineCheck () {
const params = { const params = {
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
schema: this.workspace.breadcrumbs.schema, schema: this.selectedSchema,
routine: this.workspace.breadcrumbs.procedure routine: this.selectedMisc.name
}; };
try { try {
@@ -218,14 +229,14 @@ export default {
sql = `CALL \`${this.localElement.name}\`(${params.join(',')})`; sql = `CALL \`${this.localElement.name}\`(${params.join(',')})`;
} }
this.newTab({ uid: this.workspace.uid, content: sql, autorun: true }); this.newTab({ uid: this.workspace.uid, content: sql, type: 'query', autorun: true });
this.closeContext(); this.closeContext();
}, },
async runFunctionCheck () { async runFunctionCheck () {
const params = { const params = {
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
schema: this.workspace.breadcrumbs.schema, schema: this.selectedSchema,
func: this.workspace.breadcrumbs.function func: this.selectedMisc.name
}; };
try { try {
@@ -263,7 +274,7 @@ export default {
sql = `SELECT \`${this.localElement.name}\` (${params.join(',')})`; sql = `SELECT \`${this.localElement.name}\` (${params.join(',')})`;
} }
this.newTab({ uid: this.workspace.uid, content: sql, autorun: true }); this.newTab({ uid: this.workspace.uid, content: sql, type: 'query', autorun: true });
this.closeContext(); this.closeContext();
} }
} }

View File

@@ -52,7 +52,8 @@ export default {
}, },
props: { props: {
contextEvent: MouseEvent, contextEvent: MouseEvent,
selectedMisc: String selectedMisc: String,
selectedSchema: String
}, },
data () { data () {
return { return {

View File

@@ -19,7 +19,8 @@
:key="table.name" :key="table.name"
class="menu-item" class="menu-item"
:class="{'text-bold': breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name)}" :class="{'text-bold': breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name)}"
@click="setBreadcrumbs({schema: database.name, [table.type]: table.name})" @click="selectTable({schema: database.name, table})"
@dblclick="openDataTab({schema: database.name, table})"
@contextmenu.prevent="showTableContext($event, table)" @contextmenu.prevent="showTableContext($event, table)"
> >
<a class="table-name"> <a class="table-name">
@@ -55,7 +56,8 @@
:key="trigger.name" :key="trigger.name"
class="menu-item" class="menu-item"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name}" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name}"
@click="setBreadcrumbs({schema: database.name, trigger: trigger.name})" @click="selectMisc({schema: database.name, misc: trigger, type: 'trigger'})"
@dblclick="openMiscPermanentTab({schema: database.name, misc: trigger, type: 'trigger'})"
@contextmenu.prevent="showMiscContext($event, {...trigger, type: 'trigger'})" @contextmenu.prevent="showMiscContext($event, {...trigger, type: 'trigger'})"
> >
<a class="table-name"> <a class="table-name">
@@ -73,7 +75,7 @@
<details class="accordion"> <details class="accordion">
<summary <summary
class="accordion-header misc-name" class="accordion-header misc-name"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure}" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.routine}"
@contextmenu.prevent="showMiscFolderContext($event, 'procedure')" @contextmenu.prevent="showMiscFolderContext($event, 'procedure')"
> >
<i class="misc-icon mdi mdi-18px mdi-folder-sync mr-1" /> <i class="misc-icon mdi mdi-18px mdi-folder-sync mr-1" />
@@ -86,8 +88,9 @@
v-for="(procedure, i) of filteredProcedures" v-for="(procedure, i) of filteredProcedures"
:key="`${procedure.name}-${i}`" :key="`${procedure.name}-${i}`"
class="menu-item" class="menu-item"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure === procedure.name}" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.routine === procedure.name}"
@click="setBreadcrumbs({schema: database.name, procedure: procedure.name})" @click="selectMisc({schema: database.name, misc: procedure, type: 'routine'})"
@dblclick="openMiscPermanentTab({schema: database.name, misc: procedure, type: 'routine'})"
@contextmenu.prevent="showMiscContext($event, {...procedure, type: 'procedure'})" @contextmenu.prevent="showMiscContext($event, {...procedure, type: 'procedure'})"
> >
<a class="table-name"> <a class="table-name">
@@ -119,7 +122,8 @@
:key="`${func.name}-${i}`" :key="`${func.name}-${i}`"
class="menu-item" class="menu-item"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.triggerFunction === func.name}" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.triggerFunction === func.name}"
@click="setBreadcrumbs({schema: database.name, triggerFunction: func.name})" @click="selectMisc({schema: database.name, misc: func, type: 'triggerFunction'})"
@dblclick="openMiscPermanentTab({schema: database.name, misc: func, type: 'triggerFunction'})"
@contextmenu.prevent="showMiscContext($event, {...func, type: 'triggerFunction'})" @contextmenu.prevent="showMiscContext($event, {...func, type: 'triggerFunction'})"
> >
<a class="table-name"> <a class="table-name">
@@ -151,7 +155,8 @@
:key="`${func.name}-${i}`" :key="`${func.name}-${i}`"
class="menu-item" class="menu-item"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}"
@click="setBreadcrumbs({schema: database.name, function: func.name})" @click="selectMisc({schema: database.name, misc: func, type: 'function'})"
@dblclick="openMiscPermanentTab({schema: database.name, misc: func, type: 'function'})"
@contextmenu.prevent="showMiscContext($event, {...func, type: 'function'})" @contextmenu.prevent="showMiscContext($event, {...func, type: 'function'})"
> >
<a class="table-name"> <a class="table-name">
@@ -183,7 +188,8 @@
:key="scheduler.name" :key="scheduler.name"
class="menu-item" class="menu-item"
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name}" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name}"
@click="setBreadcrumbs({schema: database.name, scheduler: scheduler.name})" @click="selectMisc({schema: database.name, misc: scheduler, type: 'scheduler'})"
@dblclick="openMiscPermanentTab({schema: database.name, misc: scheduler, type: 'scheduler'})"
@contextmenu.prevent="showMiscContext($event, {...scheduler, type: 'scheduler'})" @contextmenu.prevent="showMiscContext($event, {...scheduler, type: 'scheduler'})"
> >
<a class="table-name"> <a class="table-name">
@@ -267,6 +273,8 @@ export default {
methods: { methods: {
...mapActions({ ...mapActions({
changeBreadcrumbs: 'workspaces/changeBreadcrumbs', changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
addLoadedSchema: 'workspaces/addLoadedSchema',
newTab: 'workspaces/newTab',
refreshSchema: 'workspaces/refreshSchema' refreshSchema: 'workspaces/refreshSchema'
}), }),
formatBytes, formatBytes,
@@ -274,27 +282,68 @@ export default {
if (!this.loadedSchemas.has(schema) && !this.isLoading) { if (!this.loadedSchemas.has(schema) && !this.isLoading) {
this.isLoading = true; this.isLoading = true;
await this.refreshSchema({ uid: this.connection.uid, schema }); await this.refreshSchema({ uid: this.connection.uid, schema });
this.addLoadedSchema(schema);
this.isLoading = false; this.isLoading = false;
} }
this.changeBreadcrumbs({ schema, table: null }); this.changeBreadcrumbs({ schema, table: null });
}, },
selectTable ({ schema, table }) {
this.newTab({ uid: this.connection.uid, elementName: table.name, schema: this.database.name, type: 'temp-data', elementType: table.type });
this.setBreadcrumbs({ schema, [table.type]: table.name });
},
selectMisc ({ schema, misc, type }) {
const miscTempTabs = {
trigger: 'temp-trigger-props',
triggerFunction: 'temp-trigger-function-props',
function: 'temp-function-props',
routine: 'temp-routine-props',
scheduler: 'temp-scheduler-props'
};
this.newTab({
uid: this.connection.uid,
elementName: misc.name,
schema: this.database.name,
type: miscTempTabs[type],
elementType: type
});
this.setBreadcrumbs({ schema, [type]: misc.name });
},
openDataTab ({ schema, table }) {
this.newTab({ uid: this.connection.uid, elementName: table.name, schema: this.database.name, type: 'data', elementType: table.type });
this.setBreadcrumbs({ schema, [table.type]: table.name });
},
openMiscPermanentTab ({ schema, misc, type }) {
const miscTabs = {
trigger: 'trigger-props',
triggerFunction: 'trigger-function-props',
function: 'function-props',
routine: 'routine-props',
scheduler: 'scheduler-props'
};
this.newTab({
uid: this.connection.uid,
elementName: misc.name,
schema: this.database.name,
type: miscTabs[type],
elementType: type
});
this.setBreadcrumbs({ schema, [type]: misc.name });
},
showSchemaContext (event, schema) { showSchemaContext (event, schema) {
this.selectSchema(schema);
this.$emit('show-schema-context', { event, schema }); this.$emit('show-schema-context', { event, schema });
}, },
showTableContext (event, table) { showTableContext (event, table) {
this.setBreadcrumbs({ schema: this.database.name, [table.type]: table.name }); this.$emit('show-table-context', { event, schema: this.database.name, table });
this.$emit('show-table-context', { event, table });
}, },
showMiscContext (event, misc) { showMiscContext (event, misc) {
this.setBreadcrumbs({ schema: this.database.name, [misc.type]: misc.name }); this.$emit('show-misc-context', { event, schema: this.database.name, misc });
this.$emit('show-misc-context', { event, misc });
}, },
showMiscFolderContext (event, type) { showMiscFolderContext (event, type) {
this.selectSchema(this.database.name); this.$emit('show-misc-folder-context', { event, schema: this.database.name, type });
this.setBreadcrumbs({ schema: this.database.name, type });
this.$emit('show-misc-folder-context', { event, type });
}, },
piePercentage (val) { piePercentage (val) {
const perc = val / this.maxSize * 100; const perc = val / this.maxSize * 100;

View File

@@ -82,13 +82,13 @@
</template> </template>
<div slot="body"> <div slot="body">
<div class="mb-2"> <div class="mb-2">
{{ $t('message.deleteCorfirm') }} "<b>{{ selectedDatabase }}</b>"? {{ $t('message.deleteCorfirm') }} "<b>{{ selectedSchema }}</b>"?
</div> </div>
</div> </div>
</ConfirmModal> </ConfirmModal>
<ModalEditSchema <ModalEditSchema
v-if="isEditModal" v-if="isEditModal"
:selected-database="selectedDatabase" :selected-schema="selectedSchema"
@close="hideEditModal" @close="hideEditModal"
/> />
</BaseContextMenu> </BaseContextMenu>
@@ -110,7 +110,7 @@ export default {
}, },
props: { props: {
contextEvent: MouseEvent, contextEvent: MouseEvent,
selectedDatabase: String selectedSchema: String
}, },
data () { data () {
return { return {
@@ -173,11 +173,11 @@ export default {
try { try {
const { status, response } = await Schema.deleteSchema({ const { status, response } = await Schema.deleteSchema({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
database: this.selectedDatabase database: this.selectedSchema
}); });
if (status === 'success') { if (status === 'success') {
if (this.selectedDatabase === this.workspace.breadcrumbs.schema) if (this.selectedSchema === this.workspace.breadcrumbs.schema)
this.changeBreadcrumbs({ schema: null }); this.changeBreadcrumbs({ schema: null });
this.closeContext(); this.closeContext();

View File

@@ -3,6 +3,20 @@
:context-event="contextEvent" :context-event="contextEvent"
@close-context="closeContext" @close-context="closeContext"
> >
<div
v-if="selectedTable.type === 'table' && workspace.customizations.tableSettings"
class="context-element"
@click="openTableSettingTab"
>
<span class="d-flex"><i class="mdi mdi-18px mdi-tune-vertical-variant text-light pr-1" /> {{ $t('word.settings') }}</span>
</div>
<div
v-if="selectedTable.type === 'view' && workspace.customizations.viewSettings"
class="context-element"
@click="openViewSettingTab"
>
<span class="d-flex"><i class="mdi mdi-18px mdi-tune-vertical-variant text-light pr-1" /> {{ $t('word.settings') }}</span>
</div>
<div <div
v-if="selectedTable.type === 'table'" v-if="selectedTable.type === 'table'"
class="context-element" class="context-element"
@@ -72,7 +86,8 @@ export default {
}, },
props: { props: {
contextEvent: MouseEvent, contextEvent: MouseEvent,
selectedTable: Object selectedTable: Object,
selectedSchema: String
}, },
data () { data () {
return { return {
@@ -92,6 +107,8 @@ export default {
methods: { methods: {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
newTab: 'workspaces/newTab',
removeTabs: 'workspaces/removeTabs',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs' changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
}), }),
showCreateTableModal () { showCreateTableModal () {
@@ -112,11 +129,44 @@ export default {
closeContext () { closeContext () {
this.$emit('close-context'); this.$emit('close-context');
}, },
openTableSettingTab () {
this.newTab({
uid: this.selectedWorkspace,
elementName: this.selectedTable.name,
schema: this.selectedSchema,
type: 'table-props',
elementType: 'table'
});
this.changeBreadcrumbs({
schema: this.selectedSchema,
table: this.selectedTable.name
});
this.closeContext();
},
openViewSettingTab () {
this.newTab({
uid: this.selectedWorkspace,
elementType: 'table',
elementName: this.selectedTable.name,
schema: this.selectedSchema,
type: 'view-props'
});
this.changeBreadcrumbs({
schema: this.selectedSchema,
view: this.selectedTable.name
});
this.closeContext();
},
async duplicateTable () { async duplicateTable () {
try { try {
const { status, response } = await Tables.duplicateTable({ const { status, response } = await Tables.duplicateTable({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
table: this.selectedTable.name table: this.selectedTable.name,
schema: this.selectedSchema
}); });
if (status === 'success') { if (status === 'success') {
@@ -134,13 +184,11 @@ export default {
try { try {
const { status, response } = await Tables.truncateTable({ const { status, response } = await Tables.truncateTable({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
table: this.selectedTable.name table: this.selectedTable.name,
schema: this.selectedSchema
}); });
if (status === 'success') { if (status === 'success') {
if (this.selectedTable.name === this.workspace.breadcrumbs.table)
this.changeBreadcrumbs({ table: null });
this.closeContext(); this.closeContext();
this.$emit('reload'); this.$emit('reload');
} }
@@ -158,21 +206,27 @@ export default {
if (this.selectedTable.type === 'table') { if (this.selectedTable.type === 'table') {
res = await Tables.dropTable({ res = await Tables.dropTable({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
table: this.selectedTable.name table: this.selectedTable.name,
schema: this.selectedSchema
}); });
} }
else if (this.selectedTable.type === 'view') { else if (this.selectedTable.type === 'view') {
res = await Views.dropView({ res = await Views.dropView({
uid: this.selectedWorkspace, uid: this.selectedWorkspace,
view: this.selectedTable.name view: this.selectedTable.name,
schema: this.selectedSchema
}); });
} }
const { status, response } = res; const { status, response } = res;
if (status === 'success') { if (status === 'success') {
if (this.selectedTable.name === this.workspace.breadcrumbs.table || this.selectedTable.name === this.workspace.breadcrumbs.view) this.removeTabs({
this.changeBreadcrumbs({ table: null, view: null }); uid: this.selectedWorkspace,
elementName: this.selectedTable.name,
elementType: this.selectedTable.type,
schema: this.selectedSchema
});
this.closeContext(); this.closeContext();
this.$emit('reload'); this.$emit('reload');

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="workspace-query-tab column col-12 columns col-gapless"> <div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<div class="workspace-query-runner-footer"> <div class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
@@ -50,6 +50,11 @@
<span>{{ $t('word.options') }}</span> <span>{{ $t('word.options') }}</span>
</button> </button>
</div> </div>
<div class="workspace-query-info">
<div class="d-flex" :title="$t('word.schema')">
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
</div>
</div>
</div> </div>
</div> </div>
<div class="workspace-query-results column col-12 p-relative"> <div class="workspace-query-results column col-12 p-relative">
@@ -126,11 +131,12 @@ export default {
}, },
props: { props: {
connection: Object, connection: Object,
table: String isSelected: Boolean,
table: String,
schema: String
}, },
data () { data () {
return { return {
tabUid: 'prop',
isLoading: false, isLoading: false,
isSaving: false, isSaving: false,
isOptionsModal: false, isOptionsModal: false,
@@ -157,6 +163,9 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
tabUid () {
return this.$vnode.key;
},
tableOptions () { tableOptions () {
const db = this.workspace.structure.find(db => db.name === this.schema); const db = this.workspace.structure.find(db => db.name === this.schema);
return db && this.table ? db.tables.find(table => table.name === this.table) : {}; return db && this.table ? db.tables.find(table => table.name === this.table) : {};
@@ -164,12 +173,6 @@ export default {
defaultEngine () { defaultEngine () {
return this.getDatabaseVariable(this.connection.uid, 'default_storage_engine').value || ''; return this.getDatabaseVariable(this.connection.uid, 'default_storage_engine').value || '';
}, },
isSelected () {
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.table;
},
schema () {
return this.workspace.breadcrumbs.schema;
},
schemaTables () { schemaTables () {
const schemaTables = this.workspace.structure const schemaTables = this.workspace.structure
.filter(schema => schema.name === this.schema) .filter(schema => schema.name === this.schema)
@@ -185,6 +188,12 @@ export default {
} }
}, },
watch: { watch: {
schema () {
if (this.isSelected) {
this.getFieldsData();
this.lastTable = this.table;
}
},
table () { table () {
if (this.isSelected) { if (this.isSelected) {
this.getFieldsData(); this.getFieldsData();
@@ -192,17 +201,19 @@ export default {
} }
}, },
isSelected (val) { isSelected (val) {
if (val && this.lastTable !== this.table) { if (val) {
this.changeBreadcrumbs({ schema: this.schema, table: this.table });
if (this.lastTable !== this.table)
this.getFieldsData(); this.getFieldsData();
this.lastTable = this.table;
} }
}, },
isChanged (val) { isChanged (val) {
if (this.isSelected && this.lastTable === this.table && this.table !== null) this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
this.setUnsavedChanges(val);
} }
}, },
created () { created () {
this.getFieldsData();
window.addEventListener('keydown', this.onKey); window.addEventListener('keydown', this.onKey);
}, },
beforeDestroy () { beforeDestroy () {
@@ -213,20 +224,25 @@ export default {
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure', refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges', setUnsavedChanges: 'workspaces/setUnsavedChanges',
renameTabs: 'workspaces/renameTabs',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs' changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
}), }),
async getFieldsData () { async getFieldsData () {
if (!this.table) return; if (!this.table) return;
this.localFields = []; this.localFields = [];
this.lastTable = this.table;
this.newFieldsCounter = 0; this.newFieldsCounter = 0;
this.isLoading = true; this.isLoading = true;
try {
this.localOptions = JSON.parse(JSON.stringify(this.tableOptions)); this.localOptions = JSON.parse(JSON.stringify(this.tableOptions));
}
catch (err) {}
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.schema,
table: this.workspace.breadcrumbs.table table: this.table
}; };
try { // Columns data try { // Columns data
@@ -410,7 +426,7 @@ export default {
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.schema,
table: this.workspace.breadcrumbs.table, table: this.table,
additions, additions,
changes, changes,
deletions, deletions,
@@ -428,10 +444,17 @@ export default {
await this.refreshStructure(this.connection.uid); await this.refreshStructure(this.connection.uid);
if (oldName !== this.localOptions.name) { if (oldName !== this.localOptions.name) {
this.setUnsavedChanges(false); this.renameTabs({
uid: this.connection.uid,
schema: this.schema,
elementName: oldName,
elementNewName: this.localOptions.name,
elementType: 'table'
});
this.changeBreadcrumbs({ schema: this.schema, table: this.localOptions.name }); this.changeBreadcrumbs({ schema: this.schema, table: this.localOptions.name });
} }
else
this.getFieldsData(); this.getFieldsData();
} }
else else

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="workspace-query-tab column col-12 columns col-gapless"> <div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<div class="workspace-query-runner-footer"> <div class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
@@ -42,6 +42,11 @@
<span>{{ $t('word.options') }}</span> <span>{{ $t('word.options') }}</span>
</button> </button>
</div> </div>
<div class="workspace-query-info">
<div class="d-flex" :title="$t('word.schema')">
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
</div>
</div>
</div> </div>
</div> </div>
<div class="workspace-query-results column col-12 mt-2 p-relative"> <div class="workspace-query-results column col-12 mt-2 p-relative">
@@ -102,11 +107,12 @@ export default {
}, },
props: { props: {
connection: Object, connection: Object,
function: String function: String,
isSelected: Boolean,
schema: String
}, },
data () { data () {
return { return {
tabUid: 'prop',
isLoading: false, isLoading: false,
isSaving: false, isSaving: false,
isOptionsModal: false, isOptionsModal: false,
@@ -127,11 +133,8 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () { tabUid () {
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.function; return this.$vnode.key;
},
schema () {
return this.workspace.breadcrumbs.schema;
}, },
isChanged () { isChanged () {
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction); return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
@@ -150,6 +153,13 @@ export default {
} }
}, },
watch: { watch: {
async schema () {
if (this.isSelected) {
await this.getFunctionData();
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
this.lastFunction = this.function;
}
},
async function () { async function () {
if (this.isSelected) { if (this.isSelected) {
await this.getFunctionData(); await this.getFunctionData();
@@ -158,26 +168,28 @@ export default {
} }
}, },
async isSelected (val) { async isSelected (val) {
if (val && this.lastFunction !== this.function) { if (val) {
await this.getFunctionData(); this.changeBreadcrumbs({ schema: this.schema, function: this.function });
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
this.lastFunction = this.function; if (this.lastFunction !== this.function)
this.getRoutineData();
} }
}, },
isChanged (val) { isChanged (val) {
if (this.isSelected && this.lastFunction === this.function && this.function !== null) this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
this.setUnsavedChanges(val);
} }
}, },
async created () {
await this.getFunctionData();
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
window.addEventListener('keydown', this.onKey);
},
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () { beforeDestroy () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
@@ -185,20 +197,22 @@ export default {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure', refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges', renameTabs: 'workspaces/renameTabs',
newTab: 'workspaces/newTab',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs', changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
newTab: 'workspaces/newTab' setUnsavedChanges: 'workspaces/setUnsavedChanges'
}), }),
async getFunctionData () { async getFunctionData () {
if (!this.function) return; if (!this.function) return;
this.isLoading = true; this.isLoading = true;
this.localFunction = { sql: '' }; this.localFunction = { sql: '' };
this.lastFunction = this.function;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.schema,
func: this.workspace.breadcrumbs.function func: this.function
}; };
try { try {
@@ -229,9 +243,9 @@ export default {
this.isSaving = true; this.isSaving = true;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema,
func: { func: {
...this.localFunction, ...this.localFunction,
schema: this.schema,
oldName: this.originalFunction.name oldName: this.originalFunction.name
} }
}; };
@@ -245,10 +259,17 @@ export default {
await this.refreshStructure(this.connection.uid); await this.refreshStructure(this.connection.uid);
if (oldName !== this.localFunction.name) { if (oldName !== this.localFunction.name) {
this.setUnsavedChanges(false); this.renameTabs({
uid: this.connection.uid,
schema: this.schema,
elementName: oldName,
elementNewName: this.localFunction.name,
elementType: 'function'
});
this.changeBreadcrumbs({ schema: this.schema, function: this.localFunction.name }); this.changeBreadcrumbs({ schema: this.schema, function: this.localFunction.name });
} }
else
this.getFunctionData(); this.getFunctionData();
} }
else else
@@ -303,7 +324,7 @@ export default {
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`; sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
} }
this.newTab({ uid: this.connection.uid, content: sql, autorun: true }); this.newTab({ uid: this.connection.uid, content: sql, type: 'query', autorun: true });
}, },
showOptionsModal () { showOptionsModal () {
this.isOptionsModal = true; this.isOptionsModal = true;

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="workspace-query-tab column col-12 columns col-gapless"> <div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<div class="workspace-query-runner-footer"> <div class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
@@ -42,6 +42,11 @@
<span>{{ $t('word.options') }}</span> <span>{{ $t('word.options') }}</span>
</button> </button>
</div> </div>
<div class="workspace-query-info">
<div class="d-flex" :title="$t('word.schema')">
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
</div>
</div>
</div> </div>
</div> </div>
<div class="workspace-query-results column col-12 mt-2 p-relative"> <div class="workspace-query-results column col-12 mt-2 p-relative">
@@ -103,11 +108,12 @@ export default {
}, },
props: { props: {
connection: Object, connection: Object,
routine: String routine: String,
isSelected: Boolean,
schema: String
}, },
data () { data () {
return { return {
tabUid: 'prop',
isLoading: false, isLoading: false,
isSaving: false, isSaving: false,
isOptionsModal: false, isOptionsModal: false,
@@ -128,11 +134,8 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () { tabUid () {
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.routine; return this.$vnode.key;
},
schema () {
return this.workspace.breadcrumbs.schema;
}, },
isChanged () { isChanged () {
return JSON.stringify(this.originalRoutine) !== JSON.stringify(this.localRoutine); return JSON.stringify(this.originalRoutine) !== JSON.stringify(this.localRoutine);
@@ -149,6 +152,13 @@ export default {
} }
}, },
watch: { watch: {
async schema () {
if (this.isSelected) {
await this.getRoutineData();
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
this.lastRoutine = this.routine;
}
},
async routine () { async routine () {
if (this.isSelected) { if (this.isSelected) {
await this.getRoutineData(); await this.getRoutineData();
@@ -157,26 +167,28 @@ export default {
} }
}, },
async isSelected (val) { async isSelected (val) {
if (val && this.lastRoutine !== this.routine) { if (val) {
await this.getRoutineData(); this.changeBreadcrumbs({ schema: this.schema, routine: this.routine });
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
this.lastRoutine = this.routine; if (this.lastRoutine !== this.routine)
this.getRoutineData();
} }
}, },
isChanged (val) { isChanged (val) {
if (this.isSelected && this.lastRoutine === this.routine && this.routine !== null) this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
this.setUnsavedChanges(val);
} }
}, },
async created () {
await this.getRoutineData();
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
window.addEventListener('keydown', this.onKey);
},
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () { beforeDestroy () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
@@ -184,19 +196,22 @@ export default {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure', refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges', renameTabs: 'workspaces/renameTabs',
newTab: 'workspaces/newTab',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs', changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
newTab: 'workspaces/newTab' setUnsavedChanges: 'workspaces/setUnsavedChanges'
}), }),
async getRoutineData () { async getRoutineData () {
if (!this.routine) return; if (!this.routine) return;
this.localRoutine = { sql: '' }; this.localRoutine = { sql: '' };
this.isLoading = true; this.isLoading = true;
this.lastRoutine = this.routine;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.schema,
routine: this.workspace.breadcrumbs.procedure routine: this.routine
}; };
try { try {
@@ -227,9 +242,9 @@ export default {
this.isSaving = true; this.isSaving = true;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema,
routine: { routine: {
...this.localRoutine, ...this.localRoutine,
schema: this.schema,
oldName: this.originalRoutine.name oldName: this.originalRoutine.name
} }
}; };
@@ -243,10 +258,17 @@ export default {
await this.refreshStructure(this.connection.uid); await this.refreshStructure(this.connection.uid);
if (oldName !== this.localRoutine.name) { if (oldName !== this.localRoutine.name) {
this.setUnsavedChanges(false); this.renameTabs({
uid: this.connection.uid,
schema: this.schema,
elementName: oldName,
elementNewName: this.localRoutine.name,
elementType: 'procedure'
});
this.changeBreadcrumbs({ schema: this.schema, procedure: this.localRoutine.name }); this.changeBreadcrumbs({ schema: this.schema, procedure: this.localRoutine.name });
} }
else
this.getRoutineData(); this.getRoutineData();
} }
else else
@@ -299,7 +321,7 @@ export default {
sql = `CALL \`${this.originalRoutine.name}\`(${params.join(',')})`; sql = `CALL \`${this.originalRoutine.name}\`(${params.join(',')})`;
} }
this.newTab({ uid: this.connection.uid, content: sql, autorun: true }); this.newTab({ uid: this.connection.uid, content: sql, type: 'query', autorun: true });
}, },
showOptionsModal () { showOptionsModal () {
this.isOptionsModal = true; this.isOptionsModal = true;

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="workspace-query-tab column col-12 columns col-gapless"> <div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<div class="workspace-query-runner-footer"> <div class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
@@ -29,6 +29,11 @@
<span>{{ $t('word.timing') }}</span> <span>{{ $t('word.timing') }}</span>
</button> </button>
</div> </div>
<div class="workspace-query-info">
<div class="d-flex" :title="$t('word.schema')">
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
</div>
</div>
</div> </div>
</div> </div>
<div class="container"> <div class="container">
@@ -153,11 +158,12 @@ export default {
}, },
props: { props: {
connection: Object, connection: Object,
scheduler: String scheduler: String,
isSelected: Boolean,
schema: String
}, },
data () { data () {
return { return {
tabUid: 'prop',
isLoading: false, isLoading: false,
isSaving: false, isSaving: false,
isTimingModal: false, isTimingModal: false,
@@ -176,11 +182,8 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () { tabUid () {
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.scheduler; return this.$vnode.key;
},
schema () {
return this.workspace.breadcrumbs.schema;
}, },
isChanged () { isChanged () {
return JSON.stringify(this.originalScheduler) !== JSON.stringify(this.localScheduler); return JSON.stringify(this.originalScheduler) !== JSON.stringify(this.localScheduler);
@@ -197,6 +200,13 @@ export default {
} }
}, },
watch: { watch: {
async schema () {
if (this.isSelected) {
await this.getSchedulerData();
this.$refs.queryEditor.editor.session.setValue(this.localScheduler.sql);
this.lastScheduler = this.scheduler;
}
},
async scheduler () { async scheduler () {
if (this.isSelected) { if (this.isSelected) {
await this.getSchedulerData(); await this.getSchedulerData();
@@ -205,26 +215,28 @@ export default {
} }
}, },
async isSelected (val) { async isSelected (val) {
if (val && this.lastScheduler !== this.scheduler) { if (val) {
await this.getSchedulerData(); this.changeBreadcrumbs({ schema: this.schema, scheduler: this.scheduler });
this.$refs.queryEditor.editor.session.setValue(this.localScheduler.sql);
this.lastScheduler = this.scheduler; if (this.lastScheduler !== this.scheduler)
this.getSchedulerData();
} }
}, },
isChanged (val) { isChanged (val) {
if (this.isSelected && this.lastScheduler === this.scheduler && this.scheduler !== null) this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
this.setUnsavedChanges(val);
} }
}, },
async created () {
await this.getSchedulerData();
this.$refs.queryEditor.editor.session.setValue(this.localScheduler.sql);
window.addEventListener('keydown', this.onKey);
},
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () { beforeDestroy () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
@@ -232,17 +244,21 @@ export default {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure', refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges', renameTabs: 'workspaces/renameTabs',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs' newTab: 'workspaces/newTab',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
setUnsavedChanges: 'workspaces/setUnsavedChanges'
}), }),
async getSchedulerData () { async getSchedulerData () {
if (!this.scheduler) return; if (!this.scheduler) return;
this.isLoading = true; this.isLoading = true;
this.lastScheduler = this.scheduler;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.schema,
scheduler: this.workspace.breadcrumbs.scheduler scheduler: this.scheduler
}; };
try { try {
@@ -267,9 +283,9 @@ export default {
this.isSaving = true; this.isSaving = true;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema,
scheduler: { scheduler: {
...this.localScheduler, ...this.localScheduler,
schema: this.schema,
oldName: this.originalScheduler.name oldName: this.originalScheduler.name
} }
}; };
@@ -283,10 +299,17 @@ export default {
await this.refreshStructure(this.connection.uid); await this.refreshStructure(this.connection.uid);
if (oldName !== this.localScheduler.name) { if (oldName !== this.localScheduler.name) {
this.setUnsavedChanges(false); this.renameTabs({
uid: this.connection.uid,
schema: this.schema,
elementName: oldName,
elementNewName: this.localScheduler.name,
elementType: 'scheduler'
});
this.changeBreadcrumbs({ schema: this.schema, scheduler: this.localScheduler.name }); this.changeBreadcrumbs({ schema: this.schema, scheduler: this.localScheduler.name });
} }
else
this.getSchedulerData(); this.getSchedulerData();
} }
else else

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="workspace-query-tab column col-12 columns col-gapless"> <div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<div class="workspace-query-runner-footer"> <div class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
@@ -23,6 +23,11 @@
<span>{{ $t('word.clear') }}</span> <span>{{ $t('word.clear') }}</span>
</button> </button>
</div> </div>
<div class="workspace-query-info">
<div class="d-flex" :title="$t('word.schema')">
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
</div>
</div>
</div> </div>
</div> </div>
<div class="container"> <div class="container">
@@ -139,11 +144,12 @@ export default {
}, },
props: { props: {
connection: Object, connection: Object,
trigger: String trigger: String,
isSelected: Boolean,
schema: String
}, },
data () { data () {
return { return {
tabUid: 'prop',
isLoading: false, isLoading: false,
isSaving: false, isSaving: false,
originalTrigger: null, originalTrigger: null,
@@ -162,15 +168,12 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
tabUid () {
return this.$vnode.key;
},
customizations () { customizations () {
return this.workspace.customizations; return this.workspace.customizations;
}, },
isSelected () {
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.trigger;
},
schema () {
return this.workspace.breadcrumbs.schema;
},
isChanged () { isChanged () {
return JSON.stringify(this.originalTrigger) !== JSON.stringify(this.localTrigger); return JSON.stringify(this.originalTrigger) !== JSON.stringify(this.localTrigger);
}, },
@@ -186,6 +189,13 @@ export default {
} }
}, },
watch: { watch: {
async schema () {
if (this.isSelected) {
await this.getTriggerData();
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
this.lastTrigger = this.trigger;
}
},
async trigger () { async trigger () {
if (this.isSelected) { if (this.isSelected) {
await this.getTriggerData(); await this.getTriggerData();
@@ -194,26 +204,28 @@ export default {
} }
}, },
async isSelected (val) { async isSelected (val) {
if (val && this.lastTrigger !== this.trigger) { if (val) {
await this.getTriggerData(); this.changeBreadcrumbs({ schema: this.schema, trigger: this.trigger });
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
this.lastTrigger = this.trigger; if (this.lastTrigger !== this.trigger)
this.getTriggerData();
} }
}, },
isChanged (val) { isChanged (val) {
if (this.isSelected && this.lastTrigger === this.trigger && this.trigger !== null) this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
this.setUnsavedChanges(val);
} }
}, },
async created () {
await this.getTriggerData();
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
window.addEventListener('keydown', this.onKey);
},
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () { beforeDestroy () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
@@ -221,8 +233,10 @@ export default {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure', refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges', renameTabs: 'workspaces/renameTabs',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs' newTab: 'workspaces/newTab',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
setUnsavedChanges: 'workspaces/setUnsavedChanges'
}), }),
async getTriggerData () { async getTriggerData () {
if (!this.trigger) return; if (!this.trigger) return;
@@ -233,6 +247,7 @@ export default {
this.localTrigger = { sql: '' }; this.localTrigger = { sql: '' };
this.isLoading = true; this.isLoading = true;
this.lastTrigger = this.trigger;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
@@ -278,9 +293,9 @@ export default {
this.isSaving = true; this.isSaving = true;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema,
trigger: { trigger: {
...this.localTrigger, ...this.localTrigger,
schema: this.schema,
oldName: this.originalTrigger.name oldName: this.originalTrigger.name
} }
}; };
@@ -289,16 +304,23 @@ export default {
const { status, response } = await Triggers.alterTrigger(params); const { status, response } = await Triggers.alterTrigger(params);
if (status === 'success') { if (status === 'success') {
const oldName = this.originalTrigger.name;
await this.refreshStructure(this.connection.uid); await this.refreshStructure(this.connection.uid);
if (oldName !== this.localTrigger.name) { if (this.originalTrigger.name !== this.localTrigger.name) {
this.setUnsavedChanges(false);
const triggerName = this.customizations.triggerTableInName ? `${this.localTrigger.table}.${this.localTrigger.name}` : this.localTrigger.name; const triggerName = this.customizations.triggerTableInName ? `${this.localTrigger.table}.${this.localTrigger.name}` : this.localTrigger.name;
const triggerOldName = this.customizations.triggerTableInName ? `${this.originalTrigger.table}.${this.originalTrigger.name}` : this.originalTrigger.name;
this.renameTabs({
uid: this.connection.uid,
schema: this.schema,
elementName: triggerOldName,
elementNewName: triggerName,
elementType: 'trigger'
});
this.changeBreadcrumbs({ schema: this.schema, trigger: triggerName }); this.changeBreadcrumbs({ schema: this.schema, trigger: triggerName });
} }
else
this.getTriggerData(); this.getTriggerData();
} }
else else

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="workspace-query-tab column col-12 columns col-gapless"> <div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<div class="workspace-query-runner-footer"> <div class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
@@ -80,11 +80,12 @@ export default {
}, },
props: { props: {
connection: Object, connection: Object,
function: String function: String,
isSelected: Boolean,
schema: String
}, },
data () { data () {
return { return {
tabUid: 'prop',
isLoading: false, isLoading: false,
isSaving: false, isSaving: false,
isOptionsModal: false, isOptionsModal: false,
@@ -105,11 +106,8 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () { tabUid () {
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.function; return this.$vnode.key;
},
schema () {
return this.workspace.breadcrumbs.schema;
}, },
isChanged () { isChanged () {
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction); return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
@@ -128,6 +126,13 @@ export default {
} }
}, },
watch: { watch: {
async schema () {
if (this.isSelected) {
await this.getFunctionData();
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
this.lastFunction = this.function;
}
},
async function () { async function () {
if (this.isSelected) { if (this.isSelected) {
await this.getFunctionData(); await this.getFunctionData();
@@ -136,26 +141,28 @@ export default {
} }
}, },
async isSelected (val) { async isSelected (val) {
if (val && this.lastFunction !== this.function) { if (val) {
this.changeBreadcrumbs({ schema: this.schema, triggerFunction: this.function });
if (this.lastFunction !== this.function)
await this.getFunctionData(); await this.getFunctionData();
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
this.lastFunction = this.function;
} }
}, },
isChanged (val) { isChanged (val) {
if (this.isSelected && this.lastFunction === this.function && this.function !== null) this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
this.setUnsavedChanges(val);
} }
}, },
async created () {
await this.getFunctionData();
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
window.addEventListener('keydown', this.onKey);
},
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () { beforeDestroy () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
@@ -163,15 +170,17 @@ export default {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure', refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges', renameTabs: 'workspaces/renameTabs',
newTab: 'workspaces/newTab',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs', changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
newTab: 'workspaces/newTab' setUnsavedChanges: 'workspaces/setUnsavedChanges'
}), }),
async getFunctionData () { async getFunctionData () {
if (!this.function) return; if (!this.function) return;
this.isLoading = true; this.isLoading = true;
this.localFunction = { sql: '' }; this.localFunction = { sql: '' };
this.lastFunction = this.function;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
@@ -207,9 +216,9 @@ export default {
this.isSaving = true; this.isSaving = true;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema,
func: { func: {
...this.localFunction, ...this.localFunction,
schema: this.schema,
oldName: this.originalFunction.name oldName: this.originalFunction.name
} }
}; };
@@ -223,10 +232,17 @@ export default {
await this.refreshStructure(this.connection.uid); await this.refreshStructure(this.connection.uid);
if (oldName !== this.localFunction.name) { if (oldName !== this.localFunction.name) {
this.setUnsavedChanges(false); this.renameTabs({
this.changeBreadcrumbs({ schema: this.schema, function: this.localFunction.name }); uid: this.connection.uid,
} schema: this.schema,
elementName: oldName,
elementNewName: this.localFunction.name,
elementType: 'triggerFunction'
});
this.changeBreadcrumbs({ schema: this.schema, triggerFunction: this.localFunction.name });
}
else
this.getFunctionData(); this.getFunctionData();
} }
else else
@@ -281,7 +297,7 @@ export default {
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`; sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
} }
this.newTab({ uid: this.connection.uid, content: sql, autorun: true }); this.newTab({ uid: this.connection.uid, content: sql, type: 'query', autorun: true });
}, },
showOptionsModal () { showOptionsModal () {
this.isOptionsModal = true; this.isOptionsModal = true;

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="workspace-query-tab column col-12 columns col-gapless"> <div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<div class="workspace-query-runner-footer"> <div class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
@@ -23,6 +23,11 @@
<span>{{ $t('word.clear') }}</span> <span>{{ $t('word.clear') }}</span>
</button> </button>
</div> </div>
<div class="workspace-query-info">
<div class="d-flex" :title="$t('word.schema')">
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
</div>
</div>
</div> </div>
</div> </div>
<div class="container"> <div class="container">
@@ -161,7 +166,7 @@
<BaseLoader v-if="isLoading" /> <BaseLoader v-if="isLoading" />
<label class="form-label ml-2">{{ $t('message.selectStatement') }}</label> <label class="form-label ml-2">{{ $t('message.selectStatement') }}</label>
<QueryEditor <QueryEditor
v-if="isSelected" v-show="isSelected"
ref="queryEditor" ref="queryEditor"
:value.sync="localView.sql" :value.sync="localView.sql"
:workspace="workspace" :workspace="workspace"
@@ -186,11 +191,12 @@ export default {
}, },
props: { props: {
connection: Object, connection: Object,
isSelected: Boolean,
schema: String,
view: String view: String
}, },
data () { data () {
return { return {
tabUid: 'prop',
isLoading: false, isLoading: false,
isSaving: false, isSaving: false,
originalView: null, originalView: null,
@@ -208,11 +214,8 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () { tabUid () {
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.view; return this.$vnode.key;
},
schema () {
return this.workspace.breadcrumbs.schema;
}, },
isChanged () { isChanged () {
return JSON.stringify(this.originalView) !== JSON.stringify(this.localView); return JSON.stringify(this.originalView) !== JSON.stringify(this.localView);
@@ -222,6 +225,13 @@ export default {
} }
}, },
watch: { watch: {
async schema () {
if (this.isSelected) {
await this.getViewData();
this.$refs.queryEditor.editor.session.setValue(this.localView.sql);
this.lastView = this.view;
}
},
async view () { async view () {
if (this.isSelected) { if (this.isSelected) {
await this.getViewData(); await this.getViewData();
@@ -229,27 +239,29 @@ export default {
this.lastView = this.view; this.lastView = this.view;
} }
}, },
async isSelected (val) { isSelected (val) {
if (val && this.lastView !== this.view) { if (val) {
await this.getViewData(); this.changeBreadcrumbs({ schema: this.schema, view: this.view });
this.$refs.queryEditor.editor.session.setValue(this.localView.sql);
this.lastView = this.view; if (this.lastView !== this.view)
this.getViewData();
} }
}, },
isChanged (val) { isChanged (val) {
if (this.isSelected && this.lastView === this.view && this.view !== null) this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
this.setUnsavedChanges(val);
} }
}, },
async created () {
await this.getViewData();
this.$refs.queryEditor.editor.session.setValue(this.localView.sql);
window.addEventListener('keydown', this.onKey);
},
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { destroyed () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
created () {
window.addEventListener('keydown', this.onKey);
},
beforeDestroy () { beforeDestroy () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
@@ -258,17 +270,19 @@ export default {
addNotification: 'notifications/addNotification', addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure', refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges', setUnsavedChanges: 'workspaces/setUnsavedChanges',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs' changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
renameTabs: 'workspaces/renameTabs'
}), }),
async getViewData () { async getViewData () {
if (!this.view) return; if (!this.view) return;
this.isLoading = true; this.isLoading = true;
this.localView = { sql: '' }; this.localView = { sql: '' };
this.lastView = this.view;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.schema,
view: this.workspace.breadcrumbs.view view: this.view
}; };
try { try {
@@ -293,9 +307,9 @@ export default {
this.isSaving = true; this.isSaving = true;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema,
view: { view: {
...this.localView, ...this.localView,
schema: this.schema,
oldName: this.originalView.name oldName: this.originalView.name
} }
}; };
@@ -309,10 +323,17 @@ export default {
await this.refreshStructure(this.connection.uid); await this.refreshStructure(this.connection.uid);
if (oldName !== this.localView.name) { if (oldName !== this.localView.name) {
this.setUnsavedChanges(false); this.renameTabs({
uid: this.connection.uid,
schema: this.schema,
elementName: oldName,
elementNewName: this.localView.name,
elementType: 'view'
});
this.changeBreadcrumbs({ schema: this.schema, view: this.localView.name }); this.changeBreadcrumbs({ schema: this.schema, view: this.localView.name });
} }
else
this.getViewData(); this.getViewData();
} }
else else

View File

@@ -100,7 +100,7 @@
</div> </div>
</div> </div>
</div> </div>
<draggable <Draggable
ref="resultTable" ref="resultTable"
:list="fields" :list="fields"
class="tbody" class="tbody"
@@ -117,14 +117,14 @@
@contextmenu="contextMenu" @contextmenu="contextMenu"
@rename-field="$emit('rename-field', $event)" @rename-field="$emit('rename-field', $event)"
/> />
</draggable> </Draggable>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { mapActions, mapGetters } from 'vuex'; import { mapActions, mapGetters } from 'vuex';
import draggable from 'vuedraggable'; import Draggable from 'vuedraggable';
import TableRow from '@/components/WorkspacePropsTableRow'; import TableRow from '@/components/WorkspacePropsTableRow';
import TableContext from '@/components/WorkspacePropsTableContext'; import TableContext from '@/components/WorkspacePropsTableContext';
@@ -133,7 +133,7 @@ export default {
components: { components: {
TableRow, TableRow,
TableContext, TableContext,
draggable Draggable
}, },
props: { props: {
fields: Array, fields: Array,

View File

@@ -1,7 +1,7 @@
<template> <template>
<div <div
v-show="isSelected" v-show="isSelected"
class="workspace-query-tab column col-12 columns col-gapless no-outline" class="workspace-query-tab column col-12 columns col-gapless no-outline p-0"
tabindex="0" tabindex="0"
@keydown.116="runQuery(query)" @keydown.116="runQuery(query)"
@keydown.ctrl.87="clear" @keydown.ctrl.87="clear"
@@ -14,7 +14,7 @@
:auto-focus="true" :auto-focus="true"
:value.sync="query" :value.sync="query"
:workspace="workspace" :workspace="workspace"
:schema="schema" :schema="breadcrumbsSchema"
:is-selected="isSelected" :is-selected="isSelected"
:height="editorHeight" :height="editorHeight"
/> />
@@ -86,12 +86,16 @@
<div v-if="affectedCount"> <div v-if="affectedCount">
{{ $t('message.affectedRows') }}: <b>{{ affectedCount }}</b> {{ $t('message.affectedRows') }}: <b>{{ affectedCount }}</b>
</div> </div>
<div <div class="input-group" :title="$t('word.schema')">
v-if="workspace.breadcrumbs.schema" <i class="input-group-addon addon-sm mdi mdi-24px mdi-database" />
class="d-flex" <select v-model="selectedSchema" class="form-select select-sm text-bold">
:title="$t('word.schema')" <option :value="null">
> {{ $t('message.noSchema') }}
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ workspace.breadcrumbs.schema }}</b> </option>
<option v-for="schemaName in databaseSchemas" :key="schemaName">
{{ schemaName }}
</option>
</select>
</div> </div>
</div> </div>
</div> </div>
@@ -142,6 +146,7 @@ export default {
lastQuery: '', lastQuery: '',
isQuering: false, isQuering: false,
results: [], results: [],
selectedSchema: null,
resultsCount: 0, resultsCount: 0,
durationsCount: 0, durationsCount: 0,
affectedCount: 0, affectedCount: 0,
@@ -156,12 +161,32 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
breadcrumbsSchema () {
return this.workspace.breadcrumbs.schema || null;
},
databaseSchemas () {
return this.workspace.structure.reduce((acc, curr) => {
acc.push(curr.name);
return acc;
}, []);
},
isWorkspaceSelected () { isWorkspaceSelected () {
return this.workspace.uid === this.selectedWorkspace; return this.workspace.uid === this.selectedWorkspace;
} }
}, },
watch: {
isSelected (val) {
if (val)
this.changeBreadcrumbs({ schema: this.selectedSchema, query: `Query #${this.tab.index}` });
},
selectedSchema () {
this.changeBreadcrumbs({ schema: this.selectedSchema, query: `Query #${this.tab.index}` });
}
},
created () { created () {
this.query = this.tab.content; this.query = this.tab.content;
this.selectedSchema = this.tab.schema || this.breadcrumbsSchema;
// this.changeBreadcrumbs({ schema: this.selectedSchema, query: `Query #${this.tab.index}` });
window.addEventListener('keydown', this.onKey); window.addEventListener('keydown', this.onKey);
}, },
@@ -183,7 +208,9 @@ export default {
}, },
methods: { methods: {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification' addNotification: 'notifications/addNotification',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
updateTabContent: 'workspaces/updateTabContent'
}), }),
async runQuery (query) { async runQuery (query) {
if (!query || this.isQuering) return; if (!query || this.isQuering) return;
@@ -194,7 +221,7 @@ export default {
try { // Query Data try { // Query Data
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.selectedSchema,
query query
}; };
@@ -205,6 +232,8 @@ export default {
this.resultsCount += this.results.reduce((acc, curr) => acc + (curr.rows ? curr.rows.length : 0), 0); this.resultsCount += this.results.reduce((acc, curr) => acc + (curr.rows ? curr.rows.length : 0), 0);
this.durationsCount += this.results.reduce((acc, curr) => acc + curr.duration, 0); this.durationsCount += this.results.reduce((acc, curr) => acc + curr.duration, 0);
this.affectedCount += this.results.reduce((acc, curr) => acc + (curr.report ? curr.report.affectedRows : 0), 0); this.affectedCount += this.results.reduce((acc, curr) => acc + (curr.report ? curr.report.affectedRows : 0), 0);
this.updateTabContent({ uid: this.connection.uid, tab: this.tab.uid, type: 'query', schema: this.selectedSchema, content: query });
} }
else else
this.addNotification({ status: 'error', message: response }); this.addNotification({ status: 'error', message: response });
@@ -303,8 +332,10 @@ export default {
align-items: center; align-items: center;
height: 42px; height: 42px;
.workspace-query-buttons { .workspace-query-buttons,
.workspace-query-info {
display: flex; display: flex;
align-items: center;
.btn { .btn {
display: flex; display: flex;
@@ -314,7 +345,7 @@ export default {
} }
.workspace-query-info { .workspace-query-info {
display: flex; overflow: visible;
> div + div { > div + div {
padding-left: 0.6rem; padding-left: 0.6rem;

View File

@@ -10,6 +10,7 @@
v-if="isContext" v-if="isContext"
:context-event="contextEvent" :context-event="contextEvent"
:selected-rows="selectedRows" :selected-rows="selectedRows"
:selected-cell="selectedCell"
@show-delete-modal="showDeleteConfirmModal" @show-delete-modal="showDeleteConfirmModal"
@set-null="setNull" @set-null="setNull"
@copy-cell="copyCell" @copy-cell="copyCell"
@@ -71,6 +72,7 @@
:row="row" :row="row"
:fields="fieldsObj" :fields="fieldsObj"
:key-usage="keyUsage" :key-usage="keyUsage"
:element-type="elementType"
:class="{'selected': selectedRows.includes(row._id)}" :class="{'selected': selectedRows.includes(row._id)}"
@select-row="selectRow($event, row._id)" @select-row="selectRow($event, row._id)"
@update-field="updateField($event, row)" @update-field="updateField($event, row)"
@@ -123,7 +125,8 @@ export default {
results: Array, results: Array,
connUid: String, connUid: String,
mode: String, mode: String,
isSelected: Boolean isSelected: Boolean,
elementType: { type: String, default: 'table' }
}, },
data () { data () {
return { return {
@@ -226,6 +229,9 @@ export default {
}, },
resultsetIndex () { resultsetIndex () {
this.setLocalResults(); this.setLocalResults();
},
isSelected (val) {
if (val) this.refreshScroller();
} }
}, },
updated () { updated () {

View File

@@ -28,7 +28,7 @@
</div> </div>
</div> </div>
<div <div
v-if="selectedRows.length === 1" v-if="selectedRows.length === 1 && selectedCell.isEditable"
class="context-element" class="context-element"
@click="setNull" @click="setNull"
> >
@@ -36,7 +36,11 @@
<i class="mdi mdi-18px mdi-null text-light pr-1" /> {{ $t('message.setNull') }} <i class="mdi mdi-18px mdi-null text-light pr-1" /> {{ $t('message.setNull') }}
</span> </span>
</div> </div>
<div class="context-element" @click="showConfirmModal"> <div
v-if="selectedCell.isEditable"
class="context-element"
@click="showConfirmModal"
>
<span class="d-flex"> <span class="d-flex">
<i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $tc('message.deleteRows', selectedRows.length) }} <i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $tc('message.deleteRows', selectedRows.length) }}
</span> </span>
@@ -54,7 +58,8 @@ export default {
}, },
props: { props: {
contextEvent: MouseEvent, contextEvent: MouseEvent,
selectedRows: Array selectedRows: Array,
selectedCell: Object
}, },
computed: { computed: {
}, },

View File

@@ -260,7 +260,8 @@ export default {
props: { props: {
row: Object, row: Object,
fields: Object, fields: Object,
keyUsage: Array keyUsage: Array,
elementType: { type: String, default: 'table' }
}, },
data () { data () {
return { return {
@@ -334,6 +335,8 @@ export default {
return this.keyUsage.map(key => key.field); return this.keyUsage.map(key => key.field);
}, },
isEditable () { isEditable () {
if (this.elementType === 'view') return false;
if (this.fields) { if (this.fields) {
const nElements = Object.keys(this.fields).reduce((acc, curr) => { const nElements = Object.keys(this.fields).reduce((acc, curr) => {
acc.add(this.fields[curr].table); acc.add(this.fields[curr].table);
@@ -503,10 +506,9 @@ export default {
return this.keyUsage.find(key => key.field === keyName); return this.keyUsage.find(key => key.field === keyName);
}, },
openContext (event, payload) { openContext (event, payload) {
if (this.isEditable) {
payload.field = this.fields[payload.field].name;// Ensures field name only payload.field = this.fields[payload.field].name;// Ensures field name only
payload.isEditable = this.isEditable;
this.$emit('contextmenu', event, payload); this.$emit('contextmenu', event, payload);
}
}, },
onKey (e) { onKey (e) {
e.stopPropagation(); e.stopPropagation();

View File

@@ -1,5 +1,5 @@
<template> <template>
<div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless"> <div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless no-outline p-0">
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<div class="workspace-query-runner-footer"> <div class="workspace-query-runner-footer">
<div class="workspace-query-buttons"> <div class="workspace-query-buttons">
@@ -115,8 +115,8 @@
<div v-if="hasApproximately || (page > 1 && tableInfo.rows)"> <div v-if="hasApproximately || (page > 1 && tableInfo.rows)">
{{ $t('word.total') }}: <b>{{ tableInfo.rows | localeString }}</b> <small>({{ $t('word.approximately') }})</small> {{ $t('word.total') }}: <b>{{ tableInfo.rows | localeString }}</b> <small>({{ $t('word.approximately') }})</small>
</div> </div>
<div v-if="workspace.breadcrumbs.database"> <div class="d-flex" :title="$t('word.schema')">
{{ $t('word.schema') }}: <b>{{ workspace.breadcrumbs.database }}</b> <i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
</div> </div>
</div> </div>
</div> </div>
@@ -131,6 +131,7 @@
:conn-uid="connection.uid" :conn-uid="connection.uid"
:is-selected="isSelected" :is-selected="isSelected"
mode="table" mode="table"
:element-type="elementType"
@update-field="updateField" @update-field="updateField"
@delete-selected="deleteSelected" @delete-selected="deleteSelected"
@hard-sort="hardSort" @hard-sort="hardSort"
@@ -181,11 +182,14 @@ export default {
mixins: [tableTabs], mixins: [tableTabs],
props: { props: {
connection: Object, connection: Object,
table: String isSelected: Boolean,
table: String,
schema: String,
elementType: String
}, },
data () { data () {
return { return {
tabUid: 'data', tabUid: 'data', // ???
isQuering: false, isQuering: false,
isPageMenu: false, isPageMenu: false,
results: [], results: [],
@@ -208,9 +212,6 @@ export default {
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
isSelected () {
return this.workspace.selected_tab === 'data' && this.workspace.uid === this.selectedWorkspace;
},
isTable () { isTable () {
return !!this.workspace.breadcrumbs.table; return !!this.workspace.breadcrumbs.table;
}, },
@@ -237,6 +238,15 @@ export default {
} }
}, },
watch: { watch: {
schema () {
if (this.isSelected) {
this.page = 1;
this.sortParams = {};
this.getTableData();
this.lastTable = this.table;
this.$refs.queryTable.resetSort();
}
},
table () { table () {
if (this.isSelected) { if (this.isSelected) {
this.page = 1; this.page = 1;
@@ -253,9 +263,11 @@ export default {
} }
}, },
isSelected (val) { isSelected (val) {
if (val && this.lastTable !== this.table) { if (val) {
this.changeBreadcrumbs({ schema: this.schema, [this.elementType]: this.table });
if (this.lastTable !== this.table)
this.getTableData(); this.getTableData();
this.lastTable = this.table;
} }
} }
}, },
@@ -269,7 +281,8 @@ export default {
}, },
methods: { methods: {
...mapActions({ ...mapActions({
addNotification: 'notifications/addNotification' addNotification: 'notifications/addNotification',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
}), }),
async getTableData (sortParams) { async getTableData (sortParams) {
if (!this.table) return; if (!this.table) return;
@@ -279,10 +292,12 @@ export default {
if (this.lastTable !== this.table) if (this.lastTable !== this.table)
this.results = []; this.results = [];
this.lastTable = this.table;
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
schema: this.schema, schema: this.schema,
table: this.workspace.breadcrumbs.table || this.workspace.breadcrumbs.view, table: this.table,
limit: this.limit, limit: this.limit,
page: this.page, page: this.page,
sortParams sortParams

View File

@@ -1,489 +0,0 @@
<template>
<div class="modal active">
<a class="modal-overlay c-hand" @click="closeModal" />
<div class="modal-container">
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-server mr-1" /> {{ $t('message.editConnection') }}
</div>
</div>
<a class="btn btn-clear c-hand" @click="closeModal" />
</div>
<div class="modal-body p-0">
<div class="panel">
<div class="panel-nav">
<ul class="tab tab-block">
<li
class="tab-item c-hand"
:class="{'active': selectedTab === 'general'}"
@click="selectTab('general')"
>
<a class="tab-link">{{ $t('word.general') }}</a>
</li>
<li
class="tab-item c-hand"
:class="{'active': selectedTab === 'ssl'}"
@click="selectTab('ssl')"
>
<a class="tab-link">{{ $t('word.ssl') }}</a>
</li>
<li
class="tab-item"
:class="{'active': selectedTab === 'ssh'}"
@click="selectTab('ssh')"
>
<a class="c-hand">{{ $t('word.sshTunnel') }}</a>
</li>
</ul>
</div>
<div v-if="selectedTab === 'general'" class="panel-body py-0">
<div class="container">
<form class="form-horizontal">
<fieldset class="m-0" :disabled="isTesting">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.connectionName') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
ref="firstInput"
v-model="localConnection.name"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.client') }}</label>
</div>
<div class="col-8 col-sm-12">
<select v-model="localConnection.client" class="form-select">
<option value="mysql">
MySQL
</option>
<option value="maria">
MariaDB
</option>
<option value="pg">
PostgreSQL
</option>
<!-- <option value="mssql">
Microsoft SQL
</option>
<option value="oracledb">
Oracle DB
</option> -->
</select>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.host"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.port') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.port"
class="form-input"
type="number"
min="1"
max="65535"
>
</div>
</div>
<div v-if="customizations.database" class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.database') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.database"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.user') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.user"
class="form-input"
type="text"
:disabled="localConnection.ask"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.password') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.password"
class="form-input"
type="password"
:disabled="localConnection.ask"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12" />
<div class="col-8 col-sm-12">
<label class="form-checkbox form-inline">
<input v-model="localConnection.ask" type="checkbox"><i class="form-icon" /> {{ $t('message.askCredentials') }}
</label>
</div>
</div>
</fieldset>
</form>
</div>
<BaseToast
class="mb-2"
:message="toast.message"
:status="toast.status"
/>
</div>
<div v-if="selectedTab === 'ssl'" class="panel-body py-0">
<div class="container">
<form class="form-horizontal">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">
{{ $t('message.enableSsl') }}
</label>
</div>
<div class="col-8 col-sm-12">
<label class="form-switch d-inline-block" @click.prevent="toggleSsl">
<input type="checkbox" :checked="localConnection.ssl">
<i class="form-icon" />
</label>
</div>
</div>
<fieldset class="m-0" :disabled="isTesting || !localConnection.ssl">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.privateKey') }}</label>
</div>
<div class="col-8 col-sm-12">
<BaseUploadInput
:value="localConnection.key"
:message="$t('word.browse')"
@clear="pathClear('key')"
@change="pathSelection($event, 'key')"
/>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.certificate') }}</label>
</div>
<div class="col-8 col-sm-12">
<BaseUploadInput
:value="localConnection.cert"
:message="$t('word.browse')"
@clear="pathClear('cert')"
@change="pathSelection($event, 'cert')"
/>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.caCertificate') }}</label>
</div>
<div class="col-8 col-sm-12">
<BaseUploadInput
:value="localConnection.ca"
:message="$t('word.browse')"
@clear="pathClear('ca')"
@change="pathSelection($event, 'ca')"
/>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.ciphers') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
ref="firstInput"
v-model="localConnection.ciphers"
class="form-input"
type="text"
>
</div>
</div>
</fieldset>
</form>
</div>
<BaseToast
class="mb-2"
:message="toast.message"
:status="toast.status"
/>
</div>
<div v-if="selectedTab === 'ssh'" class="panel-body py-0">
<div class="container">
<form class="form-horizontal">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">
{{ $t('message.enableSsh') }}
</label>
</div>
<div class="col-8 col-sm-12">
<label class="form-switch d-inline-block" @click.prevent="toggleSsh">
<input type="checkbox" :checked="localConnection.ssh">
<i class="form-icon" />
</label>
</div>
</div>
<fieldset class="m-0" :disabled="isTesting || !localConnection.ssh">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.sshHost"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.user') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.sshUser"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.password') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.sshPass"
class="form-input"
type="password"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.port') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="localConnection.sshPort"
class="form-input"
type="number"
min="1"
max="65535"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.privateKey') }}</label>
</div>
<div class="col-8 col-sm-12">
<BaseUploadInput
:value="localConnection.sshKey"
:message="$t('word.browse')"
@clear="pathClear('sshKey')"
@change="pathSelection($event, 'sshKey')"
/>
</div>
</div>
</fieldset>
</form>
</div>
<BaseToast
class="mb-2"
:message="toast.message"
:status="toast.status"
/>
</div>
<div class="modal-footer text-light">
<button
class="btn btn-gray mr-2"
:class="{'loading': isTesting}"
@click="startTest"
>
{{ $t('message.testConnection') }}
</button>
<button class="btn btn-primary mr-2" @click="saveEditConnection">
{{ $t('word.save') }}
</button>
<button class="btn btn-link" @click="closeModal">
{{ $t('word.close') }}
</button>
</div>
</div>
<ModalAskCredentials
v-if="isAsking"
@close-asking="closeAsking"
@credentials="continueTest"
/>
</div>
</div>
</div>
</template>
<script>
import { mapActions } from 'vuex';
import customizations from 'common/customizations';
import Connection from '@/ipc-api/Connection';
import ModalAskCredentials from '@/components/ModalAskCredentials';
import BaseToast from '@/components/BaseToast';
import BaseUploadInput from '@/components/BaseUploadInput';
export default {
name: 'ModalEditConnection',
components: {
ModalAskCredentials,
BaseToast,
BaseUploadInput
},
props: {
connection: Object
},
data () {
return {
toast: {
status: '',
message: ''
},
isTesting: false,
isAsking: false,
localConnection: null,
selectedTab: 'general'
};
},
computed: {
customizations () {
return customizations[this.connection.client];
}
},
created () {
this.localConnection = Object.assign({}, this.connection);
window.addEventListener('keydown', this.onKey);
setTimeout(() => {
this.$refs.firstInput.focus();
}, 20);
},
beforeDestroy () {
window.removeEventListener('keydown', this.onKey);
},
methods: {
...mapActions({
editConnection: 'connections/editConnection'
}),
async startTest () {
this.isTesting = true;
this.toast = {
status: '',
message: ''
};
if (this.localConnection.ask)
this.isAsking = true;
else {
try {
const res = await Connection.makeTest(this.localConnection);
if (res.status === 'error')
this.toast = { status: 'error', message: res.response.message };
else
this.toast = { status: 'success', message: this.$t('message.connectionSuccessfullyMade') };
}
catch (err) {
this.toast = { status: 'error', message: err.stack };
}
this.isTesting = false;
}
},
async continueTest (credentials) { // if "Ask for credentials" is true
this.isAsking = false;
const params = Object.assign({}, this.localConnection, credentials);
try {
const res = await Connection.makeTest(params);
if (res.status === 'error')
this.toast = { status: 'error', message: res.response.message };
else
this.toast = { status: 'success', message: this.$t('message.connectionSuccessfullyMade') };
}
catch (err) {
this.toast = { status: 'error', message: err.stack };
}
this.isTesting = false;
},
saveEditConnection () {
this.editConnection(this.localConnection);
this.closeModal();
},
closeAsking () {
this.isTesting = false;
this.isAsking = false;
},
closeModal () {
this.$emit('close');
},
onKey (e) {
e.stopPropagation();
if (e.key === 'Escape')
this.closeModal();
},
selectTab (tab) {
this.selectedTab = tab;
},
toggleSsl () {
this.localConnection.ssl = !this.localConnection.ssl;
},
toggleSsh () {
this.localConnection.ssh = !this.localConnection.ssh;
},
pathSelection (event, name) {
const { files } = event.target;
if (!files.length) return;
this.localConnection[name] = files[0].path;
},
pathClear (name) {
this.localConnection[name] = '';
}
}
};
</script>
<style scoped>
.modal-container {
position: absolute;
max-width: 450px;
top: 17.5vh;
}
</style>

View File

@@ -1,518 +0,0 @@
<template>
<div class="modal active">
<a class="modal-overlay c-hand" @click="closeModal" />
<div class="modal-container">
<div class="modal-header pl-2">
<div class="modal-title h6">
<div class="d-flex">
<i class="mdi mdi-24px mdi-server-plus mr-1" />
<span class="cut-text">{{ $t('message.createNewConnection') }}</span>
</div>
</div>
<a class="btn btn-clear c-hand" @click="closeModal" />
</div>
<div class="modal-body p-0">
<div class="panel">
<div class="panel-nav">
<ul class="tab tab-block">
<li
class="tab-item c-hand"
:class="{'active': selectedTab === 'general'}"
@click="selectTab('general')"
>
<a class="tab-link">{{ $t('word.general') }}</a>
</li>
<li
class="tab-item c-hand"
:class="{'active': selectedTab === 'ssl'}"
@click="selectTab('ssl')"
>
<a class="tab-link">{{ $t('word.ssl') }}</a>
</li>
<li
class="tab-item"
:class="{'active': selectedTab === 'ssh'}"
@click="selectTab('ssh')"
>
<a class="c-hand">{{ $t('word.sshTunnel') }}</a>
</li>
</ul>
</div>
<div v-if="selectedTab === 'general'" class="panel-body py-0">
<div class="container">
<form class="form-horizontal">
<fieldset class="m-0" :disabled="isTesting">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.connectionName') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
ref="firstInput"
v-model="connection.name"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.client') }}</label>
</div>
<div class="col-8 col-sm-12">
<select
v-model="connection.client"
class="form-select"
@change="setDefaults"
>
<option value="mysql">
MySQL
</option>
<option value="maria">
MariaDB
</option>
<option value="pg">
PostgreSQL
</option>
<!-- <option value="mssql">
Microsoft SQL
</option>
<option value="oracledb">
Oracle DB
</option> -->
</select>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.host"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.port') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.port"
class="form-input"
type="number"
min="1"
max="65535"
>
</div>
</div>
<div v-if="customizations.database" class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.database') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.database"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.user') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.user"
class="form-input"
type="text"
:disabled="connection.ask"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.password') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.password"
class="form-input"
type="password"
:disabled="connection.ask"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12" />
<div class="col-8 col-sm-12">
<label class="form-checkbox form-inline">
<input v-model="connection.ask" type="checkbox"><i class="form-icon" /> {{ $t('message.askCredentials') }}
</label>
</div>
</div>
</fieldset>
</form>
</div>
<BaseToast
class="mb-2"
:message="toast.message"
:status="toast.status"
/>
</div>
<div v-if="selectedTab === 'ssl'" class="panel-body py-0">
<div class="container">
<form class="form-horizontal">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">
{{ $t('message.enableSsl') }}
</label>
</div>
<div class="col-8 col-sm-12">
<label class="form-switch d-inline-block" @click.prevent="toggleSsl">
<input type="checkbox" :checked="connection.ssl">
<i class="form-icon" />
</label>
</div>
</div>
<fieldset class="m-0" :disabled="isTesting || !connection.ssl">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.privateKey') }}</label>
</div>
<div class="col-8 col-sm-12">
<BaseUploadInput
:value="connection.key"
:message="$t('word.browse')"
@clear="pathClear('key')"
@change="pathSelection($event, 'key')"
/>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.certificate') }}</label>
</div>
<div class="col-8 col-sm-12">
<BaseUploadInput
:value="connection.cert"
:message="$t('word.browse')"
@clear="pathClear('cert')"
@change="pathSelection($event, 'cert')"
/>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.caCertificate') }}</label>
</div>
<div class="col-8 col-sm-12">
<BaseUploadInput
:value="connection.ca"
:message="$t('word.browse')"
@clear="pathClear('ca')"
@change="pathSelection($event, 'ca')"
/>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.ciphers') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
ref="firstInput"
v-model="connection.ciphers"
class="form-input"
type="text"
>
</div>
</div>
</fieldset>
</form>
</div>
<BaseToast
class="mb-2"
:message="toast.message"
:status="toast.status"
/>
</div>
<div v-if="selectedTab === 'ssh'" class="panel-body py-0">
<div class="container">
<form class="form-horizontal">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">
{{ $t('message.enableSsh') }}
</label>
</div>
<div class="col-8 col-sm-12">
<label class="form-switch d-inline-block" @click.prevent="toggleSsh">
<input type="checkbox" :checked="connection.ssh">
<i class="form-icon" />
</label>
</div>
</div>
<fieldset class="m-0" :disabled="isTesting || !connection.ssh">
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.sshHost"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.user') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.sshUser"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.password') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.sshPass"
class="form-input"
type="password"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.port') }}</label>
</div>
<div class="col-8 col-sm-12">
<input
v-model="connection.sshPort"
class="form-input"
type="number"
min="1"
max="65535"
>
</div>
</div>
<div class="form-group">
<div class="col-4 col-sm-12">
<label class="form-label">{{ $t('word.privateKey') }}</label>
</div>
<div class="col-8 col-sm-12">
<BaseUploadInput
:value="connection.sshKey"
:message="$t('word.browse')"
@clear="pathClear('sshKey')"
@change="pathSelection($event, 'sshKey')"
/>
</div>
</div>
</fieldset>
</form>
</div>
<BaseToast
class="mb-2"
:message="toast.message"
:status="toast.status"
/>
</div>
</div>
</div>
<div class="modal-footer text-light">
<button
class="btn btn-gray mr-2"
:class="{'loading': isTesting}"
@click="startTest"
>
{{ $t('message.testConnection') }}
</button>
<button class="btn btn-primary mr-2" @click="saveNewConnection">
{{ $t('word.save') }}
</button>
<button class="btn btn-link" @click="closeModal">
{{ $t('word.close') }}
</button>
</div>
</div>
<ModalAskCredentials
v-if="isAsking"
@close-asking="closeAsking"
@credentials="continueTest"
/>
</div>
</template>
<script>
import { mapActions } from 'vuex';
import customizations from 'common/customizations';
import Connection from '@/ipc-api/Connection';
import { uidGen } from 'common/libs/uidGen';
import ModalAskCredentials from '@/components/ModalAskCredentials';
import BaseToast from '@/components/BaseToast';
import BaseUploadInput from '@/components/BaseUploadInput';
export default {
name: 'ModalNewConnection',
components: {
ModalAskCredentials,
BaseToast,
BaseUploadInput
},
data () {
return {
connection: {
name: '',
client: 'mysql',
host: '127.0.0.1',
database: null,
port: null,
user: null,
password: '',
ask: false,
uid: uidGen('C'),
ssl: false,
cert: '',
key: '',
ca: '',
ciphers: '',
ssh: false,
sshHost: '',
sshUser: '',
sshPass: '',
sshKey: '',
sshPort: 22
},
toast: {
status: '',
message: ''
},
isTesting: false,
isAsking: false,
selectedTab: 'general'
};
},
computed: {
customizations () {
return customizations[this.connection.client];
}
},
created () {
this.setDefaults();
window.addEventListener('keydown', this.onKey);
setTimeout(() => {
this.$refs.firstInput.focus();
}, 20);
},
beforeDestroy () {
window.removeEventListener('keydown', this.onKey);
},
methods: {
...mapActions({
closeModal: 'application/hideNewConnModal',
addConnection: 'connections/addConnection'
}),
setDefaults () {
this.connection.user = this.customizations.defaultUser;
this.connection.port = this.customizations.defaultPort;
this.connection.database = this.customizations.defaultDatabase;
},
async startTest () {
this.isTesting = true;
this.toast = {
status: '',
message: ''
};
if (this.connection.ask)
this.isAsking = true;
else {
try {
const res = await Connection.makeTest(this.connection);
console.log(res.response);
if (res.status === 'error')
this.toast = { status: 'error', message: res.response.message };
else
this.toast = { status: 'success', message: this.$t('message.connectionSuccessfullyMade') };
}
catch (err) {
this.toast = { status: 'error', message: err.stack };
}
this.isTesting = false;
}
},
async continueTest (credentials) { // if "Ask for credentials" is true
this.isAsking = false;
const params = Object.assign({}, this.connection, credentials);
try {
const res = await Connection.makeTest(params);
if (res.status === 'error')
this.toast = { status: 'error', message: res.response.message };
else
this.toast = { status: 'success', message: this.$t('message.connectionSuccessfullyMade') };
}
catch (err) {
this.toast = { status: 'error', message: err.stack };
}
this.isTesting = false;
},
saveNewConnection () {
this.addConnection(this.connection);
this.closeModal();
},
closeAsking () {
this.isAsking = false;
this.isTesting = false;
},
onKey (e) {
e.stopPropagation();
if (e.key === 'Escape')
this.closeModal();
},
selectTab (tab) {
this.selectedTab = tab;
},
toggleSsl () {
this.connection.ssl = !this.connection.ssl;
},
toggleSsh () {
this.connection.ssh = !this.connection.ssh;
},
pathSelection (event, name) {
const { files } = event.target;
if (!files.length) return;
this.connection[name] = files[0].path;
},
pathClear (name) {
this.connection[name] = '';
}
}
};
</script>
<style scoped>
.modal-container {
position: absolute;
max-width: 450px;
top: 17.5vh;
}
</style>

View File

@@ -1,37 +0,0 @@
<template>
<div class="columns">
<div class="column col-12 empty text-light">
<div class="empty-icon">
<i class="mdi mdi-48px mdi-emoticon" />
</div>
<p class="empty-title h5">
{{ $t('message.appWelcome') }}
</p>
<p class="empty-subtitle">
{{ $t('message.appFirstStep') }}
</p>
<div class="empty-action">
<button class="btn btn-primary" @click="$emit('new-conn')">
{{ $t('message.createConnection') }}
</button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'TheAppWelcome'
};
</script>
<style scoped>
.empty {
height: 100%;
border-radius: 0;
background: transparent;
display: flex;
flex-direction: column;
justify-content: center;
}
</style>

View File

@@ -1,82 +0,0 @@
<template>
<div class="columns">
<div class="column col-12 empty">
<div class="empty-icon">
<i class="mdi mdi-48px mdi-power-plug-off" />
</div>
<p class="empty-title h5">
{{ isConnecting ? $t('word.connecting') : $t('word.disconnected') }}
</p>
<div class="empty-action">
<button
class="btn btn-success"
:class="{'loading': isConnecting}"
@click="startConnection"
>
{{ $t('word.connect') }}
</button>
</div>
</div>
<ModalAskCredentials
v-if="isAsking"
@close-asking="closeAsking"
@credentials="continueTest"
/>
</div>
</template>
<script>
import { mapActions } from 'vuex';
import ModalAskCredentials from '@/components/ModalAskCredentials';
export default {
name: 'WorkspaceConnectPanel',
components: {
ModalAskCredentials
},
props: {
connection: Object
},
data () {
return {
isConnecting: false,
isAsking: false
};
},
methods: {
...mapActions({
connectWorkspace: 'workspaces/connectWorkspace'
}),
async startConnection () {
this.isConnecting = true;
if (this.connection.ask)
this.isAsking = true;
else {
await this.connectWorkspace(this.connection);
this.isConnecting = false;
}
},
async continueTest (credentials) { // if "Ask for credentials" is true
this.isAsking = false;
const params = Object.assign({}, this.connection, credentials);
await this.connectWorkspace(params);
this.isConnecting = false;
},
closeAsking () {
this.isAsking = false;
this.isConnecting = false;
}
}
};
</script>
<style scoped>
.empty {
height: 100%;
border-radius: 0;
background: transparent;
display: flex;
flex-direction: column;
}
</style>

View File

@@ -169,7 +169,7 @@ module.exports = {
deleteTable: 'Delete table', deleteTable: 'Delete table',
emptyCorfirm: 'Do you confirm to empty', emptyCorfirm: 'Do you confirm to empty',
unsavedChanges: 'Unsaved changes', unsavedChanges: 'Unsaved changes',
discardUnsavedChanges: 'You have some unsaved changes. By leaving this tab these changes will be discarded.', discardUnsavedChanges: 'You have some unsaved changes. Closing this tab these changes will be discarded.',
thereAreNoIndexes: 'There are no indexes', thereAreNoIndexes: 'There are no indexes',
thereAreNoForeign: 'There are no foreign keys', thereAreNoForeign: 'There are no foreign keys',
createNewForeign: 'Create new foreign key', createNewForeign: 'Create new foreign key',
@@ -224,7 +224,10 @@ module.exports = {
dataTabPageSize: 'DATA tab page size', dataTabPageSize: 'DATA tab page size',
enableSsh: 'Enable SSH', enableSsh: 'Enable SSH',
pageNumber: 'Page number', pageNumber: 'Page number',
duplicateTable: 'Duplicate table' duplicateTable: 'Duplicate table',
noOpenTabs: 'There are no open tabs, navigate on the left bar or:',
noSchema: 'No schema',
restorePreviourSession: 'Restore previous session'
}, },
faker: { faker: {
address: 'Address', address: 'Address',

View File

@@ -0,0 +1,301 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 1024 1024"
style="enable-background:new 0 0 1024 1024;"
xml:space="preserve"
sodipodi:docname="Antares-shape-2.svg"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs80" /><sodipodi:namedview
id="namedview78"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="0.66015625"
inkscape:cx="210.55621"
inkscape:cy="511.2426"
inkscape:window-width="2560"
inkscape:window-height="1009"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g75" />
<style
type="text/css"
id="style2">
.st0{fill:url(#SVGID_1_);}
.st1{fill:url(#XMLID_15_);}
.st2{opacity:0.5;fill:#FFBC00;}
.st3{fill:#FFBC00;}
.st4{fill:url(#XMLID_16_);}
.st5{fill:url(#XMLID_17_);}
.st6{fill:url(#XMLID_18_);}
.st7{fill:url(#XMLID_19_);}
.st8{fill:url(#XMLID_20_);}
.st9{fill:url(#XMLID_21_);}
.st10{opacity:0.46;}
.st11{fill:url(#XMLID_23_);}
.st12{fill:url(#XMLID_24_);}
.st13{fill:url(#XMLID_25_);}
.st14{fill:url(#XMLID_27_);}
.st15{fill:url(#XMLID_28_);}
.st16{fill:url(#XMLID_29_);}
.st17{fill:url(#XMLID_30_);}
.st18{opacity:0.75;}
.st19{opacity:0.44;clip-path:url(#XMLID_31_);}
.st20{fill:url(#XMLID_32_);}
.st21{fill:url(#XMLID_33_);}
.st22{fill:url(#XMLID_34_);}
.st23{fill:url(#XMLID_35_);}
.st24{fill:url(#XMLID_37_);}
.st25{fill:url(#XMLID_38_);}
.st26{opacity:0.43;fill:#FFBC00;}
.st27{opacity:0.58;fill:#FFBC00;}
.st28{fill:#FFBE06;}
.st29{fill:none;}
.st30{fill:url(#XMLID_42_);}
.st31{fill:url(#XMLID_43_);}
.st32{fill:url(#XMLID_44_);}
.st33{fill:url(#XMLID_45_);}
.st34{fill:url(#XMLID_46_);}
.st35{fill:url(#SVGID_4_);}
.st36{fill:url(#SVGID_5_);}
.st37{fill:url(#SVGID_6_);}
.st38{fill:url(#SVGID_7_);}
.st39{fill:url(#SVGID_8_);}
.st40{fill:url(#SVGID_11_);}
.st41{fill:url(#SVGID_12_);}
.st42{fill:url(#SVGID_13_);}
.st43{fill:url(#SVGID_14_);}
.st44{fill:#C68D00;}
.st45{fill:#CE000F;}
</style>
<g
id="g75">
<radialGradient
id="XMLID_15_"
cx="358.2692"
cy="227.2655"
r="830.0055"
gradientUnits="userSpaceOnUse">
<stop
offset="0"
style="stop-color:#F6971E"
id="stop4" />
<stop
offset="0.6338"
style="stop-color:#F4592D"
id="stop6" />
<stop
offset="0.7025"
style="stop-color:#EF4F29"
id="stop8" />
<stop
offset="0.8178"
style="stop-color:#E1351D"
id="stop10" />
<stop
offset="0.9647"
style="stop-color:#CA0B0B"
id="stop12" />
<stop
offset="1"
style="stop-color:#C40006"
id="stop14" />
</radialGradient>
<path
id="XMLID_124_"
style="fill:#ffffff;fill-opacity:0.15000001"
class="st1"
d="M 510.30078 9.0996094 A 502.79999 502.79999 0 0 0 7.5 511.90039 A 502.79999 502.79999 0 0 0 510.30078 1014.6992 A 502.79999 502.79999 0 0 0 1013.0996 511.90039 A 502.79999 502.79999 0 0 0 510.30078 9.0996094 z M 326.17578 78.685547 C 349.74023 78.648438 372.16211 83.875 393.09961 95 C 420.19961 112.9 439.6 136.99922 455 172.69922 C 465.4 205.69922 469.30078 238.79961 465.80078 281.59961 C 457.70078 373.59961 411.09922 469.09961 341.19922 531.59961 C 312.29922 557.49961 284.4 573.59961 256.5 589.59961 L 256.69922 593.19922 C 279.39922 594.99922 301.39922 586.10078 326.19922 571.80078 C 415.89922 516.50078 483.49922 397.69922 504.69922 285.19922 C 495.49922 338.79922 478.19922 391.4 449.69922 445 C 423.79922 489.6 397.30039 527.1 359.40039 562 C 300.00039 612.9 238.89961 638.80078 183.09961 626.30078 C 122.39961 611.00078 90.399609 545.8 92.599609 461 C 92.399609 457.4 92.1 453.89961 89 455.59961 C 87.6 458.29961 84.700781 463.60078 83.300781 466.30078 C 69.000781 517.30078 77.1 562.79922 91 601.19922 C 116.5 666.39922 177.79922 688.69922 244.69922 676.19922 C 204.99922 685.99922 167.69922 683.3 134.19922 665.5 C 107.09922 647.6 84.599609 625.30039 70.599609 586.90039 C 55.899609 537.80039 50.700391 486.89922 70.400391 421.69922 C 95.300391 338.69922 139.50078 255.59922 207.30078 209.19922 C 227.30078 195.79922 245.99922 185.09961 267.69922 172.59961 C 270.79922 170.79961 270.60039 167.20039 268.90039 166.40039 C 159.40039 170.00039 46.500391 333.30039 20.900391 474.40039 C 36.400391 372.60039 87.500391 272.6 167.90039 198.5 C 213.50039 157.4 258.79922 136.89922 305.19922 130.69922 C 386.89922 122.69922 437.10078 194.1 434.80078 299.5 C 435.00078 303.1 436.70039 304.00039 438.40039 304.90039 C 446.50039 281.70039 451.39961 260.29922 451.59961 239.69922 C 451.50293 126.5894 381.21362 66.112098 291.87891 82.363281 C 288.79216 82.977349 285.71662 83.531343 282.59961 84.300781 C 285.71508 83.559319 288.80642 82.922208 291.87891 82.363281 C 303.5351 80.044429 314.99734 78.703151 326.17578 78.685547 z M 858.04883 508.3457 C 873.78027 508.64453 888.6875 512.44922 902.5 520.19922 C 920.3 532.49922 935.10078 547.69961 943.80078 573.59961 C 952.90078 606.59961 955.59961 640.70039 941.59961 683.90039 C 923.79961 739.00039 893.09961 793.80039 847.09961 823.90039 C 833.49961 832.60039 820.89922 839.4 806.19922 847.5 C 804.09922 848.6 804.20078 850.99922 805.30078 851.69922 C 878.70078 851.09922 956.39961 743.59922 975.59961 649.69922 C 963.79961 717.49922 928.2 783.50078 873.5 831.80078 C 842.5 858.60078 811.90078 871.59961 780.80078 875.09961 C 726.10078 879.29961 693.59922 830.9 696.69922 760.5 C 696.59922 758.1 695.50039 757.50039 694.40039 756.90039 C 688.70039 772.30039 685.09961 786.49922 684.59961 800.19922 C 683.15311 870.80636 723.40104 911.63389 777.53906 908.77344 C 757.57984 910.46578 738.70856 907.29881 721.59961 897.69922 C 703.79961 885.39922 691.10039 869.00039 681.40039 844.90039 C 674.90039 822.70039 672.79922 800.6 675.69922 772 C 682.39922 710.7 714.9 647.60078 762.5 606.80078 C 782.1 589.90078 801.00039 579.60078 819.90039 569.30078 L 819.80078 566.90039 C 804.70078 565.40039 789.89961 570.99922 773.09961 580.19922 C 712.39961 615.89922 665.50078 694.2 649.80078 769 C 656.70078 733.4 669.00078 698.39961 688.80078 663.09961 C 706.80078 633.69961 725.00078 609.00078 750.80078 586.30078 C 791.20078 553.20078 832.40039 536.80039 869.40039 545.90039 C 909.80039 556.90039 930.2 600.9 927.5 657.5 C 927.6 659.9 927.70078 662.29961 929.80078 661.09961 C 930.80078 659.29961 932.80078 655.8 933.80078 654 C 944.00078 620.2 939.3 589.70078 930.5 563.80078 C 914.4 519.90078 873.80039 504.1 828.90039 511.5 C 838.87539 509.25 848.60996 508.16641 858.04883 508.3457 z " />
<linearGradient
id="XMLID_16_"
gradientUnits="userSpaceOnUse"
x1="505.4734"
y1="-52.674"
x2="505.4734"
y2="155.1105">
<stop
offset="0"
style="stop-color:#FFFFFF;stop-opacity:0.4"
id="stop18" />
<stop
offset="1"
style="stop-color:#FFFFFF;stop-opacity:0"
id="stop20" />
</linearGradient>
<linearGradient
id="XMLID_17_"
gradientUnits="userSpaceOnUse"
x1="503.3253"
y1="-583.7885"
x2="503.3253"
y2="-376.4571"
gradientTransform="matrix(-1 0 0 -1 1017 456.5313)">
<stop
offset="5.263158e-03"
style="stop-color:#9E3A1D;stop-opacity:0.4"
id="stop24" />
<stop
offset="1"
style="stop-color:#9E3A1D;stop-opacity:0"
id="stop26" />
</linearGradient>
<linearGradient
id="XMLID_18_"
gradientUnits="userSpaceOnUse"
x1="506.1886"
y1="-38.7551"
x2="506.1886"
y2="169.0294"
gradientTransform="matrix(4.489700e-11 1 -1 4.489700e-11 1026.6101 -2.3899)">
<stop
offset="5.263158e-03"
style="stop-color:#9E3A1D;stop-opacity:0.4"
id="stop30" />
<stop
offset="1"
style="stop-color:#9E3A1D;stop-opacity:0"
id="stop32" />
</linearGradient>
<linearGradient
id="XMLID_19_"
gradientUnits="userSpaceOnUse"
x1="502.6101"
y1="-660.7308"
x2="502.6101"
y2="-450.373"
gradientTransform="matrix(-4.489700e-11 -1 1 -4.489700e-11 570.0789 1014.6101)">
<stop
offset="0"
style="stop-color:#FFFFFF;stop-opacity:0.4"
id="stop36" />
<stop
offset="1"
style="stop-color:#FFFFFF;stop-opacity:0"
id="stop38" />
</linearGradient>
<g
id="XMLID_39_"
class="st10">
<defs
id="defs43">
<ellipse
id="XMLID_36_"
transform="matrix(0.8019 -0.5974 0.5974 0.8019 -204.724 406.2339)"
class="st10"
cx="510.3"
cy="511.9"
ry="502.8"
rx="502.8" />
</defs>
<clipPath
id="XMLID_20_">
<use
xlink:href="#XMLID_36_"
style="overflow:visible;"
id="use45" />
</clipPath>
</g>
<g
id="XMLID_41_">
<linearGradient
id="XMLID_21_"
gradientUnits="userSpaceOnUse"
x1="64.4989"
y1="234.8705"
x2="401.1502"
y2="480.7042">
<stop
offset="0"
style="stop-color:#F4592D"
id="stop49" />
<stop
offset="1"
style="stop-color:#FFD900"
id="stop51" />
</linearGradient>
<linearGradient
id="XMLID_22_"
gradientUnits="userSpaceOnUse"
x1="438.1351"
y1="490.0881"
x2="196.6566"
y2="356.1772">
<stop
offset="0"
style="stop-color:#F64626"
id="stop55" />
<stop
offset="1"
style="stop-color:#FFD900"
id="stop57" />
</linearGradient>
</g>
<g
id="XMLID_11_">
<linearGradient
id="XMLID_23_"
gradientUnits="userSpaceOnUse"
x1="-316.9261"
y1="9.5862"
x2="-92.0354"
y2="173.8087"
gradientTransform="matrix(-0.9998 -2.148304e-02 2.148304e-02 -0.9998 625.9278 811.8477)">
<stop
offset="0"
style="stop-color:#F4592D"
id="stop62" />
<stop
offset="1"
style="stop-color:#FFD900"
id="stop64" />
</linearGradient>
<linearGradient
id="XMLID_24_"
gradientUnits="userSpaceOnUse"
x1="-52.9013"
y1="188.0779"
x2="-214.2144"
y2="98.6225"
gradientTransform="matrix(-0.9998 -2.148304e-02 2.148304e-02 -0.9998 625.9278 811.8477)">
<stop
offset="0"
style="stop-color:#F42C2D"
id="stop68" />
<stop
offset="1"
style="stop-color:#FFD900"
id="stop70" />
</linearGradient>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,301 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 1024 1024"
style="enable-background:new 0 0 1024 1024;"
xml:space="preserve"
sodipodi:docname="Antares-shape-1.svg"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs80" /><sodipodi:namedview
id="namedview78"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="0.66015625"
inkscape:cx="512"
inkscape:cy="511.2426"
inkscape:window-width="2560"
inkscape:window-height="1009"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g75" />
<style
type="text/css"
id="style2">
.st0{fill:url(#SVGID_1_);}
.st1{fill:url(#XMLID_15_);}
.st2{opacity:0.5;fill:#FFBC00;}
.st3{fill:#FFBC00;}
.st4{fill:url(#XMLID_16_);}
.st5{fill:url(#XMLID_17_);}
.st6{fill:url(#XMLID_18_);}
.st7{fill:url(#XMLID_19_);}
.st8{fill:url(#XMLID_20_);}
.st9{fill:url(#XMLID_21_);}
.st10{opacity:0.46;}
.st11{fill:url(#XMLID_23_);}
.st12{fill:url(#XMLID_24_);}
.st13{fill:url(#XMLID_25_);}
.st14{fill:url(#XMLID_27_);}
.st15{fill:url(#XMLID_28_);}
.st16{fill:url(#XMLID_29_);}
.st17{fill:url(#XMLID_30_);}
.st18{opacity:0.75;}
.st19{opacity:0.44;clip-path:url(#XMLID_31_);}
.st20{fill:url(#XMLID_32_);}
.st21{fill:url(#XMLID_33_);}
.st22{fill:url(#XMLID_34_);}
.st23{fill:url(#XMLID_35_);}
.st24{fill:url(#XMLID_37_);}
.st25{fill:url(#XMLID_38_);}
.st26{opacity:0.43;fill:#FFBC00;}
.st27{opacity:0.58;fill:#FFBC00;}
.st28{fill:#FFBE06;}
.st29{fill:none;}
.st30{fill:url(#XMLID_42_);}
.st31{fill:url(#XMLID_43_);}
.st32{fill:url(#XMLID_44_);}
.st33{fill:url(#XMLID_45_);}
.st34{fill:url(#XMLID_46_);}
.st35{fill:url(#SVGID_4_);}
.st36{fill:url(#SVGID_5_);}
.st37{fill:url(#SVGID_6_);}
.st38{fill:url(#SVGID_7_);}
.st39{fill:url(#SVGID_8_);}
.st40{fill:url(#SVGID_11_);}
.st41{fill:url(#SVGID_12_);}
.st42{fill:url(#SVGID_13_);}
.st43{fill:url(#SVGID_14_);}
.st44{fill:#C68D00;}
.st45{fill:#CE000F;}
</style>
<g
id="g75">
<radialGradient
id="XMLID_15_"
cx="358.2692"
cy="227.2655"
r="830.0055"
gradientUnits="userSpaceOnUse">
<stop
offset="0"
style="stop-color:#F6971E"
id="stop4" />
<stop
offset="0.6338"
style="stop-color:#F4592D"
id="stop6" />
<stop
offset="0.7025"
style="stop-color:#EF4F29"
id="stop8" />
<stop
offset="0.8178"
style="stop-color:#E1351D"
id="stop10" />
<stop
offset="0.9647"
style="stop-color:#CA0B0B"
id="stop12" />
<stop
offset="1"
style="stop-color:#C40006"
id="stop14" />
</radialGradient>
<path
id="XMLID_124_"
style="fill:#000000;fill-opacity:0.5"
class="st1"
d="M 510.30078 9.0996094 A 502.79999 502.79999 0 0 0 7.5 511.90039 A 502.79999 502.79999 0 0 0 510.30078 1014.6992 A 502.79999 502.79999 0 0 0 1013.0996 511.90039 A 502.79999 502.79999 0 0 0 510.30078 9.0996094 z M 326.17578 78.685547 C 349.74023 78.648438 372.16211 83.875 393.09961 95 C 420.19961 112.9 439.6 136.99922 455 172.69922 C 465.4 205.69922 469.30078 238.79961 465.80078 281.59961 C 457.70078 373.59961 411.09922 469.09961 341.19922 531.59961 C 312.29922 557.49961 284.4 573.59961 256.5 589.59961 L 256.69922 593.19922 C 279.39922 594.99922 301.39922 586.10078 326.19922 571.80078 C 415.89922 516.50078 483.49922 397.69922 504.69922 285.19922 C 495.49922 338.79922 478.19922 391.4 449.69922 445 C 423.79922 489.6 397.30039 527.1 359.40039 562 C 300.00039 612.9 238.89961 638.80078 183.09961 626.30078 C 122.39961 611.00078 90.399609 545.8 92.599609 461 C 92.399609 457.4 92.1 453.89961 89 455.59961 C 87.6 458.29961 84.700781 463.60078 83.300781 466.30078 C 69.000781 517.30078 77.1 562.79922 91 601.19922 C 116.5 666.39922 177.79922 688.69922 244.69922 676.19922 C 204.99922 685.99922 167.69922 683.3 134.19922 665.5 C 107.09922 647.6 84.599609 625.30039 70.599609 586.90039 C 55.899609 537.80039 50.700391 486.89922 70.400391 421.69922 C 95.300391 338.69922 139.50078 255.59922 207.30078 209.19922 C 227.30078 195.79922 245.99922 185.09961 267.69922 172.59961 C 270.79922 170.79961 270.60039 167.20039 268.90039 166.40039 C 159.40039 170.00039 46.500391 333.30039 20.900391 474.40039 C 36.400391 372.60039 87.500391 272.6 167.90039 198.5 C 213.50039 157.4 258.79922 136.89922 305.19922 130.69922 C 386.89922 122.69922 437.10078 194.1 434.80078 299.5 C 435.00078 303.1 436.70039 304.00039 438.40039 304.90039 C 446.50039 281.70039 451.39961 260.29922 451.59961 239.69922 C 451.50293 126.5894 381.21362 66.112098 291.87891 82.363281 C 288.79216 82.977349 285.71662 83.531343 282.59961 84.300781 C 285.71508 83.559319 288.80642 82.922208 291.87891 82.363281 C 303.5351 80.044429 314.99734 78.703151 326.17578 78.685547 z M 858.04883 508.3457 C 873.78027 508.64453 888.6875 512.44922 902.5 520.19922 C 920.3 532.49922 935.10078 547.69961 943.80078 573.59961 C 952.90078 606.59961 955.59961 640.70039 941.59961 683.90039 C 923.79961 739.00039 893.09961 793.80039 847.09961 823.90039 C 833.49961 832.60039 820.89922 839.4 806.19922 847.5 C 804.09922 848.6 804.20078 850.99922 805.30078 851.69922 C 878.70078 851.09922 956.39961 743.59922 975.59961 649.69922 C 963.79961 717.49922 928.2 783.50078 873.5 831.80078 C 842.5 858.60078 811.90078 871.59961 780.80078 875.09961 C 726.10078 879.29961 693.59922 830.9 696.69922 760.5 C 696.59922 758.1 695.50039 757.50039 694.40039 756.90039 C 688.70039 772.30039 685.09961 786.49922 684.59961 800.19922 C 683.15311 870.80636 723.40104 911.63389 777.53906 908.77344 C 757.57984 910.46578 738.70856 907.29881 721.59961 897.69922 C 703.79961 885.39922 691.10039 869.00039 681.40039 844.90039 C 674.90039 822.70039 672.79922 800.6 675.69922 772 C 682.39922 710.7 714.9 647.60078 762.5 606.80078 C 782.1 589.90078 801.00039 579.60078 819.90039 569.30078 L 819.80078 566.90039 C 804.70078 565.40039 789.89961 570.99922 773.09961 580.19922 C 712.39961 615.89922 665.50078 694.2 649.80078 769 C 656.70078 733.4 669.00078 698.39961 688.80078 663.09961 C 706.80078 633.69961 725.00078 609.00078 750.80078 586.30078 C 791.20078 553.20078 832.40039 536.80039 869.40039 545.90039 C 909.80039 556.90039 930.2 600.9 927.5 657.5 C 927.6 659.9 927.70078 662.29961 929.80078 661.09961 C 930.80078 659.29961 932.80078 655.8 933.80078 654 C 944.00078 620.2 939.3 589.70078 930.5 563.80078 C 914.4 519.90078 873.80039 504.1 828.90039 511.5 C 838.87539 509.25 848.60996 508.16641 858.04883 508.3457 z " />
<linearGradient
id="XMLID_16_"
gradientUnits="userSpaceOnUse"
x1="505.4734"
y1="-52.674"
x2="505.4734"
y2="155.1105">
<stop
offset="0"
style="stop-color:#FFFFFF;stop-opacity:0.4"
id="stop18" />
<stop
offset="1"
style="stop-color:#FFFFFF;stop-opacity:0"
id="stop20" />
</linearGradient>
<linearGradient
id="XMLID_17_"
gradientUnits="userSpaceOnUse"
x1="503.3253"
y1="-583.7885"
x2="503.3253"
y2="-376.4571"
gradientTransform="matrix(-1 0 0 -1 1017 456.5313)">
<stop
offset="5.263158e-03"
style="stop-color:#9E3A1D;stop-opacity:0.4"
id="stop24" />
<stop
offset="1"
style="stop-color:#9E3A1D;stop-opacity:0"
id="stop26" />
</linearGradient>
<linearGradient
id="XMLID_18_"
gradientUnits="userSpaceOnUse"
x1="506.1886"
y1="-38.7551"
x2="506.1886"
y2="169.0294"
gradientTransform="matrix(4.489700e-11 1 -1 4.489700e-11 1026.6101 -2.3899)">
<stop
offset="5.263158e-03"
style="stop-color:#9E3A1D;stop-opacity:0.4"
id="stop30" />
<stop
offset="1"
style="stop-color:#9E3A1D;stop-opacity:0"
id="stop32" />
</linearGradient>
<linearGradient
id="XMLID_19_"
gradientUnits="userSpaceOnUse"
x1="502.6101"
y1="-660.7308"
x2="502.6101"
y2="-450.373"
gradientTransform="matrix(-4.489700e-11 -1 1 -4.489700e-11 570.0789 1014.6101)">
<stop
offset="0"
style="stop-color:#FFFFFF;stop-opacity:0.4"
id="stop36" />
<stop
offset="1"
style="stop-color:#FFFFFF;stop-opacity:0"
id="stop38" />
</linearGradient>
<g
id="XMLID_39_"
class="st10">
<defs
id="defs43">
<ellipse
id="XMLID_36_"
transform="matrix(0.8019 -0.5974 0.5974 0.8019 -204.724 406.2339)"
class="st10"
cx="510.3"
cy="511.9"
ry="502.8"
rx="502.8" />
</defs>
<clipPath
id="XMLID_20_">
<use
xlink:href="#XMLID_36_"
style="overflow:visible;"
id="use45" />
</clipPath>
</g>
<g
id="XMLID_41_">
<linearGradient
id="XMLID_21_"
gradientUnits="userSpaceOnUse"
x1="64.4989"
y1="234.8705"
x2="401.1502"
y2="480.7042">
<stop
offset="0"
style="stop-color:#F4592D"
id="stop49" />
<stop
offset="1"
style="stop-color:#FFD900"
id="stop51" />
</linearGradient>
<linearGradient
id="XMLID_22_"
gradientUnits="userSpaceOnUse"
x1="438.1351"
y1="490.0881"
x2="196.6566"
y2="356.1772">
<stop
offset="0"
style="stop-color:#F64626"
id="stop55" />
<stop
offset="1"
style="stop-color:#FFD900"
id="stop57" />
</linearGradient>
</g>
<g
id="XMLID_11_">
<linearGradient
id="XMLID_23_"
gradientUnits="userSpaceOnUse"
x1="-316.9261"
y1="9.5862"
x2="-92.0354"
y2="173.8087"
gradientTransform="matrix(-0.9998 -2.148304e-02 2.148304e-02 -0.9998 625.9278 811.8477)">
<stop
offset="0"
style="stop-color:#F4592D"
id="stop62" />
<stop
offset="1"
style="stop-color:#FFD900"
id="stop64" />
</linearGradient>
<linearGradient
id="XMLID_24_"
gradientUnits="userSpaceOnUse"
x1="-52.9013"
y1="188.0779"
x2="-214.2144"
y2="98.6225"
gradientTransform="matrix(-0.9998 -2.148304e-02 2.148304e-02 -0.9998 625.9278 811.8477)">
<stop
offset="0"
style="stop-color:#F42C2D"
id="stop68" />
<stop
offset="1"
style="stop-color:#FFD900"
id="stop70" />
</linearGradient>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -1,11 +1,6 @@
import Tables from '@/ipc-api/Tables'; import Tables from '@/ipc-api/Tables';
export default { export default {
computed: {
schema () {
return this.workspace.breadcrumbs.schema;
}
},
methods: { methods: {
async updateField (payload) { async updateField (payload) {
this.isQuering = true; this.isQuering = true;

View File

@@ -63,6 +63,12 @@
background-color: $primary-color; background-color: $primary-color;
} }
} }
&.btn-clear {
&:hover {
background: rgba($light-color, 20%);
}
}
} }
.modal { .modal {
@@ -191,19 +197,8 @@
color: $body-font-color-dark; color: $body-font-color-dark;
} }
& &.tools-dropdown { &.tools-dropdown {
.tab-link:focus { background-color: $bg-color-light-dark;
color: $primary-color;
}
.menu {
.menu-item a {
&:hover {
color: $primary-color;
background: $bg-color-gray;
}
}
}
} }
} }
@@ -392,11 +387,6 @@
background: rgba(48, 55, 66, 0.95); background: rgba(48, 55, 66, 0.95);
color: #fff; color: #fff;
} }
&:hover .ex-tooltip-content {
visibility: visible;
opacity: 1;
}
} }
#footer { #footer {

View File

@@ -168,11 +168,6 @@
background: rgba(48, 55, 66, 0.95); background: rgba(48, 55, 66, 0.95);
color: #fff; color: #fff;
} }
&:hover .ex-tooltip-content {
visibility: visible;
opacity: 1;
}
} }
.workspace { .workspace {
@@ -191,6 +186,16 @@
} }
} }
.workspace-tabs {
.tab-block {
.tab-item {
&.tools-dropdown {
background-color: $body-bg;
}
}
}
}
.workspace-query-results { .workspace-query-results {
.table { .table {
.th { .th {

View File

@@ -16,7 +16,8 @@ export default {
line_wrap: persistentStore.get('line_wrap', true), line_wrap: persistentStore.get('line_wrap', true),
application_theme: persistentStore.get('application_theme', 'dark'), application_theme: persistentStore.get('application_theme', 'dark'),
editor_theme: persistentStore.get('editor_theme', 'twilight'), editor_theme: persistentStore.get('editor_theme', 'twilight'),
editor_font_size: persistentStore.get('editor_font_size', 'medium') editor_font_size: persistentStore.get('editor_font_size', 'medium'),
restore_tabs: persistentStore.get('restore_tabs', true)
}, },
getters: { getters: {
getLocale: state => state.locale, getLocale: state => state.locale,
@@ -28,7 +29,8 @@ export default {
getLineWrap: state => state.line_wrap, getLineWrap: state => state.line_wrap,
getApplicationTheme: state => state.application_theme, getApplicationTheme: state => state.application_theme,
getEditorTheme: state => state.editor_theme, getEditorTheme: state => state.editor_theme,
getEditorFontSize: state => state.editor_font_size getEditorFontSize: state => state.editor_font_size,
getRestoreTabs: state => state.restore_tabs
}, },
mutations: { mutations: {
SET_LOCALE (state, locale) { SET_LOCALE (state, locale) {
@@ -71,6 +73,10 @@ export default {
SET_EDITOR_FONT_SIZE (state, size) { SET_EDITOR_FONT_SIZE (state, size) {
state.editor_font_size = size; state.editor_font_size = size;
persistentStore.set('editor_font_size', state.editor_font_size); persistentStore.set('editor_font_size', state.editor_font_size);
},
SET_RESTORE_TABS (state, val) {
state.restore_tabs = val;
persistentStore.set('restore_tabs', state.restore_tabs);
} }
}, },
actions: { actions: {
@@ -103,6 +109,9 @@ export default {
}, },
changeEditorFontSize ({ commit }, size) { changeEditorFontSize ({ commit }, size) {
commit('SET_EDITOR_FONT_SIZE', size); commit('SET_EDITOR_FONT_SIZE', size);
},
changeRestoreTabs ({ commit }, size) {
commit('SET_RESTORE_TABS', size);
} }
} }
}; };

View File

@@ -1,20 +1,18 @@
'use strict'; 'use strict';
import Store from 'electron-store';
import Connection from '@/ipc-api/Connection'; import Connection from '@/ipc-api/Connection';
import Schema from '@/ipc-api/Schema'; import Schema from '@/ipc-api/Schema';
import Users from '@/ipc-api/Users'; import Users from '@/ipc-api/Users';
import { uidGen } from 'common/libs/uidGen'; import { uidGen } from 'common/libs/uidGen';
const persistentStore = new Store({ name: 'tabs' });
const tabIndex = []; const tabIndex = [];
let lastBreadcrumbs = {};
export default { export default {
namespaced: true, namespaced: true,
strict: true, strict: true,
state: { state: {
workspaces: [], workspaces: [],
selected_workspace: null, selected_workspace: null
has_unsaved_changes: false,
is_unsaved_discard_modal: false,
pending_breadcrumbs: {}
}, },
getters: { getters: {
getSelected: state => { getSelected: state => {
@@ -45,9 +43,6 @@ export default {
}, },
getSearchTerm: state => uid => { getSearchTerm: state => uid => {
return state.workspaces.find(workspace => workspace.uid === uid).search_term; return state.workspaces.find(workspace => workspace.uid === uid).search_term;
},
isUnsavedDiscardModal: state => {
return state.is_unsaved_discard_modal;
} }
}, },
mutations: { mutations: {
@@ -60,6 +55,15 @@ export default {
SET_CONNECTED (state, payload) { SET_CONNECTED (state, payload) {
const { uid, client, dataTypes, indexTypes, customizations, structure, version } = payload; const { uid, client, dataTypes, indexTypes, customizations, structure, version } = payload;
const cachedTabs = payload.restoreTabs ? persistentStore.get(uid, []) : [];
if (cachedTabs.length) {
tabIndex[uid] = cachedTabs.reduce((acc, curr) => {
if (curr.index > acc) acc = curr.index;
return acc;
}, null);
}
state.workspaces = state.workspaces.map(workspace => workspace.uid === uid state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
? { ? {
...workspace, ...workspace,
@@ -69,6 +73,8 @@ export default {
customizations, customizations,
structure, structure,
connection_status: 'connected', connection_status: 'connected',
tabs: cachedTabs,
selected_tab: cachedTabs.length ? cachedTabs[0].uid : null,
version version
} }
: workspace); : workspace);
@@ -178,18 +184,24 @@ export default {
} }
: workspace); : workspace);
}, },
NEW_TAB (state, { uid, tab, content, autorun }) { NEW_TAB (state, { uid, tab, content, type, autorun, schema, elementName, elementType }) {
if (type === 'query')
tabIndex[uid] = tabIndex[uid] ? ++tabIndex[uid] : 1; tabIndex[uid] = tabIndex[uid] ? ++tabIndex[uid] : 1;
const newTab = { const newTab = {
uid: tab, uid: tab,
index: tabIndex[uid], index: type === 'query' ? tabIndex[uid] : null,
selected: false, selected: false,
type: 'query', type,
schema,
elementName,
elementType,
fields: [], fields: [],
keyUsage: [], keyUsage: [],
content: content || '', content: content || '',
autorun: !!autorun autorun: !!autorun
}; };
state.workspaces = state.workspaces.map(workspace => { state.workspaces = state.workspaces.map(workspace => {
if (workspace.uid === uid) { if (workspace.uid === uid) {
return { return {
@@ -200,6 +212,8 @@ export default {
else else
return workspace; return workspace;
}); });
persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
}, },
REMOVE_TAB (state, { uid, tab: tUid }) { REMOVE_TAB (state, { uid, tab: tUid }) {
state.workspaces = state.workspaces.map(workspace => { state.workspaces = state.workspaces.map(workspace => {
@@ -212,10 +226,78 @@ export default {
else else
return workspace; return workspace;
}); });
persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
},
REMOVE_TABS (state, { uid, schema, elementName, elementType }) { // Multiple tabs based on schema and element name
if (elementType === 'procedure') elementType = 'routine'; // TODO: pass directly "routine"
state.workspaces = state.workspaces.map(workspace => {
if (workspace.uid === uid) {
return {
...workspace,
tabs: workspace.tabs.filter(tab =>
tab.schema !== schema ||
tab.elementName !== elementName ||
tab.elementType !== elementType
)
};
}
else
return workspace;
});
persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
},
REPLACE_TAB (state, { uid, tab: tUid, type, schema, content, elementName, elementType }) {
state.workspaces = state.workspaces.map(workspace => {
if (workspace.uid === uid) {
return {
...workspace,
tabs: workspace.tabs.map(tab => {
if (tab.uid === tUid)
return { ...tab, type, schema, content, elementName, elementType };
return tab;
})
};
}
else
return workspace;
});
persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
},
RENAME_TABS (state, { uid, schema, elementName, elementType, elementNewName }) {
state.workspaces = state.workspaces.map(workspace => {
if (workspace.uid === uid) {
return {
...workspace,
tabs: workspace.tabs.map(tab => {
if (tab.elementName === elementName && tab.schema === schema) {
return {
...tab,
elementName: elementNewName
};
}
return tab;
})
};
}
else
return workspace;
});
persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
}, },
SELECT_TAB (state, { uid, tab }) { SELECT_TAB (state, { uid, tab }) {
state.workspaces = state.workspaces.map(workspace => workspace.uid === uid ? { ...workspace, selected_tab: tab } : workspace); state.workspaces = state.workspaces.map(workspace => workspace.uid === uid ? { ...workspace, selected_tab: tab } : workspace);
}, },
UPDATE_TABS (state, { uid, tabs }) {
state.workspaces = state.workspaces.map(workspace => workspace.uid === uid ? { ...workspace, tabs } : workspace);
persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
},
SET_TAB_FIELDS (state, { cUid, tUid, fields }) { SET_TAB_FIELDS (state, { cUid, tUid, fields }) {
state.workspaces = state.workspaces.map(workspace => { state.workspaces = state.workspaces.map(workspace => {
if (workspace.uid === cUid) { if (workspace.uid === cUid) {
@@ -232,6 +314,8 @@ export default {
else else
return workspace; return workspace;
}); });
persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
}, },
SET_TAB_KEY_USAGE (state, { cUid, tUid, keyUsage }) { SET_TAB_KEY_USAGE (state, { cUid, tUid, keyUsage }) {
state.workspaces = state.workspaces.map(workspace => { state.workspaces = state.workspaces.map(workspace => {
@@ -249,15 +333,25 @@ export default {
else else
return workspace; return workspace;
}); });
persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
}, },
SET_UNSAVED_CHANGES (state, val) { SET_UNSAVED_CHANGES (state, { uid, tUid, isChanged }) {
state.has_unsaved_changes = !!val; state.workspaces = state.workspaces.map(workspace => {
}, if (workspace.uid === uid) {
SET_UNSAVED_DISCARD_MODAL (state, val) { return {
state.is_unsaved_discard_modal = !!val; ...workspace,
}, tabs: workspace.tabs.map(tab => {
SET_PENDING_BREADCRUMBS (state, payload) { if (tab.uid === tUid)
state.pending_breadcrumbs = payload; return { ...tab, isChanged };
return tab;
})
};
}
else
return workspace;
});
}, },
ADD_LOADED_SCHEMA (state, payload) { ADD_LOADED_SCHEMA (state, payload) {
state.workspaces = state.workspaces.map(workspace => { state.workspaces = state.workspaces.map(workspace => {
@@ -271,7 +365,7 @@ export default {
selectWorkspace ({ commit }, uid) { selectWorkspace ({ commit }, uid) {
commit('SELECT_WORKSPACE', uid); commit('SELECT_WORKSPACE', uid);
}, },
async connectWorkspace ({ dispatch, commit }, connection) { async connectWorkspace ({ dispatch, commit, getters, rootGetters }, connection) {
commit('SET_CONNECTING', connection.uid); commit('SET_CONNECTING', connection.uid);
try { try {
@@ -304,6 +398,7 @@ export default {
if (status === 'error') if (status === 'error')
dispatch('notifications/addNotification', { status, message: version }, { root: true }); dispatch('notifications/addNotification', { status, message: version }, { root: true });
// Check if Maria or MySQL
const isMySQL = version.name.includes('MySQL'); const isMySQL = version.name.includes('MySQL');
if (isMySQL && connection.client !== 'mysql') { if (isMySQL && connection.client !== 'mysql') {
@@ -324,7 +419,8 @@ export default {
indexTypes, indexTypes,
customizations, customizations,
structure: response, structure: response,
version version,
restoreTabs: rootGetters['settings/getRestoreTabs']
}); });
dispatch('refreshCollations', connection.uid); dispatch('refreshCollations', connection.uid);
dispatch('refreshVariables', connection.uid); dispatch('refreshVariables', connection.uid);
@@ -414,7 +510,7 @@ export default {
commit('SET_DISCONNECTED', uid); commit('SET_DISCONNECTED', uid);
commit('SELECT_TAB', { uid, tab: 0 }); commit('SELECT_TAB', { uid, tab: 0 });
}, },
addWorkspace ({ commit, dispatch, getters }, uid) { addWorkspace ({ commit }, uid) {
const workspace = { const workspace = {
uid, uid,
connection_status: 'disconnected', connection_status: 'disconnected',
@@ -430,75 +526,211 @@ export default {
}; };
commit('ADD_WORKSPACE', workspace); commit('ADD_WORKSPACE', workspace);
if (getters.getWorkspace(uid).tabs.length < 3)
dispatch('newTab', { uid });
dispatch('setUnsavedChanges', false);
}, },
changeBreadcrumbs ({ state, commit, getters }, payload) { changeBreadcrumbs ({ commit, getters }, payload) {
if (state.has_unsaved_changes) {
commit('SET_UNSAVED_DISCARD_MODAL', true);
commit('SET_PENDING_BREADCRUMBS', payload);
return;
}
const breadcrumbsObj = { const breadcrumbsObj = {
schema: null, schema: null,
table: null, table: null,
trigger: null, trigger: null,
triggerFunction: null,
procedure: null, procedure: null,
function: null, function: null,
scheduler: null, scheduler: null,
view: null view: null,
query: null
}; };
const hasLastChildren = Object.keys(lastBreadcrumbs).filter(b => b !== 'schema').some(b => lastBreadcrumbs[b]);
const hasChildren = Object.keys(payload).filter(b => b !== 'schema').some(b => payload[b]);
if (lastBreadcrumbs.schema === payload.schema && hasLastChildren && !hasChildren) return;
if (lastBreadcrumbs.schema !== payload.schema)
Schema.useSchema({ uid: getters.getSelected, schema: payload.schema });
commit('CHANGE_BREADCRUMBS', { uid: getters.getSelected, breadcrumbs: { ...breadcrumbsObj, ...payload } }); commit('CHANGE_BREADCRUMBS', { uid: getters.getSelected, breadcrumbs: { ...breadcrumbsObj, ...payload } });
lastBreadcrumbs = { ...breadcrumbsObj, ...payload }; },
addLoadedSchema ({ commit, getters }, schema) {
if (payload.schema) commit('ADD_LOADED_SCHEMA', { uid: getters.getSelected, schema });
commit('ADD_LOADED_SCHEMA', { uid: getters.getSelected, schema: payload.schema });
}, },
setSearchTerm ({ commit, getters }, term) { setSearchTerm ({ commit, getters }, term) {
commit('SET_SEARCH_TERM', { uid: getters.getSelected, term }); commit('SET_SEARCH_TERM', { uid: getters.getSelected, term });
}, },
newTab ({ commit }, { uid, content, autorun }) { newTab ({ state, commit }, { uid, content, type, autorun, schema, elementName, elementType }) {
const tab = uidGen('T'); let tabUid;
const workspaceTabs = state.workspaces.find(workspace => workspace.uid === uid);
commit('NEW_TAB', { uid, tab, content, autorun }); switch (type) {
commit('SELECT_TAB', { uid, tab }); case 'temp-data': {
const existentTab = workspaceTabs
? workspaceTabs.tabs.find(tab =>
tab.schema === schema &&
tab.elementName === elementName &&
tab.elementType === elementType &&
['temp-data', 'data'].includes(tab.type))
: false;
if (existentTab) { // if data tab exists
tabUid = existentTab.uid;
}
else {
const tempTabs = workspaceTabs ? workspaceTabs.tabs.filter(tab => tab.type === 'temp-data') : false;
if (tempTabs && tempTabs.length) { // if temp table already opened
for (const tab of tempTabs) {
commit('REPLACE_TAB', { uid, tab: tab.uid, type, schema, elementName, elementType });
tabUid = tab.uid;
}
}
else {
tabUid = uidGen('T');
commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
}
}
}
break;
case 'data': {
const existentTab = workspaceTabs
? workspaceTabs.tabs.find(tab =>
tab.schema === schema &&
tab.elementName === elementName &&
tab.elementType === elementType &&
['temp-data', 'data'].includes(tab.type))
: false;
if (existentTab) {
commit('REPLACE_TAB', { uid, tab: existentTab.uid, type, schema, elementName, elementType });
tabUid = existentTab.uid;
}
else {
tabUid = uidGen('T');
commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
}
}
break;
case 'table-props': {
const existentTab = workspaceTabs
? workspaceTabs.tabs.find(tab =>
tab.elementName === elementName &&
tab.elementType === elementType &&
tab.type === type)
: false;
if (existentTab) {
commit('REPLACE_TAB', { uid, tab: existentTab.uid, type, schema, elementName, elementType });
tabUid = existentTab.uid;
}
else {
tabUid = uidGen('T');
commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
}
}
break;
case 'temp-trigger-props':
case 'temp-trigger-function-props':
case 'temp-function-props':
case 'temp-routine-props':
case 'temp-scheduler-props': {
const existentTab = workspaceTabs
? workspaceTabs.tabs.find(tab =>
tab.schema === schema &&
tab.elementName === elementName &&
tab.elementType === elementType &&
[type, type.replace('temp-', '')].includes(tab.type))
: false;
if (existentTab) { // if tab exists
tabUid = existentTab.uid;
}
else {
const tempTabs = workspaceTabs ? workspaceTabs.tabs.filter(tab => tab.type.includes('temp-')) : false;
if (tempTabs && tempTabs.length) { // if temp tab already opened
for (const tab of tempTabs) {
if (tab.isChanged) {
commit('REPLACE_TAB', { // make permanent a temp table with unsaved changes
uid,
tab: tab.uid,
type: tab.type.replace('temp-', ''),
schema: tab.schema,
elementName: tab.elementName,
elementType: tab.elementType
});
tabUid = uidGen('T');
commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
}
else {
commit('REPLACE_TAB', { uid, tab: tab.uid, type, schema, elementName, elementType });
tabUid = tab.uid;
}
}
}
else {
tabUid = uidGen('T');
commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
}
}
}
break;
case 'trigger-props':
case 'trigger-function-props':
case 'function-props':
case 'routine-props':
case 'scheduler-props': {
const existentTab = workspaceTabs
? workspaceTabs.tabs.find(tab =>
tab.schema === schema &&
tab.elementName === elementName &&
tab.elementType === elementType &&
[`temp-${type}`, type].includes(tab.type))
: false;
if (existentTab) {
commit('REPLACE_TAB', { uid, tab: existentTab.uid, type, schema, elementName, elementType });
tabUid = existentTab.uid;
}
else {
tabUid = uidGen('T');
commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
}
}
break;
default:
tabUid = uidGen('T');
commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
break;
}
commit('SELECT_TAB', { uid, tab: tabUid });
}, },
removeTab ({ commit }, payload) { checkSelectedTabExists ({ state, commit }, uid) {
const workspace = state.workspaces.find(workspace => workspace.uid === uid);
const isSelectedExistent = workspace
? workspace.tabs.some(tab => tab.uid === workspace.selected_tab)
: false;
if (!isSelectedExistent && workspace.tabs.length)
commit('SELECT_TAB', { uid, tab: workspace.tabs[workspace.tabs.length - 1].uid });
},
updateTabContent ({ commit }, { uid, tab, type, schema, content }) {
commit('REPLACE_TAB', { uid, tab, type, schema, content });
},
renameTabs ({ commit }, payload) {
commit('RENAME_TABS', payload);
},
removeTab ({ commit, dispatch }, payload) {
commit('REMOVE_TAB', payload); commit('REMOVE_TAB', payload);
dispatch('checkSelectedTabExists', payload.uid);
},
removeTabs ({ commit, dispatch }, payload) {
commit('REMOVE_TABS', payload);
dispatch('checkSelectedTabExists', payload.uid);
}, },
selectTab ({ commit }, payload) { selectTab ({ commit }, payload) {
commit('SELECT_TAB', payload); commit('SELECT_TAB', payload);
}, },
updateTabs ({ commit }, payload) {
commit('UPDATE_TABS', payload);
},
setTabFields ({ commit }, payload) { setTabFields ({ commit }, payload) {
commit('SET_TAB_FIELDS', payload); commit('SET_TAB_FIELDS', payload);
}, },
setTabKeyUsage ({ commit }, payload) { setTabKeyUsage ({ commit }, payload) {
commit('SET_TAB_KEY_USAGE', payload); commit('SET_TAB_KEY_USAGE', payload);
}, },
setUnsavedChanges ({ commit }, val) { setUnsavedChanges ({ commit }, payload) {
commit('SET_UNSAVED_CHANGES', val); commit('SET_UNSAVED_CHANGES', payload);
},
discardUnsavedChanges ({ state, commit, dispatch }) {
dispatch('setUnsavedChanges', false);
dispatch('changeBreadcrumbs', state.pending_breadcrumbs);
commit('SET_UNSAVED_DISCARD_MODAL', false);
commit('SET_PENDING_BREADCRUMBS', {});
},
closeUnsavedChangesModal ({ commit }) {
commit('SET_UNSAVED_DISCARD_MODAL', false);
} }
} }
}; };