mirror of
https://github.com/Fabio286/antares.git
synced 2025-06-05 21:59:22 +02:00
Compare commits
38 Commits
Author | SHA1 | Date | |
---|---|---|---|
04fcaf2f6e | |||
8b914446b0 | |||
a11bac504c | |||
b9ed8dd610 | |||
1cf6485896 | |||
4bc9bbfb34 | |||
4923128236 | |||
8ff6e70145 | |||
afcf1c86ed | |||
0200ae4a0f | |||
dbe7b9dd23 | |||
ceab4ef243 | |||
1e7d4ca347 | |||
c0a32c040e | |||
f150508547 | |||
49d71722e2 | |||
59a50bc014 | |||
41d75b127c | |||
0cbea9d100 | |||
e351c903a8 | |||
6e55f27b23 | |||
27fd9ec203 | |||
0ec2710872 | |||
3bcd02fc4e | |||
aa33850286 | |||
82fdc0bcd7 | |||
d695c9f8d2 | |||
b32132ad84 | |||
3126625461 | |||
ab307f82b1 | |||
0df2b836b1 | |||
6611aad840 | |||
8c4aaec167 | |||
b7053bdf80 | |||
b6b7be098a | |||
56f2a27f00 | |||
dcf469ebed | |||
d94b49febf |
7
.versionrc.json
Normal file
7
.versionrc.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"types": [
|
||||||
|
{"type":"feat","section":"Features"},
|
||||||
|
{"type":"perf","section":"Improvements"},
|
||||||
|
{"type":"fix","section":"Bug Fixes"}
|
||||||
|
]
|
||||||
|
}
|
65
CHANGELOG.md
65
CHANGELOG.md
@@ -2,6 +2,71 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
### [0.0.15](https://github.com/Fabio286/antares/compare/v0.0.14...v0.0.15) (2021-01-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* functions and schedulers in query suggestions ([8ff6e70](https://github.com/Fabio286/antares/commit/8ff6e70145ed2a207ae8b23a2c688258382a5d74))
|
||||||
|
* loading animation in properties tabs ([1cf6485](https://github.com/Fabio286/antares/commit/1cf64858964f4894913db42f7c268013bb06e40b))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* error retriving dato of some schedulers ([b9ed8dd](https://github.com/Fabio286/antares/commit/b9ed8dd610e3be1489e01cf53f7d632cb1bd6ac5))
|
||||||
|
* unable to call stored routines from query tabs ([4923128](https://github.com/Fabio286/antares/commit/4923128236131482ca948ae8052c294bd9269ed0))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* better fields type detection ([4bc9bbf](https://github.com/Fabio286/antares/commit/4bc9bbfb34ebdc51061f718cdf9cbca8507fa0f4))
|
||||||
|
* big performance improvement in database structure loading ([a11bac5](https://github.com/Fabio286/antares/commit/a11bac504cd4ee865ea6c614a15ee809dc38202e))
|
||||||
|
|
||||||
|
### [0.0.14](https://github.com/Fabio286/antares/compare/v0.0.13...v0.0.14) (2021-01-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* export data tables to json or csv file ([0cbea9d](https://github.com/Fabio286/antares/commit/0cbea9d1007304a5b9cf893d165b4b4104266651))
|
||||||
|
* functions creation ([49d7172](https://github.com/Fabio286/antares/commit/49d71722e26172232f7b54c6568e1e588ce0d049))
|
||||||
|
* functions delete ([59a50bc](https://github.com/Fabio286/antares/commit/59a50bc014facc9643f9153cff61dc9d5a8605a9))
|
||||||
|
* functions edit ([41d75b1](https://github.com/Fabio286/antares/commit/41d75b127cbcf1481fd259a14e6e7688638e18a4))
|
||||||
|
* scheduler edit ([ceab4ef](https://github.com/Fabio286/antares/commit/ceab4ef243881ba64517fb95320844a21fce4849))
|
||||||
|
* schedulers creation ([dbe7b9d](https://github.com/Fabio286/antares/commit/dbe7b9dd239248e806377ae6236b477456f175a3))
|
||||||
|
* schedulers delete ([1e7d4ca](https://github.com/Fabio286/antares/commit/1e7d4ca347f4b9337ff266ec78bb4bbc6dd20d4d))
|
||||||
|
* triggers and stored routines in sql suggestions ([e351c90](https://github.com/Fabio286/antares/commit/e351c903a8a8d7e908d6a7d54c0491438ac6f024))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* error with empty functions/procedures ([f150508](https://github.com/Fabio286/antares/commit/f1505085477a760a768a7d245c9517a858c1379c))
|
||||||
|
* removed internal row _id from exported files ([c0a32c0](https://github.com/Fabio286/antares/commit/c0a32c040e653729ef80d580d6dd1796d1b2adcd))
|
||||||
|
|
||||||
|
### [0.0.13](https://github.com/Fabio286/antares/compare/v0.0.12...v0.0.13) (2021-01-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* option to toggle line wrap mode ([d94b49f](https://github.com/Fabio286/antares/commit/d94b49febf54b0200127859f2a8ed7ef591e56ab))
|
||||||
|
* select definer in view creation/edit ([ab307f8](https://github.com/Fabio286/antares/commit/ab307f82b1d78c5f9571233090b6678a964bd674))
|
||||||
|
* stored routines creation ([3bcd02f](https://github.com/Fabio286/antares/commit/3bcd02fc4ea9a4b780305212f906d6d78c7a8dae))
|
||||||
|
* stored routines delete ([aa33850](https://github.com/Fabio286/antares/commit/aa3385028685417860b3ce985cc7a74f9da377ad))
|
||||||
|
* stored routines edit ([82fdc0b](https://github.com/Fabio286/antares/commit/82fdc0bcd7514b321c1c9852a773adacf81baf87))
|
||||||
|
* triggers creation ([d695c9f](https://github.com/Fabio286/antares/commit/d695c9f8d2418a6a4523a7a242fa1a8cba80e035))
|
||||||
|
* triggers delete ([b32132a](https://github.com/Fabio286/antares/commit/b32132ad84d5798555b80eec3c624b681c37c339))
|
||||||
|
* triggers edit ([3126625](https://github.com/Fabio286/antares/commit/3126625461f4b6d68d641b6b0eda8fcd390bb636))
|
||||||
|
* views creation ([8c4aaec](https://github.com/Fabio286/antares/commit/8c4aaec167f58333a343b52927205b68137ad408))
|
||||||
|
* views deletion ([dcf469e](https://github.com/Fabio286/antares/commit/dcf469ebed6252b4a496800206e0c34cd83b1f5e))
|
||||||
|
* views edit ([56f2a27](https://github.com/Fabio286/antares/commit/56f2a27f0059cc10316204210db078a97408973c))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* breadcrumb not change after table rename ([b6b7be0](https://github.com/Fabio286/antares/commit/b6b7be098ad5ab4d55bfe05a7f862f045c1f54da))
|
||||||
|
* unable to rename views ([b7053bd](https://github.com/Fabio286/antares/commit/b7053bdf8036d027e1685d6b5080d6b927a80e08))
|
||||||
|
* wrong new stored routine modal icon ([0ec2710](https://github.com/Fabio286/antares/commit/0ec2710872c692b3feac076a3250d3b760af4009))
|
||||||
|
* wrong or duplicate fields in some queries ([0df2b83](https://github.com/Fabio286/antares/commit/0df2b836b15436a2397d6a2202bd049b5cd53de4))
|
||||||
|
|
||||||
### [0.0.12](https://github.com/Fabio286/antares/compare/v0.0.11...v0.0.12) (2020-12-24)
|
### [0.0.12](https://github.com/Fabio286/antares/compare/v0.0.11...v0.0.12) (2020-12-24)
|
||||||
|
|
||||||
|
|
||||||
|
20
README.md
20
README.md
@@ -32,6 +32,7 @@ An application created with minimalism and semplicity in mind, with features in
|
|||||||
- Multiple database connections at same time.
|
- Multiple database connections at same time.
|
||||||
- Database management (add/edit/delete).
|
- Database management (add/edit/delete).
|
||||||
- Full tables management, including indexes and foreign keys.
|
- Full tables management, including indexes and foreign keys.
|
||||||
|
- Views, triggers, stored routines, functions and schedulers management (add/edit/delete).
|
||||||
- Run queries on multiple tabs.
|
- Run queries on multiple tabs.
|
||||||
- Query suggestions and auto complete.
|
- Query suggestions and auto complete.
|
||||||
- Native dark theme.
|
- Native dark theme.
|
||||||
@@ -43,11 +44,10 @@ An application created with minimalism and semplicity in mind, with features in
|
|||||||
|
|
||||||
This is a roadmap with major features will come in near future.
|
This is a roadmap with major features will come in near future.
|
||||||
|
|
||||||
- Stored procedures, views, schedulers and triggers support.
|
- Support for other databases.
|
||||||
- Users management (add/edit/delete).
|
|
||||||
- Database tools (variables, process list...).
|
- Database tools (variables, process list...).
|
||||||
- SSL and SSH tunnel support.
|
- SSL and SSH tunnel support.
|
||||||
- Support for other databases.
|
- Users management (add/edit/delete).
|
||||||
- UI/UX improvements.
|
- UI/UX improvements.
|
||||||
- Query history.
|
- Query history.
|
||||||
- More context menu shortcuts.
|
- More context menu shortcuts.
|
||||||
@@ -55,7 +55,7 @@ This is a roadmap with major features will come in near future.
|
|||||||
- Query logs console.
|
- Query logs console.
|
||||||
- Fake data filler.
|
- Fake data filler.
|
||||||
- Import/export and migration.
|
- Import/export and migration.
|
||||||
- Themes.
|
- Light theme.
|
||||||
|
|
||||||
## Currently supported
|
## Currently supported
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ This is a roadmap with major features will come in near future.
|
|||||||
|
|
||||||
- [x] Windows
|
- [x] Windows
|
||||||
- [x] Linux
|
- [x] Linux
|
||||||
- [x] MacOS (needs tests)
|
- [x] MacOS (i need feedbacks)
|
||||||
|
|
||||||
#### • ARM
|
#### • ARM
|
||||||
|
|
||||||
@@ -84,6 +84,10 @@ This is a roadmap with major features will come in near future.
|
|||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
[Giuseppe Gigliotti](https://github.com/ReverbOD) / [Italian Translation](https://github.com/Fabio286/antares/pull/20)
|
**Italian Translation** (46%) / [Giuseppe Gigliotti](https://github.com/ReverbOD) [[#20](https://github.com/Fabio286/antares/pull/20)]
|
||||||
[Mohd-PH](https://github.com/Mohd-PH) / [Arabic Translation](https://github.com/Fabio286/antares/pull/29)
|
**Arabic Translation** (45%) / [Mohd-PH](https://github.com/Mohd-PH) [[#29](https://github.com/Fabio286/antares/pull/29)]
|
||||||
[hongkfui](https://github.com/hongkfui) / [Spanish Translation](https://github.com/Fabio286/antares/pull/32)
|
**Spanish Translation** (46%) / [hongkfui](https://github.com/hongkfui) [[#32](https://github.com/Fabio286/antares/pull/32)]
|
||||||
|
|
||||||
|
## Reviews
|
||||||
|
|
||||||
|
<a target="_blank" href="https://www.softx64.com/windows/antares-sql-client.html" title="Antares SQL Client review"><img src="https://www.softx64.com/softx64-review.png" alt="Antares SQL Client review" /></a>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "antares",
|
"name": "antares",
|
||||||
"productName": "Antares",
|
"productName": "Antares",
|
||||||
"version": "0.0.12",
|
"version": "0.0.15",
|
||||||
"description": "A cross-platform easy to use SQL client.",
|
"description": "A cross-platform easy to use SQL client.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": "https://github.com/Fabio286/antares.git",
|
"repository": "https://github.com/Fabio286/antares.git",
|
||||||
@@ -39,6 +39,10 @@
|
|||||||
"AppImage"
|
"AppImage"
|
||||||
],
|
],
|
||||||
"category": "Development"
|
"category": "Development"
|
||||||
|
},
|
||||||
|
"appImage": {
|
||||||
|
"license": "./LICENSE",
|
||||||
|
"category": "Development"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electronWebpack": {
|
"electronWebpack": {
|
||||||
@@ -63,8 +67,7 @@
|
|||||||
"vue-i18n": "^8.22.2",
|
"vue-i18n": "^8.22.2",
|
||||||
"vue-the-mask": "^0.11.1",
|
"vue-the-mask": "^0.11.1",
|
||||||
"vuedraggable": "^2.24.3",
|
"vuedraggable": "^2.24.3",
|
||||||
"vuex": "^3.6.0",
|
"vuex": "^3.6.0"
|
||||||
"vuex-persist": "^3.1.3"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
|
@@ -91,7 +91,7 @@ module.exports = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'TINYTEXT',
|
name: 'TINYTEXT',
|
||||||
length: true,
|
length: false,
|
||||||
collation: true,
|
collation: true,
|
||||||
unsigned: false,
|
unsigned: false,
|
||||||
zerofill: false
|
zerofill: false
|
||||||
@@ -119,7 +119,7 @@ module.exports = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'JSON',
|
name: 'JSON',
|
||||||
length: true,
|
length: false,
|
||||||
collation: true,
|
collation: true,
|
||||||
unsigned: false,
|
unsigned: false,
|
||||||
zerofill: false
|
zerofill: false
|
||||||
@@ -279,7 +279,7 @@ module.exports = [
|
|||||||
types: [
|
types: [
|
||||||
{
|
{
|
||||||
name: 'UNKNOWN',
|
name: 'UNKNOWN',
|
||||||
length: true,
|
length: false,
|
||||||
collation: false,
|
collation: false,
|
||||||
unsigned: false,
|
unsigned: false,
|
||||||
zerofill: false
|
zerofill: false
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
export const TEXT = ['CHAR', 'VARCHAR'];
|
export const TEXT = ['CHAR', 'VARCHAR'];
|
||||||
export const LONG_TEXT = ['TEXT', 'MEDIUMTEXT', 'longtext'];
|
export const LONG_TEXT = ['TEXT', 'MEDIUMTEXT', 'LONGTEXT'];
|
||||||
|
|
||||||
export const NUMBER = ['INT', 'TINYINT', 'SMALLINT', 'MEDIUMINT', 'BIGINT', 'FLOAT', 'DOUBLE', 'DECIMAL', 'BOOL'];
|
export const NUMBER = ['INT', 'TINYINT', 'SMALLINT', 'MEDIUMINT', 'BIGINT', 'FLOAT', 'DOUBLE', 'DECIMAL', 'BOOL'];
|
||||||
|
|
||||||
@@ -7,6 +7,6 @@ export const DATE = ['DATE'];
|
|||||||
export const TIME = ['TIME'];
|
export const TIME = ['TIME'];
|
||||||
export const DATETIME = ['DATETIME', 'TIMESTAMP'];
|
export const DATETIME = ['DATETIME', 'TIMESTAMP'];
|
||||||
|
|
||||||
export const BLOB = ['BLOB', 'MEDIUMBLOB', 'LONGBLOB'];
|
export const BLOB = ['BLOB', 'TINYBLOB', 'MEDIUMBLOB', 'LONGBLOB'];
|
||||||
|
|
||||||
export const BIT = ['BIT'];
|
export const BIT = ['BIT'];
|
||||||
|
@@ -46,7 +46,7 @@ export default connections => {
|
|||||||
try {
|
try {
|
||||||
await connection.connect();
|
await connection.connect();
|
||||||
|
|
||||||
const structure = await connection.getStructure();
|
const structure = await connection.getStructure(new Set());
|
||||||
|
|
||||||
connections[conn.uid] = connection;
|
connections[conn.uid] = connection;
|
||||||
|
|
||||||
|
@@ -50,9 +50,9 @@ export default connections => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('get-structure', async (event, uid) => {
|
ipcMain.handle('get-structure', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
const structure = await connections[uid].getStructure();
|
const structure = await connections[params.uid].getStructure(params.schemas);
|
||||||
|
|
||||||
return { status: 'success', response: structure };
|
return { status: 'success', response: structure };
|
||||||
}
|
}
|
||||||
|
43
src/main/ipc-handlers/functions.js
Normal file
43
src/main/ipc-handlers/functions.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { ipcMain } from 'electron';
|
||||||
|
|
||||||
|
export default (connections) => {
|
||||||
|
ipcMain.handle('get-function-informations', async (event, params) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[params.uid].getFunctionInformations(params);
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('drop-function', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].dropFunction(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('alter-function', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].alterFunction(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('create-function', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].createFunction(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@@ -1,15 +1,27 @@
|
|||||||
import connection from './connection';
|
import connection from './connection';
|
||||||
import tables from './tables';
|
import tables from './tables';
|
||||||
|
import views from './views';
|
||||||
|
import triggers from './triggers';
|
||||||
|
import routines from './routines';
|
||||||
|
import functions from './functions';
|
||||||
|
import schedulers from './schedulers';
|
||||||
import updates from './updates';
|
import updates from './updates';
|
||||||
import application from './application';
|
import application from './application';
|
||||||
import database from './database';
|
import database from './database';
|
||||||
|
import users from './users';
|
||||||
|
|
||||||
const connections = {};
|
const connections = {};
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
connection(connections);
|
connection(connections);
|
||||||
tables(connections);
|
tables(connections);
|
||||||
|
views(connections);
|
||||||
|
triggers(connections);
|
||||||
|
routines(connections);
|
||||||
|
functions(connections);
|
||||||
|
schedulers(connections);
|
||||||
database(connections);
|
database(connections);
|
||||||
|
users(connections);
|
||||||
updates();
|
updates();
|
||||||
application();
|
application();
|
||||||
};
|
};
|
||||||
|
43
src/main/ipc-handlers/routines.js
Normal file
43
src/main/ipc-handlers/routines.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { ipcMain } from 'electron';
|
||||||
|
|
||||||
|
export default (connections) => {
|
||||||
|
ipcMain.handle('get-routine-informations', async (event, params) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[params.uid].getRoutineInformations(params);
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('drop-routine', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].dropRoutine(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('alter-routine', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].alterRoutine(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('create-routine', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].createRoutine(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
43
src/main/ipc-handlers/schedulers.js
Normal file
43
src/main/ipc-handlers/schedulers.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { ipcMain } from 'electron';
|
||||||
|
|
||||||
|
export default (connections) => {
|
||||||
|
ipcMain.handle('get-scheduler-informations', async (event, params) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[params.uid].getEventInformations(params);
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('drop-scheduler', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].dropEvent(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('alter-scheduler', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].alterEvent(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('create-scheduler', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].createEvent(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
43
src/main/ipc-handlers/triggers.js
Normal file
43
src/main/ipc-handlers/triggers.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { ipcMain } from 'electron';
|
||||||
|
|
||||||
|
export default (connections) => {
|
||||||
|
ipcMain.handle('get-trigger-informations', async (event, params) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[params.uid].getTriggerInformations(params);
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('drop-trigger', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].dropTrigger(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('alter-trigger', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].alterTrigger(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('create-trigger', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].createTrigger(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
15
src/main/ipc-handlers/users.js
Normal file
15
src/main/ipc-handlers/users.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { ipcMain } from 'electron';
|
||||||
|
|
||||||
|
export default (connections) => {
|
||||||
|
ipcMain.handle('get-users', async (event, uid) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[uid].getUsers();
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
if (err.code === 'ER_TABLEACCESS_DENIED_ERROR')
|
||||||
|
return { status: 'success', response: [] };
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
43
src/main/ipc-handlers/views.js
Normal file
43
src/main/ipc-handlers/views.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { ipcMain } from 'electron';
|
||||||
|
|
||||||
|
export default (connections) => {
|
||||||
|
ipcMain.handle('get-view-informations', async (event, params) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[params.uid].getViewInformations(params);
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('drop-view', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].dropView(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('alter-view', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].alterView(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('create-view', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].createView(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@@ -1,8 +1,97 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import mysql from 'mysql';
|
import mysql from 'mysql';
|
||||||
import { AntaresCore } from '../AntaresCore';
|
import { AntaresCore } from '../AntaresCore';
|
||||||
|
import dataTypes from 'common/data-types/mysql';
|
||||||
|
|
||||||
export class MySQLClient extends AntaresCore {
|
export class MySQLClient extends AntaresCore {
|
||||||
|
constructor (args) {
|
||||||
|
super(args);
|
||||||
|
|
||||||
|
this.types = {
|
||||||
|
0: 'DECIMAL',
|
||||||
|
1: 'TINYINT',
|
||||||
|
2: 'SMALLINT',
|
||||||
|
3: 'INT',
|
||||||
|
4: 'FLOAT',
|
||||||
|
5: 'DOUBLE',
|
||||||
|
6: 'NULL',
|
||||||
|
7: 'TIMESTAMP',
|
||||||
|
8: 'BIGINT',
|
||||||
|
9: 'MEDIUMINT',
|
||||||
|
10: 'DATE',
|
||||||
|
11: 'TIME',
|
||||||
|
12: 'DATETIME',
|
||||||
|
13: 'YEAR',
|
||||||
|
14: 'NEWDATE',
|
||||||
|
15: 'VARCHAR',
|
||||||
|
16: 'BIT',
|
||||||
|
17: 'TIMESTAMP2',
|
||||||
|
18: 'DATETIME2',
|
||||||
|
19: 'TIME2',
|
||||||
|
245: 'JSON',
|
||||||
|
246: 'NEWDECIMAL',
|
||||||
|
247: 'ENUM',
|
||||||
|
248: 'SET',
|
||||||
|
249: 'TINY_BLOB',
|
||||||
|
250: 'MEDIUM_BLOB',
|
||||||
|
251: 'LONG_BLOB',
|
||||||
|
252: 'BLOB',
|
||||||
|
253: 'VARCHAR',
|
||||||
|
254: 'CHAR',
|
||||||
|
255: 'GEOMETRY'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_getType (field) {
|
||||||
|
let name = this.types[field.type];
|
||||||
|
let length = field.length;
|
||||||
|
|
||||||
|
if (['DATE', 'TIME', 'YEAR', 'DATETIME'].includes(name))
|
||||||
|
length = field.decimals;
|
||||||
|
|
||||||
|
if (name === 'CHAR' && field.charsetNr === 63)// if binary
|
||||||
|
name = 'BINARY';
|
||||||
|
|
||||||
|
if (name === 'VARCHAR' && field.charsetNr === 63)// if binary
|
||||||
|
name = 'VARBINARY';
|
||||||
|
|
||||||
|
if (name === 'BLOB') {
|
||||||
|
switch (length) {
|
||||||
|
case 765:
|
||||||
|
name = 'TYNITEXT';
|
||||||
|
break;
|
||||||
|
case 196605:
|
||||||
|
name = 'TEXT';
|
||||||
|
break;
|
||||||
|
case 50331645:
|
||||||
|
name = 'MEDIUMTEXT';
|
||||||
|
break;
|
||||||
|
case 4294967295:
|
||||||
|
name = field.charsetNr === 63 ? 'LONGBLOB' : 'LONGTEXT';
|
||||||
|
break;
|
||||||
|
case 255:
|
||||||
|
name = 'TINYBLOB';
|
||||||
|
break;
|
||||||
|
case 65535:
|
||||||
|
name = 'BLOB';
|
||||||
|
break;
|
||||||
|
case 16777215:
|
||||||
|
name = 'MEDIUMBLOB';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
name = field.charsetNr === 63 ? 'BLOB' : 'TEXT';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { name, length };
|
||||||
|
}
|
||||||
|
|
||||||
|
_getTypeInfo (type) {
|
||||||
|
return dataTypes
|
||||||
|
.reduce((acc, group) => [...acc, ...group.types], [])
|
||||||
|
.filter(_type => _type.name === type.toUpperCase())[0];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
@@ -31,26 +120,31 @@ export class MySQLClient extends AntaresCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param {Array} schemas list
|
||||||
* @returns {Array.<Object>} databases scructure
|
* @returns {Array.<Object>} databases scructure
|
||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async getStructure () {
|
async getStructure (schemas) {
|
||||||
const { rows: databases } = await this.raw('SHOW DATABASES');
|
const { rows: databases } = await this.raw('SHOW DATABASES');
|
||||||
// TODO: SHOW TABLE STATUS FROM `{DATABASE_NAME}`;
|
|
||||||
|
|
||||||
const { rows: tables } = await this
|
|
||||||
.select('*')
|
|
||||||
.schema('information_schema')
|
|
||||||
.from('TABLES')
|
|
||||||
.orderBy({ TABLE_SCHEMA: 'ASC', TABLE_NAME: 'ASC' })
|
|
||||||
.run();
|
|
||||||
|
|
||||||
const { rows: functions } = await this.raw('SHOW FUNCTION STATUS');
|
const { rows: functions } = await this.raw('SHOW FUNCTION STATUS');
|
||||||
const { rows: procedures } = await this.raw('SHOW PROCEDURE STATUS');
|
const { rows: procedures } = await this.raw('SHOW PROCEDURE STATUS');
|
||||||
const { rows: schedulers } = await this.raw('SELECT *, EVENT_SCHEMA AS `Db`, EVENT_NAME AS `Name` FROM information_schema.`EVENTS`');
|
const { rows: schedulers } = await this.raw('SELECT *, EVENT_SCHEMA AS `Db`, EVENT_NAME AS `Name` FROM information_schema.`EVENTS`');
|
||||||
|
|
||||||
|
const tablesArr = [];
|
||||||
const triggersArr = [];
|
const triggersArr = [];
|
||||||
|
|
||||||
for (const db of databases) {
|
for (const db of databases) {
|
||||||
|
if (!schemas.has(db.Database)) continue;
|
||||||
|
|
||||||
|
let { rows: tables } = await this.raw(`SHOW TABLE STATUS FROM \`${db.Database}\``);
|
||||||
|
if (tables.length) {
|
||||||
|
tables = tables.map(table => {
|
||||||
|
table.Db = db.Database;
|
||||||
|
return table;
|
||||||
|
});
|
||||||
|
tablesArr.push(...tables);
|
||||||
|
}
|
||||||
|
|
||||||
let { rows: triggers } = await this.raw(`SHOW TRIGGERS FROM \`${db.Database}\``);
|
let { rows: triggers } = await this.raw(`SHOW TRIGGERS FROM \`${db.Database}\``);
|
||||||
if (triggers.length) {
|
if (triggers.length) {
|
||||||
triggers = triggers.map(trigger => {
|
triggers = triggers.map(trigger => {
|
||||||
@@ -62,95 +156,121 @@ export class MySQLClient extends AntaresCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return databases.map(db => {
|
return databases.map(db => {
|
||||||
// TABLES
|
if (schemas.has(db.Database)) {
|
||||||
const remappedTables = tables.filter(table => table.TABLE_SCHEMA === db.Database).map(table => {
|
// TABLES
|
||||||
let tableType;
|
const remappedTables = tablesArr.filter(table => table.Db === db.Database).map(table => {
|
||||||
switch (table.TABLE_TYPE) {
|
let tableType;
|
||||||
case 'VIEW':
|
switch (table.Comment) {
|
||||||
tableType = 'view';
|
case 'VIEW':
|
||||||
break;
|
tableType = 'view';
|
||||||
default:
|
break;
|
||||||
tableType = 'table';
|
default:
|
||||||
break;
|
tableType = 'table';
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: table.Name,
|
||||||
|
type: tableType,
|
||||||
|
rows: table.Rows,
|
||||||
|
created: table.Create_time,
|
||||||
|
updated: table.Update_time,
|
||||||
|
engine: table.Engine,
|
||||||
|
comment: table.Comment,
|
||||||
|
size: table.Data_length + table.Index_length,
|
||||||
|
autoIncrement: table.Auto_increment,
|
||||||
|
collation: table.Collation
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// PROCEDURES
|
||||||
|
const remappedProcedures = procedures.filter(procedure => procedure.Db === db.Database).map(procedure => {
|
||||||
|
return {
|
||||||
|
name: procedure.Name,
|
||||||
|
type: procedure.Type,
|
||||||
|
definer: procedure.Definer,
|
||||||
|
created: procedure.Created,
|
||||||
|
updated: procedure.Modified,
|
||||||
|
comment: procedure.Comment,
|
||||||
|
charset: procedure.character_set_client,
|
||||||
|
security: procedure.Security_type
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// FUNCTIONS
|
||||||
|
const remappedFunctions = functions.filter(func => func.Db === db.Database).map(func => {
|
||||||
|
return {
|
||||||
|
name: func.Name,
|
||||||
|
type: func.Type,
|
||||||
|
definer: func.Definer,
|
||||||
|
created: func.Created,
|
||||||
|
updated: func.Modified,
|
||||||
|
comment: func.Comment,
|
||||||
|
charset: func.character_set_client,
|
||||||
|
security: func.Security_type
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// SCHEDULERS
|
||||||
|
const remappedSchedulers = schedulers.filter(scheduler => scheduler.Db === db.Database).map(scheduler => {
|
||||||
|
return {
|
||||||
|
name: scheduler.EVENT_NAME,
|
||||||
|
definition: scheduler.EVENT_DEFINITION,
|
||||||
|
type: scheduler.EVENT_TYPE,
|
||||||
|
definer: scheduler.DEFINER,
|
||||||
|
body: scheduler.EVENT_BODY,
|
||||||
|
starts: scheduler.STARTS,
|
||||||
|
ends: scheduler.ENDS,
|
||||||
|
status: scheduler.STATUS,
|
||||||
|
executeAt: scheduler.EXECUTE_AT,
|
||||||
|
intervalField: scheduler.INTERVAL_FIELD,
|
||||||
|
intervalValue: scheduler.INTERVAL_VALUE,
|
||||||
|
onCompletion: scheduler.ON_COMPLETION,
|
||||||
|
originator: scheduler.ORIGINATOR,
|
||||||
|
sqlMode: scheduler.SQL_MODE,
|
||||||
|
created: scheduler.CREATED,
|
||||||
|
updated: scheduler.LAST_ALTERED,
|
||||||
|
lastExecuted: scheduler.LAST_EXECUTED,
|
||||||
|
comment: scheduler.EVENT_COMMENT,
|
||||||
|
charset: scheduler.CHARACTER_SET_CLIENT,
|
||||||
|
timezone: scheduler.TIME_ZONE
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// TRIGGERS
|
||||||
|
const remappedTriggers = triggersArr.filter(trigger => trigger.Db === db.Database).map(trigger => {
|
||||||
|
return {
|
||||||
|
name: trigger.Trigger,
|
||||||
|
statement: trigger.Statement,
|
||||||
|
timing: trigger.Timing,
|
||||||
|
definer: trigger.Definer,
|
||||||
|
event: trigger.Event,
|
||||||
|
table: trigger.Table,
|
||||||
|
sqlMode: trigger.sql_mode,
|
||||||
|
created: trigger.Created,
|
||||||
|
charset: trigger.character_set_client
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: table.TABLE_NAME,
|
name: db.Database,
|
||||||
type: tableType,
|
tables: remappedTables,
|
||||||
rows: table.TABLE_ROWS,
|
functions: remappedFunctions,
|
||||||
created: table.CREATE_TIME,
|
procedures: remappedProcedures,
|
||||||
updated: table.UPDATE_TIME,
|
triggers: remappedTriggers,
|
||||||
engine: table.ENGINE,
|
schedulers: remappedSchedulers
|
||||||
comment: table.TABLE_COMMENT,
|
|
||||||
size: table.DATA_LENGTH + table.INDEX_LENGTH,
|
|
||||||
autoIncrement: table.AUTO_INCREMENT,
|
|
||||||
collation: table.TABLE_COLLATION
|
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
else {
|
||||||
// PROCEDURES
|
|
||||||
const remappedProcedures = procedures.filter(procedure => procedure.Db === db.Database).map(procedure => {
|
|
||||||
return {
|
return {
|
||||||
name: procedure.Name,
|
name: db.Database,
|
||||||
type: procedure.Type,
|
tables: [],
|
||||||
definer: procedure.Definer,
|
functions: [],
|
||||||
created: procedure.Created,
|
procedures: [],
|
||||||
updated: procedure.Modified,
|
triggers: [],
|
||||||
comment: procedure.Comment,
|
schedulers: []
|
||||||
charset: procedure.character_set_client,
|
|
||||||
security: procedure.Security_type
|
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
|
||||||
// SCHEDULERS
|
|
||||||
const remappedSchedulers = schedulers.filter(scheduler => scheduler.Db === db.Database).map(scheduler => {
|
|
||||||
return {
|
|
||||||
name: scheduler.EVENT_NAME,
|
|
||||||
definition: scheduler.EVENT_DEFINITION,
|
|
||||||
type: scheduler.EVENT_TYPE,
|
|
||||||
definer: scheduler.DEFINER,
|
|
||||||
body: scheduler.EVENT_BODY,
|
|
||||||
starts: scheduler.STARTS,
|
|
||||||
ends: scheduler.ENDS,
|
|
||||||
status: scheduler.STATUS,
|
|
||||||
executeAt: scheduler.EXECUTE_AT,
|
|
||||||
intervalField: scheduler.INTERVAL_FIELD,
|
|
||||||
intervalValue: scheduler.INTERVAL_VALUE,
|
|
||||||
onCompletion: scheduler.ON_COMPLETION,
|
|
||||||
originator: scheduler.ORIGINATOR,
|
|
||||||
sqlMode: scheduler.SQL_MODE,
|
|
||||||
created: scheduler.CREATED,
|
|
||||||
updated: scheduler.LAST_ALTERED,
|
|
||||||
lastExecuted: scheduler.LAST_EXECUTED,
|
|
||||||
comment: scheduler.EVENT_COMMENT,
|
|
||||||
charset: scheduler.CHARACTER_SET_CLIENT,
|
|
||||||
timezone: scheduler.TIME_ZONE
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// TRIGGERS
|
|
||||||
const remappedTriggers = triggersArr.filter(trigger => trigger.Db === db.Database).map(trigger => {
|
|
||||||
return {
|
|
||||||
name: trigger.Trigger,
|
|
||||||
statement: trigger.Statement,
|
|
||||||
timing: trigger.Timing,
|
|
||||||
definer: trigger.Definer,
|
|
||||||
event: trigger.Event,
|
|
||||||
table: trigger.Table,
|
|
||||||
sqlMode: trigger.sql_mode,
|
|
||||||
created: trigger.Created,
|
|
||||||
charset: trigger.character_set_client
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: db.Database,
|
|
||||||
tables: remappedTables,
|
|
||||||
functions: functions.filter(func => func.Db === db.Database), // TODO: remap functions
|
|
||||||
procedures: remappedProcedures,
|
|
||||||
triggers: remappedTriggers,
|
|
||||||
schedulers: remappedSchedulers
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,6 +382,482 @@ export class MySQLClient extends AntaresCore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SELECT `user`, `host`, authentication_string) AS `password` FROM `mysql`.`user`
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} users list
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async getUsers () {
|
||||||
|
const { rows } = await this.raw('SELECT `user`, `host`, authentication_string AS `password` FROM `mysql`.`user`');
|
||||||
|
|
||||||
|
return rows.map(row => {
|
||||||
|
return {
|
||||||
|
name: row.user,
|
||||||
|
host: row.host,
|
||||||
|
password: row.password
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHOW CREATE VIEW
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} view informations
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async getViewInformations ({ schema, view }) {
|
||||||
|
const sql = `SHOW CREATE VIEW \`${schema}\`.\`${view}\``;
|
||||||
|
const results = await this.raw(sql);
|
||||||
|
|
||||||
|
return results.rows.map(row => {
|
||||||
|
return {
|
||||||
|
algorithm: row['Create View'].match(/(?<=CREATE ALGORITHM=).*?(?=\s)/gs)[0],
|
||||||
|
definer: row['Create View'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
||||||
|
security: row['Create View'].match(/(?<=SQL SECURITY ).*?(?=\s)/gs)[0],
|
||||||
|
updateOption: row['Create View'].match(/(?<=WITH ).*?(?=\s)/gs) ? row['Create View'].match(/(?<=WITH ).*?(?=\s)/gs)[0] : '',
|
||||||
|
sql: row['Create View'].match(/(?<=AS ).*?$/gs)[0],
|
||||||
|
name: row.View
|
||||||
|
};
|
||||||
|
})[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DROP VIEW
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async dropView (params) {
|
||||||
|
const sql = `DROP VIEW \`${params.view}\``;
|
||||||
|
return await this.raw(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ALTER VIEW
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async alterView (params) {
|
||||||
|
const { view } = params;
|
||||||
|
let sql = `ALTER ALGORITHM = ${view.algorithm}${view.definer ? ` DEFINER=${view.definer}` : ''} SQL SECURITY ${view.security} VIEW \`${view.oldName}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}`;
|
||||||
|
|
||||||
|
if (view.name !== view.oldName)
|
||||||
|
sql += `; RENAME TABLE \`${view.oldName}\` TO \`${view.name}\``;
|
||||||
|
|
||||||
|
return await this.raw(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CREATE VIEW
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async createView (view) {
|
||||||
|
const sql = `CREATE ALGORITHM = ${view.algorithm} ${view.definer ? `DEFINER=${view.definer} ` : ''}SQL SECURITY ${view.security} VIEW \`${view.name}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}`;
|
||||||
|
return await this.raw(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHOW CREATE TRIGGER
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} view informations
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async getTriggerInformations ({ schema, trigger }) {
|
||||||
|
const sql = `SHOW CREATE TRIGGER \`${schema}\`.\`${trigger}\``;
|
||||||
|
const results = await this.raw(sql);
|
||||||
|
|
||||||
|
return results.rows.map(row => {
|
||||||
|
return {
|
||||||
|
definer: row['SQL Original Statement'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
||||||
|
sql: row['SQL Original Statement'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
||||||
|
name: row.Trigger,
|
||||||
|
table: row['SQL Original Statement'].match(/(?<=ON `).*?(?=`)/gs)[0],
|
||||||
|
event1: row['SQL Original Statement'].match(/(BEFORE|AFTER)/gs)[0],
|
||||||
|
event2: row['SQL Original Statement'].match(/(INSERT|UPDATE|DELETE)/gs)[0]
|
||||||
|
};
|
||||||
|
})[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DROP TRIGGER
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async dropTrigger (params) {
|
||||||
|
const sql = `DROP TRIGGER \`${params.trigger}\``;
|
||||||
|
return await this.raw(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ALTER TRIGGER
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async alterTrigger (params) {
|
||||||
|
const { trigger } = params;
|
||||||
|
const tempTrigger = Object.assign({}, trigger);
|
||||||
|
tempTrigger.name = `Antares_${tempTrigger.name}_tmp`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.createTrigger(tempTrigger);
|
||||||
|
await this.dropTrigger({ trigger: tempTrigger.name });
|
||||||
|
await this.dropTrigger({ trigger: trigger.oldName });
|
||||||
|
await this.createTrigger(trigger);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return Promise.reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CREATE TRIGGER
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async createTrigger (trigger) {
|
||||||
|
const sql = `CREATE ${trigger.definer ? `DEFINER=${trigger.definer} ` : ''}TRIGGER \`${trigger.name}\` ${trigger.event1} ${trigger.event2} ON \`${trigger.table}\` FOR EACH ROW ${trigger.sql}`;
|
||||||
|
return await this.raw(sql, { split: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHOW CREATE PROCEDURE
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} view informations
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async getRoutineInformations ({ schema, routine }) {
|
||||||
|
const sql = `SHOW CREATE PROCEDURE \`${schema}\`.\`${routine}\``;
|
||||||
|
const results = await this.raw(sql);
|
||||||
|
|
||||||
|
return results.rows.map(row => {
|
||||||
|
if (!row['Create Procedure']) {
|
||||||
|
return {
|
||||||
|
definer: null,
|
||||||
|
sql: '',
|
||||||
|
parameters: [],
|
||||||
|
name: row.Procedure,
|
||||||
|
comment: '',
|
||||||
|
security: 'DEFINER',
|
||||||
|
deterministic: false,
|
||||||
|
dataAccess: 'CONTAINS SQL'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const parameters = row['Create Procedure']
|
||||||
|
.match(/(?<=\().*?(?=\))/s)[0]
|
||||||
|
.replaceAll('\r', '')
|
||||||
|
.replaceAll('\t', '')
|
||||||
|
.split(',')
|
||||||
|
.map(el => {
|
||||||
|
const param = el.split(' ');
|
||||||
|
const type = param[2] ? param[2].replace(')', '').split('(') : ['', null];
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: param[1] ? param[1].replaceAll('`', '') : '',
|
||||||
|
type: type[0],
|
||||||
|
length: +type[1],
|
||||||
|
context: param[0] ? param[0].replace('\n', '') : ''
|
||||||
|
};
|
||||||
|
}).filter(el => el.name);
|
||||||
|
|
||||||
|
let dataAccess = 'CONTAINS SQL';
|
||||||
|
if (row['Create Procedure'].includes('NO SQL'))
|
||||||
|
dataAccess = 'NO SQL';
|
||||||
|
if (row['Create Procedure'].includes('READS SQL DATA'))
|
||||||
|
dataAccess = 'READS SQL DATA';
|
||||||
|
if (row['Create Procedure'].includes('MODIFIES SQL DATA'))
|
||||||
|
dataAccess = 'MODIFIES SQL DATA';
|
||||||
|
|
||||||
|
return {
|
||||||
|
definer: row['Create Procedure'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
||||||
|
sql: row['Create Procedure'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
||||||
|
parameters,
|
||||||
|
name: row.Procedure,
|
||||||
|
comment: row['Create Procedure'].match(/(?<=COMMENT ').*?(?=')/gs) ? row['Create Procedure'].match(/(?<=COMMENT ').*?(?=')/gs)[0] : '',
|
||||||
|
security: row['Create Procedure'].includes('SQL SECURITY INVOKER') ? 'INVOKER' : 'DEFINER',
|
||||||
|
deterministic: row['Create Procedure'].includes('DETERMINISTIC'),
|
||||||
|
dataAccess
|
||||||
|
};
|
||||||
|
})[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DROP PROCEDURE
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async dropRoutine (params) {
|
||||||
|
const sql = `DROP PROCEDURE \`${params.routine}\``;
|
||||||
|
return await this.raw(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ALTER PROCEDURE
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async alterRoutine (params) {
|
||||||
|
const { routine } = params;
|
||||||
|
const tempProcedure = Object.assign({}, routine);
|
||||||
|
tempProcedure.name = `Antares_${tempProcedure.name}_tmp`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.createRoutine(tempProcedure);
|
||||||
|
await this.dropRoutine({ routine: tempProcedure.name });
|
||||||
|
await this.dropRoutine({ routine: routine.oldName });
|
||||||
|
await this.createRoutine(routine);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return Promise.reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CREATE PROCEDURE
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async createRoutine (routine) {
|
||||||
|
const parameters = routine.parameters.reduce((acc, curr) => {
|
||||||
|
acc.push(`${curr.context} \`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
|
||||||
|
return acc;
|
||||||
|
}, []).join(',');
|
||||||
|
|
||||||
|
const sql = `CREATE ${routine.definer ? `DEFINER=${routine.definer} ` : ''}PROCEDURE \`${routine.name}\`(${parameters})
|
||||||
|
LANGUAGE SQL
|
||||||
|
${routine.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
|
||||||
|
${routine.dataAccess}
|
||||||
|
SQL SECURITY ${routine.security}
|
||||||
|
COMMENT '${routine.comment}'
|
||||||
|
${routine.sql}`;
|
||||||
|
|
||||||
|
return await this.raw(sql, { split: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHOW CREATE FUNCTION
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} view informations
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async getFunctionInformations ({ schema, func }) {
|
||||||
|
const sql = `SHOW CREATE FUNCTION \`${schema}\`.\`${func}\``;
|
||||||
|
const results = await this.raw(sql);
|
||||||
|
|
||||||
|
return results.rows.map(row => {
|
||||||
|
if (!row['Create Function']) {
|
||||||
|
return {
|
||||||
|
definer: null,
|
||||||
|
sql: '',
|
||||||
|
parameters: [],
|
||||||
|
name: row.Procedure,
|
||||||
|
comment: '',
|
||||||
|
security: 'DEFINER',
|
||||||
|
deterministic: false,
|
||||||
|
dataAccess: 'CONTAINS SQL',
|
||||||
|
returns: 'INT',
|
||||||
|
returnsLength: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const parameters = row['Create Function']
|
||||||
|
.match(/(?<=\().*?(?=\))/s)[0]
|
||||||
|
.replaceAll('\r', '')
|
||||||
|
.replaceAll('\t', '')
|
||||||
|
.split(',')
|
||||||
|
.map(el => {
|
||||||
|
const param = el.split(' ');
|
||||||
|
const type = param[1] ? param[1].replace(')', '').split('(') : ['', null];
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: param[0] ? param[0].replaceAll('`', '') : '',
|
||||||
|
type: type[0],
|
||||||
|
length: +type[1]
|
||||||
|
};
|
||||||
|
}).filter(el => el.name);
|
||||||
|
|
||||||
|
let dataAccess = 'CONTAINS SQL';
|
||||||
|
if (row['Create Function'].includes('NO SQL'))
|
||||||
|
dataAccess = 'NO SQL';
|
||||||
|
if (row['Create Function'].includes('READS SQL DATA'))
|
||||||
|
dataAccess = 'READS SQL DATA';
|
||||||
|
if (row['Create Function'].includes('MODIFIES SQL DATA'))
|
||||||
|
dataAccess = 'MODIFIES SQL DATA';
|
||||||
|
|
||||||
|
const output = row['Create Function'].match(/(?<=RETURNS ).*?(?=\s)/gs).length ? row['Create Function'].match(/(?<=RETURNS ).*?(?=\s)/gs)[0].replace(')', '').split('(') : ['', null];
|
||||||
|
|
||||||
|
return {
|
||||||
|
definer: row['Create Function'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
||||||
|
sql: row['Create Function'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
||||||
|
parameters,
|
||||||
|
name: row.Function,
|
||||||
|
comment: row['Create Function'].match(/(?<=COMMENT ').*?(?=')/gs) ? row['Create Function'].match(/(?<=COMMENT ').*?(?=')/gs)[0] : '',
|
||||||
|
security: row['Create Function'].includes('SQL SECURITY INVOKER') ? 'INVOKER' : 'DEFINER',
|
||||||
|
deterministic: row['Create Function'].includes('DETERMINISTIC'),
|
||||||
|
dataAccess,
|
||||||
|
returns: output[0].toUpperCase(),
|
||||||
|
returnsLength: +output[1]
|
||||||
|
};
|
||||||
|
})[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DROP FUNCTION
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async dropFunction (params) {
|
||||||
|
const sql = `DROP FUNCTION \`${params.func}\``;
|
||||||
|
return await this.raw(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ALTER FUNCTION
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async alterFunction (params) {
|
||||||
|
const { func } = params;
|
||||||
|
const tempProcedure = Object.assign({}, func);
|
||||||
|
tempProcedure.name = `Antares_${tempProcedure.name}_tmp`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.createFunction(tempProcedure);
|
||||||
|
await this.dropFunction({ func: tempProcedure.name });
|
||||||
|
await this.dropFunction({ func: func.oldName });
|
||||||
|
await this.createFunction(func);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return Promise.reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CREATE FUNCTION
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async createFunction (func) {
|
||||||
|
const parameters = func.parameters.reduce((acc, curr) => {
|
||||||
|
acc.push(`\`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
|
||||||
|
return acc;
|
||||||
|
}, []).join(',');
|
||||||
|
|
||||||
|
const sql = `CREATE ${func.definer ? `DEFINER=${func.definer} ` : ''}FUNCTION \`${func.name}\`(${parameters}) RETURNS ${func.returns}${func.returnsLength ? `(${func.returnsLength})` : ''}
|
||||||
|
LANGUAGE SQL
|
||||||
|
${func.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
|
||||||
|
${func.dataAccess}
|
||||||
|
SQL SECURITY ${func.security}
|
||||||
|
COMMENT '${func.comment}'
|
||||||
|
${func.sql}`;
|
||||||
|
|
||||||
|
return await this.raw(sql, { split: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHOW CREATE EVENT
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} view informations
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async getEventInformations ({ schema, scheduler }) {
|
||||||
|
const sql = `SHOW CREATE EVENT \`${schema}\`.\`${scheduler}\``;
|
||||||
|
const results = await this.raw(sql);
|
||||||
|
|
||||||
|
return results.rows.map(row => {
|
||||||
|
const schedule = row['Create Event'];
|
||||||
|
const execution = schedule.includes('EVERY') ? 'EVERY' : 'ONCE';
|
||||||
|
const every = execution === 'EVERY' ? row['Create Event'].match(/(?<=EVERY )(\s*([^\s]+)){0,2}/gs)[0].replaceAll('\'', '').split(' ') : [];
|
||||||
|
const starts = execution === 'EVERY' && schedule.includes('STARTS') ? schedule.match(/(?<=STARTS ').*?(?='\s)/gs)[0] : '';
|
||||||
|
const ends = execution === 'EVERY' && schedule.includes('ENDS') ? schedule.match(/(?<=ENDS ').*?(?='\s)/gs)[0] : '';
|
||||||
|
const at = execution === 'ONCE' && schedule.includes('AT') ? schedule.match(/(?<=AT ').*?(?='\s)/gs)[0] : '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
definer: row['Create Event'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
||||||
|
sql: row['Create Event'].match(/(?<=DO )(.*)/gs)[0],
|
||||||
|
name: row.Event,
|
||||||
|
comment: row['Create Event'].match(/(?<=COMMENT ').*?(?=')/gs) ? row['Create Event'].match(/(?<=COMMENT ').*?(?=')/gs)[0] : '',
|
||||||
|
state: row['Create Event'].includes('ENABLE') ? 'ENABLE' : row['Create Event'].includes('DISABLE ON SLAVE') ? 'DISABLE ON SLAVE' : 'DISABLE',
|
||||||
|
preserve: row['Create Event'].includes('ON COMPLETION PRESERVE'),
|
||||||
|
execution,
|
||||||
|
every,
|
||||||
|
starts,
|
||||||
|
ends,
|
||||||
|
at
|
||||||
|
};
|
||||||
|
})[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DROP EVENT
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async dropEvent (params) {
|
||||||
|
const sql = `DROP EVENT \`${params.scheduler}\``;
|
||||||
|
return await this.raw(sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ALTER EVENT
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async alterEvent (params) {
|
||||||
|
const { scheduler } = params;
|
||||||
|
|
||||||
|
if (scheduler.execution === 'EVERY' && scheduler.every[0].includes('-'))
|
||||||
|
scheduler.every[0] = `'${scheduler.every[0]}'`;
|
||||||
|
|
||||||
|
const sql = `ALTER ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${scheduler.oldName}\`
|
||||||
|
ON SCHEDULE
|
||||||
|
${scheduler.execution === 'EVERY'
|
||||||
|
? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}`
|
||||||
|
: `AT '${scheduler.at}'`}
|
||||||
|
ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE
|
||||||
|
${scheduler.name !== scheduler.oldName ? `RENAME TO \`${scheduler.name}\`` : ''}
|
||||||
|
${scheduler.state}
|
||||||
|
COMMENT '${scheduler.comment}'
|
||||||
|
DO ${scheduler.sql}`;
|
||||||
|
|
||||||
|
return await this.raw(sql, { split: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CREATE EVENT
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async createEvent (scheduler) {
|
||||||
|
const sql = `CREATE ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${scheduler.name}\`
|
||||||
|
ON SCHEDULE
|
||||||
|
${scheduler.execution === 'EVERY'
|
||||||
|
? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}`
|
||||||
|
: `AT '${scheduler.at}'`}
|
||||||
|
ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE
|
||||||
|
${scheduler.state}
|
||||||
|
COMMENT '${scheduler.comment}'
|
||||||
|
DO ${scheduler.sql}`;
|
||||||
|
|
||||||
|
return await this.raw(sql, { split: false });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SHOW COLLATION
|
* SHOW COLLATION
|
||||||
*
|
*
|
||||||
@@ -371,7 +967,8 @@ export class MySQLClient extends AntaresCore {
|
|||||||
|
|
||||||
// ADD FIELDS
|
// ADD FIELDS
|
||||||
additions.forEach(addition => {
|
additions.forEach(addition => {
|
||||||
const length = addition.numLength || addition.charLength || addition.datePrecision;
|
const typeInfo = this._getTypeInfo(addition.type);
|
||||||
|
const length = typeInfo.length ? addition.numLength || addition.charLength || addition.datePrecision : false;
|
||||||
|
|
||||||
alterColumns.push(`ADD COLUMN \`${addition.name}\`
|
alterColumns.push(`ADD COLUMN \`${addition.name}\`
|
||||||
${addition.type.toUpperCase()}${length ? `(${length})` : ''}
|
${addition.type.toUpperCase()}${length ? `(${length})` : ''}
|
||||||
@@ -408,7 +1005,8 @@ export class MySQLClient extends AntaresCore {
|
|||||||
|
|
||||||
// CHANGE FIELDS
|
// CHANGE FIELDS
|
||||||
changes.forEach(change => {
|
changes.forEach(change => {
|
||||||
const length = change.numLength || change.charLength || change.datePrecision;
|
const typeInfo = this._getTypeInfo(change.type);
|
||||||
|
const length = typeInfo.length ? change.numLength || change.charLength || change.datePrecision : false;
|
||||||
|
|
||||||
alterColumns.push(`CHANGE COLUMN \`${change.orgName}\` \`${change.name}\`
|
alterColumns.push(`CHANGE COLUMN \`${change.orgName}\` \`${change.name}\`
|
||||||
${change.type.toUpperCase()}${length ? `(${length})` : ''}
|
${change.type.toUpperCase()}${length ? `(${length})` : ''}
|
||||||
@@ -563,6 +1161,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @param {object} args
|
* @param {object} args
|
||||||
* @param {boolean} args.nest
|
* @param {boolean} args.nest
|
||||||
* @param {boolean} args.details
|
* @param {boolean} args.details
|
||||||
|
* @param {boolean} args.split
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
@@ -570,19 +1169,18 @@ export class MySQLClient extends AntaresCore {
|
|||||||
args = {
|
args = {
|
||||||
nest: false,
|
nest: false,
|
||||||
details: false,
|
details: false,
|
||||||
|
split: true,
|
||||||
...args
|
...args
|
||||||
};
|
};
|
||||||
const nestTables = args.nest ? '.' : false;
|
const nestTables = args.nest ? '.' : false;
|
||||||
const resultsArr = [];
|
const resultsArr = [];
|
||||||
let paramsArr = [];
|
let paramsArr = [];
|
||||||
let selectedFields = [];
|
const queries = args.split ? sql.split(';') : [sql];
|
||||||
const queries = sql.split(';');
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
for (const query of queries) {
|
for (const query of queries) {
|
||||||
if (!query) continue;
|
if (!query) continue;
|
||||||
let fieldsArr = [];
|
|
||||||
let keysArr = [];
|
let keysArr = [];
|
||||||
|
|
||||||
const { rows, report, fields, keys } = await new Promise((resolve, reject) => {
|
const { rows, report, fields, keys } = await new Promise((resolve, reject) => {
|
||||||
@@ -592,32 +1190,34 @@ export class MySQLClient extends AntaresCore {
|
|||||||
if (err)
|
if (err)
|
||||||
reject(err);
|
reject(err);
|
||||||
else {
|
else {
|
||||||
const remappedFields = fields
|
let remappedFields = fields
|
||||||
? fields.map(field => {
|
? fields.map(field => {
|
||||||
|
if (!field || Array.isArray(field))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const type = this._getType(field);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: field.name,
|
name: field.orgName,
|
||||||
|
alias: field.name,
|
||||||
orgName: field.orgName,
|
orgName: field.orgName,
|
||||||
schema: field.db,
|
schema: field.db,
|
||||||
table: field.table,
|
table: field.table,
|
||||||
|
tableAlias: field.table,
|
||||||
|
zerofill: field.zerofill,
|
||||||
orgTable: field.orgTable,
|
orgTable: field.orgTable,
|
||||||
type: 'varchar'
|
type: type.name,
|
||||||
|
length: type.length
|
||||||
};
|
};
|
||||||
})
|
}).filter(Boolean)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
if (args.details) {
|
if (args.details) {
|
||||||
let cachedTable;
|
let cachedTable;
|
||||||
|
|
||||||
if (remappedFields.length) {
|
if (remappedFields.length) {
|
||||||
selectedFields = remappedFields.map(field => {
|
|
||||||
return {
|
|
||||||
name: field.orgName || field.name,
|
|
||||||
table: field.orgTable || field.table
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
paramsArr = remappedFields.map(field => {
|
paramsArr = remappedFields.map(field => {
|
||||||
if (field.table) cachedTable = field.table;// Needed for some queries on information_schema
|
if (field.orgTable) cachedTable = field.orgTable;// Needed for some queries on information_schema
|
||||||
return {
|
return {
|
||||||
table: field.orgTable || cachedTable,
|
table: field.orgTable || cachedTable,
|
||||||
schema: field.schema || 'INFORMATION_SCHEMA'
|
schema: field.schema || 'INFORMATION_SCHEMA'
|
||||||
@@ -625,37 +1225,16 @@ export class MySQLClient extends AntaresCore {
|
|||||||
}).filter((val, i, arr) => arr.findIndex(el => el.schema === val.schema && el.table === val.table) === i);
|
}).filter((val, i, arr) => arr.findIndex(el => el.schema === val.schema && el.table === val.table) === i);
|
||||||
|
|
||||||
for (const paramObj of paramsArr) {
|
for (const paramObj of paramsArr) {
|
||||||
try { // Table data
|
if (!paramObj.table || !paramObj.schema) continue;
|
||||||
|
|
||||||
|
try { // Column details
|
||||||
const response = await this.getTableColumns(paramObj);
|
const response = await this.getTableColumns(paramObj);
|
||||||
|
remappedFields = remappedFields.map(field => {
|
||||||
let detailedFields = response.length
|
const detailedField = response.find(f => f.name === field.name);
|
||||||
? selectedFields.map(selField => {
|
if (field.orgTable === paramObj.table && field.schema === paramObj.schema && detailedField.name === field.orgName)
|
||||||
return response.find(field => field.name === selField.name && field.table === selField.table);
|
field = { ...detailedField, ...field };
|
||||||
}).filter(el => !!el)
|
return field;
|
||||||
: [];
|
});
|
||||||
|
|
||||||
if (selectedFields.length) {
|
|
||||||
detailedFields = detailedFields.map(field => {
|
|
||||||
const aliasObj = remappedFields.find(resField => resField.orgName === field.name);
|
|
||||||
return {
|
|
||||||
...field,
|
|
||||||
alias: aliasObj.name || field.name,
|
|
||||||
tableAlias: aliasObj.table || field.table
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!detailedFields.length) {
|
|
||||||
detailedFields = remappedFields.map(field => {
|
|
||||||
return {
|
|
||||||
...field,
|
|
||||||
alias: field.name,
|
|
||||||
tableAlias: field.table
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fieldsArr = fieldsArr ? [...fieldsArr, ...detailedFields] : detailedFields;
|
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
@@ -673,9 +1252,9 @@ export class MySQLClient extends AntaresCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
rows: Array.isArray(queryResult) ? queryResult : false,
|
rows: Array.isArray(queryResult) ? queryResult.some(el => Array.isArray(el)) ? [] : queryResult : false,
|
||||||
report: !Array.isArray(queryResult) ? queryResult : false,
|
report: !Array.isArray(queryResult) ? queryResult : false,
|
||||||
fields: fieldsArr.length ? fieldsArr : remappedFields,
|
fields: remappedFields,
|
||||||
keys: keysArr
|
keys: keysArr
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -69,75 +69,81 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.context {
|
.context {
|
||||||
|
display: flex;
|
||||||
|
color: $body-font-color;
|
||||||
|
font-size: 16px;
|
||||||
|
z-index: 400;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
position: fixed;
|
||||||
|
height: 100vh;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
.context-container {
|
||||||
|
min-width: 100px;
|
||||||
|
z-index: 10;
|
||||||
|
box-shadow: 0 0 2px 0 #000;
|
||||||
|
padding: 0;
|
||||||
|
background: #1d1d1d;
|
||||||
|
border-radius: 0.1rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
color: $body-font-color;
|
flex-direction: column;
|
||||||
font-size: 16px;
|
position: absolute;
|
||||||
z-index: 400;
|
pointer-events: initial;
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
overflow: hidden;
|
|
||||||
position: fixed;
|
|
||||||
height: 100vh;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
|
|
||||||
.context-container {
|
.context-element {
|
||||||
min-width: 100px;
|
|
||||||
z-index: 10;
|
|
||||||
box-shadow: 0 0 2px 0 #000;
|
|
||||||
padding: 0;
|
|
||||||
background: #1d1d1d;
|
|
||||||
border-radius: 0.1rem;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
align-items: center;
|
||||||
position: absolute;
|
padding: 0.1rem 0.3rem;
|
||||||
pointer-events: initial;
|
cursor: pointer;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.context-element {
|
.context-submenu {
|
||||||
display: flex;
|
opacity: 0;
|
||||||
align-items: center;
|
visibility: hidden;
|
||||||
padding: 0.1rem 0.3rem;
|
transition: opacity 0.2s;
|
||||||
cursor: pointer;
|
position: absolute;
|
||||||
justify-content: space-between;
|
left: 100%;
|
||||||
position: relative;
|
top: 0;
|
||||||
|
background: #1d1d1d;
|
||||||
|
box-shadow: 0 0 2px 0 #000;
|
||||||
|
min-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $primary-color;
|
||||||
|
|
||||||
.context-submenu {
|
.context-submenu {
|
||||||
opacity: 0;
|
display: block;
|
||||||
visibility: hidden;
|
visibility: visible;
|
||||||
transition: opacity 0.2s;
|
opacity: 1;
|
||||||
position: absolute;
|
|
||||||
left: 100%;
|
|
||||||
top: 0;
|
|
||||||
background: #1d1d1d;
|
|
||||||
box-shadow: 0 0 2px 0 #000;
|
|
||||||
min-width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: $primary-color;
|
|
||||||
|
|
||||||
.context-submenu {
|
|
||||||
display: block;
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-overlay {
|
|
||||||
background: transparent;
|
|
||||||
bottom: 0;
|
|
||||||
cursor: default;
|
|
||||||
display: block;
|
|
||||||
left: 0;
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.context-overlay {
|
||||||
|
background: transparent;
|
||||||
|
bottom: 0;
|
||||||
|
cursor: default;
|
||||||
|
display: block;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
filter: grayscale(100%);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
22
src/renderer/components/BaseLoader.vue
Normal file
22
src/renderer/components/BaseLoader.vue
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<template>
|
||||||
|
<div class="empty">
|
||||||
|
<div class="loading loading-lg" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'BaseLoader'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.empty {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
left: 0;
|
||||||
|
justify-content: center;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -15,7 +15,7 @@
|
|||||||
<form class="form-horizontal">
|
<form class="form-horizontal">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<label class="form-label">{{ $t('word.user') }}:</label>
|
<label class="form-label">{{ $t('word.user') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<input
|
<input
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<label class="form-label">{{ $t('word.password') }}:</label>
|
<label class="form-label">{{ $t('word.password') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<input
|
<input
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
<fieldset class="m-0" :disabled="isTesting">
|
<fieldset class="m-0" :disabled="isTesting">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-4 col-sm-12">
|
<div class="col-4 col-sm-12">
|
||||||
<label class="form-label">{{ $t('word.connectionName') }}:</label>
|
<label class="form-label">{{ $t('word.connectionName') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<div class="col-8 col-sm-12">
|
||||||
<input
|
<input
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-4 col-sm-12">
|
<div class="col-4 col-sm-12">
|
||||||
<label class="form-label">{{ $t('word.client') }}:</label>
|
<label class="form-label">{{ $t('word.client') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<div class="col-8 col-sm-12">
|
||||||
<select v-model="localConnection.client" class="form-select">
|
<select v-model="localConnection.client" class="form-select">
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-4 col-sm-12">
|
<div class="col-4 col-sm-12">
|
||||||
<label class="form-label">{{ $t('word.hostName') }}/IP:</label>
|
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<div class="col-8 col-sm-12">
|
||||||
<input
|
<input
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-4 col-sm-12">
|
<div class="col-4 col-sm-12">
|
||||||
<label class="form-label">{{ $t('word.port') }}:</label>
|
<label class="form-label">{{ $t('word.port') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<div class="col-8 col-sm-12">
|
||||||
<input
|
<input
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-4 col-sm-12">
|
<div class="col-4 col-sm-12">
|
||||||
<label class="form-label">{{ $t('word.user') }}:</label>
|
<label class="form-label">{{ $t('word.user') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<div class="col-8 col-sm-12">
|
||||||
<input
|
<input
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-4 col-sm-12">
|
<div class="col-4 col-sm-12">
|
||||||
<label class="form-label">{{ $t('word.password') }}:</label>
|
<label class="form-label">{{ $t('word.password') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<div class="col-8 col-sm-12">
|
||||||
<input
|
<input
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
<form class="form-horizontal">
|
<form class="form-horizontal">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<label class="form-label">{{ $t('word.name') }}:</label>
|
<label class="form-label">{{ $t('word.name') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<input
|
<input
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<label class="form-label">{{ $t('word.collation') }}:</label>
|
<label class="form-label">{{ $t('word.collation') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<select
|
<select
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
<fieldset class="m-0" :disabled="isTesting">
|
<fieldset class="m-0" :disabled="isTesting">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-4 col-sm-12">
|
<div class="col-4 col-sm-12">
|
||||||
<label class="form-label">{{ $t('word.connectionName') }}:</label>
|
<label class="form-label">{{ $t('word.connectionName') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<div class="col-8 col-sm-12">
|
||||||
<input
|
<input
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-4 col-sm-12">
|
<div class="col-4 col-sm-12">
|
||||||
<label class="form-label">{{ $t('word.client') }}:</label>
|
<label class="form-label">{{ $t('word.client') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<div class="col-8 col-sm-12">
|
||||||
<select
|
<select
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-4 col-sm-12">
|
<div class="col-4 col-sm-12">
|
||||||
<label class="form-label">{{ $t('word.hostName') }}/IP:</label>
|
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<div class="col-8 col-sm-12">
|
||||||
<input
|
<input
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-4 col-sm-12">
|
<div class="col-4 col-sm-12">
|
||||||
<label class="form-label">{{ $t('word.port') }}:</label>
|
<label class="form-label">{{ $t('word.port') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<div class="col-8 col-sm-12">
|
||||||
<input
|
<input
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-4 col-sm-12">
|
<div class="col-4 col-sm-12">
|
||||||
<label class="form-label">{{ $t('word.user') }}:</label>
|
<label class="form-label">{{ $t('word.user') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<div class="col-8 col-sm-12">
|
||||||
<input
|
<input
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-4 col-sm-12">
|
<div class="col-4 col-sm-12">
|
||||||
<label class="form-label">{{ $t('word.password') }}:</label>
|
<label class="form-label">{{ $t('word.password') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<div class="col-8 col-sm-12">
|
||||||
<input
|
<input
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
<form class="form-horizontal">
|
<form class="form-horizontal">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<label class="form-label">{{ $t('word.name') }}:</label>
|
<label class="form-label">{{ $t('word.name') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<input
|
<input
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<label class="form-label">{{ $t('word.collation') }}:</label>
|
<label class="form-label">{{ $t('word.collation') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<select v-model="database.collation" class="form-select">
|
<select v-model="database.collation" class="form-select">
|
||||||
|
184
src/renderer/components/ModalNewFunction.vue
Normal file
184
src/renderer/components/ModalNewFunction.vue
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.confirm')"
|
||||||
|
size="400"
|
||||||
|
@confirm="confirmNewFunction"
|
||||||
|
@hide="$emit('close')"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-plus mr-1" /> {{ $t('message.createNewRoutine') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.name') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="localFunction.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.definer') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select
|
||||||
|
v-if="workspace.users.length"
|
||||||
|
v-model="localFunction.definer"
|
||||||
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="user in workspace.users"
|
||||||
|
:key="`${user.name}@${user.host}`"
|
||||||
|
:value="`\`${user.name}\`@\`${user.host}\``"
|
||||||
|
>
|
||||||
|
{{ user.name }}@{{ user.host }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<select v-if="!workspace.users.length" class="form-select">
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.returns') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<div class="input-group">
|
||||||
|
<select
|
||||||
|
v-model="localFunction.returns"
|
||||||
|
class="form-select text-uppercase"
|
||||||
|
style="width: 0;"
|
||||||
|
>
|
||||||
|
<optgroup
|
||||||
|
v-for="group in workspace.dataTypes"
|
||||||
|
:key="group.group"
|
||||||
|
:label="group.group"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="type in group.types"
|
||||||
|
:key="type.name"
|
||||||
|
:selected="localFunction.returns === type.name"
|
||||||
|
:value="type.name"
|
||||||
|
>
|
||||||
|
{{ type.name }}
|
||||||
|
</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
<input
|
||||||
|
v-model="localFunction.returnsLength"
|
||||||
|
class="form-input"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.comment') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
v-model="localFunction.comment"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('message.sqlSecurity') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="localFunction.security" class="form-select">
|
||||||
|
<option>DEFINER</option>
|
||||||
|
<option>INVOKER</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('message.dataAccess') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="localFunction.dataAccess" class="form-select">
|
||||||
|
<option>CONTAINS SQL</option>
|
||||||
|
<option>NO SQL</option>
|
||||||
|
<option>READS SQL DATA</option>
|
||||||
|
<option>MODIFIES SQL DATA</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4" />
|
||||||
|
<div class="column">
|
||||||
|
<label class="form-checkbox form-inline">
|
||||||
|
<input v-model="localFunction.deterministic" type="checkbox"><i class="form-icon" /> {{ $t('word.deterministic') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalNewFunction',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
localFunction: {
|
||||||
|
definer: '',
|
||||||
|
sql: 'BEGIN\r\n RETURN NULL;\r\nEND',
|
||||||
|
parameters: [],
|
||||||
|
name: '',
|
||||||
|
comment: '',
|
||||||
|
returns: 'INT',
|
||||||
|
returnsLength: 10,
|
||||||
|
security: 'DEFINER',
|
||||||
|
deterministic: false,
|
||||||
|
dataAccess: 'CONTAINS SQL'
|
||||||
|
},
|
||||||
|
isOptionsChanging: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
schema () {
|
||||||
|
return this.workspace.breadcrumbs.schema;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmNewFunction () {
|
||||||
|
this.$emit('open-create-function-editor', this.localFunction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
147
src/renderer/components/ModalNewRoutine.vue
Normal file
147
src/renderer/components/ModalNewRoutine.vue
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.confirm')"
|
||||||
|
size="400"
|
||||||
|
@confirm="confirmNewRoutine"
|
||||||
|
@hide="$emit('close')"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-plus mr-1" /> {{ $t('message.createNewRoutine') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.name') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="localRoutine.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.definer') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select
|
||||||
|
v-if="workspace.users.length"
|
||||||
|
v-model="localRoutine.definer"
|
||||||
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="user in workspace.users"
|
||||||
|
:key="`${user.name}@${user.host}`"
|
||||||
|
:value="`\`${user.name}\`@\`${user.host}\``"
|
||||||
|
>
|
||||||
|
{{ user.name }}@{{ user.host }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<select v-if="!workspace.users.length" class="form-select">
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.comment') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
v-model="localRoutine.comment"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('message.sqlSecurity') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="localRoutine.security" class="form-select">
|
||||||
|
<option>DEFINER</option>
|
||||||
|
<option>INVOKER</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('message.dataAccess') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="localRoutine.dataAccess" class="form-select">
|
||||||
|
<option>CONTAINS SQL</option>
|
||||||
|
<option>NO SQL</option>
|
||||||
|
<option>READS SQL DATA</option>
|
||||||
|
<option>MODIFIES SQL DATA</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4" />
|
||||||
|
<div class="column">
|
||||||
|
<label class="form-checkbox form-inline">
|
||||||
|
<input v-model="localRoutine.deterministic" type="checkbox"><i class="form-icon" /> {{ $t('word.deterministic') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalNewRoutine',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
localRoutine: {
|
||||||
|
definer: '',
|
||||||
|
sql: 'BEGIN\r\n\r\nEND',
|
||||||
|
parameters: [],
|
||||||
|
name: '',
|
||||||
|
comment: '',
|
||||||
|
security: 'DEFINER',
|
||||||
|
deterministic: false,
|
||||||
|
dataAccess: 'CONTAINS SQL'
|
||||||
|
},
|
||||||
|
isOptionsChanging: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
schema () {
|
||||||
|
return this.workspace.breadcrumbs.schema;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmNewRoutine () {
|
||||||
|
this.$emit('open-create-routine-editor', this.localRoutine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
109
src/renderer/components/ModalNewScheduler.vue
Normal file
109
src/renderer/components/ModalNewScheduler.vue
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.confirm')"
|
||||||
|
size="400"
|
||||||
|
@confirm="confirmNewTrigger"
|
||||||
|
@hide="$emit('close')"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-plus mr-1" /> {{ $t('message.createNewScheduler') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.name') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="localScheduler.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.definer') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select
|
||||||
|
v-if="workspace.users.length"
|
||||||
|
v-model="localScheduler.definer"
|
||||||
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="user in workspace.users"
|
||||||
|
:key="`${user.name}@${user.host}`"
|
||||||
|
:value="`\`${user.name}\`@\`${user.host}\``"
|
||||||
|
>
|
||||||
|
{{ user.name }}@{{ user.host }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<select v-if="!workspace.users.length" class="form-select">
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.comment') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
v-model="localScheduler.comment"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalNewScheduler',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
localScheduler: {
|
||||||
|
definer: '',
|
||||||
|
sql: 'BEGIN\r\n\r\nEND',
|
||||||
|
name: '',
|
||||||
|
comment: '',
|
||||||
|
execution: 'EVERY',
|
||||||
|
every: ['1', 'DAY'],
|
||||||
|
preserve: true,
|
||||||
|
state: 'DISABLE'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmNewTrigger () {
|
||||||
|
this.$emit('open-create-scheduler-editor', this.localScheduler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@@ -84,7 +84,6 @@ export default {
|
|||||||
ConfirmModal
|
ConfirmModal
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
table: String,
|
|
||||||
workspace: Object
|
workspace: Object
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
|
@@ -253,7 +253,7 @@ export default {
|
|||||||
},
|
},
|
||||||
fieldLength (field) {
|
fieldLength (field) {
|
||||||
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
||||||
return field.numLength || field.datePrecision || field.charLength || 0;
|
return field.length;
|
||||||
},
|
},
|
||||||
inputProps (field) {
|
inputProps (field) {
|
||||||
if ([...TEXT, ...LONG_TEXT].includes(field.type))
|
if ([...TEXT, ...LONG_TEXT].includes(field.type))
|
||||||
|
140
src/renderer/components/ModalNewTrigger.vue
Normal file
140
src/renderer/components/ModalNewTrigger.vue
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.confirm')"
|
||||||
|
size="400"
|
||||||
|
@confirm="confirmNewTrigger"
|
||||||
|
@hide="$emit('close')"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-plus mr-1" /> {{ $t('message.createNewTrigger') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.name') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="localTrigger.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.definer') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select
|
||||||
|
v-if="workspace.users.length"
|
||||||
|
v-model="localTrigger.definer"
|
||||||
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="user in workspace.users"
|
||||||
|
:key="`${user.name}@${user.host}`"
|
||||||
|
:value="`\`${user.name}\`@\`${user.host}\``"
|
||||||
|
>
|
||||||
|
{{ user.name }}@{{ user.host }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<select v-if="!workspace.users.length" class="form-select">
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.table') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="localTrigger.table" class="form-select">
|
||||||
|
<option v-for="table in schemaTables" :key="table.name">
|
||||||
|
{{ table.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.event') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<div class="input-group">
|
||||||
|
<select v-model="localTrigger.event1" class="form-select">
|
||||||
|
<option>BEFORE</option>
|
||||||
|
<option>AFTER</option>
|
||||||
|
</select>
|
||||||
|
<select v-model="localTrigger.event2" class="form-select">
|
||||||
|
<option>INSERT</option>
|
||||||
|
<option>UPDATE</option>
|
||||||
|
<option>DELETE</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalNewTrigger',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
localTrigger: {
|
||||||
|
definer: '',
|
||||||
|
sql: 'BEGIN\r\n\r\nEND',
|
||||||
|
name: '',
|
||||||
|
table: '',
|
||||||
|
event1: 'BEFORE',
|
||||||
|
event2: 'INSERT'
|
||||||
|
},
|
||||||
|
isOptionsChanging: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
schema () {
|
||||||
|
return this.workspace.breadcrumbs.schema;
|
||||||
|
},
|
||||||
|
schemaTables () {
|
||||||
|
const schemaTables = this.workspace.structure
|
||||||
|
.filter(schema => schema.name === this.schema)
|
||||||
|
.map(schema => schema.tables);
|
||||||
|
|
||||||
|
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.localTrigger.table = this.schemaTables.length ? this.schemaTables[0].name : '';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmNewTrigger () {
|
||||||
|
this.$emit('open-create-trigger-editor', this.localTrigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
192
src/renderer/components/ModalNewView.vue
Normal file
192
src/renderer/components/ModalNewView.vue
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.confirm')"
|
||||||
|
size="medium"
|
||||||
|
@confirm="confirmOptionsChange"
|
||||||
|
@hide="$emit('close')"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-eye-plus mr-1" /> {{ $t('message.createNewView') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<div class="container">
|
||||||
|
<div class="columns mb-4">
|
||||||
|
<div class="column col-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('word.name') }}</label>
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="localView.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('word.definer') }}</label>
|
||||||
|
<select v-model="localView.definer" class="form-select">
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="user in workspace.users"
|
||||||
|
:key="`${user.name}@${user.host}`"
|
||||||
|
:value="`\`${user.name}\`@\`${user.host}\``"
|
||||||
|
>
|
||||||
|
{{ user.name }}@{{ user.host }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column col-4">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('message.sqlSecurity') }}</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="localView.security"
|
||||||
|
type="radio"
|
||||||
|
name="security"
|
||||||
|
value="DEFINER"
|
||||||
|
>
|
||||||
|
<i class="form-icon" /> DEFINER
|
||||||
|
</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="localView.security"
|
||||||
|
type="radio"
|
||||||
|
name="security"
|
||||||
|
value="INVOKER"
|
||||||
|
>
|
||||||
|
<i class="form-icon" /> INVOKER
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-4">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('word.algorithm') }}</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="localView.algorithm"
|
||||||
|
type="radio"
|
||||||
|
name="algorithm"
|
||||||
|
value="UNDEFINED"
|
||||||
|
>
|
||||||
|
<i class="form-icon" /> UNDEFINED
|
||||||
|
</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="localView.algorithm"
|
||||||
|
type="radio"
|
||||||
|
value="MERGE"
|
||||||
|
name="algorithm"
|
||||||
|
>
|
||||||
|
<i class="form-icon" /> MERGE
|
||||||
|
</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="localView.algorithm"
|
||||||
|
type="radio"
|
||||||
|
value="TEMPTABLE"
|
||||||
|
name="algorithm"
|
||||||
|
>
|
||||||
|
<i class="form-icon" /> TEMPTABLE
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-4">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('message.updateOption') }}</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="localView.updateOption"
|
||||||
|
type="radio"
|
||||||
|
name="update"
|
||||||
|
value=""
|
||||||
|
>
|
||||||
|
<i class="form-icon" /> None
|
||||||
|
</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="localView.updateOption"
|
||||||
|
type="radio"
|
||||||
|
name="update"
|
||||||
|
value="CASCADED"
|
||||||
|
>
|
||||||
|
<i class="form-icon" /> CASCADED
|
||||||
|
</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="localView.updateOption"
|
||||||
|
type="radio"
|
||||||
|
name="update"
|
||||||
|
value="LOCAL"
|
||||||
|
>
|
||||||
|
<i class="form-icon" /> LOCAL
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="workspace-query-results column col-12 mt-2">
|
||||||
|
<label class="form-label ml-2">{{ $t('message.selectStatement') }}</label>
|
||||||
|
<QueryEditor
|
||||||
|
ref="queryEditor"
|
||||||
|
:value.sync="localView.sql"
|
||||||
|
:workspace="workspace"
|
||||||
|
:schema="schema"
|
||||||
|
:height="editorHeight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
import QueryEditor from '@/components/QueryEditor';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalNewView',
|
||||||
|
components: {
|
||||||
|
ConfirmModal,
|
||||||
|
QueryEditor
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
localView: {
|
||||||
|
algorithm: 'UNDEFINED',
|
||||||
|
definer: '',
|
||||||
|
security: 'DEFINER',
|
||||||
|
updateOption: '',
|
||||||
|
sql: '',
|
||||||
|
name: ''
|
||||||
|
},
|
||||||
|
isOptionsChanging: false,
|
||||||
|
editorHeight: 300
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
schema () {
|
||||||
|
return this.workspace.breadcrumbs.schema;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmOptionsChange () {
|
||||||
|
this.$emit('open-create-view-editor', this.localView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@@ -56,7 +56,7 @@
|
|||||||
<div class="col-6 col-sm-12">
|
<div class="col-6 col-sm-12">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
<i class="mdi mdi-18px mdi-translate mr-1" />
|
<i class="mdi mdi-18px mdi-translate mr-1" />
|
||||||
{{ $t('word.language') }}:
|
{{ $t('word.language') }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 col-sm-12">
|
<div class="col-6 col-sm-12">
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
<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">
|
||||||
{{ $t('message.notificationsTimeout') }}:
|
{{ $t('message.notificationsTimeout') }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 col-sm-12">
|
<div class="col-6 col-sm-12">
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
<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">
|
||||||
{{ $t('word.autoCompletion') }}:
|
{{ $t('word.autoCompletion') }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 col-sm-12">
|
<div class="col-6 col-sm-12">
|
||||||
@@ -114,6 +114,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-6 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('message.wrapLongLines') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-sm-12">
|
||||||
|
<label class="form-switch d-inline-block" @click.prevent="toggleLineWrap">
|
||||||
|
<input type="checkbox" :checked="selectedLineWrap">
|
||||||
|
<i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -190,9 +205,9 @@
|
|||||||
<img :src="require('@/images/logo.svg').default" width="128">
|
<img :src="require('@/images/logo.svg').default" width="128">
|
||||||
<h4>{{ appName }}</h4>
|
<h4>{{ appName }}</h4>
|
||||||
<p>
|
<p>
|
||||||
{{ $t('word.version') }}: {{ appVersion }}<br>
|
{{ $t('word.version') }} {{ appVersion }}<br>
|
||||||
<a class="c-hand" @click="openOutside('https://github.com/Fabio286/antares')">GitHub</a><br>
|
<a class="c-hand" @click="openOutside('https://github.com/Fabio286/antares')">GitHub</a> | <a class="c-hand" @click="openOutside('https://github.com/Fabio286/antares/blob/master/CHANGELOG.md')">CHANGELOG</a><br>
|
||||||
<small>{{ $t('word.author') }}: <a class="c-hand" @click="openOutside('https://github.com/Fabio286')">Fabio Di Stasio</a></small><br>
|
<small>{{ $t('word.author') }} <a class="c-hand" @click="openOutside('https://github.com/Fabio286')">Fabio Di Stasio</a></small><br>
|
||||||
<small>{{ $t('message.madeWithJS') }}</small>
|
<small>{{ $t('message.madeWithJS') }}</small>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -280,6 +295,7 @@ export default {
|
|||||||
selectedSettingTab: 'application/selectedSettingTab',
|
selectedSettingTab: 'application/selectedSettingTab',
|
||||||
selectedLocale: 'settings/getLocale',
|
selectedLocale: 'settings/getLocale',
|
||||||
selectedAutoComplete: 'settings/getAutoComplete',
|
selectedAutoComplete: 'settings/getAutoComplete',
|
||||||
|
selectedLineWrap: 'settings/getLineWrap',
|
||||||
notificationsTimeout: 'settings/getNotificationsTimeout',
|
notificationsTimeout: 'settings/getNotificationsTimeout',
|
||||||
applicationTheme: 'settings/getApplicationTheme',
|
applicationTheme: 'settings/getApplicationTheme',
|
||||||
editorTheme: 'settings/getEditorTheme',
|
editorTheme: 'settings/getEditorTheme',
|
||||||
@@ -333,6 +349,7 @@ ORDER BY
|
|||||||
closeModal: 'application/hideSettingModal',
|
closeModal: 'application/hideSettingModal',
|
||||||
changeLocale: 'settings/changeLocale',
|
changeLocale: 'settings/changeLocale',
|
||||||
changeAutoComplete: 'settings/changeAutoComplete',
|
changeAutoComplete: 'settings/changeAutoComplete',
|
||||||
|
changeLineWrap: 'settings/changeLineWrap',
|
||||||
changeEditorTheme: 'settings/changeEditorTheme',
|
changeEditorTheme: 'settings/changeEditorTheme',
|
||||||
updateNotificationsTimeout: 'settings/updateNotificationsTimeout'
|
updateNotificationsTimeout: 'settings/updateNotificationsTimeout'
|
||||||
}),
|
}),
|
||||||
@@ -355,6 +372,9 @@ ORDER BY
|
|||||||
},
|
},
|
||||||
toggleAutoComplete () {
|
toggleAutoComplete () {
|
||||||
this.changeAutoComplete(!this.selectedAutoComplete);
|
this.changeAutoComplete(!this.selectedAutoComplete);
|
||||||
|
},
|
||||||
|
toggleLineWrap () {
|
||||||
|
this.changeLineWrap(!this.selectedLineWrap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -35,7 +35,8 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
editorTheme: 'settings/getEditorTheme',
|
editorTheme: 'settings/getEditorTheme',
|
||||||
autoComplete: 'settings/getAutoComplete'
|
autoComplete: 'settings/getAutoComplete',
|
||||||
|
lineWrap: 'settings/getLineWrap'
|
||||||
}),
|
}),
|
||||||
tables () {
|
tables () {
|
||||||
return this.workspace
|
return this.workspace
|
||||||
@@ -46,13 +47,68 @@ export default {
|
|||||||
}, []).map(table => {
|
}, []).map(table => {
|
||||||
return {
|
return {
|
||||||
name: table.name,
|
name: table.name,
|
||||||
comment: table.comment,
|
|
||||||
type: table.type,
|
type: table.type,
|
||||||
fields: []
|
fields: []
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
},
|
},
|
||||||
|
triggers () {
|
||||||
|
return this.workspace
|
||||||
|
? this.workspace.structure.filter(schema => schema.name === this.schema)
|
||||||
|
.reduce((acc, curr) => {
|
||||||
|
acc.push(...curr.triggers);
|
||||||
|
return acc;
|
||||||
|
}, []).map(trigger => {
|
||||||
|
return {
|
||||||
|
name: trigger.name,
|
||||||
|
type: 'trigger'
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
procedures () {
|
||||||
|
return this.workspace
|
||||||
|
? this.workspace.structure.filter(schema => schema.name === this.schema)
|
||||||
|
.reduce((acc, curr) => {
|
||||||
|
acc.push(...curr.procedures);
|
||||||
|
return acc;
|
||||||
|
}, []).map(procedure => {
|
||||||
|
return {
|
||||||
|
name: `${procedure.name}()`,
|
||||||
|
type: 'routine'
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
functions () {
|
||||||
|
return this.workspace
|
||||||
|
? this.workspace.structure.filter(schema => schema.name === this.schema)
|
||||||
|
.reduce((acc, curr) => {
|
||||||
|
acc.push(...curr.functions);
|
||||||
|
return acc;
|
||||||
|
}, []).map(func => {
|
||||||
|
return {
|
||||||
|
name: `${func.name}()`,
|
||||||
|
type: 'function'
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
schedulers () {
|
||||||
|
return this.workspace
|
||||||
|
? this.workspace.structure.filter(schema => schema.name === this.schema)
|
||||||
|
.reduce((acc, curr) => {
|
||||||
|
acc.push(...curr.schedulers);
|
||||||
|
return acc;
|
||||||
|
}, []).map(scheduler => {
|
||||||
|
return {
|
||||||
|
name: scheduler.name,
|
||||||
|
type: 'scheduler'
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
},
|
||||||
mode () {
|
mode () {
|
||||||
switch (this.workspace.client) {
|
switch (this.workspace.client) {
|
||||||
case 'mysql':
|
case 'mysql':
|
||||||
@@ -100,6 +156,13 @@ export default {
|
|||||||
enableLiveAutocompletion: this.autoComplete
|
enableLiveAutocompletion: this.autoComplete
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
lineWrap () {
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.setOptions({
|
||||||
|
wrap: this.lineWrap
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
@@ -114,6 +177,7 @@ export default {
|
|||||||
|
|
||||||
this.editor.setOptions({
|
this.editor.setOptions({
|
||||||
enableBasicAutocompletion: true,
|
enableBasicAutocompletion: true,
|
||||||
|
wrap: this.lineWrap,
|
||||||
enableSnippets: true,
|
enableSnippets: true,
|
||||||
enableLiveAutocompletion: this.autoComplete
|
enableLiveAutocompletion: this.autoComplete
|
||||||
});
|
});
|
||||||
@@ -121,11 +185,16 @@ export default {
|
|||||||
this.editor.completers.push({
|
this.editor.completers.push({
|
||||||
getCompletions: (editor, session, pos, prefix, callback) => {
|
getCompletions: (editor, session, pos, prefix, callback) => {
|
||||||
const completions = [];
|
const completions = [];
|
||||||
this.tables.forEach(table => {
|
[
|
||||||
|
...this.tables,
|
||||||
|
...this.triggers,
|
||||||
|
...this.procedures,
|
||||||
|
...this.functions,
|
||||||
|
...this.schedulers
|
||||||
|
].forEach(el => {
|
||||||
completions.push({
|
completions.push({
|
||||||
value: table.name,
|
value: el.name,
|
||||||
meta: table.type,
|
meta: el.type
|
||||||
caption: table.comment
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
callback(null, completions);
|
callback(null, completions);
|
||||||
@@ -173,8 +242,13 @@ export default {
|
|||||||
if (this.autoFocus) {
|
if (this.autoFocus) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.editor.focus();
|
this.editor.focus();
|
||||||
|
this.editor.resize();
|
||||||
}, 20);
|
}, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.editor.resize();
|
||||||
|
}, 20);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@@ -13,25 +13,25 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
v-if="workspace.breadcrumbs.table"
|
v-if="schemaChild"
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{'active': selectedTab === 'prop'}"
|
:class="{'active': selectedTab === 'prop'}"
|
||||||
@click="selectTab({uid: workspace.uid, tab: 'prop'})"
|
@click="selectTab({uid: workspace.uid, tab: 'prop'})"
|
||||||
>
|
>
|
||||||
<a class="tab-link">
|
<a class="tab-link">
|
||||||
<i class="mdi mdi-18px mdi-tune mr-1" />
|
<i class="mdi mdi-18px mdi-tune mr-1" />
|
||||||
<span :title="workspace.breadcrumbs.table">{{ $t('word.properties').toUpperCase() }}: {{ workspace.breadcrumbs.table }}</span>
|
<span :title="schemaChild">{{ $t('word.properties').toUpperCase() }}: {{ schemaChild }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
v-if="workspace.breadcrumbs.table"
|
v-if="workspace.breadcrumbs.table || workspace.breadcrumbs.view"
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{'active': selectedTab === 'data'}"
|
:class="{'active': selectedTab === 'data'}"
|
||||||
@click="selectTab({uid: workspace.uid, tab: 'data'})"
|
@click="selectTab({uid: workspace.uid, tab: 'data'})"
|
||||||
>
|
>
|
||||||
<a class="tab-link">
|
<a class="tab-link">
|
||||||
<i class="mdi mdi-18px mdi-table mr-1" />
|
<i class="mdi mdi-18px mr-1" :class="workspace.breadcrumbs.table ? 'mdi-table' : 'mdi-table-eye'" />
|
||||||
<span :title="workspace.breadcrumbs.table">{{ $t('word.data').toUpperCase() }}: {{ workspace.breadcrumbs.table }}</span>
|
<span :title="schemaChild">{{ $t('word.data').toUpperCase() }}: {{ schemaChild }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
@@ -66,15 +66,45 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<WorkspacePropsTab
|
<WorkspacePropsTab
|
||||||
v-show="selectedTab === 'prop'"
|
v-show="selectedTab === 'prop' && workspace.breadcrumbs.table"
|
||||||
:is-selected="selectedTab === 'prop'"
|
:is-selected="selectedTab === 'prop'"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:table="workspace.breadcrumbs.table"
|
: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"
|
||||||
|
/>
|
||||||
|
<WorkspacePropsTabScheduler
|
||||||
|
v-show="selectedTab === 'prop' && workspace.breadcrumbs.scheduler"
|
||||||
|
:is-selected="selectedTab === 'prop'"
|
||||||
|
:connection="connection"
|
||||||
|
:scheduler="workspace.breadcrumbs.scheduler"
|
||||||
|
/>
|
||||||
<WorkspaceTableTab
|
<WorkspaceTableTab
|
||||||
v-show="selectedTab === 'data'"
|
v-show="selectedTab === 'data'"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:table="workspace.breadcrumbs.table"
|
:table="workspace.breadcrumbs.table || workspace.breadcrumbs.view"
|
||||||
/>
|
/>
|
||||||
<WorkspaceQueryTab
|
<WorkspaceQueryTab
|
||||||
v-for="tab of queryTabs"
|
v-for="tab of queryTabs"
|
||||||
@@ -94,6 +124,11 @@ import WorkspaceExploreBar from '@/components/WorkspaceExploreBar';
|
|||||||
import WorkspaceQueryTab from '@/components/WorkspaceQueryTab';
|
import WorkspaceQueryTab from '@/components/WorkspaceQueryTab';
|
||||||
import WorkspaceTableTab from '@/components/WorkspaceTableTab';
|
import WorkspaceTableTab from '@/components/WorkspaceTableTab';
|
||||||
import WorkspacePropsTab from '@/components/WorkspacePropsTab';
|
import WorkspacePropsTab from '@/components/WorkspacePropsTab';
|
||||||
|
import WorkspacePropsTabView from '@/components/WorkspacePropsTabView';
|
||||||
|
import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger';
|
||||||
|
import WorkspacePropsTabRoutine from '@/components/WorkspacePropsTabRoutine';
|
||||||
|
import WorkspacePropsTabFunction from '@/components/WorkspacePropsTabFunction';
|
||||||
|
import WorkspacePropsTabScheduler from '@/components/WorkspacePropsTabScheduler';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Workspace',
|
name: 'Workspace',
|
||||||
@@ -101,7 +136,12 @@ export default {
|
|||||||
WorkspaceExploreBar,
|
WorkspaceExploreBar,
|
||||||
WorkspaceQueryTab,
|
WorkspaceQueryTab,
|
||||||
WorkspaceTableTab,
|
WorkspaceTableTab,
|
||||||
WorkspacePropsTab
|
WorkspacePropsTab,
|
||||||
|
WorkspacePropsTabView,
|
||||||
|
WorkspacePropsTabTrigger,
|
||||||
|
WorkspacePropsTabRoutine,
|
||||||
|
WorkspacePropsTabFunction,
|
||||||
|
WorkspacePropsTabScheduler
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object
|
connection: Object
|
||||||
@@ -123,7 +163,15 @@ export default {
|
|||||||
return this.selectedWorkspace === this.connection.uid;
|
return this.selectedWorkspace === this.connection.uid;
|
||||||
},
|
},
|
||||||
selectedTab () {
|
selectedTab () {
|
||||||
if (this.workspace.breadcrumbs.table === null && ['data', 'prop'].includes(this.workspace.selected_tab))
|
if (
|
||||||
|
this.workspace.breadcrumbs.table === null &&
|
||||||
|
this.workspace.breadcrumbs.view === null &&
|
||||||
|
this.workspace.breadcrumbs.trigger === null &&
|
||||||
|
this.workspace.breadcrumbs.procedure === null &&
|
||||||
|
this.workspace.breadcrumbs.function === null &&
|
||||||
|
this.workspace.breadcrumbs.scheduler === null &&
|
||||||
|
['data', 'prop'].includes(this.workspace.selected_tab)
|
||||||
|
)
|
||||||
return this.queryTabs[0].uid;
|
return this.queryTabs[0].uid;
|
||||||
|
|
||||||
return this.queryTabs.find(tab => tab.uid === this.workspace.selected_tab) ||
|
return this.queryTabs.find(tab => tab.uid === this.workspace.selected_tab) ||
|
||||||
@@ -133,6 +181,13 @@ export default {
|
|||||||
},
|
},
|
||||||
queryTabs () {
|
queryTabs () {
|
||||||
return this.workspace.tabs.filter(tab => tab.type === 'query');
|
return this.workspace.tabs.filter(tab => tab.type === 'query');
|
||||||
|
},
|
||||||
|
schemaChild () {
|
||||||
|
for (const key in this.workspace.breadcrumbs) {
|
||||||
|
if (key === 'schema') continue;
|
||||||
|
if (this.workspace.breadcrumbs[key]) return this.workspace.breadcrumbs[key];
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created () {
|
async created () {
|
||||||
|
@@ -40,6 +40,7 @@
|
|||||||
:connection="connection"
|
:connection="connection"
|
||||||
@show-database-context="openDatabaseContext"
|
@show-database-context="openDatabaseContext"
|
||||||
@show-table-context="openTableContext"
|
@show-table-context="openTableContext"
|
||||||
|
@show-misc-context="openMiscContext"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -54,12 +55,47 @@
|
|||||||
@close="hideCreateTableModal"
|
@close="hideCreateTableModal"
|
||||||
@open-create-table-editor="openCreateTableEditor"
|
@open-create-table-editor="openCreateTableEditor"
|
||||||
/>
|
/>
|
||||||
|
<ModalNewView
|
||||||
|
v-if="isNewViewModal"
|
||||||
|
:workspace="workspace"
|
||||||
|
@close="hideCreateViewModal"
|
||||||
|
@open-create-view-editor="openCreateViewEditor"
|
||||||
|
/>
|
||||||
|
<ModalNewTrigger
|
||||||
|
v-if="isNewTriggerModal"
|
||||||
|
:workspace="workspace"
|
||||||
|
@close="hideCreateTriggerModal"
|
||||||
|
@open-create-trigger-editor="openCreateTriggerEditor"
|
||||||
|
/>
|
||||||
|
<ModalNewRoutine
|
||||||
|
v-if="isNewRoutineModal"
|
||||||
|
:workspace="workspace"
|
||||||
|
@close="hideCreateRoutineModal"
|
||||||
|
@open-create-routine-editor="openCreateRoutineEditor"
|
||||||
|
/>
|
||||||
|
<ModalNewFunction
|
||||||
|
v-if="isNewFunctionModal"
|
||||||
|
:workspace="workspace"
|
||||||
|
@close="hideCreateFunctionModal"
|
||||||
|
@open-create-function-editor="openCreateFunctionEditor"
|
||||||
|
/>
|
||||||
|
<ModalNewScheduler
|
||||||
|
v-if="isNewSchedulerModal"
|
||||||
|
:workspace="workspace"
|
||||||
|
@close="hideCreateSchedulerModal"
|
||||||
|
@open-create-scheduler-editor="openCreateSchedulerEditor"
|
||||||
|
/>
|
||||||
<DatabaseContext
|
<DatabaseContext
|
||||||
v-if="isDatabaseContext"
|
v-if="isDatabaseContext"
|
||||||
:selected-database="selectedDatabase"
|
:selected-database="selectedDatabase"
|
||||||
:context-event="databaseContextEvent"
|
:context-event="databaseContextEvent"
|
||||||
@close-context="closeDatabaseContext"
|
@close-context="closeDatabaseContext"
|
||||||
@show-create-table-modal="showCreateTableModal"
|
@show-create-table-modal="showCreateTableModal"
|
||||||
|
@show-create-view-modal="showCreateViewModal"
|
||||||
|
@show-create-trigger-modal="showCreateTriggerModal"
|
||||||
|
@show-create-routine-modal="showCreateRoutineModal"
|
||||||
|
@show-create-function-modal="showCreateFunctionModal"
|
||||||
|
@show-create-scheduler-modal="showCreateSchedulerModal"
|
||||||
@reload="refresh"
|
@reload="refresh"
|
||||||
/>
|
/>
|
||||||
<TableContext
|
<TableContext
|
||||||
@@ -69,19 +105,39 @@
|
|||||||
@close-context="closeTableContext"
|
@close-context="closeTableContext"
|
||||||
@reload="refresh"
|
@reload="refresh"
|
||||||
/>
|
/>
|
||||||
|
<MiscContext
|
||||||
|
v-if="isMiscContext"
|
||||||
|
:selected-misc="selectedMisc"
|
||||||
|
:context-event="miscContextEvent"
|
||||||
|
@close-context="closeMiscContext"
|
||||||
|
@reload="refresh"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';// TODO: remove
|
||||||
|
|
||||||
import Tables from '@/ipc-api/Tables';
|
import Tables from '@/ipc-api/Tables';
|
||||||
|
import Views from '@/ipc-api/Views';
|
||||||
|
import Triggers from '@/ipc-api/Triggers';
|
||||||
|
import Routines from '@/ipc-api/Routines';
|
||||||
|
import Functions from '@/ipc-api/Functions';
|
||||||
|
import Schedulers from '@/ipc-api/Schedulers';
|
||||||
|
|
||||||
import WorkspaceConnectPanel from '@/components/WorkspaceConnectPanel';
|
import WorkspaceConnectPanel from '@/components/WorkspaceConnectPanel';
|
||||||
import WorkspaceExploreBarDatabase from '@/components/WorkspaceExploreBarDatabase';
|
import WorkspaceExploreBarDatabase from '@/components/WorkspaceExploreBarDatabase';
|
||||||
import DatabaseContext from '@/components/WorkspaceExploreBarDatabaseContext';
|
import DatabaseContext from '@/components/WorkspaceExploreBarDatabaseContext';
|
||||||
import TableContext from '@/components/WorkspaceExploreBarTableContext';
|
import TableContext from '@/components/WorkspaceExploreBarTableContext';
|
||||||
|
import MiscContext from '@/components/WorkspaceExploreBarMiscContext';
|
||||||
import ModalNewDatabase from '@/components/ModalNewDatabase';
|
import ModalNewDatabase from '@/components/ModalNewDatabase';
|
||||||
import ModalNewTable from '@/components/ModalNewTable';
|
import ModalNewTable from '@/components/ModalNewTable';
|
||||||
|
import ModalNewView from '@/components/ModalNewView';
|
||||||
|
import ModalNewTrigger from '@/components/ModalNewTrigger';
|
||||||
|
import ModalNewRoutine from '@/components/ModalNewRoutine';
|
||||||
|
import ModalNewFunction from '@/components/ModalNewFunction';
|
||||||
|
import ModalNewScheduler from '@/components/ModalNewScheduler';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WorkspaceExploreBar',
|
name: 'WorkspaceExploreBar',
|
||||||
@@ -90,8 +146,14 @@ export default {
|
|||||||
WorkspaceExploreBarDatabase,
|
WorkspaceExploreBarDatabase,
|
||||||
DatabaseContext,
|
DatabaseContext,
|
||||||
TableContext,
|
TableContext,
|
||||||
|
MiscContext,
|
||||||
ModalNewDatabase,
|
ModalNewDatabase,
|
||||||
ModalNewTable
|
ModalNewTable,
|
||||||
|
ModalNewView,
|
||||||
|
ModalNewTrigger,
|
||||||
|
ModalNewRoutine,
|
||||||
|
ModalNewFunction,
|
||||||
|
ModalNewScheduler
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object,
|
connection: Object,
|
||||||
@@ -100,15 +162,27 @@ export default {
|
|||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
isRefreshing: false,
|
isRefreshing: false,
|
||||||
|
|
||||||
isNewDBModal: false,
|
isNewDBModal: false,
|
||||||
isNewTableModal: false,
|
isNewTableModal: false,
|
||||||
|
isNewViewModal: false,
|
||||||
|
isNewTriggerModal: false,
|
||||||
|
isNewRoutineModal: false,
|
||||||
|
isNewFunctionModal: false,
|
||||||
|
isNewSchedulerModal: false,
|
||||||
|
|
||||||
localWidth: null,
|
localWidth: null,
|
||||||
isDatabaseContext: false,
|
isDatabaseContext: false,
|
||||||
isTableContext: false,
|
isTableContext: false,
|
||||||
|
isMiscContext: false,
|
||||||
|
|
||||||
databaseContextEvent: null,
|
databaseContextEvent: null,
|
||||||
tableContextEvent: null,
|
tableContextEvent: null,
|
||||||
|
miscContextEvent: null,
|
||||||
|
|
||||||
selectedDatabase: '',
|
selectedDatabase: '',
|
||||||
selectedTable: ''
|
selectedTable: null,
|
||||||
|
selectedMisc: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -215,6 +289,129 @@ export default {
|
|||||||
},
|
},
|
||||||
closeTableContext () {
|
closeTableContext () {
|
||||||
this.isTableContext = false;
|
this.isTableContext = false;
|
||||||
|
},
|
||||||
|
openMiscContext (payload) {
|
||||||
|
this.selectedMisc = payload.misc;
|
||||||
|
this.miscContextEvent = payload.event;
|
||||||
|
this.isMiscContext = true;
|
||||||
|
},
|
||||||
|
closeMiscContext () {
|
||||||
|
this.isMiscContext = false;
|
||||||
|
},
|
||||||
|
showCreateViewModal () {
|
||||||
|
this.closeDatabaseContext();
|
||||||
|
this.isNewViewModal = true;
|
||||||
|
},
|
||||||
|
hideCreateViewModal () {
|
||||||
|
this.isNewViewModal = false;
|
||||||
|
},
|
||||||
|
async openCreateViewEditor (payload) {
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
...payload
|
||||||
|
};
|
||||||
|
|
||||||
|
const { status, response } = await Views.createView(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
await this.refresh();
|
||||||
|
this.changeBreadcrumbs({ schema: this.selectedDatabase, view: payload.name });
|
||||||
|
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
},
|
||||||
|
showCreateTriggerModal () {
|
||||||
|
this.closeDatabaseContext();
|
||||||
|
this.isNewTriggerModal = true;
|
||||||
|
},
|
||||||
|
hideCreateTriggerModal () {
|
||||||
|
this.isNewTriggerModal = false;
|
||||||
|
},
|
||||||
|
async openCreateTriggerEditor (payload) {
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
...payload
|
||||||
|
};
|
||||||
|
|
||||||
|
const { status, response } = await Triggers.createTrigger(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
await this.refresh();
|
||||||
|
this.changeBreadcrumbs({ schema: this.selectedDatabase, trigger: payload.name });
|
||||||
|
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
},
|
||||||
|
showCreateRoutineModal () {
|
||||||
|
this.closeDatabaseContext();
|
||||||
|
this.isNewRoutineModal = true;
|
||||||
|
},
|
||||||
|
hideCreateRoutineModal () {
|
||||||
|
this.isNewRoutineModal = false;
|
||||||
|
},
|
||||||
|
async openCreateRoutineEditor (payload) {
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
...payload
|
||||||
|
};
|
||||||
|
|
||||||
|
const { status, response } = await Routines.createRoutine(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
await this.refresh();
|
||||||
|
this.changeBreadcrumbs({ schema: this.selectedDatabase, procedure: payload.name });
|
||||||
|
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
},
|
||||||
|
showCreateFunctionModal () {
|
||||||
|
this.closeDatabaseContext();
|
||||||
|
this.isNewFunctionModal = true;
|
||||||
|
},
|
||||||
|
hideCreateFunctionModal () {
|
||||||
|
this.isNewFunctionModal = false;
|
||||||
|
},
|
||||||
|
showCreateSchedulerModal () {
|
||||||
|
this.closeDatabaseContext();
|
||||||
|
this.isNewSchedulerModal = true;
|
||||||
|
},
|
||||||
|
hideCreateSchedulerModal () {
|
||||||
|
this.isNewSchedulerModal = false;
|
||||||
|
},
|
||||||
|
async openCreateFunctionEditor (payload) {
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
...payload
|
||||||
|
};
|
||||||
|
|
||||||
|
const { status, response } = await Functions.createFunction(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
await this.refresh();
|
||||||
|
this.changeBreadcrumbs({ schema: this.selectedDatabase, function: payload.name });
|
||||||
|
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
},
|
||||||
|
async openCreateSchedulerEditor (payload) {
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
...payload
|
||||||
|
};
|
||||||
|
|
||||||
|
const { status, response } = await Schedulers.createScheduler(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
await this.refresh();
|
||||||
|
this.changeBreadcrumbs({ schema: this.selectedDatabase, scheduler: payload.name });
|
||||||
|
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -3,10 +3,11 @@
|
|||||||
<summary
|
<summary
|
||||||
class="accordion-header database-name"
|
class="accordion-header database-name"
|
||||||
:class="{'text-bold': breadcrumbs.schema === database.name}"
|
:class="{'text-bold': breadcrumbs.schema === database.name}"
|
||||||
@click="changeBreadcrumbs({schema: database.name, table: null})"
|
@click="selectSchema(database.name)"
|
||||||
@contextmenu.prevent="showDatabaseContext($event, database.name)"
|
@contextmenu.prevent="showDatabaseContext($event, database.name)"
|
||||||
>
|
>
|
||||||
<i class="icon mdi mdi-18px mdi-chevron-right" />
|
<div v-if="isLoading" class="icon loading" />
|
||||||
|
<i v-else class="icon mdi mdi-18px mdi-chevron-right" />
|
||||||
<i class="database-icon mdi mdi-18px mdi-database mr-1" />
|
<i class="database-icon mdi mdi-18px mdi-database mr-1" />
|
||||||
<span>{{ database.name }}</span>
|
<span>{{ database.name }}</span>
|
||||||
</summary>
|
</summary>
|
||||||
@@ -17,20 +18,136 @@
|
|||||||
v-for="table of database.tables"
|
v-for="table of database.tables"
|
||||||
:key="table.name"
|
:key="table.name"
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.table === table.name}"
|
:class="{'text-bold': breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name)}"
|
||||||
@click="setBreadcrumbs({schema: database.name, table: table.name})"
|
@click="setBreadcrumbs({schema: database.name, [table.type]: table.name})"
|
||||||
@contextmenu.prevent="showTableContext($event, table.name)"
|
@contextmenu.prevent="showTableContext($event, table)"
|
||||||
>
|
>
|
||||||
<a class="table-name">
|
<a class="table-name">
|
||||||
<i class="table-icon mdi mdi-18px mr-1" :class="table.type === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
|
<i class="table-icon mdi mdi-18px mr-1" :class="table.type === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
|
||||||
<span>{{ table.name }}</span>
|
<span>{{ table.name }}</span>
|
||||||
</a>
|
</a>
|
||||||
<div class="table-size tooltip tooltip-left mr-1" :data-tooltip="formatBytes(table.size)">
|
<div
|
||||||
|
v-if="table.type === 'table'"
|
||||||
|
class="table-size tooltip tooltip-left mr-1"
|
||||||
|
:data-tooltip="formatBytes(table.size)"
|
||||||
|
>
|
||||||
<div class="pie" :style="piePercentage(table.size)" />
|
<div class="pie" :style="piePercentage(table.size)" />
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="database.triggers.length" class="database-misc">
|
||||||
|
<details class="accordion">
|
||||||
|
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger}">
|
||||||
|
<i class="misc-icon mdi mdi-18px mdi-folder-cog mr-1" />
|
||||||
|
{{ $tc('word.trigger', 2) }}
|
||||||
|
</summary>
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div>
|
||||||
|
<ul class="menu menu-nav pt-0">
|
||||||
|
<li
|
||||||
|
v-for="trigger of database.triggers"
|
||||||
|
:key="trigger.name"
|
||||||
|
class="menu-item"
|
||||||
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name}"
|
||||||
|
@click="setBreadcrumbs({schema: database.name, trigger: trigger.name})"
|
||||||
|
@contextmenu.prevent="showMiscContext($event, {...trigger, type: 'trigger'})"
|
||||||
|
>
|
||||||
|
<a class="table-name">
|
||||||
|
<i class="table-icon mdi mdi-table-cog mdi-18px mr-1" />
|
||||||
|
<span>{{ trigger.name }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="database.procedures.length" class="database-misc">
|
||||||
|
<details class="accordion">
|
||||||
|
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure}">
|
||||||
|
<i class="misc-icon mdi mdi-18px mdi-folder-sync mr-1" />
|
||||||
|
{{ $tc('word.storedRoutine', 2) }}
|
||||||
|
</summary>
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div>
|
||||||
|
<ul class="menu menu-nav pt-0">
|
||||||
|
<li
|
||||||
|
v-for="procedure of database.procedures"
|
||||||
|
:key="procedure.name"
|
||||||
|
class="menu-item"
|
||||||
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure === procedure.name}"
|
||||||
|
@click="setBreadcrumbs({schema: database.name, procedure: procedure.name})"
|
||||||
|
@contextmenu.prevent="showMiscContext($event, {...procedure, type: 'procedure'})"
|
||||||
|
>
|
||||||
|
<a class="table-name">
|
||||||
|
<i class="table-icon mdi mdi-sync-circle mdi-18px mr-1" />
|
||||||
|
<span>{{ procedure.name }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="database.functions.length" class="database-misc">
|
||||||
|
<details class="accordion">
|
||||||
|
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function}">
|
||||||
|
<i class="misc-icon mdi mdi-18px mdi-folder-move mr-1" />
|
||||||
|
{{ $tc('word.function', 2) }}
|
||||||
|
</summary>
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div>
|
||||||
|
<ul class="menu menu-nav pt-0">
|
||||||
|
<li
|
||||||
|
v-for="func of database.functions"
|
||||||
|
:key="func.name"
|
||||||
|
class="menu-item"
|
||||||
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}"
|
||||||
|
@click="setBreadcrumbs({schema: database.name, function: func.name})"
|
||||||
|
@contextmenu.prevent="showMiscContext($event, {...func, type: 'function'})"
|
||||||
|
>
|
||||||
|
<a class="table-name">
|
||||||
|
<i class="table-icon mdi mdi-arrow-right-bold-box mdi-18px mr-1" />
|
||||||
|
<span>{{ func.name }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="database.schedulers.length" class="database-misc">
|
||||||
|
<details class="accordion">
|
||||||
|
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler}">
|
||||||
|
<i class="misc-icon mdi mdi-18px mdi-folder-clock mr-1" />
|
||||||
|
{{ $tc('word.scheduler', 2) }}
|
||||||
|
</summary>
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div>
|
||||||
|
<ul class="menu menu-nav pt-0">
|
||||||
|
<li
|
||||||
|
v-for="scheduler of database.schedulers"
|
||||||
|
:key="scheduler.name"
|
||||||
|
class="menu-item"
|
||||||
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name}"
|
||||||
|
@click="setBreadcrumbs({schema: database.name, scheduler: scheduler.name})"
|
||||||
|
@contextmenu.prevent="showMiscContext($event, {...scheduler, type: 'scheduler'})"
|
||||||
|
>
|
||||||
|
<a class="table-name">
|
||||||
|
<i class="table-icon mdi mdi-calendar-clock mdi-18px mr-1" />
|
||||||
|
<span>{{ scheduler.name }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</template>
|
</template>
|
||||||
@@ -45,13 +162,22 @@ export default {
|
|||||||
database: Object,
|
database: Object,
|
||||||
connection: Object
|
connection: Object
|
||||||
},
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isLoading: false
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
|
getLoadedSchemas: 'workspaces/getLoadedSchemas',
|
||||||
getWorkspace: 'workspaces/getWorkspace'
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
}),
|
}),
|
||||||
breadcrumbs () {
|
breadcrumbs () {
|
||||||
return this.getWorkspace(this.connection.uid).breadcrumbs;
|
return this.getWorkspace(this.connection.uid).breadcrumbs;
|
||||||
},
|
},
|
||||||
|
loadedSchemas () {
|
||||||
|
return this.getLoadedSchemas(this.connection.uid);
|
||||||
|
},
|
||||||
maxSize () {
|
maxSize () {
|
||||||
return this.database.tables.reduce((acc, curr) => {
|
return this.database.tables.reduce((acc, curr) => {
|
||||||
if (curr.size > acc) acc = curr.size;
|
if (curr.size > acc) acc = curr.size;
|
||||||
@@ -64,15 +190,31 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
|
refreshSchema: 'workspaces/refreshSchema'
|
||||||
}),
|
}),
|
||||||
formatBytes,
|
formatBytes,
|
||||||
|
async selectSchema (schema) {
|
||||||
|
if (!this.loadedSchemas.has(schema)) {
|
||||||
|
this.isLoading = true;
|
||||||
|
await this.refreshSchema({ uid: this.connection.uid, schema });
|
||||||
|
this.isLoading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.changeBreadcrumbs({ schema, table: null });
|
||||||
|
},
|
||||||
showDatabaseContext (event, database) {
|
showDatabaseContext (event, database) {
|
||||||
|
this.changeBreadcrumbs({ schema: database, table: null });
|
||||||
this.$emit('show-database-context', { event, database });
|
this.$emit('show-database-context', { event, database });
|
||||||
},
|
},
|
||||||
showTableContext (event, table) {
|
showTableContext (event, table) {
|
||||||
|
this.setBreadcrumbs({ schema: this.database.name, [table.type]: table.name });
|
||||||
this.$emit('show-table-context', { event, table });
|
this.$emit('show-table-context', { event, table });
|
||||||
},
|
},
|
||||||
|
showMiscContext (event, misc) {
|
||||||
|
this.setBreadcrumbs({ schema: this.database.name, [misc.type]: misc.name });
|
||||||
|
this.$emit('show-misc-context', { event, misc });
|
||||||
|
},
|
||||||
piePercentage (val) {
|
piePercentage (val) {
|
||||||
const perc = val / this.maxSize * 100;
|
const perc = val / this.maxSize * 100;
|
||||||
return { background: `conic-gradient(lime ${perc}%, white 0)` };
|
return { background: `conic-gradient(lime ${perc}%, white 0)` };
|
||||||
@@ -88,6 +230,7 @@ export default {
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.workspace-explorebar-database {
|
.workspace-explorebar-database {
|
||||||
.database-name,
|
.database-name,
|
||||||
|
.misc-name,
|
||||||
a.table-name {
|
a.table-name {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -103,12 +246,30 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.database-icon,
|
.database-icon,
|
||||||
.table-icon {
|
.table-icon,
|
||||||
|
.misc-icon {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
height: 0.6rem;
|
||||||
|
width: 0.6rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.database-name {
|
.misc-name {
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0.1rem 1rem 0.1rem 0.1rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.database-name,
|
||||||
|
.misc-name {
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $body-font-color;
|
color: $body-font-color;
|
||||||
background: rgba($color: #fff, $alpha: 0.05);
|
background: rgba($color: #fff, $alpha: 0.05);
|
||||||
@@ -138,6 +299,18 @@ export default {
|
|||||||
margin-left: 1.2rem;
|
margin-left: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.database-misc {
|
||||||
|
margin-left: 1.6rem;
|
||||||
|
|
||||||
|
.accordion[open] .accordion-header > .misc-icon:first-child::before {
|
||||||
|
content: "\F0770";
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-body {
|
||||||
|
margin-bottom: 0.2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.table-size {
|
.table-size {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@@ -10,6 +10,21 @@
|
|||||||
<div class="context-element" @click="showCreateTableModal">
|
<div class="context-element" @click="showCreateTableModal">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table text-light pr-1" /> {{ $t('word.table') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table text-light pr-1" /> {{ $t('word.table') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="context-element" @click="showCreateViewModal">
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table-eye text-light pr-1" /> {{ $t('word.view') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="context-element" @click="showCreateTriggerModal">
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ $tc('word.trigger', 1) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="context-element" @click="showCreateRoutineModal">
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-sync-circle pr-1" /> {{ $tc('word.storedRoutine', 1) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="context-element" @click="showCreateFunctionModal">
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.function', 1) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="context-element" @click="showCreateSchedulerModal">
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ $tc('word.scheduler', 1) }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element" @click="showEditModal">
|
<div class="context-element" @click="showEditModal">
|
||||||
@@ -84,6 +99,21 @@ export default {
|
|||||||
showCreateTableModal () {
|
showCreateTableModal () {
|
||||||
this.$emit('show-create-table-modal');
|
this.$emit('show-create-table-modal');
|
||||||
},
|
},
|
||||||
|
showCreateViewModal () {
|
||||||
|
this.$emit('show-create-view-modal');
|
||||||
|
},
|
||||||
|
showCreateTriggerModal () {
|
||||||
|
this.$emit('show-create-trigger-modal');
|
||||||
|
},
|
||||||
|
showCreateRoutineModal () {
|
||||||
|
this.$emit('show-create-routine-modal');
|
||||||
|
},
|
||||||
|
showCreateFunctionModal () {
|
||||||
|
this.$emit('show-create-function-modal');
|
||||||
|
},
|
||||||
|
showCreateSchedulerModal () {
|
||||||
|
this.$emit('show-create-scheduler-modal');
|
||||||
|
},
|
||||||
showDeleteModal () {
|
showDeleteModal () {
|
||||||
this.isDeleteModal = true;
|
this.isDeleteModal = true;
|
||||||
},
|
},
|
||||||
@@ -124,3 +154,8 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.context-submenu {
|
||||||
|
min-width: 150px !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
154
src/renderer/components/WorkspaceExploreBarMiscContext.vue
Normal file
154
src/renderer/components/WorkspaceExploreBarMiscContext.vue
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
<template>
|
||||||
|
<BaseContextMenu
|
||||||
|
:context-event="contextEvent"
|
||||||
|
@close-context="closeContext"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="['procedure', 'function'].includes(selectedMisc.type)"
|
||||||
|
class="context-element disabled"
|
||||||
|
@click="showRunModal"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ $t('word.run') }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="context-element" @click="showDeleteModal">
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table-remove text-light pr-1" /> {{ $t('word.delete') }}</span>
|
||||||
|
</div>
|
||||||
|
<ConfirmModal
|
||||||
|
v-if="isDeleteModal"
|
||||||
|
@confirm="deleteMisc"
|
||||||
|
@hide="hideDeleteModal"
|
||||||
|
>
|
||||||
|
<template slot="header">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-delete mr-1" /> {{ deleteMessage }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div slot="body">
|
||||||
|
<div class="mb-2">
|
||||||
|
{{ $t('message.deleteCorfirm') }} "<b>{{ selectedMisc.name }}</b>"?
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</BaseContextMenu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import BaseContextMenu from '@/components/BaseContextMenu';
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
import Triggers from '@/ipc-api/Triggers';
|
||||||
|
import Routines from '@/ipc-api/Routines';
|
||||||
|
import Functions from '@/ipc-api/Functions';
|
||||||
|
import Schedulers from '@/ipc-api/Schedulers';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspaceExploreBarMiscContext',
|
||||||
|
components: {
|
||||||
|
BaseContextMenu,
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
contextEvent: MouseEvent,
|
||||||
|
selectedMisc: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isDeleteModal: false,
|
||||||
|
isRunModal: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.selectedWorkspace);
|
||||||
|
},
|
||||||
|
deleteMessage () {
|
||||||
|
switch (this.selectedMisc.type) {
|
||||||
|
case 'trigger':
|
||||||
|
return this.$t('message.deleteTrigger');
|
||||||
|
case 'procedure':
|
||||||
|
return this.$t('message.deleteRoutine');
|
||||||
|
case 'function':
|
||||||
|
return this.$t('message.deleteFunction');
|
||||||
|
case 'scheduler':
|
||||||
|
return this.$t('message.deleteScheduler');
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||||
|
}),
|
||||||
|
showCreateTableModal () {
|
||||||
|
this.$emit('show-create-table-modal');
|
||||||
|
},
|
||||||
|
showDeleteModal () {
|
||||||
|
this.isDeleteModal = true;
|
||||||
|
},
|
||||||
|
hideDeleteModal () {
|
||||||
|
this.isDeleteModal = false;
|
||||||
|
},
|
||||||
|
showRunModal () {
|
||||||
|
this.isRunModal = true;
|
||||||
|
},
|
||||||
|
hideRunModal () {
|
||||||
|
this.isRunModal = false;
|
||||||
|
},
|
||||||
|
closeContext () {
|
||||||
|
this.$emit('close-context');
|
||||||
|
},
|
||||||
|
async deleteMisc () {
|
||||||
|
try {
|
||||||
|
let res;
|
||||||
|
|
||||||
|
switch (this.selectedMisc.type) {
|
||||||
|
case 'trigger':
|
||||||
|
res = await Triggers.dropTrigger({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
trigger: this.selectedMisc.name
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'procedure':
|
||||||
|
res = await Routines.dropRoutine({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
routine: this.selectedMisc.name
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'function':
|
||||||
|
res = await Functions.dropFunction({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
func: this.selectedMisc.name
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'scheduler':
|
||||||
|
res = await Schedulers.dropScheduler({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
scheduler: this.selectedMisc.name
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { status, response } = res;
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
this.changeBreadcrumbs({ [this.selectedMisc.type]: null });
|
||||||
|
|
||||||
|
this.closeContext();
|
||||||
|
this.$emit('reload');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@@ -3,7 +3,11 @@
|
|||||||
:context-event="contextEvent"
|
:context-event="contextEvent"
|
||||||
@close-context="closeContext"
|
@close-context="closeContext"
|
||||||
>
|
>
|
||||||
<div class="context-element" @click="showEmptyModal">
|
<div
|
||||||
|
v-if="selectedTable.type === 'table'"
|
||||||
|
class="context-element"
|
||||||
|
@click="showEmptyModal"
|
||||||
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table-off text-light pr-1" /> {{ $t('message.emptyTable') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table-off text-light pr-1" /> {{ $t('message.emptyTable') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element" @click="showDeleteModal">
|
<div class="context-element" @click="showDeleteModal">
|
||||||
@@ -22,7 +26,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<div slot="body">
|
<div slot="body">
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
{{ $t('message.emptyCorfirm') }} "<b>{{ selectedTable }}</b>"?
|
{{ $t('message.emptyCorfirm') }} "<b>{{ selectedTable.name }}</b>"?
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
@@ -33,12 +37,12 @@
|
|||||||
>
|
>
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-table-remove mr-1" /> {{ $t('message.deleteTable') }}
|
<i class="mdi mdi-24px mdi-table-remove mr-1" /> {{ selectedTable.type === 'table' ? $t('message.deleteTable') : $t('message.deleteView') }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div slot="body">
|
<div slot="body">
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
{{ $t('message.deleteCorfirm') }} "<b>{{ selectedTable }}</b>"?
|
{{ $t('message.deleteCorfirm') }} "<b>{{ selectedTable.name }}</b>"?
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
@@ -50,6 +54,7 @@ import { mapGetters, mapActions } from 'vuex';
|
|||||||
import BaseContextMenu from '@/components/BaseContextMenu';
|
import BaseContextMenu from '@/components/BaseContextMenu';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
import Tables from '@/ipc-api/Tables';
|
import Tables from '@/ipc-api/Tables';
|
||||||
|
import Views from '@/ipc-api/Views';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WorkspaceExploreBarTableContext',
|
name: 'WorkspaceExploreBarTableContext',
|
||||||
@@ -59,7 +64,7 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
contextEvent: MouseEvent,
|
contextEvent: MouseEvent,
|
||||||
selectedTable: String
|
selectedTable: Object
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -103,11 +108,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
|
table: this.selectedTable.name
|
||||||
});
|
});
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
if (this.selectedTable === this.workspace.breadcrumbs.table)
|
if (this.selectedTable.name === this.workspace.breadcrumbs.table)
|
||||||
this.changeBreadcrumbs({ table: null });
|
this.changeBreadcrumbs({ table: null });
|
||||||
|
|
||||||
this.closeContext();
|
this.closeContext();
|
||||||
@@ -122,14 +127,26 @@ export default {
|
|||||||
},
|
},
|
||||||
async deleteTable () {
|
async deleteTable () {
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Tables.dropTable({
|
let res;
|
||||||
uid: this.selectedWorkspace,
|
|
||||||
table: this.selectedTable
|
if (this.selectedTable.type === 'table') {
|
||||||
});
|
res = await Tables.dropTable({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
table: this.selectedTable.name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (this.selectedTable.type === 'view') {
|
||||||
|
res = await Views.dropView({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
view: this.selectedTable.name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { status, response } = res;
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
if (this.selectedTable === this.workspace.breadcrumbs.table)
|
if (this.selectedTable.name === this.workspace.breadcrumbs.table || this.selectedTable.name === this.workspace.breadcrumbs.view)
|
||||||
this.changeBreadcrumbs({ table: null });
|
this.changeBreadcrumbs({ table: null, view: null });
|
||||||
|
|
||||||
this.closeContext();
|
this.closeContext();
|
||||||
this.$emit('reload');
|
this.$emit('reload');
|
||||||
|
180
src/renderer/components/WorkspacePropsFunctionOptionsModal.vue
Normal file
180
src/renderer/components/WorkspacePropsFunctionOptionsModal.vue
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.confirm')"
|
||||||
|
size="400"
|
||||||
|
@confirm="confirmOptionsChange"
|
||||||
|
@hide="$emit('hide')"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-cogs mr-1" /> {{ $t('word.options') }} "{{ localOptions.name }}"
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.name') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="optionsProxy.name"
|
||||||
|
class="form-input"
|
||||||
|
:class="{'is-error': !isTableNameValid}"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.definer') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select
|
||||||
|
v-if="workspace.users.length"
|
||||||
|
v-model="optionsProxy.definer"
|
||||||
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="user in workspace.users"
|
||||||
|
:key="`${user.name}@${user.host}`"
|
||||||
|
:value="`\`${user.name}\`@\`${user.host}\``"
|
||||||
|
>
|
||||||
|
{{ user.name }}@{{ user.host }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<select v-if="!workspace.users.length" class="form-select">
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.returns') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<div class="input-group">
|
||||||
|
<select
|
||||||
|
v-model="optionsProxy.returns"
|
||||||
|
class="form-select text-uppercase"
|
||||||
|
style="width: 0;"
|
||||||
|
>
|
||||||
|
<optgroup
|
||||||
|
v-for="group in workspace.dataTypes"
|
||||||
|
:key="group.group"
|
||||||
|
:label="group.group"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="type in group.types"
|
||||||
|
:key="type.name"
|
||||||
|
:selected="optionsProxy.returns === type.name"
|
||||||
|
:value="type.name"
|
||||||
|
>
|
||||||
|
{{ type.name }}
|
||||||
|
</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
<input
|
||||||
|
v-model="optionsProxy.returnsLength"
|
||||||
|
class="form-input"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.comment') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
v-model="optionsProxy.comment"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('message.sqlSecurity') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="optionsProxy.security" class="form-select">
|
||||||
|
<option>DEFINER</option>
|
||||||
|
<option>INVOKER</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('message.dataAccess') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="optionsProxy.dataAccess" class="form-select">
|
||||||
|
<option>CONTAINS SQL</option>
|
||||||
|
<option>NO SQL</option>
|
||||||
|
<option>READS SQL DATA</option>
|
||||||
|
<option>MODIFIES SQL DATA</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4" />
|
||||||
|
<div class="column">
|
||||||
|
<label class="form-checkbox form-inline">
|
||||||
|
<input v-model="optionsProxy.deterministic" type="checkbox"><i class="form-icon" /> {{ $t('word.deterministic') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsFunctionOptionsModal',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
localOptions: Object,
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
optionsProxy: {},
|
||||||
|
isOptionsChanging: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isTableNameValid () {
|
||||||
|
return this.optionsProxy.name !== '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.optionsProxy = JSON.parse(JSON.stringify(this.localOptions));
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmOptionsChange () {
|
||||||
|
if (!this.isTableNameValid)
|
||||||
|
this.optionsProxy.name = this.localOptions.name;
|
||||||
|
|
||||||
|
this.$emit('options-update', this.optionsProxy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
270
src/renderer/components/WorkspacePropsFunctionParamsModal.vue
Normal file
270
src/renderer/components/WorkspacePropsFunctionParamsModal.vue
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.confirm')"
|
||||||
|
size="medium"
|
||||||
|
@confirm="confirmIndexesChange"
|
||||||
|
@hide="$emit('hide')"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" /> {{ $t('word.parameters') }} "{{ func }}"
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<div class="columns col-gapless">
|
||||||
|
<div class="column col-5">
|
||||||
|
<div class="panel" :style="{ height: modalInnerHeight + 'px'}">
|
||||||
|
<div class="panel-header pt-0 pl-0">
|
||||||
|
<div class="d-flex">
|
||||||
|
<button class="btn btn-dark btn-sm d-flex" @click="addParameter">
|
||||||
|
<span>{{ $t('word.add') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-plus ml-1" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
||||||
|
:title="$t('message.clearChanges')"
|
||||||
|
:disabled="!isChanged"
|
||||||
|
@click.prevent="clearChanges"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.clear') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ref="parametersPanel" class="panel-body p-0 pr-1">
|
||||||
|
<div
|
||||||
|
v-for="param in parametersProxy"
|
||||||
|
:key="param.name"
|
||||||
|
class="tile tile-centered c-hand mb-1 p-1"
|
||||||
|
:class="{'selected-param': selectedParam === param.name}"
|
||||||
|
@click="selectParameter($event, param.name)"
|
||||||
|
>
|
||||||
|
<div class="tile-icon">
|
||||||
|
<div>
|
||||||
|
<i class="mdi mdi-hexagon mdi-24px" :class="`type-${param.type.toLowerCase()}`" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tile-content">
|
||||||
|
<div class="tile-title">
|
||||||
|
{{ param.name }}
|
||||||
|
</div>
|
||||||
|
<small class="tile-subtitle text-gray">{{ param.type }}{{ param.length ? `(${param.length})` : '' }}</small>
|
||||||
|
</div>
|
||||||
|
<div class="tile-action">
|
||||||
|
<button
|
||||||
|
class="btn btn-link remove-field p-0 mr-2"
|
||||||
|
:title="$t('word.delete')"
|
||||||
|
@click.prevent="removeParameter(param.name)"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-close" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column col-7 pl-2 editor-col">
|
||||||
|
<form
|
||||||
|
v-if="selectedParamObj"
|
||||||
|
:style="{ height: modalInnerHeight + 'px'}"
|
||||||
|
class="form-horizontal"
|
||||||
|
>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-3">
|
||||||
|
{{ $t('word.name') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
v-model="selectedParamObj.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-3">
|
||||||
|
{{ $t('word.type') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="selectedParamObj.type" class="form-select text-uppercase">
|
||||||
|
<optgroup
|
||||||
|
v-for="group in workspace.dataTypes"
|
||||||
|
:key="group.group"
|
||||||
|
:label="group.group"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="type in group.types"
|
||||||
|
:key="type.name"
|
||||||
|
:selected="selectedParamObj.type.toUpperCase() === type.name"
|
||||||
|
:value="type.name"
|
||||||
|
>
|
||||||
|
{{ type.name }}
|
||||||
|
</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-3">
|
||||||
|
{{ $t('word.length') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
v-model="selectedParamObj.length"
|
||||||
|
class="form-input"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div v-if="!parametersProxy.length" class="empty">
|
||||||
|
<div class="empty-icon">
|
||||||
|
<i class="mdi mdi-dots-horizontal mdi-48px" />
|
||||||
|
</div>
|
||||||
|
<p class="empty-title h5">
|
||||||
|
{{ $t('message.thereAreNoParameters') }}
|
||||||
|
</p>
|
||||||
|
<div class="empty-action">
|
||||||
|
<button class="btn btn-primary" @click="addParameter">
|
||||||
|
{{ $t('message.createNewParameter') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsRoutineParamsModal',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
localParameters: Array,
|
||||||
|
func: String,
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
parametersProxy: [],
|
||||||
|
isOptionsChanging: false,
|
||||||
|
selectedParam: '',
|
||||||
|
modalInnerHeight: 400,
|
||||||
|
i: 1
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selectedParamObj () {
|
||||||
|
return this.parametersProxy.find(param => param.name === this.selectedParam);
|
||||||
|
},
|
||||||
|
isChanged () {
|
||||||
|
return JSON.stringify(this.localParameters) !== JSON.stringify(this.parametersProxy);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.parametersProxy = JSON.parse(JSON.stringify(this.localParameters));
|
||||||
|
this.i = this.parametersProxy.length + 1;
|
||||||
|
|
||||||
|
if (this.parametersProxy.length)
|
||||||
|
this.resetSelectedID();
|
||||||
|
|
||||||
|
this.getModalInnerHeight();
|
||||||
|
window.addEventListener('resize', this.getModalInnerHeight);
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
window.removeEventListener('resize', this.getModalInnerHeight);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmIndexesChange () {
|
||||||
|
this.$emit('parameters-update', this.parametersProxy);
|
||||||
|
},
|
||||||
|
selectParameter (event, name) {
|
||||||
|
if (this.selectedParam !== name && !event.target.classList.contains('remove-field'))
|
||||||
|
this.selectedParam = name;
|
||||||
|
},
|
||||||
|
getModalInnerHeight () {
|
||||||
|
const modalBody = document.querySelector('.modal-body');
|
||||||
|
if (modalBody)
|
||||||
|
this.modalInnerHeight = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom));
|
||||||
|
},
|
||||||
|
addParameter () {
|
||||||
|
this.parametersProxy = [...this.parametersProxy, {
|
||||||
|
name: `Param${this.i++}`,
|
||||||
|
type: 'INT',
|
||||||
|
context: 'IN',
|
||||||
|
length: 10
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (this.parametersProxy.length === 1)
|
||||||
|
this.resetSelectedID();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.parametersPanel.scrollTop = this.$refs.parametersPanel.scrollHeight + 60;
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
removeParameter (name) {
|
||||||
|
this.parametersProxy = this.parametersProxy.filter(param => param.name !== name);
|
||||||
|
|
||||||
|
if (this.selectedParam === name && this.parametersProxy.length)
|
||||||
|
this.resetSelectedID();
|
||||||
|
},
|
||||||
|
clearChanges () {
|
||||||
|
this.parametersProxy = JSON.parse(JSON.stringify(this.localParameters));
|
||||||
|
this.i = this.parametersProxy.length + 1;
|
||||||
|
|
||||||
|
if (!this.parametersProxy.some(param => param.name === this.selectedParam))
|
||||||
|
this.resetSelectedID();
|
||||||
|
},
|
||||||
|
resetSelectedID () {
|
||||||
|
this.selectedParam = this.parametersProxy.length ? this.parametersProxy[0].name : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tile {
|
||||||
|
border-radius: 2px;
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: background 0.2s;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
|
||||||
|
.tile-action {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $bg-color-light;
|
||||||
|
|
||||||
|
.tile-action {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected-param {
|
||||||
|
background: $bg-color-light;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-col {
|
||||||
|
border-left: 2px solid $bg-color-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fields-list {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-field .mdi {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
</style>
|
145
src/renderer/components/WorkspacePropsRoutineOptionsModal.vue
Normal file
145
src/renderer/components/WorkspacePropsRoutineOptionsModal.vue
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.confirm')"
|
||||||
|
size="400"
|
||||||
|
@confirm="confirmOptionsChange"
|
||||||
|
@hide="$emit('hide')"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-cogs mr-1" /> {{ $t('word.options') }} "{{ localOptions.name }}"
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.name') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="optionsProxy.name"
|
||||||
|
class="form-input"
|
||||||
|
:class="{'is-error': !isTableNameValid}"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.definer') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select
|
||||||
|
v-if="workspace.users.length"
|
||||||
|
v-model="optionsProxy.definer"
|
||||||
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="user in workspace.users"
|
||||||
|
:key="`${user.name}@${user.host}`"
|
||||||
|
:value="`\`${user.name}\`@\`${user.host}\``"
|
||||||
|
>
|
||||||
|
{{ user.name }}@{{ user.host }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<select v-if="!workspace.users.length" class="form-select">
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.comment') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
v-model="optionsProxy.comment"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('message.sqlSecurity') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="optionsProxy.security" class="form-select">
|
||||||
|
<option>DEFINER</option>
|
||||||
|
<option>INVOKER</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('message.dataAccess') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="optionsProxy.dataAccess" class="form-select">
|
||||||
|
<option>CONTAINS SQL</option>
|
||||||
|
<option>NO SQL</option>
|
||||||
|
<option>READS SQL DATA</option>
|
||||||
|
<option>MODIFIES SQL DATA</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4" />
|
||||||
|
<div class="column">
|
||||||
|
<label class="form-checkbox form-inline">
|
||||||
|
<input v-model="optionsProxy.deterministic" type="checkbox"><i class="form-icon" /> {{ $t('word.deterministic') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsRoutineOptionsModal',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
localOptions: Object,
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
optionsProxy: {},
|
||||||
|
isOptionsChanging: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isTableNameValid () {
|
||||||
|
return this.optionsProxy.name !== '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.optionsProxy = JSON.parse(JSON.stringify(this.localOptions));
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmOptionsChange () {
|
||||||
|
if (!this.isTableNameValid)
|
||||||
|
this.optionsProxy.name = this.localOptions.name;
|
||||||
|
|
||||||
|
this.$emit('options-update', this.optionsProxy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
301
src/renderer/components/WorkspacePropsRoutineParamsModal.vue
Normal file
301
src/renderer/components/WorkspacePropsRoutineParamsModal.vue
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.confirm')"
|
||||||
|
size="medium"
|
||||||
|
@confirm="confirmIndexesChange"
|
||||||
|
@hide="$emit('hide')"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" /> {{ $t('word.parameters') }} "{{ routine }}"
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<div class="columns col-gapless">
|
||||||
|
<div class="column col-5">
|
||||||
|
<div class="panel" :style="{ height: modalInnerHeight + 'px'}">
|
||||||
|
<div class="panel-header pt-0 pl-0">
|
||||||
|
<div class="d-flex">
|
||||||
|
<button class="btn btn-dark btn-sm d-flex" @click="addParameter">
|
||||||
|
<span>{{ $t('word.add') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-plus ml-1" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
||||||
|
:title="$t('message.clearChanges')"
|
||||||
|
:disabled="!isChanged"
|
||||||
|
@click.prevent="clearChanges"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.clear') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ref="parametersPanel" class="panel-body p-0 pr-1">
|
||||||
|
<div
|
||||||
|
v-for="param in parametersProxy"
|
||||||
|
:key="param.name"
|
||||||
|
class="tile tile-centered c-hand mb-1 p-1"
|
||||||
|
:class="{'selected-param': selectedParam === param.name}"
|
||||||
|
@click="selectParameter($event, param.name)"
|
||||||
|
>
|
||||||
|
<div class="tile-icon">
|
||||||
|
<div>
|
||||||
|
<i class="mdi mdi-hexagon mdi-24px" :class="`type-${param.type.toLowerCase()}`" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tile-content">
|
||||||
|
<div class="tile-title">
|
||||||
|
{{ param.name }}
|
||||||
|
</div>
|
||||||
|
<small class="tile-subtitle text-gray">{{ param.type }}{{ param.length ? `(${param.length})` : '' }} · {{ param.context }}</small>
|
||||||
|
</div>
|
||||||
|
<div class="tile-action">
|
||||||
|
<button
|
||||||
|
class="btn btn-link remove-field p-0 mr-2"
|
||||||
|
:title="$t('word.delete')"
|
||||||
|
@click.prevent="removeParameter(param.name)"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-close" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column col-7 pl-2 editor-col">
|
||||||
|
<form
|
||||||
|
v-if="selectedParamObj"
|
||||||
|
:style="{ height: modalInnerHeight + 'px'}"
|
||||||
|
class="form-horizontal"
|
||||||
|
>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-3">
|
||||||
|
{{ $t('word.name') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
v-model="selectedParamObj.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-3">
|
||||||
|
{{ $t('word.type') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="selectedParamObj.type" class="form-select text-uppercase">
|
||||||
|
<optgroup
|
||||||
|
v-for="group in workspace.dataTypes"
|
||||||
|
:key="group.group"
|
||||||
|
:label="group.group"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="type in group.types"
|
||||||
|
:key="type.name"
|
||||||
|
:selected="selectedParamObj.type.toUpperCase() === type.name"
|
||||||
|
:value="type.name"
|
||||||
|
>
|
||||||
|
{{ type.name }}
|
||||||
|
</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-3">
|
||||||
|
{{ $t('word.length') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
v-model="selectedParamObj.length"
|
||||||
|
class="form-input"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-3">
|
||||||
|
{{ $t('word.context') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="selectedParamObj.context"
|
||||||
|
type="radio"
|
||||||
|
name="context"
|
||||||
|
value="IN"
|
||||||
|
> <i class="form-icon" /> IN
|
||||||
|
</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="selectedParamObj.context"
|
||||||
|
type="radio"
|
||||||
|
value="OUT"
|
||||||
|
name="context"
|
||||||
|
> <i class="form-icon" /> OUT
|
||||||
|
</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="selectedParamObj.context"
|
||||||
|
type="radio"
|
||||||
|
value="INOUT"
|
||||||
|
name="context"
|
||||||
|
> <i class="form-icon" /> INOUT
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div v-if="!parametersProxy.length" class="empty">
|
||||||
|
<div class="empty-icon">
|
||||||
|
<i class="mdi mdi-dots-horizontal mdi-48px" />
|
||||||
|
</div>
|
||||||
|
<p class="empty-title h5">
|
||||||
|
{{ $t('message.thereAreNoParameters') }}
|
||||||
|
</p>
|
||||||
|
<div class="empty-action">
|
||||||
|
<button class="btn btn-primary" @click="addParameter">
|
||||||
|
{{ $t('message.createNewParameter') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsRoutineParamsModal',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
localParameters: Array,
|
||||||
|
routine: String,
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
parametersProxy: [],
|
||||||
|
isOptionsChanging: false,
|
||||||
|
selectedParam: '',
|
||||||
|
modalInnerHeight: 400,
|
||||||
|
i: 1
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selectedParamObj () {
|
||||||
|
return this.parametersProxy.find(param => param.name === this.selectedParam);
|
||||||
|
},
|
||||||
|
isChanged () {
|
||||||
|
return JSON.stringify(this.localParameters) !== JSON.stringify(this.parametersProxy);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.parametersProxy = JSON.parse(JSON.stringify(this.localParameters));
|
||||||
|
this.i = this.parametersProxy.length + 1;
|
||||||
|
|
||||||
|
if (this.parametersProxy.length)
|
||||||
|
this.resetSelectedID();
|
||||||
|
|
||||||
|
this.getModalInnerHeight();
|
||||||
|
window.addEventListener('resize', this.getModalInnerHeight);
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
window.removeEventListener('resize', this.getModalInnerHeight);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmIndexesChange () {
|
||||||
|
this.$emit('parameters-update', this.parametersProxy);
|
||||||
|
},
|
||||||
|
selectParameter (event, name) {
|
||||||
|
if (this.selectedParam !== name && !event.target.classList.contains('remove-field'))
|
||||||
|
this.selectedParam = name;
|
||||||
|
},
|
||||||
|
getModalInnerHeight () {
|
||||||
|
const modalBody = document.querySelector('.modal-body');
|
||||||
|
if (modalBody)
|
||||||
|
this.modalInnerHeight = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom));
|
||||||
|
},
|
||||||
|
addParameter () {
|
||||||
|
this.parametersProxy = [...this.parametersProxy, {
|
||||||
|
name: `Param${this.i++}`,
|
||||||
|
type: 'INT',
|
||||||
|
context: 'IN',
|
||||||
|
length: 10
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (this.parametersProxy.length === 1)
|
||||||
|
this.resetSelectedID();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.parametersPanel.scrollTop = this.$refs.parametersPanel.scrollHeight + 60;
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
removeParameter (name) {
|
||||||
|
this.parametersProxy = this.parametersProxy.filter(param => param.name !== name);
|
||||||
|
|
||||||
|
if (this.selectedParam === name && this.parametersProxy.length)
|
||||||
|
this.resetSelectedID();
|
||||||
|
},
|
||||||
|
clearChanges () {
|
||||||
|
this.parametersProxy = JSON.parse(JSON.stringify(this.localParameters));
|
||||||
|
this.i = this.parametersProxy.length + 1;
|
||||||
|
|
||||||
|
if (!this.parametersProxy.some(param => param.name === this.selectedParam))
|
||||||
|
this.resetSelectedID();
|
||||||
|
},
|
||||||
|
resetSelectedID () {
|
||||||
|
this.selectedParam = this.parametersProxy.length ? this.parametersProxy[0].name : '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tile {
|
||||||
|
border-radius: 2px;
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: background 0.2s;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
|
||||||
|
.tile-action {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $bg-color-light;
|
||||||
|
|
||||||
|
.tile-action {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected-param {
|
||||||
|
background: $bg-color-light;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-col {
|
||||||
|
border-left: 2px solid $bg-color-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fields-list {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-field .mdi {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
</style>
|
192
src/renderer/components/WorkspacePropsSchedulerTimingModal.vue
Normal file
192
src/renderer/components/WorkspacePropsSchedulerTimingModal.vue
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.confirm')"
|
||||||
|
size="400"
|
||||||
|
@confirm="confirmOptionsChange"
|
||||||
|
@hide="$emit('hide')"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-timer mr-1" /> {{ $t('word.timing') }} "{{ localOptions.name }}"
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.execution') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="optionsProxy.execution"
|
||||||
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option>EVERY</option>
|
||||||
|
<option>ONCE</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="optionsProxy.execution === 'EVERY'">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4" />
|
||||||
|
<div class="column">
|
||||||
|
<div class="input-group">
|
||||||
|
<input
|
||||||
|
v-model="optionsProxy.every[0]"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
@keypress="isNumberOrMinus($event)"
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
v-model="optionsProxy.every[1]"
|
||||||
|
class="form-select text-uppercase"
|
||||||
|
style="width: 0;"
|
||||||
|
>
|
||||||
|
<option>YEAR</option>
|
||||||
|
<option>QUARTER</option>
|
||||||
|
<option>MONTH</option>
|
||||||
|
<option>WEEK</option>
|
||||||
|
<option>DAY</option>
|
||||||
|
<option>HOUR</option>
|
||||||
|
<option>MINUTE</option>
|
||||||
|
<option>SECOND</option>
|
||||||
|
<option>YEAR_MONTH</option>
|
||||||
|
<option>DAY_HOUR</option>
|
||||||
|
<option>DAY_MINUTE</option>
|
||||||
|
<option>DAY_SECOND</option>
|
||||||
|
<option>HOUR_MINUTE</option>
|
||||||
|
<option>HOUR_SECOND</option>
|
||||||
|
<option>MINUTE_SECOND</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.starts') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<div class="input-group">
|
||||||
|
<label class="form-checkbox">
|
||||||
|
<input v-model="hasStart" type="checkbox"><i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
v-model="optionsProxy.starts"
|
||||||
|
v-mask="'####-##-## ##:##:##'"
|
||||||
|
type="text"
|
||||||
|
class="form-input"
|
||||||
|
>
|
||||||
|
<span class="input-group-addon p-vcentered">
|
||||||
|
<i class="form-icon mdi mdi-calendar" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.ends') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<div class="input-group">
|
||||||
|
<label class="form-checkbox">
|
||||||
|
<input v-model="hasEnd" type="checkbox"><i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
v-model="optionsProxy.ends"
|
||||||
|
v-mask="'####-##-## ##:##:##'"
|
||||||
|
type="text"
|
||||||
|
class="form-input"
|
||||||
|
>
|
||||||
|
<span class="input-group-addon p-vcentered">
|
||||||
|
<i class="form-icon mdi mdi-calendar" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4" />
|
||||||
|
<div class="column">
|
||||||
|
<div class="input-group">
|
||||||
|
<input
|
||||||
|
v-model="optionsProxy.at"
|
||||||
|
v-mask="'####-##-## ##:##:##'"
|
||||||
|
type="text"
|
||||||
|
class="form-input"
|
||||||
|
>
|
||||||
|
<span class="input-group-addon p-vcentered">
|
||||||
|
<i class="form-icon mdi mdi-calendar" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4" />
|
||||||
|
<div class="column">
|
||||||
|
<label class="form-checkbox form-inline mt-2">
|
||||||
|
<input v-model="optionsProxy.preserve" type="checkbox"><i class="form-icon" /> {{ $t('message.preserveOnCompletion') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
import { mask } from 'vue-the-mask';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsSchedulerTimingModal',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
mask
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
localOptions: Object,
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
optionsProxy: {},
|
||||||
|
isOptionsChanging: false,
|
||||||
|
hasStart: false,
|
||||||
|
hasEnd: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.optionsProxy = JSON.parse(JSON.stringify(this.localOptions));
|
||||||
|
|
||||||
|
this.hasStart = !!this.optionsProxy.starts;
|
||||||
|
this.hasEnd = !!this.optionsProxy.ends;
|
||||||
|
|
||||||
|
if (!this.optionsProxy.at) this.optionsProxy.at = moment().format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
if (!this.optionsProxy.starts) this.optionsProxy.starts = moment().format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
if (!this.optionsProxy.ends) this.optionsProxy.ends = moment().format('YYYY-MM-DD HH:mm:ss');
|
||||||
|
if (!this.optionsProxy.every.length) this.optionsProxy.every = ['1', 'DAY'];
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmOptionsChange () {
|
||||||
|
if (!this.hasStart) this.optionsProxy.starts = '';
|
||||||
|
if (!this.hasEnd) this.optionsProxy.ends = '';
|
||||||
|
|
||||||
|
this.$emit('options-update', this.optionsProxy);
|
||||||
|
},
|
||||||
|
isNumberOrMinus (event) {
|
||||||
|
if (!/\d/.test(event.key) && event.key !== '-')
|
||||||
|
return event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@@ -51,7 +51,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-results column col-12">
|
<div class="workspace-query-results column col-12 p-relative">
|
||||||
|
<BaseLoader v-if="isLoading" />
|
||||||
<WorkspacePropsTable
|
<WorkspacePropsTable
|
||||||
v-if="localFields"
|
v-if="localFields"
|
||||||
ref="indexTable"
|
ref="indexTable"
|
||||||
@@ -106,6 +107,7 @@
|
|||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import Tables from '@/ipc-api/Tables';
|
import Tables from '@/ipc-api/Tables';
|
||||||
|
import BaseLoader from '@/components/BaseLoader';
|
||||||
import WorkspacePropsTable from '@/components/WorkspacePropsTable';
|
import WorkspacePropsTable from '@/components/WorkspacePropsTable';
|
||||||
import WorkspacePropsOptionsModal from '@/components/WorkspacePropsOptionsModal';
|
import WorkspacePropsOptionsModal from '@/components/WorkspacePropsOptionsModal';
|
||||||
import WorkspacePropsIndexesModal from '@/components/WorkspacePropsIndexesModal';
|
import WorkspacePropsIndexesModal from '@/components/WorkspacePropsIndexesModal';
|
||||||
@@ -114,6 +116,7 @@ import WorkspacePropsForeignModal from '@/components/WorkspacePropsForeignModal'
|
|||||||
export default {
|
export default {
|
||||||
name: 'WorkspacePropsTab',
|
name: 'WorkspacePropsTab',
|
||||||
components: {
|
components: {
|
||||||
|
BaseLoader,
|
||||||
WorkspacePropsTable,
|
WorkspacePropsTable,
|
||||||
WorkspacePropsOptionsModal,
|
WorkspacePropsOptionsModal,
|
||||||
WorkspacePropsIndexesModal,
|
WorkspacePropsIndexesModal,
|
||||||
@@ -126,7 +129,7 @@ export default {
|
|||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
tabUid: 'prop',
|
tabUid: 'prop',
|
||||||
isQuering: false,
|
isLoading: false,
|
||||||
isSaving: false,
|
isSaving: false,
|
||||||
isOptionsModal: false,
|
isOptionsModal: false,
|
||||||
isIndexesModal: false,
|
isIndexesModal: false,
|
||||||
@@ -200,12 +203,15 @@ export default {
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
refreshStructure: 'workspaces/refreshStructure',
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
setUnsavedChanges: 'workspaces/setUnsavedChanges'
|
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||||
}),
|
}),
|
||||||
async getFieldsData () {
|
async getFieldsData () {
|
||||||
if (!this.table) return;
|
if (!this.table) return;
|
||||||
|
|
||||||
|
this.localFields = [];
|
||||||
this.newFieldsCounter = 0;
|
this.newFieldsCounter = 0;
|
||||||
this.isQuering = true;
|
this.isLoading = true;
|
||||||
this.localOptions = JSON.parse(JSON.stringify(this.tableOptions));
|
this.localOptions = JSON.parse(JSON.stringify(this.tableOptions));
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
@@ -280,7 +286,7 @@ export default {
|
|||||||
this.addNotification({ status: 'error', message: err.stack });
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isQuering = false;
|
this.isLoading = false;
|
||||||
},
|
},
|
||||||
async saveChanges () {
|
async saveChanges () {
|
||||||
if (this.isSaving) return;
|
if (this.isSaving) return;
|
||||||
@@ -394,7 +400,15 @@ export default {
|
|||||||
const { status, response } = await Tables.alterTable(params);
|
const { status, response } = await Tables.alterTable(params);
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
|
const oldName = this.tableOptions.name;
|
||||||
|
|
||||||
await this.refreshStructure(this.connection.uid);
|
await this.refreshStructure(this.connection.uid);
|
||||||
|
|
||||||
|
if (oldName !== this.localOptions.name) {
|
||||||
|
this.setUnsavedChanges(false);
|
||||||
|
this.changeBreadcrumbs({ schema: this.schema, table: this.localOptions.name });
|
||||||
|
}
|
||||||
|
|
||||||
this.getFieldsData();
|
this.getFieldsData();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
267
src/renderer/components/WorkspacePropsTabFunction.vue
Normal file
267
src/renderer/components/WorkspacePropsTabFunction.vue
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
<template>
|
||||||
|
<div class="workspace-query-tab column col-12 columns col-gapless">
|
||||||
|
<div class="workspace-query-runner column col-12">
|
||||||
|
<div class="workspace-query-runner-footer">
|
||||||
|
<div class="workspace-query-buttons">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary btn-sm"
|
||||||
|
:disabled="!isChanged"
|
||||||
|
:class="{'loading':isSaving}"
|
||||||
|
@click="saveChanges"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.save') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save ml-1" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:disabled="!isChanged"
|
||||||
|
class="btn btn-link btn-sm mr-0"
|
||||||
|
:title="$t('message.clearChanges')"
|
||||||
|
@click="clearChanges"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.clear') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="divider-vert py-3" />
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn-dark btn-sm disabled"
|
||||||
|
:disabled="isChanged"
|
||||||
|
@click="false"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.run') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-play ml-1" />
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-dark btn-sm" @click="showParamsModal">
|
||||||
|
<span>{{ $t('word.parameters') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-dots-horizontal ml-1" />
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-dark btn-sm" @click="showOptionsModal">
|
||||||
|
<span>{{ $t('word.options') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-cogs ml-1" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||||
|
<BaseLoader v-if="isLoading" />
|
||||||
|
<label class="form-label ml-2">{{ $t('message.functionBody') }}</label>
|
||||||
|
<QueryEditor
|
||||||
|
v-if="isSelected"
|
||||||
|
ref="queryEditor"
|
||||||
|
:value.sync="localFunction.sql"
|
||||||
|
:workspace="workspace"
|
||||||
|
:schema="schema"
|
||||||
|
:height="editorHeight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<WorkspacePropsFunctionOptionsModal
|
||||||
|
v-if="isOptionsModal"
|
||||||
|
:local-options="localFunction"
|
||||||
|
:workspace="workspace"
|
||||||
|
@hide="hideOptionsModal"
|
||||||
|
@options-update="optionsUpdate"
|
||||||
|
/>
|
||||||
|
<WorkspacePropsFunctionParamsModal
|
||||||
|
v-if="isParamsModal"
|
||||||
|
:local-parameters="localFunction.parameters"
|
||||||
|
:workspace="workspace"
|
||||||
|
:func="localFunction.name"
|
||||||
|
@hide="hideParamsModal"
|
||||||
|
@parameters-update="parametersUpdate"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import BaseLoader from '@/components/BaseLoader';
|
||||||
|
import QueryEditor from '@/components/QueryEditor';
|
||||||
|
import WorkspacePropsFunctionOptionsModal from '@/components/WorkspacePropsFunctionOptionsModal';
|
||||||
|
import WorkspacePropsFunctionParamsModal from '@/components/WorkspacePropsFunctionParamsModal';
|
||||||
|
import Functions from '@/ipc-api/Functions';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsTabFunction',
|
||||||
|
components: {
|
||||||
|
BaseLoader,
|
||||||
|
QueryEditor,
|
||||||
|
WorkspacePropsFunctionOptionsModal,
|
||||||
|
WorkspacePropsFunctionParamsModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
connection: Object,
|
||||||
|
function: String
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
tabUid: 'prop',
|
||||||
|
isLoading: false,
|
||||||
|
isSaving: false,
|
||||||
|
isOptionsModal: false,
|
||||||
|
isParamsModal: false,
|
||||||
|
originalFunction: null,
|
||||||
|
localFunction: { sql: '' },
|
||||||
|
lastFunction: null,
|
||||||
|
sqlProxy: '',
|
||||||
|
editorHeight: 300
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.connection.uid);
|
||||||
|
},
|
||||||
|
isSelected () {
|
||||||
|
return this.workspace.selected_tab === 'prop';
|
||||||
|
},
|
||||||
|
schema () {
|
||||||
|
return this.workspace.breadcrumbs.schema;
|
||||||
|
},
|
||||||
|
isChanged () {
|
||||||
|
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
|
||||||
|
},
|
||||||
|
isDefinerInUsers () {
|
||||||
|
return this.originalFunction ? this.workspace.users.some(user => this.originalFunction.definer === `\`${user.name}\`@\`${user.host}\``) : true;
|
||||||
|
},
|
||||||
|
schemaTables () {
|
||||||
|
const schemaTables = this.workspace.structure
|
||||||
|
.filter(schema => schema.name === this.schema)
|
||||||
|
.map(schema => schema.tables);
|
||||||
|
|
||||||
|
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
async function () {
|
||||||
|
if (this.isSelected) {
|
||||||
|
await this.getFunctionData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
||||||
|
this.lastFunction = this.function;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async isSelected (val) {
|
||||||
|
if (val && this.lastFunction !== this.function) {
|
||||||
|
await this.getFunctionData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
||||||
|
this.lastFunction = this.function;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isChanged (val) {
|
||||||
|
if (this.isSelected && this.lastFunction === this.function && this.function !== null)
|
||||||
|
this.setUnsavedChanges(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
window.addEventListener('resize', this.resizeQueryEditor);
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification',
|
||||||
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
|
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||||
|
}),
|
||||||
|
async getFunctionData () {
|
||||||
|
if (!this.function) return;
|
||||||
|
|
||||||
|
this.isLoading = true;
|
||||||
|
this.localFunction = { sql: '' };
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
func: this.workspace.breadcrumbs.function
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Functions.getFunctionInformations(params);
|
||||||
|
if (status === 'success') {
|
||||||
|
this.originalFunction = response;
|
||||||
|
this.localFunction = JSON.parse(JSON.stringify(this.originalFunction));
|
||||||
|
this.sqlProxy = this.localFunction.sql;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resizeQueryEditor();
|
||||||
|
this.isLoading = false;
|
||||||
|
},
|
||||||
|
async saveChanges () {
|
||||||
|
if (this.isSaving) return;
|
||||||
|
this.isSaving = true;
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
func: {
|
||||||
|
...this.localFunction,
|
||||||
|
oldName: this.originalFunction.name
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Functions.alterFunction(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
const oldName = this.originalFunction.name;
|
||||||
|
|
||||||
|
await this.refreshStructure(this.connection.uid);
|
||||||
|
|
||||||
|
if (oldName !== this.localFunction.name) {
|
||||||
|
this.setUnsavedChanges(false);
|
||||||
|
this.changeBreadcrumbs({ schema: this.schema, function: this.localFunction.name });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getFunctionData();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isSaving = false;
|
||||||
|
},
|
||||||
|
clearChanges () {
|
||||||
|
this.localFunction = JSON.parse(JSON.stringify(this.originalFunction));
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
||||||
|
},
|
||||||
|
resizeQueryEditor () {
|
||||||
|
if (this.$refs.queryEditor) {
|
||||||
|
const footer = document.getElementById('footer');
|
||||||
|
const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight;
|
||||||
|
this.editorHeight = size;
|
||||||
|
this.$refs.queryEditor.editor.resize();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
optionsUpdate (options) {
|
||||||
|
this.localFunction = options;
|
||||||
|
},
|
||||||
|
parametersUpdate (parameters) {
|
||||||
|
this.localFunction = { ...this.localFunction, parameters };
|
||||||
|
},
|
||||||
|
showOptionsModal () {
|
||||||
|
this.isOptionsModal = true;
|
||||||
|
},
|
||||||
|
hideOptionsModal () {
|
||||||
|
this.isOptionsModal = false;
|
||||||
|
},
|
||||||
|
showParamsModal () {
|
||||||
|
this.isParamsModal = true;
|
||||||
|
},
|
||||||
|
hideParamsModal () {
|
||||||
|
this.isParamsModal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
266
src/renderer/components/WorkspacePropsTabRoutine.vue
Normal file
266
src/renderer/components/WorkspacePropsTabRoutine.vue
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
<template>
|
||||||
|
<div class="workspace-query-tab column col-12 columns col-gapless">
|
||||||
|
<div class="workspace-query-runner column col-12">
|
||||||
|
<div class="workspace-query-runner-footer">
|
||||||
|
<div class="workspace-query-buttons">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary btn-sm"
|
||||||
|
:disabled="!isChanged"
|
||||||
|
:class="{'loading':isSaving}"
|
||||||
|
@click="saveChanges"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.save') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save ml-1" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:disabled="!isChanged"
|
||||||
|
class="btn btn-link btn-sm mr-0"
|
||||||
|
:title="$t('message.clearChanges')"
|
||||||
|
@click="clearChanges"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.clear') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="divider-vert py-3" />
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn-dark btn-sm disabled"
|
||||||
|
:disabled="isChanged"
|
||||||
|
@click="false"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.run') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-play ml-1" />
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-dark btn-sm" @click="showParamsModal">
|
||||||
|
<span>{{ $t('word.parameters') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-dots-horizontal ml-1" />
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-dark btn-sm" @click="showOptionsModal">
|
||||||
|
<span>{{ $t('word.options') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-cogs ml-1" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||||
|
<BaseLoader v-if="isLoading" />
|
||||||
|
<label class="form-label ml-2">{{ $t('message.routineBody') }}</label>
|
||||||
|
<QueryEditor
|
||||||
|
v-if="isSelected"
|
||||||
|
ref="queryEditor"
|
||||||
|
:value.sync="localRoutine.sql"
|
||||||
|
:workspace="workspace"
|
||||||
|
:schema="schema"
|
||||||
|
:height="editorHeight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<WorkspacePropsRoutineOptionsModal
|
||||||
|
v-if="isOptionsModal"
|
||||||
|
:local-options="localRoutine"
|
||||||
|
:workspace="workspace"
|
||||||
|
@hide="hideOptionsModal"
|
||||||
|
@options-update="optionsUpdate"
|
||||||
|
/>
|
||||||
|
<WorkspacePropsRoutineParamsModal
|
||||||
|
v-if="isParamsModal"
|
||||||
|
:local-parameters="localRoutine.parameters"
|
||||||
|
:workspace="workspace"
|
||||||
|
:routine="localRoutine.name"
|
||||||
|
@hide="hideParamsModal"
|
||||||
|
@parameters-update="parametersUpdate"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import QueryEditor from '@/components/QueryEditor';
|
||||||
|
import BaseLoader from '@/components/BaseLoader';
|
||||||
|
import WorkspacePropsRoutineOptionsModal from '@/components/WorkspacePropsRoutineOptionsModal';
|
||||||
|
import WorkspacePropsRoutineParamsModal from '@/components/WorkspacePropsRoutineParamsModal';
|
||||||
|
import Routines from '@/ipc-api/Routines';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsTabRoutine',
|
||||||
|
components: {
|
||||||
|
QueryEditor,
|
||||||
|
BaseLoader,
|
||||||
|
WorkspacePropsRoutineOptionsModal,
|
||||||
|
WorkspacePropsRoutineParamsModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
connection: Object,
|
||||||
|
routine: String
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
tabUid: 'prop',
|
||||||
|
isLoading: false,
|
||||||
|
isSaving: false,
|
||||||
|
isOptionsModal: false,
|
||||||
|
isParamsModal: false,
|
||||||
|
originalRoutine: null,
|
||||||
|
localRoutine: { sql: '' },
|
||||||
|
lastRoutine: null,
|
||||||
|
sqlProxy: '',
|
||||||
|
editorHeight: 300
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.connection.uid);
|
||||||
|
},
|
||||||
|
isSelected () {
|
||||||
|
return this.workspace.selected_tab === 'prop';
|
||||||
|
},
|
||||||
|
schema () {
|
||||||
|
return this.workspace.breadcrumbs.schema;
|
||||||
|
},
|
||||||
|
isChanged () {
|
||||||
|
return JSON.stringify(this.originalRoutine) !== JSON.stringify(this.localRoutine);
|
||||||
|
},
|
||||||
|
isDefinerInUsers () {
|
||||||
|
return this.originalRoutine ? this.workspace.users.some(user => this.originalRoutine.definer === `\`${user.name}\`@\`${user.host}\``) : true;
|
||||||
|
},
|
||||||
|
schemaTables () {
|
||||||
|
const schemaTables = this.workspace.structure
|
||||||
|
.filter(schema => schema.name === this.schema)
|
||||||
|
.map(schema => schema.tables);
|
||||||
|
|
||||||
|
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
async routine () {
|
||||||
|
if (this.isSelected) {
|
||||||
|
await this.getRoutineData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
|
||||||
|
this.lastRoutine = this.routine;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async isSelected (val) {
|
||||||
|
if (val && this.lastRoutine !== this.routine) {
|
||||||
|
await this.getRoutineData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
|
||||||
|
this.lastRoutine = this.routine;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isChanged (val) {
|
||||||
|
if (this.isSelected && this.lastRoutine === this.routine && this.routine !== null)
|
||||||
|
this.setUnsavedChanges(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
window.addEventListener('resize', this.resizeQueryEditor);
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification',
|
||||||
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
|
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||||
|
}),
|
||||||
|
async getRoutineData () {
|
||||||
|
if (!this.routine) return;
|
||||||
|
this.localRoutine = { sql: '' };
|
||||||
|
this.isLoading = true;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
routine: this.workspace.breadcrumbs.procedure
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Routines.getRoutineInformations(params);
|
||||||
|
if (status === 'success') {
|
||||||
|
this.originalRoutine = response;
|
||||||
|
this.localRoutine = JSON.parse(JSON.stringify(this.originalRoutine));
|
||||||
|
this.sqlProxy = this.localRoutine.sql;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resizeQueryEditor();
|
||||||
|
this.isLoading = false;
|
||||||
|
},
|
||||||
|
async saveChanges () {
|
||||||
|
if (this.isSaving) return;
|
||||||
|
this.isSaving = true;
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
routine: {
|
||||||
|
...this.localRoutine,
|
||||||
|
oldName: this.originalRoutine.name
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Routines.alterRoutine(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
const oldName = this.originalRoutine.name;
|
||||||
|
|
||||||
|
await this.refreshStructure(this.connection.uid);
|
||||||
|
|
||||||
|
if (oldName !== this.localRoutine.name) {
|
||||||
|
this.setUnsavedChanges(false);
|
||||||
|
this.changeBreadcrumbs({ schema: this.schema, procedure: this.localRoutine.name });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getRoutineData();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isSaving = false;
|
||||||
|
},
|
||||||
|
clearChanges () {
|
||||||
|
this.localRoutine = JSON.parse(JSON.stringify(this.originalRoutine));
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
|
||||||
|
},
|
||||||
|
resizeQueryEditor () {
|
||||||
|
if (this.$refs.queryEditor) {
|
||||||
|
const footer = document.getElementById('footer');
|
||||||
|
const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight;
|
||||||
|
this.editorHeight = size;
|
||||||
|
this.$refs.queryEditor.editor.resize();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
optionsUpdate (options) {
|
||||||
|
this.localRoutine = options;
|
||||||
|
},
|
||||||
|
parametersUpdate (parameters) {
|
||||||
|
this.localRoutine = { ...this.localRoutine, parameters };
|
||||||
|
},
|
||||||
|
showOptionsModal () {
|
||||||
|
this.isOptionsModal = true;
|
||||||
|
},
|
||||||
|
hideOptionsModal () {
|
||||||
|
this.isOptionsModal = false;
|
||||||
|
},
|
||||||
|
showParamsModal () {
|
||||||
|
this.isParamsModal = true;
|
||||||
|
},
|
||||||
|
hideParamsModal () {
|
||||||
|
this.isParamsModal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
316
src/renderer/components/WorkspacePropsTabScheduler.vue
Normal file
316
src/renderer/components/WorkspacePropsTabScheduler.vue
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
<template>
|
||||||
|
<div class="workspace-query-tab column col-12 columns col-gapless">
|
||||||
|
<div class="workspace-query-runner column col-12">
|
||||||
|
<div class="workspace-query-runner-footer">
|
||||||
|
<div class="workspace-query-buttons">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary btn-sm"
|
||||||
|
:disabled="!isChanged"
|
||||||
|
:class="{'loading':isSaving}"
|
||||||
|
@click="saveChanges"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.save') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save ml-1" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:disabled="!isChanged"
|
||||||
|
class="btn btn-link btn-sm mr-0"
|
||||||
|
:title="$t('message.clearChanges')"
|
||||||
|
@click="clearChanges"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.clear') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="divider-vert py-3" />
|
||||||
|
<button class="btn btn-dark btn-sm" @click="showTimingModal">
|
||||||
|
<span>{{ $t('word.timing') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-timer ml-1" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
<div class="columns mb-4">
|
||||||
|
<div class="column col-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('word.name') }}</label>
|
||||||
|
<input
|
||||||
|
v-model="localScheduler.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('word.definer') }}</label>
|
||||||
|
<select
|
||||||
|
v-if="workspace.users.length"
|
||||||
|
v-model="localScheduler.definer"
|
||||||
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
<option v-if="!isDefinerInUsers" :value="originalScheduler.definer">
|
||||||
|
{{ originalScheduler.definer.replaceAll('`', '') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="user in workspace.users"
|
||||||
|
:key="`${user.name}@${user.host}`"
|
||||||
|
:value="`\`${user.name}\`@\`${user.host}\``"
|
||||||
|
>
|
||||||
|
{{ user.name }}@{{ user.host }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<select v-if="!workspace.users.length" class="form-select">
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('word.comment') }}</label>
|
||||||
|
<input
|
||||||
|
v-model="localScheduler.comment"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label mr-2">{{ $t('word.state') }}</label>
|
||||||
|
<label class="form-radio form-inline">
|
||||||
|
<input
|
||||||
|
v-model="localScheduler.state"
|
||||||
|
type="radio"
|
||||||
|
name="state"
|
||||||
|
value="ENABLE"
|
||||||
|
><i class="form-icon" /> ENABLE
|
||||||
|
</label>
|
||||||
|
<label class="form-radio form-inline">
|
||||||
|
<input
|
||||||
|
v-model="localScheduler.state"
|
||||||
|
type="radio"
|
||||||
|
name="state"
|
||||||
|
value="DISABLE"
|
||||||
|
><i class="form-icon" /> DISABLE
|
||||||
|
</label>
|
||||||
|
<label class="form-radio form-inline">
|
||||||
|
<input
|
||||||
|
v-model="localScheduler.state"
|
||||||
|
type="radio"
|
||||||
|
name="state"
|
||||||
|
value="DISABLE ON SLAVE"
|
||||||
|
><i class="form-icon" /> DISABLE ON SLAVE
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||||
|
<BaseLoader v-if="isLoading" />
|
||||||
|
<label class="form-label ml-2">{{ $t('message.schedulerBody') }}</label>
|
||||||
|
<QueryEditor
|
||||||
|
v-if="isSelected"
|
||||||
|
ref="queryEditor"
|
||||||
|
:value.sync="localScheduler.sql"
|
||||||
|
:workspace="workspace"
|
||||||
|
:schema="schema"
|
||||||
|
:height="editorHeight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<WorkspacePropsSchedulerTimingModal
|
||||||
|
v-if="isTimingModal"
|
||||||
|
:local-options="localScheduler"
|
||||||
|
:workspace="workspace"
|
||||||
|
@hide="hideTimingModal"
|
||||||
|
@options-update="timingUpdate"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import BaseLoader from '@/components/BaseLoader';
|
||||||
|
import QueryEditor from '@/components/QueryEditor';
|
||||||
|
import WorkspacePropsSchedulerTimingModal from '@/components/WorkspacePropsSchedulerTimingModal';
|
||||||
|
import Schedulers from '@/ipc-api/Schedulers';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsTabScheduler',
|
||||||
|
components: {
|
||||||
|
BaseLoader,
|
||||||
|
QueryEditor,
|
||||||
|
WorkspacePropsSchedulerTimingModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
connection: Object,
|
||||||
|
scheduler: String
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
tabUid: 'prop',
|
||||||
|
isLoading: false,
|
||||||
|
isSaving: false,
|
||||||
|
isTimingModal: false,
|
||||||
|
originalScheduler: null,
|
||||||
|
localScheduler: { sql: '' },
|
||||||
|
lastScheduler: null,
|
||||||
|
sqlProxy: '',
|
||||||
|
editorHeight: 300
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.connection.uid);
|
||||||
|
},
|
||||||
|
isSelected () {
|
||||||
|
return this.workspace.selected_tab === 'prop';
|
||||||
|
},
|
||||||
|
schema () {
|
||||||
|
return this.workspace.breadcrumbs.schema;
|
||||||
|
},
|
||||||
|
isChanged () {
|
||||||
|
return JSON.stringify(this.originalScheduler) !== JSON.stringify(this.localScheduler);
|
||||||
|
},
|
||||||
|
isDefinerInUsers () {
|
||||||
|
return this.originalScheduler ? this.workspace.users.some(user => this.originalScheduler.definer === `\`${user.name}\`@\`${user.host}\``) : true;
|
||||||
|
},
|
||||||
|
schemaTables () {
|
||||||
|
const schemaTables = this.workspace.structure
|
||||||
|
.filter(schema => schema.name === this.schema)
|
||||||
|
.map(schema => schema.tables);
|
||||||
|
|
||||||
|
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
async scheduler () {
|
||||||
|
if (this.isSelected) {
|
||||||
|
await this.getSchedulerData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localScheduler.sql);
|
||||||
|
this.lastScheduler = this.scheduler;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async isSelected (val) {
|
||||||
|
if (val && this.lastScheduler !== this.scheduler) {
|
||||||
|
await this.getSchedulerData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localScheduler.sql);
|
||||||
|
this.lastScheduler = this.scheduler;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isChanged (val) {
|
||||||
|
if (this.isSelected && this.lastScheduler === this.scheduler && this.scheduler !== null)
|
||||||
|
this.setUnsavedChanges(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
window.addEventListener('resize', this.resizeQueryEditor);
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification',
|
||||||
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
|
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||||
|
}),
|
||||||
|
async getSchedulerData () {
|
||||||
|
if (!this.scheduler) return;
|
||||||
|
this.isLoading = true;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
scheduler: this.workspace.breadcrumbs.scheduler
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Schedulers.getSchedulerInformations(params);
|
||||||
|
if (status === 'success') {
|
||||||
|
this.originalScheduler = response;
|
||||||
|
this.localScheduler = JSON.parse(JSON.stringify(this.originalScheduler));
|
||||||
|
this.sqlProxy = this.localScheduler.sql;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resizeQueryEditor();
|
||||||
|
this.isLoading = false;
|
||||||
|
},
|
||||||
|
async saveChanges () {
|
||||||
|
if (this.isSaving) return;
|
||||||
|
this.isSaving = true;
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
scheduler: {
|
||||||
|
...this.localScheduler,
|
||||||
|
oldName: this.originalScheduler.name
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Schedulers.alterScheduler(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
const oldName = this.originalScheduler.name;
|
||||||
|
|
||||||
|
await this.refreshStructure(this.connection.uid);
|
||||||
|
|
||||||
|
if (oldName !== this.localScheduler.name) {
|
||||||
|
this.setUnsavedChanges(false);
|
||||||
|
this.changeBreadcrumbs({ schema: this.schema, scheduler: this.localScheduler.name });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getSchedulerData();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isSaving = false;
|
||||||
|
},
|
||||||
|
clearChanges () {
|
||||||
|
this.localScheduler = JSON.parse(JSON.stringify(this.originalScheduler));
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localScheduler.sql);
|
||||||
|
},
|
||||||
|
resizeQueryEditor () {
|
||||||
|
if (this.$refs.queryEditor) {
|
||||||
|
const footer = document.getElementById('footer');
|
||||||
|
const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight;
|
||||||
|
this.editorHeight = size;
|
||||||
|
this.$refs.queryEditor.editor.resize();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showTimingModal () {
|
||||||
|
this.isTimingModal = true;
|
||||||
|
},
|
||||||
|
hideTimingModal () {
|
||||||
|
this.isTimingModal = false;
|
||||||
|
},
|
||||||
|
timingUpdate (options) {
|
||||||
|
this.localScheduler = options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
280
src/renderer/components/WorkspacePropsTabTrigger.vue
Normal file
280
src/renderer/components/WorkspacePropsTabTrigger.vue
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
<template>
|
||||||
|
<div class="workspace-query-tab column col-12 columns col-gapless">
|
||||||
|
<div class="workspace-query-runner column col-12">
|
||||||
|
<div class="workspace-query-runner-footer">
|
||||||
|
<div class="workspace-query-buttons">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary btn-sm"
|
||||||
|
:disabled="!isChanged"
|
||||||
|
:class="{'loading':isSaving}"
|
||||||
|
@click="saveChanges"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.save') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save ml-1" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:disabled="!isChanged"
|
||||||
|
class="btn btn-link btn-sm mr-0"
|
||||||
|
:title="$t('message.clearChanges')"
|
||||||
|
@click="clearChanges"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.clear') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
<div class="columns mb-4">
|
||||||
|
<div class="column col-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('word.name') }}</label>
|
||||||
|
<input
|
||||||
|
v-model="localTrigger.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('word.definer') }}</label>
|
||||||
|
<select
|
||||||
|
v-if="workspace.users.length"
|
||||||
|
v-model="localTrigger.definer"
|
||||||
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
<option v-if="!isDefinerInUsers" :value="originalTrigger.definer">
|
||||||
|
{{ originalTrigger.definer.replaceAll('`', '') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="user in workspace.users"
|
||||||
|
:key="`${user.name}@${user.host}`"
|
||||||
|
:value="`\`${user.name}\`@\`${user.host}\``"
|
||||||
|
>
|
||||||
|
{{ user.name }}@{{ user.host }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<select v-if="!workspace.users.length" class="form-select">
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column col-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('word.table') }}</label>
|
||||||
|
<select v-model="localTrigger.table" class="form-select">
|
||||||
|
<option v-for="table in schemaTables" :key="table.name">
|
||||||
|
{{ table.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('word.event') }}</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<select v-model="localTrigger.event1" class="form-select">
|
||||||
|
<option>BEFORE</option>
|
||||||
|
<option>AFTER</option>
|
||||||
|
</select>
|
||||||
|
<select v-model="localTrigger.event2" class="form-select">
|
||||||
|
<option>INSERT</option>
|
||||||
|
<option>UPDATE</option>
|
||||||
|
<option>DELETE</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||||
|
<BaseLoader v-if="isLoading" />
|
||||||
|
<label class="form-label ml-2">{{ $t('message.triggerStatement') }}</label>
|
||||||
|
<QueryEditor
|
||||||
|
v-if="isSelected"
|
||||||
|
ref="queryEditor"
|
||||||
|
:value.sync="localTrigger.sql"
|
||||||
|
:workspace="workspace"
|
||||||
|
:schema="schema"
|
||||||
|
:height="editorHeight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import QueryEditor from '@/components/QueryEditor';
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import BaseLoader from '@/components/BaseLoader';
|
||||||
|
import Triggers from '@/ipc-api/Triggers';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsTabTrigger',
|
||||||
|
components: {
|
||||||
|
BaseLoader,
|
||||||
|
QueryEditor
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
connection: Object,
|
||||||
|
trigger: String
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
tabUid: 'prop',
|
||||||
|
isLoading: false,
|
||||||
|
isSaving: false,
|
||||||
|
originalTrigger: null,
|
||||||
|
localTrigger: { sql: '' },
|
||||||
|
lastTrigger: null,
|
||||||
|
sqlProxy: '',
|
||||||
|
editorHeight: 300
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.connection.uid);
|
||||||
|
},
|
||||||
|
isSelected () {
|
||||||
|
return this.workspace.selected_tab === 'prop';
|
||||||
|
},
|
||||||
|
schema () {
|
||||||
|
return this.workspace.breadcrumbs.schema;
|
||||||
|
},
|
||||||
|
isChanged () {
|
||||||
|
return JSON.stringify(this.originalTrigger) !== JSON.stringify(this.localTrigger);
|
||||||
|
},
|
||||||
|
isDefinerInUsers () {
|
||||||
|
return this.originalTrigger ? this.workspace.users.some(user => this.originalTrigger.definer === `\`${user.name}\`@\`${user.host}\``) : true;
|
||||||
|
},
|
||||||
|
schemaTables () {
|
||||||
|
const schemaTables = this.workspace.structure
|
||||||
|
.filter(schema => schema.name === this.schema)
|
||||||
|
.map(schema => schema.tables);
|
||||||
|
|
||||||
|
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
async trigger () {
|
||||||
|
if (this.isSelected) {
|
||||||
|
await this.getTriggerData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
|
||||||
|
this.lastTrigger = this.trigger;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async isSelected (val) {
|
||||||
|
if (val && this.lastTrigger !== this.trigger) {
|
||||||
|
await this.getTriggerData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
|
||||||
|
this.lastTrigger = this.trigger;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isChanged (val) {
|
||||||
|
if (this.isSelected && this.lastTrigger === this.trigger && this.trigger !== null)
|
||||||
|
this.setUnsavedChanges(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
window.addEventListener('resize', this.resizeQueryEditor);
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification',
|
||||||
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
|
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||||
|
}),
|
||||||
|
async getTriggerData () {
|
||||||
|
if (!this.trigger) return;
|
||||||
|
|
||||||
|
this.localTrigger = { sql: '' };
|
||||||
|
this.isLoading = true;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
trigger: this.workspace.breadcrumbs.trigger
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Triggers.getTriggerInformations(params);
|
||||||
|
if (status === 'success') {
|
||||||
|
this.originalTrigger = response;
|
||||||
|
this.localTrigger = JSON.parse(JSON.stringify(this.originalTrigger));
|
||||||
|
this.sqlProxy = this.localTrigger.sql;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resizeQueryEditor();
|
||||||
|
this.isLoading = false;
|
||||||
|
},
|
||||||
|
async saveChanges () {
|
||||||
|
if (this.isSaving) return;
|
||||||
|
this.isSaving = true;
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
trigger: {
|
||||||
|
...this.localTrigger,
|
||||||
|
oldName: this.originalTrigger.name
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Triggers.alterTrigger(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
const oldName = this.originalTrigger.name;
|
||||||
|
|
||||||
|
await this.refreshStructure(this.connection.uid);
|
||||||
|
|
||||||
|
if (oldName !== this.localTrigger.name) {
|
||||||
|
this.setUnsavedChanges(false);
|
||||||
|
this.changeBreadcrumbs({ schema: this.schema, trigger: this.localTrigger.name });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getTriggerData();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isSaving = false;
|
||||||
|
},
|
||||||
|
clearChanges () {
|
||||||
|
this.localTrigger = JSON.parse(JSON.stringify(this.originalTrigger));
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
|
||||||
|
},
|
||||||
|
resizeQueryEditor () {
|
||||||
|
if (this.$refs.queryEditor) {
|
||||||
|
const footer = document.getElementById('footer');
|
||||||
|
const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight;
|
||||||
|
this.editorHeight = size;
|
||||||
|
this.$refs.queryEditor.editor.resize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
333
src/renderer/components/WorkspacePropsTabView.vue
Normal file
333
src/renderer/components/WorkspacePropsTabView.vue
Normal file
@@ -0,0 +1,333 @@
|
|||||||
|
<template>
|
||||||
|
<div class="workspace-query-tab column col-12 columns col-gapless">
|
||||||
|
<div class="workspace-query-runner column col-12">
|
||||||
|
<div class="workspace-query-runner-footer">
|
||||||
|
<div class="workspace-query-buttons">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary btn-sm"
|
||||||
|
:disabled="!isChanged"
|
||||||
|
:class="{'loading':isSaving}"
|
||||||
|
@click="saveChanges"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.save') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save ml-1" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:disabled="!isChanged"
|
||||||
|
class="btn btn-link btn-sm mr-0"
|
||||||
|
:title="$t('message.clearChanges')"
|
||||||
|
@click="clearChanges"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.clear') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
<div class="columns mb-4">
|
||||||
|
<div class="column col-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('word.name') }}</label>
|
||||||
|
<input
|
||||||
|
v-model="localView.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-3">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('word.definer') }}</label>
|
||||||
|
<select
|
||||||
|
v-if="workspace.users.length"
|
||||||
|
v-model="localView.definer"
|
||||||
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
<option v-if="!isDefinerInUsers" :value="originalView.definer">
|
||||||
|
{{ originalView.definer.replaceAll('`', '') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="user in workspace.users"
|
||||||
|
:key="`${user.name}@${user.host}`"
|
||||||
|
:value="`\`${user.name}\`@\`${user.host}\``"
|
||||||
|
>
|
||||||
|
{{ user.name }}@{{ user.host }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<select v-if="!workspace.users.length" class="form-select">
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column col-2">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('message.sqlSecurity') }}</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="localView.security"
|
||||||
|
type="radio"
|
||||||
|
name="security"
|
||||||
|
value="DEFINER"
|
||||||
|
>
|
||||||
|
<i class="form-icon" /> DEFINER
|
||||||
|
</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="localView.security"
|
||||||
|
type="radio"
|
||||||
|
name="security"
|
||||||
|
value="INVOKER"
|
||||||
|
>
|
||||||
|
<i class="form-icon" /> INVOKER
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-2">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('word.algorithm') }}</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="localView.algorithm"
|
||||||
|
type="radio"
|
||||||
|
name="algorithm"
|
||||||
|
value="UNDEFINED"
|
||||||
|
>
|
||||||
|
<i class="form-icon" /> UNDEFINED
|
||||||
|
</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="localView.algorithm"
|
||||||
|
type="radio"
|
||||||
|
value="MERGE"
|
||||||
|
name="algorithm"
|
||||||
|
>
|
||||||
|
<i class="form-icon" /> MERGE
|
||||||
|
</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="localView.algorithm"
|
||||||
|
type="radio"
|
||||||
|
value="TEMPTABLE"
|
||||||
|
name="algorithm"
|
||||||
|
>
|
||||||
|
<i class="form-icon" /> TEMPTABLE
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-2">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('message.updateOption') }}</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="localView.updateOption"
|
||||||
|
type="radio"
|
||||||
|
name="update"
|
||||||
|
value=""
|
||||||
|
>
|
||||||
|
<i class="form-icon" /> None
|
||||||
|
</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="localView.updateOption"
|
||||||
|
type="radio"
|
||||||
|
name="update"
|
||||||
|
value="CASCADED"
|
||||||
|
>
|
||||||
|
<i class="form-icon" /> CASCADED
|
||||||
|
</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="localView.updateOption"
|
||||||
|
type="radio"
|
||||||
|
name="update"
|
||||||
|
value="LOCAL"
|
||||||
|
>
|
||||||
|
<i class="form-icon" /> LOCAL
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||||
|
<BaseLoader v-if="isLoading" />
|
||||||
|
<label class="form-label ml-2">{{ $t('message.selectStatement') }}</label>
|
||||||
|
<QueryEditor
|
||||||
|
v-if="isSelected"
|
||||||
|
ref="queryEditor"
|
||||||
|
:value.sync="localView.sql"
|
||||||
|
:workspace="workspace"
|
||||||
|
:schema="schema"
|
||||||
|
:height="editorHeight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import BaseLoader from '@/components/BaseLoader';
|
||||||
|
import QueryEditor from '@/components/QueryEditor';
|
||||||
|
import Views from '@/ipc-api/Views';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsTabView',
|
||||||
|
components: {
|
||||||
|
BaseLoader,
|
||||||
|
QueryEditor
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
connection: Object,
|
||||||
|
view: String
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
tabUid: 'prop',
|
||||||
|
isLoading: false,
|
||||||
|
isSaving: false,
|
||||||
|
originalView: null,
|
||||||
|
localView: { sql: '' },
|
||||||
|
lastView: null,
|
||||||
|
sqlProxy: '',
|
||||||
|
editorHeight: 300
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.connection.uid);
|
||||||
|
},
|
||||||
|
isSelected () {
|
||||||
|
return this.workspace.selected_tab === 'prop';
|
||||||
|
},
|
||||||
|
schema () {
|
||||||
|
return this.workspace.breadcrumbs.schema;
|
||||||
|
},
|
||||||
|
isChanged () {
|
||||||
|
return JSON.stringify(this.originalView) !== JSON.stringify(this.localView);
|
||||||
|
},
|
||||||
|
isDefinerInUsers () {
|
||||||
|
return this.originalView ? this.workspace.users.some(user => this.originalView.definer === `\`${user.name}\`@\`${user.host}\``) : true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
async view () {
|
||||||
|
if (this.isSelected) {
|
||||||
|
await this.getViewData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localView.sql);
|
||||||
|
this.lastView = this.view;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async isSelected (val) {
|
||||||
|
if (val && this.lastView !== this.view) {
|
||||||
|
await this.getViewData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localView.sql);
|
||||||
|
this.lastView = this.view;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isChanged (val) {
|
||||||
|
if (this.isSelected && this.lastView === this.view && this.view !== null)
|
||||||
|
this.setUnsavedChanges(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
window.addEventListener('resize', this.resizeQueryEditor);
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification',
|
||||||
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
|
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||||
|
}),
|
||||||
|
async getViewData () {
|
||||||
|
if (!this.view) return;
|
||||||
|
this.isLoading = true;
|
||||||
|
this.localView = { sql: '' };
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
view: this.workspace.breadcrumbs.view
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Views.getViewInformations(params);
|
||||||
|
if (status === 'success') {
|
||||||
|
this.originalView = response;
|
||||||
|
this.localView = JSON.parse(JSON.stringify(this.originalView));
|
||||||
|
this.sqlProxy = this.localView.sql;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resizeQueryEditor();
|
||||||
|
this.isLoading = false;
|
||||||
|
},
|
||||||
|
async saveChanges () {
|
||||||
|
if (this.isSaving) return;
|
||||||
|
this.isSaving = true;
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
view: {
|
||||||
|
...this.localView,
|
||||||
|
oldName: this.originalView.name
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Views.alterView(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
const oldName = this.originalView.name;
|
||||||
|
|
||||||
|
await this.refreshStructure(this.connection.uid);
|
||||||
|
|
||||||
|
if (oldName !== this.localView.name) {
|
||||||
|
this.setUnsavedChanges(false);
|
||||||
|
this.changeBreadcrumbs({ schema: this.schema, view: this.localView.name });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getViewData();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isSaving = false;
|
||||||
|
},
|
||||||
|
clearChanges () {
|
||||||
|
this.localView = JSON.parse(JSON.stringify(this.originalView));
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localView.sql);
|
||||||
|
},
|
||||||
|
resizeQueryEditor () {
|
||||||
|
if (this.$refs.queryEditor) {
|
||||||
|
const footer = document.getElementById('footer');
|
||||||
|
const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight;
|
||||||
|
this.editorHeight = size;
|
||||||
|
this.$refs.queryEditor.editor.resize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@@ -77,11 +77,3 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.disabled {
|
|
||||||
pointer-events: none;
|
|
||||||
filter: grayscale(100%);
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@@ -80,6 +80,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
|
import arrayToFile from '../libs/arrayToFile';
|
||||||
import { LONG_TEXT, BLOB } from 'common/fieldTypes';
|
import { LONG_TEXT, BLOB } from 'common/fieldTypes';
|
||||||
import BaseVirtualScroll from '@/components/BaseVirtualScroll';
|
import BaseVirtualScroll from '@/components/BaseVirtualScroll';
|
||||||
import WorkspaceQueryTableRow from '@/components/WorkspaceQueryTableRow';
|
import WorkspaceQueryTableRow from '@/components/WorkspaceQueryTableRow';
|
||||||
@@ -195,7 +196,7 @@ export default {
|
|||||||
},
|
},
|
||||||
fieldLength (field) {
|
fieldLength (field) {
|
||||||
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
||||||
return field.numLength || field.datePrecision || field.charLength || 0;
|
return field.length;
|
||||||
},
|
},
|
||||||
keyName (key) {
|
keyName (key) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
@@ -354,6 +355,20 @@ export default {
|
|||||||
},
|
},
|
||||||
selectResultset (index) {
|
selectResultset (index) {
|
||||||
this.resultsetIndex = index;
|
this.resultsetIndex = index;
|
||||||
|
},
|
||||||
|
downloadTable (format, filename) {
|
||||||
|
if (!this.sortedResults) return;
|
||||||
|
|
||||||
|
const rows = [...this.sortedResults].map(row => {
|
||||||
|
delete row._id;
|
||||||
|
return row;
|
||||||
|
});
|
||||||
|
|
||||||
|
arrayToFile({
|
||||||
|
type: format,
|
||||||
|
content: rows,
|
||||||
|
filename
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -32,13 +32,30 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
class="btn btn-dark btn-sm"
|
<button class="btn btn-dark btn-sm" @click="showAddModal">
|
||||||
@click="showAddModal"
|
|
||||||
>
|
|
||||||
<span>{{ $t('word.add') }}</span>
|
<span>{{ $t('word.add') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-playlist-plus ml-1" />
|
<i class="mdi mdi-24px mdi-playlist-plus ml-1" />
|
||||||
</button>
|
</button>
|
||||||
|
<div class="dropdown export-dropdown">
|
||||||
|
<button
|
||||||
|
:disabled="isQuering"
|
||||||
|
class="btn btn-dark btn-sm dropdown-toggle mr-0 pr-0"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.export') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-file-export ml-1" />
|
||||||
|
<i class="mdi mdi-24px mdi-menu-down" />
|
||||||
|
</button>
|
||||||
|
<ul class="menu text-left">
|
||||||
|
<li class="menu-item">
|
||||||
|
<a class="c-hand" @click="downloadTable('json')">JSON</a>
|
||||||
|
</li>
|
||||||
|
<li class="menu-item">
|
||||||
|
<a class="c-hand" @click="downloadTable('csv')">CSV</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-info">
|
<div class="workspace-query-info">
|
||||||
<div v-if="results.length && results[0].rows">
|
<div v-if="results.length && results[0].rows">
|
||||||
@@ -171,7 +188,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.workspace.breadcrumbs.table || this.workspace.breadcrumbs.view,
|
||||||
sortParams
|
sortParams
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -222,7 +239,21 @@ export default {
|
|||||||
this.reloadTable();
|
this.reloadTable();
|
||||||
}, this.autorefreshTimer * 1000);
|
}, this.autorefreshTimer * 1000);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
downloadTable (format) {
|
||||||
|
this.$refs.queryTable.downloadTable(format, this.table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.export-dropdown {
|
||||||
|
.menu {
|
||||||
|
min-width: 100%;
|
||||||
|
|
||||||
|
.menu-item a:hover {
|
||||||
|
background: $bg-color-gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@@ -67,7 +67,25 @@ module.exports = {
|
|||||||
dark: 'Dark',
|
dark: 'Dark',
|
||||||
autoCompletion: 'Auto Completion',
|
autoCompletion: 'Auto Completion',
|
||||||
application: 'Application',
|
application: 'Application',
|
||||||
editor: 'Editor'
|
editor: 'Editor',
|
||||||
|
view: 'View',
|
||||||
|
definer: 'Definer',
|
||||||
|
algorithm: 'Algorithm',
|
||||||
|
trigger: 'Trigger | Triggers',
|
||||||
|
storedRoutine: 'Stored routine | Stored routines',
|
||||||
|
scheduler: 'Scheduler | Schedulers',
|
||||||
|
event: 'Event',
|
||||||
|
parameters: 'Parameters',
|
||||||
|
function: 'Function | Functions',
|
||||||
|
deterministic: 'Deterministic',
|
||||||
|
context: 'Context',
|
||||||
|
export: 'Export',
|
||||||
|
returns: 'Returns',
|
||||||
|
timing: 'Timing',
|
||||||
|
state: 'State',
|
||||||
|
execution: 'Execution',
|
||||||
|
starts: 'Starts',
|
||||||
|
ends: 'Ends'
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
appWelcome: 'Welcome to Antares SQL Client!',
|
appWelcome: 'Welcome to Antares SQL Client!',
|
||||||
@@ -131,7 +149,30 @@ module.exports = {
|
|||||||
invalidDefault: 'Invalid default',
|
invalidDefault: 'Invalid default',
|
||||||
onDelete: 'On delete',
|
onDelete: 'On delete',
|
||||||
applicationTheme: 'Application Theme',
|
applicationTheme: 'Application Theme',
|
||||||
editorTheme: 'Editor Theme'
|
editorTheme: 'Editor Theme',
|
||||||
|
wrapLongLines: 'Wrap long lines',
|
||||||
|
selectStatement: 'Select statement',
|
||||||
|
triggerStatement: 'Trigger statement',
|
||||||
|
sqlSecurity: 'SQL security',
|
||||||
|
updateOption: 'Update option',
|
||||||
|
deleteView: 'Delete view',
|
||||||
|
createNewView: 'Create new view',
|
||||||
|
deleteTrigger: 'Delete trigger',
|
||||||
|
createNewTrigger: 'Create new trigger',
|
||||||
|
currentUser: 'Current user',
|
||||||
|
routineBody: 'Routine body',
|
||||||
|
dataAccess: 'Data access',
|
||||||
|
thereAreNoParameters: 'There are no parameters',
|
||||||
|
createNewParameter: 'Create new parameter',
|
||||||
|
createNewRoutine: 'Create new stored routine',
|
||||||
|
deleteRoutine: 'Delete stored routine',
|
||||||
|
functionBody: 'Function body',
|
||||||
|
createNewFunction: 'Create new function',
|
||||||
|
deleteFunction: 'Delete function',
|
||||||
|
schedulerBody: 'Scheduler body',
|
||||||
|
createNewScheduler: 'Create new scheduler',
|
||||||
|
deleteScheduler: 'Delete scheduler',
|
||||||
|
preserveOnCompletion: 'Preserve on completion'
|
||||||
},
|
},
|
||||||
// Date and Time
|
// Date and Time
|
||||||
short: {
|
short: {
|
||||||
|
@@ -18,8 +18,8 @@ export default class {
|
|||||||
return ipcRenderer.invoke('delete-database', params);
|
return ipcRenderer.invoke('delete-database', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getStructure (uid) {
|
static getStructure (params) {
|
||||||
return ipcRenderer.invoke('get-structure', uid);
|
return ipcRenderer.invoke('get-structure', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getCollations (uid) {
|
static getCollations (uid) {
|
||||||
|
20
src/renderer/ipc-api/Functions.js
Normal file
20
src/renderer/ipc-api/Functions.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
'use strict';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
|
export default class {
|
||||||
|
static getFunctionInformations (params) {
|
||||||
|
return ipcRenderer.invoke('get-function-informations', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static dropFunction (params) {
|
||||||
|
return ipcRenderer.invoke('drop-function', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static alterFunction (params) {
|
||||||
|
return ipcRenderer.invoke('alter-function', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createFunction (params) {
|
||||||
|
return ipcRenderer.invoke('create-function', params);
|
||||||
|
}
|
||||||
|
}
|
20
src/renderer/ipc-api/Routines.js
Normal file
20
src/renderer/ipc-api/Routines.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
'use strict';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
|
export default class {
|
||||||
|
static getRoutineInformations (params) {
|
||||||
|
return ipcRenderer.invoke('get-routine-informations', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static dropRoutine (params) {
|
||||||
|
return ipcRenderer.invoke('drop-routine', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static alterRoutine (params) {
|
||||||
|
return ipcRenderer.invoke('alter-routine', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createRoutine (params) {
|
||||||
|
return ipcRenderer.invoke('create-routine', params);
|
||||||
|
}
|
||||||
|
}
|
20
src/renderer/ipc-api/Schedulers.js
Normal file
20
src/renderer/ipc-api/Schedulers.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
'use strict';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
|
export default class {
|
||||||
|
static getSchedulerInformations (params) {
|
||||||
|
return ipcRenderer.invoke('get-scheduler-informations', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static dropScheduler (params) {
|
||||||
|
return ipcRenderer.invoke('drop-scheduler', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static alterScheduler (params) {
|
||||||
|
return ipcRenderer.invoke('alter-scheduler', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createScheduler (params) {
|
||||||
|
return ipcRenderer.invoke('create-scheduler', params);
|
||||||
|
}
|
||||||
|
}
|
20
src/renderer/ipc-api/Triggers.js
Normal file
20
src/renderer/ipc-api/Triggers.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
'use strict';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
|
export default class {
|
||||||
|
static getTriggerInformations (params) {
|
||||||
|
return ipcRenderer.invoke('get-trigger-informations', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static dropTrigger (params) {
|
||||||
|
return ipcRenderer.invoke('drop-trigger', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static alterTrigger (params) {
|
||||||
|
return ipcRenderer.invoke('alter-trigger', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createTrigger (params) {
|
||||||
|
return ipcRenderer.invoke('create-trigger', params);
|
||||||
|
}
|
||||||
|
}
|
8
src/renderer/ipc-api/Users.js
Normal file
8
src/renderer/ipc-api/Users.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
'use strict';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
|
export default class {
|
||||||
|
static getUsers (params) {
|
||||||
|
return ipcRenderer.invoke('get-users', params);
|
||||||
|
}
|
||||||
|
}
|
20
src/renderer/ipc-api/Views.js
Normal file
20
src/renderer/ipc-api/Views.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
'use strict';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
|
export default class {
|
||||||
|
static getViewInformations (params) {
|
||||||
|
return ipcRenderer.invoke('get-view-informations', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static dropView (params) {
|
||||||
|
return ipcRenderer.invoke('drop-view', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static alterView (params) {
|
||||||
|
return ipcRenderer.invoke('alter-view', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
static createView (params) {
|
||||||
|
return ipcRenderer.invoke('create-view', params);
|
||||||
|
}
|
||||||
|
}
|
37
src/renderer/libs/arrayToFile.js
Normal file
37
src/renderer/libs/arrayToFile.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
const arrayToFile = args => {
|
||||||
|
let mime;
|
||||||
|
let content;
|
||||||
|
|
||||||
|
switch (args.type) {
|
||||||
|
case 'csv': {
|
||||||
|
mime = 'text/csv';
|
||||||
|
const csv = [];
|
||||||
|
|
||||||
|
if (args.content.length)
|
||||||
|
csv.push(Object.keys(args.content[0]).join(';'));
|
||||||
|
|
||||||
|
for (const row of args.content)
|
||||||
|
csv.push(Object.values(row).map(col => typeof col === 'string' ? `"${col}"` : col).join(';'));
|
||||||
|
|
||||||
|
content = csv.join('\n');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'json':
|
||||||
|
mime = 'application/json';
|
||||||
|
content = JSON.stringify(args.content, null, 3);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = new Blob([content], { type: mime });
|
||||||
|
const downloadLink = document.createElement('a');
|
||||||
|
downloadLink.download = `${args.filename}.${args.type}`;
|
||||||
|
downloadLink.href = window.URL.createObjectURL(file);
|
||||||
|
downloadLink.style.display = 'none';
|
||||||
|
document.body.appendChild(downloadLink);
|
||||||
|
downloadLink.click();
|
||||||
|
downloadLink.remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
export default arrayToFile;
|
@@ -1239,6 +1239,18 @@ ace.define('ace/autocomplete/popup', ['require', 'exports', 'module', 'ace/virtu
|
|||||||
case 'view':
|
case 'view':
|
||||||
iconClass = 'mdi-table-eye';
|
iconClass = 'mdi-table-eye';
|
||||||
break;
|
break;
|
||||||
|
case 'trigger':
|
||||||
|
iconClass = 'mdi-table-cog';
|
||||||
|
break;
|
||||||
|
case 'routine':
|
||||||
|
iconClass = 'mdi-sync-circle';
|
||||||
|
break;
|
||||||
|
case 'function':
|
||||||
|
iconClass = 'mdi-arrow-right-bold-box';
|
||||||
|
break;
|
||||||
|
case 'scheduler':
|
||||||
|
iconClass = 'mdi-calendar-clock';
|
||||||
|
break;
|
||||||
case 'keyword':
|
case 'keyword':
|
||||||
iconClass = 'mdi-cube';
|
iconClass = 'mdi-cube';
|
||||||
break;
|
break;
|
||||||
|
@@ -212,6 +212,7 @@ body {
|
|||||||
|
|
||||||
.input-group .input-group-addon {
|
.input-group .input-group-addon {
|
||||||
border-color: #3f3f3f;
|
border-color: #3f3f3f;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu {
|
.menu {
|
||||||
@@ -225,7 +226,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.accordion-body {
|
.accordion-body {
|
||||||
max-height: 500rem !important;
|
max-height: 5000rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn.loading {
|
.btn.loading {
|
||||||
|
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import VuexPersist from 'vuex-persist';
|
|
||||||
|
|
||||||
import application from './modules/application.store';
|
import application from './modules/application.store';
|
||||||
import settings from './modules/settings.store';
|
import settings from './modules/settings.store';
|
||||||
@@ -12,15 +11,6 @@ import notifications from './modules/notifications.store';
|
|||||||
|
|
||||||
import ipcUpdates from './plugins/ipcUpdates';
|
import ipcUpdates from './plugins/ipcUpdates';
|
||||||
|
|
||||||
const vuexLocalStorage = new VuexPersist({
|
|
||||||
key: 'application', // The key to store the state on in the storage provider.
|
|
||||||
storage: window.localStorage,
|
|
||||||
reducer: state => ({
|
|
||||||
connections: state.connections,
|
|
||||||
settings: state.settings
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
@@ -33,7 +23,6 @@ export default new Vuex.Store({
|
|||||||
notifications
|
notifications
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
vuexLocalStorage.plugin,
|
|
||||||
ipcUpdates
|
ipcUpdates
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
@@ -11,6 +11,7 @@ export default {
|
|||||||
explorebar_size: persistentStore.get('explorebar_size') || null,
|
explorebar_size: persistentStore.get('explorebar_size') || null,
|
||||||
notifications_timeout: persistentStore.get('notifications_timeout') || 5,
|
notifications_timeout: persistentStore.get('notifications_timeout') || 5,
|
||||||
auto_complete: persistentStore.get('auto_complete') || true,
|
auto_complete: persistentStore.get('auto_complete') || 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'
|
||||||
},
|
},
|
||||||
@@ -19,6 +20,7 @@ export default {
|
|||||||
getExplorebarSize: state => state.explorebar_size,
|
getExplorebarSize: state => state.explorebar_size,
|
||||||
getNotificationsTimeout: state => state.notifications_timeout,
|
getNotificationsTimeout: state => state.notifications_timeout,
|
||||||
getAutoComplete: state => state.auto_complete,
|
getAutoComplete: state => state.auto_complete,
|
||||||
|
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
|
||||||
},
|
},
|
||||||
@@ -36,6 +38,10 @@ export default {
|
|||||||
state.auto_complete = val;
|
state.auto_complete = val;
|
||||||
persistentStore.set('auto_complete', state.auto_complete);
|
persistentStore.set('auto_complete', state.auto_complete);
|
||||||
},
|
},
|
||||||
|
SET_LINE_WRAP (state, val) {
|
||||||
|
state.line_wrap = val;
|
||||||
|
persistentStore.set('line_wrap', state.line_wrap);
|
||||||
|
},
|
||||||
SET_EXPLOREBAR_SIZE (state, size) {
|
SET_EXPLOREBAR_SIZE (state, size) {
|
||||||
state.explorebar_size = size;
|
state.explorebar_size = size;
|
||||||
persistentStore.set('explorebar_size', state.explorebar_size);
|
persistentStore.set('explorebar_size', state.explorebar_size);
|
||||||
@@ -57,6 +63,9 @@ export default {
|
|||||||
changeAutoComplete ({ commit }, val) {
|
changeAutoComplete ({ commit }, val) {
|
||||||
commit('SET_AUTO_COMPLETE', val);
|
commit('SET_AUTO_COMPLETE', val);
|
||||||
},
|
},
|
||||||
|
changeLineWrap ({ commit }, val) {
|
||||||
|
commit('SET_LINE_WRAP', val);
|
||||||
|
},
|
||||||
changeEditorTheme ({ commit }, theme) {
|
changeEditorTheme ({ commit }, theme) {
|
||||||
commit('SET_EDITOR_THEME', theme);
|
commit('SET_EDITOR_THEME', theme);
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import Connection from '@/ipc-api/Connection';
|
import Connection from '@/ipc-api/Connection';
|
||||||
import Database from '@/ipc-api/Database';
|
import Database from '@/ipc-api/Database';
|
||||||
|
import Users from '@/ipc-api/Users';
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
const tabIndex = [];
|
const tabIndex = [];
|
||||||
let lastBreadcrumbs = {};
|
let lastBreadcrumbs = {};
|
||||||
@@ -39,6 +40,9 @@ export default {
|
|||||||
.filter(workspace => workspace.connected)
|
.filter(workspace => workspace.connected)
|
||||||
.map(workspace => workspace.uid);
|
.map(workspace => workspace.uid);
|
||||||
},
|
},
|
||||||
|
getLoadedSchemas: state => uid => {
|
||||||
|
return state.workspaces.find(workspace => workspace.uid === uid).loaded_schemas;
|
||||||
|
},
|
||||||
isUnsavedDiscardModal: state => {
|
isUnsavedDiscardModal: state => {
|
||||||
return state.is_unsaved_discard_modal;
|
return state.is_unsaved_discard_modal;
|
||||||
}
|
}
|
||||||
@@ -64,6 +68,8 @@ export default {
|
|||||||
? {
|
? {
|
||||||
...workspace,
|
...workspace,
|
||||||
structure: {},
|
structure: {},
|
||||||
|
breadcrumbs: {},
|
||||||
|
loaded_schemas: new Set(),
|
||||||
connected: false
|
connected: false
|
||||||
}
|
}
|
||||||
: workspace);
|
: workspace);
|
||||||
@@ -76,6 +82,19 @@ export default {
|
|||||||
}
|
}
|
||||||
: workspace);
|
: workspace);
|
||||||
},
|
},
|
||||||
|
REFRESH_SCHEMA (state, { uid, schema, schemaElements }) {
|
||||||
|
state.workspaces = state.workspaces.map(workspace => {
|
||||||
|
if (workspace.uid === uid) {
|
||||||
|
const schemaIndex = workspace.structure.findIndex(s => s.name === schema);
|
||||||
|
|
||||||
|
if (schemaIndex !== -1)
|
||||||
|
workspace.structure[schemaIndex] = schemaElements;
|
||||||
|
else
|
||||||
|
workspace.structure.push(schemaElements);
|
||||||
|
}
|
||||||
|
return workspace;
|
||||||
|
});
|
||||||
|
},
|
||||||
REFRESH_COLLATIONS (state, { uid, collations }) {
|
REFRESH_COLLATIONS (state, { uid, collations }) {
|
||||||
state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
|
state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
|
||||||
? {
|
? {
|
||||||
@@ -100,6 +119,14 @@ export default {
|
|||||||
}
|
}
|
||||||
: workspace);
|
: workspace);
|
||||||
},
|
},
|
||||||
|
REFRESH_USERS (state, { uid, users }) {
|
||||||
|
state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
|
||||||
|
? {
|
||||||
|
...workspace,
|
||||||
|
users
|
||||||
|
}
|
||||||
|
: workspace);
|
||||||
|
},
|
||||||
ADD_WORKSPACE (state, workspace) {
|
ADD_WORKSPACE (state, workspace) {
|
||||||
state.workspaces.push(workspace);
|
state.workspaces.push(workspace);
|
||||||
},
|
},
|
||||||
@@ -189,6 +216,13 @@ export default {
|
|||||||
},
|
},
|
||||||
SET_PENDING_BREADCRUMBS (state, payload) {
|
SET_PENDING_BREADCRUMBS (state, payload) {
|
||||||
state.pending_breadcrumbs = payload;
|
state.pending_breadcrumbs = payload;
|
||||||
|
},
|
||||||
|
ADD_LOADED_SCHEMA (state, payload) {
|
||||||
|
state.workspaces = state.workspaces.map(workspace => {
|
||||||
|
if (workspace.uid === payload.uid)
|
||||||
|
workspace.loaded_schemas.add(payload.schema);
|
||||||
|
return workspace;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
@@ -221,15 +255,17 @@ export default {
|
|||||||
dispatch('refreshCollations', connection.uid);
|
dispatch('refreshCollations', connection.uid);
|
||||||
dispatch('refreshVariables', connection.uid);
|
dispatch('refreshVariables', connection.uid);
|
||||||
dispatch('refreshEngines', connection.uid);
|
dispatch('refreshEngines', connection.uid);
|
||||||
|
dispatch('refreshUsers', connection.uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
|
dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async refreshStructure ({ dispatch, commit }, uid) {
|
async refreshStructure ({ dispatch, commit, getters }, uid) {
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Database.getStructure(uid);
|
const { status, response } = await Database.getStructure({ uid, schemas: getters.getLoadedSchemas(uid) });
|
||||||
|
|
||||||
if (status === 'error')
|
if (status === 'error')
|
||||||
dispatch('notifications/addNotification', { status, message: response }, { root: true });
|
dispatch('notifications/addNotification', { status, message: response }, { root: true });
|
||||||
else
|
else
|
||||||
@@ -239,6 +275,18 @@ export default {
|
|||||||
dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
|
dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async refreshSchema ({ dispatch, commit }, { uid, schema }) {
|
||||||
|
try {
|
||||||
|
const { status, response } = await Database.getStructure({ uid, schemas: new Set([schema]) });
|
||||||
|
if (status === 'error')
|
||||||
|
dispatch('notifications/addNotification', { status, message: response }, { root: true });
|
||||||
|
else
|
||||||
|
commit('REFRESH_SCHEMA', { uid, schema, schemaElements: response.find(_schema => _schema.name === schema) });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
|
||||||
|
}
|
||||||
|
},
|
||||||
async refreshCollations ({ dispatch, commit }, uid) {
|
async refreshCollations ({ dispatch, commit }, uid) {
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Database.getCollations(uid);
|
const { status, response } = await Database.getCollations(uid);
|
||||||
@@ -275,6 +323,18 @@ export default {
|
|||||||
dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
|
dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async refreshUsers ({ dispatch, commit }, uid) {
|
||||||
|
try {
|
||||||
|
const { status, response } = await Users.getUsers(uid);
|
||||||
|
if (status === 'error')
|
||||||
|
dispatch('notifications/addNotification', { status, message: response }, { root: true });
|
||||||
|
else
|
||||||
|
commit('REFRESH_USERS', { uid, users: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
|
||||||
|
}
|
||||||
|
},
|
||||||
removeConnected ({ commit }, uid) {
|
removeConnected ({ commit }, uid) {
|
||||||
Connection.disconnect(uid);
|
Connection.disconnect(uid);
|
||||||
commit('REMOVE_CONNECTED', uid);
|
commit('REMOVE_CONNECTED', uid);
|
||||||
@@ -285,22 +345,13 @@ export default {
|
|||||||
uid,
|
uid,
|
||||||
connected: false,
|
connected: false,
|
||||||
selected_tab: 0,
|
selected_tab: 0,
|
||||||
tabs: [{
|
tabs: [],
|
||||||
uid: 'data',
|
|
||||||
type: 'table',
|
|
||||||
fields: [],
|
|
||||||
keyUsage: []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
uid: 'prop',
|
|
||||||
type: 'table',
|
|
||||||
fields: [],
|
|
||||||
keyUsage: []
|
|
||||||
}],
|
|
||||||
structure: {},
|
structure: {},
|
||||||
variables: [],
|
variables: [],
|
||||||
collations: [],
|
collations: [],
|
||||||
breadcrumbs: {}
|
users: [],
|
||||||
|
breadcrumbs: {},
|
||||||
|
loaded_schemas: new Set()
|
||||||
};
|
};
|
||||||
|
|
||||||
commit('ADD_WORKSPACE', workspace);
|
commit('ADD_WORKSPACE', workspace);
|
||||||
@@ -322,7 +373,9 @@ export default {
|
|||||||
table: null,
|
table: null,
|
||||||
trigger: null,
|
trigger: null,
|
||||||
procedure: null,
|
procedure: null,
|
||||||
scheduler: null
|
function: null,
|
||||||
|
scheduler: null,
|
||||||
|
view: null
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasLastChildren = Object.keys(lastBreadcrumbs).filter(b => b !== 'schema').some(b => lastBreadcrumbs[b]);
|
const hasLastChildren = Object.keys(lastBreadcrumbs).filter(b => b !== 'schema').some(b => lastBreadcrumbs[b]);
|
||||||
@@ -335,6 +388,9 @@ export default {
|
|||||||
|
|
||||||
commit('CHANGE_BREADCRUMBS', { uid: getters.getSelected, breadcrumbs: { ...breadcrumbsObj, ...payload } });
|
commit('CHANGE_BREADCRUMBS', { uid: getters.getSelected, breadcrumbs: { ...breadcrumbsObj, ...payload } });
|
||||||
lastBreadcrumbs = { ...breadcrumbsObj, ...payload };
|
lastBreadcrumbs = { ...breadcrumbsObj, ...payload };
|
||||||
|
|
||||||
|
if (payload.schema)
|
||||||
|
commit('ADD_LOADED_SCHEMA', { uid: getters.getSelected, schema: payload.schema });
|
||||||
},
|
},
|
||||||
newTab ({ commit }, uid) {
|
newTab ({ commit }, uid) {
|
||||||
const tab = uidGen('T');
|
const tab = uidGen('T');
|
||||||
|
Reference in New Issue
Block a user