mirror of
https://github.com/Fabio286/antares.git
synced 2025-06-05 21:59:22 +02:00
Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
3b4f1475df | |||
155154b43d | |||
a95b8d188c | |||
cb1fce6f99 | |||
0014f48079 | |||
fc35f271d7 | |||
65ad0e954d | |||
8cafade8b1 | |||
d13b708377 | |||
206597e5b8 | |||
c5458159d1 | |||
1476e899d1 | |||
797ab70e7c | |||
f81312aeb0 | |||
3ed5ea023e | |||
9291a7a7b4 | |||
5cfdc9b92d | |||
15b08d7ea8 | |||
acebe435ff | |||
5712b80022 | |||
d38583262e | |||
e0e2131981 |
7
.gitattributes
vendored
7
.gitattributes
vendored
@@ -1 +1,6 @@
|
|||||||
* text eol=lf
|
* text eol=lf
|
||||||
|
*.jpg binary
|
||||||
|
*.png binary
|
||||||
|
*.gif binary
|
||||||
|
*.ico binary
|
||||||
|
*.icns binary
|
5
.github/workflows/codeql-analysis.yml
vendored
5
.github/workflows/codeql-analysis.yml
vendored
@@ -31,11 +31,6 @@ jobs:
|
|||||||
# a pull request then we can checkout the head.
|
# a pull request then we can checkout the head.
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
# If this run was triggered by a pull request event, then checkout
|
|
||||||
# the head of the pull request instead of the merge commit.
|
|
||||||
- run: git checkout HEAD^2
|
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v1
|
uses: github/codeql-action/init@v1
|
||||||
|
11
.travis.yml
11
.travis.yml
@@ -1,9 +1,6 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
node_js: 12
|
node_js: 12
|
||||||
|
|
||||||
before_install:
|
|
||||||
- npm install
|
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
directories:
|
directories:
|
||||||
- node_modules
|
- node_modules
|
||||||
@@ -20,6 +17,9 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
include:
|
include:
|
||||||
- stage: Test
|
- stage: Test
|
||||||
|
before_install:
|
||||||
|
- sudo apt-get install libsecret-1-dev
|
||||||
|
- npm install
|
||||||
script:
|
script:
|
||||||
- npm test
|
- npm test
|
||||||
|
|
||||||
@@ -27,6 +27,9 @@ jobs:
|
|||||||
if: tag IS present
|
if: tag IS present
|
||||||
os: linux
|
os: linux
|
||||||
services: docker
|
services: docker
|
||||||
|
before_install:
|
||||||
|
- sudo apt-get install libsecret-1-dev
|
||||||
|
- npm install
|
||||||
script:
|
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"
|
- 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:
|
before_cache:
|
||||||
@@ -35,6 +38,8 @@ jobs:
|
|||||||
- stage: Deploy Mac
|
- stage: Deploy Mac
|
||||||
if: tag IS present
|
if: tag IS present
|
||||||
os: osx
|
os: osx
|
||||||
|
before_install:
|
||||||
|
- npm install
|
||||||
osx_image: xcode10.2
|
osx_image: xcode10.2
|
||||||
script:
|
script:
|
||||||
- npm run build -- -p always
|
- npm run build -- -p always
|
||||||
|
31
CHANGELOG.md
31
CHANGELOG.md
@@ -2,6 +2,37 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
### [0.0.12](https://github.com/Fabio286/antares/compare/v0.0.11...v0.0.12) (2020-12-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* better security connections credentials storage ([fc35f27](https://github.com/Fabio286/antares/commit/fc35f271d7fe384cd786ce33547c0ef17135ddd8))
|
||||||
|
* option to change editor theme ([a95b8d1](https://github.com/Fabio286/antares/commit/a95b8d188cfcc8f563ad73b4f0b676d068775d36))
|
||||||
|
* option to toggle editor auto completion ([155154b](https://github.com/Fabio286/antares/commit/155154b43d0cd02ae875ded3ce865a37a999da5c))
|
||||||
|
* query editor auto-completer for tables and columns ([cb1fce6](https://github.com/Fabio286/antares/commit/cb1fce6f998ea7332886820910e245ab19416a9d))
|
||||||
|
|
||||||
|
### [0.0.11](https://github.com/Fabio286/antares/compare/v0.0.10...v0.0.11) (2020-12-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* auto focus on first input in modals ([1476e89](https://github.com/Fabio286/antares/commit/1476e899d164562f12342ced8c76903b9bdcfa55))
|
||||||
|
* foreign keys management ([206597e](https://github.com/Fabio286/antares/commit/206597e5b891e13e6f7635075bd11599355ab778))
|
||||||
|
* improved data table sorts ([5712b80](https://github.com/Fabio286/antares/commit/5712b8002203b32027f0e820f98a61e2ec965e79))
|
||||||
|
* query tabs auto focus ([f81312a](https://github.com/Fabio286/antares/commit/f81312aeb02ef55affd2ae9e81a9b4cb4c2e6da2))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* data tab sort not maintained at refresh ([15b08d7](https://github.com/Fabio286/antares/commit/15b08d7ea858cb28111c7b548af9a13b1bf0da91))
|
||||||
|
* deletion of rows with non-numeric ID ([d385832](https://github.com/Fabio286/antares/commit/d38583262e672a2b47c5ad0aca0f13c129830a7b))
|
||||||
|
* file field editor not show ([9291a7a](https://github.com/Fabio286/antares/commit/9291a7a7b41e7aeb9b65c7f32e496f523e482272))
|
||||||
|
* improved changes dedection in props tab ([acebe43](https://github.com/Fabio286/antares/commit/acebe435ff6fa1581692fbf7ee1bc23b334e3947))
|
||||||
|
* some properties do not reset after fields changes ([3ed5ea0](https://github.com/Fabio286/antares/commit/3ed5ea023e1852d724b2b59ab156f8722876f85b))
|
||||||
|
* unable to switch tabs when no table selected ([c545815](https://github.com/Fabio286/antares/commit/c5458159d1e30cecf6615086c074d98b4b599637))
|
||||||
|
* wrong field type detection ([5cfdc9b](https://github.com/Fabio286/antares/commit/5cfdc9b92d4b778a7863b02fd64e6445236c89bc))
|
||||||
|
|
||||||
### [0.0.10](https://github.com/Fabio286/antares/compare/v0.0.9...v0.0.10) (2020-12-04)
|
### [0.0.10](https://github.com/Fabio286/antares/compare/v0.0.9...v0.0.10) (2020-12-04)
|
||||||
|
|
||||||
|
|
||||||
|
19
README.md
19
README.md
@@ -9,7 +9,9 @@
|
|||||||
Antares is an SQL client based on [Electron.js](https://github.com/electron/electron) and [Vue.js](https://github.com/vuejs/vue) that aims to become a useful tool, especially for developers.
|
Antares is an SQL client based on [Electron.js](https://github.com/electron/electron) and [Vue.js](https://github.com/vuejs/vue) 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.
|
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 an alpha, it lacks many features, and isn't ready as a main SQL client**. However i'm actively working on it (yes, i'm a lone dev), hoping to provide all essential features as soon as possible.
|
**At the moment this application is an alpha, it lacks many features** and supports only MySQL.
|
||||||
|
Most of its current features might be enough for basic MySQL use, so give it a chance and send me your feedback, I would really appreciate it.
|
||||||
|
I'm actively working on it (yes, i'm a lone dev), hoping to provide cool features and fixes 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/Fabio286/antares/releases).
|
🔗 If you are curious to try this early state of Antares you can download and install the [latest release](https://github.com/Fabio286/antares/releases).
|
||||||
👁 To stay tuned for new releases watch this repo on **Release only** channel.
|
👁 To stay tuned for new releases watch this repo on **Release only** channel.
|
||||||
@@ -29,33 +31,30 @@ An application created with minimalism and semplicity in mind, with features in
|
|||||||
|
|
||||||
- Multiple database connections at same time.
|
- Multiple database connections at same time.
|
||||||
- Database management (add/edit/delete).
|
- Database management (add/edit/delete).
|
||||||
- Tables fields management (add/edit/delete).
|
- Full tables management, including indexes and foreign keys.
|
||||||
- Tables content management (add/edit/delete).
|
|
||||||
- Run queries on multiple tabs.
|
- Run queries on multiple tabs.
|
||||||
- Query suggestions.
|
- Query suggestions and auto complete.
|
||||||
- Native dark theme.
|
- Native dark theme.
|
||||||
- Multi language.
|
- Multi language.
|
||||||
|
- Secure password storage.
|
||||||
- Auto updates.
|
- Auto updates.
|
||||||
|
|
||||||
## Coming soon
|
## Coming soon
|
||||||
|
|
||||||
This is a roadmap with major features will come in near future.
|
This is a roadmap with major features will come in near future.
|
||||||
|
|
||||||
- Tables management (add/edit/delete).
|
|
||||||
- Users management (add/edit/delete).
|
|
||||||
- Stored procedures, views, schedulers and triggers support.
|
- Stored procedures, views, schedulers and triggers support.
|
||||||
- More secure password storage.
|
- Users management (add/edit/delete).
|
||||||
- Database tools (variables, process list...).
|
- Database tools (variables, process list...).
|
||||||
|
- SSL and SSH tunnel support.
|
||||||
- Support for other databases.
|
- Support for other databases.
|
||||||
- Improvements of query editor area.
|
- UI/UX improvements.
|
||||||
- Improvements of query suggestions.
|
|
||||||
- Query history.
|
- Query history.
|
||||||
- More context menu shortcuts.
|
- More context menu shortcuts.
|
||||||
- More keyboard shortcuts.
|
- More keyboard shortcuts.
|
||||||
- Query logs console.
|
- Query logs console.
|
||||||
- Fake data filler.
|
- Fake data filler.
|
||||||
- Import/export and migration.
|
- Import/export and migration.
|
||||||
- SSL and SSH tunnel support.
|
|
||||||
- Themes.
|
- Themes.
|
||||||
|
|
||||||
## Currently supported
|
## Currently supported
|
||||||
|
5
jsconfig.json
Normal file
5
jsconfig.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"include": [
|
||||||
|
"./src/renderer/**/*"
|
||||||
|
]
|
||||||
|
}
|
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "antares",
|
"name": "antares",
|
||||||
"productName": "Antares",
|
"productName": "Antares",
|
||||||
"version": "0.0.10",
|
"version": "0.0.12",
|
||||||
"description": "A cross-platform easy to use SQL client.",
|
"description": "A cross-platform easy to use SQL client.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": "https://github.com/Fabio286/antares.git",
|
"repository": "https://github.com/Fabio286/antares.git",
|
||||||
@@ -48,11 +48,13 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/font": "^5.8.55",
|
"@mdi/font": "^5.8.55",
|
||||||
|
"ace-builds": "^1.4.12",
|
||||||
"electron-log": "^4.3.0",
|
"electron-log": "^4.3.0",
|
||||||
|
"electron-store": "^6.0.1",
|
||||||
"electron-updater": "^4.3.5",
|
"electron-updater": "^4.3.5",
|
||||||
|
"keytar": "^7.3.0",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"monaco-editor": "^0.20.0",
|
|
||||||
"mssql": "^6.2.3",
|
"mssql": "^6.2.3",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"pg": "^8.5.1",
|
"pg": "^8.5.1",
|
||||||
@@ -78,7 +80,6 @@
|
|||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
"eslint-plugin-vue": "^7.1.0",
|
"eslint-plugin-vue": "^7.1.0",
|
||||||
"monaco-editor-webpack-plugin": "^1.9.1",
|
|
||||||
"node-sass": "^5.0.0",
|
"node-sass": "^5.0.0",
|
||||||
"sass-loader": "^10.1.0",
|
"sass-loader": "^10.1.0",
|
||||||
"standard-version": "^9.0.0",
|
"standard-version": "^9.0.0",
|
||||||
|
@@ -206,7 +206,7 @@ module.exports = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'TIMESTAMP',
|
name: 'TIMESTAMP',
|
||||||
length: true,
|
length: false,
|
||||||
collation: false,
|
collation: false,
|
||||||
unsigned: false,
|
unsigned: false,
|
||||||
zerofill: false
|
zerofill: false
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
export const TEXT = ['char', 'varchar'];
|
export const TEXT = ['CHAR', 'VARCHAR'];
|
||||||
export const LONG_TEXT = ['text', 'mediumtext', 'longtext'];
|
export const LONG_TEXT = ['TEXT', 'MEDIUMTEXT', 'longtext'];
|
||||||
|
|
||||||
export const NUMBER = ['int', 'tinyint', 'smallint', 'mediumint', 'bigint', 'float', 'double', 'decimal', 'bool'];
|
export const NUMBER = ['INT', 'TINYINT', 'SMALLINT', 'MEDIUMINT', 'BIGINT', 'FLOAT', 'DOUBLE', 'DECIMAL', 'BOOL'];
|
||||||
|
|
||||||
export const DATE = ['date'];
|
export const DATE = ['DATE'];
|
||||||
export const TIME = ['time'];
|
export const TIME = ['TIME'];
|
||||||
export const DATETIME = ['datetime', 'timestamp'];
|
export const DATETIME = ['DATETIME', 'TIMESTAMP'];
|
||||||
|
|
||||||
export const BLOB = ['blob', 'mediumblob', 'longblob'];
|
export const BLOB = ['BLOB', 'MEDIUMBLOB', 'LONGBLOB'];
|
||||||
|
|
||||||
export const BIT = ['bit'];
|
export const BIT = ['BIT'];
|
||||||
|
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
import { app, BrowserWindow, nativeImage } from 'electron';
|
import { app, BrowserWindow, nativeImage } from 'electron';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import crypto from 'crypto';
|
||||||
import { format as formatUrl } from 'url';
|
import { format as formatUrl } from 'url';
|
||||||
|
import keytar from 'keytar';
|
||||||
import ipcHandlers from './ipc-handlers';
|
import ipcHandlers from './ipc-handlers';
|
||||||
|
|
||||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||||
@@ -89,7 +91,14 @@ else {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// create main BrowserWindow when electron is ready
|
// create main BrowserWindow when electron is ready
|
||||||
app.on('ready', () => {
|
app.on('ready', async () => {
|
||||||
|
let key = await keytar.getPassword('antares', 'user');
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
key = crypto.randomBytes(16).toString('hex');
|
||||||
|
keytar.setPassword('antares', 'user', key);
|
||||||
|
}
|
||||||
|
|
||||||
mainWindow = createMainWindow();
|
mainWindow = createMainWindow();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,13 @@
|
|||||||
|
import keytar from 'keytar';
|
||||||
import { app, ipcMain } from 'electron';
|
import { app, ipcMain } from 'electron';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
ipcMain.on('close-app', () => {
|
ipcMain.on('close-app', () => {
|
||||||
app.exit();
|
app.exit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.on('get-key', async event => {
|
||||||
|
const key = await keytar.getPassword('antares', 'user');
|
||||||
|
event.returnValue = key;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@@ -14,14 +14,18 @@ export default (connections) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('get-table-data', async (event, { uid, schema, table }) => {
|
ipcMain.handle('get-table-data', async (event, { uid, schema, table, sortParams }) => {
|
||||||
try {
|
try {
|
||||||
const result = await connections[uid]
|
const query = connections[uid]
|
||||||
.select('*')
|
.select('*')
|
||||||
.schema(schema)
|
.schema(schema)
|
||||||
.from(table)
|
.from(table)
|
||||||
.limit(1000)
|
.limit(1000);
|
||||||
.run({ details: true });
|
|
||||||
|
if (sortParams && sortParams.field && sortParams.dir)
|
||||||
|
query.orderBy({ [sortParams.field]: sortParams.dir.toUpperCase() });
|
||||||
|
|
||||||
|
const result = await query.run({ details: true });
|
||||||
|
|
||||||
return { status: 'success', response: result };
|
return { status: 'success', response: result };
|
||||||
}
|
}
|
||||||
@@ -89,11 +93,18 @@ export default (connections) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('delete-table-rows', async (event, params) => {
|
ipcMain.handle('delete-table-rows', async (event, params) => {
|
||||||
|
let idString;
|
||||||
|
|
||||||
|
if (typeof params.rows[0] === 'string')
|
||||||
|
idString = params.rows.map(row => `"${row}"`).join(',');
|
||||||
|
else
|
||||||
|
idString = params.rows.join(',');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await connections[params.uid]
|
const result = await connections[params.uid]
|
||||||
.schema(params.schema)
|
.schema(params.schema)
|
||||||
.delete(params.table)
|
.delete(params.table)
|
||||||
.where({ [params.primary]: `IN (${params.rows.join(',')})` })
|
.where({ [params.primary]: `IN (${idString})` })
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
return { status: 'success', response: result };
|
return { status: 'success', response: result };
|
||||||
|
@@ -177,7 +177,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
return {
|
return {
|
||||||
name: field.COLUMN_NAME,
|
name: field.COLUMN_NAME,
|
||||||
key: field.COLUMN_KEY.toLowerCase(),
|
key: field.COLUMN_KEY.toLowerCase(),
|
||||||
type: field.DATA_TYPE,
|
type: field.DATA_TYPE.toUpperCase(),
|
||||||
schema: field.TABLE_SCHEMA,
|
schema: field.TABLE_SCHEMA,
|
||||||
table: field.TABLE_NAME,
|
table: field.TABLE_NAME,
|
||||||
numPrecision: field.NUMERIC_PRECISION,
|
numPrecision: field.NUMERIC_PRECISION,
|
||||||
@@ -237,17 +237,27 @@ export class MySQLClient extends AntaresCore {
|
|||||||
.where({ TABLE_SCHEMA: `= '${schema}'`, TABLE_NAME: `= '${table}'`, REFERENCED_TABLE_NAME: 'IS NOT NULL' })
|
.where({ TABLE_SCHEMA: `= '${schema}'`, TABLE_NAME: `= '${table}'`, REFERENCED_TABLE_NAME: 'IS NOT NULL' })
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
|
const { rows: extras } = await this
|
||||||
|
.select('*')
|
||||||
|
.schema('information_schema')
|
||||||
|
.from('REFERENTIAL_CONSTRAINTS')
|
||||||
|
.where({ CONSTRAINT_SCHEMA: `= '${schema}'`, TABLE_NAME: `= '${table}'`, REFERENCED_TABLE_NAME: 'IS NOT NULL' })
|
||||||
|
.run();
|
||||||
|
|
||||||
return rows.map(field => {
|
return rows.map(field => {
|
||||||
|
const extra = extras.find(x => x.CONSTRAINT_NAME === field.CONSTRAINT_NAME);
|
||||||
return {
|
return {
|
||||||
schema: field.TABLE_SCHEMA,
|
schema: field.TABLE_SCHEMA,
|
||||||
table: field.TABLE_NAME,
|
table: field.TABLE_NAME,
|
||||||
column: field.COLUMN_NAME,
|
field: field.COLUMN_NAME,
|
||||||
position: field.ORDINAL_POSITION,
|
position: field.ORDINAL_POSITION,
|
||||||
constraintPosition: field.POSITION_IN_UNIQUE_CONSTRAINT,
|
constraintPosition: field.POSITION_IN_UNIQUE_CONSTRAINT,
|
||||||
constraintName: field.CONSTRAINT_NAME,
|
constraintName: field.CONSTRAINT_NAME,
|
||||||
refSchema: field.REFERENCED_TABLE_SCHEMA,
|
refSchema: field.REFERENCED_TABLE_SCHEMA,
|
||||||
refTable: field.REFERENCED_TABLE_NAME,
|
refTable: field.REFERENCED_TABLE_NAME,
|
||||||
refColumn: field.REFERENCED_COLUMN_NAME
|
refField: field.REFERENCED_COLUMN_NAME,
|
||||||
|
onUpdate: extra.UPDATE_RULE,
|
||||||
|
onDelete: extra.DELETE_RULE
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -346,6 +356,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
deletions,
|
deletions,
|
||||||
changes,
|
changes,
|
||||||
indexChanges,
|
indexChanges,
|
||||||
|
foreignChanges,
|
||||||
options
|
options
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
@@ -390,6 +401,11 @@ export class MySQLClient extends AntaresCore {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ADD FOREIGN KEYS
|
||||||
|
foreignChanges.additions.forEach(addition => {
|
||||||
|
alterColumns.push(`ADD CONSTRAINT \`${addition.constraintName}\` FOREIGN KEY (\`${addition.field}\`) REFERENCES \`${addition.refTable}\` (\`${addition.refField}\`) ON UPDATE ${addition.onUpdate} ON DELETE ${addition.onDelete}`);
|
||||||
|
});
|
||||||
|
|
||||||
// CHANGE FIELDS
|
// CHANGE FIELDS
|
||||||
changes.forEach(change => {
|
changes.forEach(change => {
|
||||||
const length = change.numLength || change.charLength || change.datePrecision;
|
const length = change.numLength || change.charLength || change.datePrecision;
|
||||||
@@ -427,6 +443,12 @@ export class MySQLClient extends AntaresCore {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// CHANGE FOREIGN KEYS
|
||||||
|
foreignChanges.changes.forEach(change => {
|
||||||
|
alterColumns.push(`DROP FOREIGN KEY \`${change.oldName}\``);
|
||||||
|
alterColumns.push(`ADD CONSTRAINT \`${change.constraintName}\` FOREIGN KEY (\`${change.field}\`) REFERENCES \`${change.refTable}\` (\`${change.refField}\`) ON UPDATE ${change.onUpdate} ON DELETE ${change.onDelete}`);
|
||||||
|
});
|
||||||
|
|
||||||
// DROP FIELDS
|
// DROP FIELDS
|
||||||
deletions.forEach(deletion => {
|
deletions.forEach(deletion => {
|
||||||
alterColumns.push(`DROP COLUMN \`${deletion.name}\``);
|
alterColumns.push(`DROP COLUMN \`${deletion.name}\``);
|
||||||
@@ -440,6 +462,11 @@ export class MySQLClient extends AntaresCore {
|
|||||||
alterColumns.push(`DROP INDEX \`${deletion.name}\``);
|
alterColumns.push(`DROP INDEX \`${deletion.name}\``);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// DROP FOREIGN KEYS
|
||||||
|
foreignChanges.deletions.forEach(deletion => {
|
||||||
|
alterColumns.push(`DROP FOREIGN KEY \`${deletion.constraintName}\``);
|
||||||
|
});
|
||||||
|
|
||||||
sql += alterColumns.join(', ');
|
sql += alterColumns.join(', ');
|
||||||
|
|
||||||
// RENAME
|
// RENAME
|
||||||
|
@@ -1,10 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<select
|
<select
|
||||||
ref="editField"
|
ref="editField"
|
||||||
class="px-1"
|
class="form-select pl-1 pr-4"
|
||||||
|
:class="{'small-select': size === 'small'}"
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
@blur="$emit('blur')"
|
@blur="$emit('blur')"
|
||||||
>
|
>
|
||||||
|
<option v-if="!isValidDefault" :value="value">
|
||||||
|
{{ value }} - {{ $t('message.invalidDefault') }}
|
||||||
|
</option>
|
||||||
<option
|
<option
|
||||||
v-for="row in foreignList"
|
v-for="row in foreignList"
|
||||||
:key="row.foreignColumn"
|
:key="row.foreignColumn"
|
||||||
@@ -30,7 +34,11 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: [String, Number],
|
value: [String, Number],
|
||||||
keyUsage: Object
|
keyUsage: Object,
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -40,7 +48,10 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
selectedWorkspace: 'workspaces/getSelected'
|
selectedWorkspace: 'workspaces/getSelected'
|
||||||
})
|
}),
|
||||||
|
isValidDefault () {
|
||||||
|
return this.foreignList.some(foreign => foreign.foreignColumn.toString() === this.value.toString());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async created () {
|
async created () {
|
||||||
let firstTextField;
|
let firstTextField;
|
||||||
@@ -64,7 +75,7 @@ export default {
|
|||||||
try { // Foregn list
|
try { // Foregn list
|
||||||
const { status, response } = await Tables.getForeignList({
|
const { status, response } = await Tables.getForeignList({
|
||||||
...params,
|
...params,
|
||||||
column: this.keyUsage.refColumn,
|
column: this.keyUsage.refField,
|
||||||
description: firstTextField
|
description: firstTextField
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<input
|
<input
|
||||||
|
ref="firstInput"
|
||||||
v-model="credentials.user"
|
v-model="credentials.user"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -63,6 +64,11 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
created () {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
closeModal () {
|
closeModal () {
|
||||||
this.$emit('close-asking');
|
this.$emit('close-asking');
|
||||||
|
@@ -20,6 +20,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<div class="col-8 col-sm-12">
|
||||||
<input
|
<input
|
||||||
|
ref="firstInput"
|
||||||
v-model="localConnection.name"
|
v-model="localConnection.name"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -172,6 +173,10 @@ export default {
|
|||||||
created () {
|
created () {
|
||||||
this.localConnection = Object.assign({}, this.connection);
|
this.localConnection = Object.assign({}, this.connection);
|
||||||
window.addEventListener('keydown', this.onKey);
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
window.removeEventListener('keydown', this.onKey);
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
@@ -33,7 +33,11 @@
|
|||||||
<label class="form-label">{{ $t('word.collation') }}:</label>
|
<label class="form-label">{{ $t('word.collation') }}:</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<select v-model="database.collation" class="form-select">
|
<select
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="database.collation"
|
||||||
|
class="form-select"
|
||||||
|
>
|
||||||
<option
|
<option
|
||||||
v-for="collation in collations"
|
v-for="collation in collations"
|
||||||
:key="collation.id"
|
:key="collation.id"
|
||||||
@@ -114,6 +118,10 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
window.removeEventListener('keydown', this.onKey);
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
@@ -20,6 +20,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<div class="col-8 col-sm-12">
|
||||||
<input
|
<input
|
||||||
|
ref="firstInput"
|
||||||
v-model="connection.name"
|
v-model="connection.name"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -182,6 +183,10 @@ export default {
|
|||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
window.addEventListener('keydown', this.onKey);
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
window.removeEventListener('keydown', this.onKey);
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<input
|
<input
|
||||||
|
ref="firstInput"
|
||||||
v-model="database.name"
|
v-model="database.name"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -89,6 +90,9 @@ export default {
|
|||||||
created () {
|
created () {
|
||||||
this.database = { ...this.database, collation: this.defaultCollation };
|
this.database = { ...this.database, collation: this.defaultCollation };
|
||||||
window.addEventListener('keydown', this.onKey);
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
window.removeEventListener('keydown', this.onKey);
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<input
|
<input
|
||||||
|
ref="firstInput"
|
||||||
v-model="localOptions.name"
|
v-model="localOptions.name"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -112,6 +113,10 @@ export default {
|
|||||||
mounted () {
|
mounted () {
|
||||||
this.localOptions.collation = this.defaultCollation;
|
this.localOptions.collation = this.defaultCollation;
|
||||||
this.localOptions.engine = this.defaultEngine;
|
this.localOptions.engine = this.defaultEngine;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
confirmOptionsChange () {
|
confirmOptionsChange () {
|
||||||
|
@@ -25,6 +25,7 @@
|
|||||||
<div class="input-group col-8 col-sm-12">
|
<div class="input-group col-8 col-sm-12">
|
||||||
<ForeignKeySelect
|
<ForeignKeySelect
|
||||||
v-if="foreignKeys.includes(field.name)"
|
v-if="foreignKeys.includes(field.name)"
|
||||||
|
ref="formInput"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
:value.sync="localRow[field.name]"
|
:value.sync="localRow[field.name]"
|
||||||
:key-usage="getKeyUsage(field.name)"
|
:key-usage="getKeyUsage(field.name)"
|
||||||
@@ -32,6 +33,7 @@
|
|||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
v-else-if="inputProps(field).mask"
|
v-else-if="inputProps(field).mask"
|
||||||
|
ref="formInput"
|
||||||
v-model="localRow[field.name]"
|
v-model="localRow[field.name]"
|
||||||
v-mask="inputProps(field).mask"
|
v-mask="inputProps(field).mask"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
@@ -41,6 +43,7 @@
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
v-else-if="inputProps(field).type === 'file'"
|
v-else-if="inputProps(field).type === 'file'"
|
||||||
|
ref="formInput"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
type="file"
|
type="file"
|
||||||
:disabled="fieldsToExclude.includes(field.name)"
|
:disabled="fieldsToExclude.includes(field.name)"
|
||||||
@@ -49,13 +52,14 @@
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
v-else
|
v-else
|
||||||
|
ref="formInput"
|
||||||
v-model="localRow[field.name]"
|
v-model="localRow[field.name]"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
:type="inputProps(field).type"
|
:type="inputProps(field).type"
|
||||||
:disabled="fieldsToExclude.includes(field.name)"
|
:disabled="fieldsToExclude.includes(field.name)"
|
||||||
:tabindex="key+1"
|
:tabindex="key+1"
|
||||||
>
|
>
|
||||||
<span class="input-group-addon" :class="`type-${field.type}`">
|
<span class="input-group-addon" :class="`type-${field.type.toLowerCase()}`">
|
||||||
{{ field.type }} {{ fieldLength(field) | wrapNumber }}
|
{{ field.type }} {{ fieldLength(field) | wrapNumber }}
|
||||||
</span>
|
</span>
|
||||||
<label class="form-checkbox ml-3" :title="$t('word.insert')">
|
<label class="form-checkbox ml-3" :title="$t('word.insert')">
|
||||||
@@ -146,7 +150,7 @@ export default {
|
|||||||
return this.getWorkspace(this.selectedWorkspace);
|
return this.getWorkspace(this.selectedWorkspace);
|
||||||
},
|
},
|
||||||
foreignKeys () {
|
foreignKeys () {
|
||||||
return this.keyUsage.map(key => key.column);
|
return this.keyUsage.map(key => key.field);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -192,6 +196,12 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.localRow = { ...rowObj };
|
this.localRow = { ...rowObj };
|
||||||
|
|
||||||
|
// Auto focus
|
||||||
|
setTimeout(() => {
|
||||||
|
const firstSelectableInput = this.$refs.formInput.find(input => !input.disabled);
|
||||||
|
firstSelectableInput.focus();
|
||||||
|
}, 20);
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
window.removeEventListener('keydown', this.onKey);
|
window.removeEventListener('keydown', this.onKey);
|
||||||
@@ -296,7 +306,7 @@ export default {
|
|||||||
this.localRow[field] = files[0].path;
|
this.localRow[field] = files[0].path;
|
||||||
},
|
},
|
||||||
getKeyUsage (keyName) {
|
getKeyUsage (keyName) {
|
||||||
return this.keyUsage.find(key => key.column === keyName);
|
return this.keyUsage.find(key => key.field === keyName);
|
||||||
},
|
},
|
||||||
onKey (e) {
|
onKey (e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
@@ -45,59 +45,139 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="selectedTab === 'general'" class="panel-body py-4">
|
<div v-if="selectedTab === 'general'" class="panel-body py-4">
|
||||||
<form class="form-horizontal">
|
<div class="container">
|
||||||
<div class="col-8 col-sm-12">
|
<form class="form-horizontal columns">
|
||||||
<div class="form-group mb-4">
|
<div class="column col-12 h6 text-uppercase mb-1">
|
||||||
<div class="col-6 col-sm-12">
|
{{ $t('word.application') }}
|
||||||
<label class="form-label">
|
</div>
|
||||||
<i class="mdi mdi-18px mdi-translate mr-1" />
|
<div class="column col-8 col-sm-12 mb-2">
|
||||||
{{ $t('word.language') }}:
|
<div class="form-group mb-4">
|
||||||
</label>
|
<div class="col-6 col-sm-12">
|
||||||
</div>
|
<label class="form-label">
|
||||||
<div class="col-6 col-sm-12">
|
<i class="mdi mdi-18px mdi-translate mr-1" />
|
||||||
<select
|
{{ $t('word.language') }}:
|
||||||
v-model="localLocale"
|
</label>
|
||||||
class="form-select"
|
</div>
|
||||||
@change="changeLocale(localLocale)"
|
<div class="col-6 col-sm-12">
|
||||||
>
|
<select
|
||||||
<option
|
v-model="localLocale"
|
||||||
v-for="(locale, key) in locales"
|
class="form-select"
|
||||||
:key="key"
|
@change="changeLocale(localLocale)"
|
||||||
:value="locale.code"
|
|
||||||
>
|
>
|
||||||
{{ locale.name }}
|
<option
|
||||||
</option>
|
v-for="(locale, key) in locales"
|
||||||
</select>
|
:key="key"
|
||||||
|
:value="locale.code"
|
||||||
|
>
|
||||||
|
{{ locale.name }}
|
||||||
|
</option>
|
||||||
|
</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>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-6 col-sm-12">
|
<div class="column col-12 h6 mt-4 text-uppercase mb-1">
|
||||||
<label class="form-label">
|
{{ $t('word.editor') }}
|
||||||
{{ $t('message.notificationsTimeout') }}:
|
</div>
|
||||||
</label>
|
<div class="column col-8 col-sm-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-6 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('word.autoCompletion') }}:
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-sm-12">
|
||||||
|
<label class="form-switch d-inline-block" @click.prevent="toggleAutoComplete">
|
||||||
|
<input type="checkbox" :checked="selectedAutoComplete">
|
||||||
|
<i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 col-sm-12">
|
</div>
|
||||||
<div class="input-group">
|
</form>
|
||||||
<input
|
</div>
|
||||||
v-model="localTimeout"
|
</div>
|
||||||
class="form-input"
|
|
||||||
type="number"
|
<div v-if="selectedTab === 'themes'" class="panel-body py-4">
|
||||||
min="1"
|
<div class="container">
|
||||||
@focusout="checkNotificationsTimeout"
|
<div class="columns">
|
||||||
>
|
<div class="column col-12 h6 text-uppercase mb-2">
|
||||||
<span class="input-group-addon">{{ $t('word.seconds') }}</span>
|
{{ $t('message.applicationTheme') }}
|
||||||
|
</div>
|
||||||
|
<div class="column col-6 c-hand theme-block" :class="{'selected': applicationTheme === 'dark'}">
|
||||||
|
<img :src="require('@/images/dark.png').default" class="img-responsive img-fit-cover s-rounded">
|
||||||
|
<div class="theme-name">
|
||||||
|
<i class="mdi mdi-moon-waning-crescent mdi-48px" />
|
||||||
|
<div class="h6 mt-4">
|
||||||
|
{{ $t('word.dark') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-6 theme-block disabled" :class="{'selected': applicationTheme === 'light'}">
|
||||||
|
<div class="theme-name">
|
||||||
|
<i class="mdi mdi-white-balance-sunny mdi-48px" />
|
||||||
|
<div class="h6 mt-4">
|
||||||
|
{{ $t('word.light') }} (Coming)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="selectedTab === 'themes'" class="panel-body py-4">
|
<div class="columns mt-4">
|
||||||
<div class="text-center">
|
<div class="column col-12 h6 text-uppercase mb-2 mt-4">
|
||||||
<p>In future releases</p>
|
{{ $t('message.editorTheme') }}
|
||||||
|
</div>
|
||||||
|
<div class="column col-6 h5 mb-4">
|
||||||
|
<select
|
||||||
|
v-model="localEditorTheme"
|
||||||
|
class="form-select"
|
||||||
|
@change="changeEditorTheme(localEditorTheme)"
|
||||||
|
>
|
||||||
|
<optgroup
|
||||||
|
v-for="group in editorThemes"
|
||||||
|
:key="group.group"
|
||||||
|
:label="group.group"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="theme in group.themes"
|
||||||
|
:key="theme.name"
|
||||||
|
:value="theme.code"
|
||||||
|
:selected="editorTheme === theme.code"
|
||||||
|
>
|
||||||
|
{{ theme.name }}
|
||||||
|
</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="column col-12">
|
||||||
|
<QueryEditor
|
||||||
|
:value="exampleQuery"
|
||||||
|
:workspace="workspace"
|
||||||
|
:read-only="true"
|
||||||
|
:height="270"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -111,7 +191,8 @@
|
|||||||
<h4>{{ appName }}</h4>
|
<h4>{{ appName }}</h4>
|
||||||
<p>
|
<p>
|
||||||
{{ $t('word.version') }}: {{ appVersion }}<br>
|
{{ $t('word.version') }}: {{ appVersion }}<br>
|
||||||
<a class="c-hand" @click="openOutside('https://github.com/EStarium/antares')">GitHub</a><br>
|
<a class="c-hand" @click="openOutside('https://github.com/Fabio286/antares')">GitHub</a><br>
|
||||||
|
<small>{{ $t('word.author') }}: <a class="c-hand" @click="openOutside('https://github.com/Fabio286')">Fabio Di Stasio</a></small><br>
|
||||||
<small>{{ $t('message.madeWithJS') }}</small>
|
<small>{{ $t('message.madeWithJS') }}</small>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -126,18 +207,70 @@
|
|||||||
import { mapActions, mapGetters } from 'vuex';
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
import localesNames from '@/i18n/supported-locales';
|
import localesNames from '@/i18n/supported-locales';
|
||||||
import ModalSettingsUpdate from '@/components/ModalSettingsUpdate';
|
import ModalSettingsUpdate from '@/components/ModalSettingsUpdate';
|
||||||
|
import QueryEditor from '@/components/QueryEditor';
|
||||||
const { shell } = require('electron');
|
const { shell } = require('electron');
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ModalSettings',
|
name: 'ModalSettings',
|
||||||
components: {
|
components: {
|
||||||
ModalSettingsUpdate
|
ModalSettingsUpdate,
|
||||||
|
QueryEditor
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
localLocale: null,
|
localLocale: null,
|
||||||
localTimeout: null,
|
localTimeout: null,
|
||||||
selectedTab: 'general'
|
localEditorTheme: null,
|
||||||
|
selectedTab: 'general',
|
||||||
|
editorThemes: [
|
||||||
|
{
|
||||||
|
group: this.$t('word.light'),
|
||||||
|
themes: [
|
||||||
|
{ code: 'chrome', name: 'Chrome' },
|
||||||
|
{ code: 'clouds', name: 'Clouds' },
|
||||||
|
{ code: 'crimson_editor', name: 'Crimson Editor' },
|
||||||
|
{ code: 'dawn', name: 'Dawn' },
|
||||||
|
{ code: 'dreamweaver', name: 'Dreamweaver' },
|
||||||
|
{ code: 'eclupse', name: 'Eclipse' },
|
||||||
|
{ code: 'github', name: 'GitHub' },
|
||||||
|
{ code: 'iplastic', name: 'IPlastic' },
|
||||||
|
{ code: 'solarized_light', name: 'Solarized Light' },
|
||||||
|
{ code: 'textmate', name: 'TextMate' },
|
||||||
|
{ code: 'tomorrow', name: 'Tomorrow' },
|
||||||
|
{ code: 'xcode', name: 'Xcode' },
|
||||||
|
{ code: 'kuroir', name: 'Kuroir' },
|
||||||
|
{ code: 'katzenmilch', name: 'KatzenMilch' },
|
||||||
|
{ code: 'sqlserver', name: 'SQL Server' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: this.$t('word.dark'),
|
||||||
|
themes: [
|
||||||
|
{ code: 'ambiance', name: 'Ambiance' },
|
||||||
|
{ code: 'chaos', name: 'Chaos' },
|
||||||
|
{ code: 'clouds_midnight', name: 'Clouds Midnight' },
|
||||||
|
{ code: 'dracula', name: 'Dracula' },
|
||||||
|
{ code: 'cobalt', name: 'Cobalt' },
|
||||||
|
{ code: 'gruvbox', name: 'Gruvbox' },
|
||||||
|
{ code: 'gob', name: 'Green on Black' },
|
||||||
|
{ code: 'idle_fingers', name: 'Idle Fingers' },
|
||||||
|
{ code: 'kr_theme', name: 'krTheme' },
|
||||||
|
{ code: 'merbivore', name: 'Merbivore' },
|
||||||
|
{ code: 'mono_industrial', name: 'Mono Industrial' },
|
||||||
|
{ code: 'monokai', name: 'Monokai' },
|
||||||
|
{ code: 'nord_dark', name: 'Nord Dark' },
|
||||||
|
{ code: 'pastel_on_dark', name: 'Pastel on Dark' },
|
||||||
|
{ code: 'solarized_dark', name: 'Solarized Dark' },
|
||||||
|
{ code: 'terminal', name: 'Terminal' },
|
||||||
|
{ code: 'tomorrow_night', name: 'Tomorrow Night' },
|
||||||
|
{ code: 'tomorrow_night_blue', name: 'Tomorrow Night Blue' },
|
||||||
|
{ code: 'tomorrow_night_bright', name: 'Tomorrow Night Bright' },
|
||||||
|
{ code: 'tomorrow_night_eighties', name: 'Tomorrow Night 80s' },
|
||||||
|
{ code: 'twilight', name: 'Twilight' },
|
||||||
|
{ code: 'vibrant_ink', name: 'Vibrant Ink' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -146,8 +279,13 @@ export default {
|
|||||||
appVersion: 'application/appVersion',
|
appVersion: 'application/appVersion',
|
||||||
selectedSettingTab: 'application/selectedSettingTab',
|
selectedSettingTab: 'application/selectedSettingTab',
|
||||||
selectedLocale: 'settings/getLocale',
|
selectedLocale: 'settings/getLocale',
|
||||||
|
selectedAutoComplete: 'settings/getAutoComplete',
|
||||||
notificationsTimeout: 'settings/getNotificationsTimeout',
|
notificationsTimeout: 'settings/getNotificationsTimeout',
|
||||||
updateStatus: 'application/getUpdateStatus'
|
applicationTheme: 'settings/getApplicationTheme',
|
||||||
|
editorTheme: 'settings/getEditorTheme',
|
||||||
|
updateStatus: 'application/getUpdateStatus',
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
}),
|
}),
|
||||||
locales () {
|
locales () {
|
||||||
const locales = [];
|
const locales = [];
|
||||||
@@ -158,11 +296,32 @@ export default {
|
|||||||
},
|
},
|
||||||
hasUpdates () {
|
hasUpdates () {
|
||||||
return ['available', 'downloading', 'downloaded'].includes(this.updateStatus);
|
return ['available', 'downloading', 'downloaded'].includes(this.updateStatus);
|
||||||
|
},
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.selectedWorkspace);
|
||||||
|
},
|
||||||
|
exampleQuery () {
|
||||||
|
return `-- This is an example
|
||||||
|
SELECT
|
||||||
|
employee.id,
|
||||||
|
employee.first_name,
|
||||||
|
employee.last_name,
|
||||||
|
SUM(DATEDIFF("SECOND", call.start, call.end)) AS call_duration
|
||||||
|
FROM call
|
||||||
|
INNER JOIN employee ON call.employee_id = employee.id
|
||||||
|
GROUP BY
|
||||||
|
employee.id,
|
||||||
|
employee.first_name,
|
||||||
|
employee.last_name
|
||||||
|
ORDER BY
|
||||||
|
employee.id ASC;
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.localLocale = this.selectedLocale;
|
this.localLocale = this.selectedLocale;
|
||||||
this.localTimeout = this.notificationsTimeout;
|
this.localTimeout = this.notificationsTimeout;
|
||||||
|
this.localEditorTheme = this.editorTheme;
|
||||||
this.selectedTab = this.selectedSettingTab;
|
this.selectedTab = this.selectedSettingTab;
|
||||||
window.addEventListener('keydown', this.onKey);
|
window.addEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
@@ -173,6 +332,8 @@ export default {
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
closeModal: 'application/hideSettingModal',
|
closeModal: 'application/hideSettingModal',
|
||||||
changeLocale: 'settings/changeLocale',
|
changeLocale: 'settings/changeLocale',
|
||||||
|
changeAutoComplete: 'settings/changeAutoComplete',
|
||||||
|
changeEditorTheme: 'settings/changeEditorTheme',
|
||||||
updateNotificationsTimeout: 'settings/updateNotificationsTimeout'
|
updateNotificationsTimeout: 'settings/updateNotificationsTimeout'
|
||||||
}),
|
}),
|
||||||
selectTab (tab) {
|
selectTab (tab) {
|
||||||
@@ -191,6 +352,9 @@ export default {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.key === 'Escape')
|
if (e.key === 'Escape')
|
||||||
this.closeModal();
|
this.closeModal();
|
||||||
|
},
|
||||||
|
toggleAutoComplete () {
|
||||||
|
this.changeAutoComplete(!this.selectedAutoComplete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -204,6 +368,34 @@ export default {
|
|||||||
.panel-body {
|
.panel-body {
|
||||||
height: calc(70vh - 70px);
|
height: calc(70vh - 70px);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
|
.theme-block {
|
||||||
|
position: relative;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
img {
|
||||||
|
box-shadow: 0 0 0 3px $primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-name {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
text-shadow: 0 0 8px #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge::after {
|
.badge::after {
|
||||||
|
@@ -1,70 +1,208 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="editor-wrapper">
|
<div class="editor-wrapper">
|
||||||
<div ref="editor" class="editor" />
|
<div
|
||||||
|
ref="editor"
|
||||||
|
class="editor"
|
||||||
|
:style="{height: `${height}px`}"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import * as ace from 'ace-builds';
|
||||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
import 'ace-builds/webpack-resolver';
|
||||||
import { completionItemProvider } from '@/suggestions/sql';
|
import '../libs/ext-language_tools';
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
monaco.languages.registerCompletionItemProvider('sql', completionItemProvider(monaco));
|
import Tables from '@/ipc-api/Tables';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'QueryEditor',
|
name: 'QueryEditor',
|
||||||
props: {
|
props: {
|
||||||
value: String
|
value: String,
|
||||||
|
workspace: Object,
|
||||||
|
schema: { type: String, default: '' },
|
||||||
|
autoFocus: { type: Boolean, default: false },
|
||||||
|
readOnly: { type: Boolean, default: false },
|
||||||
|
height: { type: Number, default: 200 }
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
editor: null
|
editor: null,
|
||||||
|
fields: [],
|
||||||
|
baseCompleter: []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
editorTheme: 'settings/getEditorTheme',
|
||||||
|
autoComplete: 'settings/getAutoComplete'
|
||||||
|
}),
|
||||||
|
tables () {
|
||||||
|
return this.workspace
|
||||||
|
? this.workspace.structure.filter(schema => schema.name === this.schema)
|
||||||
|
.reduce((acc, curr) => {
|
||||||
|
acc.push(...curr.tables);
|
||||||
|
return acc;
|
||||||
|
}, []).map(table => {
|
||||||
|
return {
|
||||||
|
name: table.name,
|
||||||
|
comment: table.comment,
|
||||||
|
type: table.type,
|
||||||
|
fields: []
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
mode () {
|
||||||
|
switch (this.workspace.client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
return 'mysql';
|
||||||
|
case 'mssql':
|
||||||
|
return 'sqlserver';
|
||||||
|
case 'pg':
|
||||||
|
return 'pgsql';
|
||||||
|
default:
|
||||||
|
return 'sql';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lastWord () {
|
||||||
|
const words = this.value.split(' ');
|
||||||
|
return words[words.length - 1];
|
||||||
|
},
|
||||||
|
isLastWordATable () {
|
||||||
|
return /\w+\.\w*/gm.test(this.lastWord);
|
||||||
|
},
|
||||||
|
fieldsCompleter () {
|
||||||
|
return {
|
||||||
|
getCompletions: (editor, session, pos, prefix, callback) => {
|
||||||
|
const completions = [];
|
||||||
|
this.fields.forEach(field => {
|
||||||
|
completions.push({
|
||||||
|
value: field,
|
||||||
|
meta: 'column',
|
||||||
|
score: 1000
|
||||||
|
});
|
||||||
|
});
|
||||||
|
callback(null, completions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
editorTheme () {
|
||||||
|
if (this.editor)
|
||||||
|
this.editor.setTheme(`ace/theme/${this.editorTheme}`);
|
||||||
|
},
|
||||||
|
autoComplete () {
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.setOptions({
|
||||||
|
enableLiveAutocompletion: this.autoComplete
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.editor = monaco.editor.create(this.$refs.editor, {
|
this.editor = ace.edit(this.$refs.editor, {
|
||||||
|
mode: `ace/mode/${this.mode}`,
|
||||||
|
theme: `ace/theme/${this.editorTheme}`,
|
||||||
value: this.value,
|
value: this.value,
|
||||||
language: 'sql',
|
fontSize: '14px',
|
||||||
theme: 'vs-dark',
|
printMargin: false,
|
||||||
autoIndent: true,
|
readOnly: this.readOnly
|
||||||
minimap: {
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
contextmenu: false,
|
|
||||||
wordBasedSuggestions: true,
|
|
||||||
acceptSuggestionOnEnter: 'smart',
|
|
||||||
quickSuggestions: true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.editor.onDidChangeModelContent(e => {
|
this.editor.setOptions({
|
||||||
|
enableBasicAutocompletion: true,
|
||||||
|
enableSnippets: true,
|
||||||
|
enableLiveAutocompletion: this.autoComplete
|
||||||
|
});
|
||||||
|
|
||||||
|
this.editor.completers.push({
|
||||||
|
getCompletions: (editor, session, pos, prefix, callback) => {
|
||||||
|
const completions = [];
|
||||||
|
this.tables.forEach(table => {
|
||||||
|
completions.push({
|
||||||
|
value: table.name,
|
||||||
|
meta: table.type,
|
||||||
|
caption: table.comment
|
||||||
|
});
|
||||||
|
});
|
||||||
|
callback(null, completions);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.baseCompleter = this.editor.completers;
|
||||||
|
|
||||||
|
this.editor.commands.on('afterExec', e => {
|
||||||
|
if (['insertstring', 'backspace', 'del'].includes(e.command.name)) {
|
||||||
|
if (this.isLastWordATable || e.args === '.') {
|
||||||
|
if (e.args !== ' ') {
|
||||||
|
const table = this.tables.find(t => t.name === this.lastWord.split('.').pop());
|
||||||
|
|
||||||
|
if (table) {
|
||||||
|
const params = {
|
||||||
|
uid: this.workspace.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
table: table.name
|
||||||
|
};
|
||||||
|
|
||||||
|
Tables.getTableColumns(params).then(res => {
|
||||||
|
if (res.response.length)
|
||||||
|
this.fields = res.response.map(field => field.name);
|
||||||
|
this.editor.completers = [this.fieldsCompleter];
|
||||||
|
this.editor.execCommand('startAutocomplete');
|
||||||
|
}).catch(console.log);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.editor.completers = this.baseCompleter;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.editor.completers = this.baseCompleter;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.editor.completers = this.baseCompleter;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.editor.session.on('change', () => {
|
||||||
const content = this.editor.getValue();
|
const content = this.editor.getValue();
|
||||||
this.$emit('update:value', content);
|
this.$emit('update:value', content);
|
||||||
});
|
});
|
||||||
},
|
|
||||||
beforeDestroy () {
|
if (this.autoFocus) {
|
||||||
this.editor && this.editor.dispose();
|
setTimeout(() => {
|
||||||
|
this.editor.focus();
|
||||||
|
}, 20);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.editor-wrapper {
|
.editor-wrapper {
|
||||||
border-bottom: 1px solid #444;
|
border-bottom: 1px solid #444;
|
||||||
|
|
||||||
.editor {
|
.editor {
|
||||||
height: 200px;
|
width: 100%;
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.CodeMirror {
|
.ace_.mdi {
|
||||||
.CodeMirror-scroll {
|
display: inline-block;
|
||||||
max-width: 100%;
|
width: 17px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror-line {
|
.ace_dark.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {
|
||||||
word-break: break-word !important;
|
background-color: #c9561a99;
|
||||||
white-space: pre-wrap !important;
|
}
|
||||||
}
|
|
||||||
}
|
.ace_dark.ace_editor.ace_autocomplete .ace_marker-layer .ace_line-hover {
|
||||||
|
background-color: #c9571a33;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ace_dark.ace_editor.ace_autocomplete .ace_completion-highlight {
|
||||||
|
color: #e0d00c;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
<i class="mdi mdi-18px mdi-coffee mr-1" />
|
<i class="mdi mdi-18px mdi-coffee mr-1" />
|
||||||
<small>{{ $t('word.donate') }}</small>
|
<small>{{ $t('word.donate') }}</small>
|
||||||
</li>
|
</li>
|
||||||
<li class="footer-element footer-link" @click="openOutside('https://github.com/EStarium/antares/issues')">
|
<li class="footer-element footer-link" @click="openOutside('https://github.com/Fabio286/antares/issues')">
|
||||||
<i class="mdi mdi-18px mdi-bug" />
|
<i class="mdi mdi-18px mdi-bug" />
|
||||||
</li>
|
</li>
|
||||||
<li class="footer-element footer-link" @click="showSettingModal('about')">
|
<li class="footer-element footer-link" @click="showSettingModal('about')">
|
||||||
|
@@ -123,7 +123,7 @@ export default {
|
|||||||
return this.selectedWorkspace === this.connection.uid;
|
return this.selectedWorkspace === this.connection.uid;
|
||||||
},
|
},
|
||||||
selectedTab () {
|
selectedTab () {
|
||||||
if (this.workspace.breadcrumbs.table === null)
|
if (this.workspace.breadcrumbs.table === null && ['data', 'prop'].includes(this.workspace.selected_tab))
|
||||||
return this.queryTabs[0].uid;
|
return this.queryTabs[0].uid;
|
||||||
|
|
||||||
return this.queryTabs.find(tab => tab.uid === this.workspace.selected_tab) ||
|
return this.queryTabs.find(tab => tab.uid === this.workspace.selected_tab) ||
|
||||||
@@ -193,6 +193,7 @@ export default {
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: 2px;
|
width: 2px;
|
||||||
|
416
src/renderer/components/WorkspacePropsForeignModal.vue
Normal file
416
src/renderer/components/WorkspacePropsForeignModal.vue
Normal file
@@ -0,0 +1,416 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.confirm')"
|
||||||
|
size="medium"
|
||||||
|
@confirm="confirmForeignsChange"
|
||||||
|
@hide="$emit('hide')"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-key-link mr-1" /> {{ $t('word.foreignKeys') }} "{{ table }}"
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<div class="columns col-gapless">
|
||||||
|
<div class="column col-5">
|
||||||
|
<div class="panel" :style="{ height: modalInnerHeight + 'px'}">
|
||||||
|
<div class="panel-header pt-0 pl-0">
|
||||||
|
<div class="d-flex">
|
||||||
|
<button class="btn btn-dark btn-sm d-flex" @click="addForeign">
|
||||||
|
<span>{{ $t('word.add') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-link-plus ml-1" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
||||||
|
:title="$t('message.clearChanges')"
|
||||||
|
:disabled="!isChanged"
|
||||||
|
@click.prevent="clearChanges"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.clear') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div ref="indexesPanel" class="panel-body p-0 pr-1">
|
||||||
|
<div
|
||||||
|
v-for="foreign in foreignProxy"
|
||||||
|
:key="foreign._id"
|
||||||
|
class="tile tile-centered c-hand mb-1 p-1"
|
||||||
|
:class="{'selected-foreign': selectedForeignID === foreign._id}"
|
||||||
|
@click="selectForeign($event, foreign._id)"
|
||||||
|
>
|
||||||
|
<div class="tile-icon">
|
||||||
|
<div>
|
||||||
|
<i class="mdi mdi-key-link mdi-24px" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tile-content">
|
||||||
|
<div class="tile-title">
|
||||||
|
{{ foreign.constraintName }}
|
||||||
|
</div>
|
||||||
|
<small class="tile-subtitle text-gray d-flex">
|
||||||
|
<i class="mdi mdi-link-variant mr-1" />
|
||||||
|
<div class="fk-details-wrapper">
|
||||||
|
<span v-if="foreign.table !== ''" class="fk-details">
|
||||||
|
<i class="mdi mdi-table mr-1" />
|
||||||
|
<span>{{ foreign.table }}.{{ foreign.field }}</span>
|
||||||
|
</span>
|
||||||
|
<span v-if="foreign.refTable !== ''" class="fk-details">
|
||||||
|
<i class="mdi mdi-table mr-1" />
|
||||||
|
<span>{{ foreign.refTable }}.{{ foreign.refField }}</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="tile-action">
|
||||||
|
<button
|
||||||
|
class="btn btn-link remove-field p-0 mr-2"
|
||||||
|
:title="$t('word.delete')"
|
||||||
|
@click.prevent="removeIndex(foreign._id)"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-close" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="column col-7 pl-2 editor-col">
|
||||||
|
<form
|
||||||
|
v-if="selectedForeignObj"
|
||||||
|
:style="{ height: modalInnerHeight + 'px'}"
|
||||||
|
class="form-horizontal"
|
||||||
|
>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-3">
|
||||||
|
{{ $t('word.name') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
v-model="selectedForeignObj.constraintName"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-4">
|
||||||
|
<label class="form-label col-3">
|
||||||
|
{{ $tc('word.field', 1) }}
|
||||||
|
</label>
|
||||||
|
<div class="fields-list column pt-1">
|
||||||
|
<label
|
||||||
|
v-for="(field, i) in fields"
|
||||||
|
:key="`${field.name}-${i}`"
|
||||||
|
class="form-checkbox m-0"
|
||||||
|
@click.prevent="toggleField(field.name)"
|
||||||
|
>
|
||||||
|
<input type="checkbox" :checked="selectedForeignObj.field === field.name">
|
||||||
|
<i class="form-icon" /> {{ field.name }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-3 pt-0">
|
||||||
|
{{ $t('message.referenceTable') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select
|
||||||
|
v-model="selectedForeignObj.refTable"
|
||||||
|
class="form-select"
|
||||||
|
@change="reloadRefFields"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="schemaTable in schemaTables"
|
||||||
|
:key="schemaTable.name"
|
||||||
|
:value="schemaTable.name"
|
||||||
|
>
|
||||||
|
{{ schemaTable.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-4">
|
||||||
|
<label class="form-label col-3">
|
||||||
|
{{ $t('message.referenceField') }}
|
||||||
|
</label>
|
||||||
|
<div class="fields-list column pt-1">
|
||||||
|
<label
|
||||||
|
v-for="(field, i) in refFields[selectedForeignID]"
|
||||||
|
:key="`${field.name}-${i}`"
|
||||||
|
class="form-checkbox m-0"
|
||||||
|
@click.prevent="toggleRefField(field.name)"
|
||||||
|
>
|
||||||
|
<input type="checkbox" :checked="selectedForeignObj.refField === field.name && selectedForeignObj.refTable === field.table">
|
||||||
|
<i class="form-icon" /> {{ field.name }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-3">
|
||||||
|
{{ $t('message.onUpdate') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="selectedForeignObj.onUpdate" class="form-select">
|
||||||
|
<option
|
||||||
|
v-for="action in foreignActions"
|
||||||
|
:key="action"
|
||||||
|
:value="action"
|
||||||
|
>
|
||||||
|
{{ action }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-3">
|
||||||
|
{{ $t('message.onDelete') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="selectedForeignObj.onDelete" class="form-select">
|
||||||
|
<option
|
||||||
|
v-for="action in foreignActions"
|
||||||
|
:key="action"
|
||||||
|
:value="action"
|
||||||
|
>
|
||||||
|
{{ action }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div v-if="!foreignProxy.length" class="empty">
|
||||||
|
<div class="empty-icon">
|
||||||
|
<i class="mdi mdi-key-link mdi-48px" />
|
||||||
|
</div>
|
||||||
|
<p class="empty-title h5">
|
||||||
|
{{ $t('message.thereAreNoForeign') }}
|
||||||
|
</p>
|
||||||
|
<div class="empty-action">
|
||||||
|
<button class="btn btn-primary" @click="addForeign">
|
||||||
|
{{ $t('message.createNewForeign') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapActions } from 'vuex';
|
||||||
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
|
import Tables from '@/ipc-api/Tables';
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsForeignModal',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
localKeyUsage: Array,
|
||||||
|
connection: Object,
|
||||||
|
table: String,
|
||||||
|
schema: String,
|
||||||
|
schemaTables: Array,
|
||||||
|
fields: Array,
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
foreignProxy: [],
|
||||||
|
isOptionsChanging: false,
|
||||||
|
selectedForeignID: '',
|
||||||
|
modalInnerHeight: 400,
|
||||||
|
refFields: {},
|
||||||
|
foreignActions: [
|
||||||
|
'RESTRICT',
|
||||||
|
'CASCADE',
|
||||||
|
'SET NULL',
|
||||||
|
'NO ACTION'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selectedForeignObj () {
|
||||||
|
return this.foreignProxy.find(foreign => foreign._id === this.selectedForeignID);
|
||||||
|
},
|
||||||
|
isChanged () {
|
||||||
|
return JSON.stringify(this.localKeyUsage) !== JSON.stringify(this.foreignProxy);
|
||||||
|
},
|
||||||
|
hasPrimary () {
|
||||||
|
return this.foreignProxy.some(foreign => foreign.type === 'PRIMARY');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.foreignProxy = JSON.parse(JSON.stringify(this.localKeyUsage));
|
||||||
|
|
||||||
|
if (this.foreignProxy.length)
|
||||||
|
this.resetSelectedID();
|
||||||
|
|
||||||
|
if (this.selectedForeignObj)
|
||||||
|
this.getRefFields();
|
||||||
|
|
||||||
|
this.getModalInnerHeight();
|
||||||
|
window.addEventListener('resize', this.getModalInnerHeight);
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
window.removeEventListener('resize', this.getModalInnerHeight);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification'
|
||||||
|
}),
|
||||||
|
confirmForeignsChange () {
|
||||||
|
this.$emit('foreigns-update', this.foreignProxy);
|
||||||
|
},
|
||||||
|
selectForeign (event, id) {
|
||||||
|
if (this.selectedForeignID !== id && !event.target.classList.contains('remove-field'))
|
||||||
|
this.selectedForeignID = id;
|
||||||
|
},
|
||||||
|
getModalInnerHeight () {
|
||||||
|
const modalBody = document.querySelector('.modal-body');
|
||||||
|
if (modalBody)
|
||||||
|
this.modalInnerHeight = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom));
|
||||||
|
},
|
||||||
|
addForeign () {
|
||||||
|
this.foreignProxy = [...this.foreignProxy, {
|
||||||
|
_id: uidGen(),
|
||||||
|
constraintName: `FK_${this.foreignProxy.length + 1}`,
|
||||||
|
refSchema: this.schema,
|
||||||
|
table: this.table,
|
||||||
|
refTable: '',
|
||||||
|
field: '',
|
||||||
|
refField: '',
|
||||||
|
onUpdate: this.foreignActions[0],
|
||||||
|
onDelete: this.foreignActions[0]
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (this.foreignProxy.length === 1)
|
||||||
|
this.resetSelectedID();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.indexesPanel.scrollTop = this.$refs.indexesPanel.scrollHeight + 60;
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
removeIndex (id) {
|
||||||
|
this.foreignProxy = this.foreignProxy.filter(foreign => foreign._id !== id);
|
||||||
|
|
||||||
|
if (this.selectedForeignID === id && this.foreignProxy.length)
|
||||||
|
this.resetSelectedID();
|
||||||
|
},
|
||||||
|
clearChanges () {
|
||||||
|
this.foreignProxy = JSON.parse(JSON.stringify(this.localKeyUsage));
|
||||||
|
if (!this.foreignProxy.some(foreign => foreign._id === this.selectedForeignID))
|
||||||
|
this.resetSelectedID();
|
||||||
|
},
|
||||||
|
toggleField (field) {
|
||||||
|
this.foreignProxy = this.foreignProxy.map(foreign => {
|
||||||
|
if (foreign._id === this.selectedForeignID)
|
||||||
|
foreign.field = field;
|
||||||
|
|
||||||
|
return foreign;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
toggleRefField (field) {
|
||||||
|
this.foreignProxy = this.foreignProxy.map(foreign => {
|
||||||
|
if (foreign._id === this.selectedForeignID)
|
||||||
|
foreign.refField = field;
|
||||||
|
|
||||||
|
return foreign;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
resetSelectedID () {
|
||||||
|
this.selectedForeignID = this.foreignProxy.length ? this.foreignProxy[0]._id : '';
|
||||||
|
},
|
||||||
|
async getRefFields () {
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.selectedForeignObj.refSchema,
|
||||||
|
table: this.selectedForeignObj.refTable
|
||||||
|
};
|
||||||
|
|
||||||
|
try { // Field data
|
||||||
|
const { status, response } = await Tables.getTableColumns(params);
|
||||||
|
if (status === 'success') {
|
||||||
|
this.refFields = {
|
||||||
|
...this.refFields,
|
||||||
|
[this.selectedForeignID]: response
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reloadRefFields () {
|
||||||
|
this.selectedForeignObj.refField = '';
|
||||||
|
this.getRefFields();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.tile {
|
||||||
|
border-radius: 2px;
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: background 0.2s;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
|
||||||
|
.tile-action {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $bg-color-light;
|
||||||
|
|
||||||
|
.tile-action {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected-foreign {
|
||||||
|
background: $bg-color-light;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-col {
|
||||||
|
border-left: 2px solid $bg-color-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fields-list {
|
||||||
|
max-height: 80px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-field .mdi {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fk-details-wrapper {
|
||||||
|
max-width: calc(100% - 1rem);
|
||||||
|
|
||||||
|
.fk-details {
|
||||||
|
display: flex;
|
||||||
|
line-height: 1;
|
||||||
|
align-items: baseline;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: block;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
@@ -65,37 +65,45 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="column col-7 pl-2 editor-col">
|
<div class="column col-7 pl-2 editor-col">
|
||||||
<form v-if="selectedIndexObj" :style="{ height: modalInnerHeight + 'px'}">
|
<form
|
||||||
|
v-if="selectedIndexObj"
|
||||||
|
:style="{ height: modalInnerHeight + 'px'}"
|
||||||
|
class="form-horizontal"
|
||||||
|
>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.name') }}
|
{{ $t('word.name') }}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<div class="column">
|
||||||
v-model="selectedIndexObj.name"
|
<input
|
||||||
class="form-input"
|
v-model="selectedIndexObj.name"
|
||||||
type="text"
|
class="form-input"
|
||||||
>
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.type') }}
|
{{ $t('word.type') }}
|
||||||
</label>
|
</label>
|
||||||
<select v-model="selectedIndexObj.type" class="form-select">
|
<div class="column">
|
||||||
<option
|
<select v-model="selectedIndexObj.type" class="form-select">
|
||||||
v-for="index in indexTypes"
|
<option
|
||||||
:key="index"
|
v-for="index in indexTypes"
|
||||||
:value="index"
|
:key="index"
|
||||||
:disabled="index === 'PRIMARY' && hasPrimary"
|
:value="index"
|
||||||
>
|
:disabled="index === 'PRIMARY' && hasPrimary"
|
||||||
{{ index }}
|
>
|
||||||
</option>
|
{{ index }}
|
||||||
</select>
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label col-3">
|
||||||
{{ $tc('word.field', fields.length) }}
|
{{ $tc('word.field', fields.length) }}
|
||||||
</label>
|
</label>
|
||||||
<div class="fields-list">
|
<div class="fields-list column pt-1">
|
||||||
<label
|
<label
|
||||||
v-for="(field, i) in fields"
|
v-for="(field, i) in fields"
|
||||||
:key="`${field.name}-${i}`"
|
:key="`${field.name}-${i}`"
|
||||||
@@ -108,6 +116,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<div v-if="!indexesProxy.length" class="empty">
|
||||||
|
<div class="empty-icon">
|
||||||
|
<i class="mdi mdi-key-outline mdi-48px" />
|
||||||
|
</div>
|
||||||
|
<p class="empty-title h5">
|
||||||
|
{{ $t('message.thereAreNoIndexes') }}
|
||||||
|
</p>
|
||||||
|
<div class="empty-action">
|
||||||
|
<button class="btn btn-primary" @click="addIndex">
|
||||||
|
{{ $t('message.createNewIndex') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -216,7 +237,7 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
resetSelectedID () {
|
resetSelectedID () {
|
||||||
this.selectedIndexID = this.indexesProxy[0]._id;
|
this.selectedIndexID = this.indexesProxy.length ? this.indexesProxy[0]._id : '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -253,7 +274,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fields-list {
|
.fields-list {
|
||||||
max-height: 200px;
|
max-height: 300px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<input
|
<input
|
||||||
|
ref="firstInput"
|
||||||
v-model="optionsProxy.name"
|
v-model="optionsProxy.name"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
:class="{'is-error': !isTableNameValid}"
|
:class="{'is-error': !isTableNameValid}"
|
||||||
@@ -112,6 +113,10 @@ export default {
|
|||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.optionsProxy = JSON.parse(JSON.stringify(this.localOptions));
|
this.optionsProxy = JSON.parse(JSON.stringify(this.localOptions));
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
confirmOptionsChange () {
|
confirmOptionsChange () {
|
||||||
|
@@ -40,7 +40,7 @@
|
|||||||
<span>{{ $t('word.indexes') }}</span>
|
<span>{{ $t('word.indexes') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-key mdi-rotate-45 ml-1" />
|
<i class="mdi mdi-24px mdi-key mdi-rotate-45 ml-1" />
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-dark btn-sm d-none">
|
<button class="btn btn-dark btn-sm" @click="showForeignModal">
|
||||||
<span>{{ $t('word.foreignKeys') }}</span>
|
<span>{{ $t('word.foreignKeys') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-key-link ml-1" />
|
<i class="mdi mdi-24px mdi-key-link ml-1" />
|
||||||
</button>
|
</button>
|
||||||
@@ -57,6 +57,7 @@
|
|||||||
ref="indexTable"
|
ref="indexTable"
|
||||||
:fields="localFields"
|
:fields="localFields"
|
||||||
:indexes="localIndexes"
|
:indexes="localIndexes"
|
||||||
|
:foreigns="localKeyUsage"
|
||||||
:tab-uid="tabUid"
|
:tab-uid="tabUid"
|
||||||
:conn-uid="connection.uid"
|
:conn-uid="connection.uid"
|
||||||
:index-types="workspace.indexTypes"
|
:index-types="workspace.indexTypes"
|
||||||
@@ -86,6 +87,18 @@
|
|||||||
@hide="hideIndexesModal"
|
@hide="hideIndexesModal"
|
||||||
@indexes-update="indexesUpdate"
|
@indexes-update="indexesUpdate"
|
||||||
/>
|
/>
|
||||||
|
<WorkspacePropsForeignModal
|
||||||
|
v-if="isForeignModal"
|
||||||
|
:local-key-usage="localKeyUsage"
|
||||||
|
:connection="connection"
|
||||||
|
:table="table"
|
||||||
|
:schema="schema"
|
||||||
|
:schema-tables="schemaTables"
|
||||||
|
:fields="localFields"
|
||||||
|
:workspace="workspace"
|
||||||
|
@hide="hideForeignModal"
|
||||||
|
@foreigns-update="foreignsUpdate"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -96,13 +109,15 @@ import Tables from '@/ipc-api/Tables';
|
|||||||
import WorkspacePropsTable from '@/components/WorkspacePropsTable';
|
import WorkspacePropsTable from '@/components/WorkspacePropsTable';
|
||||||
import WorkspacePropsOptionsModal from '@/components/WorkspacePropsOptionsModal';
|
import WorkspacePropsOptionsModal from '@/components/WorkspacePropsOptionsModal';
|
||||||
import WorkspacePropsIndexesModal from '@/components/WorkspacePropsIndexesModal';
|
import WorkspacePropsIndexesModal from '@/components/WorkspacePropsIndexesModal';
|
||||||
|
import WorkspacePropsForeignModal from '@/components/WorkspacePropsForeignModal';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WorkspacePropsTab',
|
name: 'WorkspacePropsTab',
|
||||||
components: {
|
components: {
|
||||||
WorkspacePropsTable,
|
WorkspacePropsTable,
|
||||||
WorkspacePropsOptionsModal,
|
WorkspacePropsOptionsModal,
|
||||||
WorkspacePropsIndexesModal
|
WorkspacePropsIndexesModal,
|
||||||
|
WorkspacePropsForeignModal
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object,
|
connection: Object,
|
||||||
@@ -115,6 +130,7 @@ export default {
|
|||||||
isSaving: false,
|
isSaving: false,
|
||||||
isOptionsModal: false,
|
isOptionsModal: false,
|
||||||
isIndexesModal: false,
|
isIndexesModal: false,
|
||||||
|
isForeignModal: false,
|
||||||
isOptionsChanging: false,
|
isOptionsChanging: false,
|
||||||
originalFields: [],
|
originalFields: [],
|
||||||
localFields: [],
|
localFields: [],
|
||||||
@@ -148,6 +164,13 @@ export default {
|
|||||||
schema () {
|
schema () {
|
||||||
return this.workspace.breadcrumbs.schema;
|
return this.workspace.breadcrumbs.schema;
|
||||||
},
|
},
|
||||||
|
schemaTables () {
|
||||||
|
const schemaTables = this.workspace.structure
|
||||||
|
.filter(schema => schema.name === this.schema)
|
||||||
|
.map(schema => schema.tables);
|
||||||
|
|
||||||
|
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
||||||
|
},
|
||||||
isChanged () {
|
isChanged () {
|
||||||
return JSON.stringify(this.originalFields) !== JSON.stringify(this.localFields) ||
|
return JSON.stringify(this.originalFields) !== JSON.stringify(this.localFields) ||
|
||||||
JSON.stringify(this.originalKeyUsage) !== JSON.stringify(this.localKeyUsage) ||
|
JSON.stringify(this.originalKeyUsage) !== JSON.stringify(this.localKeyUsage) ||
|
||||||
@@ -242,8 +265,13 @@ export default {
|
|||||||
const { status, response } = await Tables.getKeyUsage(params);
|
const { status, response } = await Tables.getKeyUsage(params);
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.originalKeyUsage = response;
|
this.originalKeyUsage = response.map(foreign => {
|
||||||
this.localKeyUsage = JSON.parse(JSON.stringify(response));
|
return {
|
||||||
|
_id: uidGen(),
|
||||||
|
...foreign
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.localKeyUsage = JSON.parse(JSON.stringify(this.originalKeyUsage));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
this.addNotification({ status: 'error', message: response });
|
||||||
@@ -321,6 +349,35 @@ export default {
|
|||||||
// Index Deletions
|
// Index Deletions
|
||||||
indexChanges.deletions = this.originalIndexes.filter(index => !localIndexIDs.includes(index._id));
|
indexChanges.deletions = this.originalIndexes.filter(index => !localIndexIDs.includes(index._id));
|
||||||
|
|
||||||
|
// FOREIGN KEYS
|
||||||
|
const foreignChanges = {
|
||||||
|
additions: [],
|
||||||
|
changes: [],
|
||||||
|
deletions: []
|
||||||
|
};
|
||||||
|
const originalForeignIDs = this.originalKeyUsage.reduce((acc, curr) => [...acc, curr._id], []);
|
||||||
|
const localForeignIDs = this.localKeyUsage.reduce((acc, curr) => [...acc, curr._id], []);
|
||||||
|
|
||||||
|
// Foreigns Additions
|
||||||
|
foreignChanges.additions = this.localKeyUsage.filter(foreign => !originalForeignIDs.includes(foreign._id));
|
||||||
|
|
||||||
|
// Foreigns Changes
|
||||||
|
this.originalKeyUsage.forEach(originalForeign => {
|
||||||
|
const lI = this.localKeyUsage.findIndex(localForeign => localForeign._id === originalForeign._id);
|
||||||
|
if (JSON.stringify(originalForeign) !== JSON.stringify(this.localKeyUsage[lI])) {
|
||||||
|
if (this.localKeyUsage[lI]) {
|
||||||
|
foreignChanges.changes.push({
|
||||||
|
...this.localKeyUsage[lI],
|
||||||
|
oldName: originalForeign.constraintName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Foreigns Deletions
|
||||||
|
foreignChanges.deletions = this.originalKeyUsage.filter(foreign => !localForeignIDs.includes(foreign._id));
|
||||||
|
|
||||||
|
// ALTER
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.schema,
|
schema: this.schema,
|
||||||
@@ -329,10 +386,11 @@ export default {
|
|||||||
changes,
|
changes,
|
||||||
deletions,
|
deletions,
|
||||||
indexChanges,
|
indexChanges,
|
||||||
|
foreignChanges,
|
||||||
options
|
options
|
||||||
};
|
};
|
||||||
|
|
||||||
try { // Key usage (foreign keys)
|
try {
|
||||||
const { status, response } = await Tables.alterTable(params);
|
const { status, response } = await Tables.alterTable(params);
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
@@ -423,6 +481,15 @@ export default {
|
|||||||
},
|
},
|
||||||
indexesUpdate (indexes) {
|
indexesUpdate (indexes) {
|
||||||
this.localIndexes = indexes;
|
this.localIndexes = indexes;
|
||||||
|
},
|
||||||
|
showForeignModal () {
|
||||||
|
this.isForeignModal = true;
|
||||||
|
},
|
||||||
|
hideForeignModal () {
|
||||||
|
this.isForeignModal = false;
|
||||||
|
},
|
||||||
|
foreignsUpdate (foreigns) {
|
||||||
|
this.localKeyUsage = foreigns;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -29,14 +29,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="th">
|
<div class="th">
|
||||||
<div class="column-resizable">
|
<div class="column-resizable min-100">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('word.name') }}
|
{{ $t('word.name') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="th">
|
<div class="th">
|
||||||
<div class="column-resizable">
|
<div class="column-resizable min-100">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('word.type') }}
|
{{ $t('word.type') }}
|
||||||
</div>
|
</div>
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="th">
|
<div class="th">
|
||||||
<div class="column-resizable">
|
<div class="column-resizable min-100">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('word.collation') }}
|
{{ $t('word.collation') }}
|
||||||
</div>
|
</div>
|
||||||
@@ -104,6 +104,7 @@
|
|||||||
:key="row._id"
|
:key="row._id"
|
||||||
:row="row"
|
:row="row"
|
||||||
:indexes="getIndexes(row.name)"
|
:indexes="getIndexes(row.name)"
|
||||||
|
:foreigns="getForeigns(row.name)"
|
||||||
:data-types="dataTypes"
|
:data-types="dataTypes"
|
||||||
@contextmenu="contextMenu"
|
@contextmenu="contextMenu"
|
||||||
/>
|
/>
|
||||||
@@ -128,6 +129,7 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
fields: Array,
|
fields: Array,
|
||||||
indexes: Array,
|
indexes: Array,
|
||||||
|
foreigns: Array,
|
||||||
indexTypes: Array,
|
indexTypes: Array,
|
||||||
tabUid: [String, Number],
|
tabUid: [String, Number],
|
||||||
connUid: String,
|
connUid: String,
|
||||||
@@ -214,6 +216,13 @@ export default {
|
|||||||
acc.push(...curr.fields.map(f => ({ name: f, type: curr.type })));
|
acc.push(...curr.fields.map(f => ({ name: f, type: curr.type })));
|
||||||
return acc;
|
return acc;
|
||||||
}, []).filter(f => f.name === field);
|
}, []).filter(f => f.name === field);
|
||||||
|
},
|
||||||
|
getForeigns (field) {
|
||||||
|
return this.foreigns.reduce((acc, curr) => {
|
||||||
|
if (curr.field === field)
|
||||||
|
acc.push(`${curr.refTable}.${curr.refField}`);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -231,4 +240,8 @@ export default {
|
|||||||
.vscroll {
|
.vscroll {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.min-100 {
|
||||||
|
min-width: 100px !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="tr" @contextmenu.prevent="$emit('contextmenu', $event, localRow._id)">
|
<div class="tr" @contextmenu.prevent="$emit('contextmenu', $event, localRow._id)">
|
||||||
<div class="td">
|
<div class="td" tabindex="0">
|
||||||
<div class="row-draggable">
|
<div class="row-draggable">
|
||||||
<i class="mdi mdi-drag-horizontal row-draggable-icon" />
|
<i class="mdi mdi-drag-horizontal row-draggable-icon" />
|
||||||
{{ localRow.order }}
|
{{ localRow.order }}
|
||||||
@@ -15,9 +15,15 @@
|
|||||||
class="d-inline-block mdi mdi-key column-key c-help"
|
class="d-inline-block mdi mdi-key column-key c-help"
|
||||||
:class="`key-${index.type}`"
|
:class="`key-${index.type}`"
|
||||||
/>
|
/>
|
||||||
|
<i
|
||||||
|
v-for="foreign in foreigns"
|
||||||
|
:key="foreign"
|
||||||
|
:title="foreign"
|
||||||
|
class="d-inline-block mdi mdi-key-link c-help"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="td">
|
<div class="td" tabindex="0">
|
||||||
<span
|
<span
|
||||||
v-if="!isInlineEditor.name"
|
v-if="!isInlineEditor.name"
|
||||||
class="cell-content"
|
class="cell-content"
|
||||||
@@ -35,11 +41,15 @@
|
|||||||
@blur="editOFF"
|
@blur="editOFF"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="td text-uppercase text-left" :class="`type-${lowerCase(localRow.type)}`">
|
<div
|
||||||
|
class="td text-uppercase"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
v-if="!isInlineEditor.type"
|
v-if="!isInlineEditor.type"
|
||||||
class="cell-content"
|
class="cell-content text-left"
|
||||||
@dblclick="editON($event, localRow.type.toUpperCase(), 'type')"
|
:class="`type-${lowerCase(localRow.type)}`"
|
||||||
|
@click="editON($event, localRow.type.toUpperCase(), 'type')"
|
||||||
>
|
>
|
||||||
{{ localRow.type }}
|
{{ localRow.type }}
|
||||||
</span>
|
</span>
|
||||||
@@ -47,7 +57,7 @@
|
|||||||
v-else
|
v-else
|
||||||
ref="editField"
|
ref="editField"
|
||||||
v-model="editingContent"
|
v-model="editingContent"
|
||||||
class="editable-field px-1 text-uppercase"
|
class="form-select editable-field small-select text-uppercase"
|
||||||
@blur="editOFF"
|
@blur="editOFF"
|
||||||
>
|
>
|
||||||
<optgroup
|
<optgroup
|
||||||
@@ -66,7 +76,7 @@
|
|||||||
</optgroup>
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="td type-int">
|
<div class="td type-int" tabindex="0">
|
||||||
<template v-if="fieldType.length">
|
<template v-if="fieldType.length">
|
||||||
<span
|
<span
|
||||||
v-if="!isInlineEditor.length"
|
v-if="!isInlineEditor.length"
|
||||||
@@ -86,7 +96,7 @@
|
|||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="td">
|
<div class="td" tabindex="0">
|
||||||
<label class="form-checkbox">
|
<label class="form-checkbox">
|
||||||
<input
|
<input
|
||||||
v-model="localRow.unsigned"
|
v-model="localRow.unsigned"
|
||||||
@@ -96,17 +106,17 @@
|
|||||||
<i class="form-icon" />
|
<i class="form-icon" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="td">
|
<div class="td" tabindex="0">
|
||||||
<label class="form-checkbox">
|
<label class="form-checkbox">
|
||||||
<input
|
<input
|
||||||
v-model="localRow.nullable"
|
v-model="localRow.nullable"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:disabled="localRow.key === 'pri'"
|
:disabled="!isNullable"
|
||||||
>
|
>
|
||||||
<i class="form-icon" />
|
<i class="form-icon" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="td">
|
<div class="td" tabindex="0">
|
||||||
<label class="form-checkbox">
|
<label class="form-checkbox">
|
||||||
<input
|
<input
|
||||||
v-model="localRow.zerofill"
|
v-model="localRow.zerofill"
|
||||||
@@ -116,12 +126,12 @@
|
|||||||
<i class="form-icon" />
|
<i class="form-icon" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="td">
|
<div class="td" tabindex="0">
|
||||||
<span class="cell-content" @dblclick="editON($event, localRow.default, 'default')">
|
<span class="cell-content" @dblclick="editON($event, localRow.default, 'default')">
|
||||||
{{ fieldDefault }}
|
{{ fieldDefault }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="td type-varchar">
|
<div class="td type-varchar" tabindex="0">
|
||||||
<span
|
<span
|
||||||
v-if="!isInlineEditor.comment"
|
v-if="!isInlineEditor.comment"
|
||||||
class="cell-content"
|
class="cell-content"
|
||||||
@@ -139,12 +149,12 @@
|
|||||||
@blur="editOFF"
|
@blur="editOFF"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="td">
|
<div class="td" tabindex="0">
|
||||||
<template v-if="fieldType.collation">
|
<template v-if="fieldType.collation">
|
||||||
<span
|
<span
|
||||||
v-if="!isInlineEditor.collation"
|
v-if="!isInlineEditor.collation"
|
||||||
class="cell-content"
|
class="cell-content"
|
||||||
@dblclick="editON($event, localRow.collation, 'collation')"
|
@click="editON($event, localRow.collation, 'collation')"
|
||||||
>
|
>
|
||||||
{{ localRow.collation }}
|
{{ localRow.collation }}
|
||||||
</span>
|
</span>
|
||||||
@@ -152,7 +162,7 @@
|
|||||||
v-else
|
v-else
|
||||||
ref="editField"
|
ref="editField"
|
||||||
v-model="editingContent"
|
v-model="editingContent"
|
||||||
class="editable-field px-1"
|
class="form-select small-select editable-field"
|
||||||
@blur="editOFF"
|
@blur="editOFF"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
@@ -224,7 +234,7 @@
|
|||||||
<label class="form-radio form-inline">
|
<label class="form-radio form-inline">
|
||||||
<input
|
<input
|
||||||
v-model="defaultValue.type"
|
v-model="defaultValue.type"
|
||||||
:disabled="localRow.key !== 'pri'"
|
:disabled="!canAutoincrement"
|
||||||
type="radio"
|
type="radio"
|
||||||
name="default"
|
name="default"
|
||||||
value="autoincrement"
|
value="autoincrement"
|
||||||
@@ -283,7 +293,8 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
row: Object,
|
row: Object,
|
||||||
dataTypes: Array,
|
dataTypes: Array,
|
||||||
indexes: Array
|
indexes: Array,
|
||||||
|
foreigns: Array
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -297,6 +308,7 @@ export default {
|
|||||||
onUpdate: ''
|
onUpdate: ''
|
||||||
},
|
},
|
||||||
editingContent: null,
|
editingContent: null,
|
||||||
|
originalContent: null,
|
||||||
editingField: null
|
editingField: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -325,6 +337,12 @@ export default {
|
|||||||
},
|
},
|
||||||
collations () {
|
collations () {
|
||||||
return this.getWorkspace(this.selectedWorkspace).collations;
|
return this.getWorkspace(this.selectedWorkspace).collations;
|
||||||
|
},
|
||||||
|
canAutoincrement () {
|
||||||
|
return this.indexes.some(index => ['PRIMARY', 'UNIQUE'].includes(index.type));
|
||||||
|
},
|
||||||
|
isNullable () {
|
||||||
|
return !this.indexes.some(index => ['PRIMARY'].includes(index.type));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -333,6 +351,13 @@ export default {
|
|||||||
},
|
},
|
||||||
row () {
|
row () {
|
||||||
this.localRow = this.row;
|
this.localRow = this.row;
|
||||||
|
},
|
||||||
|
indexes () {
|
||||||
|
if (!this.canAutoincrement)
|
||||||
|
this.localRow.autoIncrement = false;
|
||||||
|
|
||||||
|
if (!this.isNullable)
|
||||||
|
this.localRow.nullable = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
@@ -397,6 +422,7 @@ export default {
|
|||||||
this.editingField = field;
|
this.editingField = field;
|
||||||
|
|
||||||
this.editingContent = content;
|
this.editingContent = content;
|
||||||
|
this.originalContent = content;
|
||||||
|
|
||||||
const obj = { [field]: true };
|
const obj = { [field]: true };
|
||||||
this.isInlineEditor = { ...this.isInlineEditor, ...obj };
|
this.isInlineEditor = { ...this.isInlineEditor, ...obj };
|
||||||
@@ -414,10 +440,10 @@ export default {
|
|||||||
editOFF () {
|
editOFF () {
|
||||||
this.localRow[this.editingField] = this.editingContent;
|
this.localRow[this.editingField] = this.editingContent;
|
||||||
|
|
||||||
if (this.editingField === 'type') {
|
if (this.editingField === 'type' && this.editingContent !== this.originalContent) {
|
||||||
this.localRow.numLength = false;
|
this.localRow.numLength = null;
|
||||||
this.localRow.charLength = false;
|
this.localRow.charLength = null;
|
||||||
this.localRow.datePrecision = false;
|
this.localRow.datePrecision = null;
|
||||||
|
|
||||||
if (this.fieldType.length) {
|
if (this.fieldType.length) {
|
||||||
if (['integer', 'float', 'binary', 'spatial'].includes(this.fieldType.group)) this.localRow.numLength = 11;
|
if (['integer', 'float', 'binary', 'spatial'].includes(this.fieldType.group)) this.localRow.numLength = 11;
|
||||||
@@ -427,6 +453,12 @@ export default {
|
|||||||
|
|
||||||
if (!this.fieldType.collation)
|
if (!this.fieldType.collation)
|
||||||
this.localRow.collation = null;
|
this.localRow.collation = null;
|
||||||
|
|
||||||
|
if (!this.fieldType.unsigned)
|
||||||
|
this.localRow.unsigned = false;
|
||||||
|
|
||||||
|
if (!this.fieldType.zerofill)
|
||||||
|
this.localRow.zerofill = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.editingField === 'default') {
|
if (this.editingField === 'default') {
|
||||||
@@ -460,6 +492,7 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.editingContent = null;
|
this.editingContent = null;
|
||||||
|
this.originalContent = null;
|
||||||
this.editingField = null;
|
this.editingField = null;
|
||||||
},
|
},
|
||||||
hideDefaultModal () {
|
hideDefaultModal () {
|
||||||
|
@@ -1,7 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
|
<div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
|
||||||
<div class="workspace-query-runner column col-12">
|
<div class="workspace-query-runner column col-12">
|
||||||
<QueryEditor v-if="isSelected" :value.sync="query" />
|
<QueryEditor
|
||||||
|
v-if="isSelected"
|
||||||
|
:auto-focus="true"
|
||||||
|
:value.sync="query"
|
||||||
|
:workspace="workspace"
|
||||||
|
:schema="schema"
|
||||||
|
/>
|
||||||
<div class="workspace-query-runner-footer">
|
<div class="workspace-query-runner-footer">
|
||||||
<div class="workspace-query-buttons">
|
<div class="workspace-query-buttons">
|
||||||
<button
|
<button
|
||||||
@@ -99,6 +105,7 @@ export default {
|
|||||||
if (!query || this.isQuering) return;
|
if (!query || this.isQuering) return;
|
||||||
this.isQuering = true;
|
this.isQuering = true;
|
||||||
this.clearTabData();
|
this.clearTabData();
|
||||||
|
this.$refs.queryTable.resetSort();
|
||||||
|
|
||||||
try { // Query Data
|
try { // Query Data
|
||||||
const params = {
|
const params = {
|
||||||
|
@@ -123,8 +123,11 @@ export default {
|
|||||||
primaryField () {
|
primaryField () {
|
||||||
return this.fields.filter(field => ['pri', 'uni'].includes(field.key))[0] || false;
|
return this.fields.filter(field => ['pri', 'uni'].includes(field.key))[0] || false;
|
||||||
},
|
},
|
||||||
|
isHardSort () {
|
||||||
|
return this.mode === 'table' && this.localResults.length === 1000;
|
||||||
|
},
|
||||||
sortedResults () {
|
sortedResults () {
|
||||||
if (this.currentSort) {
|
if (this.currentSort && !this.isHardSort) {
|
||||||
return [...this.localResults].sort((a, b) => {
|
return [...this.localResults].sort((a, b) => {
|
||||||
let modifier = 1;
|
let modifier = 1;
|
||||||
const valA = typeof a[this.currentSort] === 'string' ? a[this.currentSort].toLowerCase() : a[this.currentSort];
|
const valA = typeof a[this.currentSort] === 'string' ? a[this.currentSort].toLowerCase() : a[this.currentSort];
|
||||||
@@ -228,7 +231,6 @@ export default {
|
|||||||
return row[primaryFieldName];
|
return row[primaryFieldName];
|
||||||
},
|
},
|
||||||
setLocalResults () {
|
setLocalResults () {
|
||||||
this.resetSort();
|
|
||||||
this.localResults = this.resultsWithRows[this.resultsetIndex] && this.resultsWithRows[this.resultsetIndex].rows
|
this.localResults = this.resultsWithRows[this.resultsetIndex] && this.resultsWithRows[this.resultsetIndex].rows
|
||||||
? this.resultsWithRows[this.resultsetIndex].rows.map(item => {
|
? this.resultsWithRows[this.resultsetIndex].rows.map(item => {
|
||||||
return { ...item, _id: uidGen() };
|
return { ...item, _id: uidGen() };
|
||||||
@@ -342,6 +344,9 @@ export default {
|
|||||||
this.currentSortDir = 'asc';
|
this.currentSortDir = 'asc';
|
||||||
this.currentSort = field;
|
this.currentSort = field;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isHardSort)
|
||||||
|
this.$emit('hard-sort', { field: this.currentSort, dir: this.currentSortDir });
|
||||||
},
|
},
|
||||||
resetSort () {
|
resetSort () {
|
||||||
this.currentSort = '';
|
this.currentSort = '';
|
||||||
|
@@ -20,6 +20,7 @@
|
|||||||
class="editable-field"
|
class="editable-field"
|
||||||
:value.sync="editingContent"
|
:value.sync="editingContent"
|
||||||
:key-usage="getKeyUsage(cKey)"
|
:key-usage="getKeyUsage(cKey)"
|
||||||
|
size="small"
|
||||||
@blur="editOFF"
|
@blur="editOFF"
|
||||||
/>
|
/>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
@@ -157,6 +158,8 @@ export default {
|
|||||||
typeFormat (val, type, precision) {
|
typeFormat (val, type, precision) {
|
||||||
if (!val) return val;
|
if (!val) return val;
|
||||||
|
|
||||||
|
type = type.toUpperCase();
|
||||||
|
|
||||||
if (DATE.includes(type))
|
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') : val;
|
||||||
|
|
||||||
@@ -254,7 +257,7 @@ export default {
|
|||||||
return ['gif', 'jpg', 'png', 'bmp', 'ico', 'tif'].includes(this.contentInfo.ext);
|
return ['gif', 'jpg', 'png', 'bmp', 'ico', 'tif'].includes(this.contentInfo.ext);
|
||||||
},
|
},
|
||||||
foreignKeys () {
|
foreignKeys () {
|
||||||
return this.keyUsage.map(key => key.column);
|
return this.keyUsage.map(key => key.field);
|
||||||
},
|
},
|
||||||
isEditable () {
|
isEditable () {
|
||||||
return this.fields ? !!(this.fields[0].schema && this.fields[0].table) : false;
|
return this.fields ? !!(this.fields[0].schema && this.fields[0].table) : false;
|
||||||
@@ -274,7 +277,7 @@ export default {
|
|||||||
if (field)
|
if (field)
|
||||||
type = field.type;
|
type = field.type;
|
||||||
|
|
||||||
return type;
|
return type.toLowerCase();
|
||||||
},
|
},
|
||||||
getFieldPrecision (cKey) {
|
getFieldPrecision (cKey) {
|
||||||
let length = 0;
|
let length = 0;
|
||||||
@@ -313,7 +316,7 @@ export default {
|
|||||||
editON (event, content, field) {
|
editON (event, content, field) {
|
||||||
if (!this.isEditable) return;
|
if (!this.isEditable) return;
|
||||||
|
|
||||||
const type = this.getFieldType(field);
|
const type = this.getFieldType(field).toUpperCase(); ;
|
||||||
this.originalContent = content;
|
this.originalContent = content;
|
||||||
this.editingType = type;
|
this.editingType = type;
|
||||||
this.editingField = field;
|
this.editingField = field;
|
||||||
@@ -420,7 +423,7 @@ export default {
|
|||||||
this.$emit('select-row', event, row);
|
this.$emit('select-row', event, row);
|
||||||
},
|
},
|
||||||
getKeyUsage (keyName) {
|
getKeyUsage (keyName) {
|
||||||
return this.keyUsage.find(key => key.column === keyName);
|
return this.keyUsage.find(key => key.field === keyName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -63,6 +63,7 @@
|
|||||||
mode="table"
|
mode="table"
|
||||||
@update-field="updateField"
|
@update-field="updateField"
|
||||||
@delete-selected="deleteSelected"
|
@delete-selected="deleteSelected"
|
||||||
|
@hard-sort="hardSort"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ModalNewTableRow
|
<ModalNewTableRow
|
||||||
@@ -102,7 +103,8 @@ export default {
|
|||||||
lastTable: null,
|
lastTable: null,
|
||||||
isAddModal: false,
|
isAddModal: false,
|
||||||
autorefreshTimer: 0,
|
autorefreshTimer: 0,
|
||||||
refreshInterval: null
|
refreshInterval: null,
|
||||||
|
sortParams: {}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -133,8 +135,10 @@ export default {
|
|||||||
watch: {
|
watch: {
|
||||||
table () {
|
table () {
|
||||||
if (this.isSelected) {
|
if (this.isSelected) {
|
||||||
|
this.sortParams = {};
|
||||||
this.getTableData();
|
this.getTableData();
|
||||||
this.lastTable = this.table;
|
this.lastTable = this.table;
|
||||||
|
this.$refs.queryTable.resetSort();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isSelected (val) {
|
isSelected (val) {
|
||||||
@@ -156,7 +160,7 @@ export default {
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification'
|
addNotification: 'notifications/addNotification'
|
||||||
}),
|
}),
|
||||||
async getTableData () {
|
async getTableData (sortParams) {
|
||||||
if (!this.table) return;
|
if (!this.table) return;
|
||||||
this.isQuering = true;
|
this.isQuering = true;
|
||||||
|
|
||||||
@@ -167,7 +171,8 @@ export default {
|
|||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.schema,
|
schema: this.schema,
|
||||||
table: this.workspace.breadcrumbs.table
|
table: this.workspace.breadcrumbs.table,
|
||||||
|
sortParams
|
||||||
};
|
};
|
||||||
|
|
||||||
try { // Table data
|
try { // Table data
|
||||||
@@ -188,7 +193,11 @@ export default {
|
|||||||
return this.table;
|
return this.table;
|
||||||
},
|
},
|
||||||
reloadTable () {
|
reloadTable () {
|
||||||
this.getTableData();
|
this.getTableData(this.sortParams);
|
||||||
|
},
|
||||||
|
hardSort (sortParams) {
|
||||||
|
this.sortParams = sortParams;
|
||||||
|
this.getTableData(sortParams);
|
||||||
},
|
},
|
||||||
showAddModal () {
|
showAddModal () {
|
||||||
this.isAddModal = true;
|
this.isAddModal = true;
|
||||||
|
@@ -61,7 +61,13 @@ module.exports = {
|
|||||||
total: 'Total',
|
total: 'Total',
|
||||||
table: 'Table',
|
table: 'Table',
|
||||||
discard: 'Discard',
|
discard: 'Discard',
|
||||||
stay: 'Stay'
|
stay: 'Stay',
|
||||||
|
author: 'Author',
|
||||||
|
light: 'Light',
|
||||||
|
dark: 'Dark',
|
||||||
|
autoCompletion: 'Auto Completion',
|
||||||
|
application: 'Application',
|
||||||
|
editor: 'Editor'
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
appWelcome: 'Welcome to Antares SQL Client!',
|
appWelcome: 'Welcome to Antares SQL Client!',
|
||||||
@@ -115,7 +121,17 @@ module.exports = {
|
|||||||
deleteTable: 'Delete table',
|
deleteTable: 'Delete table',
|
||||||
emptyCorfirm: 'Do you confirm to empty',
|
emptyCorfirm: 'Do you confirm to empty',
|
||||||
unsavedChanges: 'Unsaved changes',
|
unsavedChanges: 'Unsaved changes',
|
||||||
discardUnsavedChanges: 'You have some unsaved changes. By leaving this tab these changes will be discarded.'
|
discardUnsavedChanges: 'You have some unsaved changes. By leaving this tab these changes will be discarded.',
|
||||||
|
thereAreNoIndexes: 'There are no indexes',
|
||||||
|
thereAreNoForeign: 'There are no foreign keys',
|
||||||
|
createNewForeign: 'Create new foreign key',
|
||||||
|
referenceTable: 'Ref. table',
|
||||||
|
referenceField: 'Ref. field',
|
||||||
|
foreignFields: 'Foreign fields',
|
||||||
|
invalidDefault: 'Invalid default',
|
||||||
|
onDelete: 'On delete',
|
||||||
|
applicationTheme: 'Application Theme',
|
||||||
|
editorTheme: 'Editor Theme'
|
||||||
},
|
},
|
||||||
// Date and Time
|
// Date and Time
|
||||||
short: {
|
short: {
|
||||||
|
BIN
src/renderer/images/dark.png
Normal file
BIN
src/renderer/images/dark.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
8
src/renderer/ipc-api/Application.js
Normal file
8
src/renderer/ipc-api/Application.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
'use strict';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
|
export default class {
|
||||||
|
static getKey (params) {
|
||||||
|
return ipcRenderer.sendSync('get-key', params);
|
||||||
|
}
|
||||||
|
}
|
2263
src/renderer/libs/ext-language_tools.js
Normal file
2263
src/renderer/libs/ext-language_tools.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -180,6 +180,12 @@ body {
|
|||||||
|
|
||||||
.form-select {
|
.form-select {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.small-select {
|
||||||
|
height: 1rem;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
padding: 1px 0.4rem 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-select,
|
.form-select,
|
||||||
@@ -228,3 +234,7 @@ body {
|
|||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty {
|
||||||
|
color: $body-font-color;
|
||||||
|
}
|
||||||
|
@@ -1,10 +1,18 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
import Store from 'electron-store';
|
||||||
|
import Application from '../../ipc-api/Application';
|
||||||
|
const key = Application.getKey();
|
||||||
|
|
||||||
|
const persistentStore = new Store({
|
||||||
|
name: 'connections',
|
||||||
|
encryptionKey: key
|
||||||
|
});
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
strict: true,
|
strict: true,
|
||||||
state: {
|
state: {
|
||||||
connections: []
|
connections: persistentStore.get('connections') || []
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
getConnections: state => state.connections,
|
getConnections: state => state.connections,
|
||||||
@@ -20,9 +28,11 @@ export default {
|
|||||||
mutations: {
|
mutations: {
|
||||||
ADD_CONNECTION (state, connection) {
|
ADD_CONNECTION (state, connection) {
|
||||||
state.connections.push(connection);
|
state.connections.push(connection);
|
||||||
|
persistentStore.set('connections', state.connections);
|
||||||
},
|
},
|
||||||
DELETE_CONNECTION (state, connection) {
|
DELETE_CONNECTION (state, connection) {
|
||||||
state.connections = state.connections.filter(el => el.uid !== connection.uid);
|
state.connections = state.connections.filter(el => el.uid !== connection.uid);
|
||||||
|
persistentStore.set('connections', state.connections);
|
||||||
},
|
},
|
||||||
EDIT_CONNECTION (state, connection) {
|
EDIT_CONNECTION (state, connection) {
|
||||||
const editedConnections = state.connections.map(conn => {
|
const editedConnections = state.connections.map(conn => {
|
||||||
@@ -31,9 +41,11 @@ export default {
|
|||||||
});
|
});
|
||||||
state.connections = editedConnections;
|
state.connections = editedConnections;
|
||||||
state.selected_conection = {};
|
state.selected_conection = {};
|
||||||
|
persistentStore.set('connections', state.connections);
|
||||||
},
|
},
|
||||||
UPDATE_CONNECTIONS (state, connections) {
|
UPDATE_CONNECTIONS (state, connections) {
|
||||||
state.connections = connections;
|
state.connections = connections;
|
||||||
|
persistentStore.set('connections', state.connections);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
@@ -1,29 +1,47 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import i18n from '@/i18n';
|
import i18n from '@/i18n';
|
||||||
|
import Store from 'electron-store';
|
||||||
|
const persistentStore = new Store({ name: 'settings' });
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
strict: true,
|
strict: true,
|
||||||
state: {
|
state: {
|
||||||
locale: 'en-US',
|
locale: persistentStore.get('locale') || 'en-US',
|
||||||
explorebar_size: null,
|
explorebar_size: persistentStore.get('explorebar_size') || null,
|
||||||
notifications_timeout: 5
|
notifications_timeout: persistentStore.get('notifications_timeout') || 5,
|
||||||
|
auto_complete: persistentStore.get('auto_complete') || true,
|
||||||
|
application_theme: persistentStore.get('application_theme') || 'dark',
|
||||||
|
editor_theme: persistentStore.get('editor_theme') || 'twilight'
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
getLocale: state => state.locale,
|
getLocale: state => state.locale,
|
||||||
getExplorebarSize: state => state.explorebar_size,
|
getExplorebarSize: state => state.explorebar_size,
|
||||||
getNotificationsTimeout: state => state.notifications_timeout
|
getNotificationsTimeout: state => state.notifications_timeout,
|
||||||
|
getAutoComplete: state => state.auto_complete,
|
||||||
|
getApplicationTheme: state => state.application_theme,
|
||||||
|
getEditorTheme: state => state.editor_theme
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
SET_LOCALE (state, locale) {
|
SET_LOCALE (state, locale) {
|
||||||
state.locale = locale;
|
state.locale = locale;
|
||||||
i18n.locale = locale;
|
i18n.locale = locale;
|
||||||
|
persistentStore.set('locale', state.locale);
|
||||||
},
|
},
|
||||||
SET_NOTIFICATIONS_TIMEOUT (state, timeout) {
|
SET_NOTIFICATIONS_TIMEOUT (state, timeout) {
|
||||||
state.notifications_timeout = timeout;
|
state.notifications_timeout = timeout;
|
||||||
|
persistentStore.set('notifications_timeout', state.notifications_timeout);
|
||||||
|
},
|
||||||
|
SET_AUTO_COMPLETE (state, val) {
|
||||||
|
state.auto_complete = val;
|
||||||
|
persistentStore.set('auto_complete', state.auto_complete);
|
||||||
},
|
},
|
||||||
SET_EXPLOREBAR_SIZE (state, size) {
|
SET_EXPLOREBAR_SIZE (state, size) {
|
||||||
state.explorebar_size = size;
|
state.explorebar_size = size;
|
||||||
|
persistentStore.set('explorebar_size', state.explorebar_size);
|
||||||
|
},
|
||||||
|
SET_EDITOR_THEME (state, theme) {
|
||||||
|
state.editor_theme = theme;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
@@ -35,6 +53,12 @@ export default {
|
|||||||
},
|
},
|
||||||
changeExplorebarSize ({ commit }, size) {
|
changeExplorebarSize ({ commit }, size) {
|
||||||
commit('SET_EXPLOREBAR_SIZE', size);
|
commit('SET_EXPLOREBAR_SIZE', size);
|
||||||
|
},
|
||||||
|
changeAutoComplete ({ commit }, val) {
|
||||||
|
commit('SET_AUTO_COMPLETE', val);
|
||||||
|
},
|
||||||
|
changeEditorTheme ({ commit }, theme) {
|
||||||
|
commit('SET_EDITOR_THEME', theme);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -111,11 +111,10 @@ export default {
|
|||||||
}
|
}
|
||||||
: workspace);
|
: workspace);
|
||||||
},
|
},
|
||||||
NEW_TAB (state, uid) {
|
NEW_TAB (state, { uid, tab }) {
|
||||||
tabIndex[uid] = tabIndex[uid] ? ++tabIndex[uid] : 1;
|
tabIndex[uid] = tabIndex[uid] ? ++tabIndex[uid] : 1;
|
||||||
|
|
||||||
const newTab = {
|
const newTab = {
|
||||||
uid: uidGen('T'),
|
uid: tab,
|
||||||
index: tabIndex[uid],
|
index: tabIndex[uid],
|
||||||
selected: false,
|
selected: false,
|
||||||
type: 'query',
|
type: 'query',
|
||||||
@@ -338,7 +337,10 @@ export default {
|
|||||||
lastBreadcrumbs = { ...breadcrumbsObj, ...payload };
|
lastBreadcrumbs = { ...breadcrumbsObj, ...payload };
|
||||||
},
|
},
|
||||||
newTab ({ commit }, uid) {
|
newTab ({ commit }, uid) {
|
||||||
commit('NEW_TAB', uid);
|
const tab = uidGen('T');
|
||||||
|
|
||||||
|
commit('NEW_TAB', { uid, tab });
|
||||||
|
commit('SELECT_TAB', { uid, tab });
|
||||||
},
|
},
|
||||||
removeTab ({ commit }, payload) {
|
removeTab ({ commit }, payload) {
|
||||||
commit('REMOVE_TAB', payload);
|
commit('REMOVE_TAB', payload);
|
||||||
|
@@ -1,12 +0,0 @@
|
|||||||
import { functions } from '@/suggestions/sql/sql-functions';
|
|
||||||
import { keywords } from '@/suggestions/sql/sql-keywords';
|
|
||||||
import { operators } from '@/suggestions/sql/sql-operators';
|
|
||||||
import { variables } from '@/suggestions/sql/sql-variables';
|
|
||||||
|
|
||||||
export const completionItemProvider = (monaco) => {
|
|
||||||
return {
|
|
||||||
provideCompletionItems () {
|
|
||||||
return { suggestions: [...functions(monaco), ...keywords(monaco), ...operators(monaco), ...variables(monaco)] };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,142 +0,0 @@
|
|||||||
export const operators = (monaco) => {
|
|
||||||
return [{
|
|
||||||
label: 'ALL',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'ALL'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'AND',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'AND'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'ANY',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'ANY'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'BETWEEN',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'BETWEEN'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'EXISTS',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'EXISTS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'IN',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'IN'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'LIKE',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'LIKE'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'NOT',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'NOT'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'OR',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'OR'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'SOME',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'SOME'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'EXCEPT',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'EXCEPT'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'INTERSECT',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'INTERSECT'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'UNION',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'UNION'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'APPLY',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'APPLY'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'CROSS',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'CROSS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'FULL',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'FULL'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'INNER',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'INNER'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'JOIN',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'JOIN'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'LEFT',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'LEFT'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'OUTER',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'OUTER'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'RIGHT',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'RIGHT'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'CONTAINS',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'CONTAINS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'FREETEXT',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'FREETEXT'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'IS',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'IS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'NULL',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'NULL'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'PIVOT',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'PIVOT'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'UNPIVOT',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'UNPIVOT'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'MATCHED',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Operator,
|
|
||||||
insertText: 'MATCHED'
|
|
||||||
}];
|
|
||||||
};
|
|
@@ -1,172 +0,0 @@
|
|||||||
export const variables = (monaco) => {
|
|
||||||
return [{
|
|
||||||
label: '@@DATEFIRST',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@DATEFIRST'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@DBTS',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@DBTS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@LANGID',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@LANGID'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@LANGUAGE',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@LANGUAGE'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@LOCK_TIMEOUT',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@LOCK_TIMEOUT'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@MAX_CONNECTIONS',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@MAX_CONNECTIONS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@MAX_PRECISION',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@MAX_PRECISION'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@NESTLEVEL',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@NESTLEVEL'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@OPTIONS',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@OPTIONS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@REMSERVER',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@REMSERVER'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@SERVERNAME',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@SERVERNAME'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@SERVICENAME',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@SERVICENAME'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@SPID',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@SPID'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@TEXTSIZE',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@TEXTSIZE'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@VERSION',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@VERSION'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@CURSOR_ROWS',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@CURSOR_ROWS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@FETCH_STATUS',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@FETCH_STATUS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@DATEFIRST',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@DATEFIRST'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@PROCID',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@PROCID'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@ERROR',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@ERROR'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@IDENTITY',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@IDENTITY'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@ROWCOUNT',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@ROWCOUNT'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@TRANCOUNT',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@TRANCOUNT'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@CONNECTIONS',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@CONNECTIONS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@CPU_BUSY',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@CPU_BUSY'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@IDLE',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@IDLE'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@IO_BUSY',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@IO_BUSY'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@PACKET_ERRORS',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@PACKET_ERRORS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@PACK_RECEIVED',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@PACK_RECEIVED'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@PACK_SENT',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@PACK_SENT'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@TIMETICKS',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@TIMETICKS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@TOTAL_ERRORS',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@TOTAL_ERRORS'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@TOTAL_READ',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@TOTAL_READ'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '@@TOTAL_WRITE',
|
|
||||||
kind: monaco.languages.CompletionItemKind.Variable,
|
|
||||||
insertText: '@@TOTAL_WRITE'
|
|
||||||
}];
|
|
||||||
};
|
|
@@ -1,12 +1,8 @@
|
|||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const MonacoEditorPlugin = require('monaco-editor-webpack-plugin');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
stats: 'errors-warnings',
|
stats: 'errors-warnings',
|
||||||
plugins: [
|
plugins: [
|
||||||
new MonacoEditorPlugin({
|
|
||||||
languages: ['sql']
|
|
||||||
}),
|
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
'process.env': {
|
'process.env': {
|
||||||
PACKAGE_VERSION: JSON.stringify(require('./package.json').version)
|
PACKAGE_VERSION: JSON.stringify(require('./package.json').version)
|
||||||
|
Reference in New Issue
Block a user