mirror of
https://github.com/Fabio286/antares.git
synced 2025-06-05 21:59:22 +02:00
Compare commits
33 Commits
v0.0.3-alp
...
v0.0.4
Author | SHA1 | Date | |
---|---|---|---|
949f7add8f | |||
968ec1edf7 | |||
a9d3a57281 | |||
|
f787439009 | ||
b03d461e21 | |||
5c05e3e9e9 | |||
0089c0cbac | |||
4fd72ec9e7 | |||
712fe9f00d | |||
092e8a0732 | |||
70908eb076 | |||
f8a0783769 | |||
|
cdf964ef07 | ||
413b56916c | |||
acd3310228 | |||
|
e014f81a9c | ||
530361144e | |||
d69e411581 | |||
9a3a0513e2 | |||
|
be8fa96c93 | ||
587116bd20 | |||
|
145d1dd1ad | ||
457410b65a | |||
|
b183eacae1 | ||
76cafdb69a | |||
|
2d63ddb9c7 | ||
f12f00dbb7 | |||
1ecb6d892c | |||
fcd83f35d8 | |||
|
7a4d8286a6 | ||
|
dd5ec2c661 | ||
3f0e5d3512 | |||
ac01511c10 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,4 +6,4 @@ thumbs.db
|
||||
.vscode
|
||||
TODO.md
|
||||
*.txt
|
||||
dev-app-update.yml
|
||||
package-lock.json
|
14
.stylelintrc
Normal file
14
.stylelintrc
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": [
|
||||
"stylelint-config-standard"
|
||||
],
|
||||
"fix": true,
|
||||
"formatter": "verbose",
|
||||
"plugins": [
|
||||
"stylelint-scss"
|
||||
],
|
||||
"rules": {
|
||||
"at-rule-no-unknown": null
|
||||
},
|
||||
"syntax": "scss"
|
||||
}
|
38
.travis.yml
Normal file
38
.travis.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
language: node_js
|
||||
node_js: 12
|
||||
|
||||
before_install:
|
||||
- npm install
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
- app/node_modules
|
||||
- $HOME/.cache/electron
|
||||
- $HOME/.cache/electron-builder
|
||||
- $HOME/.npm/_prebuilds
|
||||
|
||||
env:
|
||||
global:
|
||||
- ELECTRON_CACHE=$HOME/.cache/electron
|
||||
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: Test
|
||||
script:
|
||||
- npm test
|
||||
- stage: Deploy Linux & Windows
|
||||
if: tag IS present
|
||||
os: linux
|
||||
services: docker
|
||||
script:
|
||||
- docker run --rm --env-file <(env | grep -iE 'DEBUG|NODE_|ELECTRON_|NPM_|CI|CIRCLE|TRAVIS|APPVEYOR_|CSC_|_TOKEN|_KEY|AWS_|STRIP|BUILD_') -v ${PWD}:/project -v ~/.cache/electron:/root/.cache/electron -v ~/.cache/electron-builder:/root/.cache/electron-builder electronuserland/builder:wine /bin/bash -c "npm run build -- --linux --win -p always"
|
||||
before_cache:
|
||||
- rm -rf $HOME/.cache/electron-builder/wine
|
||||
- stage: Deploy Mac
|
||||
if: tag IS present
|
||||
os: osx
|
||||
osx_image: xcode10.2
|
||||
script:
|
||||
- npm run build -- -p always
|
15
CHANGELOG.md
15
CHANGELOG.md
@@ -1,18 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.0.2-alpha](https://github.com/Fabio286/antares/releases/tag/v0.0.2-alpha) - 2020-06-26
|
||||
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.
|
||||
|
||||
### Added
|
||||
### [0.0.4](https://github.com/EStarium/antares/compare/v0.0.3-alpha...v0.0.4) (2020-08-06)
|
||||
|
||||
- **Edit table fields:** You can edit fields from tables by double click. For now is available only on numeric and textual values.
|
||||
- Various improvements under the hood.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Connection test.
|
||||
|
||||
## [0.0.1-alpha](https://github.com/Fabio286/antares/releases/tag/v0.0.1-alpha) - 2020-06-19
|
||||
|
||||
### Features
|
||||
|
||||
- **Initial release:**
|
||||
* blob fields edit/view/download ([712fe9f](https://github.com/EStarium/antares/commit/712fe9f00d210db0f2317eca61e7fb648383e3fe))
|
||||
* window title in app title bar ([0089c0c](https://github.com/EStarium/antares/commit/0089c0cbac6caf0a6fd195849099f18713580228))
|
||||
|
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2017 Jakub Szwacz
|
||||
Copyright (c) 2020 Fabio Di Stasio
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
10
README.md
10
README.md
@@ -4,12 +4,14 @@
|
||||
|
||||
# Antares SQL Client
|
||||
|
||||
 [](https://travis-ci.com/EStarium/antares)  
|
||||
|
||||
Antares is an SQL client based on Electron.js and Vue.js that aims to become a useful tool, especially for developers.
|
||||
My target is to support as many databases as possible, and all major operating systems, including the ARM versions.
|
||||
|
||||
At the moment this application is in a development state, it lacks many features, and is'nt ready as a main SQL client. However i'm actively working on it, hoping to provide all all minimum features as soon as possible.
|
||||
At the moment this application is in a development state, it lacks many features, and is'nt ready as a main SQL client. However i'm actively working on it, hoping to provide all essential features as soon as possible.
|
||||
|
||||
If you are curious to try this early state of Antares you can download and install the [latest release](https://github.com/EStarium/antares/releases), and stay tuned for updates. At moment is only available for Windows.
|
||||
If you are curious to try this early state of Antares you can download and install the [latest release](https://github.com/EStarium/antares/releases), and stay tuned for updates. At moment i'm testing only on Windows.
|
||||
|
||||
## Currently supported
|
||||
|
||||
@@ -27,3 +29,7 @@ If you are curious to try this early state of Antares you can download and insta
|
||||
- [x] Windows
|
||||
- [ ] Linux
|
||||
- [ ] MacOS
|
||||
|
||||
## Translations
|
||||
|
||||
[Giuseppe Gigliotti](https://github.com/ReverbOD) / [Italian Translation](https://github.com/EStarium/antares/pull/20)
|
||||
|
BIN
build/icon.png
BIN
build/icon.png
Binary file not shown.
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 25 KiB |
13771
package-lock.json
generated
13771
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
60
package.json
60
package.json
@@ -1,24 +1,56 @@
|
||||
{
|
||||
"name": "antares",
|
||||
"productName": "Antares",
|
||||
"version": "0.0.2-alpha",
|
||||
"version": "0.0.4",
|
||||
"description": "A cross-platform easy to use SQL client.",
|
||||
"main": "src/main/index.js",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/EStarium/antares.git",
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development electron-webpack dev",
|
||||
"compile": "electron-webpack",
|
||||
"dist": "cross-env NODE_ENV=production npm run compile && electron-builder",
|
||||
"dist:dir": "cross-env NODE_ENV=production npm run dist --dir -c.compression=store -c.mac.identity=null",
|
||||
"publish": "electron-builder -p always"
|
||||
"build": "cross-env NODE_ENV=production npm run compile && electron-builder",
|
||||
"release": "standard-version",
|
||||
"release:pre": "npm run release -- --prerelease alpha"
|
||||
},
|
||||
"author": "Fabio Di Stasio <fabio286@gmail.com>",
|
||||
"build": {
|
||||
"npmRebuild": false,
|
||||
"asar": true,
|
||||
"appId": "com.estarium.antares",
|
||||
"artifactName": "${productName}-${version}-${os}_${arch}.${ext}",
|
||||
"files": [
|
||||
"static/*"
|
||||
]
|
||||
"dist/**/*",
|
||||
"src/**/*",
|
||||
"node_modules/**/*",
|
||||
"package.json"
|
||||
],
|
||||
"dmg": {
|
||||
"contents": [
|
||||
{
|
||||
"x": 130,
|
||||
"y": 220
|
||||
},
|
||||
{
|
||||
"x": 410,
|
||||
"y": 220,
|
||||
"type": "link",
|
||||
"path": "/Applications"
|
||||
}
|
||||
]
|
||||
},
|
||||
"win": {
|
||||
"target": [
|
||||
"nsis"
|
||||
]
|
||||
},
|
||||
"linux": {
|
||||
"target": [
|
||||
"deb",
|
||||
"AppImage"
|
||||
],
|
||||
"category": "Development"
|
||||
}
|
||||
},
|
||||
"electronWebpack": {
|
||||
"whiteListedModules": [
|
||||
@@ -31,7 +63,7 @@
|
||||
"dependencies": {
|
||||
"codemirror": "^5.56.0",
|
||||
"electron-log": "^4.2.2",
|
||||
"electron-updater": "^4.3.1",
|
||||
"electron-updater": "^4.3.4",
|
||||
"lodash": "^4.17.19",
|
||||
"material-design-icons": "^3.0.1",
|
||||
"moment": "^2.27.0",
|
||||
@@ -41,7 +73,7 @@
|
||||
"source-map-support": "^0.5.16",
|
||||
"spectre.css": "^0.5.9",
|
||||
"vue-click-outside": "^1.1.0",
|
||||
"vue-i18n": "^8.18.2",
|
||||
"vue-i18n": "^8.20.0",
|
||||
"vue-the-mask": "^0.11.1",
|
||||
"vuedraggable": "^2.24.0",
|
||||
"vuex": "^3.5.1",
|
||||
@@ -50,8 +82,8 @@
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^10.1.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"electron": "^8.4.0",
|
||||
"electron-builder": "^22.7.0",
|
||||
"electron": "^9.1.2",
|
||||
"electron-builder": "^22.8.0",
|
||||
"electron-devtools-installer": "^3.1.1",
|
||||
"electron-webpack": "^2.8.2",
|
||||
"electron-webpack-vue": "^2.4.0",
|
||||
@@ -63,8 +95,12 @@
|
||||
"eslint-plugin-standard": "^4.0.1",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"node-sass": "^4.14.1",
|
||||
"sass-loader": "^9.0.2",
|
||||
"sass-loader": "^9.0.3",
|
||||
"standard-version": "^8.0.2",
|
||||
"stylelint": "^13.6.1",
|
||||
"stylelint-config-standard": "^20.0.0",
|
||||
"stylelint-scss": "^3.18.0",
|
||||
"vue": "^2.6.11",
|
||||
"webpack": "^4.43.0"
|
||||
"webpack": "^4.44.1"
|
||||
}
|
||||
}
|
||||
|
12
src/common/fieldTypes.js
Normal file
12
src/common/fieldTypes.js
Normal file
@@ -0,0 +1,12 @@
|
||||
export const TEXT = ['char', 'varchar'];
|
||||
export const LONG_TEXT = ['text', 'mediumtext', 'longtext'];
|
||||
|
||||
export const NUMBER = ['int', 'tinyint', 'smallint', 'mediumint', 'bigint', 'float', 'double', 'decimal'];
|
||||
|
||||
export const DATE = ['date'];
|
||||
export const TIME = ['time'];
|
||||
export const DATETIME = ['datetime', 'timestamp'];
|
||||
|
||||
export const BLOB = ['blob', 'mediumblob', 'longblob'];
|
||||
|
||||
export const BIT = ['bit'];
|
7
src/common/libs/bufferToBase64.js
Normal file
7
src/common/libs/bufferToBase64.js
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
export function bufferToBase64 (buf) {
|
||||
const binstr = Array.prototype.map.call(buf, ch => {
|
||||
return String.fromCharCode(ch);
|
||||
}).join('');
|
||||
return btoa(binstr);
|
||||
}
|
12
src/common/libs/formatBytes.js
Normal file
12
src/common/libs/formatBytes.js
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
export function formatBytes (bytes, decimals = 2) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||
}
|
@@ -1,13 +1,10 @@
|
||||
export function uidGen () {
|
||||
return Math.random().toString(36).substr(2, 9).toUpperCase();
|
||||
};
|
||||
|
||||
'use strict';
|
||||
export function mimeFromHex (hex) {
|
||||
switch (hex.substring(0, 4)) { // 2 bytes
|
||||
case '424D':
|
||||
return { ext: 'bmp', mime: 'image/bmp' };
|
||||
case '1F8B':
|
||||
return { ext: 'gz', mime: 'application/gzip' };
|
||||
return { ext: 'tar.gz', mime: 'application/gzip' };
|
||||
case '0B77':
|
||||
return { ext: 'ac3', mime: 'audio/vnd.dolby.dd-raw' };
|
||||
case '7801':
|
||||
@@ -20,7 +17,7 @@ export function mimeFromHex (hex) {
|
||||
default:
|
||||
switch (hex.substring(0, 6)) { // 3 bytes
|
||||
case 'FFD8FF':
|
||||
return { ext: 'jpj', mime: 'image/jpeg' };
|
||||
return { ext: 'jpg', mime: 'image/jpeg' };
|
||||
case '4949BC':
|
||||
return { ext: 'jxr', mime: 'image/vnd.ms-photo' };
|
||||
case '425A68':
|
||||
@@ -39,21 +36,11 @@ export function mimeFromHex (hex) {
|
||||
return { ext: 'bpg', mime: 'image/bpg' };
|
||||
case '4D4D002A':
|
||||
return { ext: 'tif', mime: 'image/tiff' };
|
||||
case '00000100':
|
||||
return { ext: 'ico', mime: 'image/x-icon' };
|
||||
default:
|
||||
return { ext: '???', mime: 'unknown ' + hex };
|
||||
return { ext: '', mime: 'unknown ' + hex };
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function formatBytes (bytes, decimals = 2) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||
}
|
19
src/common/libs/sqlEscaper.js
Normal file
19
src/common/libs/sqlEscaper.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/* eslint-disable no-useless-escape */
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const regex = new RegExp(/[\0\x08\x09\x1a\n\r"'\\\%]/g);
|
||||
|
||||
/**
|
||||
* Escapes a string
|
||||
*
|
||||
* @param {String} string
|
||||
* @returns {String}
|
||||
*/
|
||||
function sqlEscaper (string) {
|
||||
return string.replace(regex, (char) => {
|
||||
const m = ['\\0', '\\x08', '\\x09', '\\x1a', '\\n', '\\r', '\'', '"', '\\', '\\\\', '%'];
|
||||
const r = ['\\\\0', '\\\\b', '\\\\t', '\\\\z', '\\\\n', '\\\\r', '\'\'', '""', '\\\\', '\\\\\\\\', '\\%'];
|
||||
return r[m.indexOf(char)];
|
||||
});
|
||||
}
|
||||
|
||||
export { sqlEscaper };
|
4
src/common/libs/uidGen.js
Normal file
4
src/common/libs/uidGen.js
Normal file
@@ -0,0 +1,4 @@
|
||||
'use strict';
|
||||
export function uidGen () {
|
||||
return Math.random().toString(36).substr(2, 9).toUpperCase();
|
||||
};
|
@@ -12,35 +12,29 @@ process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true';
|
||||
// global reference to mainWindow (necessary to prevent window from being garbage collected)
|
||||
let mainWindow;
|
||||
|
||||
function createMainWindow () {
|
||||
async function createMainWindow () {
|
||||
const icon = require('../renderer/images/logo-32.png');
|
||||
const window = new BrowserWindow({
|
||||
width: 1600,
|
||||
height: 1000,
|
||||
minHeight: 550,
|
||||
width: 1024,
|
||||
height: 800,
|
||||
minWidth: 900,
|
||||
minHeight: 550,
|
||||
title: 'Antares',
|
||||
autoHideMenuBar: true,
|
||||
icon: nativeImage.createFromDataURL(icon.default),
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
'web-security': false
|
||||
'web-security': false,
|
||||
enableRemoteModule: true,
|
||||
spellcheck: false
|
||||
},
|
||||
frame: false,
|
||||
backgroundColor: '#1d1d1d'
|
||||
});
|
||||
|
||||
if (isDevelopment)
|
||||
window.loadURL(`http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`);
|
||||
else {
|
||||
window.loadURL(formatUrl({
|
||||
pathname: path.join(__dirname, 'index.html'),
|
||||
protocol: 'file',
|
||||
slashes: true
|
||||
}));
|
||||
}
|
||||
|
||||
if (isDevelopment) {
|
||||
await window.loadURL(`http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`);
|
||||
|
||||
const { default: installExtension, VUEJS_DEVTOOLS } = require('electron-devtools-installer');
|
||||
window.webContents.openDevTools();
|
||||
|
||||
@@ -52,6 +46,13 @@ function createMainWindow () {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
else {
|
||||
await window.loadURL(formatUrl({
|
||||
pathname: path.join(__dirname, 'index.html'),
|
||||
protocol: 'file',
|
||||
slashes: true
|
||||
}));
|
||||
}
|
||||
|
||||
window.on('closed', () => {
|
||||
mainWindow = null;
|
||||
@@ -64,12 +65,12 @@ function createMainWindow () {
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize ipcHandlers
|
||||
ipcHandlers();
|
||||
|
||||
return window;
|
||||
};
|
||||
|
||||
// Initialize ipcHandlers
|
||||
ipcHandlers();
|
||||
|
||||
// quit application when all windows are closed
|
||||
app.on('window-all-closed', () => {
|
||||
// on macOS it is common for applications to stay open until the user explicitly quits
|
||||
|
7
src/main/ipc-handlers/application.js
Normal file
7
src/main/ipc-handlers/application.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import { app, ipcMain } from 'electron';
|
||||
|
||||
export default () => {
|
||||
ipcMain.on('closeApp', () => {
|
||||
app.exit();
|
||||
});
|
||||
};
|
@@ -1,6 +1,7 @@
|
||||
import connection from './connection';
|
||||
import tables from './tables';
|
||||
import updates from './updates';
|
||||
import application from './application';
|
||||
|
||||
const connections = {};
|
||||
|
||||
@@ -8,4 +9,5 @@ export default () => {
|
||||
connection(connections);
|
||||
tables(connections);
|
||||
updates();
|
||||
application();
|
||||
};
|
||||
|
@@ -20,6 +20,7 @@ export class AntaresConnector {
|
||||
this._params = args.params;
|
||||
this._poolSize = args.poolSize || false;
|
||||
this._connection = null;
|
||||
this._logger = args.logger || console.log;
|
||||
|
||||
this._queryDefaults = {
|
||||
schema: '',
|
||||
@@ -247,7 +248,7 @@ export class AntaresConnector {
|
||||
* @memberof AntaresConnector
|
||||
*/
|
||||
async raw (sql) {
|
||||
if (process.env.NODE_ENV === 'development') console.log(sql);
|
||||
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
|
||||
|
||||
switch (this._client) { // TODO: uniform fields with every client type, needed table name and fields array
|
||||
case 'maria':
|
||||
|
@@ -13,13 +13,22 @@ export default class {
|
||||
.run();
|
||||
}
|
||||
|
||||
static getTableColumns (connection, schema, table) {
|
||||
return connection
|
||||
static async getTableColumns (connection, schema, table) {
|
||||
const { rows } = await connection
|
||||
.select('*')
|
||||
.schema('information_schema')
|
||||
.from('COLUMNS')
|
||||
.where({ TABLE_SCHEMA: `= '${schema}'`, TABLE_NAME: `= '${table}'` })
|
||||
.orderBy({ ORDINAL_POSITION: 'ASC' })
|
||||
.run();
|
||||
|
||||
return rows.map(field => {
|
||||
return {
|
||||
name: field.COLUMN_NAME,
|
||||
key: field.COLUMN_KEY.toLowerCase(),
|
||||
type: field.DATA_TYPE,
|
||||
precision: field.DATETIME_PRECISION
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,8 @@
|
||||
'use strict';
|
||||
import { sqlEscaper } from 'common/libs/sqlEscaper';
|
||||
import { TEXT, LONG_TEXT, NUMBER, BLOB } from 'common/fieldTypes';
|
||||
import fs from 'fs';
|
||||
|
||||
export default class {
|
||||
static async getTableData (connection, schema, table) {
|
||||
return connection
|
||||
@@ -9,13 +13,34 @@ export default class {
|
||||
.run();
|
||||
}
|
||||
|
||||
static async updateTableCell (connection, params) { // TODO: Handle different field types
|
||||
return connection
|
||||
.update({ [params.field]: `= "${params.content}"` })
|
||||
static async updateTableCell (connection, params) {
|
||||
let escapedParam;
|
||||
let reload = false;
|
||||
|
||||
if (NUMBER.includes(params.type))
|
||||
escapedParam = params.content;
|
||||
else if ([...TEXT, ...LONG_TEXT].includes(params.type))
|
||||
escapedParam = `"${sqlEscaper(params.content)}"`;
|
||||
else if (BLOB.includes(params.type)) {
|
||||
if (params.content) {
|
||||
const fileBlob = fs.readFileSync(params.content);
|
||||
escapedParam = `0x${fileBlob.toString('hex')}`;
|
||||
reload = true;
|
||||
}
|
||||
else
|
||||
escapedParam = '""';
|
||||
}
|
||||
else
|
||||
escapedParam = `"${sqlEscaper(params.content)}"`;
|
||||
|
||||
await connection
|
||||
.update({ [params.field]: `= ${escapedParam}` })
|
||||
.schema(params.schema)
|
||||
.from(params.table)
|
||||
.where({ [params.primary]: `= ${params.id}` })
|
||||
.run();
|
||||
|
||||
return { reload };
|
||||
}
|
||||
|
||||
static async deleteTableRows (connection, params) {
|
||||
|
@@ -40,8 +40,7 @@ export default {
|
||||
ModalSettings: () => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings')
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
};
|
||||
return {};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
@@ -64,30 +63,30 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
html,
|
||||
body{
|
||||
height: 100%;
|
||||
}
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#wrapper{
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
#wrapper {
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#window-content{
|
||||
display: flex;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
#window-content {
|
||||
display: flex;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#main-content {
|
||||
padding: 0;
|
||||
justify-content: flex-start;
|
||||
height: calc(100vh - #{$excluding-size});
|
||||
width: calc(100% - #{$settingbar-width});
|
||||
#main-content {
|
||||
padding: 0;
|
||||
justify-content: flex-start;
|
||||
height: calc(100vh - #{$excluding-size});
|
||||
width: calc(100% - #{$settingbar-width});
|
||||
|
||||
> .columns{
|
||||
height: calc(100vh - #{$footer-height});
|
||||
}
|
||||
}
|
||||
> .columns {
|
||||
height: calc(100vh - #{$footer-height});
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="modal modal-sm active">
|
||||
<div class="modal active" :class="modalSizeClass">
|
||||
<a class="modal-overlay" @click="hideModal" />
|
||||
<div class="modal-container">
|
||||
<div v-if="hasHeader" class="modal-header">
|
||||
@@ -29,13 +29,13 @@
|
||||
class="btn btn-primary mr-2"
|
||||
@click="confirmModal"
|
||||
>
|
||||
{{ $t('word.confirm') }}
|
||||
{{ confirmText || $t('word.confirm') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-link"
|
||||
@click="hideModal"
|
||||
>
|
||||
{{ $t('word.cancel') }}
|
||||
{{ cancelText || $t('word.cancel') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -45,6 +45,15 @@
|
||||
<script>
|
||||
export default {
|
||||
name: 'BaseConfirmModal',
|
||||
props: {
|
||||
size: {
|
||||
type: String,
|
||||
validator: prop => ['small', 'medium', 'large'].includes(prop),
|
||||
default: 'small'
|
||||
},
|
||||
confirmText: String,
|
||||
cancelText: String
|
||||
},
|
||||
computed: {
|
||||
hasHeader () {
|
||||
return !!this.$slots.header;
|
||||
@@ -54,6 +63,13 @@ export default {
|
||||
},
|
||||
hasDefault () {
|
||||
return !!this.$slots.default;
|
||||
},
|
||||
modalSizeClass () {
|
||||
if (this.size === 'small')
|
||||
return 'modal-sm';
|
||||
else if (this.size === 'large')
|
||||
return 'modal-lg';
|
||||
else return '';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -70,7 +86,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.modal.modal-sm .modal-container{
|
||||
padding: 0;
|
||||
}
|
||||
.modal.modal-sm .modal-container {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
@@ -38,58 +38,57 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.context{
|
||||
.context {
|
||||
display: flex;
|
||||
color: $body-font-color;
|
||||
font-size: 16px;
|
||||
z-index: 400;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
padding: 0.4rem;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
|
||||
.context-container {
|
||||
min-width: 100px;
|
||||
max-width: 150px;
|
||||
z-index: 1;
|
||||
box-shadow: 0 0 1px 0 #000;
|
||||
padding: 0;
|
||||
background: #1d1d1d;
|
||||
border-radius: 0.1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
pointer-events: initial;
|
||||
|
||||
.context-element {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.1rem 0.3rem;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.context-overlay {
|
||||
background: transparent;
|
||||
bottom: 0;
|
||||
cursor: default;
|
||||
display: block;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
color: $body-font-color;
|
||||
font-size: 16px;
|
||||
z-index: 400;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
padding: 0.4rem;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
|
||||
.context-container{
|
||||
min-width: 100px;
|
||||
max-width: 150px;
|
||||
z-index: 1;
|
||||
box-shadow: 0px 0px 1px 0px #000;
|
||||
padding: 0;
|
||||
background: #1d1d1d;
|
||||
border-radius: 0.1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
pointer-events: initial;
|
||||
|
||||
.context-element{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: .1rem .3rem;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover{
|
||||
background: $primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.context-overlay{
|
||||
background: transparent;
|
||||
bottom: 0;
|
||||
cursor: default;
|
||||
display: block;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@@ -71,25 +71,24 @@ export default {
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.toast{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
user-select: text;
|
||||
word-break: break-all;
|
||||
width: fit-content;
|
||||
margin-left: auto;
|
||||
}
|
||||
.toast {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
user-select: text;
|
||||
word-break: break-all;
|
||||
width: fit-content;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.notification-message{
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
max-width: 30rem;
|
||||
user-select: none;
|
||||
}
|
||||
.notification-message {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: inline-block;
|
||||
max-width: 30rem;
|
||||
}
|
||||
|
||||
.expanded .notification-message{
|
||||
white-space: initial;
|
||||
}
|
||||
.expanded .notification-message {
|
||||
white-space: initial;
|
||||
}
|
||||
</style>
|
||||
|
@@ -70,10 +70,10 @@ export default {
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.toast{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
user-select: text;
|
||||
word-break: break-all;
|
||||
}
|
||||
.toast {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
user-select: text;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
|
@@ -71,7 +71,3 @@ export default {
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
@@ -36,7 +36,7 @@
|
||||
<option value="maria">
|
||||
MariaDB
|
||||
</option>
|
||||
<option value="mssql">
|
||||
<!-- <option value="mssql">
|
||||
Microsoft SQL
|
||||
</option>
|
||||
<option value="pg">
|
||||
@@ -44,7 +44,7 @@
|
||||
</option>
|
||||
<option value="oracledb">
|
||||
Oracle DB
|
||||
</option>
|
||||
</option> -->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -230,7 +230,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.modal-container{
|
||||
max-width: 450px;
|
||||
}
|
||||
.modal-container {
|
||||
max-width: 450px;
|
||||
}
|
||||
</style>
|
||||
|
@@ -40,7 +40,7 @@
|
||||
<option value="maria">
|
||||
MariaDB
|
||||
</option>
|
||||
<option value="mssql">
|
||||
<!-- <option value="mssql">
|
||||
Microsoft SQL
|
||||
</option>
|
||||
<option value="pg">
|
||||
@@ -48,7 +48,7 @@
|
||||
</option>
|
||||
<option value="oracledb">
|
||||
Oracle DB
|
||||
</option>
|
||||
</option> -->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -148,7 +148,7 @@
|
||||
<script>
|
||||
import { mapActions } from 'vuex';
|
||||
import Connection from '@/ipc-api/Connection';
|
||||
import { uidGen } from 'common/libs/utilities';
|
||||
import { uidGen } from 'common/libs/uidGen';
|
||||
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
||||
import BaseToast from '@/components/BaseToast';
|
||||
|
||||
@@ -253,7 +253,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.modal-container{
|
||||
max-width: 450px;
|
||||
}
|
||||
.modal-container {
|
||||
max-width: 450px;
|
||||
}
|
||||
</style>
|
||||
|
@@ -45,8 +45,8 @@
|
||||
|
||||
<div v-if="selectedTab === 'general'" class="panel-body py-4">
|
||||
<form class="form-horizontal">
|
||||
<div class="col-6 col-sm-12">
|
||||
<div class="form-group">
|
||||
<div class="col-8 col-sm-12">
|
||||
<div class="form-group mb-4">
|
||||
<div class="col-6 col-sm-12">
|
||||
<label class="form-label">
|
||||
<i class="material-icons md-18 mr-1">translate</i>
|
||||
@@ -69,6 +69,25 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-6 col-sm-12">
|
||||
<label class="form-label">
|
||||
{{ $t('message.notificationsTimeout') }}:
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-6 col-sm-12">
|
||||
<div class="input-group">
|
||||
<input
|
||||
v-model="localTimeout"
|
||||
class="form-input"
|
||||
type="number"
|
||||
min="1"
|
||||
@focusout="checkNotificationsTimeout"
|
||||
>
|
||||
<span class="input-group-addon">{{ $t('word.seconds') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -115,6 +134,7 @@ export default {
|
||||
return {
|
||||
isUpdate: false,
|
||||
localLocale: null,
|
||||
localTimeout: null,
|
||||
selectedTab: 'general'
|
||||
};
|
||||
},
|
||||
@@ -123,7 +143,8 @@ export default {
|
||||
appName: 'application/appName',
|
||||
appVersion: 'application/appVersion',
|
||||
selectedSettingTab: 'application/selectedSettingTab',
|
||||
selectedLocale: 'settings/getLocale'
|
||||
selectedLocale: 'settings/getLocale',
|
||||
notificationsTimeout: 'settings/getNotificationsTimeout'
|
||||
}),
|
||||
locales () {
|
||||
const locales = [];
|
||||
@@ -135,42 +156,49 @@ export default {
|
||||
},
|
||||
created () {
|
||||
this.localLocale = this.selectedLocale;
|
||||
this.localTimeout = this.notificationsTimeout;
|
||||
this.selectedTab = this.selectedSettingTab;
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
closeModal: 'application/hideSettingModal',
|
||||
changeLocale: 'settings/changeLocale'
|
||||
changeLocale: 'settings/changeLocale',
|
||||
updateNotificationsTimeout: 'settings/updateNotificationsTimeout'
|
||||
}),
|
||||
selectTab (tab) {
|
||||
this.selectedTab = tab;
|
||||
},
|
||||
openOutside (link) {
|
||||
shell.openExternal(link);
|
||||
},
|
||||
checkNotificationsTimeout () {
|
||||
if (!this.localTimeout)
|
||||
this.localTimeout = 10;
|
||||
|
||||
this.updateNotificationsTimeout(+this.localTimeout);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
#settings{
|
||||
.modal-body{
|
||||
overflow: hidden;
|
||||
#settings {
|
||||
.modal-body {
|
||||
overflow: hidden;
|
||||
|
||||
.panel-body{
|
||||
height: calc(70vh - 70px);
|
||||
overflow: auto;
|
||||
}
|
||||
.panel-body {
|
||||
height: calc(70vh - 70px);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.badge::after{
|
||||
background: #32b643;
|
||||
}
|
||||
|
||||
.form-label{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
.badge::after {
|
||||
background: #32b643;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -78,7 +78,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.empty{
|
||||
color: $body-font-color;
|
||||
.empty {
|
||||
color: $body-font-color;
|
||||
}
|
||||
</style>
|
||||
|
@@ -69,21 +69,20 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.editor-wrapper{
|
||||
border-bottom: 1px solid #444444;
|
||||
}
|
||||
.editor-wrapper {
|
||||
border-bottom: 1px solid #444;
|
||||
}
|
||||
|
||||
.CodeMirror{
|
||||
height: 200px;
|
||||
.CodeMirror {
|
||||
height: 200px;
|
||||
|
||||
.CodeMirror-scroll{
|
||||
max-width: 100%;
|
||||
}
|
||||
.CodeMirror-scroll {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.CodeMirror-line {
|
||||
word-break: break-word!important;
|
||||
white-space: pre-wrap!important;
|
||||
word-break: normal;
|
||||
}
|
||||
}
|
||||
.CodeMirror-line {
|
||||
word-break: break-word !important;
|
||||
white-space: pre-wrap !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -69,7 +69,3 @@ export default {
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
@@ -26,12 +26,12 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.empty{
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.empty {
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
@@ -50,41 +50,41 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
#footer{
|
||||
height: $footer-height;
|
||||
#footer {
|
||||
height: $footer-height;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: $primary-color;
|
||||
padding: 0 0.2rem;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
box-shadow: 0 0 1px 0 #000;
|
||||
|
||||
.footer-elements {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: $primary-color;
|
||||
padding: 0 .2rem;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
box-shadow: 0 0 1px 0px #000;
|
||||
|
||||
.footer-elements{
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.footer-element {
|
||||
height: $footer-height;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 0.4rem;
|
||||
margin: 0;
|
||||
|
||||
.footer-element{
|
||||
height: $footer-height;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 .4rem;
|
||||
margin: 0;
|
||||
&.footer-link {
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
|
||||
&.footer-link{
|
||||
cursor: pointer;
|
||||
transition: background .2s;
|
||||
|
||||
&:hover{
|
||||
background: rgba($color: #fff, $alpha: .1);
|
||||
}
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background: rgba($color: #fff, $alpha: 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,5 +1,9 @@
|
||||
<template>
|
||||
<div id="notifications-board">
|
||||
<div
|
||||
id="notifications-board"
|
||||
@mouseenter="clearTimeouts"
|
||||
@mouseleave="rearmTimeouts"
|
||||
>
|
||||
<transition-group name="slide-fade">
|
||||
<BaseNotification
|
||||
v-for="notification in latestNotifications"
|
||||
@@ -21,27 +25,60 @@ export default {
|
||||
components: {
|
||||
BaseNotification
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
timeouts: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
notifications: 'notifications/getNotifications'
|
||||
notifications: 'notifications/getNotifications',
|
||||
notificationsTimeout: 'settings/getNotificationsTimeout'
|
||||
}),
|
||||
latestNotifications () {
|
||||
return this.notifications.slice(0, 10);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
notifications: {
|
||||
deep: true,
|
||||
handler: function (notification) {
|
||||
if (notification.length) {
|
||||
this.timeouts[notification[0].uid] = setTimeout(() => {
|
||||
this.removeNotification(notification[0].uid);
|
||||
delete this.timeouts[notification.uid];
|
||||
}, this.notificationsTimeout * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
removeNotification: 'notifications/removeNotification'
|
||||
})
|
||||
}),
|
||||
clearTimeouts () {
|
||||
for (const uid in this.timeouts) {
|
||||
clearTimeout(this.timeouts[uid]);
|
||||
delete this.timeouts[uid];
|
||||
}
|
||||
},
|
||||
rearmTimeouts () {
|
||||
for (const notification of this.notifications) {
|
||||
this.timeouts[notification.uid] = setTimeout(() => {
|
||||
this.removeNotification(notification.uid);
|
||||
delete this.timeouts[notification.uid];
|
||||
}, this.notificationsTimeout * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
#notifications-board{
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
right: 1rem;
|
||||
bottom: 1rem;
|
||||
}
|
||||
#notifications-board {
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
right: 1rem;
|
||||
bottom: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
@@ -106,101 +106,98 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
#settingbar{
|
||||
width: $settingbar-width;
|
||||
height: calc(100vh - #{$excluding-size});
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
#settingbar {
|
||||
width: $settingbar-width;
|
||||
height: calc(100vh - #{$excluding-size});
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: $bg-color-light;
|
||||
padding: 0;
|
||||
box-shadow: 0 0 1px 0 #000;
|
||||
z-index: 9;
|
||||
|
||||
.settingbar-top-elements {
|
||||
overflow-x: hidden;
|
||||
overflow-y: overlay;
|
||||
max-height: calc((100vh - 3.5rem) - #{$excluding-size});
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.settingbar-bottom-elements {
|
||||
padding-top: 0.5rem;
|
||||
background: $bg-color-light;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.settingbar-elements {
|
||||
list-style: none;
|
||||
text-align: center;
|
||||
width: $settingbar-width;
|
||||
padding: 0;
|
||||
box-shadow: 0 0 1px 0px #000;
|
||||
z-index: 9;
|
||||
margin: 0;
|
||||
|
||||
.settingbar-top-elements{
|
||||
overflow-x: hidden;
|
||||
overflow-y: overlay;
|
||||
max-height: calc((100vh - 3.5rem) - #{$excluding-size});
|
||||
.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;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 3px;
|
||||
}
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-left-color: $body-font-color;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.settingbar-element-icon {
|
||||
&.badge::after {
|
||||
bottom: -10px;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
background: $success-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settingbar-bottom-elements{
|
||||
padding-top: .5rem;
|
||||
background: $bg-color-light;
|
||||
z-index: 1;
|
||||
}
|
||||
.ex-tooltip {// Because both overflow-x: visible and overflow-y:auto are evil!!!
|
||||
.ex-tooltip-content {
|
||||
z-index: 999;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
display: block;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
margin: 0 0 0 calc(#{$settingbar-width} - 5px);
|
||||
left: 0;
|
||||
padding: 0.2rem 0.4rem;
|
||||
font-size: 0.7rem;
|
||||
background: rgba(48, 55, 66, 0.95);
|
||||
border-radius: 0.1rem;
|
||||
color: #fff;
|
||||
max-width: 320px;
|
||||
pointer-events: none;
|
||||
text-overflow: ellipsis;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.settingbar-elements{
|
||||
list-style: none;
|
||||
text-align: center;
|
||||
width: $settingbar-width;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
.settingbar-element{
|
||||
height: $settingbar-width;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
border-left: 3px solid transparent;
|
||||
opacity: .5;
|
||||
transition: opacity .2s;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
|
||||
&:hover{
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.selected{
|
||||
border-left-color: $body-font-color;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.settingbar-element-icon{
|
||||
|
||||
&.badge::after{
|
||||
bottom: -10px;
|
||||
right: 0;
|
||||
position: absolute;
|
||||
background: $success-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.ex-tooltip{// Because both overflow-x: visible and overflow-y:auto are evil!!!
|
||||
.ex-tooltip-content{
|
||||
z-index: 999;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
display:block;
|
||||
position:absolute;
|
||||
background-color:#feffe1;
|
||||
text-align: center;
|
||||
margin:.0 0 0 calc(#{$settingbar-width} - 5px);
|
||||
left: 0;
|
||||
padding: .2rem .4rem;
|
||||
font-size: .7rem;
|
||||
background: rgba(48,55,66,.95);
|
||||
border-radius: .1rem;
|
||||
color: #fff;
|
||||
max-width: 320px;
|
||||
pointer-events: none;
|
||||
text-overflow: ellipsis;
|
||||
transition: opacity .2s;
|
||||
}
|
||||
|
||||
&:hover .ex-tooltip-content{
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
&:hover .ex-tooltip-content {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<img class="titlebar-logo" :src="require('@/images/logo.svg').default">
|
||||
</div>
|
||||
<div class="titlebar-elements">
|
||||
<!-- -->
|
||||
{{ windowTitle }}
|
||||
</div>
|
||||
<div class="titlebar-elements">
|
||||
<div
|
||||
@@ -37,7 +37,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { remote } from 'electron';
|
||||
import { remote, ipcRenderer } from 'electron';
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'TheTitleBar',
|
||||
@@ -48,6 +49,22 @@ export default {
|
||||
isDevelopment: process.env.NODE_ENV === 'development'
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
getConnectionName: 'connections/getConnectionName',
|
||||
selectedWorkspace: 'workspaces/getSelected',
|
||||
getWorkspace: 'workspaces/getWorkspace'
|
||||
}),
|
||||
windowTitle () {
|
||||
if (!this.selectedWorkspace) return '';
|
||||
|
||||
const connectionName = this.getConnectionName(this.selectedWorkspace);
|
||||
const workspace = this.getWorkspace(this.selectedWorkspace);
|
||||
const breadcrumbs = Object.values(workspace.breadcrumbs).filter(breadcrumb => breadcrumb);
|
||||
|
||||
return [connectionName, ...breadcrumbs].join(' • ');
|
||||
}
|
||||
},
|
||||
created () {
|
||||
window.addEventListener('resize', this.onResize);
|
||||
},
|
||||
@@ -56,7 +73,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
closeApp () {
|
||||
this.w.close();
|
||||
ipcRenderer.send('closeApp');
|
||||
},
|
||||
minimizeApp () {
|
||||
this.w.minimize();
|
||||
@@ -81,55 +98,55 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
#titlebar{
|
||||
#titlebar {
|
||||
display: flex;
|
||||
position: relative;
|
||||
justify-content: space-between;
|
||||
background: $bg-color-light;
|
||||
align-items: center;
|
||||
height: $titlebar-height;
|
||||
-webkit-app-region: drag;
|
||||
user-select: none;
|
||||
box-shadow: 0 0 1px 0 #000;
|
||||
z-index: 9999;
|
||||
|
||||
.titlebar-resizer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
z-index: 999;
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
.titlebar-elements {
|
||||
display: flex;
|
||||
position: relative;
|
||||
justify-content: space-between;
|
||||
background: $bg-color-light;
|
||||
align-items: center;
|
||||
height: $titlebar-height;
|
||||
-webkit-app-region: drag;
|
||||
user-select: none;
|
||||
box-shadow: 0 0 1px 0px #000;
|
||||
z-index: 9999;
|
||||
|
||||
.titlebar-resizer{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
z-index: 999;
|
||||
-webkit-app-region: no-drag;
|
||||
.titlebar-logo {
|
||||
height: $titlebar-height;
|
||||
padding: 0 0.4rem;
|
||||
}
|
||||
|
||||
.titlebar-elements{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.titlebar-element {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: $titlebar-height;
|
||||
line-height: 0;
|
||||
padding: 0 0.7rem;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.2s;
|
||||
-webkit-app-region: no-drag;
|
||||
|
||||
.titlebar-logo{
|
||||
height: $titlebar-height;
|
||||
padding: 0 .4rem;
|
||||
}
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
background: rgba($color: #fff, $alpha: 0.2);
|
||||
}
|
||||
|
||||
.titlebar-element{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: $titlebar-height;
|
||||
line-height: 0;
|
||||
padding: 0 .7rem;
|
||||
opacity: .7;
|
||||
transition: opacity .2s;
|
||||
-webkit-app-region: no-drag;
|
||||
|
||||
&:hover{
|
||||
opacity: 1;
|
||||
background: rgba($color: #fff, $alpha: .2);
|
||||
}
|
||||
|
||||
&.close-button:hover{
|
||||
background: red;
|
||||
}
|
||||
}
|
||||
&.close-button:hover {
|
||||
background: red;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -92,94 +92,94 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.workspace{
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
.workspace {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
.workspace-tabs{
|
||||
overflow: auto;
|
||||
height: calc(100vh - #{$excluding-size});
|
||||
.workspace-tabs {
|
||||
overflow: auto;
|
||||
height: calc(100vh - #{$excluding-size});
|
||||
|
||||
.tab-block{
|
||||
background: $bg-color-light;
|
||||
margin-top: 0;
|
||||
.tab-block {
|
||||
background: $bg-color-light;
|
||||
margin-top: 0;
|
||||
|
||||
.tab-item{
|
||||
max-width: 12rem;
|
||||
width: fit-content;
|
||||
flex: initial;
|
||||
.tab-item {
|
||||
max-width: 12rem;
|
||||
width: fit-content;
|
||||
flex: initial;
|
||||
|
||||
&.active a{
|
||||
opacity: 1;
|
||||
}
|
||||
&.active a {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
> a{
|
||||
padding: .2rem .8rem;
|
||||
color: $body-font-color;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
opacity: .7;
|
||||
transition: opacity .2s;
|
||||
> a {
|
||||
padding: 0.2rem 0.8rem;
|
||||
color: $body-font-color;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.2s;
|
||||
|
||||
&:hover{
|
||||
opacity: 1;
|
||||
}
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
> span {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-query-results{
|
||||
overflow: auto;
|
||||
white-space: nowrap;
|
||||
|
||||
.table{
|
||||
width: auto;
|
||||
border-collapse: separate;
|
||||
|
||||
.th{
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: $bg-color;
|
||||
border: 1px solid;
|
||||
border-left: none;
|
||||
border-bottom-width: 2px;
|
||||
border-color: $bg-color-light;
|
||||
padding: 0;
|
||||
font-weight: 700;
|
||||
font-size: .7rem;
|
||||
|
||||
> div {
|
||||
padding: .1rem .4rem;
|
||||
min-width: -webkit-fill-available;
|
||||
}
|
||||
}
|
||||
|
||||
.td{
|
||||
border-right: 1px solid;
|
||||
border-bottom: 1px solid;
|
||||
border-color: $bg-color-light;
|
||||
padding: 0 .4rem;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 200px;
|
||||
white-space: nowrap;
|
||||
> span {
|
||||
overflow: hidden;
|
||||
font-size: .7rem;
|
||||
|
||||
&:focus{
|
||||
box-shadow:inset 0px 0px 0px 1px $body-font-color;
|
||||
background: rgba($color: #000000, $alpha: .3);
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-query-results {
|
||||
overflow: auto;
|
||||
white-space: nowrap;
|
||||
|
||||
.table {
|
||||
width: auto;
|
||||
border-collapse: separate;
|
||||
|
||||
.th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: $bg-color;
|
||||
border: 1px solid;
|
||||
border-left: none;
|
||||
border-bottom-width: 2px;
|
||||
border-color: $bg-color-light;
|
||||
padding: 0;
|
||||
font-weight: 700;
|
||||
font-size: 0.7rem;
|
||||
|
||||
> div {
|
||||
padding: 0.1rem 0.4rem;
|
||||
min-width: -webkit-fill-available;
|
||||
}
|
||||
}
|
||||
|
||||
.td {
|
||||
border-right: 1px solid;
|
||||
border-bottom: 1px solid;
|
||||
border-color: $bg-color-light;
|
||||
padding: 0 0.4rem;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 200px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
font-size: 0.7rem;
|
||||
|
||||
&:focus {
|
||||
box-shadow: inset 0 0 0 1px $body-font-color;
|
||||
background: rgba($color: #000, $alpha: 0.3);
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -72,11 +72,11 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.empty{
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.empty {
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
|
@@ -123,70 +123,70 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.workspace-explorebar-resizer{
|
||||
position: absolute;
|
||||
width: 4px;
|
||||
right: -2px;
|
||||
top: 0;
|
||||
height: calc(100vh - #{$excluding-size});
|
||||
cursor: ew-resize;
|
||||
z-index: 99;
|
||||
}
|
||||
.workspace-explorebar-resizer {
|
||||
position: absolute;
|
||||
width: 4px;
|
||||
right: -2px;
|
||||
top: 0;
|
||||
height: calc(100vh - #{$excluding-size});
|
||||
cursor: ew-resize;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.workspace-explorebar{
|
||||
width: $explorebar-width;
|
||||
.workspace-explorebar {
|
||||
width: $explorebar-width;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
text-align: left;
|
||||
background: $bg-color-gray;
|
||||
box-shadow: 0 0 1px 0 #000;
|
||||
z-index: 8;
|
||||
flex: initial;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
|
||||
.workspace-explorebar-header {
|
||||
width: 100%;
|
||||
padding: 0.3rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
text-align: left;
|
||||
background: $bg-color-gray;
|
||||
box-shadow: 0 0 1px 0px #000;
|
||||
z-index: 8;
|
||||
flex: initial;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
justify-content: space-between;
|
||||
font-size: 0.6rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
|
||||
.workspace-explorebar-header{
|
||||
width: 100%;
|
||||
padding: .3rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: .6rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
|
||||
.workspace-explorebar-title{
|
||||
width: 80%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: block;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.workspace-explorebar-tools {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> i{
|
||||
opacity: .6;
|
||||
transition: opacity .2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:hover{
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.workspace-explorebar-title {
|
||||
width: 80%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: block;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.workspace-explorebar-body{
|
||||
width: 100%;
|
||||
height: calc((100vh - 30px) - #{$excluding-size});
|
||||
overflow: overlay;
|
||||
padding: 0 .1rem;
|
||||
.workspace-explorebar-tools {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> i {
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-explorebar-body {
|
||||
width: 100%;
|
||||
height: calc((100vh - 30px) - #{$excluding-size});
|
||||
overflow: overlay;
|
||||
padding: 0 0.1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -56,35 +56,35 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.workspace-explorebar-database{
|
||||
.database-name,
|
||||
a.table-name{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: .1rem;
|
||||
cursor: pointer;
|
||||
font-size: .7rem;
|
||||
.workspace-explorebar-database {
|
||||
.database-name,
|
||||
a.table-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.1rem;
|
||||
cursor: pointer;
|
||||
font-size: 0.7rem;
|
||||
|
||||
> span{
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
&:hover{
|
||||
color: $body-font-color;
|
||||
background: rgba($color: #FFF, $alpha: .05);
|
||||
border-radius: 2px;
|
||||
}
|
||||
> span {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.menu-item{
|
||||
line-height: 1.2;
|
||||
&:hover {
|
||||
color: $body-font-color;
|
||||
background: rgba($color: #fff, $alpha: 0.05);
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.database-tables{
|
||||
margin-left: 1.2rem;
|
||||
}
|
||||
}
|
||||
.menu-item {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.database-tables {
|
||||
margin-left: 1.2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -8,15 +8,11 @@
|
||||
class="btn btn-link btn-sm"
|
||||
:class="{'loading':isQuering}"
|
||||
:disabled="!query"
|
||||
@click="runQuery"
|
||||
@click="runQuery(query)"
|
||||
>
|
||||
<span>{{ $t('word.run') }}</span>
|
||||
<i class="material-icons text-success">play_arrow</i>
|
||||
</button>
|
||||
<!-- <button class="btn btn-link btn-sm">
|
||||
<span>{{ $t('word.save') }}</span>
|
||||
<i class="material-icons ml-1">save</i>
|
||||
</button> -->
|
||||
</div>
|
||||
<div class="workspace-query-info">
|
||||
<div v-if="results.rows">
|
||||
@@ -33,7 +29,7 @@
|
||||
v-if="results"
|
||||
ref="queryTable"
|
||||
:results="results"
|
||||
:fields="resultsFields"
|
||||
:fields="fields"
|
||||
@updateField="updateField"
|
||||
@deleteSelected="deleteSelected"
|
||||
/>
|
||||
@@ -62,6 +58,7 @@ export default {
|
||||
data () {
|
||||
return {
|
||||
query: '',
|
||||
lastQuery: '',
|
||||
isQuering: false,
|
||||
results: {},
|
||||
fields: []
|
||||
@@ -74,25 +71,6 @@ export default {
|
||||
workspace () {
|
||||
return this.getWorkspace(this.connection.uid);
|
||||
},
|
||||
resultsFields () {
|
||||
if (this.results) {
|
||||
return this.fields.map(field => { // TODO: move to main process
|
||||
return {
|
||||
name: field.COLUMN_NAME,
|
||||
key: field.COLUMN_KEY.toLowerCase(),
|
||||
type: field.DATA_TYPE,
|
||||
precision: field.DATETIME_PRECISION
|
||||
};
|
||||
}).filter(field => {
|
||||
if (this.results.fields) {
|
||||
const queryFields = this.results.fields.map(field => field.name);
|
||||
if (queryFields.includes(field.name)) return field;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
return [];
|
||||
},
|
||||
table () {
|
||||
if (this.results.fields.length)
|
||||
return this.results.fields[0].orgTable;
|
||||
@@ -103,15 +81,15 @@ export default {
|
||||
...mapActions({
|
||||
addNotification: 'notifications/addNotification'
|
||||
}),
|
||||
async runQuery () {
|
||||
if (!this.query) return;
|
||||
async runQuery (query) {
|
||||
if (!query) return;
|
||||
this.isQuering = true;
|
||||
this.results = {};
|
||||
|
||||
try {
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
query: this.query,
|
||||
query,
|
||||
schema: this.workspace.breadcrumbs.schema
|
||||
};
|
||||
|
||||
@@ -134,7 +112,7 @@ export default {
|
||||
|
||||
const { status, response } = await Tables.getTableColumns(params);
|
||||
if (status === 'success')
|
||||
this.fields = response.rows;
|
||||
this.fields = response;
|
||||
else
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
}
|
||||
@@ -143,43 +121,46 @@ export default {
|
||||
}
|
||||
|
||||
this.isQuering = false;
|
||||
this.lastQuery = query;
|
||||
},
|
||||
reloadTable () {
|
||||
this.runQuery(this.lastQuery);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.workspace-tabs{
|
||||
align-content: baseline;
|
||||
.workspace-tabs {
|
||||
align-content: baseline;
|
||||
|
||||
.workspace-query-runner{
|
||||
.workspace-query-runner {
|
||||
.workspace-query-runner-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.3rem 0.6rem 0.4rem;
|
||||
align-items: center;
|
||||
|
||||
.workspace-query-runner-footer{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: .3rem .6rem .4rem;
|
||||
align-items: center;
|
||||
.workspace-query-buttons {
|
||||
display: flex;
|
||||
|
||||
.workspace-query-buttons{
|
||||
display: flex;
|
||||
|
||||
.btn{
|
||||
display: flex;
|
||||
align-self: center;
|
||||
color: $body-font-color;
|
||||
margin-right: .4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-query-info{
|
||||
display: flex;
|
||||
|
||||
> div + div{
|
||||
padding-left: .6rem;
|
||||
}
|
||||
}
|
||||
.btn {
|
||||
display: flex;
|
||||
align-self: center;
|
||||
color: $body-font-color;
|
||||
margin-right: 0.4rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-query-info {
|
||||
display: flex;
|
||||
|
||||
> div + div {
|
||||
padding-left: 0.6rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@@ -66,7 +66,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { uidGen } from 'common/libs/utilities';
|
||||
import { uidGen } from 'common/libs/uidGen';
|
||||
import BaseVirtualScroll from '@/components/BaseVirtualScroll';
|
||||
import WorkspaceQueryTableCell from '@/components/WorkspaceQueryTableCell';
|
||||
import TableContext from '@/components/WorkspaceQueryTableContext';
|
||||
@@ -269,46 +269,46 @@ export default {
|
||||
|
||||
<style lang="scss">
|
||||
.vscroll {
|
||||
height: 1000px;
|
||||
overflow: auto;
|
||||
overflow-anchor: none;
|
||||
height: 1000px;
|
||||
overflow: auto;
|
||||
overflow-anchor: none;
|
||||
}
|
||||
|
||||
.column-resizable{
|
||||
&:hover,
|
||||
&:active{
|
||||
resize: horizontal;
|
||||
overflow: hidden;
|
||||
}
|
||||
.column-resizable {
|
||||
&:hover,
|
||||
&:active {
|
||||
resize: horizontal;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.table-column-title{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.table-column-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sort-icon{
|
||||
font-size: .7rem;
|
||||
line-height: 1;
|
||||
margin-left: .2rem;
|
||||
.sort-icon {
|
||||
font-size: 0.7rem;
|
||||
line-height: 1;
|
||||
margin-left: 0.2rem;
|
||||
}
|
||||
|
||||
.column-key{
|
||||
transform: rotate(90deg);
|
||||
font-size: .7rem;
|
||||
line-height: 1.5;
|
||||
margin-right: .2rem;
|
||||
.column-key {
|
||||
transform: rotate(90deg);
|
||||
font-size: 0.7rem;
|
||||
line-height: 1.5;
|
||||
margin-right: 0.2rem;
|
||||
|
||||
&.key-pri{
|
||||
color: goldenrod;
|
||||
}
|
||||
&.key-pri {
|
||||
color: goldenrod;
|
||||
}
|
||||
|
||||
&.key-uni{
|
||||
color: deepskyblue;
|
||||
}
|
||||
&.key-uni {
|
||||
color: deepskyblue;
|
||||
}
|
||||
|
||||
&.key-mul{
|
||||
color: palegreen;
|
||||
}
|
||||
&.key-mul {
|
||||
color: palegreen;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -2,15 +2,14 @@
|
||||
<div
|
||||
v-if="field !== '_id'"
|
||||
ref="cell"
|
||||
class="td"
|
||||
:class="`type-${type} p-0`"
|
||||
class="td p-0"
|
||||
tabindex="0"
|
||||
@contextmenu.prevent="$emit('contextmenu', $event)"
|
||||
>
|
||||
<span
|
||||
v-if="!isEditing"
|
||||
v-if="!isInlineEditor"
|
||||
class="cell-content px-2"
|
||||
:class="isNull(content)"
|
||||
:class="`${isNull(content)} type-${type}`"
|
||||
@dblclick="editON"
|
||||
>{{ content | typeFormat(type, precision) | cutText }}</span>
|
||||
<template v-else>
|
||||
@@ -34,18 +33,102 @@
|
||||
@blur="editOFF"
|
||||
>
|
||||
</template>
|
||||
<ConfirmModal
|
||||
v-if="isTextareaEditor"
|
||||
:confirm-text="$t('word.update')"
|
||||
size="medium"
|
||||
@confirm="editOFF"
|
||||
@hide="hideEditorModal"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
{{ $t('word.edit') }} "{{ field }}"
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
<div class="mb-2">
|
||||
<div>
|
||||
<textarea
|
||||
v-model="localContent"
|
||||
class="form-input textarea-editor"
|
||||
/>
|
||||
</div>
|
||||
<div class="editor-field-info">
|
||||
<div><b>{{ $t('word.size') }}</b>: {{ localContent.length }}</div>
|
||||
<div><b>{{ $t('word.type') }}</b>: {{ type.toUpperCase() }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ConfirmModal>
|
||||
<ConfirmModal
|
||||
v-if="isBlobEditor"
|
||||
:confirm-text="$t('word.update')"
|
||||
@confirm="editOFF"
|
||||
@hide="hideEditorModal"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
{{ $t('word.edit') }} "{{ field }}"
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
<div class="mb-2">
|
||||
<transition name="jump-down">
|
||||
<div v-if="contentInfo.size">
|
||||
<img
|
||||
v-if="isImage"
|
||||
:src="`data:${contentInfo.mime};base64, ${bufferToBase64(localContent)}`"
|
||||
class="img-responsive p-centered bg-checkered"
|
||||
>
|
||||
<div v-else class="text-center">
|
||||
<i class="material-icons md-36">insert_drive_file</i>
|
||||
</div>
|
||||
<div class="editor-buttons mt-2">
|
||||
<button class="btn btn-link btn-sm" @click="downloadFile">
|
||||
<span>{{ $t('word.download') }}</span>
|
||||
<i class="material-icons ml-1">file_download</i>
|
||||
</button>
|
||||
<button class="btn btn-link btn-sm" @click="prepareToDelete">
|
||||
<span>{{ $t('word.delete') }}</span>
|
||||
<i class="material-icons ml-1">delete_forever</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
<div class="editor-field-info">
|
||||
<div>
|
||||
<b>{{ $t('word.size') }}</b>: {{ localContent.length | formatBytes }}<br>
|
||||
<b>{{ $t('word.mimeType') }}</b>: {{ contentInfo.mime }}
|
||||
</div>
|
||||
<div><b>{{ $t('word.type') }}</b>: {{ type.toUpperCase() }}</div>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<label>{{ $t('message.uploadFile') }}</label>
|
||||
<input
|
||||
class="form-input"
|
||||
type="file"
|
||||
@change="filesChange($event)"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ConfirmModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment';
|
||||
import { mimeFromHex, formatBytes } from 'common/libs/utilities';
|
||||
import { mimeFromHex } from 'common/libs/mimeFromHex';
|
||||
import { formatBytes } from 'common/libs/formatBytes';
|
||||
import { bufferToBase64 } from 'common/libs/bufferToBase64';
|
||||
import hexToBinary from 'common/libs/hexToBinary';
|
||||
import { TEXT, LONG_TEXT, NUMBER, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
|
||||
import { mask } from 'vue-the-mask';
|
||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||
|
||||
export default {
|
||||
name: 'WorkspaceQueryTableCell',
|
||||
components: {
|
||||
ConfirmModal
|
||||
},
|
||||
filters: {
|
||||
formatBytes,
|
||||
cutText (val) {
|
||||
if (typeof val !== 'string') return val;
|
||||
return val.length > 128 ? `${val.substring(0, 128)}[...]` : val;
|
||||
@@ -53,39 +136,31 @@ export default {
|
||||
typeFormat (val, type, precision) {
|
||||
if (!val) return val;
|
||||
|
||||
switch (type) {
|
||||
case 'char':
|
||||
case 'varchar':
|
||||
case 'text':
|
||||
case 'mediumtext':
|
||||
return val;
|
||||
case 'date': {
|
||||
return moment(val).isValid() ? moment(val).format('YYYY-MM-DD') : val;
|
||||
}
|
||||
case 'datetime':
|
||||
case 'timestamp': {
|
||||
let datePrecision = '';
|
||||
for (let i = 0; i < precision; i++)
|
||||
datePrecision += i === 0 ? '.S' : 'S';
|
||||
if (DATE.includes(type))
|
||||
return moment(val).isValid() ? moment(val).format('YYYY-MM-DD') : val;
|
||||
|
||||
return moment(val).isValid() ? moment(val).format(`YYYY-MM-DD HH:mm:ss${datePrecision}`) : val;
|
||||
}
|
||||
case 'blob':
|
||||
case 'mediumblob':
|
||||
case 'longblob': {
|
||||
const buff = Buffer.from(val);
|
||||
if (!buff.length) return '';
|
||||
if (DATETIME.includes(type)) {
|
||||
let datePrecision = '';
|
||||
for (let i = 0; i < precision; i++)
|
||||
datePrecision += i === 0 ? '.S' : 'S';
|
||||
|
||||
const hex = buff.toString('hex').substring(0, 8).toUpperCase();
|
||||
return `${mimeFromHex(hex).mime} (${formatBytes(buff.length)})`;
|
||||
}
|
||||
case 'bit': {
|
||||
const hex = Buffer.from(val).toString('hex');
|
||||
return hexToBinary(hex);
|
||||
}
|
||||
default:
|
||||
return val;
|
||||
return moment(val).isValid() ? moment(val).format(`YYYY-MM-DD HH:mm:ss${datePrecision}`) : val;
|
||||
}
|
||||
|
||||
if (BLOB.includes(type)) {
|
||||
const buff = Buffer.from(val);
|
||||
if (!buff.length) return '';
|
||||
|
||||
const hex = buff.toString('hex').substring(0, 8).toUpperCase();
|
||||
return `${mimeFromHex(hex).mime} (${formatBytes(buff.length)})`;
|
||||
}
|
||||
|
||||
if (BIT.includes(type)) {
|
||||
const hex = Buffer.from(val).toString('hex');
|
||||
return hexToBinary(hex);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
},
|
||||
directives: {
|
||||
@@ -99,81 +174,188 @@ export default {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isEditing: false,
|
||||
localContent: ''
|
||||
isInlineEditor: false,
|
||||
isTextareaEditor: false,
|
||||
isBlobEditor: false,
|
||||
willBeDeleted: false,
|
||||
localContent: null,
|
||||
contentInfo: {
|
||||
ext: '',
|
||||
mime: '',
|
||||
size: null
|
||||
},
|
||||
fileToUpload: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
inputProps () {
|
||||
switch (this.type) {
|
||||
case 'char':
|
||||
case 'varchar':
|
||||
case 'text':
|
||||
case 'mediumtext':
|
||||
return { type: 'text', mask: false };
|
||||
case 'int':
|
||||
case 'tinyint':
|
||||
case 'smallint':
|
||||
case 'mediumint':
|
||||
return { type: 'number', mask: false };
|
||||
case 'date':
|
||||
return { type: 'text', mask: '####-##-##' };
|
||||
case 'datetime':
|
||||
case 'timestamp': {
|
||||
let datetimeMask = '####-##-## ##:##:##';
|
||||
for (let i = 0; i < this.precision; i++)
|
||||
datetimeMask += i === 0 ? '.#' : '#';
|
||||
return { type: 'text', mask: datetimeMask };
|
||||
}
|
||||
case 'blob':
|
||||
case 'mediumblob':
|
||||
case 'longblob':
|
||||
case 'bit':
|
||||
return { type: 'file', mask: false };
|
||||
default:
|
||||
return 'hidden';
|
||||
if ([...TEXT, ...LONG_TEXT].includes(this.type))
|
||||
return { type: 'text', mask: false };
|
||||
|
||||
if (NUMBER.includes(this.type))
|
||||
return { type: 'number', mask: false };
|
||||
|
||||
if (TIME.includes(this.type))
|
||||
return { type: 'number', mask: false };
|
||||
|
||||
if (DATE.includes(this.type))
|
||||
return { type: 'text', mask: '####-##-##' };
|
||||
|
||||
if (DATETIME.includes(this.type)) {
|
||||
let datetimeMask = '####-##-## ##:##:##';
|
||||
for (let i = 0; i < this.precision; i++)
|
||||
datetimeMask += i === 0 ? '.#' : '#';
|
||||
return { type: 'text', mask: datetimeMask };
|
||||
}
|
||||
|
||||
if (BLOB.includes(this.type))
|
||||
return { type: 'file', mask: false };
|
||||
|
||||
if (BIT.includes(this.type))
|
||||
return { type: 'text', mask: false };
|
||||
|
||||
return { type: 'text', mask: false };
|
||||
},
|
||||
isImage () {
|
||||
return ['gif', 'jpg', 'png', 'bmp', 'ico', 'tif'].includes(this.contentInfo.ext);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isNull (value) {
|
||||
return value === null ? ' is-null' : '';
|
||||
},
|
||||
bufferToBase64 (val) {
|
||||
return bufferToBase64(val);
|
||||
},
|
||||
editON () {
|
||||
if (['file'].includes(this.inputProps.type)) return;// TODO: remove temporary file block
|
||||
if (LONG_TEXT.includes(this.type)) {
|
||||
this.isTextareaEditor = true;
|
||||
this.localContent = this.$options.filters.typeFormat(this.content, this.type);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
if (BLOB.includes(this.type)) {
|
||||
this.isBlobEditor = true;
|
||||
this.localContent = this.content ? this.content : '';
|
||||
this.fileToUpload = null;
|
||||
this.willBeDeleted = false;
|
||||
|
||||
if (this.content !== null) {
|
||||
const buff = Buffer.from(this.localContent);
|
||||
if (buff.length) {
|
||||
const hex = buff.toString('hex').substring(0, 8).toUpperCase();
|
||||
const { ext, mime } = mimeFromHex(hex);
|
||||
this.contentInfo = {
|
||||
ext,
|
||||
mime,
|
||||
size: this.localContent.length
|
||||
};
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Inline editable fields
|
||||
this.localContent = this.$options.filters.typeFormat(this.content, this.type);
|
||||
this.$nextTick(() => { // Focus on input
|
||||
this.$refs.cell.blur();
|
||||
|
||||
this.$nextTick(() => this.$refs.editField.focus());
|
||||
});
|
||||
this.localContent = this.$options.filters.typeFormat(this.content, this.type);
|
||||
this.isEditing = true;
|
||||
this.isInlineEditor = true;
|
||||
},
|
||||
editOFF () {
|
||||
this.isEditing = false;
|
||||
if (this.localContent === this.$options.filters.typeFormat(this.content, this.type)) return;
|
||||
this.isInlineEditor = false;
|
||||
let content;
|
||||
if (!['blob', 'mediumblob', 'longblob'].includes(this.type)) {
|
||||
if (this.localContent === this.$options.filters.typeFormat(this.content, this.type)) return;// If not changed
|
||||
content = this.localContent;
|
||||
}
|
||||
else { // Handle file upload
|
||||
if (this.willBeDeleted) {
|
||||
content = '';
|
||||
this.willBeDeleted = false;
|
||||
}
|
||||
else {
|
||||
if (!this.fileToUpload) return;
|
||||
content = this.fileToUpload.file.path;
|
||||
}
|
||||
}
|
||||
|
||||
const { field, type, localContent: content } = this;
|
||||
this.$emit('updateField', { field, type, content });
|
||||
this.$emit('updateField', {
|
||||
field: this.field,
|
||||
type: this.type,
|
||||
content
|
||||
});
|
||||
},
|
||||
hideEditorModal () {
|
||||
this.isTextareaEditor = false;
|
||||
this.isBlobEditor = false;
|
||||
},
|
||||
downloadFile () {
|
||||
const downloadLink = document.createElement('a');
|
||||
|
||||
downloadLink.href = `data:${this.contentInfo.mime};base64, ${bufferToBase64(this.localContent)}`;
|
||||
downloadLink.setAttribute('download', `${this.field}.${this.contentInfo.ext}`);
|
||||
document.body.appendChild(downloadLink);
|
||||
|
||||
downloadLink.click();
|
||||
downloadLink.remove();
|
||||
},
|
||||
filesChange (event) {
|
||||
const { files } = event.target;
|
||||
if (!files.length) return;
|
||||
|
||||
this.fileToUpload = { name: files[0].name, file: files[0] };
|
||||
this.willBeDeleted = false;
|
||||
},
|
||||
prepareToDelete () {
|
||||
this.localContent = '';
|
||||
this.contentInfo = {
|
||||
ext: '',
|
||||
mime: '',
|
||||
size: null
|
||||
};
|
||||
this.willBeDeleted = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.editable-field{
|
||||
margin: 0;
|
||||
border: none;
|
||||
line-height: 1;
|
||||
width: 100%;
|
||||
.editable-field {
|
||||
margin: 0;
|
||||
border: none;
|
||||
line-height: 1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cell-content{
|
||||
display: block;
|
||||
min-height: .8rem;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
.cell-content {
|
||||
display: block;
|
||||
min-height: 0.8rem;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.textarea-editor {
|
||||
height: 50vh !important;
|
||||
}
|
||||
|
||||
.editor-field-info {
|
||||
margin-top: 0.6rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.editor-buttons {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
|
||||
.btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
|
||||
<ConfirmModal
|
||||
v-if="isConfirmModal"
|
||||
@confirm="deleteRows()"
|
||||
@confirm="deleteRows"
|
||||
@hide="hideConfirmModal"
|
||||
>
|
||||
<template :slot="'header'">
|
||||
@@ -67,7 +67,3 @@ export default {
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
@@ -6,15 +6,19 @@
|
||||
<button
|
||||
class="btn btn-link btn-sm"
|
||||
:class="{'loading':isQuering}"
|
||||
@click="getTableData"
|
||||
@click="reloadTable"
|
||||
>
|
||||
<span>{{ $t('word.refresh') }}</span>
|
||||
<i class="material-icons ml-1">refresh</i>
|
||||
</button>
|
||||
<!-- <button class="btn btn-link btn-sm">
|
||||
<span>{{ $t('word.save') }}</span>
|
||||
<i class="material-icons ml-1">save</i>
|
||||
</button> -->
|
||||
<button
|
||||
class="btn btn-link btn-sm"
|
||||
:class="{'disabled':isQuering}"
|
||||
@click="showAddModal"
|
||||
>
|
||||
<span>{{ $t('word.add') }}</span>
|
||||
<i class="material-icons ml-1">playlist_add</i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="workspace-query-info">
|
||||
<div v-if="results.rows">
|
||||
@@ -31,7 +35,7 @@
|
||||
v-if="results"
|
||||
ref="queryTable"
|
||||
:results="results"
|
||||
:fields="resultsFields"
|
||||
:fields="fields"
|
||||
@updateField="updateField"
|
||||
@deleteSelected="deleteSelected"
|
||||
/>
|
||||
@@ -72,16 +76,6 @@ export default {
|
||||
},
|
||||
isSelected () {
|
||||
return this.workspace.selected_tab === 1;
|
||||
},
|
||||
resultsFields () {
|
||||
return this.fields.map(field => { // TODO: move to main process
|
||||
return {
|
||||
name: field.COLUMN_NAME,
|
||||
key: field.COLUMN_KEY.toLowerCase(),
|
||||
type: field.DATA_TYPE,
|
||||
precision: field.DATETIME_PRECISION
|
||||
};
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -119,7 +113,7 @@ export default {
|
||||
try {
|
||||
const { status, response } = await Tables.getTableColumns(params);
|
||||
if (status === 'success')
|
||||
this.fields = response.rows;
|
||||
this.fields = response;
|
||||
else
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
}
|
||||
@@ -140,43 +134,46 @@ export default {
|
||||
}
|
||||
|
||||
this.isQuering = false;
|
||||
}
|
||||
},
|
||||
reloadTable () {
|
||||
this.getTableData();
|
||||
},
|
||||
showAddModal () {}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.workspace-tabs{
|
||||
align-content: baseline;
|
||||
.workspace-tabs {
|
||||
align-content: baseline;
|
||||
|
||||
.workspace-query-runner{
|
||||
.workspace-query-runner {
|
||||
.workspace-query-runner-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.3rem 0.6rem 0.4rem;
|
||||
align-items: center;
|
||||
|
||||
.workspace-query-runner-footer{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: .3rem .6rem .4rem;
|
||||
align-items: center;
|
||||
.workspace-query-buttons {
|
||||
display: flex;
|
||||
|
||||
.workspace-query-buttons{
|
||||
display: flex;
|
||||
|
||||
.btn{
|
||||
display: flex;
|
||||
align-self: center;
|
||||
color: $body-font-color;
|
||||
margin-right: .4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-query-info{
|
||||
display: flex;
|
||||
|
||||
> div + div{
|
||||
padding-left: .6rem;
|
||||
}
|
||||
}
|
||||
.btn {
|
||||
display: flex;
|
||||
align-self: center;
|
||||
color: $body-font-color;
|
||||
margin-right: 0.4rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-query-info {
|
||||
display: flex;
|
||||
|
||||
> div + div {
|
||||
padding-left: 0.6rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@@ -29,7 +29,13 @@ module.exports = {
|
||||
donate: 'Donate',
|
||||
run: 'Run',
|
||||
schema: 'Schema',
|
||||
results: 'Results'
|
||||
results: 'Results',
|
||||
size: 'Size',
|
||||
seconds: 'Seconds',
|
||||
type: 'Type',
|
||||
mimeType: 'Mime-Type',
|
||||
download: 'Download',
|
||||
add: 'Add'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Welcome to Antares SQL Client!',
|
||||
@@ -55,7 +61,9 @@ module.exports = {
|
||||
unableEditFieldWithoutPrimary: 'Unable to edit a field without a primary key in resultset',
|
||||
editCell: 'Edit cell',
|
||||
deleteRows: 'Delete row | Delete {count} rows',
|
||||
confirmToDeleteRows: 'Do you confirm to delete one row? | Do you confirm to delete {count} rows?'
|
||||
confirmToDeleteRows: 'Do you confirm to delete one row? | Do you confirm to delete {count} rows?',
|
||||
notificationsTimeout: 'Notifications timeout',
|
||||
uploadFile: 'Upload file'
|
||||
},
|
||||
// Date and Time
|
||||
short: {
|
||||
|
@@ -27,7 +27,9 @@ module.exports = {
|
||||
language: 'Lingua',
|
||||
version: 'Versione',
|
||||
donate: 'Dona',
|
||||
run: 'Esegui'
|
||||
run: 'Esegui',
|
||||
schema: 'Schema',
|
||||
results: 'Results'
|
||||
},
|
||||
message: {
|
||||
appWelcome: 'Benvenuto in Antares SQL Client!',
|
||||
@@ -41,7 +43,19 @@ module.exports = {
|
||||
deleteConnection: 'Elimina connessione',
|
||||
deleteConnectionCorfirm: 'Confermi l\'eliminazione di',
|
||||
connectionSuccessfullyMade: 'Connessione avvenuta con successo!',
|
||||
madeWithJS: 'Fatto con 💛 e JavaScript!'
|
||||
madeWithJS: 'Fatto con 💛 e JavaScript!',
|
||||
checkForUpdates: 'Cerca aggiornamenti',
|
||||
noUpdatesAvailable: 'Nessun aggiornamento disponibile',
|
||||
checkingForUpdate: 'Controllo aggiornamenti in corso',
|
||||
checkFailure: 'Controllo fallito, riprova più tardi',
|
||||
updateAvailable: 'Aggiornamento disponibile',
|
||||
downloadingUpdate: 'Download dell\'aggiornamento',
|
||||
updateDownloaded: 'Aggiornamento scaricato',
|
||||
restartToInstall: 'Riavvia Antares per installare l\'aggiornamento',
|
||||
unableEditFieldWithoutPrimary: 'Impossibile modificare il campo senza una primary key nel resultset',
|
||||
editCell: 'Modifica cella',
|
||||
deleteRows: 'Elimina riga | Elimina {count} righe',
|
||||
confirmToDeleteRows: 'Confermi di voler cancellare una riga? | Confermi di voler cancellare {count} righe?'
|
||||
},
|
||||
// Date and Time
|
||||
short: {
|
||||
|
@@ -3,6 +3,8 @@ import Tables from '@/ipc-api/Tables';
|
||||
export default {
|
||||
methods: {
|
||||
async updateField (payload) {
|
||||
this.isQuering = true;
|
||||
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
schema: this.workspace.breadcrumbs.schema,
|
||||
@@ -12,16 +14,24 @@ export default {
|
||||
|
||||
try {
|
||||
const { status, response } = await Tables.updateTableCell(params);
|
||||
if (status === 'success')
|
||||
this.$refs.queryTable.applyUpdate(payload);
|
||||
if (status === 'success') {
|
||||
if (response.reload)// Needed for blob fields
|
||||
this.reloadTable();
|
||||
else
|
||||
this.$refs.queryTable.applyUpdate(payload);
|
||||
}
|
||||
else
|
||||
this.addNotification({ status: 'error', message: response });
|
||||
}
|
||||
catch (err) {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
|
||||
this.isQuering = false;
|
||||
},
|
||||
async deleteSelected (payload) {
|
||||
this.isQuering = true;
|
||||
|
||||
const params = {
|
||||
uid: this.connection.uid,
|
||||
schema: this.workspace.breadcrumbs.schema,
|
||||
@@ -42,6 +52,8 @@ export default {
|
||||
catch (err) {
|
||||
this.addNotification({ status: 'error', message: err.stack });
|
||||
}
|
||||
|
||||
this.isQuering = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -1,43 +1,46 @@
|
||||
@mixin type-colors($types) {
|
||||
@each $type, $color in $types {
|
||||
.type-#{$type} {
|
||||
color: $color;
|
||||
@each $type, $color in $types {
|
||||
.type-#{$type} {
|
||||
color: $color;
|
||||
|
||||
@if $type == "number" {
|
||||
text-align: right;
|
||||
}
|
||||
@if $type == "number" {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include type-colors(
|
||||
(
|
||||
"char": seagreen,
|
||||
"varchar": seagreen,
|
||||
"text": seagreen,
|
||||
"mediumtext": seagreen,
|
||||
"longtext": seagreen,
|
||||
"int": cornflowerblue,
|
||||
"tinyint": cornflowerblue,
|
||||
"smallint": cornflowerblue,
|
||||
"mediumint": cornflowerblue,
|
||||
"bigint": cornflowerblue,
|
||||
"datetime": coral,
|
||||
"date": coral,
|
||||
"time": coral,
|
||||
"timestamp": coral,
|
||||
"bit": lightskyblue,
|
||||
"blob": darkorchid,
|
||||
"mediumblob": darkorchid,
|
||||
"longblob": darkorchid,
|
||||
"unknown": gray,
|
||||
)
|
||||
(
|
||||
"char": seagreen,
|
||||
"varchar": seagreen,
|
||||
"text": seagreen,
|
||||
"mediumtext": seagreen,
|
||||
"longtext": seagreen,
|
||||
"int": cornflowerblue,
|
||||
"tinyint": cornflowerblue,
|
||||
"smallint": cornflowerblue,
|
||||
"mediumint": cornflowerblue,
|
||||
"float": cornflowerblue,
|
||||
"double": cornflowerblue,
|
||||
"decimal": cornflowerblue,
|
||||
"bigint": cornflowerblue,
|
||||
"datetime": coral,
|
||||
"date": coral,
|
||||
"time": coral,
|
||||
"timestamp": coral,
|
||||
"bit": lightskyblue,
|
||||
"blob": darkorchid,
|
||||
"mediumblob": darkorchid,
|
||||
"longblob": darkorchid,
|
||||
"unknown": gray,
|
||||
)
|
||||
);
|
||||
|
||||
.is-null {
|
||||
color: gray;
|
||||
color: gray;
|
||||
|
||||
&::after {
|
||||
content: "NULL";
|
||||
}
|
||||
&::after {
|
||||
content: "NULL";
|
||||
}
|
||||
}
|
||||
|
@@ -1,26 +1,26 @@
|
||||
.dbi {
|
||||
display: inline-block;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
background-size: cover;
|
||||
display: inline-block;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
background-size: cover;
|
||||
|
||||
&.dbi-mysql {
|
||||
background-image: url("../images/svg/mysql.svg");
|
||||
}
|
||||
&.dbi-mysql {
|
||||
background-image: url("../images/svg/mysql.svg");
|
||||
}
|
||||
|
||||
&.dbi-maria {
|
||||
background-image: url("../images/svg/mariadb.svg");
|
||||
}
|
||||
&.dbi-maria {
|
||||
background-image: url("../images/svg/mariadb.svg");
|
||||
}
|
||||
|
||||
&.dbi-mssql {
|
||||
background-image: url("../images/svg/mssql.svg");
|
||||
}
|
||||
&.dbi-mssql {
|
||||
background-image: url("../images/svg/mssql.svg");
|
||||
}
|
||||
|
||||
&.dbi-pg {
|
||||
background-image: url("../images/svg/pg.svg");
|
||||
}
|
||||
&.dbi-pg {
|
||||
background-image: url("../images/svg/pg.svg");
|
||||
}
|
||||
|
||||
&.dbi-oracledb {
|
||||
background-image: url("../images/svg/oracledb.svg");
|
||||
}
|
||||
&.dbi-oracledb {
|
||||
background-image: url("../images/svg/oracledb.svg");
|
||||
}
|
||||
}
|
||||
|
@@ -5,12 +5,35 @@
|
||||
display: table;
|
||||
table-layout: fixed;
|
||||
|
||||
&.table-striped {
|
||||
.tbody {
|
||||
.tr:nth-of-type(odd) {
|
||||
background: $bg-color;
|
||||
}
|
||||
}
|
||||
.tbody {
|
||||
display: table-row-group;
|
||||
}
|
||||
|
||||
.tr {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
// Scollable tables
|
||||
&.table-scroll {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding-bottom: 0.75rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.thead {
|
||||
display: table-header-group;
|
||||
}
|
||||
|
||||
.td,
|
||||
.th {
|
||||
border-bottom: $border-width solid $border-color;
|
||||
padding: $unit-3 $unit-2;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.th {
|
||||
border-bottom-width: $border-width-lg;
|
||||
}
|
||||
|
||||
&,
|
||||
@@ -20,6 +43,7 @@
|
||||
&.selected {
|
||||
background: #333 !important;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: $bg-color-dark;
|
||||
}
|
||||
@@ -37,33 +61,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Scollable tables
|
||||
&.table-scroll {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding-bottom: 0.75rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.thead {
|
||||
display: table-header-group;
|
||||
}
|
||||
|
||||
.tbody {
|
||||
display: table-row-group;
|
||||
}
|
||||
|
||||
.tr {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.td,
|
||||
.th {
|
||||
border-bottom: $border-width solid $border-color;
|
||||
padding: $unit-3 $unit-2;
|
||||
display: table-cell;
|
||||
}
|
||||
.th {
|
||||
border-bottom-width: $border-width-lg;
|
||||
&.table-striped {
|
||||
.tbody {
|
||||
.tr:nth-of-type(odd) {
|
||||
background: $bg-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,16 +1,38 @@
|
||||
.material-icons {
|
||||
// TODO: rewrite with rem
|
||||
|
||||
.material-icons{// TODO: rewrite with rem
|
||||
/* Rules for sizing the icon. */
|
||||
&.md-18 { font-size: 18px; }
|
||||
&.md-24 { font-size: 24px; }
|
||||
&.md-36 { font-size: 36px; }
|
||||
&.md-48 { font-size: 48px; }
|
||||
|
||||
/* Rules for using icons as black on a light background. */
|
||||
&.md-dark { color: rgba(0, 0, 0, 0.54); }
|
||||
&.md-dark.md-inactive { color: rgba(0, 0, 0, 0.26); }
|
||||
|
||||
/* Rules for using icons as white on a dark background. */
|
||||
&.md-light { color: rgba(255, 255, 255, 1); }
|
||||
&.md-light.md-inactive { color: rgba(255, 255, 255, 0.3); }
|
||||
}
|
||||
/* Rules for sizing the icon. */
|
||||
&.md-18 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
&.md-24 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
&.md-36 {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
&.md-48 {
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
/* Rules for using icons as black on a light background. */
|
||||
&.md-dark {
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
}
|
||||
|
||||
&.md-dark.md-inactive {
|
||||
color: rgba(0, 0, 0, 0.26);
|
||||
}
|
||||
|
||||
/* Rules for using icons as white on a dark background. */
|
||||
&.md-light {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
&.md-light.md-inactive {
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,41 @@
|
||||
.slide-fade-enter-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.slide-fade-leave-active {
|
||||
transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
|
||||
}
|
||||
|
||||
.slide-fade-enter,
|
||||
.slide-fade-leave-to {
|
||||
transform: translateX(10px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.jump-down-enter-active {
|
||||
animation: jump-down-in 0.2s;
|
||||
}
|
||||
|
||||
.jump-down-leave-active {
|
||||
animation: jump-down-in 0.2s reverse;
|
||||
}
|
||||
|
||||
@keyframes jump-down-in {
|
||||
0% {
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
/*Colors*/
|
||||
/* Colors */
|
||||
$body-bg: #1d1d1d;
|
||||
$body-font-color: #fff;
|
||||
$bg-color: #1d1d1d;
|
||||
@@ -9,9 +9,9 @@ $success-color: #32b643;
|
||||
$error-color: #de3b28;
|
||||
$warning-color: #e0a40c;
|
||||
|
||||
/*Sizes*/
|
||||
/* Sizes */
|
||||
$titlebar-height: 1.5rem;
|
||||
$settingbar-width: 3rem;
|
||||
$explorebar-width: 14rem;
|
||||
$footer-height: 1.5rem;
|
||||
$excluding-size: $footer-height + $titlebar-height;
|
||||
$excluding-size: $footer-height + $titlebar-height;
|
||||
|
@@ -9,109 +9,119 @@
|
||||
@import "~spectre.css/src/spectre-exp";
|
||||
|
||||
body {
|
||||
user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/*Additions*/
|
||||
/* Additions */
|
||||
@include margin-variant(3, $unit-3);
|
||||
@include margin-variant(4, $unit-4);
|
||||
@include padding-variant(3, $unit-3);
|
||||
@include padding-variant(4, $unit-4);
|
||||
|
||||
.btn.btn-gray {
|
||||
color: #fff;
|
||||
background: $bg-color-gray;
|
||||
color: #fff;
|
||||
background: $bg-color-gray;
|
||||
|
||||
&:hover {
|
||||
background: $bg-color;
|
||||
}
|
||||
&:hover {
|
||||
background: $bg-color;
|
||||
}
|
||||
}
|
||||
|
||||
.p-vcentered {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.c-help {
|
||||
cursor: help;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.bg-checkered {
|
||||
background-image:
|
||||
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%);
|
||||
background-blend-mode: normal, difference, normal;
|
||||
background-size: 2em 2em;
|
||||
}
|
||||
|
||||
// Scrollbars
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: $bg-color-light;
|
||||
background: $bg-color-light;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba($color: #fff, $alpha: 0.5);
|
||||
background: rgba($color: #fff, $alpha: 0.5);
|
||||
|
||||
&:hover {
|
||||
background: rgba($color: #fff, $alpha: 1);
|
||||
}
|
||||
&:hover {
|
||||
background: rgba($color: #fff, $alpha: 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Animations
|
||||
@keyframes rotation {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
|
||||
.rotate {
|
||||
animation: rotation 0.8s infinite linear;
|
||||
animation: rotation 0.8s infinite linear;
|
||||
}
|
||||
|
||||
/*Override*/
|
||||
/* Override */
|
||||
.modal {
|
||||
.modal-overlay,
|
||||
&.active .modal-overlay {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
.modal-overlay,
|
||||
&.active .modal-overlay {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.modal-sm .modal-container,
|
||||
.modal-container {
|
||||
box-shadow: 0 0 1px 0px #000;
|
||||
padding: 0;
|
||||
background: $bg-color;
|
||||
.modal-container,
|
||||
.modal-sm .modal-container {
|
||||
box-shadow: 0 0 1px 0 #000;
|
||||
padding: 0;
|
||||
background: $bg-color;
|
||||
|
||||
.modal-header {
|
||||
padding: 0.4rem 0.8rem;
|
||||
text-transform: uppercase;
|
||||
background: $bg-color-gray;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.modal-header {
|
||||
padding: 0.4rem 0.8rem;
|
||||
text-transform: uppercase;
|
||||
background: $bg-color-gray;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab {
|
||||
border-color: #272727;
|
||||
border-color: #272727;
|
||||
}
|
||||
|
||||
.panel {
|
||||
border: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.badge {
|
||||
&[data-badge],
|
||||
&:not([data-badge]) {
|
||||
&::after {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
&[data-badge],
|
||||
&:not([data-badge]) {
|
||||
&::after {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-select {
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.form-select,
|
||||
@@ -119,30 +129,35 @@ body {
|
||||
.form-input,
|
||||
.form-checkbox .form-icon,
|
||||
.form-radio .form-icon {
|
||||
border-color: $bg-color-light;
|
||||
background: $bg-color-gray;
|
||||
border-color: $bg-color-light;
|
||||
background: $bg-color-gray;
|
||||
}
|
||||
|
||||
.form-select:not([multiple]):not([size]):focus {
|
||||
border-color: $primary-color;
|
||||
border-color: $primary-color;
|
||||
}
|
||||
|
||||
.input-group .input-group-addon {
|
||||
border-color: #3f3f3f;
|
||||
}
|
||||
|
||||
.menu {
|
||||
font-size: 0.7rem;
|
||||
.menu-item {
|
||||
+ .menu-item {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
font-size: 0.7rem;
|
||||
|
||||
.menu-item {
|
||||
+ .menu-item {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.accordion-body {
|
||||
max-height: 500rem !important;
|
||||
max-height: 500rem !important;
|
||||
}
|
||||
|
||||
.btn.loading {
|
||||
> .material-icons,
|
||||
> span {
|
||||
visibility: hidden;
|
||||
}
|
||||
> .material-icons,
|
||||
> span {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
'use strict';
|
||||
import { uidGen } from 'common/libs/utilities';
|
||||
import { uidGen } from 'common/libs/uidGen';
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
|
@@ -6,17 +6,22 @@ export default {
|
||||
strict: true,
|
||||
state: {
|
||||
locale: 'en-US',
|
||||
explorebar_size: null
|
||||
explorebar_size: null,
|
||||
notifications_timeout: 10
|
||||
},
|
||||
getters: {
|
||||
getLocale: state => state.locale,
|
||||
getExplorebarSize: state => state.explorebar_size
|
||||
getExplorebarSize: state => state.explorebar_size,
|
||||
getNotificationsTimeout: state => state.notifications_timeout
|
||||
},
|
||||
mutations: {
|
||||
SET_LOCALE (state, locale) {
|
||||
state.locale = locale;
|
||||
i18n.locale = locale;
|
||||
},
|
||||
SET_NOTIFICATIONS_TIMEOUT (state, timeout) {
|
||||
state.notifications_timeout = timeout;
|
||||
},
|
||||
SET_EXPLOREBAR_SIZE (state, size) {
|
||||
state.explorebar_size = size;
|
||||
}
|
||||
@@ -25,6 +30,9 @@ export default {
|
||||
changeLocale ({ commit }, locale) {
|
||||
commit('SET_LOCALE', locale);
|
||||
},
|
||||
updateNotificationsTimeout ({ commit }, timeout) {
|
||||
commit('SET_NOTIFICATIONS_TIMEOUT', timeout);
|
||||
},
|
||||
changeExplorebarSize ({ commit }, size) {
|
||||
commit('SET_EXPLOREBAR_SIZE', size);
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
import Connection from '@/ipc-api/Connection';
|
||||
import { uidGen } from 'common/libs/utilities';
|
||||
import { uidGen } from 'common/libs/uidGen';
|
||||
|
||||
function remapStructure (structure) {
|
||||
const databases = structure.map(table => table.TABLE_SCHEMA)
|
||||
|
Reference in New Issue
Block a user