mirror of
https://github.com/Fabio286/antares.git
synced 2025-06-05 21:59:22 +02:00
Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
5c21b67b3d | |||
08d5b1b329 | |||
61a42d51f5 | |||
d96907ca2d | |||
75bbd5f66e | |||
faa07a077c | |||
7ca69e51ae | |||
d868c772b9 | |||
a69bdeb20d | |||
51db2795bb | |||
2d278aa14e | |||
f74bca7bb4 | |||
978a7c5f5b | |||
0110756204 | |||
7df0cf8389 | |||
3aef7e953e | |||
a975df38dd | |||
a0a025e450 | |||
7cc5e7b67f | |||
e2ebb04a90 | |||
e579f37438 | |||
3829b94bf7 | |||
5c8ee66f43 | |||
b77e77f3bb | |||
1d5614278c | |||
8a20befd09 | |||
4133fc452f | |||
690a4541f9 |
2
.github/workflows/build-linux.yml
vendored
2
.github/workflows/build-linux.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
- name: Install Node.js, NPM and Yarn
|
- name: Install Node.js, NPM and Yarn
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
|
|
||||||
- name: Build/release Electron app
|
- name: Build/release Electron app
|
||||||
uses: samuelmeuli/action-electron-builder@v1
|
uses: samuelmeuli/action-electron-builder@v1
|
||||||
|
2
.github/workflows/build-mac.yml
vendored
2
.github/workflows/build-mac.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
- name: Install Node.js, NPM and Yarn
|
- name: Install Node.js, NPM and Yarn
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
|
|
||||||
- name: Build/release Electron app
|
- name: Build/release Electron app
|
||||||
uses: samuelmeuli/action-electron-builder@v1
|
uses: samuelmeuli/action-electron-builder@v1
|
||||||
|
2
.github/workflows/build-win.yml
vendored
2
.github/workflows/build-win.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
- name: Install Node.js, NPM and Yarn
|
- name: Install Node.js, NPM and Yarn
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
|
|
||||||
- name: Build/release Electron app
|
- name: Build/release Electron app
|
||||||
uses: samuelmeuli/action-electron-builder@v1
|
uses: samuelmeuli/action-electron-builder@v1
|
||||||
|
49
CHANGELOG.md
49
CHANGELOG.md
@@ -2,6 +2,55 @@
|
|||||||
|
|
||||||
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.2.0](https://github.com/Fabio286/antares/compare/v0.1.13...v0.2.0) (2021-07-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* contextual option to duplicate tables ([08d5b1b](https://github.com/Fabio286/antares/commit/08d5b1b3299adc5c53419f50892aee3b58e6fe72))
|
||||||
|
* **PostgreSQL:** trigger functions support ([75bbd5f](https://github.com/Fabio286/antares/commit/75bbd5f66e38a971482acff9dde636cf89858c8e))
|
||||||
|
* **PostgreSQL:** triggers creation ([7df0cf8](https://github.com/Fabio286/antares/commit/7df0cf83895ba784e30efa7814433cf329175364))
|
||||||
|
* **UI:** active tab animation ([978a7c5](https://github.com/Fabio286/antares/commit/978a7c5f5b314f2ba4067c86471755fdcc341b3f))
|
||||||
|
* **UI:** context option to copy cell or row value ([d868c77](https://github.com/Fabio286/antares/commit/d868c772b9e7c70e173d1f80ad8de51aa40991ef))
|
||||||
|
* **UI:** resizer border mouse hover animation ([a69bdeb](https://github.com/Fabio286/antares/commit/a69bdeb20db7d67613dace554ae37a0c5e112ec2))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **PostgreSQL:** error opening setting tab for some stored routines ([d96907c](https://github.com/Fabio286/antares/commit/d96907ca2de344114e3edae52843f6258468934b))
|
||||||
|
* **UI:** contextual sub-menu alignment when close to the lower edge of the window ([61a42d5](https://github.com/Fabio286/antares/commit/61a42d51f55ec3010c4ee67bad2f699ff14c9f7c))
|
||||||
|
* fields default not correctly set in table filler ([faa07a0](https://github.com/Fabio286/antares/commit/faa07a077c899af5c71124e0c20869876e57fd9a))
|
||||||
|
* rows loses internal id after export ([2d278aa](https://github.com/Fabio286/antares/commit/2d278aa14e0e5080725196538b7265ec00a7aebb))
|
||||||
|
* **UI:** editor blinking on first connection with a dark theme ([0110756](https://github.com/Fabio286/antares/commit/0110756204b79388535a74017b303e1ba41e8fd8))
|
||||||
|
* unhandled exception in connection test ([a975df3](https://github.com/Fabio286/antares/commit/a975df38dd471ab0ac8645a1021470a4600e6598))
|
||||||
|
* vulnerability in server error toast messages ([3aef7e9](https://github.com/Fabio286/antares/commit/3aef7e953ea82a9105d470cc62c68aacfc97f9d9))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* improved contextual menu appearance ([f74bca7](https://github.com/Fabio286/antares/commit/f74bca7bb4fc6802143446a44663fbf12319bf29))
|
||||||
|
* **UI:** increased application border-radius ([a0a025e](https://github.com/Fabio286/antares/commit/a0a025e4502141e95394ca9717b1ec65df2728c1))
|
||||||
|
|
||||||
|
### [0.1.13](https://github.com/Fabio286/antares/compare/v0.1.12...v0.1.13) (2021-06-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **PostgreSQL:** alter trigger support ([5c8ee66](https://github.com/Fabio286/antares/commit/5c8ee66f432585db7bd72103fa814558bf406bcc))
|
||||||
|
* **UI:** ability to manually insert page number in DATA tabs, closes [#65](https://github.com/Fabio286/antares/issues/65) ([e2ebb04](https://github.com/Fabio286/antares/commit/e2ebb04a9047d25187889644aa625fe675de808b))
|
||||||
|
* **UI:** option to change query editors font size, closes [#77](https://github.com/Fabio286/antares/issues/77) ([e579f37](https://github.com/Fabio286/antares/commit/e579f374381b329422b646b8f7ab5acf298db981))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** pool connections not released after MySQL errors, causing endless load animation ([4133fc4](https://github.com/Fabio286/antares/commit/4133fc452fc2e961eb587590e010f4968675db7e))
|
||||||
|
* **UI:** various fixes in displaying content with small window size ([8a20bef](https://github.com/Fabio286/antares/commit/8a20befd0941cb2e6d5e29552606454bd871b092))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* remove comments from queries before execution ([690a454](https://github.com/Fabio286/antares/commit/690a4541f96eaf831dea07b777a421729173b654))
|
||||||
|
|
||||||
### [0.1.12](https://github.com/Fabio286/antares/compare/v0.1.11...v0.1.12) (2021-06-09)
|
### [0.1.12](https://github.com/Fabio286/antares/compare/v0.1.11...v0.1.12) (2021-06-09)
|
||||||
|
|
||||||
|
|
||||||
|
18
package.json
18
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "antares",
|
"name": "antares",
|
||||||
"productName": "Antares",
|
"productName": "Antares",
|
||||||
"version": "0.1.12",
|
"version": "0.2.0",
|
||||||
"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",
|
||||||
@@ -87,14 +87,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/remote": "^1.1.0",
|
"@electron/remote": "^1.2.0",
|
||||||
"@mdi/font": "^5.9.55",
|
"@mdi/font": "^5.9.55",
|
||||||
"ace-builds": "^1.4.12",
|
"ace-builds": "^1.4.12",
|
||||||
"electron-log": "^4.3.5",
|
"electron-log": "^4.3.5",
|
||||||
"electron-store": "^8.0.0",
|
"electron-store": "^8.0.0",
|
||||||
"electron-updater": "^4.3.9",
|
"electron-updater": "^4.3.9",
|
||||||
"faker": "^5.5.3",
|
"faker": "^5.5.3",
|
||||||
"marked": "^2.0.7",
|
"marked": "^2.1.1",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"mysql2": "^2.2.5",
|
"mysql2": "^2.2.5",
|
||||||
"pg": "^8.5.1",
|
"pg": "^8.5.1",
|
||||||
@@ -108,20 +108,20 @@
|
|||||||
"vuex": "^3.6.2"
|
"vuex": "^3.6.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/eslint-parser": "^7.14.4",
|
"@babel/eslint-parser": "^7.14.5",
|
||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"electron": "^13.1.1",
|
"electron": "^13.1.2",
|
||||||
"electron-builder": "22.10.5",
|
"electron-builder": "^22.11.7",
|
||||||
"electron-devtools-installer": "^3.2.0",
|
"electron-devtools-installer": "^3.2.0",
|
||||||
"electron-webpack": "^2.8.2",
|
"electron-webpack": "^2.8.2",
|
||||||
"electron-webpack-vue": "^2.4.0",
|
"electron-webpack-vue": "^2.4.0",
|
||||||
"eslint": "^7.28.0",
|
"eslint": "^7.29.0",
|
||||||
"eslint-config-standard": "^16.0.3",
|
"eslint-config-standard": "^16.0.3",
|
||||||
"eslint-plugin-import": "^2.23.4",
|
"eslint-plugin-import": "^2.23.4",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^5.1.0",
|
"eslint-plugin-promise": "^5.1.0",
|
||||||
"eslint-plugin-vue": "^7.10.0",
|
"eslint-plugin-vue": "^7.11.1",
|
||||||
"sass": "^1.34.1",
|
"sass": "^1.35.1",
|
||||||
"sass-loader": "^10.2.0",
|
"sass-loader": "^10.2.0",
|
||||||
"standard-version": "^9.3.0",
|
"standard-version": "^9.3.0",
|
||||||
"stylelint": "^13.13.1",
|
"stylelint": "^13.13.1",
|
||||||
|
@@ -62,10 +62,14 @@ module.exports = {
|
|||||||
functionSql: false,
|
functionSql: false,
|
||||||
functionContext: false,
|
functionContext: false,
|
||||||
functionLanguage: false,
|
functionLanguage: false,
|
||||||
triggerMiltipleEvents: false,
|
triggerSql: false,
|
||||||
|
triggerStatementInCreation: false,
|
||||||
|
triggerMultipleEvents: false,
|
||||||
triggerTableInName: false,
|
triggerTableInName: false,
|
||||||
triggerUpdateColumns: false,
|
triggerUpdateColumns: false,
|
||||||
triggerOnlyRename: false,
|
triggerOnlyRename: false,
|
||||||
|
triggerFunctionSql: false,
|
||||||
|
triggerFunctionlanguages: false,
|
||||||
parametersLength: false,
|
parametersLength: false,
|
||||||
languages: false
|
languages: false
|
||||||
};
|
};
|
||||||
|
@@ -51,6 +51,7 @@ module.exports = {
|
|||||||
procedureDataAccess: true,
|
procedureDataAccess: true,
|
||||||
procedureSql: 'BEGIN\r\n\r\nEND',
|
procedureSql: 'BEGIN\r\n\r\nEND',
|
||||||
procedureContext: true,
|
procedureContext: true,
|
||||||
|
triggerSql: 'BEGIN\r\n\r\nEND',
|
||||||
functionDeterministic: true,
|
functionDeterministic: true,
|
||||||
functionDataAccess: true,
|
functionDataAccess: true,
|
||||||
functionSql: 'BEGIN\r\n\r\nEND',
|
functionSql: 'BEGIN\r\n\r\nEND',
|
||||||
|
@@ -14,32 +14,39 @@ module.exports = {
|
|||||||
tables: true,
|
tables: true,
|
||||||
views: true,
|
views: true,
|
||||||
triggers: true,
|
triggers: true,
|
||||||
|
triggerFunctions: true,
|
||||||
routines: true,
|
routines: true,
|
||||||
functions: true,
|
functions: true,
|
||||||
// Settings
|
// Settings
|
||||||
tableAdd: true,
|
tableAdd: true,
|
||||||
viewAdd: true,
|
viewAdd: true,
|
||||||
triggerAdd: false,
|
triggerAdd: true,
|
||||||
|
triggerFunctionAdd: true,
|
||||||
routineAdd: true,
|
routineAdd: true,
|
||||||
functionAdd: true,
|
functionAdd: true,
|
||||||
databaseEdit: false,
|
databaseEdit: false,
|
||||||
tableSettings: true,
|
tableSettings: true,
|
||||||
viewSettings: true,
|
viewSettings: true,
|
||||||
triggerSettings: true,
|
triggerSettings: true,
|
||||||
|
triggerFunctionSettings: true,
|
||||||
routineSettings: true,
|
routineSettings: true,
|
||||||
functionSettings: true,
|
functionSettings: true,
|
||||||
indexes: true,
|
indexes: true,
|
||||||
foreigns: true,
|
foreigns: true,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
tableArray: true,
|
tableArray: true,
|
||||||
procedureSql: '$BODY$\r\n\r\n$BODY$',
|
procedureSql: '$procedure$\r\n\r\n$procedure$',
|
||||||
procedureContext: true,
|
procedureContext: true,
|
||||||
procedureLanguage: true,
|
procedureLanguage: true,
|
||||||
functionSql: '$BODY$\r\n\r\n$BODY$',
|
functionSql: '$function$\r\n\r\n$function$',
|
||||||
|
triggerFunctionSql: '$function$\r\nBEGIN\r\n\r\nEND\r\n$function$',
|
||||||
|
triggerFunctionlanguages: ['plpgsql'],
|
||||||
functionContext: true,
|
functionContext: true,
|
||||||
functionLanguage: true,
|
functionLanguage: true,
|
||||||
|
triggerSql: 'EXECUTE PROCEDURE ',
|
||||||
|
triggerStatementInCreation: true,
|
||||||
triggerMultipleEvents: true,
|
triggerMultipleEvents: true,
|
||||||
triggerTableInName: true,
|
triggerTableInName: true,
|
||||||
triggerOnlyRename: true,
|
triggerOnlyRename: false,
|
||||||
languages: ['sql', 'plpgsql', 'c', 'internal']
|
languages: ['sql', 'plpgsql', 'c', 'internal']
|
||||||
};
|
};
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { app, BrowserWindow, nativeImage } from 'electron';
|
import { app, BrowserWindow, /* session, */ nativeImage } from 'electron';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { format as formatUrl } from 'url';
|
|
||||||
import Store from 'electron-store';
|
import Store from 'electron-store';
|
||||||
|
|
||||||
import ipcHandlers from './ipc-handlers';
|
import ipcHandlers from './ipc-handlers';
|
||||||
@@ -39,21 +38,18 @@ async function createMainWindow () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isDevelopment) {
|
if (isDevelopment) { //
|
||||||
await window.loadURL(`http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`);
|
await window.loadURL(`http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`);
|
||||||
|
|
||||||
const { default: installExtension, VUEJS_DEVTOOLS } = require('electron-devtools-installer');
|
// const { default: installExtension, VUEJS3_DEVTOOLS } = require('electron-devtools-installer');
|
||||||
|
|
||||||
const toolName = await installExtension(VUEJS_DEVTOOLS);
|
// const oldDevToolsID = session.defaultSession.getAllExtensions().find(ext => ext.name === 'Vue.js devtools').id;
|
||||||
console.log(toolName, 'installed');
|
// session.defaultSession.removeExtension(oldDevToolsID);
|
||||||
}
|
// const toolName = await installExtension(VUEJS3_DEVTOOLS);
|
||||||
else {
|
// console.log(toolName, 'installed');
|
||||||
await window.loadURL(formatUrl({
|
|
||||||
pathname: path.join(__dirname, 'index.html'),
|
|
||||||
protocol: 'file',
|
|
||||||
slashes: true
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
await window.loadURL(new URL(`file:///${path.join(__dirname, 'index.html')}`).href);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
@@ -29,9 +29,8 @@ export default connections => {
|
|||||||
params
|
params
|
||||||
});
|
});
|
||||||
|
|
||||||
await connection.connect();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
await connection.connect();
|
||||||
await connection.select('1+1').run();
|
await connection.select('1+1').run();
|
||||||
connection.destroy();
|
connection.destroy();
|
||||||
|
|
||||||
|
@@ -31,6 +31,16 @@ export default (connections) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('alter-trigger-function', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].alterTriggerFunction(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.handle('create-function', async (event, params) => {
|
ipcMain.handle('create-function', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
await connections[params.uid].createFunction(params);
|
await connections[params.uid].createFunction(params);
|
||||||
@@ -40,4 +50,14 @@ export default (connections) => {
|
|||||||
return { status: 'error', response: err.toString() };
|
return { status: 'error', response: err.toString() };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('create-trigger-function', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].createTriggerFunction(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@@ -424,6 +424,16 @@ export default (connections) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('duplicate-table', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].duplicateTable(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.handle('truncate-table', async (event, params) => {
|
ipcMain.handle('truncate-table', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
await connections[params.uid].truncateTable(params);
|
await connections[params.uid].truncateTable(params);
|
||||||
|
@@ -333,8 +333,15 @@ export class MySQLClient extends AntaresCore {
|
|||||||
|
|
||||||
const details = fieldArr.slice(2).join(' ');
|
const details = fieldArr.slice(2).join(' ');
|
||||||
let defaultValue = null;
|
let defaultValue = null;
|
||||||
if (details.includes('DEFAULT'))
|
if (details.includes('DEFAULT')) {
|
||||||
defaultValue = details.match(/(?<=DEFAULT ).*?$/gs)[0].split(' COMMENT')[0];
|
defaultValue = details.match(/(?<=DEFAULT ).*?$/gs)[0].split(' COMMENT')[0];
|
||||||
|
const defaultValueArr = defaultValue.split('');
|
||||||
|
if (defaultValueArr[0] === '\'') {
|
||||||
|
defaultValueArr.shift();
|
||||||
|
defaultValueArr.pop();
|
||||||
|
defaultValue = defaultValueArr.join('');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const typeAndLength = nameAndType[1].replace(')', '').split('(');
|
const typeAndLength = nameAndType[1].replace(')', '').split('(');
|
||||||
|
|
||||||
@@ -1236,6 +1243,17 @@ export class MySQLClient extends AntaresCore {
|
|||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DUPLICATE TABLE
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async duplicateTable (params) {
|
||||||
|
const sql = `CREATE TABLE \`${this._schema}\`.\`${params.table}_copy\` LIKE \`${this._schema}\`.\`${params.table}\``;
|
||||||
|
return await this.raw(sql);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TRUNCATE TABLE
|
* TRUNCATE TABLE
|
||||||
*
|
*
|
||||||
@@ -1325,6 +1343,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async raw (sql, args) {
|
async raw (sql, args) {
|
||||||
|
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');
|
||||||
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
|
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
@@ -1337,7 +1356,11 @@ export class MySQLClient extends AntaresCore {
|
|||||||
const nestTables = args.nest ? '.' : false;
|
const nestTables = args.nest ? '.' : false;
|
||||||
const resultsArr = [];
|
const resultsArr = [];
|
||||||
let paramsArr = [];
|
let paramsArr = [];
|
||||||
const queries = args.split ? sql.split(/((?:[^;'"]*(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*')[^;'"]*)+)|;/gm) : [sql];
|
const queries = args.split
|
||||||
|
? sql.split(/((?:[^;'"]*(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*')[^;'"]*)+)|;/gm)
|
||||||
|
.filter(Boolean)
|
||||||
|
.map(q => q.trim())
|
||||||
|
: [sql];
|
||||||
const isPool = typeof this._connection.getConnection === 'function';
|
const isPool = typeof this._connection.getConnection === 'function';
|
||||||
const connection = isPool ? await this._connection.getConnection() : this._connection;
|
const connection = isPool ? await this._connection.getConnection() : this._connection;
|
||||||
|
|
||||||
@@ -1424,7 +1447,10 @@ export class MySQLClient extends AntaresCore {
|
|||||||
fields: remappedFields,
|
fields: remappedFields,
|
||||||
keys: keysArr
|
keys: keysArr
|
||||||
});
|
});
|
||||||
}).catch(reject);
|
}).catch((err) => {
|
||||||
|
if (isPool) connection.release();
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
resultsArr.push({ rows, report, fields, keys, duration });
|
resultsArr.push({ rows, report, fields, keys, duration });
|
||||||
|
@@ -215,6 +215,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
functions: [],
|
functions: [],
|
||||||
procedures: [],
|
procedures: [],
|
||||||
triggers: [],
|
triggers: [],
|
||||||
|
triggerFunctions: [],
|
||||||
schedulers: []
|
schedulers: []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -535,7 +536,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
*/
|
*/
|
||||||
async dropTrigger (params) {
|
async dropTrigger (params) {
|
||||||
const triggerParts = params.trigger.split('.');
|
const triggerParts = params.trigger.split('.');
|
||||||
const sql = `DROP TRIGGER ${triggerParts[1]} ON ${triggerParts[0]}`;
|
const sql = `DROP TRIGGER "${triggerParts[1]}" ON "${triggerParts[0]}"`;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,21 +548,18 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
*/
|
*/
|
||||||
async alterTrigger (params) {
|
async alterTrigger (params) {
|
||||||
const { trigger } = params;
|
const { trigger } = params;
|
||||||
// const tempTrigger = Object.assign({}, trigger);
|
const tempTrigger = Object.assign({}, trigger);
|
||||||
// tempTrigger.name = `Antares_${tempTrigger.name}_tmp`;
|
tempTrigger.name = `Antares_${tempTrigger.name}_tmp`;
|
||||||
|
|
||||||
// try {
|
try {
|
||||||
// await this.createTrigger(tempTrigger);
|
await this.createTrigger(tempTrigger);
|
||||||
// await this.dropTrigger({ trigger: tempTrigger.name });
|
await this.dropTrigger({ trigger: `${tempTrigger.table}.${tempTrigger.name}` });
|
||||||
// await this.dropTrigger({ trigger: trigger.oldName });
|
await this.dropTrigger({ trigger: `${trigger.table}.${trigger.oldName}` });
|
||||||
// await this.createTrigger(trigger);
|
await this.createTrigger(trigger);
|
||||||
// }
|
}
|
||||||
// catch (err) {
|
catch (err) {
|
||||||
// return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
// }
|
}
|
||||||
|
|
||||||
const sql = `ALTER TRIGGER ${trigger.oldName} ON ${trigger.table} RENAME TO ${trigger.name}`;
|
|
||||||
return await this.raw(sql);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -571,7 +569,8 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
* @memberof PostgreSQLClient
|
* @memberof PostgreSQLClient
|
||||||
*/
|
*/
|
||||||
async createTrigger (trigger) {
|
async createTrigger (trigger) {
|
||||||
const sql = `CREATE ${trigger.definer ? `DEFINER=${trigger.definer} ` : ''}TRIGGER \`${trigger.name}\` ${trigger.event} ${trigger.activation} ON \`${trigger.table}\` FOR EACH ROW ${trigger.sql}`;
|
const eventsString = Array.isArray(trigger.event) ? trigger.event.join(' OR ') : trigger.event;
|
||||||
|
const sql = `CREATE TRIGGER "${trigger.name}" ${trigger.activation} ${eventsString} ON "${trigger.table}" FOR EACH ROW ${trigger.sql}`;
|
||||||
return await this.raw(sql, { split: false });
|
return await this.raw(sql, { split: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -625,7 +624,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
const parameters = results.rows.map(row => {
|
const parameters = results.rows.map(row => {
|
||||||
return {
|
return {
|
||||||
name: row.parameter_name,
|
name: row.parameter_name,
|
||||||
type: row.data_type.toUpperCase(),
|
type: row.data_type ? row.data_type.toUpperCase() : '',
|
||||||
length: '',
|
length: '',
|
||||||
context: row.parameter_mode
|
context: row.parameter_mode
|
||||||
};
|
};
|
||||||
@@ -824,7 +823,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
if (this._schema !== 'public')
|
if (this._schema !== 'public')
|
||||||
await this.use(this._schema);
|
await this.use(this._schema);
|
||||||
|
|
||||||
const body = func.returns ? func.sql : '$BODY$\n$BODY$';
|
const body = func.returns ? func.sql : '$function$\n$function$';
|
||||||
|
|
||||||
const sql = `CREATE FUNCTION ${this._schema}.${func.name}(${parameters})
|
const sql = `CREATE FUNCTION ${this._schema}.${func.name}(${parameters})
|
||||||
RETURNS ${func.returns || 'void'}
|
RETURNS ${func.returns || 'void'}
|
||||||
@@ -835,6 +834,48 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
return await this.raw(sql, { split: false });
|
return await this.raw(sql, { split: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ALTER TRIGGER FUNCTION
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof PostgreSQLClient
|
||||||
|
*/
|
||||||
|
async alterTriggerFunction (params) {
|
||||||
|
const { func } = params;
|
||||||
|
|
||||||
|
if (this._schema !== 'public')
|
||||||
|
await this.use(this._schema);
|
||||||
|
|
||||||
|
const body = func.returns ? func.sql : '$function$\n$function$';
|
||||||
|
|
||||||
|
const sql = `CREATE OR REPLACE FUNCTION ${this._schema}.${func.name}()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE ${func.language}
|
||||||
|
AS ${body}`;
|
||||||
|
|
||||||
|
return await this.raw(sql, { split: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CREATE TRIGGER FUNCTION
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof PostgreSQLClient
|
||||||
|
*/
|
||||||
|
async createTriggerFunction (func) {
|
||||||
|
if (this._schema !== 'public')
|
||||||
|
await this.use(this._schema);
|
||||||
|
|
||||||
|
const body = func.returns ? func.sql : '$function$\r\nBEGIN\r\n\r\nEND\r\n$function$';
|
||||||
|
|
||||||
|
const sql = `CREATE FUNCTION ${this._schema}.${func.name}()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE ${func.language}
|
||||||
|
AS ${body}`;
|
||||||
|
|
||||||
|
return await this.raw(sql, { split: false });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SELECT * FROM pg_collation
|
* SELECT * FROM pg_collation
|
||||||
*
|
*
|
||||||
@@ -1085,6 +1126,17 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DUPLICATE TABLE
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof PostgreSQLClient
|
||||||
|
*/
|
||||||
|
async duplicateTable (params) {
|
||||||
|
const sql = `CREATE TABLE ${this._schema}.${params.table}_copy (LIKE ${this._schema}.${params.table} INCLUDING ALL)`;
|
||||||
|
return await this.raw(sql);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TRUNCATE TABLE
|
* TRUNCATE TABLE
|
||||||
*
|
*
|
||||||
@@ -1174,6 +1226,8 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
* @memberof PostgreSQLClient
|
* @memberof PostgreSQLClient
|
||||||
*/
|
*/
|
||||||
async raw (sql, args) {
|
async raw (sql, args) {
|
||||||
|
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
nest: false,
|
nest: false,
|
||||||
details: false,
|
details: false,
|
||||||
@@ -1186,7 +1240,11 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
|
|
||||||
const resultsArr = [];
|
const resultsArr = [];
|
||||||
let paramsArr = [];
|
let paramsArr = [];
|
||||||
const queries = args.split ? sql.split(/(?!\B'[^']*);(?![^']*'\B)/gm) : [sql];
|
const queries = args.split
|
||||||
|
? sql.split(/(?!\B'[^']*);(?![^']*'\B)/gm)
|
||||||
|
.filter(Boolean)
|
||||||
|
.map(q => q.trim())
|
||||||
|
: [sql];
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
<TheScratchpad v-if="isScratchpad" />
|
<TheScratchpad v-if="isScratchpad" />
|
||||||
<ModalSettings v-if="isSettingModal" />
|
<ModalSettings v-if="isSettingModal" />
|
||||||
<ModalDiscardChanges v-if="isUnsavedDiscardModal" />
|
<ModalDiscardChanges v-if="isUnsavedDiscardModal" />
|
||||||
|
<BaseTextEditor class="d-none" value="" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -40,7 +41,8 @@ export default {
|
|||||||
ModalNewConnection: () => import(/* webpackChunkName: "ModalNewConnection" */'@/components/ModalNewConnection'),
|
ModalNewConnection: () => import(/* webpackChunkName: "ModalNewConnection" */'@/components/ModalNewConnection'),
|
||||||
ModalSettings: () => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings'),
|
ModalSettings: () => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings'),
|
||||||
TheScratchpad: () => import(/* webpackChunkName: "TheScratchpad" */'@/components/TheScratchpad'),
|
TheScratchpad: () => import(/* webpackChunkName: "TheScratchpad" */'@/components/TheScratchpad'),
|
||||||
ModalDiscardChanges: () => import(/* webpackChunkName: "ModalDiscardChanges" */'@/components/ModalDiscardChanges')
|
ModalDiscardChanges: () => import(/* webpackChunkName: "ModalDiscardChanges" */'@/components/ModalDiscardChanges'),
|
||||||
|
BaseTextEditor: () => import(/* webpackChunkName: "BaseTextEditor" */'@/components/BaseTextEditor')
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {};
|
return {};
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="context">
|
<div class="context" :class="{'bottom': isBottom}">
|
||||||
<a
|
<a
|
||||||
class="context-overlay"
|
class="context-overlay"
|
||||||
@click="close"
|
@click="close"
|
||||||
@@ -23,18 +23,21 @@ export default {
|
|||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
contextSize: null
|
contextSize: null,
|
||||||
|
isBottom: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
position () {
|
position () {
|
||||||
const { clientY, clientX } = this.contextEvent;
|
const { clientY, clientX } = this.contextEvent;
|
||||||
let topCord = `${clientY + 5}px`;
|
let topCord = `${clientY + 2}px`;
|
||||||
let leftCord = `${clientX + 5}px`;
|
let leftCord = `${clientX + 5}px`;
|
||||||
|
|
||||||
if (this.contextSize) {
|
if (this.contextSize) {
|
||||||
if (clientY + this.contextSize.height + 5 >= window.innerHeight)
|
if (clientY + (this.contextSize.height < 200 ? 200 : this.contextSize.height) + 5 >= window.innerHeight) {
|
||||||
topCord = `${clientY - this.contextSize.height}px`;
|
topCord = `${clientY + 3 - this.contextSize.height}px`;
|
||||||
|
this.isBottom = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (clientX + this.contextSize.width + 5 >= window.innerWidth)
|
if (clientX + this.contextSize.width + 5 >= window.innerWidth)
|
||||||
leftCord = `${clientX - this.contextSize.width}px`;
|
leftCord = `${clientX - this.contextSize.width}px`;
|
||||||
@@ -83,12 +86,21 @@ export default {
|
|||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
||||||
|
&:not(.bottom) .context-submenu {
|
||||||
|
top: -0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.bottom .context-submenu {
|
||||||
|
bottom: -0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.context-container {
|
.context-container {
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: #1d1d1d;
|
background: #1d1d1d;
|
||||||
border-radius: 0.1rem;
|
border-radius: $border-radius;
|
||||||
|
border: 1px solid $bg-color-light-dark;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -97,19 +109,24 @@ export default {
|
|||||||
.context-element {
|
.context-element {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin: 0.2rem;
|
||||||
padding: 0.1rem 0.3rem;
|
padding: 0.1rem 0.3rem;
|
||||||
|
border-radius: $border-radius;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
.context-submenu {
|
.context-submenu {
|
||||||
|
border-radius: $border-radius;
|
||||||
|
border: 1px solid $bg-color-light-dark;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 100%;
|
left: 100%;
|
||||||
top: 0;
|
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
|
background: #1d1d1d;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@@ -34,6 +34,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
editorTheme: 'settings/getEditorTheme',
|
editorTheme: 'settings/getEditorTheme',
|
||||||
|
editorFontSize: 'settings/getEditorFontSize',
|
||||||
autoComplete: 'settings/getAutoComplete',
|
autoComplete: 'settings/getAutoComplete',
|
||||||
lineWrap: 'settings/getLineWrap'
|
lineWrap: 'settings/getLineWrap'
|
||||||
})
|
})
|
||||||
@@ -47,6 +48,19 @@ export default {
|
|||||||
if (this.editor)
|
if (this.editor)
|
||||||
this.editor.setTheme(`ace/theme/${this.editorTheme}`);
|
this.editor.setTheme(`ace/theme/${this.editorTheme}`);
|
||||||
},
|
},
|
||||||
|
editorFontSize () {
|
||||||
|
const sizes = {
|
||||||
|
small: '12px',
|
||||||
|
medium: '14px',
|
||||||
|
large: '16px'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.setOptions({
|
||||||
|
fontSize: sizes[this.editorFontSize]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
autoComplete () {
|
autoComplete () {
|
||||||
if (this.editor) {
|
if (this.editor) {
|
||||||
this.editor.setOptions({
|
this.editor.setOptions({
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
class="toast mt-2"
|
class="toast mt-2"
|
||||||
:class="toastStatus.className"
|
:class="toastStatus.className"
|
||||||
>
|
>
|
||||||
<span class="p-vcentered text-left" v-html="`${toastStatus.iconTag} ${message}`" />
|
<span class="p-vcentered text-left"><i class="mdi mdi-24px mr-1" :class="toastStatus.iconName" /> {{ message }}</span>
|
||||||
<button class="btn btn-clear" @click="hideToast" />
|
<button class="btn btn-clear" @click="hideToast" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -30,27 +30,27 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
toastStatus () {
|
toastStatus () {
|
||||||
let className = '';
|
let className = '';
|
||||||
let iconTag = '';
|
let iconName = '';
|
||||||
switch (this.status) {
|
switch (this.status) {
|
||||||
case 'success':
|
case 'success':
|
||||||
className = 'toast-success';
|
className = 'toast-success';
|
||||||
iconTag = '<i class="mdi mdi-24px mdi-check mr-1"></i>';
|
iconName = 'mdi-check';
|
||||||
break;
|
break;
|
||||||
case 'error':
|
case 'error':
|
||||||
className = 'toast-error';
|
className = 'toast-error';
|
||||||
iconTag = '<i class="mdi mdi-24px mdi-alert-rhombus mr-1"></i>';
|
iconName = 'mdi-alert-rhombus';
|
||||||
break;
|
break;
|
||||||
case 'warning':
|
case 'warning':
|
||||||
className = 'toast-warning';
|
className = 'toast-warning';
|
||||||
iconTag = '<i class="mdi mdi-24px mdi-alert mr-1"></i>';
|
iconName = 'mdi-alert';
|
||||||
break;
|
break;
|
||||||
case 'primary':
|
case 'primary':
|
||||||
className = 'toast-primary';
|
className = 'toast-primary';
|
||||||
iconTag = '<i class="mdi mdi-24px mdi-information-outline mr-1"></i>';
|
iconName = 'mdi-information-outline';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { className, iconTag };
|
return { className, iconName };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@@ -63,7 +63,7 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.file-uploader {
|
.file-uploader {
|
||||||
border-radius: 0.1rem;
|
border-radius: $border-radius;
|
||||||
height: 1.8rem;
|
height: 1.8rem;
|
||||||
line-height: 1.2rem;
|
line-height: 1.2rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@@ -8,7 +8,8 @@
|
|||||||
>
|
>
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-play mr-1" /> {{ $t('word.parameters') }}: {{ localRoutine.name }}
|
<i class="mdi mdi-24px mdi-play mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.parameters') }}: {{ localRoutine.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div slot="body">
|
<div slot="body">
|
||||||
@@ -19,10 +20,10 @@
|
|||||||
:key="parameter._id"
|
:key="parameter._id"
|
||||||
class="form-group"
|
class="form-group"
|
||||||
>
|
>
|
||||||
<div class="col-3">
|
<div class="col-4">
|
||||||
<label class="form-label">{{ parameter.name }}</label>
|
<label class="form-label">{{ parameter.name }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-8">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input
|
<input
|
||||||
:ref="i === 0 ? 'firstInput' : ''"
|
:ref="i === 0 ? 'firstInput' : ''"
|
||||||
|
@@ -15,18 +15,18 @@
|
|||||||
<div class="panel-nav">
|
<div class="panel-nav">
|
||||||
<ul class="tab tab-block">
|
<ul class="tab tab-block">
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'general'}"
|
:class="{'active': selectedTab === 'general'}"
|
||||||
@click="selectTab('general')"
|
@click="selectTab('general')"
|
||||||
>
|
>
|
||||||
<a class="c-hand">{{ $t('word.general') }}</a>
|
<a class="tab-link">{{ $t('word.general') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'ssl'}"
|
:class="{'active': selectedTab === 'ssl'}"
|
||||||
@click="selectTab('ssl')"
|
@click="selectTab('ssl')"
|
||||||
>
|
>
|
||||||
<a class="c-hand">{{ $t('word.ssl') }}</a>
|
<a class="tab-link">{{ $t('word.ssl') }}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -5,7 +5,8 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-database-edit mr-1" /> {{ $t('message.editSchema') }}
|
<i class="mdi mdi-24px mdi-database-edit mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.editSchema') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
|
@@ -5,7 +5,8 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-playlist-plus mr-1" /> {{ $t('message.tableFiller') }}
|
<i class="mdi mdi-24px mdi-playlist-plus mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.tableFiller') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
@@ -251,32 +252,31 @@ export default {
|
|||||||
else {
|
else {
|
||||||
if ([...NUMBER, ...FLOAT].includes(field.type))
|
if ([...NUMBER, ...FLOAT].includes(field.type))
|
||||||
fieldDefault = Number.isNaN(+field.default) ? null : +field.default;
|
fieldDefault = Number.isNaN(+field.default) ? null : +field.default;
|
||||||
|
else if ([...TEXT, ...LONG_TEXT].includes(field.type)) {
|
||||||
if ([...TEXT, ...LONG_TEXT].includes(field.type)) {
|
|
||||||
fieldDefault = field.default
|
fieldDefault = field.default
|
||||||
? field.default.includes('\'')
|
? field.default.includes('\'')
|
||||||
? field.default.split('\'')[1]
|
? field.default.split('\'')[1]
|
||||||
: field.default
|
: field.default
|
||||||
: '';
|
: '';
|
||||||
}
|
}
|
||||||
|
else if ([...TIME, ...DATE].includes(field.type))
|
||||||
if ([...TIME, ...DATE].includes(field.type))
|
|
||||||
fieldDefault = field.default;
|
fieldDefault = field.default;
|
||||||
|
else if (BIT.includes(field.type))
|
||||||
if (BIT.includes(field.type))
|
|
||||||
fieldDefault = field.default.replaceAll('\'', '').replaceAll('b', '');
|
fieldDefault = field.default.replaceAll('\'', '').replaceAll('b', '');
|
||||||
|
else if (DATETIME.includes(field.type)) {
|
||||||
if (DATETIME.includes(field.type)) {
|
if (field.default && ['current_timestamp', 'now()'].includes(field.default.toLowerCase())) {
|
||||||
if (field.default && field.default.toLowerCase().includes('current_timestamp')) {
|
|
||||||
let datePrecision = '';
|
let datePrecision = '';
|
||||||
for (let i = 0; i < field.datePrecision; i++)
|
for (let i = 0; i < field.datePrecision; i++)
|
||||||
datePrecision += i === 0 ? '.S' : 'S';
|
datePrecision += i === 0 ? '.S' : 'S';
|
||||||
fieldDefault = moment().format(`YYYY-MM-DD HH:mm:ss${datePrecision}`);
|
fieldDefault = moment().format(`YYYY-MM-DD HH:mm:ss${datePrecision}`);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
fieldDefault = field.default;
|
||||||
}
|
}
|
||||||
|
else if (field.enumValues)
|
||||||
if (field.enumValues)
|
|
||||||
fieldDefault = field.enumValues.replaceAll('\'', '').split(',');
|
fieldDefault = field.enumValues.replaceAll('\'', '').split(',');
|
||||||
|
else
|
||||||
|
fieldDefault = field.default;
|
||||||
}
|
}
|
||||||
|
|
||||||
rowObj[field.name] = { value: fieldDefault };
|
rowObj[field.name] = { value: fieldDefault };
|
||||||
|
@@ -5,7 +5,8 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-server-plus mr-1" /> {{ $t('message.createNewConnection') }}
|
<i class="mdi mdi-24px mdi-server-plus mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.createNewConnection') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click="closeModal" />
|
<a class="btn btn-clear c-hand" @click="closeModal" />
|
||||||
@@ -15,18 +16,18 @@
|
|||||||
<div class="panel-nav">
|
<div class="panel-nav">
|
||||||
<ul class="tab tab-block">
|
<ul class="tab tab-block">
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'general'}"
|
:class="{'active': selectedTab === 'general'}"
|
||||||
@click="selectTab('general')"
|
@click="selectTab('general')"
|
||||||
>
|
>
|
||||||
<a class="c-hand">{{ $t('word.general') }}</a>
|
<a class="tab-link">{{ $t('word.general') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'ssl'}"
|
:class="{'active': selectedTab === 'ssl'}"
|
||||||
@click="selectTab('ssl')"
|
@click="selectTab('ssl')"
|
||||||
>
|
>
|
||||||
<a class="c-hand">{{ $t('word.ssl') }}</a>
|
<a class="tab-link">{{ $t('word.ssl') }}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -343,6 +344,7 @@ export default {
|
|||||||
else {
|
else {
|
||||||
try {
|
try {
|
||||||
const res = await Connection.makeTest(this.connection);
|
const res = await Connection.makeTest(this.connection);
|
||||||
|
console.log(res.response);
|
||||||
if (res.status === 'error')
|
if (res.status === 'error')
|
||||||
this.toast = { status: 'error', message: res.response.message };
|
this.toast = { status: 'error', message: res.response.message };
|
||||||
else
|
else
|
||||||
|
@@ -155,8 +155,8 @@ export default {
|
|||||||
if (this.customizations.languages)
|
if (this.customizations.languages)
|
||||||
this.localFunction.language = this.customizations.languages[0];
|
this.localFunction.language = this.customizations.languages[0];
|
||||||
|
|
||||||
if (this.customizations.procedureSql)
|
if (this.customizations.functionSql)
|
||||||
this.localFunction.sql = this.customizations.procedureSql;
|
this.localFunction.sql = this.customizations.functionSql;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$refs.firstInput.focus();
|
this.$refs.firstInput.focus();
|
||||||
}, 20);
|
}, 20);
|
||||||
|
@@ -5,7 +5,8 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-database-plus mr-1" /> {{ $t('message.createNewSchema') }}
|
<i class="mdi mdi-24px mdi-database-plus mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.createNewSchema') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
|
@@ -5,7 +5,8 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-playlist-plus mr-1" /> {{ $t('message.addNewRow') }}
|
<i class="mdi mdi-24px mdi-playlist-plus mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.addNewRow') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.definer" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.definer') }}
|
{{ $t('word.definer') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -75,26 +75,52 @@
|
|||||||
<option>BEFORE</option>
|
<option>BEFORE</option>
|
||||||
<option>AFTER</option>
|
<option>AFTER</option>
|
||||||
</select>
|
</select>
|
||||||
<select v-model="localTrigger.event" class="form-select">
|
<select
|
||||||
<option>INSERT</option>
|
v-if="!customizations.triggerMultipleEvents"
|
||||||
<option>UPDATE</option>
|
v-model="localTrigger.event"
|
||||||
<option>DELETE</option>
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option v-for="event in Object.keys(localEvents)" :key="event">
|
||||||
|
{{ event }}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
|
<div v-if="customizations.triggerMultipleEvents" class="px-4">
|
||||||
|
<label
|
||||||
|
v-for="event in Object.keys(localEvents)"
|
||||||
|
:key="event"
|
||||||
|
class="form-checkbox form-inline"
|
||||||
|
@change.prevent="changeEvents(event)"
|
||||||
|
>
|
||||||
|
<input :checked="localEvents[event]" type="checkbox"><i class="form-icon" /> {{ event }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<div v-if="customizations.triggerStatementInCreation" class="workspace-query-results column col-12 mt-2">
|
||||||
|
<label class="form-label ml-2">{{ $t('message.triggerStatement') }}</label>
|
||||||
|
<QueryEditor
|
||||||
|
ref="queryEditor"
|
||||||
|
:value.sync="localTrigger.sql"
|
||||||
|
:workspace="workspace"
|
||||||
|
:schema="schema"
|
||||||
|
:height="editorHeight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
import QueryEditor from '@/components/QueryEditor';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ModalNewTrigger',
|
name: 'ModalNewTrigger',
|
||||||
components: {
|
components: {
|
||||||
ConfirmModal
|
ConfirmModal,
|
||||||
|
QueryEditor
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
workspace: Object
|
workspace: Object
|
||||||
@@ -103,13 +129,15 @@ export default {
|
|||||||
return {
|
return {
|
||||||
localTrigger: {
|
localTrigger: {
|
||||||
definer: '',
|
definer: '',
|
||||||
sql: 'BEGIN\r\n\r\nEND',
|
sql: '',
|
||||||
name: '',
|
name: '',
|
||||||
table: '',
|
table: '',
|
||||||
activation: 'BEFORE',
|
activation: 'BEFORE',
|
||||||
event: 'INSERT'
|
event: 'INSERT'
|
||||||
},
|
},
|
||||||
isOptionsChanging: false
|
isOptionsChanging: false,
|
||||||
|
localEvents: { INSERT: false, UPDATE: false, DELETE: false },
|
||||||
|
editorHeight: 150
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -122,11 +150,16 @@ export default {
|
|||||||
.map(schema => schema.tables);
|
.map(schema => schema.tables);
|
||||||
|
|
||||||
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace.customizations;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
created () {
|
||||||
this.localTrigger.table = this.schemaTables.length ? this.schemaTables[0].name : '';
|
this.localTrigger.table = this.schemaTables.length ? this.schemaTables[0].name : '';
|
||||||
|
this.localTrigger.sql = this.customizations.triggerSql;
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$refs.firstInput.focus();
|
this.$refs.firstInput.focus();
|
||||||
}, 20);
|
}, 20);
|
||||||
@@ -134,6 +167,16 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
confirmNewTrigger () {
|
confirmNewTrigger () {
|
||||||
this.$emit('open-create-trigger-editor', this.localTrigger);
|
this.$emit('open-create-trigger-editor', this.localTrigger);
|
||||||
|
},
|
||||||
|
changeEvents (event) {
|
||||||
|
if (this.customizations.triggerMultipleEvents) {
|
||||||
|
this.localEvents[event] = !this.localEvents[event];
|
||||||
|
this.localTrigger.event = [];
|
||||||
|
for (const key in this.localEvents) {
|
||||||
|
if (this.localEvents[key])
|
||||||
|
this.localTrigger.event.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
132
src/renderer/components/ModalNewTriggerFunction.vue
Normal file
132
src/renderer/components/ModalNewTriggerFunction.vue
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<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.createNewFunction') }}
|
||||||
|
</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 v-if="customizations.languages" class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.language') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="localFunction.language" class="form-select">
|
||||||
|
<option v-for="language in customizations.triggerFunctionlanguages" :key="language">
|
||||||
|
{{ language }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.definer" 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 v-if="customizations.comment" 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>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalNewTriggerFunction',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
localFunction: {
|
||||||
|
definer: '',
|
||||||
|
sql: '',
|
||||||
|
name: '',
|
||||||
|
comment: '',
|
||||||
|
language: null
|
||||||
|
},
|
||||||
|
isOptionsChanging: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
schema () {
|
||||||
|
return this.workspace.breadcrumbs.schema;
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace.customizations;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
if (this.customizations.triggerFunctionlanguages)
|
||||||
|
this.localFunction.language = this.customizations.triggerFunctionlanguages[0];
|
||||||
|
|
||||||
|
if (this.customizations.triggerFunctionSql)
|
||||||
|
this.localFunction.sql = this.customizations.triggerFunctionSql;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmNewFunction () {
|
||||||
|
this.$emit('open-create-function-editor', this.localFunction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@@ -5,7 +5,8 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-memory mr-1" /> {{ $t('message.processesList') }}: {{ connectionName }}
|
<i class="mdi mdi-24px mdi-memory mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.processesList') }}: {{ connectionName }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-cog mr-1" />
|
<i class="mdi mdi-24px mdi-cog mr-1" />
|
||||||
{{ $t('word.settings') }}
|
<span class="cut-text">{{ $t('word.settings') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click="closeModal" />
|
<a class="btn btn-clear c-hand" @click="closeModal" />
|
||||||
@@ -16,40 +16,40 @@
|
|||||||
<div class="panel-nav">
|
<div class="panel-nav">
|
||||||
<ul class="tab tab-block">
|
<ul class="tab tab-block">
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'general'}"
|
:class="{'active': selectedTab === 'general'}"
|
||||||
@click="selectTab('general')"
|
@click="selectTab('general')"
|
||||||
>
|
>
|
||||||
<a class="c-hand">{{ $t('word.general') }}</a>
|
<a class="tab-link">{{ $t('word.general') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'themes'}"
|
:class="{'active': selectedTab === 'themes'}"
|
||||||
@click="selectTab('themes')"
|
@click="selectTab('themes')"
|
||||||
>
|
>
|
||||||
<a class="c-hand">{{ $t('word.themes') }}</a>
|
<a class="tab-link">{{ $t('word.themes') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
v-if="updateStatus !== 'disabled'"
|
v-if="updateStatus !== 'disabled'"
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'update'}"
|
:class="{'active': selectedTab === 'update'}"
|
||||||
@click="selectTab('update')"
|
@click="selectTab('update')"
|
||||||
>
|
>
|
||||||
<a class="c-hand" :class="{'badge badge-update': hasUpdates}">{{ $t('word.update') }}</a>
|
<a class="tab-link" :class="{'badge badge-update': hasUpdates}">{{ $t('word.update') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'changelog'}"
|
:class="{'active': selectedTab === 'changelog'}"
|
||||||
@click="selectTab('changelog')"
|
@click="selectTab('changelog')"
|
||||||
>
|
>
|
||||||
<a class="c-hand">{{ $t('word.changelog') }}</a>
|
<a class="tab-link">{{ $t('word.changelog') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'about'}"
|
:class="{'active': selectedTab === 'about'}"
|
||||||
@click="selectTab('about')"
|
@click="selectTab('about')"
|
||||||
>
|
>
|
||||||
<a class="c-hand">{{ $t('word.about') }}</a>
|
<a class="tab-link">{{ $t('word.about') }}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -222,6 +222,31 @@
|
|||||||
</optgroup>
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="column col-6 mb-4">
|
||||||
|
<div class="btn-group btn-group-block">
|
||||||
|
<button
|
||||||
|
class="btn btn-dark cut-text"
|
||||||
|
:class="{'active': editorFontSize === 'small'}"
|
||||||
|
@click="changeEditorFontSize('small')"
|
||||||
|
>
|
||||||
|
{{ $t('word.small') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-dark cut-text"
|
||||||
|
:class="{'active': editorFontSize === 'medium'}"
|
||||||
|
@click="changeEditorFontSize('medium')"
|
||||||
|
>
|
||||||
|
{{ $t('word.medium') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-dark cut-text"
|
||||||
|
:class="{'active': editorFontSize === 'large'}"
|
||||||
|
@click="changeEditorFontSize('large')"
|
||||||
|
>
|
||||||
|
{{ $t('word.large') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="column col-12">
|
<div class="column col-12">
|
||||||
<BaseTextEditor
|
<BaseTextEditor
|
||||||
:value="exampleQuery"
|
:value="exampleQuery"
|
||||||
@@ -346,6 +371,7 @@ export default {
|
|||||||
notificationsTimeout: 'settings/getNotificationsTimeout',
|
notificationsTimeout: 'settings/getNotificationsTimeout',
|
||||||
applicationTheme: 'settings/getApplicationTheme',
|
applicationTheme: 'settings/getApplicationTheme',
|
||||||
editorTheme: 'settings/getEditorTheme',
|
editorTheme: 'settings/getEditorTheme',
|
||||||
|
editorFontSize: 'settings/getEditorFontSize',
|
||||||
updateStatus: 'application/getUpdateStatus',
|
updateStatus: 'application/getUpdateStatus',
|
||||||
selectedWorkspace: 'workspaces/getSelected',
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
getWorkspace: 'workspaces/getWorkspace'
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
@@ -401,6 +427,7 @@ ORDER BY
|
|||||||
changeLineWrap: 'settings/changeLineWrap',
|
changeLineWrap: 'settings/changeLineWrap',
|
||||||
changeApplicationTheme: 'settings/changeApplicationTheme',
|
changeApplicationTheme: 'settings/changeApplicationTheme',
|
||||||
changeEditorTheme: 'settings/changeEditorTheme',
|
changeEditorTheme: 'settings/changeEditorTheme',
|
||||||
|
changeEditorFontSize: 'settings/changeEditorFontSize',
|
||||||
updateNotificationsTimeout: 'settings/updateNotificationsTimeout'
|
updateNotificationsTimeout: 'settings/updateNotificationsTimeout'
|
||||||
}),
|
}),
|
||||||
selectTab (tab) {
|
selectTab (tab) {
|
||||||
|
@@ -38,6 +38,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
editorTheme: 'settings/getEditorTheme',
|
editorTheme: 'settings/getEditorTheme',
|
||||||
|
editorFontSize: 'settings/getEditorFontSize',
|
||||||
autoComplete: 'settings/getAutoComplete',
|
autoComplete: 'settings/getAutoComplete',
|
||||||
lineWrap: 'settings/getLineWrap',
|
lineWrap: 'settings/getLineWrap',
|
||||||
baseCompleter: 'application/getBaseCompleter'
|
baseCompleter: 'application/getBaseCompleter'
|
||||||
@@ -158,6 +159,19 @@ export default {
|
|||||||
if (this.editor)
|
if (this.editor)
|
||||||
this.editor.setTheme(`ace/theme/${this.editorTheme}`);
|
this.editor.setTheme(`ace/theme/${this.editorTheme}`);
|
||||||
},
|
},
|
||||||
|
editorFontSize () {
|
||||||
|
const sizes = {
|
||||||
|
small: '12px',
|
||||||
|
medium: '14px',
|
||||||
|
large: '16px'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.setOptions({
|
||||||
|
fontSize: sizes[this.editorFontSize]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
autoComplete () {
|
autoComplete () {
|
||||||
if (this.editor) {
|
if (this.editor) {
|
||||||
this.editor.setOptions({
|
this.editor.setOptions({
|
||||||
@@ -173,8 +187,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
isSelected () {
|
isSelected () {
|
||||||
if (this.isSelected)
|
if (this.isSelected) {
|
||||||
this.lastSchema = this.schema;
|
this.lastSchema = this.schema;
|
||||||
|
this.editor.resize();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
height () {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.editor.resize();
|
||||||
|
}, 20);
|
||||||
},
|
},
|
||||||
lastSchema () {
|
lastSchema () {
|
||||||
if (this.editor) {
|
if (this.editor) {
|
||||||
|
@@ -174,6 +174,7 @@ export default {
|
|||||||
align-content: center;
|
align-content: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@@ -181,6 +182,21 @@ export default {
|
|||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
height: $settingbar-width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
height: 0;
|
||||||
|
width: 3px;
|
||||||
|
transition: height 0.2s;
|
||||||
|
background-color: $primary-color;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
border-radius: $border-radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settingbar-element-icon {
|
.settingbar-element-icon {
|
||||||
@@ -211,7 +227,7 @@ export default {
|
|||||||
padding: 0.2rem 0.4rem;
|
padding: 0.2rem 0.4rem;
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
background: rgba(48, 55, 66, 0.95);
|
background: rgba(48, 55, 66, 0.95);
|
||||||
border-radius: 0.1rem;
|
border-radius: $border-radius;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
max-width: 320px;
|
max-width: 320px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
@@ -127,6 +127,12 @@
|
|||||||
:connection="connection"
|
:connection="connection"
|
||||||
:function="workspace.breadcrumbs.function"
|
:function="workspace.breadcrumbs.function"
|
||||||
/>
|
/>
|
||||||
|
<WorkspacePropsTabTriggerFunction
|
||||||
|
v-show="selectedTab === 'prop' && workspace.breadcrumbs.triggerFunction"
|
||||||
|
:is-selected="selectedTab === 'prop'"
|
||||||
|
:connection="connection"
|
||||||
|
:function="workspace.breadcrumbs.triggerFunction"
|
||||||
|
/>
|
||||||
<WorkspacePropsTabScheduler
|
<WorkspacePropsTabScheduler
|
||||||
v-show="selectedTab === 'prop' && workspace.breadcrumbs.scheduler"
|
v-show="selectedTab === 'prop' && workspace.breadcrumbs.scheduler"
|
||||||
:is-selected="selectedTab === 'prop'"
|
:is-selected="selectedTab === 'prop'"
|
||||||
@@ -165,6 +171,7 @@ import WorkspacePropsTabView from '@/components/WorkspacePropsTabView';
|
|||||||
import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger';
|
import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger';
|
||||||
import WorkspacePropsTabRoutine from '@/components/WorkspacePropsTabRoutine';
|
import WorkspacePropsTabRoutine from '@/components/WorkspacePropsTabRoutine';
|
||||||
import WorkspacePropsTabFunction from '@/components/WorkspacePropsTabFunction';
|
import WorkspacePropsTabFunction from '@/components/WorkspacePropsTabFunction';
|
||||||
|
import WorkspacePropsTabTriggerFunction from '@/components/WorkspacePropsTabTriggerFunction';
|
||||||
import WorkspacePropsTabScheduler from '@/components/WorkspacePropsTabScheduler';
|
import WorkspacePropsTabScheduler from '@/components/WorkspacePropsTabScheduler';
|
||||||
import ModalProcessesList from '@/components/ModalProcessesList';
|
import ModalProcessesList from '@/components/ModalProcessesList';
|
||||||
|
|
||||||
@@ -179,6 +186,7 @@ export default {
|
|||||||
WorkspacePropsTabTrigger,
|
WorkspacePropsTabTrigger,
|
||||||
WorkspacePropsTabRoutine,
|
WorkspacePropsTabRoutine,
|
||||||
WorkspacePropsTabFunction,
|
WorkspacePropsTabFunction,
|
||||||
|
WorkspacePropsTabTriggerFunction,
|
||||||
WorkspacePropsTabScheduler,
|
WorkspacePropsTabScheduler,
|
||||||
ModalProcessesList
|
ModalProcessesList
|
||||||
},
|
},
|
||||||
@@ -208,6 +216,7 @@ export default {
|
|||||||
if (this.workspace.breadcrumbs.trigger && this.workspace.customizations.triggerSettings) return true;
|
if (this.workspace.breadcrumbs.trigger && this.workspace.customizations.triggerSettings) return true;
|
||||||
if (this.workspace.breadcrumbs.procedure && this.workspace.customizations.routineSettings) return true;
|
if (this.workspace.breadcrumbs.procedure && this.workspace.customizations.routineSettings) return true;
|
||||||
if (this.workspace.breadcrumbs.function && this.workspace.customizations.functionSettings) return true;
|
if (this.workspace.breadcrumbs.function && this.workspace.customizations.functionSettings) return true;
|
||||||
|
if (this.workspace.breadcrumbs.triggerFunction && this.workspace.customizations.functionSettings) return true;
|
||||||
if (this.workspace.breadcrumbs.scheduler && this.workspace.customizations.schedulerSettings) return true;
|
if (this.workspace.breadcrumbs.scheduler && this.workspace.customizations.schedulerSettings) return true;
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
@@ -219,6 +228,7 @@ export default {
|
|||||||
this.workspace.breadcrumbs.trigger === null &&
|
this.workspace.breadcrumbs.trigger === null &&
|
||||||
this.workspace.breadcrumbs.procedure === null &&
|
this.workspace.breadcrumbs.procedure === null &&
|
||||||
this.workspace.breadcrumbs.function === null &&
|
this.workspace.breadcrumbs.function === null &&
|
||||||
|
this.workspace.breadcrumbs.triggerFunction === null &&
|
||||||
this.workspace.breadcrumbs.scheduler === null &&
|
this.workspace.breadcrumbs.scheduler === null &&
|
||||||
['data', 'prop'].includes(this.workspace.selected_tab)
|
['data', 'prop'].includes(this.workspace.selected_tab)
|
||||||
) ||
|
) ||
|
||||||
@@ -362,7 +372,7 @@ export default {
|
|||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
|
|
||||||
.menu-item a {
|
.menu-item a {
|
||||||
border-radius: 0.1rem;
|
border-radius: $border-radius;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0 -0.4rem;
|
margin: 0 -0.4rem;
|
||||||
|
@@ -90,6 +90,12 @@
|
|||||||
@close="hideCreateFunctionModal"
|
@close="hideCreateFunctionModal"
|
||||||
@open-create-function-editor="openCreateFunctionEditor"
|
@open-create-function-editor="openCreateFunctionEditor"
|
||||||
/>
|
/>
|
||||||
|
<ModalNewTriggerFunction
|
||||||
|
v-if="isNewTriggerFunctionModal"
|
||||||
|
:workspace="workspace"
|
||||||
|
@close="hideCreateTriggerFunctionModal"
|
||||||
|
@open-create-function-editor="openCreateTriggerFunctionEditor"
|
||||||
|
/>
|
||||||
<ModalNewScheduler
|
<ModalNewScheduler
|
||||||
v-if="isNewSchedulerModal"
|
v-if="isNewSchedulerModal"
|
||||||
:workspace="workspace"
|
:workspace="workspace"
|
||||||
@@ -106,6 +112,7 @@
|
|||||||
@show-create-trigger-modal="showCreateTriggerModal"
|
@show-create-trigger-modal="showCreateTriggerModal"
|
||||||
@show-create-routine-modal="showCreateRoutineModal"
|
@show-create-routine-modal="showCreateRoutineModal"
|
||||||
@show-create-function-modal="showCreateFunctionModal"
|
@show-create-function-modal="showCreateFunctionModal"
|
||||||
|
@show-create-trigger-function-modal="showCreateTriggerFunctionModal"
|
||||||
@show-create-scheduler-modal="showCreateSchedulerModal"
|
@show-create-scheduler-modal="showCreateSchedulerModal"
|
||||||
@reload="refresh"
|
@reload="refresh"
|
||||||
/>
|
/>
|
||||||
@@ -147,6 +154,7 @@ import ModalNewView from '@/components/ModalNewView';
|
|||||||
import ModalNewTrigger from '@/components/ModalNewTrigger';
|
import ModalNewTrigger from '@/components/ModalNewTrigger';
|
||||||
import ModalNewRoutine from '@/components/ModalNewRoutine';
|
import ModalNewRoutine from '@/components/ModalNewRoutine';
|
||||||
import ModalNewFunction from '@/components/ModalNewFunction';
|
import ModalNewFunction from '@/components/ModalNewFunction';
|
||||||
|
import ModalNewTriggerFunction from '@/components/ModalNewTriggerFunction';
|
||||||
import ModalNewScheduler from '@/components/ModalNewScheduler';
|
import ModalNewScheduler from '@/components/ModalNewScheduler';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -163,6 +171,7 @@ export default {
|
|||||||
ModalNewTrigger,
|
ModalNewTrigger,
|
||||||
ModalNewRoutine,
|
ModalNewRoutine,
|
||||||
ModalNewFunction,
|
ModalNewFunction,
|
||||||
|
ModalNewTriggerFunction,
|
||||||
ModalNewScheduler
|
ModalNewScheduler
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
@@ -179,6 +188,7 @@ export default {
|
|||||||
isNewTriggerModal: false,
|
isNewTriggerModal: false,
|
||||||
isNewRoutineModal: false,
|
isNewRoutineModal: false,
|
||||||
isNewFunctionModal: false,
|
isNewFunctionModal: false,
|
||||||
|
isNewTriggerFunctionModal: false,
|
||||||
isNewSchedulerModal: false,
|
isNewSchedulerModal: false,
|
||||||
|
|
||||||
localWidth: null,
|
localWidth: null,
|
||||||
@@ -209,6 +219,9 @@ export default {
|
|||||||
},
|
},
|
||||||
connectionName () {
|
connectionName () {
|
||||||
return this.getConnectionName(this.connection.uid);
|
return this.getConnectionName(this.connection.uid);
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace.customizations;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -363,7 +376,8 @@ export default {
|
|||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
await this.refresh();
|
await this.refresh();
|
||||||
this.changeBreadcrumbs({ schema: this.selectedDatabase, trigger: payload.name });
|
const triggerName = this.customizations.triggerTableInName ? `${payload.table}.${payload.name}` : payload.name;
|
||||||
|
this.changeBreadcrumbs({ schema: this.selectedDatabase, trigger: triggerName });
|
||||||
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
|
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -399,6 +413,13 @@ export default {
|
|||||||
hideCreateFunctionModal () {
|
hideCreateFunctionModal () {
|
||||||
this.isNewFunctionModal = false;
|
this.isNewFunctionModal = false;
|
||||||
},
|
},
|
||||||
|
showCreateTriggerFunctionModal () {
|
||||||
|
this.closeDatabaseContext();
|
||||||
|
this.isNewTriggerFunctionModal = true;
|
||||||
|
},
|
||||||
|
hideCreateTriggerFunctionModal () {
|
||||||
|
this.isNewTriggerFunctionModal = false;
|
||||||
|
},
|
||||||
showCreateSchedulerModal () {
|
showCreateSchedulerModal () {
|
||||||
this.closeDatabaseContext();
|
this.closeDatabaseContext();
|
||||||
this.isNewSchedulerModal = true;
|
this.isNewSchedulerModal = true;
|
||||||
@@ -422,6 +443,22 @@ export default {
|
|||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
this.addNotification({ status: 'error', message: response });
|
||||||
},
|
},
|
||||||
|
async openCreateTriggerFunctionEditor (payload) {
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
...payload
|
||||||
|
};
|
||||||
|
|
||||||
|
const { status, response } = await Functions.createTriggerFunction(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
await this.refresh();
|
||||||
|
this.changeBreadcrumbs({ schema: this.selectedDatabase, triggerFunction: payload.name });
|
||||||
|
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
},
|
||||||
async openCreateSchedulerEditor (payload) {
|
async openCreateSchedulerEditor (payload) {
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
@@ -451,6 +488,11 @@ export default {
|
|||||||
height: calc(100vh - #{$excluding-size});
|
height: calc(100vh - #{$excluding-size});
|
||||||
cursor: ew-resize;
|
cursor: ew-resize;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
|
transition: background 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba($primary-color, 50%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspace-explorebar {
|
.workspace-explorebar {
|
||||||
|
@@ -20,7 +20,8 @@
|
|||||||
>
|
>
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-delete mr-1" /> {{ deleteMessage }}
|
<i class="mdi mdi-24px mdi-delete mr-1" />
|
||||||
|
<span class="cut-text">{{ deleteMessage }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div slot="body">
|
<div slot="body">
|
||||||
@@ -83,6 +84,7 @@ export default {
|
|||||||
case 'procedure':
|
case 'procedure':
|
||||||
return this.$t('message.deleteRoutine');
|
return this.$t('message.deleteRoutine');
|
||||||
case 'function':
|
case 'function':
|
||||||
|
case 'triggerFunction':
|
||||||
return this.$t('message.deleteFunction');
|
return this.$t('message.deleteFunction');
|
||||||
case 'scheduler':
|
case 'scheduler':
|
||||||
return this.$t('message.deleteScheduler');
|
return this.$t('message.deleteScheduler');
|
||||||
@@ -134,6 +136,7 @@ export default {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'function':
|
case 'function':
|
||||||
|
case 'triggerFunction':
|
||||||
res = await Functions.dropFunction({
|
res = await Functions.dropFunction({
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
func: this.selectedMisc.name
|
func: this.selectedMisc.name
|
||||||
|
@@ -93,6 +93,34 @@
|
|||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="filteredTriggerFunctions.length && customizations.triggerFunctions" class="database-misc">
|
||||||
|
<details class="accordion">
|
||||||
|
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.triggerFunction}">
|
||||||
|
<i class="misc-icon mdi mdi-18px mdi-folder-refresh mr-1" />
|
||||||
|
{{ $tc('word.triggerFunction', 2) }}
|
||||||
|
</summary>
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div>
|
||||||
|
<ul class="menu menu-nav pt-0">
|
||||||
|
<li
|
||||||
|
v-for="(func, i) of filteredTriggerFunctions"
|
||||||
|
:key="`${func.name}-${i}`"
|
||||||
|
class="menu-item"
|
||||||
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.triggerFunction === func.name}"
|
||||||
|
@click="setBreadcrumbs({schema: database.name, triggerFunction: func.name})"
|
||||||
|
@contextmenu.prevent="showMiscContext($event, {...func, type: 'triggerFunction'})"
|
||||||
|
>
|
||||||
|
<a class="table-name">
|
||||||
|
<i class="table-icon mdi mdi-cog-clockwise mdi-18px mr-1" />
|
||||||
|
<span v-html="highlightWord(func.name)" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="filteredFunctions.length && customizations.functions" class="database-misc">
|
<div v-if="filteredFunctions.length && customizations.functions" class="database-misc">
|
||||||
<details class="accordion">
|
<details class="accordion">
|
||||||
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function}">
|
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function}">
|
||||||
@@ -189,6 +217,11 @@ export default {
|
|||||||
filteredFunctions () {
|
filteredFunctions () {
|
||||||
return this.database.functions.filter(func => func.name.search(this.searchTerm) >= 0);
|
return this.database.functions.filter(func => func.name.search(this.searchTerm) >= 0);
|
||||||
},
|
},
|
||||||
|
filteredTriggerFunctions () {
|
||||||
|
return this.database.triggerFunctions
|
||||||
|
? this.database.triggerFunctions.filter(func => func.name.search(this.searchTerm) >= 0)
|
||||||
|
: [];
|
||||||
|
},
|
||||||
filteredSchedulers () {
|
filteredSchedulers () {
|
||||||
return this.database.schedulers.filter(scheduler => scheduler.name.search(this.searchTerm) >= 0);
|
return this.database.schedulers.filter(scheduler => scheduler.name.search(this.searchTerm) >= 0);
|
||||||
},
|
},
|
||||||
@@ -311,7 +344,7 @@ export default {
|
|||||||
.database-name,
|
.database-name,
|
||||||
.misc-name {
|
.misc-name {
|
||||||
&:hover {
|
&:hover {
|
||||||
border-radius: 2px;
|
border-radius: $border-radius;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +353,7 @@ export default {
|
|||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
border-radius: 2px;
|
border-radius: $border-radius;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -42,6 +42,13 @@
|
|||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.function', 1) }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.function', 1) }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="workspace.customizations.triggerFunctionAdd"
|
||||||
|
class="context-element"
|
||||||
|
@click="showCreateTriggerFunctionModal"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-cog-clockwise pr-1" /> {{ $tc('word.triggerFunction', 1) }}</span>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="workspace.customizations.schedulerAdd"
|
v-if="workspace.customizations.schedulerAdd"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@@ -69,7 +76,8 @@
|
|||||||
>
|
>
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-database-remove mr-1" /> {{ $t('message.deleteSchema') }}
|
<i class="mdi mdi-24px mdi-database-remove mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.deleteSchema') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div slot="body">
|
<div slot="body">
|
||||||
@@ -139,6 +147,9 @@ export default {
|
|||||||
showCreateFunctionModal () {
|
showCreateFunctionModal () {
|
||||||
this.$emit('show-create-function-modal');
|
this.$emit('show-create-function-modal');
|
||||||
},
|
},
|
||||||
|
showCreateTriggerFunctionModal () {
|
||||||
|
this.$emit('show-create-trigger-function-modal');
|
||||||
|
},
|
||||||
showCreateSchedulerModal () {
|
showCreateSchedulerModal () {
|
||||||
this.$emit('show-create-scheduler-modal');
|
this.$emit('show-create-scheduler-modal');
|
||||||
},
|
},
|
||||||
|
@@ -3,6 +3,13 @@
|
|||||||
:context-event="contextEvent"
|
:context-event="contextEvent"
|
||||||
@close-context="closeContext"
|
@close-context="closeContext"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
v-if="selectedTable.type === 'table'"
|
||||||
|
class="context-element"
|
||||||
|
@click="duplicateTable"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table-multiple text-light pr-1" /> {{ $t('message.duplicateTable') }}</span>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="selectedTable.type === 'table'"
|
v-if="selectedTable.type === 'table'"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@@ -21,7 +28,7 @@
|
|||||||
>
|
>
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-table-off mr-1" /> {{ $t('message.emptyTable') }}
|
<i class="mdi mdi-24px mdi-table-off mr-1" /> <span class="cut-text">{{ $t('message.emptyTable') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div slot="body">
|
<div slot="body">
|
||||||
@@ -37,7 +44,8 @@
|
|||||||
>
|
>
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-table-remove mr-1" /> {{ selectedTable.type === 'table' ? $t('message.deleteTable') : $t('message.deleteView') }}
|
<i class="mdi mdi-24px mdi-table-remove mr-1" />
|
||||||
|
<span class="cut-text">{{ selectedTable.type === 'table' ? $t('message.deleteTable') : $t('message.deleteView') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div slot="body">
|
<div slot="body">
|
||||||
@@ -104,6 +112,24 @@ export default {
|
|||||||
closeContext () {
|
closeContext () {
|
||||||
this.$emit('close-context');
|
this.$emit('close-context');
|
||||||
},
|
},
|
||||||
|
async duplicateTable () {
|
||||||
|
try {
|
||||||
|
const { status, response } = await Tables.duplicateTable({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
table: this.selectedTable.name
|
||||||
|
});
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
this.closeContext();
|
||||||
|
this.$emit('reload');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
},
|
||||||
async emptyTable () {
|
async emptyTable () {
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Tables.truncateTable({
|
const { status, response } = await Tables.truncateTable({
|
||||||
|
@@ -8,7 +8,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-key-link mr-1" /> {{ $t('word.foreignKeys') }} "{{ table }}"
|
<i class="mdi mdi-24px mdi-key-link mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.foreignKeys') }} "{{ table }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -361,7 +362,7 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.tile {
|
.tile {
|
||||||
border-radius: 2px;
|
border-radius: $border-radius;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
|
@@ -7,7 +7,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-cogs mr-1" /> {{ $t('word.options') }} "{{ localOptions.name }}"
|
<i class="mdi mdi-24px mdi-cogs mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.options') }} "{{ localOptions.name }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
|
@@ -8,7 +8,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" /> {{ $t('word.parameters') }} "{{ func }}"
|
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.parameters') }} "{{ func }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -273,7 +274,7 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.tile {
|
.tile {
|
||||||
border-radius: 2px;
|
border-radius: $border-radius;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
|
@@ -8,7 +8,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-key mdi-rotate-45 mr-1" /> {{ $t('word.indexes') }} "{{ table }}"
|
<i class="mdi mdi-24px mdi-key mdi-rotate-45 mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.indexes') }} "{{ table }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -246,7 +247,7 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.tile {
|
.tile {
|
||||||
border-radius: 2px;
|
border-radius: $border-radius;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
|
@@ -7,7 +7,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-cogs mr-1" /> {{ $t('word.options') }} "{{ table }}"
|
<i class="mdi mdi-24px mdi-cogs mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.options') }} "{{ table }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -47,6 +48,7 @@
|
|||||||
v-model="optionsProxy.autoIncrement"
|
v-model="optionsProxy.autoIncrement"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
type="number"
|
type="number"
|
||||||
|
:disabled="optionsProxy.autoIncrement === null"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -7,7 +7,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-cogs mr-1" /> {{ $t('word.options') }} "{{ localOptions.name }}"
|
<i class="mdi mdi-24px mdi-cogs mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.options') }} "{{ localOptions.name }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
|
@@ -8,7 +8,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" /> {{ $t('word.parameters') }} "{{ routine }}"
|
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.parameters') }} "{{ routine }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -273,7 +274,7 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.tile {
|
.tile {
|
||||||
border-radius: 2px;
|
border-radius: $border-radius;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
|
@@ -7,7 +7,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-timer mr-1" /> {{ $t('word.timing') }} "{{ localOptions.name }}"
|
<i class="mdi mdi-24px mdi-timer mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.timing') }} "{{ localOptions.name }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
|
@@ -137,7 +137,9 @@ export default {
|
|||||||
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
|
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
|
||||||
},
|
},
|
||||||
isDefinerInUsers () {
|
isDefinerInUsers () {
|
||||||
return this.originalFunction ? this.workspace.users.some(user => this.originalFunction.definer === `\`${user.name}\`@\`${user.host}\``) : true;
|
return this.originalFunction
|
||||||
|
? this.workspace.users.some(user => this.originalFunction.definer === `\`${user.name}\`@\`${user.host}\``)
|
||||||
|
: true;
|
||||||
},
|
},
|
||||||
schemaTables () {
|
schemaTables () {
|
||||||
const schemaTables = this.workspace.structure
|
const schemaTables = this.workspace.structure
|
||||||
|
315
src/renderer/components/WorkspacePropsTabTriggerFunction.vue
Normal file
315
src/renderer/components/WorkspacePropsTabTriggerFunction.vue
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
<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}"
|
||||||
|
title="CTRL+S"
|
||||||
|
@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="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-show="isSelected"
|
||||||
|
ref="queryEditor"
|
||||||
|
:value.sync="localFunction.sql"
|
||||||
|
:workspace="workspace"
|
||||||
|
:schema="schema"
|
||||||
|
:height="editorHeight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<WorkspacePropsTriggerFunctionOptionsModal
|
||||||
|
v-if="isOptionsModal"
|
||||||
|
:local-options="localFunction"
|
||||||
|
:workspace="workspace"
|
||||||
|
@hide="hideOptionsModal"
|
||||||
|
@options-update="optionsUpdate"
|
||||||
|
/>
|
||||||
|
<ModalAskParameters
|
||||||
|
v-if="isAskingParameters"
|
||||||
|
:local-routine="localFunction"
|
||||||
|
:client="workspace.client"
|
||||||
|
@confirm="runFunction"
|
||||||
|
@close="hideAskParamsModal"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
|
import BaseLoader from '@/components/BaseLoader';
|
||||||
|
import QueryEditor from '@/components/QueryEditor';
|
||||||
|
import WorkspacePropsTriggerFunctionOptionsModal from '@/components/WorkspacePropsTriggerFunctionOptionsModal';
|
||||||
|
import ModalAskParameters from '@/components/ModalAskParameters';
|
||||||
|
import Functions from '@/ipc-api/Functions';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsTabTriggerFunction',
|
||||||
|
components: {
|
||||||
|
BaseLoader,
|
||||||
|
QueryEditor,
|
||||||
|
WorkspacePropsTriggerFunctionOptionsModal,
|
||||||
|
ModalAskParameters
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
connection: Object,
|
||||||
|
function: String
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
tabUid: 'prop',
|
||||||
|
isLoading: false,
|
||||||
|
isSaving: false,
|
||||||
|
isOptionsModal: false,
|
||||||
|
isParamsModal: false,
|
||||||
|
isAskingParameters: false,
|
||||||
|
originalFunction: null,
|
||||||
|
localFunction: { sql: '' },
|
||||||
|
lastFunction: null,
|
||||||
|
sqlProxy: '',
|
||||||
|
editorHeight: 300
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.connection.uid);
|
||||||
|
},
|
||||||
|
isSelected () {
|
||||||
|
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.function;
|
||||||
|
},
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification',
|
||||||
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
|
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
|
newTab: 'workspaces/newTab'
|
||||||
|
}),
|
||||||
|
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.triggerFunction
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Functions.getFunctionInformations(params);
|
||||||
|
if (status === 'success') {
|
||||||
|
this.originalFunction = response;
|
||||||
|
|
||||||
|
this.originalFunction.parameters = [...this.originalFunction.parameters.map(param => {
|
||||||
|
param._id = uidGen();
|
||||||
|
return param;
|
||||||
|
})];
|
||||||
|
|
||||||
|
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.alterTriggerFunction(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 };
|
||||||
|
},
|
||||||
|
runFunctionCheck () {
|
||||||
|
if (this.localFunction.parameters.length)
|
||||||
|
this.showAskParamsModal();
|
||||||
|
else
|
||||||
|
this.runFunction();
|
||||||
|
},
|
||||||
|
runFunction (params) {
|
||||||
|
if (!params) params = [];
|
||||||
|
|
||||||
|
let sql;
|
||||||
|
switch (this.connection.client) { // TODO: move in a better place
|
||||||
|
case 'maria':
|
||||||
|
case 'mysql':
|
||||||
|
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
sql = `SELECT ${this.originalFunction.name}(${params.join(',')})`;
|
||||||
|
break;
|
||||||
|
case 'mssql':
|
||||||
|
sql = `SELECT ${this.originalFunction.name} ${params.join(',')}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.newTab({ uid: this.connection.uid, content: sql, autorun: true });
|
||||||
|
},
|
||||||
|
showOptionsModal () {
|
||||||
|
this.isOptionsModal = true;
|
||||||
|
},
|
||||||
|
hideOptionsModal () {
|
||||||
|
this.isOptionsModal = false;
|
||||||
|
},
|
||||||
|
showParamsModal () {
|
||||||
|
this.isParamsModal = true;
|
||||||
|
},
|
||||||
|
hideParamsModal () {
|
||||||
|
this.isParamsModal = false;
|
||||||
|
},
|
||||||
|
showAskParamsModal () {
|
||||||
|
this.isAskingParameters = true;
|
||||||
|
},
|
||||||
|
hideAskParamsModal () {
|
||||||
|
this.isAskingParameters = false;
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
if (this.isSelected) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
||||||
|
if (this.isChanged)
|
||||||
|
this.saveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@@ -232,7 +232,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" /> {{ $t('word.default') }} "{{ row.name }}"
|
<i class="mdi mdi-24px mdi-playlist-edit mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.default') }} "{{ row.name }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
|
@@ -0,0 +1,125 @@
|
|||||||
|
<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" />
|
||||||
|
<span class="cut-text">{{ $t('word.options') }} "{{ localOptions.name }}"</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div v-if="customizations.triggerFunctionlanguages" class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.language') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="optionsProxy.language" class="form-select">
|
||||||
|
<option v-for="language in customizations.triggerFunctionlanguages" :key="language">
|
||||||
|
{{ language }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.definer" 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 v-if="customizations.comment" 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>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsTriggerFunctionOptionsModal',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
localOptions: Object,
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
optionsProxy: {},
|
||||||
|
isOptionsChanging: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isTableNameValid () {
|
||||||
|
return this.optionsProxy.name !== '';
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace.customizations;
|
||||||
|
},
|
||||||
|
isInDataTypes () {
|
||||||
|
let typeNames = [];
|
||||||
|
for (const group of this.workspace.dataTypes) {
|
||||||
|
typeNames = group.types.reduce((acc, curr) => {
|
||||||
|
acc.push(curr.name);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
return typeNames.includes(this.localOptions.returns);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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>
|
@@ -31,25 +31,6 @@
|
|||||||
<span>{{ $t('word.run') }}</span>
|
<span>{{ $t('word.run') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-play" />
|
<i class="mdi mdi-24px mdi-play" />
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown export-dropdown pr-2">
|
|
||||||
<button
|
|
||||||
:disabled="!results.length || 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>
|
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm"
|
class="btn btn-dark btn-sm"
|
||||||
:disabled="!query || isQuering"
|
:disabled="!query || isQuering"
|
||||||
@@ -68,6 +49,28 @@
|
|||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ $t('word.clear') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep pl-1" />
|
<i class="mdi mdi-24px mdi-delete-sweep pl-1" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<div class="divider-vert py-3" />
|
||||||
|
|
||||||
|
<div class="dropdown table-dropdown pr-2">
|
||||||
|
<button
|
||||||
|
:disabled="!results.length || 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
|
<div
|
||||||
@@ -281,11 +284,16 @@ export default {
|
|||||||
|
|
||||||
.query-area-resizer {
|
.query-area-resizer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 5px;
|
height: 4px;
|
||||||
bottom: 40px;
|
bottom: 40px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
cursor: ns-resize;
|
cursor: ns-resize;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
|
transition: background 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba($primary-color, 50%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspace-query-runner-footer {
|
.workspace-query-runner-footer {
|
||||||
@@ -319,10 +327,4 @@ export default {
|
|||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.export-dropdown {
|
|
||||||
.menu {
|
|
||||||
min-width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@@ -12,6 +12,8 @@
|
|||||||
:selected-rows="selectedRows"
|
:selected-rows="selectedRows"
|
||||||
@show-delete-modal="showDeleteConfirmModal"
|
@show-delete-modal="showDeleteConfirmModal"
|
||||||
@set-null="setNull"
|
@set-null="setNull"
|
||||||
|
@copy-cell="copyCell"
|
||||||
|
@copy-row="copyRow"
|
||||||
@close-context="closeContext"
|
@close-context="closeContext"
|
||||||
/>
|
/>
|
||||||
<ul v-if="resultsWithRows.length > 1" class="tab tab-block result-tabs">
|
<ul v-if="resultsWithRows.length > 1" class="tab tab-block result-tabs">
|
||||||
@@ -85,7 +87,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-delete mr-1" /> {{ $tc('message.deleteRows', selectedRows.length) }}
|
<i class="mdi mdi-24px mdi-delete mr-1" />
|
||||||
|
<span class="cut-text">{{ $tc('message.deleteRows', selectedRows.length) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -384,6 +387,22 @@ export default {
|
|||||||
};
|
};
|
||||||
this.$emit('update-field', params);
|
this.$emit('update-field', params);
|
||||||
},
|
},
|
||||||
|
copyCell () {
|
||||||
|
const row = this.localResults.find(row => this.selectedRows.includes(row._id));
|
||||||
|
const cellName = Object.keys(row).find(prop => [
|
||||||
|
this.selectedCell.field,
|
||||||
|
`${this.fields[0].table}.${this.selectedCell.field}`,
|
||||||
|
`${this.fields[0].tableAlias}.${this.selectedCell.field}`
|
||||||
|
].includes(prop));
|
||||||
|
const valueToCopy = row[cellName];
|
||||||
|
navigator.clipboard.writeText(valueToCopy);
|
||||||
|
},
|
||||||
|
copyRow () {
|
||||||
|
const row = this.localResults.find(row => this.selectedRows.includes(row._id));
|
||||||
|
const rowToCopy = JSON.parse(JSON.stringify(row));
|
||||||
|
delete rowToCopy._id;
|
||||||
|
navigator.clipboard.writeText(JSON.stringify(rowToCopy));
|
||||||
|
},
|
||||||
applyUpdate (params) {
|
applyUpdate (params) {
|
||||||
const { primary, id, field, table, content } = params;
|
const { primary, id, field, table, content } = params;
|
||||||
|
|
||||||
@@ -464,7 +483,7 @@ export default {
|
|||||||
downloadTable (format, filename) {
|
downloadTable (format, filename) {
|
||||||
if (!this.sortedResults) return;
|
if (!this.sortedResults) return;
|
||||||
|
|
||||||
const rows = [...this.sortedResults].map(row => {
|
const rows = JSON.parse(JSON.stringify(this.sortedResults)).map(row => {
|
||||||
delete row._id;
|
delete row._id;
|
||||||
return row;
|
return row;
|
||||||
});
|
});
|
||||||
|
@@ -3,6 +3,30 @@
|
|||||||
:context-event="contextEvent"
|
:context-event="contextEvent"
|
||||||
@close-context="closeContext"
|
@close-context="closeContext"
|
||||||
>
|
>
|
||||||
|
<div v-if="selectedRows.length === 1" class="context-element">
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-content-copy text-light pr-1" /> {{ $t('word.copy') }}</span>
|
||||||
|
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
|
||||||
|
<div class="context-submenu">
|
||||||
|
<div
|
||||||
|
v-if="selectedRows.length === 1"
|
||||||
|
class="context-element"
|
||||||
|
@click="copyCell"
|
||||||
|
>
|
||||||
|
<span class="d-flex">
|
||||||
|
<i class="mdi mdi-18px mdi-numeric-0 mdi-rotate-90 text-light pr-1" /> {{ $tc('word.cell', 1) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="selectedRows.length === 1"
|
||||||
|
class="context-element"
|
||||||
|
@click="copyRow"
|
||||||
|
>
|
||||||
|
<span class="d-flex">
|
||||||
|
<i class="mdi mdi-18px mdi-table-row text-light pr-1" /> {{ $tc('word.row', 1) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="selectedRows.length === 1"
|
v-if="selectedRows.length === 1"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@@ -44,6 +68,14 @@ export default {
|
|||||||
setNull () {
|
setNull () {
|
||||||
this.$emit('set-null');
|
this.$emit('set-null');
|
||||||
this.closeContext();
|
this.closeContext();
|
||||||
|
},
|
||||||
|
copyCell () {
|
||||||
|
this.$emit('copy-cell');
|
||||||
|
this.closeContext();
|
||||||
|
},
|
||||||
|
copyRow () {
|
||||||
|
this.$emit('copy-row');
|
||||||
|
this.closeContext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -74,7 +74,7 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" /> {{ $t('word.edit') }} "{{ editingField }}"
|
<i class="mdi mdi-24px mdi-playlist-edit mr-1" /> <span class="cut-text">{{ $t('word.edit') }} "{{ editingField }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -138,7 +138,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" /> {{ $t('word.edit') }} "{{ editingField }}"
|
<i class="mdi mdi-24px mdi-playlist-edit mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.edit') }} "{{ editingField }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
|
@@ -41,9 +41,24 @@
|
|||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-skip-previous" />
|
<i class="mdi mdi-24px mdi-skip-previous" />
|
||||||
</button>
|
</button>
|
||||||
<div class="btn btn-dark btn-sm mr-0 text-bold c-auto px-2">
|
<div class="dropdown" :class="{'active': isPageMenu}">
|
||||||
|
<div @click="openPageMenu">
|
||||||
|
<div class="btn btn-dark btn-sm mr-0 no-radius dropdown-toggle text-bold px-3">
|
||||||
{{ page }}
|
{{ page }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="menu px-3">
|
||||||
|
<span>{{ $t('message.pageNumber') }}</span>
|
||||||
|
<input
|
||||||
|
ref="pageSelect"
|
||||||
|
v-model="pageProxy"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
class="form-input"
|
||||||
|
@blur="setPageNumber"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm mr-0"
|
class="btn btn-dark btn-sm mr-0"
|
||||||
:disabled="isQuering || (results.length && results[0].rows.length < limit)"
|
:disabled="isQuering || (results.length && results[0].rows.length < limit)"
|
||||||
@@ -66,7 +81,7 @@
|
|||||||
<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 pr-2">
|
<div class="dropdown table-dropdown pr-2">
|
||||||
<button
|
<button
|
||||||
:disabled="isQuering"
|
:disabled="isQuering"
|
||||||
class="btn btn-dark btn-sm dropdown-toggle mr-0 pr-0"
|
class="btn btn-dark btn-sm dropdown-toggle mr-0 pr-0"
|
||||||
@@ -172,6 +187,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
tabUid: 'data',
|
tabUid: 'data',
|
||||||
isQuering: false,
|
isQuering: false,
|
||||||
|
isPageMenu: false,
|
||||||
results: [],
|
results: [],
|
||||||
lastTable: null,
|
lastTable: null,
|
||||||
isAddModal: false,
|
isAddModal: false,
|
||||||
@@ -179,7 +195,8 @@ export default {
|
|||||||
autorefreshTimer: 0,
|
autorefreshTimer: 0,
|
||||||
refreshInterval: null,
|
refreshInterval: null,
|
||||||
sortParams: {},
|
sortParams: {},
|
||||||
page: 1
|
page: 1,
|
||||||
|
pageProxy: 1
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -229,8 +246,11 @@ export default {
|
|||||||
this.$refs.queryTable.resetSort();
|
this.$refs.queryTable.resetSort();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
page () {
|
page (val, oldVal) {
|
||||||
|
if (val && val > 0 && val !== oldVal) {
|
||||||
|
this.pageProxy = this.page;
|
||||||
this.getTableData();
|
this.getTableData();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
isSelected (val) {
|
isSelected (val) {
|
||||||
if (val && this.lastTable !== this.table) {
|
if (val && this.lastTable !== this.table) {
|
||||||
@@ -292,11 +312,30 @@ export default {
|
|||||||
this.sortParams = sortParams;
|
this.sortParams = sortParams;
|
||||||
this.getTableData(sortParams);
|
this.getTableData(sortParams);
|
||||||
},
|
},
|
||||||
|
openPageMenu () {
|
||||||
|
if (this.isQuering) return;
|
||||||
|
|
||||||
|
this.isPageMenu = true;
|
||||||
|
if (this.isPageMenu)
|
||||||
|
setTimeout(() => this.$refs.pageSelect.focus(), 20);
|
||||||
|
},
|
||||||
|
setPageNumber () {
|
||||||
|
this.isPageMenu = false;
|
||||||
|
|
||||||
|
if (this.pageProxy > 0)
|
||||||
|
this.page = this.pageProxy;
|
||||||
|
else
|
||||||
|
this.pageProxy = this.page;
|
||||||
|
},
|
||||||
pageChange (direction) {
|
pageChange (direction) {
|
||||||
if (this.isQuering) return;
|
if (this.isQuering) return;
|
||||||
|
|
||||||
if (direction === 'next' && (this.results.length && this.results[0].rows.length === this.limit))
|
if (direction === 'next' && (this.results.length && this.results[0].rows.length === this.limit)) {
|
||||||
|
if (!this.page)
|
||||||
|
this.page = 2;
|
||||||
|
else
|
||||||
this.page++;
|
this.page++;
|
||||||
|
}
|
||||||
else if (direction === 'prev' && this.page > 1)
|
else if (direction === 'prev' && this.page > 1)
|
||||||
this.page--;
|
this.page--;
|
||||||
},
|
},
|
||||||
@@ -344,10 +383,3 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
|
||||||
.export-dropdown {
|
|
||||||
.menu {
|
|
||||||
min-width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@@ -105,7 +105,14 @@ module.exports = {
|
|||||||
scratchpad: 'Scratchpad',
|
scratchpad: 'Scratchpad',
|
||||||
array: 'Array',
|
array: 'Array',
|
||||||
changelog: 'Changelog',
|
changelog: 'Changelog',
|
||||||
format: 'Format'
|
format: 'Format',
|
||||||
|
structure: 'Structure',
|
||||||
|
small: 'Small',
|
||||||
|
medium: 'Medium',
|
||||||
|
large: 'Large',
|
||||||
|
row: 'Row | Rows',
|
||||||
|
cell: 'Cell | Cells',
|
||||||
|
triggerFunction: 'Trigger function | Trigger functions'
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
appWelcome: 'Welcome to Antares SQL Client!',
|
appWelcome: 'Welcome to Antares SQL Client!',
|
||||||
@@ -211,7 +218,9 @@ module.exports = {
|
|||||||
deleteSchema: 'Delete schema',
|
deleteSchema: 'Delete schema',
|
||||||
markdownSupported: 'Markdown supported',
|
markdownSupported: 'Markdown supported',
|
||||||
plantATree: 'Plant a Tree',
|
plantATree: 'Plant a Tree',
|
||||||
dataTabPageSize: 'DATA tab page size'
|
dataTabPageSize: 'DATA tab page size',
|
||||||
|
pageNumber: 'Page number',
|
||||||
|
duplicateTable: 'Duplicate table'
|
||||||
},
|
},
|
||||||
faker: {
|
faker: {
|
||||||
address: 'Address',
|
address: 'Address',
|
||||||
|
@@ -14,7 +14,15 @@ export default class {
|
|||||||
return ipcRenderer.invoke('alter-function', params);
|
return ipcRenderer.invoke('alter-function', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static alterTriggerFunction (params) {
|
||||||
|
return ipcRenderer.invoke('alter-trigger-function', params);
|
||||||
|
}
|
||||||
|
|
||||||
static createFunction (params) {
|
static createFunction (params) {
|
||||||
return ipcRenderer.invoke('create-function', params);
|
return ipcRenderer.invoke('create-function', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static createTriggerFunction (params) {
|
||||||
|
return ipcRenderer.invoke('create-trigger-function', params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -46,6 +46,10 @@ export default class {
|
|||||||
return ipcRenderer.invoke('alter-table', params);
|
return ipcRenderer.invoke('alter-table', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static duplicateTable (params) {
|
||||||
|
return ipcRenderer.invoke('duplicate-table', params);
|
||||||
|
}
|
||||||
|
|
||||||
static truncateTable (params) {
|
static truncateTable (params) {
|
||||||
return ipcRenderer.invoke('truncate-table', params);
|
return ipcRenderer.invoke('truncate-table', params);
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,7 @@ $enum-color: goldenrod;
|
|||||||
$unknown-color: gray;
|
$unknown-color: gray;
|
||||||
|
|
||||||
/* Sizes */
|
/* Sizes */
|
||||||
|
$border-radius: 0.3rem;
|
||||||
$titlebar-height: 1.5rem;
|
$titlebar-height: 1.5rem;
|
||||||
$settingbar-width: 3rem;
|
$settingbar-width: 3rem;
|
||||||
$explorebar-width: 14rem;
|
$explorebar-width: 14rem;
|
||||||
|
@@ -34,12 +34,22 @@ body {
|
|||||||
outline: none !important;
|
outline: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.no-radius {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.no-border {
|
.no-border {
|
||||||
outline: none !important;
|
outline: none !important;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cut-text {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
.workspace-tabs {
|
.workspace-tabs {
|
||||||
align-content: baseline;
|
align-content: baseline;
|
||||||
|
|
||||||
@@ -103,7 +113,7 @@ body {
|
|||||||
.modal-container,
|
.modal-container,
|
||||||
.modal-sm .modal-container {
|
.modal-sm .modal-container {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border-radius: 3px;
|
border-radius: $border-radius;
|
||||||
|
|
||||||
.modal-header {
|
.modal-header {
|
||||||
padding: 0.4rem 0.8rem;
|
padding: 0.4rem 0.8rem;
|
||||||
@@ -111,13 +121,46 @@ body {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-radius: 3px 3px 0 0;
|
border-radius: $border-radius $border-radius 0 0;
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab {
|
.tab {
|
||||||
.tab-item {
|
.tab-item {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.tab-link {
|
||||||
|
min-width: 0;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
.tab-link {
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
height: 2px;
|
||||||
|
width: 0;
|
||||||
|
transition: width 0.2s;
|
||||||
|
background-color: $primary-color;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-clear {
|
.btn-clear {
|
||||||
margin-top: -0.1rem;
|
margin-top: -0.1rem;
|
||||||
font-size: 0.6rem;
|
font-size: 0.6rem;
|
||||||
@@ -185,9 +228,47 @@ body {
|
|||||||
max-height: 5000rem !important;
|
max-height: 5000rem !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-group {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.btn.loading {
|
.btn.loading {
|
||||||
> .mdi,
|
> .mdi,
|
||||||
> span {
|
> span {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-dropdown {
|
||||||
|
.menu {
|
||||||
|
min-width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
> a {
|
||||||
|
margin: 0.2rem;
|
||||||
|
padding: 0.1rem 0.3rem;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ace Editor
|
||||||
|
.ace_editor {
|
||||||
|
&.ace_autocomplete {
|
||||||
|
border-radius: $border-radius;
|
||||||
|
|
||||||
|
.ace_marker-layer {
|
||||||
|
.ace_active-line,
|
||||||
|
.ace_line-hover {
|
||||||
|
border-radius: $border-radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -58,6 +58,10 @@
|
|||||||
&:hover {
|
&:hover {
|
||||||
background: $bg-color-gray;
|
background: $bg-color-gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: $primary-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,10 +215,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.bg-checkered {
|
.bg-checkered {
|
||||||
background-image:
|
background-image: linear-gradient(to right, rgba(192, 192, 192, 0.75), rgba(192, 192, 192, 0.75)),
|
||||||
linear-gradient(to right, rgba(192, 192, 192, 0.75), rgba(192, 192, 192, 0.75)),
|
linear-gradient(to right, black 50%, white 50%), linear-gradient(to bottom, black 50%, white 50%);
|
||||||
linear-gradient(to right, black 50%, white 50%),
|
|
||||||
linear-gradient(to bottom, black 50%, white 50%);
|
|
||||||
background-blend-mode: normal, difference, normal;
|
background-blend-mode: normal, difference, normal;
|
||||||
background-size: 2em 2em;
|
background-size: 2em 2em;
|
||||||
}
|
}
|
||||||
@@ -233,7 +235,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $primary-color;
|
background: rgba($light-color, 15%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -264,7 +266,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.editor-col {
|
.editor-col {
|
||||||
border-left: 2px solid $bg-color-light-dark;
|
border-left: 0.05rem solid rgba($bg-color-light-dark, 60%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
@@ -361,26 +363,6 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
.settingbar-element {
|
.settingbar-element {
|
||||||
height: $settingbar-width;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
border-left: 3px solid transparent;
|
|
||||||
opacity: 0.5;
|
|
||||||
transition: opacity 0.2s;
|
|
||||||
display: flex;
|
|
||||||
align-content: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
border-left-color: $body-font-color-dark;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settingbar-element-icon {
|
.settingbar-element-icon {
|
||||||
&.badge::after {
|
&.badge::after {
|
||||||
bottom: -10px;
|
bottom: -10px;
|
||||||
|
@@ -62,6 +62,10 @@
|
|||||||
&:hover {
|
&:hover {
|
||||||
background: $bg-color-gray;
|
background: $bg-color-gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: $primary-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +98,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.editor-col {
|
.editor-col {
|
||||||
border-left: 2px solid darken($bg-color-light-gray, 15%);
|
border-left: 0.05rem solid darken($bg-color-light-gray, 15%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-uploader {
|
.file-uploader {
|
||||||
@@ -143,26 +147,6 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
.settingbar-element {
|
.settingbar-element {
|
||||||
height: $settingbar-width;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
border-left: 3px solid transparent;
|
|
||||||
opacity: 0.5;
|
|
||||||
transition: opacity 0.2s;
|
|
||||||
display: flex;
|
|
||||||
align-content: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.selected {
|
|
||||||
border-left-color: $body-font-color-dark;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.settingbar-element-icon {
|
.settingbar-element-icon {
|
||||||
&.badge::after {
|
&.badge::after {
|
||||||
bottom: -10px;
|
bottom: -10px;
|
||||||
@@ -235,7 +219,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $primary-color;
|
background: rgba($light-color, 15%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,7 +15,8 @@ export default {
|
|||||||
auto_complete: persistentStore.get('auto_complete', true),
|
auto_complete: persistentStore.get('auto_complete', true),
|
||||||
line_wrap: persistentStore.get('line_wrap', true),
|
line_wrap: persistentStore.get('line_wrap', true),
|
||||||
application_theme: persistentStore.get('application_theme', 'dark'),
|
application_theme: persistentStore.get('application_theme', 'dark'),
|
||||||
editor_theme: persistentStore.get('editor_theme', 'twilight')
|
editor_theme: persistentStore.get('editor_theme', 'twilight'),
|
||||||
|
editor_font_size: persistentStore.get('editor_font_size', 'medium')
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
getLocale: state => state.locale,
|
getLocale: state => state.locale,
|
||||||
@@ -26,7 +27,8 @@ export default {
|
|||||||
getAutoComplete: state => state.auto_complete,
|
getAutoComplete: state => state.auto_complete,
|
||||||
getLineWrap: state => state.line_wrap,
|
getLineWrap: state => state.line_wrap,
|
||||||
getApplicationTheme: state => state.application_theme,
|
getApplicationTheme: state => state.application_theme,
|
||||||
getEditorTheme: state => state.editor_theme
|
getEditorTheme: state => state.editor_theme,
|
||||||
|
getEditorFontSize: state => state.editor_font_size
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
SET_LOCALE (state, locale) {
|
SET_LOCALE (state, locale) {
|
||||||
@@ -65,6 +67,10 @@ export default {
|
|||||||
SET_EDITOR_THEME (state, theme) {
|
SET_EDITOR_THEME (state, theme) {
|
||||||
state.editor_theme = theme;
|
state.editor_theme = theme;
|
||||||
persistentStore.set('editor_theme', state.editor_theme);
|
persistentStore.set('editor_theme', state.editor_theme);
|
||||||
|
},
|
||||||
|
SET_EDITOR_FONT_SIZE (state, size) {
|
||||||
|
state.editor_font_size = size;
|
||||||
|
persistentStore.set('editor_font_size', state.editor_font_size);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
@@ -94,6 +100,9 @@ export default {
|
|||||||
},
|
},
|
||||||
changeEditorTheme ({ commit }, theme) {
|
changeEditorTheme ({ commit }, theme) {
|
||||||
commit('SET_EDITOR_THEME', theme);
|
commit('SET_EDITOR_THEME', theme);
|
||||||
|
},
|
||||||
|
changeEditorFontSize ({ commit }, size) {
|
||||||
|
commit('SET_EDITOR_FONT_SIZE', size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user