mirror of
https://github.com/Fabio286/antares.git
synced 2025-06-05 21:59:22 +02:00
Compare commits
139 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1df57dc705 | |||
| 86240fb53c | |||
| 1d363f755e | |||
| 0d77aee3eb | |||
| a41cf1ab56 | |||
| 5ceddb8e00 | |||
| 16e17b39b6 | |||
| 20cba6ee9b | |||
| 9ffd443a66 | |||
| f82dbd24dc | |||
| 6eb2977568 | |||
| cafb65560a | |||
| 532d963019 | |||
| 1b0a63ff31 | |||
| c22187c305 | |||
| dcccb544f9 | |||
| 7d2ace9456 | |||
| 2584c9b9c2 | |||
| a6b75ad0dc | |||
| 90fd9db917 | |||
| c0f54b9514 | |||
| cd31413256 | |||
| b33199ea59 | |||
| dea5ec7513 | |||
| be816e8588 | |||
| 1e938adc5d | |||
| 8735a0c5f9 | |||
| 3dde1c109e | |||
| d0b3e1b1b8 | |||
| c20bff7bcb | |||
| 9f5ec0276c | |||
| 55932fe115 | |||
| d374372e20 | |||
| bb5f44681f | |||
| 49a4e1cb7b | |||
| c2f76e490a | |||
| e349dd5eab | |||
| 280697698e | |||
| 0783f8b57e | |||
| c981244d7a | |||
| dcb135dd01 | |||
| 99f7511c4d | |||
| fe4c8e12b3 | |||
| 21728a663d | |||
| 9ca03f4625 | |||
| affb7288b0 | |||
| 614e0d3275 | |||
| feef5e30ee | |||
| 82c25711b6 | |||
| 2ca2988832 | |||
| e3f259c6e8 | |||
| e7401cc96e | |||
| 13b9840f3d | |||
| d20414b692 | |||
| 22a8c25717 | |||
| db47b4040a | |||
| e89911b185 | |||
| fccfe92453 | |||
| d465e18dba | |||
| ffb1712a59 | |||
| 9f6a183d9b | |||
| 1f80a64fe1 | |||
| fc651149b9 | |||
| 964570247f | |||
| 8a6c59f7ce | |||
| 4d844fe2c9 | |||
| d892fa6fb3 | |||
| 8c9e4f6e96 | |||
| 966ca60c89 | |||
| 9bbe218f90 | |||
| a1c6be372b | |||
| 7d0c929fb8 | |||
| 25d72e3952 | |||
| b232a3bb5f | |||
| e9a26c1bc0 | |||
| 76c5c0c680 | |||
| ddfb713124 | |||
| 0081a4167c | |||
| 239cb4488f | |||
| fb5adbe676 | |||
| 9cd51c8d8b | |||
| 8dfaa3b7be | |||
| 5d7efa75b7 | |||
| 4862d51fba | |||
| 07f60c3917 | |||
| 049143d143 | |||
| db4430609e | |||
| 71b4310117 | |||
| 201fad9265 | |||
| 45351faeae | |||
| b4ead6992c | |||
| b1ea32b680 | |||
| 39ca1974bc | |||
| 777b73fa6f | |||
| 4494e637f7 | |||
| 219da0aba4 | |||
| 7e8167154f | |||
| 3aa2159a1a | |||
| 76d92cd106 | |||
| c8545a250b | |||
| dbab06fcb8 | |||
| b54fefbf25 | |||
| 9a1bf32128 | |||
| 110b0b414c | |||
| 2f58007af4 | |||
| 9b60bfff8d | |||
| 3b37b7432e | |||
| 7c4ca999ce | |||
| 94c4952319 | |||
| 014257147e | |||
| 970de4962b | |||
| b5a828309f | |||
| 5b21d17f3a | |||
| 2c6e35288f | |||
| bcadac6e95 | |||
|
|
18a93ef1aa | ||
| 6c62052b47 | |||
| 9d5ebefdce | |||
| 34ebc6b72d | |||
| 288ff4c1a1 | |||
| a176174b8d | |||
| 0f69d1dbb7 | |||
| 0386bbac50 | |||
| b0576acdf6 | |||
| 9a190854fe | |||
| 2aace28e80 | |||
| 02c03e3d26 | |||
| a0d85520fb | |||
| ede6fe81ce | |||
| 4e72bb1587 | |||
| 15417e8a77 | |||
| 88ab7c5a62 | |||
| 5940b0b842 | |||
| 574d493908 | |||
| af96647603 | |||
| ab70ff239e | |||
| bacf458936 | |||
| 379d6c169a | |||
|
|
36352f8028 |
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
|
|
||||||
github: [fabio286]
|
github: [fabio286]
|
||||||
patreon: fabio286
|
patreon: #fabio286
|
||||||
open_collective: # Replace with a single Open Collective username
|
open_collective: # Replace with a single Open Collective username
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
|||||||
9
.github/ISSUE_TEMPLATE/bug_report.md
vendored
9
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -3,7 +3,7 @@ name: Bug report
|
|||||||
about: Create a report to help us improve
|
about: Create a report to help us improve
|
||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
labels: ''
|
||||||
assignees: ''
|
assignees: Fabio286
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -25,13 +25,6 @@ If applicable, add screenshots to help explain your problem.
|
|||||||
|
|
||||||
**Desktop (please complete the following information):**
|
**Desktop (please complete the following information):**
|
||||||
- OS: [e.g. iOS]
|
- OS: [e.g. iOS]
|
||||||
- Browser [e.g. chrome, safari]
|
|
||||||
- Version [e.g. 22]
|
|
||||||
|
|
||||||
**Smartphone (please complete the following information):**
|
|
||||||
- Device: [e.g. iPhone6]
|
|
||||||
- OS: [e.g. iOS8.1]
|
|
||||||
- Browser [e.g. stock browser, safari]
|
|
||||||
- Version [e.g. 22]
|
- Version [e.g. 22]
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -3,7 +3,7 @@ name: Feature request
|
|||||||
about: Suggest an idea for this project
|
about: Suggest an idea for this project
|
||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
labels: ''
|
||||||
assignees: ''
|
assignees: Fabio286
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
26
.github/workflows/build.yml
vendored
Normal file
26
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
name: Build/release
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out Git repository
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Install Node.js, NPM and Yarn
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 10
|
||||||
|
|
||||||
|
- name: Build/release Electron app
|
||||||
|
uses: samuelmeuli/action-electron-builder@v1
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.github_token }}
|
||||||
|
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
@@ -8,7 +8,8 @@
|
|||||||
"stylelint-scss"
|
"stylelint-scss"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"at-rule-no-unknown": null
|
"at-rule-no-unknown": null,
|
||||||
|
"no-descending-specificity": null
|
||||||
},
|
},
|
||||||
"syntax": "scss"
|
"syntax": "scss"
|
||||||
}
|
}
|
||||||
52
.travis.yml
52
.travis.yml
@@ -1,52 +0,0 @@
|
|||||||
language: node_js
|
|
||||||
node_js: 12
|
|
||||||
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- node_modules
|
|
||||||
- app/node_modules
|
|
||||||
- $HOME/.cache/electron
|
|
||||||
- $HOME/.cache/electron-builder
|
|
||||||
- $HOME/.npm/_prebuilds
|
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- ELECTRON_CACHE=$HOME/.cache/electron
|
|
||||||
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
include:
|
|
||||||
- stage: Test
|
|
||||||
before_install:
|
|
||||||
- sudo apt-get install libsecret-1-dev
|
|
||||||
- npm install
|
|
||||||
script:
|
|
||||||
- npm test
|
|
||||||
|
|
||||||
- stage: Deploy Linux & Windows
|
|
||||||
if: tag IS present
|
|
||||||
os: linux
|
|
||||||
services: docker
|
|
||||||
before_install:
|
|
||||||
- sudo apt-get install libsecret-1-dev
|
|
||||||
- npm install
|
|
||||||
script:
|
|
||||||
- docker run --rm --env-file <(env | grep -iE 'DEBUG|NODE_|ELECTRON_|NPM_|CI|CIRCLE|TRAVIS|APPVEYOR_|CSC_|_TOKEN|_KEY|AWS_|STRIP|BUILD_') -v ${PWD}:/project -v ~/.cache/electron:/root/.cache/electron -v ~/.cache/electron-builder:/root/.cache/electron-builder electronuserland/builder:wine /bin/bash -c "npm run build -- --linux --win -p always"
|
|
||||||
before_cache:
|
|
||||||
- rm -rf $HOME/.cache/electron-builder/wine
|
|
||||||
|
|
||||||
- stage: Deploy Mac
|
|
||||||
if: tag IS present
|
|
||||||
os: osx
|
|
||||||
before_install:
|
|
||||||
- npm install
|
|
||||||
osx_image: xcode10.2
|
|
||||||
script:
|
|
||||||
- npm run build -- -p always
|
|
||||||
|
|
||||||
# - stage: Deploy ARM Linux
|
|
||||||
# if: tag IS present
|
|
||||||
# os: linux
|
|
||||||
# arch: arm64
|
|
||||||
# script:
|
|
||||||
# - npm run build -- --linux AppImage -p always
|
|
||||||
220
CHANGELOG.md
220
CHANGELOG.md
@@ -2,6 +2,226 @@
|
|||||||
|
|
||||||
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.1.4](https://github.com/Fabio286/antares/compare/v0.1.3...v0.1.4) (2021-04-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* query results export ([1d363f7](https://github.com/Fabio286/antares/commit/1d363f755e025d0fc6fec61cbd47ff87a8f25728))
|
||||||
|
* **UI:** canc press to delete selected rows ([20cba6e](https://github.com/Fabio286/antares/commit/20cba6ee9bc1daa902b04d6e2ddcb31d04fbf805))
|
||||||
|
* **UI:** ctrl+s shortcut to save changes ([16e17b3](https://github.com/Fabio286/antares/commit/16e17b39b6c8b561cc018d02afee2276190ce304))
|
||||||
|
* **UI:** format and clear queries ([9ffd443](https://github.com/Fabio286/antares/commit/9ffd443a66303f88fc4529896f6d1d7917454f7a))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* launch from shortcut of procedures or functions with parameters without name dont works ([f82dbd2](https://github.com/Fabio286/antares/commit/f82dbd24dcef7b4d8d127a604e256b3f79a6c617))
|
||||||
|
* wrong changelog in some cases ([a41cf1a](https://github.com/Fabio286/antares/commit/a41cf1ab5662f5f5fdedff4a9e1c626c23071377))
|
||||||
|
* **UI:** data type not listed in selection if not present in global types ([6eb2977](https://github.com/Fabio286/antares/commit/6eb2977568987b9440b62ae7dbd7183338bfcc9b))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **UI:** improved connection status indicator ([5ceddb8](https://github.com/Fabio286/antares/commit/5ceddb8e00f3bc1984b8e47de270dc39b367903f))
|
||||||
|
|
||||||
|
### [0.1.3](https://github.com/Fabio286/antares/compare/v0.1.2...v0.1.3) (2021-04-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **PostgreSQL:** functions management ([cd31413](https://github.com/Fabio286/antares/commit/cd3141325681ea572c06b8998dd7bd334ceb3236))
|
||||||
|
* **PostgreSQL:** procedure language select ([b33199e](https://github.com/Fabio286/antares/commit/b33199ea59c60b467601f333857494aa40adf4e8))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** invalid JavaScript datetime values not shown ([dcccb54](https://github.com/Fabio286/antares/commit/dcccb544f9ec24ad693c9e81fb4bcfbdbb7cc4e1))
|
||||||
|
* approximate row count shown for results less than 1000 ([a6b75ad](https://github.com/Fabio286/antares/commit/a6b75ad0dc0d884332464c277e8542b2698630b9))
|
||||||
|
* field apparently loses index or foreign key on rename in table editor ([7d2ace9](https://github.com/Fabio286/antares/commit/7d2ace94562f8da307b15b83c89d919727d800c8))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **MySQL:** improved the way to get routine and functions parameters ([90fd9db](https://github.com/Fabio286/antares/commit/90fd9db917c40262f2bc2501ab86f5feba3d8db4))
|
||||||
|
* **UI:** improved table fields suggestion in query editor ([c22187c](https://github.com/Fabio286/antares/commit/c22187c3053aef368a351cc35e2f1d407ecde209))
|
||||||
|
|
||||||
|
### [0.1.2](https://github.com/Fabio286/antares/compare/v0.1.1...v0.1.2) (2021-04-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* in-app last release changelog ([1e938ad](https://github.com/Fabio286/antares/commit/1e938adc5d8eb5ad16ab16342375eecd88f68d20))
|
||||||
|
* **PostgreSQL:** edit timezone in cell editor ([8735a0c](https://github.com/Fabio286/antares/commit/8735a0c5f9e5b6b3bcaadf37ce158aa7beae2c48))
|
||||||
|
* **PostgreSQL:** procedures management ([3dde1c1](https://github.com/Fabio286/antares/commit/3dde1c109e23342d94362626ef7350dc123ea859))
|
||||||
|
* **PostgreSQL:** support of arrays in table settings ([d0b3e1b](https://github.com/Fabio286/antares/commit/d0b3e1b1b8be9d2c038d70e16d4478671315de8f))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* cell edit doesn't properly use primary or unique index to update if both present, closes [#51](https://github.com/Fabio286/antares/issues/51) ([55932fe](https://github.com/Fabio286/antares/commit/55932fe11583bd5ff48f82b8408965adba4f5071))
|
||||||
|
* deletion of rows from query results ([c20bff7](https://github.com/Fabio286/antares/commit/c20bff7bcbe340ac99ebcacaba3359edd61c068a))
|
||||||
|
* no foreign key select when cell value is NULL, closes [#50](https://github.com/Fabio286/antares/issues/50) ([9f5ec02](https://github.com/Fabio286/antares/commit/9f5ec0276c92904975fdaea34b4c845c92bfe8d4))
|
||||||
|
* wrong datetime conversion when updating a row without an unique key ([d374372](https://github.com/Fabio286/antares/commit/d374372e208318d7e50b258a8041145bdf7992c5))
|
||||||
|
* **PostgreSQL:** issue with selected schema different than public ([49a4e1c](https://github.com/Fabio286/antares/commit/49a4e1cb7b24642641265d5830d3fee370cceeb4))
|
||||||
|
* **UI:** white readonly inputs with dark theme ([bb5f446](https://github.com/Fabio286/antares/commit/bb5f44681f87aacf2cd2f60a6d958c5732289790))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **UI:** improved setting modal rendering ([be816e8](https://github.com/Fabio286/antares/commit/be816e85888b4f3d26cbb9caac0adbc4dde0ea94))
|
||||||
|
|
||||||
|
### [0.1.1](https://github.com/Fabio286/antares/compare/v0.1.0...v0.1.1) (2021-04-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* scratchpad to save persistent notes ([e349dd5](https://github.com/Fabio286/antares/commit/e349dd5eaba608591257f2799b830805e4936c27))
|
||||||
|
* **PostgreSQL:** foreign keys management ([fe4c8e1](https://github.com/Fabio286/antares/commit/fe4c8e12b39dd3cdfc233f07e3fe2ff0676252b0))
|
||||||
|
* **PostgreSQL:** indexes management ([9ca03f4](https://github.com/Fabio286/antares/commit/9ca03f462560b634970a19d3d97b878d60509acc))
|
||||||
|
* **PostgreSQL:** table fields edit ([e3f259c](https://github.com/Fabio286/antares/commit/e3f259c6e8327d71bd7dd0a9c33d957dc6ca1fb8))
|
||||||
|
* **PostgreSQL:** tables addition ([feef5e3](https://github.com/Fabio286/antares/commit/feef5e30eec915cbb219223cc428bd4e98d2e9c5))
|
||||||
|
* **PostgreSQL:** unique keys management ([614e0d3](https://github.com/Fabio286/antares/commit/614e0d32758c13b59139d349d4682a5bafc3ca88))
|
||||||
|
* **PostgreSQL:** views management ([99f7511](https://github.com/Fabio286/antares/commit/99f7511c4d5fab4030b30d5134cd03248167faea))
|
||||||
|
* **UI:** light theme ([2806976](https://github.com/Fabio286/antares/commit/280697698ea5fae6d54326970c823878888c196c))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **UI:** editor theme preview not properly loaded in some cases ([c981244](https://github.com/Fabio286/antares/commit/c981244d7aa93ca18ca2de44bf8df06f253b9d20))
|
||||||
|
* fields of ref. table not automatically loaded in foreign keys modal ([e7401cc](https://github.com/Fabio286/antares/commit/e7401cc96e76e00100a88eea9f40541fd8027adb))
|
||||||
|
* hide update tab for Windows Store distributions ([dcb135d](https://github.com/Fabio286/antares/commit/dcb135dd015b8f8c6cfb44021211bb8cf3089192))
|
||||||
|
|
||||||
|
## [0.1.0](https://github.com/Fabio286/antares/compare/v0.0.20...v0.1.0) (2021-03-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **PostgreSQL:** database in connection parameters ([9645702](https://github.com/Fabio286/antares/commit/964570247ff5b7b8317419730eec5bed4f0f0580))
|
||||||
|
* **PostgreSQL:** edit array and text search fields ([fc65114](https://github.com/Fabio286/antares/commit/fc651149b95399c52d2d63e946731e9c1b0303a9))
|
||||||
|
* **PostgreSQL:** insert and edit blob fields ([1f80a64](https://github.com/Fabio286/antares/commit/1f80a64fe1400baacca26f1a762c5aeb4ef6350d))
|
||||||
|
* **PostgreSQL:** partial postgre implementation ([d892fa6](https://github.com/Fabio286/antares/commit/d892fa6fb3e86fbb96887d8eb67319ae855260a1))
|
||||||
|
* **PostgreSQL:** support to microseconds ([d465e18](https://github.com/Fabio286/antares/commit/d465e18dba8ea3aa00726e33f9b1f70ca4c0683c))
|
||||||
|
* **UI:** support to boolean fields ([ffb1712](https://github.com/Fabio286/antares/commit/ffb1712a593d1421793011e48a17369b884ea3c0))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* update or delete rows with more than one primary key ([22a8c25](https://github.com/Fabio286/antares/commit/22a8c25717a4d4b285855426098a3a2846ce7448))
|
||||||
|
* **MySQL:** handle NEWDECIMAL data type ([a1c6be3](https://github.com/Fabio286/antares/commit/a1c6be372b570cf13e89ef7ecf9b7a7c033a9293))
|
||||||
|
* **PostgreSQL:** issue getting foreign keys informations ([db47b40](https://github.com/Fabio286/antares/commit/db47b4040a5282a6ac0711b1926c4c2ac867999e))
|
||||||
|
* remove last char from datetime and time if is a dot ([e89911b](https://github.com/Fabio286/antares/commit/e89911b1851c19813d4acf2c79adfbc2ac7c1112))
|
||||||
|
* **PostgreSQL:** single quote escape ([9f6a183](https://github.com/Fabio286/antares/commit/9f6a183d9b293dfe9ad9f3759f2375f05f37db8e))
|
||||||
|
* **PostgreSQL:** various issues in query results ([fccfe92](https://github.com/Fabio286/antares/commit/fccfe92453325cd54c0331cc5670af0a56822c5b))
|
||||||
|
* schema content not loaded if selected with right click ([8a6c59f](https://github.com/Fabio286/antares/commit/8a6c59f7ce7d051315b04cea38a96e4739b5b9d3))
|
||||||
|
|
||||||
|
### [0.0.20](https://github.com/Fabio286/antares/compare/v0.0.19...v0.0.20) (2021-03-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **UI:** loader layers on query and data tabs ([b232a3b](https://github.com/Fabio286/antares/commit/b232a3bb5ff7e38c83aa33e8b96ec7202bc4881e))
|
||||||
|
* **UI:** row markers in sql editors ([ddfb713](https://github.com/Fabio286/antares/commit/ddfb7131248a47fa2055ccfb72e223986a17f986))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **UI:** avoid unnecessary updates when cell content not change ([e9a26c1](https://github.com/Fabio286/antares/commit/e9a26c1bc0641b7087d8143cc948405850d7552f))
|
||||||
|
* **UI:** row mark not applied on first click ([25d72e3](https://github.com/Fabio286/antares/commit/25d72e39529884c09cf1286ff64ba00c8a5c7b24))
|
||||||
|
* **UI:** table rows lose internal id after an update ([76c5c0c](https://github.com/Fabio286/antares/commit/76c5c0c680521b4a20de1f12bab25314ac084d5c))
|
||||||
|
|
||||||
|
### [0.0.19](https://github.com/Fabio286/antares/compare/v0.0.18...v0.0.19) (2021-03-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **UI:** modal that shows process query ([07f60c3](https://github.com/Fabio286/antares/commit/07f60c39173e9db452909d74573c3aecf4b6466b))
|
||||||
|
* processes list tool ([049143d](https://github.com/Fabio286/antares/commit/049143d143d8187bfcb6377f2bf374c471c28046))
|
||||||
|
* **MySQL:** support to new mysql8 authentication, closes [#45](https://github.com/Fabio286/antares/issues/45) ([db44306](https://github.com/Fabio286/antares/commit/db4430609e22816f4a3a8ecdbf61e9b51cde2579))
|
||||||
|
* context menu shortcut to set NULL a table cell ([71b4310](https://github.com/Fabio286/antares/commit/71b43101172c27d810d533c936534bcf089dbca2))
|
||||||
|
* **UI:** esc key to cancel cell edit ([45351fa](https://github.com/Fabio286/antares/commit/45351faeaea6ad4af8280da6c0ec1b4ded0f86fe))
|
||||||
|
* setting to enable beta updates (future use) ([b1ea32b](https://github.com/Fabio286/antares/commit/b1ea32b68024593481120e2ef67642e127554888))
|
||||||
|
* **UI:** query duration calc ([777b73f](https://github.com/Fabio286/antares/commit/777b73fa6f9d6f7806e0d3d07589dfaee0c40786))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** wrong TIMESTAMP fields length ([201fad9](https://github.com/Fabio286/antares/commit/201fad9265e01e19ae4c8dc16bff84ee9dc9c894))
|
||||||
|
* **UI:** modal processes list does not regain size on window resize ([5d7efa7](https://github.com/Fabio286/antares/commit/5d7efa75b76f59b85654603b4df8035a36af0576))
|
||||||
|
* **UI:** wrong height in scrolling tables in some cases ([4862d51](https://github.com/Fabio286/antares/commit/4862d51fba863e055ca6735586b0abe4894037eb))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **UI:** big performance improvement in tables rendering ([39ca197](https://github.com/Fabio286/antares/commit/39ca1974bcea84c9047779893ed3f458300474e3))
|
||||||
|
* **UI:** improvements of date time inputs ([b4ead69](https://github.com/Fabio286/antares/commit/b4ead6992c8ddc172380a97e7aa125e0dd078f1e))
|
||||||
|
|
||||||
|
### [0.0.18](https://github.com/Fabio286/antares/compare/v0.0.17...v0.0.18) (2021-02-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **UI:** context menu for input and textarea tags ([b54fefb](https://github.com/Fabio286/antares/commit/b54fefbf255c91f3cdd0c52b917b239ea40dae16))
|
||||||
|
* **UI:** html, xml, json, svg and yaml editor modes in long text fields edit ([9a1bf32](https://github.com/Fabio286/antares/commit/9a1bf3212850d3783ac6382f8a980eb25c4f4226))
|
||||||
|
* **UI:** run procedures/functions from sidebar context menu ([219da0a](https://github.com/Fabio286/antares/commit/219da0aba46839295807a01056199d42584e0af9))
|
||||||
|
* **UI:** run routines/functions from settings tab ([7e81671](https://github.com/Fabio286/antares/commit/7e8167154ffd64e46d78a50cd13cf90cee61370c))
|
||||||
|
* **UI:** search filter in explore bar ([2f58007](https://github.com/Fabio286/antares/commit/2f58007af4f32207fe4be9fc15bb43916e8e0abc))
|
||||||
|
* **UI:** sticky schema names in explore bar ([110b0b4](https://github.com/Fabio286/antares/commit/110b0b414cef25395ae28493032c2048307e4783))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** issue obtaining routine/function parameters ([3aa2159](https://github.com/Fabio286/antares/commit/3aa2159a1ae3b6785646fc7ac1e866cb0277d087))
|
||||||
|
* issue managing function/routine parameters ([76d92cd](https://github.com/Fabio286/antares/commit/76d92cd106b0409e8752c43d7d587d09dd3e1e32))
|
||||||
|
* **UI:** data tab opened when non-table element is selected ([dbab06f](https://github.com/Fabio286/antares/commit/dbab06fcb80ccdae85215ceae9ba24af3e7ff263))
|
||||||
|
* **UI:** elements from previous selected schemas in query suggestions ([c8545a2](https://github.com/Fabio286/antares/commit/c8545a250bc696c339e418a69a2942d5bac10cbf))
|
||||||
|
* disabled sort for fields without a name property ([3b37b74](https://github.com/Fabio286/antares/commit/3b37b7432e969e6315ebd6ed74905ec3980a049c))
|
||||||
|
* prevents F5 shortcut to run in non-selected workspaces ([7c4ca99](https://github.com/Fabio286/antares/commit/7c4ca999ce64af8077d29e419f430d5beaaead93))
|
||||||
|
* support of bit fields in table filler ([94c4952](https://github.com/Fabio286/antares/commit/94c49523197284510361ebbb6984816a3bb1b243))
|
||||||
|
|
||||||
|
### [0.0.17](https://github.com/Fabio286/antares/compare/v0.0.16...v0.0.17) (2021-02-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Added french language ([18a93ef](https://github.com/Fabio286/antares/commit/18a93ef1aae530e69c9062bbeb08a3beec206eda))
|
||||||
|
* fake table data generator ([a176174](https://github.com/Fabio286/antares/commit/a176174b8d7dc232920f4cd7c5e3f8e4c58d51a0))
|
||||||
|
* min and max option for random floats and numbers ([6c62052](https://github.com/Fabio286/antares/commit/6c62052b4764731c774fef90784342447b36deb7))
|
||||||
|
* support to fake data locales ([970de49](https://github.com/Fabio286/antares/commit/970de4962b3bffec271bfa6d4747e6fb9d408ba6))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **UI:** file uploader in table filler ([b5a8283](https://github.com/Fabio286/antares/commit/b5a828309f067636eae2120032f07e01233706e2))
|
||||||
|
* **UI:** no foreign key select editing query results ([5b21d17](https://github.com/Fabio286/antares/commit/5b21d17f3a8f2482c3aafe85f26c34cd3c0a1fcf))
|
||||||
|
* cut faker text based on field length ([288ff4c](https://github.com/Fabio286/antares/commit/288ff4c1a1c77f4b8b86b24649d805836366fdd3))
|
||||||
|
* wrong date or time detection in field default ([9d5ebef](https://github.com/Fabio286/antares/commit/9d5ebefdced999af595f3d8dc2fac2b18fa8258b))
|
||||||
|
* **UI:** better text on ssl file selectors ([9a19085](https://github.com/Fabio286/antares/commit/9a190854fe3c73ed4e7f89545ff259e15ee9f947))
|
||||||
|
* **UI:** wrong length for char fields on table header ([0f69d1d](https://github.com/Fabio286/antares/commit/0f69d1dbb7958e45059b6b738c845abea1ad3225))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **core:** bulk inserts support ([b0576ac](https://github.com/Fabio286/antares/commit/b0576acdf65d41c1c8e0b0bca6cf6522dcb372be))
|
||||||
|
|
||||||
|
### [0.0.16](https://github.com/Fabio286/antares/compare/v0.0.15...v0.0.16) (2021-02-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* MySQL and MariaDB auto detection ([02c03e3](https://github.com/Fabio286/antares/commit/02c03e3d266052872ac8edeb159ec8182f41c6a5))
|
||||||
|
* **UI:** enanched file upload input ([a0d8552](https://github.com/Fabio286/antares/commit/a0d85520fb0669d5eec4475f5e255adb1e2a0159))
|
||||||
|
* support to ssl connections ([4e72bb1](https://github.com/Fabio286/antares/commit/4e72bb15874324214aa0f5b89057cdd565744468))
|
||||||
|
* **UI:** database version in app footer ([15417e8](https://github.com/Fabio286/antares/commit/15417e8a776c6aa9f90762d198d70b26163bb2df))
|
||||||
|
* **UI:** resize query editor area ([88ab7c5](https://github.com/Fabio286/antares/commit/88ab7c5a62654c6a6026b63464224226d33b1950))
|
||||||
|
* delete rows from tables without a primary key ([574d493](https://github.com/Fabio286/antares/commit/574d4939083577ffcb8e7c65f572c364eb8415fb))
|
||||||
|
* edit rows from tables without a primary key ([5940b0b](https://github.com/Fabio286/antares/commit/5940b0b84207093da141b5c617c41e0a18fcb1d7))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* compatibility with electron-store 7 ([bacf458](https://github.com/Fabio286/antares/commit/bacf45893676cb0744907703f6534ffd472bd1dd))
|
||||||
|
* edit bit fields ([ede6fe8](https://github.com/Fabio286/antares/commit/ede6fe81cefc91cdce2bbb0cd7cd6f85bbca99b8))
|
||||||
|
|
||||||
### [0.0.15](https://github.com/Fabio286/antares/compare/v0.0.14...v0.0.15) (2021-01-23)
|
### [0.0.15](https://github.com/Fabio286/antares/compare/v0.0.14...v0.0.15) (2021-01-23)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
49
README.md
49
README.md
@@ -1,19 +1,19 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img width="800" src="https://raw.githubusercontent.com/Fabio286/antares/master/docs/screen-alpha.png">
|
<img width="800" src="https://raw.githubusercontent.com/Fabio286/antares/master/docs/gh-logo.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
# Antares SQL Client
|
# Antares SQL Client
|
||||||
|
|
||||||
 [](https://travis-ci.com/Fabio286/antares)  
|
   [](https://snapcraft.io/antares) [](https://snapcraft.io/antares) [](https://www.treedom.net/en/user/fabio-di-stasio/event/antares-for-the-planet)
|
||||||
|
|
||||||
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 supports only MySQL.
|
**At the moment this application is in development state, many features will come in future updates**, and supports only MySQL/MariaDB and PostgreSQL (partially).
|
||||||
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.
|
Many of its current features are enough to have a pleasant user experience with MySQL/MariaDB, and basic functionalites with PostgreSQL, 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.
|
I'm actively working on it, 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 Antares you can download and install the [latest release](https://github.com/Fabio286/antares/releases/latest).
|
||||||
👁 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.
|
||||||
🌟 Don't forget to **leave a star** if you appreciate this project.
|
🌟 Don't forget to **leave a star** if you appreciate this project.
|
||||||
|
|
||||||
@@ -21,7 +21,12 @@ I'm actively working on it (yes, i'm a lone dev), hoping to provide cool feature
|
|||||||
|
|
||||||
Why am I developing an SQL client when there are a lot of them on the market?
|
Why am I developing an SQL client when there are a lot of them on the market?
|
||||||
The main goal is to develop a totally free, full featured, cross platform and open source alternative, empowered by JavaScript's ecosystem.
|
The main goal is to develop a totally free, full featured, cross platform and open source alternative, empowered by JavaScript's ecosystem.
|
||||||
An application created with minimalism and semplicity in mind, with features in the right places, not hundreds of tiny buttons or submenu.
|
A modern application created with minimalism and semplicity in mind, with features in the right places, not hundreds of tiny buttons, tabs or submenu.
|
||||||
|
|
||||||
|
## Download
|
||||||
|
|
||||||
|
[](https://snapcraft.io/antares) [](https://www.microsoft.com/p/antares-sql-client/9nhtb9sq51r1?cid=storebadge&ocid=badge&rtc=1&activetab=pivot:overviewtab)
|
||||||
|
🚀 **[Other Downloads](https://github.com/Fabio286/antares/releases/latest)**
|
||||||
|
|
||||||
## How to contribute
|
## How to contribute
|
||||||
|
|
||||||
@@ -33,9 +38,11 @@ An application created with minimalism and semplicity in mind, with features in
|
|||||||
- Database management (add/edit/delete).
|
- Database management (add/edit/delete).
|
||||||
- Full tables management, including indexes and foreign keys.
|
- Full tables management, including indexes and foreign keys.
|
||||||
- Views, triggers, stored routines, functions and schedulers management (add/edit/delete).
|
- Views, triggers, stored routines, functions and schedulers management (add/edit/delete).
|
||||||
|
- Fake table data filler.
|
||||||
- Run queries on multiple tabs.
|
- Run queries on multiple tabs.
|
||||||
- Query suggestions and auto complete.
|
- Query suggestions and auto complete.
|
||||||
- Native dark theme.
|
- Dark and light theme.
|
||||||
|
- Scratchpad.
|
||||||
- Multi language.
|
- Multi language.
|
||||||
- Secure password storage.
|
- Secure password storage.
|
||||||
- Auto updates.
|
- Auto updates.
|
||||||
@@ -45,26 +52,35 @@ An application created with minimalism and semplicity in mind, with features in
|
|||||||
This is a roadmap with major features will come in near future.
|
This is a roadmap with major features will come in near future.
|
||||||
|
|
||||||
- Support for other databases.
|
- Support for other databases.
|
||||||
- Database tools (variables, process list...).
|
- Database tools.
|
||||||
- SSL and SSH tunnel support.
|
- SSH tunnel support.
|
||||||
- Users management (add/edit/delete).
|
- Users management (add/edit/delete).
|
||||||
- UI/UX improvements.
|
- UI/UX improvements.
|
||||||
- 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.
|
|
||||||
- Import/export and migration.
|
- Import/export and migration.
|
||||||
- Light theme.
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### **Linux**
|
||||||
|
|
||||||
|
With KDE may need necessary installation of the additional `gnome-keyring` package.
|
||||||
|
Depending on your distribution, you will need to run the following command:
|
||||||
|
|
||||||
|
- Debian/Ubuntu: `sudo apt-get install gnome-keyring`
|
||||||
|
- Red Hat-based: `sudo yum install gnome-keyring`
|
||||||
|
- Arch Linux: `sudo pacman -S gnome-keyring`
|
||||||
|
|
||||||
## Currently supported
|
## Currently supported
|
||||||
|
|
||||||
### Databases
|
### Databases
|
||||||
|
|
||||||
- [x] MySQL/MariaDB
|
- [x] MySQL/MariaDB
|
||||||
- [ ] PostgreSQL
|
- [x] PostgreSQL (partially, work in progress)
|
||||||
- [ ] MSSQL
|
|
||||||
- [ ] SQLite
|
- [ ] SQLite
|
||||||
|
- [ ] MSSQL
|
||||||
- [ ] OracleDB
|
- [ ] OracleDB
|
||||||
- [ ] More...
|
- [ ] More...
|
||||||
|
|
||||||
@@ -74,7 +90,7 @@ This is a roadmap with major features will come in near future.
|
|||||||
|
|
||||||
- [x] Windows
|
- [x] Windows
|
||||||
- [x] Linux
|
- [x] Linux
|
||||||
- [x] MacOS (i need feedbacks)
|
- [x] MacOS (not tested due lack of hardware)
|
||||||
|
|
||||||
#### • ARM
|
#### • ARM
|
||||||
|
|
||||||
@@ -86,7 +102,8 @@ This is a roadmap with major features will come in near future.
|
|||||||
|
|
||||||
**Italian Translation** (46%) / [Giuseppe Gigliotti](https://github.com/ReverbOD) [[#20](https://github.com/Fabio286/antares/pull/20)]
|
**Italian Translation** (46%) / [Giuseppe Gigliotti](https://github.com/ReverbOD) [[#20](https://github.com/Fabio286/antares/pull/20)]
|
||||||
**Arabic Translation** (45%) / [Mohd-PH](https://github.com/Mohd-PH) [[#29](https://github.com/Fabio286/antares/pull/29)]
|
**Arabic Translation** (45%) / [Mohd-PH](https://github.com/Mohd-PH) [[#29](https://github.com/Fabio286/antares/pull/29)]
|
||||||
**Spanish Translation** (46%) / [hongkfui](https://github.com/hongkfui) [[#32](https://github.com/Fabio286/antares/pull/32)]
|
**Spanish Translation** (46%) / [hongkfui](https://github.com/hongkfui) [[#32](https://github.com/Fabio286/antares/pull/32)]
|
||||||
|
**French Translation** (100%) / [MrAnyx](https://github.com/MrAnyx) [[#44](https://github.com/Fabio286/antares/pull/44)]
|
||||||
|
|
||||||
## Reviews
|
## Reviews
|
||||||
|
|
||||||
|
|||||||
BIN
build/appx/Square150x150Logo.png
Normal file
BIN
build/appx/Square150x150Logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
BIN
build/appx/Square44x44Logo.png
Normal file
BIN
build/appx/Square44x44Logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
BIN
build/appx/StoreLogo.png
Normal file
BIN
build/appx/StoreLogo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
build/appx/Wide310x150Logo.png
Normal file
BIN
build/appx/Wide310x150Logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
docs/gh-logo.png
Normal file
BIN
docs/gh-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
55
package.json
55
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "antares",
|
"name": "antares",
|
||||||
"productName": "Antares",
|
"productName": "Antares",
|
||||||
"version": "0.0.15",
|
"version": "0.1.4",
|
||||||
"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",
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
"dev": "cross-env NODE_ENV=development electron-webpack dev",
|
"dev": "cross-env NODE_ENV=development electron-webpack dev",
|
||||||
"compile": "electron-webpack",
|
"compile": "electron-webpack",
|
||||||
"build": "cross-env NODE_ENV=production npm run compile && electron-builder",
|
"build": "cross-env NODE_ENV=production npm run compile && electron-builder",
|
||||||
|
"build:appx": "npm run build -- --win appx",
|
||||||
"release": "standard-version",
|
"release": "standard-version",
|
||||||
"release:pre": "npm run release -- --prerelease alpha",
|
"release:pre": "npm run release -- --prerelease alpha",
|
||||||
"test": "npm run lint",
|
"test": "npm run lint",
|
||||||
@@ -19,6 +20,12 @@
|
|||||||
"build": {
|
"build": {
|
||||||
"appId": "com.fabio286.antares",
|
"appId": "com.fabio286.antares",
|
||||||
"artifactName": "${productName}-${version}-${os}_${arch}.${ext}",
|
"artifactName": "${productName}-${version}-${os}_${arch}.${ext}",
|
||||||
|
"win": {
|
||||||
|
"target": [
|
||||||
|
"nsis",
|
||||||
|
"portable"
|
||||||
|
]
|
||||||
|
},
|
||||||
"dmg": {
|
"dmg": {
|
||||||
"contents": [
|
"contents": [
|
||||||
{
|
{
|
||||||
@@ -43,6 +50,15 @@
|
|||||||
"appImage": {
|
"appImage": {
|
||||||
"license": "./LICENSE",
|
"license": "./LICENSE",
|
||||||
"category": "Development"
|
"category": "Development"
|
||||||
|
},
|
||||||
|
"portable": {
|
||||||
|
"artifactName": "${productName}-${version}-portable.exe"
|
||||||
|
},
|
||||||
|
"appx": {
|
||||||
|
"displayName": "Antares SQL Client",
|
||||||
|
"identityName": "62514FabioDiStasio.AntaresSQLClient",
|
||||||
|
"publisher": "CN=1A2729ED-865C-41D2-9038-39AE2A63AA52",
|
||||||
|
"applicationId": "FabioDiStasio.AntaresSQLClient"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electronWebpack": {
|
"electronWebpack": {
|
||||||
@@ -51,46 +67,49 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/font": "^5.8.55",
|
"@mdi/font": "^5.9.55",
|
||||||
"ace-builds": "^1.4.12",
|
"ace-builds": "^1.4.12",
|
||||||
"electron-log": "^4.3.0",
|
"electron-log": "^4.3.0",
|
||||||
"electron-store": "^6.0.1",
|
"electron-store": "^7.0.0",
|
||||||
"electron-updater": "^4.3.5",
|
"electron-updater": "^4.3.5",
|
||||||
"keytar": "^7.3.0",
|
"faker": "^5.3.1",
|
||||||
"lodash": "^4.17.20",
|
"marked": "^2.0.2",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"mssql": "^6.2.3",
|
"mssql": "^6.2.3",
|
||||||
"mysql": "^2.18.1",
|
"mysql2": "^2.2.5",
|
||||||
|
"node-sql-parser": "^3.1.0",
|
||||||
"pg": "^8.5.1",
|
"pg": "^8.5.1",
|
||||||
|
"pgsql-ast-parser": "^7.0.2",
|
||||||
"source-map-support": "^0.5.16",
|
"source-map-support": "^0.5.16",
|
||||||
"spectre.css": "^0.5.9",
|
"spectre.css": "^0.5.9",
|
||||||
"vue-i18n": "^8.22.2",
|
"sql-formatter": "^4.0.2",
|
||||||
"vue-the-mask": "^0.11.1",
|
"v-mask": "^2.2.4",
|
||||||
|
"vue-i18n": "^8.22.4",
|
||||||
"vuedraggable": "^2.24.3",
|
"vuedraggable": "^2.24.3",
|
||||||
"vuex": "^3.6.0"
|
"vuex": "^3.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"electron": "^11.0.2",
|
"electron": "^11.4.3",
|
||||||
"electron-builder": "^22.9.1",
|
"electron-builder": "^22.9.1",
|
||||||
"electron-devtools-installer": "^3.1.1",
|
"electron-devtools-installer": "^3.1.1",
|
||||||
"electron-webpack": "^2.8.2",
|
"electron-webpack": "^2.8.2",
|
||||||
"electron-webpack-vue": "^2.4.0",
|
"electron-webpack-vue": "^2.4.0",
|
||||||
"eslint": "^7.14.0",
|
"eslint": "^7.24.0",
|
||||||
"eslint-config-standard": "^16.0.2",
|
"eslint-config-standard": "^16.0.2",
|
||||||
"eslint-plugin-import": "^2.22.1",
|
"eslint-plugin-import": "^2.22.1",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^5.1.0",
|
||||||
"eslint-plugin-vue": "^7.1.0",
|
"eslint-plugin-vue": "^7.9.0",
|
||||||
"node-sass": "^5.0.0",
|
"node-sass": "^5.0.0",
|
||||||
"sass-loader": "^10.1.0",
|
"sass-loader": "^10.1.1",
|
||||||
"standard-version": "^9.0.0",
|
"standard-version": "^9.2.0",
|
||||||
"stylelint": "^13.8.0",
|
"stylelint": "^13.12.0",
|
||||||
"stylelint-config-standard": "^20.0.0",
|
"stylelint-config-standard": "^21.0.0",
|
||||||
"stylelint-scss": "^3.18.0",
|
"stylelint-scss": "^3.19.0",
|
||||||
"vue": "^2.6.12",
|
"vue": "^2.6.12",
|
||||||
"vue-template-compiler": "^2.6.12",
|
"vue-template-compiler": "^2.6.12",
|
||||||
"webpack": "^4.44.2"
|
"webpack": "^4.46.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
96
snap/snapcraft.yaml
Normal file
96
snap/snapcraft.yaml
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
name: antares
|
||||||
|
adopt-info: antares
|
||||||
|
summary: Open source SQL client made to be simple and complete.
|
||||||
|
description: |
|
||||||
|
Antares is an SQL client that aims to become an useful and complete tool, especially for developers.
|
||||||
|
The 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 and supports only MySQL and x86 architecture.
|
||||||
|
Most of its current features might be enough for MySQL management, so give it a chance and send us your feedback, we would really appreciate it.
|
||||||
|
base: core18
|
||||||
|
|
||||||
|
grade: stable
|
||||||
|
confinement: strict
|
||||||
|
|
||||||
|
architectures:
|
||||||
|
- build-on: amd64
|
||||||
|
compression: lzo
|
||||||
|
|
||||||
|
parts:
|
||||||
|
antares:
|
||||||
|
plugin: dump
|
||||||
|
source: .
|
||||||
|
override-build: |
|
||||||
|
snapcraftctl build
|
||||||
|
ARCHITECTURE=$(dpkg --print-architecture)
|
||||||
|
if [ "${ARCHITECTURE}" = "amd64" ]; then
|
||||||
|
FILTER="amd64.deb"
|
||||||
|
else
|
||||||
|
echo "ERROR! Antares only produces debs for amd64. Failing the build here."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Get the latest releases json
|
||||||
|
echo "Get GitHub releases..."
|
||||||
|
wget --quiet https://api.github.com/repos/fabio286/antares/releases/latest -O releases.json
|
||||||
|
# Get the version from the tag_name and the download URL.
|
||||||
|
VERSION=$(jq . releases.json | grep tag_name | cut -d'"' -f4 | sed s'/release-//')
|
||||||
|
DEB_URL=$(cat releases.json | jq -r ".assets[] | select(.name | test(\"${FILTER}\")) | .browser_download_url")
|
||||||
|
DEB=$(basename "${DEB_URL}")
|
||||||
|
echo "Downloading ${DEB_URL}..."
|
||||||
|
wget --quiet "${DEB_URL}" -O "${SNAPCRAFT_PART_INSTALL}/${DEB}"
|
||||||
|
echo "Unpacking ${DEB}..."
|
||||||
|
dpkg -x "${SNAPCRAFT_PART_INSTALL}/${DEB}" ${SNAPCRAFT_PART_INSTALL}
|
||||||
|
rm -f releases.json 2>/dev/null
|
||||||
|
rm -f "${SNAPCRAFT_PART_INSTALL}/${DEB}" 2>/dev/null
|
||||||
|
echo $VERSION > $SNAPCRAFT_STAGE/version
|
||||||
|
# Correct path to icon.
|
||||||
|
sed -i 's|Icon=antares|Icon=/usr/share/icons/hicolor/256x256/apps/antares\.png|g' ${SNAPCRAFT_PART_INSTALL}/usr/share/applications/antares.desktop
|
||||||
|
# Delete usr/bin/antares, it's a broken symlink pointing outside the snap.
|
||||||
|
rm -f ${SNAPCRAFT_PART_INSTALL}/usr/bin/antares
|
||||||
|
chmod -s ${SNAPCRAFT_PART_INSTALL}/opt/Antares/chrome-sandbox
|
||||||
|
snapcraftctl set-version "$(echo $VERSION)"
|
||||||
|
build-packages:
|
||||||
|
- dpkg
|
||||||
|
- jq
|
||||||
|
- sed
|
||||||
|
- wget
|
||||||
|
stage-packages:
|
||||||
|
- fcitx-frontend-gtk3
|
||||||
|
- libappindicator3-1
|
||||||
|
- libasound2
|
||||||
|
- libgconf-2-4
|
||||||
|
- libgtk-3-0
|
||||||
|
- libnotify4
|
||||||
|
- libnspr4
|
||||||
|
- libnss3
|
||||||
|
- libpcre3
|
||||||
|
- libpulse0
|
||||||
|
- libxss1
|
||||||
|
- libsecret-1-0
|
||||||
|
- libxtst6
|
||||||
|
- libxkbfile1
|
||||||
|
cleanup:
|
||||||
|
after: [antares]
|
||||||
|
plugin: nil
|
||||||
|
build-snaps: [ gnome-3-28-1804 ]
|
||||||
|
override-prime: |
|
||||||
|
set -eux
|
||||||
|
cd /snap/gnome-3-28-1804/current
|
||||||
|
find . -type f,l -exec rm -f $SNAPCRAFT_PRIME/{} \;
|
||||||
|
|
||||||
|
apps:
|
||||||
|
antares:
|
||||||
|
command: opt/Antares/antares --no-sandbox
|
||||||
|
desktop: usr/share/applications/antares.desktop
|
||||||
|
extensions: [gnome-3-28]
|
||||||
|
environment:
|
||||||
|
# Fallback to XWayland if running in a Wayland session.
|
||||||
|
DISABLE_WAYLAND: 1
|
||||||
|
plugs:
|
||||||
|
- browser-support
|
||||||
|
- cups-control
|
||||||
|
- home
|
||||||
|
- network
|
||||||
|
- opengl
|
||||||
|
- pulseaudio
|
||||||
|
- removable-media
|
||||||
|
- unity7
|
||||||
217
src/common/FakerMethods.js
Normal file
217
src/common/FakerMethods.js
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
export default class {
|
||||||
|
static get _methods () {
|
||||||
|
return [
|
||||||
|
{ name: 'zipCode', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'zipCodeByState', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'city', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'cityPrefix', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'citySuffix', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'streetName', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'streetAddress', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'streetSuffix', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'streetPrefix', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'secondaryAddress', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'county', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'country', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'countryCode', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'state', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'stateAbbr', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'latitude', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'longitude', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'direction', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'cardinalDirection', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'ordinalDirection', group: 'address', types: ['string'] },
|
||||||
|
// { name: 'nearbyGPSCoordinate', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'timeZone', group: 'address', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'color', group: 'commerce', types: ['string'] },
|
||||||
|
{ name: 'department', group: 'commerce', types: ['string'] },
|
||||||
|
{ name: 'productName', group: 'commerce', types: ['string'] },
|
||||||
|
{ name: 'price', group: 'commerce', types: ['string', 'float'] },
|
||||||
|
{ name: 'productAdjective', group: 'commerce', types: ['string'] },
|
||||||
|
{ name: 'productMaterial', group: 'commerce', types: ['string'] },
|
||||||
|
{ name: 'product', group: 'commerce', types: ['string'] },
|
||||||
|
{ name: 'productDescription', group: 'commerce', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'suffixes', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'companyName', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'companySuffix', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'catchPhrase', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'bs', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'catchPhraseAdjective', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'catchPhraseDescriptor', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'catchPhraseNoun', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'bsAdjective', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'bsBuzz', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'bsNoun', group: 'company', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'column', group: 'database', types: ['string'] },
|
||||||
|
{ name: 'type', group: 'database', types: ['string'] },
|
||||||
|
{ name: 'collation', group: 'database', types: ['string'] },
|
||||||
|
{ name: 'engine', group: 'database', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'past', group: 'date', types: ['string', 'datetime'] },
|
||||||
|
{ name: 'future', group: 'date', types: ['string', 'datetime'] },
|
||||||
|
// { name: 'between', group: 'date', types: ['string'] },
|
||||||
|
{ name: 'recent', group: 'date', types: ['string', 'datetime'] },
|
||||||
|
{ name: 'soon', group: 'date', types: ['string', 'datetime'] },
|
||||||
|
{ name: 'month', group: 'date', types: ['string'] },
|
||||||
|
{ name: 'weekday', group: 'date', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'account', group: 'finance', types: ['string', 'number'] },
|
||||||
|
{ name: 'accountName', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'routingNumber', group: 'finance', types: ['string', 'number'] },
|
||||||
|
{ name: 'mask', group: 'finance', types: ['string', 'number'] },
|
||||||
|
{ name: 'amount', group: 'finance', types: ['string', 'float'] },
|
||||||
|
{ name: 'transactionType', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'currencyCode', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'currencyName', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'currencySymbol', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'bitcoinAddress', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'litecoinAddress', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'creditCardNumber', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'creditCardCVV', group: 'finance', types: ['string', 'number'] },
|
||||||
|
{ name: 'ethereumAddress', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'iban', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'bic', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'transactionDescription', group: 'finance', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'branch', group: 'git', types: ['string'] },
|
||||||
|
{ name: 'commitEntry', group: 'git', types: ['string'] },
|
||||||
|
{ name: 'commitMessage', group: 'git', types: ['string'] },
|
||||||
|
{ name: 'commitSha', group: 'git', types: ['string'] },
|
||||||
|
{ name: 'shortSha', group: 'git', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'abbreviation', group: 'hacker', types: ['string'] },
|
||||||
|
{ name: 'adjective', group: 'hacker', types: ['string'] },
|
||||||
|
{ name: 'noun', group: 'hacker', types: ['string'] },
|
||||||
|
{ name: 'verb', group: 'hacker', types: ['string'] },
|
||||||
|
{ name: 'ingverb', group: 'hacker', types: ['string'] },
|
||||||
|
{ name: 'phrase', group: 'hacker', types: ['string'] },
|
||||||
|
|
||||||
|
// { name: 'avatar', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'email', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'exampleEmail', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'userName', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'protocol', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'url', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'domainName', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'domainSuffix', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'domainWord', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'ip', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'ipv6', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'userAgent', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'color', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'mac', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'password', group: 'internet', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'word', group: 'lorem', types: ['string'] },
|
||||||
|
{ name: 'words', group: 'lorem', types: ['string'] },
|
||||||
|
{ name: 'sentence', group: 'lorem', types: ['string'] },
|
||||||
|
{ name: 'slug', group: 'lorem', types: ['string'] },
|
||||||
|
{ name: 'sentences', group: 'lorem', types: ['string'] },
|
||||||
|
{ name: 'paragraph', group: 'lorem', types: ['string'] },
|
||||||
|
{ name: 'paragraphs', group: 'lorem', types: ['string'] },
|
||||||
|
{ name: 'text', group: 'lorem', types: ['string'] },
|
||||||
|
{ name: 'lines', group: 'lorem', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'genre', group: 'music', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'firstName', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'lastName', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'middleName', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'findName', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'jobTitle', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'gender', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'prefix', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'suffix', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'title', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'jobDescriptor', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'jobArea', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'jobType', group: 'name', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'phoneNumber', group: 'phone', types: ['string'] },
|
||||||
|
{ name: 'phoneNumberFormat', group: 'phone', types: ['string'] },
|
||||||
|
{ name: 'phoneFormats', group: 'phone', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'number', group: 'random', types: ['string', 'number'], params: ['min', 'max'] },
|
||||||
|
{ name: 'float', group: 'random', types: ['string', 'float'], params: ['min', 'max'] },
|
||||||
|
{ name: 'arrayElement', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'arrayElements', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'objectElement', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'uuid', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'boolean', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'word', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'words', group: 'random', types: ['string'] },
|
||||||
|
// { name: 'image', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'locale', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'alpha', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'alphaNumeric', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'hexaDecimal', group: 'random', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'fileName', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'commonFileName', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'mimeType', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'commonFileType', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'commonFileExt', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'fileType', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'fileExt', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'directoryPath', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'filePath', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'semver', group: 'system', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'recent', group: 'time', types: ['string', 'time'] },
|
||||||
|
|
||||||
|
{ name: 'vehicle', group: 'vehicle', types: ['string'] },
|
||||||
|
{ name: 'manufacturer', group: 'vehicle', types: ['string'] },
|
||||||
|
{ name: 'model', group: 'vehicle', types: ['string'] },
|
||||||
|
{ name: 'type', group: 'vehicle', types: ['string'] },
|
||||||
|
{ name: 'fuel', group: 'vehicle', types: ['string'] },
|
||||||
|
{ name: 'vin', group: 'vehicle', types: ['string'] },
|
||||||
|
{ name: 'color', group: 'vehicle', types: ['string'] }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
static getGroups () {
|
||||||
|
const groupsObj = this._methods.reduce((acc, curr) => {
|
||||||
|
if (curr.group in acc)
|
||||||
|
curr.types.forEach(type => acc[curr.group].add(type));
|
||||||
|
else
|
||||||
|
acc[curr.group] = new Set(curr.types);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const groupsArr = [];
|
||||||
|
|
||||||
|
for (const key in groupsObj)
|
||||||
|
groupsArr.push({ name: key, types: [...groupsObj[key]] });
|
||||||
|
|
||||||
|
return groupsArr.sort((a, b) => {
|
||||||
|
if (a.name < b.name)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (b.name > a.name)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}); ;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getGroupsByType (type) {
|
||||||
|
if (!type) return [];
|
||||||
|
return this.getGroups().filter(group => group.types.includes(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
static getMethods ({ type, group }) {
|
||||||
|
return this._methods.filter(method => method.group === group && method.types.includes(type)).sort((a, b) => {
|
||||||
|
if (a.name < b.name)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (b.name > a.name)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
69
src/common/customizations/defaults.js
Normal file
69
src/common/customizations/defaults.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
module.exports = {
|
||||||
|
// Defaults
|
||||||
|
defaultPort: null,
|
||||||
|
defaultUser: null,
|
||||||
|
defaultDatabase: null,
|
||||||
|
// Core
|
||||||
|
database: false,
|
||||||
|
collations: false,
|
||||||
|
engines: false,
|
||||||
|
// Tools
|
||||||
|
processesList: false,
|
||||||
|
usersManagement: false,
|
||||||
|
variables: false,
|
||||||
|
// Structure
|
||||||
|
schemas: false,
|
||||||
|
tables: false,
|
||||||
|
views: false,
|
||||||
|
triggers: false,
|
||||||
|
triggerFunctions: false,
|
||||||
|
routines: false,
|
||||||
|
functions: false,
|
||||||
|
schedulers: false,
|
||||||
|
// Settings
|
||||||
|
tableAdd: false,
|
||||||
|
viewAdd: false,
|
||||||
|
triggerAdd: false,
|
||||||
|
triggerFunctionAdd: false,
|
||||||
|
routineAdd: false,
|
||||||
|
functionAdd: false,
|
||||||
|
schedulerAdd: false,
|
||||||
|
databaseEdit: false,
|
||||||
|
schemaEdit: false,
|
||||||
|
tableSettings: false,
|
||||||
|
viewSettings: false,
|
||||||
|
triggerSettings: false,
|
||||||
|
triggerFunctionSettings: false,
|
||||||
|
routineSettings: false,
|
||||||
|
functionSettings: false,
|
||||||
|
schedulerSettings: false,
|
||||||
|
indexes: false,
|
||||||
|
foreigns: false,
|
||||||
|
sortableFields: false,
|
||||||
|
unsigned: false,
|
||||||
|
nullable: false,
|
||||||
|
zerofill: false,
|
||||||
|
autoIncrement: false,
|
||||||
|
comment: false,
|
||||||
|
collation: false,
|
||||||
|
definer: false,
|
||||||
|
onUpdate: false,
|
||||||
|
tableArray: false,
|
||||||
|
viewAlgorithm: false,
|
||||||
|
viewSqlSecurity: false,
|
||||||
|
viewUpdateOption: false,
|
||||||
|
procedureDeterministic: false,
|
||||||
|
procedureDataAccess: false,
|
||||||
|
procedureSql: false,
|
||||||
|
procedureContext: false,
|
||||||
|
procedureLanguage: false,
|
||||||
|
functionDeterministic: false,
|
||||||
|
functionDataAccess: false,
|
||||||
|
functionSql: false,
|
||||||
|
functionContext: false,
|
||||||
|
functionLanguage: false,
|
||||||
|
triggerMiltipleEvents: false,
|
||||||
|
triggerUpdateColumns: false,
|
||||||
|
parametersLength: false,
|
||||||
|
languages: false
|
||||||
|
};
|
||||||
5
src/common/customizations/index.js
Normal file
5
src/common/customizations/index.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
maria: require('./mysql'),
|
||||||
|
mysql: require('./mysql'),
|
||||||
|
pg: require('./postgresql')
|
||||||
|
};
|
||||||
58
src/common/customizations/mysql.js
Normal file
58
src/common/customizations/mysql.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
const defaults = require('./defaults');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
...defaults,
|
||||||
|
// Defaults
|
||||||
|
defaultPort: 3306,
|
||||||
|
defaultUser: 'root',
|
||||||
|
defaultDatabase: null,
|
||||||
|
// Core
|
||||||
|
collations: true,
|
||||||
|
engines: true,
|
||||||
|
// Tools
|
||||||
|
processesList: true,
|
||||||
|
// Structure
|
||||||
|
schemas: true,
|
||||||
|
tables: true,
|
||||||
|
views: true,
|
||||||
|
triggers: true,
|
||||||
|
routines: true,
|
||||||
|
functions: true,
|
||||||
|
schedulers: true,
|
||||||
|
// Settings
|
||||||
|
tableAdd: true,
|
||||||
|
viewAdd: true,
|
||||||
|
triggerAdd: true,
|
||||||
|
routineAdd: true,
|
||||||
|
functionAdd: true,
|
||||||
|
schedulerAdd: true,
|
||||||
|
schemaEdit: true,
|
||||||
|
tableSettings: true,
|
||||||
|
viewSettings: true,
|
||||||
|
triggerSettings: true,
|
||||||
|
routineSettings: true,
|
||||||
|
functionSettings: true,
|
||||||
|
schedulerSettings: true,
|
||||||
|
indexes: true,
|
||||||
|
foreigns: true,
|
||||||
|
sortableFields: true,
|
||||||
|
unsigned: true,
|
||||||
|
nullable: true,
|
||||||
|
zerofill: true,
|
||||||
|
autoIncrement: true,
|
||||||
|
comment: true,
|
||||||
|
collation: true,
|
||||||
|
definer: true,
|
||||||
|
onUpdate: true,
|
||||||
|
viewAlgorithm: true,
|
||||||
|
viewSqlSecurity: true,
|
||||||
|
viewUpdateOption: true,
|
||||||
|
procedureDeterministic: true,
|
||||||
|
procedureDataAccess: true,
|
||||||
|
procedureSql: 'BEGIN\r\n\r\nEND',
|
||||||
|
procedureContext: true,
|
||||||
|
functionDeterministic: true,
|
||||||
|
functionDataAccess: true,
|
||||||
|
functionSql: 'BEGIN\r\n\r\nEND',
|
||||||
|
parametersLength: true
|
||||||
|
};
|
||||||
42
src/common/customizations/postgresql.js
Normal file
42
src/common/customizations/postgresql.js
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
const defaults = require('./defaults');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
...defaults,
|
||||||
|
// Defaults
|
||||||
|
defaultPort: 5432,
|
||||||
|
defaultUser: 'postgres',
|
||||||
|
defaultDatabase: 'postgres',
|
||||||
|
// Core
|
||||||
|
database: true,
|
||||||
|
// Tools
|
||||||
|
processesList: true,
|
||||||
|
// Structure
|
||||||
|
tables: true,
|
||||||
|
views: true,
|
||||||
|
triggers: false,
|
||||||
|
routines: true,
|
||||||
|
functions: true,
|
||||||
|
// Settings
|
||||||
|
tableAdd: true,
|
||||||
|
viewAdd: true,
|
||||||
|
triggerAdd: false,
|
||||||
|
routineAdd: true,
|
||||||
|
functionAdd: true,
|
||||||
|
databaseEdit: false,
|
||||||
|
tableSettings: true,
|
||||||
|
viewSettings: true,
|
||||||
|
triggerSettings: false,
|
||||||
|
routineSettings: true,
|
||||||
|
functionSettings: true,
|
||||||
|
indexes: true,
|
||||||
|
foreigns: true,
|
||||||
|
nullable: true,
|
||||||
|
tableArray: true,
|
||||||
|
procedureSql: '$BODY$\r\n\r\n$BODY$',
|
||||||
|
procedureContext: true,
|
||||||
|
procedureLanguage: true,
|
||||||
|
functionSql: '$BODY$\r\n\r\n$BODY$',
|
||||||
|
functionContext: true,
|
||||||
|
functionLanguage: true,
|
||||||
|
languages: ['sql', 'plpgsql', 'c', 'internal']
|
||||||
|
};
|
||||||
@@ -4,7 +4,7 @@ module.exports = [
|
|||||||
types: [
|
types: [
|
||||||
{
|
{
|
||||||
name: 'TINYINT',
|
name: 'TINYINT',
|
||||||
length: true,
|
length: 4,
|
||||||
collation: false,
|
collation: false,
|
||||||
unsigned: true,
|
unsigned: true,
|
||||||
zerofill: true
|
zerofill: true
|
||||||
|
|||||||
292
src/common/data-types/postgresql.js
Normal file
292
src/common/data-types/postgresql.js
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
group: 'integer',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'SMALLINT',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'INTEGER',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BIGINT',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DECIMAL',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NUMERIC',
|
||||||
|
length: true,
|
||||||
|
unsigned: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'SMALLSERIAL',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'SERIAL',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BIGSERIAL',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'float',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'REAL',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DOUBLE PRECISION',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'monetary',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'money',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'string',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'CHARACTER VARYING',
|
||||||
|
length: true,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'CHARACTER',
|
||||||
|
length: true,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TEXT',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '"CHAR"',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NAME',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'binary',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'BYTEA',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'time',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'TIMESTAMP WITHOUT TIME ZONE',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TIMESTAMP WITH TIME ZONE',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DATE',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TIME WITHOUT TIME ZONE',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TIME WITH TIME ZONE',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'INTERVAL',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'boolean',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'BOOLEAN',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'geometric',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'POINT',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LINE',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LSEG',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BOX',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'PATH',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'POLYGON',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'CIRCLE',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'network',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'CIDR',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'INET',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MACADDR',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MACADDR8',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'bit',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'BIT',
|
||||||
|
length: true,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BIT VARYING',
|
||||||
|
length: true,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'text search',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'TSVECTOR',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TSQUERY',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'uuid',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'UUID',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'xml',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'XML',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'json',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'JSON',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'JSONB',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'JSONPATH',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -1,12 +1,82 @@
|
|||||||
export const TEXT = ['CHAR', 'VARCHAR'];
|
export const TEXT = [
|
||||||
export const LONG_TEXT = ['TEXT', 'MEDIUMTEXT', 'LONGTEXT'];
|
'CHAR',
|
||||||
|
'VARCHAR',
|
||||||
|
'CHARACTER',
|
||||||
|
'CHARACTER VARYING'
|
||||||
|
];
|
||||||
|
export const LONG_TEXT = [
|
||||||
|
'TEXT',
|
||||||
|
'MEDIUMTEXT',
|
||||||
|
'LONGTEXT'
|
||||||
|
];
|
||||||
|
|
||||||
export const NUMBER = ['INT', 'TINYINT', 'SMALLINT', 'MEDIUMINT', 'BIGINT', 'FLOAT', 'DOUBLE', 'DECIMAL', 'BOOL'];
|
export const ARRAY = [
|
||||||
|
'ARRAY',
|
||||||
|
'ANYARRAY'
|
||||||
|
];
|
||||||
|
|
||||||
|
export const TEXT_SEARCH = [
|
||||||
|
'TSVECTOR',
|
||||||
|
'TSQUERY'
|
||||||
|
];
|
||||||
|
|
||||||
|
export const NUMBER = [
|
||||||
|
'INT',
|
||||||
|
'TINYINT',
|
||||||
|
'SMALLINT',
|
||||||
|
'MEDIUMINT',
|
||||||
|
'BIGINT',
|
||||||
|
'DECIMAL',
|
||||||
|
'NUMERIC',
|
||||||
|
'INTEGER',
|
||||||
|
'SMALLSERIAL',
|
||||||
|
'SERIAL',
|
||||||
|
'BIGSERIAL',
|
||||||
|
'OID',
|
||||||
|
'XID'
|
||||||
|
];
|
||||||
|
|
||||||
|
export const FLOAT = [
|
||||||
|
'FLOAT',
|
||||||
|
'DOUBLE',
|
||||||
|
'REAL',
|
||||||
|
'DOUBLE PRECISION',
|
||||||
|
'MONEY'
|
||||||
|
];
|
||||||
|
|
||||||
|
export const BOOLEAN = [
|
||||||
|
'BOOL',
|
||||||
|
'BOOLEAN'
|
||||||
|
];
|
||||||
|
|
||||||
export const DATE = ['DATE'];
|
export const DATE = ['DATE'];
|
||||||
export const TIME = ['TIME'];
|
export const TIME = [
|
||||||
export const DATETIME = ['DATETIME', 'TIMESTAMP'];
|
'TIME',
|
||||||
|
'TIME WITH TIME ZONE'
|
||||||
|
];
|
||||||
|
|
||||||
export const BLOB = ['BLOB', 'TINYBLOB', 'MEDIUMBLOB', 'LONGBLOB'];
|
export const DATETIME = [
|
||||||
|
'DATETIME',
|
||||||
|
'TIMESTAMP',
|
||||||
|
'TIMESTAMP WITHOUT TIME ZONE',
|
||||||
|
'TIMESTAMP WITH TIME ZONE'
|
||||||
|
];
|
||||||
|
|
||||||
export const BIT = ['BIT'];
|
// Used to check datetime fields only
|
||||||
|
export const HAS_TIMEZONE = [
|
||||||
|
'TIMESTAMP WITH TIME ZONE',
|
||||||
|
'TIME WITH TIME ZONE'
|
||||||
|
];
|
||||||
|
|
||||||
|
export const BLOB = [
|
||||||
|
'BLOB',
|
||||||
|
'TINYBLOB',
|
||||||
|
'MEDIUMBLOB',
|
||||||
|
'LONGBLOB',
|
||||||
|
'BYTEA'
|
||||||
|
];
|
||||||
|
|
||||||
|
export const BIT = [
|
||||||
|
'BIT',
|
||||||
|
'BIT VARYING'
|
||||||
|
];
|
||||||
|
|||||||
5
src/common/index-types/postgresql.js
Normal file
5
src/common/index-types/postgresql.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module.exports = [
|
||||||
|
'PRIMARY',
|
||||||
|
'INDEX',
|
||||||
|
'UNIQUE'
|
||||||
|
];
|
||||||
@@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
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 Store from 'electron-store';
|
||||||
|
|
||||||
import ipcHandlers from './ipc-handlers';
|
import ipcHandlers from './ipc-handlers';
|
||||||
|
|
||||||
|
Store.initRenderer();
|
||||||
|
|
||||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||||
const gotTheLock = app.requestSingleInstanceLock();
|
const gotTheLock = app.requestSingleInstanceLock();
|
||||||
|
|
||||||
@@ -92,13 +94,6 @@ else {
|
|||||||
|
|
||||||
// create main BrowserWindow when electron is ready
|
// create main BrowserWindow when electron is ready
|
||||||
app.on('ready', async () => {
|
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,4 +1,3 @@
|
|||||||
import keytar from 'keytar';
|
|
||||||
import { app, ipcMain } from 'electron';
|
import { app, ipcMain } from 'electron';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
@@ -7,7 +6,7 @@ export default () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('get-key', async event => {
|
ipcMain.on('get-key', async event => {
|
||||||
const key = await keytar.getPassword('antares', 'user');
|
const key = false;
|
||||||
event.returnValue = key;
|
event.returnValue = key;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,17 +1,32 @@
|
|||||||
|
import fs from 'fs';
|
||||||
import { ipcMain } from 'electron';
|
import { ipcMain } from 'electron';
|
||||||
import { ClientsFactory } from '../libs/ClientsFactory';
|
import { ClientsFactory } from '../libs/ClientsFactory';
|
||||||
|
|
||||||
export default connections => {
|
export default connections => {
|
||||||
ipcMain.handle('test-connection', async (event, conn) => {
|
ipcMain.handle('test-connection', async (event, conn) => {
|
||||||
|
const params = {
|
||||||
|
host: conn.host,
|
||||||
|
port: +conn.port,
|
||||||
|
user: conn.user,
|
||||||
|
password: conn.password,
|
||||||
|
application_name: 'Antares SQL'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (conn.database)
|
||||||
|
params.database = conn.database;
|
||||||
|
|
||||||
|
if (conn.ssl) {
|
||||||
|
params.ssl = {
|
||||||
|
key: conn.key ? fs.readFileSync(conn.key) : null,
|
||||||
|
cert: conn.cert ? fs.readFileSync(conn.cert) : null,
|
||||||
|
ca: conn.ca ? fs.readFileSync(conn.ca) : null,
|
||||||
|
ciphers: conn.ciphers
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const connection = ClientsFactory.getConnection({
|
const connection = ClientsFactory.getConnection({
|
||||||
client: conn.client,
|
client: conn.client,
|
||||||
params: {
|
params
|
||||||
host: conn.host,
|
|
||||||
port: +conn.port,
|
|
||||||
user: conn.user,
|
|
||||||
password: conn.password
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await connection.connect();
|
await connection.connect();
|
||||||
@@ -32,18 +47,33 @@ export default connections => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('connect', async (event, conn) => {
|
ipcMain.handle('connect', async (event, conn) => {
|
||||||
const connection = ClientsFactory.getConnection({
|
const params = {
|
||||||
client: conn.client,
|
host: conn.host,
|
||||||
params: {
|
port: +conn.port,
|
||||||
host: conn.host,
|
user: conn.user,
|
||||||
port: +conn.port,
|
password: conn.password,
|
||||||
user: conn.user,
|
application_name: 'Antares SQL'
|
||||||
password: conn.password
|
};
|
||||||
},
|
|
||||||
poolSize: 1
|
if (conn.database)
|
||||||
});
|
params.database = conn.database;
|
||||||
|
|
||||||
|
if (conn.ssl) {
|
||||||
|
params.ssl = {
|
||||||
|
key: conn.key ? fs.readFileSync(conn.key) : null,
|
||||||
|
cert: conn.cert ? fs.readFileSync(conn.cert) : null,
|
||||||
|
ca: conn.ca ? fs.readFileSync(conn.ca) : null,
|
||||||
|
ciphers: conn.ciphers
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const connection = ClientsFactory.getConnection({
|
||||||
|
client: conn.client,
|
||||||
|
params,
|
||||||
|
poolSize: 1
|
||||||
|
});
|
||||||
|
|
||||||
await connection.connect();
|
await connection.connect();
|
||||||
|
|
||||||
const structure = await connection.getStructure(new Set());
|
const structure = await connection.getStructure(new Set());
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import functions from './functions';
|
|||||||
import schedulers from './schedulers';
|
import schedulers from './schedulers';
|
||||||
import updates from './updates';
|
import updates from './updates';
|
||||||
import application from './application';
|
import application from './application';
|
||||||
import database from './database';
|
import schema from './schema';
|
||||||
import users from './users';
|
import users from './users';
|
||||||
|
|
||||||
const connections = {};
|
const connections = {};
|
||||||
@@ -20,7 +20,7 @@ export default () => {
|
|||||||
routines(connections);
|
routines(connections);
|
||||||
functions(connections);
|
functions(connections);
|
||||||
schedulers(connections);
|
schedulers(connections);
|
||||||
database(connections);
|
schema(connections);
|
||||||
users(connections);
|
users(connections);
|
||||||
updates();
|
updates();
|
||||||
application();
|
application();
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
import { ipcMain } from 'electron';
|
import { ipcMain } from 'electron';
|
||||||
|
|
||||||
export default connections => {
|
export default connections => {
|
||||||
ipcMain.handle('create-database', async (event, params) => {
|
ipcMain.handle('create-schema', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
const query = `CREATE DATABASE \`${params.name}\` COLLATE ${params.collation}`;
|
await connections[params.uid].createSchema(params);
|
||||||
await connections[params.uid].raw(query);
|
|
||||||
|
|
||||||
return { status: 'success' };
|
return { status: 'success' };
|
||||||
}
|
}
|
||||||
@@ -14,10 +13,9 @@ export default connections => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('update-database', async (event, params) => {
|
ipcMain.handle('update-schema', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
const query = `ALTER DATABASE \`${params.name}\` COLLATE ${params.collation}`;
|
await connections[params.uid].alterSchema(params);
|
||||||
await connections[params.uid].raw(query);
|
|
||||||
|
|
||||||
return { status: 'success' };
|
return { status: 'success' };
|
||||||
}
|
}
|
||||||
@@ -26,10 +24,9 @@ export default connections => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('delete-database', async (event, params) => {
|
ipcMain.handle('delete-schema', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
const query = `DROP DATABASE \`${params.database}\``;
|
await connections[params.uid].dropSchema(params);
|
||||||
await connections[params.uid].raw(query);
|
|
||||||
|
|
||||||
return { status: 'success' };
|
return { status: 'success' };
|
||||||
}
|
}
|
||||||
@@ -38,10 +35,9 @@ export default connections => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('get-database-collation', async (event, params) => { // TODO: move to mysql class
|
ipcMain.handle('get-schema-collation', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
const query = `SELECT \`DEFAULT_COLLATION_NAME\` FROM \`information_schema\`.\`SCHEMATA\` WHERE \`SCHEMA_NAME\`='${params.database}'`;
|
const collation = await connections[params.uid].getDatabaseCollation(params);
|
||||||
const collation = await connections[params.uid].raw(query);
|
|
||||||
|
|
||||||
return { status: 'success', response: collation.rows.length ? collation.rows[0].DEFAULT_COLLATION_NAME : '' };
|
return { status: 'success', response: collation.rows.length ? collation.rows[0].DEFAULT_COLLATION_NAME : '' };
|
||||||
}
|
}
|
||||||
@@ -94,6 +90,28 @@ export default connections => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('get-version', async (event, uid) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[uid].getVersion();
|
||||||
|
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('get-processes', async (event, uid) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[uid].getProcesses();
|
||||||
|
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.handle('use-schema', async (event, { uid, schema }) => {
|
ipcMain.handle('use-schema', async (event, { uid, schema }) => {
|
||||||
if (!schema) return;
|
if (!schema) return;
|
||||||
|
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import { ipcMain } from 'electron';
|
import { ipcMain } from 'electron';
|
||||||
|
import faker from 'faker';
|
||||||
|
import moment from 'moment';
|
||||||
import { sqlEscaper } from 'common/libs/sqlEscaper';
|
import { sqlEscaper } from 'common/libs/sqlEscaper';
|
||||||
import { TEXT, LONG_TEXT, NUMBER, BLOB } from 'common/fieldTypes';
|
import { TEXT, LONG_TEXT, ARRAY, TEXT_SEARCH, NUMBER, FLOAT, BLOB, BIT, DATE, DATETIME } from 'common/fieldTypes';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
export default (connections) => {
|
export default (connections) => {
|
||||||
@@ -57,33 +59,94 @@ export default (connections) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('update-table-cell', async (event, params) => {
|
ipcMain.handle('update-table-cell', async (event, params) => {
|
||||||
try {
|
try { // TODO: move to client classes
|
||||||
let escapedParam;
|
let escapedParam;
|
||||||
let reload = false;
|
let reload = false;
|
||||||
const id = typeof params.id === 'number' ? params.id : `"${params.id}"`;
|
const id = typeof params.id === 'number' ? params.id : `"${params.id}"`;
|
||||||
|
|
||||||
if (NUMBER.includes(params.type))
|
if ([...NUMBER, ...FLOAT].includes(params.type))
|
||||||
escapedParam = params.content;
|
escapedParam = params.content;
|
||||||
else if ([...TEXT, ...LONG_TEXT].includes(params.type))
|
else if ([...TEXT, ...LONG_TEXT].includes(params.type)) {
|
||||||
escapedParam = `"${sqlEscaper(params.content)}"`;
|
switch (connections[params.uid]._client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
escapedParam = `"${sqlEscaper(params.content)}"`;
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
escapedParam = `'${params.content.replaceAll('\'', '\'\'')}'`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ARRAY.includes(params.type))
|
||||||
|
escapedParam = `'${params.content}'`;
|
||||||
|
else if (TEXT_SEARCH.includes(params.type))
|
||||||
|
escapedParam = `'${params.content.replaceAll('\'', '\'\'')}'`;
|
||||||
else if (BLOB.includes(params.type)) {
|
else if (BLOB.includes(params.type)) {
|
||||||
if (params.content) {
|
if (params.content) {
|
||||||
const fileBlob = fs.readFileSync(params.content);
|
let fileBlob;
|
||||||
escapedParam = `0x${fileBlob.toString('hex')}`;
|
|
||||||
|
switch (connections[params.uid]._client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
fileBlob = fs.readFileSync(params.content);
|
||||||
|
escapedParam = `0x${fileBlob.toString('hex')}`;
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
fileBlob = fs.readFileSync(params.content);
|
||||||
|
escapedParam = `decode('${fileBlob.toString('hex')}', 'hex')`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
reload = true;
|
reload = true;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
escapedParam = '""';
|
switch (connections[params.uid]._client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
escapedParam = '\'\'';
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
escapedParam = 'decode(\'\', \'hex\')';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else if ([...BIT].includes(params.type)) {
|
||||||
|
escapedParam = `b'${sqlEscaper(params.content)}'`;
|
||||||
|
reload = true;
|
||||||
|
}
|
||||||
|
else if (params.content === null)
|
||||||
|
escapedParam = 'NULL';
|
||||||
else
|
else
|
||||||
escapedParam = `"${sqlEscaper(params.content)}"`;
|
escapedParam = `'${sqlEscaper(params.content)}'`;
|
||||||
|
|
||||||
await connections[params.uid]
|
if (params.primary) { // TODO: handle multiple primary
|
||||||
.update({ [params.field]: `= ${escapedParam}` })
|
await connections[params.uid]
|
||||||
.schema(params.schema)
|
.update({ [params.field]: `= ${escapedParam}` })
|
||||||
.from(params.table)
|
.schema(params.schema)
|
||||||
.where({ [params.primary]: `= ${id}` })
|
.from(params.table)
|
||||||
.run();
|
.where({ [params.primary]: `= ${id}` })
|
||||||
|
.limit(1)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const { orgRow } = params;
|
||||||
|
reload = true;
|
||||||
|
|
||||||
|
for (const key in orgRow) {
|
||||||
|
if (typeof orgRow[key] === 'string')
|
||||||
|
orgRow[key] = `'${orgRow[key]}'`;
|
||||||
|
|
||||||
|
orgRow[key] = `= ${orgRow[key]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await connections[params.uid]
|
||||||
|
.schema(params.schema)
|
||||||
|
.update({ [params.field]: `= ${escapedParam}` })
|
||||||
|
.from(params.table)
|
||||||
|
.where(orgRow)
|
||||||
|
.limit(1)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
return { status: 'success', response: { reload } };
|
return { status: 'success', response: { reload } };
|
||||||
}
|
}
|
||||||
@@ -93,29 +156,56 @@ export default (connections) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('delete-table-rows', async (event, params) => {
|
ipcMain.handle('delete-table-rows', async (event, params) => {
|
||||||
let idString;
|
if (params.primary) {
|
||||||
|
const idString = params.rows.map(row => {
|
||||||
|
const fieldName = Object.keys(row)[0].includes('.') ? `${params.table}.${params.primary}` : params.primary;
|
||||||
|
|
||||||
if (typeof params.rows[0] === 'string')
|
return typeof row[fieldName] === 'string'
|
||||||
idString = params.rows.map(row => `"${row}"`).join(',');
|
? `"${row[fieldName]}"`
|
||||||
else
|
: row[fieldName];
|
||||||
idString = params.rows.join(',');
|
}).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 (${idString})` })
|
.where({ [params.primary]: `IN (${idString})` })
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
return { status: 'success', response: result };
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (err) {
|
else {
|
||||||
return { status: 'error', response: err.toString() };
|
try {
|
||||||
|
for (const row of params.rows) {
|
||||||
|
for (const key in row) {
|
||||||
|
if (typeof row[key] === 'string')
|
||||||
|
row[key] = `'${row[key]}'`;
|
||||||
|
|
||||||
|
row[key] = `= ${row[key]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await connections[params.uid]
|
||||||
|
.schema(params.schema)
|
||||||
|
.delete(params.table)
|
||||||
|
.where(row)
|
||||||
|
.limit(1)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
return { status: 'success', response: [] };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('insert-table-rows', async (event, params) => {
|
ipcMain.handle('insert-table-rows', async (event, params) => {
|
||||||
try {
|
try { // TODO: move to client classes
|
||||||
const insertObj = {};
|
const insertObj = {};
|
||||||
for (const key in params.row) {
|
for (const key in params.row) {
|
||||||
const type = params.fields[key];
|
const type = params.fields[key];
|
||||||
@@ -123,32 +213,166 @@ export default (connections) => {
|
|||||||
|
|
||||||
if (params.row[key] === null)
|
if (params.row[key] === null)
|
||||||
escapedParam = 'NULL';
|
escapedParam = 'NULL';
|
||||||
else if (NUMBER.includes(type))
|
else if ([...NUMBER, ...FLOAT].includes(type))
|
||||||
escapedParam = params.row[key];
|
escapedParam = +params.row[key];
|
||||||
else if ([...TEXT, ...LONG_TEXT].includes(type))
|
else if ([...TEXT, ...LONG_TEXT].includes(type)) {
|
||||||
escapedParam = `"${sqlEscaper(params.row[key])}"`;
|
switch (connections[params.uid]._client) {
|
||||||
else if (BLOB.includes(type)) {
|
case 'mysql':
|
||||||
if (params.row[key]) {
|
case 'maria':
|
||||||
const fileBlob = fs.readFileSync(params.row[key]);
|
escapedParam = `"${sqlEscaper(params.row[key].value)}"`;
|
||||||
escapedParam = `0x${fileBlob.toString('hex')}`;
|
break;
|
||||||
|
case 'pg':
|
||||||
|
escapedParam = `'${params.row[key].value.replaceAll('\'', '\'\'')}'`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (BLOB.includes(type)) {
|
||||||
|
if (params.row[key].value) {
|
||||||
|
let fileBlob;
|
||||||
|
|
||||||
|
switch (connections[params.uid]._client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
fileBlob = fs.readFileSync(params.row[key].value);
|
||||||
|
escapedParam = `0x${fileBlob.toString('hex')}`;
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
fileBlob = fs.readFileSync(params.row[key].value);
|
||||||
|
escapedParam = `decode('${fileBlob.toString('hex')}', 'hex')`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch (connections[params.uid]._client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
escapedParam = '""';
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
escapedParam = 'decode(\'\', \'hex\')';
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
escapedParam = '""';
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
escapedParam = `"${sqlEscaper(params.row[key])}"`;
|
|
||||||
|
|
||||||
insertObj[key] = escapedParam;
|
insertObj[key] = escapedParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < params.repeat; i++) {
|
const rows = new Array(+params.repeat).fill(insertObj);
|
||||||
await connections[params.uid]
|
|
||||||
.schema(params.schema)
|
await connections[params.uid]
|
||||||
.into(params.table)
|
.schema(params.schema)
|
||||||
.insert(insertObj)
|
.into(params.table)
|
||||||
.run();
|
.insert(rows)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('insert-table-fake-rows', async (event, params) => {
|
||||||
|
try { // TODO: move to client classes
|
||||||
|
const rows = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < +params.repeat; i++) {
|
||||||
|
const insertObj = {};
|
||||||
|
|
||||||
|
for (const key in params.row) {
|
||||||
|
const type = params.fields[key];
|
||||||
|
let escapedParam;
|
||||||
|
|
||||||
|
if (!('group' in params.row[key]) || params.row[key].group === 'manual') { // Manual value
|
||||||
|
if (params.row[key].value === null || params.row[key].value === undefined)
|
||||||
|
escapedParam = 'NULL';
|
||||||
|
else if ([...NUMBER, ...FLOAT].includes(type))
|
||||||
|
escapedParam = params.row[key].value;
|
||||||
|
else if ([...TEXT, ...LONG_TEXT].includes(type)) {
|
||||||
|
switch (connections[params.uid]._client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
escapedParam = `"${sqlEscaper(params.row[key].value)}"`;
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
escapedParam = `'${params.row[key].value.replaceAll('\'', '\'\'')}'`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (BLOB.includes(type)) {
|
||||||
|
if (params.row[key].value) {
|
||||||
|
let fileBlob;
|
||||||
|
|
||||||
|
switch (connections[params.uid]._client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
fileBlob = fs.readFileSync(params.row[key].value);
|
||||||
|
escapedParam = `0x${fileBlob.toString('hex')}`;
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
fileBlob = fs.readFileSync(params.row[key].value);
|
||||||
|
escapedParam = `decode('${fileBlob.toString('hex')}', 'hex')`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch (connections[params.uid]._client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
escapedParam = '""';
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
escapedParam = 'decode(\'\', \'hex\')';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (BIT.includes(type))
|
||||||
|
escapedParam = `b'${sqlEscaper(params.row[key].value)}'`;
|
||||||
|
else
|
||||||
|
escapedParam = `'${sqlEscaper(params.row[key].value)}'`;
|
||||||
|
|
||||||
|
insertObj[key] = escapedParam;
|
||||||
|
}
|
||||||
|
else { // Faker value
|
||||||
|
const parsedParams = {};
|
||||||
|
let fakeValue;
|
||||||
|
|
||||||
|
if (params.locale)
|
||||||
|
faker.locale = params.locale;
|
||||||
|
|
||||||
|
if (Object.keys(params.row[key].params).length) {
|
||||||
|
Object.keys(params.row[key].params).forEach(param => {
|
||||||
|
if (!isNaN(params.row[key].params[param]))
|
||||||
|
parsedParams[param] = +params.row[key].params[param];
|
||||||
|
});
|
||||||
|
fakeValue = faker[params.row[key].group][params.row[key].method](parsedParams);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fakeValue = faker[params.row[key].group][params.row[key].method]();
|
||||||
|
|
||||||
|
if (typeof fakeValue === 'string') {
|
||||||
|
if (params.row[key].length)
|
||||||
|
fakeValue = fakeValue.substr(0, params.row[key].length);
|
||||||
|
fakeValue = `'${sqlEscaper(fakeValue)}'`;
|
||||||
|
}
|
||||||
|
else if ([...DATE, ...DATETIME].includes(type))
|
||||||
|
fakeValue = `'${moment(fakeValue).format('YYYY-MM-DD HH:mm:ss.SSSSSS')}'`;
|
||||||
|
|
||||||
|
insertObj[key] = fakeValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.push(insertObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await connections[params.uid]
|
||||||
|
.schema(params.schema)
|
||||||
|
.into(params.table)
|
||||||
|
.insert(rows)
|
||||||
|
.run();
|
||||||
|
|
||||||
return { status: 'success' };
|
return { status: 'success' };
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
@@ -159,13 +383,13 @@ export default (connections) => {
|
|||||||
ipcMain.handle('get-foreign-list', async (event, { uid, schema, table, column, description }) => {
|
ipcMain.handle('get-foreign-list', async (event, { uid, schema, table, column, description }) => {
|
||||||
try {
|
try {
|
||||||
const query = connections[uid]
|
const query = connections[uid]
|
||||||
.select(`${column} AS foreignColumn`)
|
.select(`${column} AS foreign_column`)
|
||||||
.schema(schema)
|
.schema(schema)
|
||||||
.from(table)
|
.from(table)
|
||||||
.orderBy('foreignColumn ASC');
|
.orderBy('foreign_column ASC');
|
||||||
|
|
||||||
if (description)
|
if (description)
|
||||||
query.select(`LEFT(${description}, 20) AS foreignDescription`);
|
query.select(`LEFT(${description}, 20) AS foreign_description`);
|
||||||
|
|
||||||
const results = await query.run();
|
const results = await query.run();
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
import { ipcMain } from 'electron';
|
import { ipcMain } from 'electron';
|
||||||
import { autoUpdater } from 'electron-updater';
|
import { autoUpdater } from 'electron-updater';
|
||||||
|
import Store from 'electron-store';
|
||||||
|
const persistentStore = new Store({ name: 'settings' });
|
||||||
|
|
||||||
let mainWindow;
|
let mainWindow;
|
||||||
autoUpdater.allowPrerelease = true;
|
autoUpdater.allowPrerelease = persistentStore.get('allow_prerelease', true);
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
ipcMain.on('check-for-updates', event => {
|
ipcMain.on('check-for-updates', event => {
|
||||||
mainWindow = event;
|
mainWindow = event;
|
||||||
|
if (process.windowsStore)
|
||||||
autoUpdater.checkForUpdatesAndNotify().catch(() => {
|
mainWindow.reply('no-auto-update');
|
||||||
mainWindow.reply('check-failed');
|
else {
|
||||||
});
|
autoUpdater.checkForUpdatesAndNotify().catch(() => {
|
||||||
|
mainWindow.reply('check-failed');
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('restart-to-update', () => {
|
ipcMain.on('restart-to-update', () => {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export class AntaresCore {
|
|||||||
limit: [],
|
limit: [],
|
||||||
join: [],
|
join: [],
|
||||||
update: [],
|
update: [],
|
||||||
insert: {},
|
insert: [],
|
||||||
delete: false
|
delete: false
|
||||||
};
|
};
|
||||||
this._query = Object.assign({}, this._queryDefaults);
|
this._query = Object.assign({}, this._queryDefaults);
|
||||||
@@ -120,12 +120,12 @@ export class AntaresCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} obj field: value
|
* @param {Array} arr Array of row objects
|
||||||
* @returns
|
* @returns
|
||||||
* @memberof AntaresCore
|
* @memberof AntaresCore
|
||||||
*/
|
*/
|
||||||
insert (obj) {
|
insert (arr) {
|
||||||
this._query.insert = { ...this._query.insert, ...obj };
|
this._query.insert = [...this._query.insert, ...arr];
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { MySQLClient } from './clients/MySQLClient';
|
import { MySQLClient } from './clients/MySQLClient';
|
||||||
|
import { PostgreSQLClient } from './clients/PostgreSQLClient';
|
||||||
|
|
||||||
export class ClientsFactory {
|
export class ClientsFactory {
|
||||||
/**
|
/**
|
||||||
@@ -20,8 +21,10 @@ export class ClientsFactory {
|
|||||||
case 'mysql':
|
case 'mysql':
|
||||||
case 'maria':
|
case 'maria':
|
||||||
return new MySQLClient(args);
|
return new MySQLClient(args);
|
||||||
|
case 'pg':
|
||||||
|
return new PostgreSQLClient(args);
|
||||||
default:
|
default:
|
||||||
return new Error(`Unknown database client: ${args.client}`);
|
throw new Error(`Unknown database client: ${args.client}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import mysql from 'mysql';
|
import mysql from 'mysql2';
|
||||||
import { AntaresCore } from '../AntaresCore';
|
import { AntaresCore } from '../AntaresCore';
|
||||||
import dataTypes from 'common/data-types/mysql';
|
import dataTypes from 'common/data-types/mysql';
|
||||||
|
|
||||||
@@ -7,6 +7,8 @@ export class MySQLClient extends AntaresCore {
|
|||||||
constructor (args) {
|
constructor (args) {
|
||||||
super(args);
|
super(args);
|
||||||
|
|
||||||
|
this._schema = null;
|
||||||
|
|
||||||
this.types = {
|
this.types = {
|
||||||
0: 'DECIMAL',
|
0: 'DECIMAL',
|
||||||
1: 'TINYINT',
|
1: 'TINYINT',
|
||||||
@@ -43,17 +45,21 @@ export class MySQLClient extends AntaresCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_getType (field) {
|
_getType (field) {
|
||||||
let name = this.types[field.type];
|
let name = this.types[field.columnType];
|
||||||
let length = field.length;
|
let length = field.columnLength;
|
||||||
|
|
||||||
if (['DATE', 'TIME', 'YEAR', 'DATETIME'].includes(name))
|
if (['DATE', 'TIME', 'YEAR', 'DATETIME'].includes(name))
|
||||||
length = field.decimals;
|
length = field.decimals;
|
||||||
|
|
||||||
if (name === 'CHAR' && field.charsetNr === 63)// if binary
|
if (name === 'TIMESTAMP')
|
||||||
name = 'BINARY';
|
length = 0;
|
||||||
|
|
||||||
if (name === 'VARCHAR' && field.charsetNr === 63)// if binary
|
if (field.charsetNr === 63) { // if binary
|
||||||
name = 'VARBINARY';
|
if (name === 'CHAR')
|
||||||
|
name = 'BINARY';
|
||||||
|
else if (name === 'VARCHAR')
|
||||||
|
name = 'VARBINARY';
|
||||||
|
}
|
||||||
|
|
||||||
if (name === 'BLOB') {
|
if (name === 'BLOB') {
|
||||||
switch (length) {
|
switch (length) {
|
||||||
@@ -98,8 +104,18 @@ export class MySQLClient extends AntaresCore {
|
|||||||
async connect () {
|
async connect () {
|
||||||
if (!this._poolSize)
|
if (!this._poolSize)
|
||||||
this._connection = mysql.createConnection(this._params);
|
this._connection = mysql.createConnection(this._params);
|
||||||
else
|
else {
|
||||||
this._connection = mysql.createPool({ ...this._params, connectionLimit: this._poolSize });
|
this._connection = mysql.createPool({
|
||||||
|
...this._params,
|
||||||
|
connectionLimit: this._poolSize,
|
||||||
|
typeCast: (field, next) => {
|
||||||
|
if (field.type === 'DATETIME')
|
||||||
|
return field.string();
|
||||||
|
else
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -116,6 +132,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
use (schema) {
|
use (schema) {
|
||||||
|
this._schema = schema;
|
||||||
return this.raw(`USE \`${schema}\``);
|
return this.raw(`USE \`${schema}\``);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,6 +417,44 @@ export class MySQLClient extends AntaresCore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CREATE DATABASE
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async createSchema (params) {
|
||||||
|
return await this.raw(`CREATE DATABASE \`${params.name}\` COLLATE ${params.collation}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ALTER DATABASE
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async alterSchema (params) {
|
||||||
|
return await this.raw(`ALTER DATABASE \`${params.name}\` COLLATE ${params.collation}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DROP DATABASE
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async dropSchema (params) {
|
||||||
|
return await this.raw(`DROP DATABASE \`${params.database}\``);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async getDatabaseCollation (params) {
|
||||||
|
return await this.raw(`SELECT \`DEFAULT_COLLATION_NAME\` FROM \`information_schema\`.\`SCHEMATA\` WHERE \`SCHEMA_NAME\`='${params.database}'`);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SHOW CREATE VIEW
|
* SHOW CREATE VIEW
|
||||||
*
|
*
|
||||||
@@ -536,7 +591,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
const sql = `SHOW CREATE PROCEDURE \`${schema}\`.\`${routine}\``;
|
const sql = `SHOW CREATE PROCEDURE \`${schema}\`.\`${routine}\``;
|
||||||
const results = await this.raw(sql);
|
const results = await this.raw(sql);
|
||||||
|
|
||||||
return results.rows.map(row => {
|
return results.rows.map(async row => {
|
||||||
if (!row['Create Procedure']) {
|
if (!row['Create Procedure']) {
|
||||||
return {
|
return {
|
||||||
definer: null,
|
definer: null,
|
||||||
@@ -550,22 +605,23 @@ export class MySQLClient extends AntaresCore {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const parameters = row['Create Procedure']
|
const sql = `SELECT *
|
||||||
.match(/(?<=\().*?(?=\))/s)[0]
|
FROM information_schema.parameters
|
||||||
.replaceAll('\r', '')
|
WHERE SPECIFIC_NAME = '${routine}'
|
||||||
.replaceAll('\t', '')
|
AND SPECIFIC_SCHEMA = '${schema}'
|
||||||
.split(',')
|
ORDER BY ORDINAL_POSITION
|
||||||
.map(el => {
|
`;
|
||||||
const param = el.split(' ');
|
|
||||||
const type = param[2] ? param[2].replace(')', '').split('(') : ['', null];
|
|
||||||
|
|
||||||
return {
|
const results = await this.raw(sql);
|
||||||
name: param[1] ? param[1].replaceAll('`', '') : '',
|
|
||||||
type: type[0],
|
const parameters = results.rows.map(row => {
|
||||||
length: +type[1],
|
return {
|
||||||
context: param[0] ? param[0].replace('\n', '') : ''
|
name: row.PARAMETER_NAME,
|
||||||
};
|
type: row.DATA_TYPE.toUpperCase(),
|
||||||
}).filter(el => el.name);
|
length: row.NUMERIC_PRECISION || row.DATETIME_PRECISION || row.CHARACTER_MAXIMUM_LENGTH || '',
|
||||||
|
context: row.PARAMETER_MODE
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
let dataAccess = 'CONTAINS SQL';
|
let dataAccess = 'CONTAINS SQL';
|
||||||
if (row['Create Procedure'].includes('NO SQL'))
|
if (row['Create Procedure'].includes('NO SQL'))
|
||||||
@@ -578,7 +634,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
return {
|
return {
|
||||||
definer: row['Create Procedure'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
definer: row['Create Procedure'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
||||||
sql: row['Create Procedure'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
sql: row['Create Procedure'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
||||||
parameters,
|
parameters: parameters || [],
|
||||||
name: row.Procedure,
|
name: row.Procedure,
|
||||||
comment: row['Create Procedure'].match(/(?<=COMMENT ').*?(?=')/gs) ? row['Create Procedure'].match(/(?<=COMMENT ').*?(?=')/gs)[0] : '',
|
comment: row['Create Procedure'].match(/(?<=COMMENT ').*?(?=')/gs) ? row['Create Procedure'].match(/(?<=COMMENT ').*?(?=')/gs)[0] : '',
|
||||||
security: row['Create Procedure'].includes('SQL SECURITY INVOKER') ? 'INVOKER' : 'DEFINER',
|
security: row['Create Procedure'].includes('SQL SECURITY INVOKER') ? 'INVOKER' : 'DEFINER',
|
||||||
@@ -628,10 +684,12 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async createRoutine (routine) {
|
async createRoutine (routine) {
|
||||||
const parameters = routine.parameters.reduce((acc, curr) => {
|
const parameters = 'parameters' in routine
|
||||||
acc.push(`${curr.context} \`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
|
? routine.parameters.reduce((acc, curr) => {
|
||||||
return acc;
|
acc.push(`${curr.context} \`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
|
||||||
}, []).join(',');
|
return acc;
|
||||||
|
}, []).join(',')
|
||||||
|
: '';
|
||||||
|
|
||||||
const sql = `CREATE ${routine.definer ? `DEFINER=${routine.definer} ` : ''}PROCEDURE \`${routine.name}\`(${parameters})
|
const sql = `CREATE ${routine.definer ? `DEFINER=${routine.definer} ` : ''}PROCEDURE \`${routine.name}\`(${parameters})
|
||||||
LANGUAGE SQL
|
LANGUAGE SQL
|
||||||
@@ -654,7 +712,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
const sql = `SHOW CREATE FUNCTION \`${schema}\`.\`${func}\``;
|
const sql = `SHOW CREATE FUNCTION \`${schema}\`.\`${func}\``;
|
||||||
const results = await this.raw(sql);
|
const results = await this.raw(sql);
|
||||||
|
|
||||||
return results.rows.map(row => {
|
return results.rows.map(async row => {
|
||||||
if (!row['Create Function']) {
|
if (!row['Create Function']) {
|
||||||
return {
|
return {
|
||||||
definer: null,
|
definer: null,
|
||||||
@@ -670,21 +728,23 @@ export class MySQLClient extends AntaresCore {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const parameters = row['Create Function']
|
const sql = `SELECT *
|
||||||
.match(/(?<=\().*?(?=\))/s)[0]
|
FROM information_schema.parameters
|
||||||
.replaceAll('\r', '')
|
WHERE SPECIFIC_NAME = '${func}'
|
||||||
.replaceAll('\t', '')
|
AND SPECIFIC_SCHEMA = '${schema}'
|
||||||
.split(',')
|
ORDER BY ORDINAL_POSITION
|
||||||
.map(el => {
|
`;
|
||||||
const param = el.split(' ');
|
|
||||||
const type = param[1] ? param[1].replace(')', '').split('(') : ['', null];
|
|
||||||
|
|
||||||
return {
|
const results = await this.raw(sql);
|
||||||
name: param[0] ? param[0].replaceAll('`', '') : '',
|
|
||||||
type: type[0],
|
const parameters = results.rows.filter(row => row.PARAMETER_MODE).map(row => {
|
||||||
length: +type[1]
|
return {
|
||||||
};
|
name: row.PARAMETER_NAME,
|
||||||
}).filter(el => el.name);
|
type: row.DATA_TYPE.toUpperCase(),
|
||||||
|
length: row.NUMERIC_PRECISION || row.DATETIME_PRECISION || row.CHARACTER_MAXIMUM_LENGTH || '',
|
||||||
|
context: row.PARAMETER_MODE
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
let dataAccess = 'CONTAINS SQL';
|
let dataAccess = 'CONTAINS SQL';
|
||||||
if (row['Create Function'].includes('NO SQL'))
|
if (row['Create Function'].includes('NO SQL'))
|
||||||
@@ -699,7 +759,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
return {
|
return {
|
||||||
definer: row['Create Function'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
definer: row['Create Function'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
||||||
sql: row['Create Function'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
sql: row['Create Function'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
||||||
parameters,
|
parameters: parameters || [],
|
||||||
name: row.Function,
|
name: row.Function,
|
||||||
comment: row['Create Function'].match(/(?<=COMMENT ').*?(?=')/gs) ? row['Create Function'].match(/(?<=COMMENT ').*?(?=')/gs)[0] : '',
|
comment: row['Create Function'].match(/(?<=COMMENT ').*?(?=')/gs) ? row['Create Function'].match(/(?<=COMMENT ').*?(?=')/gs)[0] : '',
|
||||||
security: row['Create Function'].includes('SQL SECURITY INVOKER') ? 'INVOKER' : 'DEFINER',
|
security: row['Create Function'].includes('SQL SECURITY INVOKER') ? 'INVOKER' : 'DEFINER',
|
||||||
@@ -756,13 +816,15 @@ export class MySQLClient extends AntaresCore {
|
|||||||
return acc;
|
return acc;
|
||||||
}, []).join(',');
|
}, []).join(',');
|
||||||
|
|
||||||
const sql = `CREATE ${func.definer ? `DEFINER=${func.definer} ` : ''}FUNCTION \`${func.name}\`(${parameters}) RETURNS ${func.returns}${func.returnsLength ? `(${func.returnsLength})` : ''}
|
const body = func.returns ? func.sql : 'BEGIN\n RETURN 0;\nEND';
|
||||||
|
|
||||||
|
const sql = `CREATE ${func.definer ? `DEFINER=${func.definer} ` : ''}FUNCTION \`${func.name}\`(${parameters}) RETURNS ${func.returns || 'SMALLINT'}${func.returnsLength ? `(${func.returnsLength})` : ''}
|
||||||
LANGUAGE SQL
|
LANGUAGE SQL
|
||||||
${func.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
|
${func.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
|
||||||
${func.dataAccess}
|
${func.dataAccess}
|
||||||
SQL SECURITY ${func.security}
|
SQL SECURITY ${func.security}
|
||||||
COMMENT '${func.comment}'
|
COMMENT '${func.comment}'
|
||||||
${func.sql}`;
|
${body}`;
|
||||||
|
|
||||||
return await this.raw(sql, { split: false });
|
return await this.raw(sql, { split: false });
|
||||||
}
|
}
|
||||||
@@ -920,6 +982,54 @@ export class MySQLClient extends AntaresCore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SHOW VARIABLES LIKE '%vers%'
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} version parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async getVersion () {
|
||||||
|
const sql = 'SHOW VARIABLES LIKE "%vers%"';
|
||||||
|
const { rows } = await this.raw(sql);
|
||||||
|
|
||||||
|
return rows.reduce((acc, curr) => {
|
||||||
|
switch (curr.Variable_name) {
|
||||||
|
case 'version':
|
||||||
|
acc.number = curr.Value.split('-')[0];
|
||||||
|
break;
|
||||||
|
case 'version_comment':
|
||||||
|
acc.name = curr.Value.replace('(GPL)', '');
|
||||||
|
break;
|
||||||
|
case 'version_compile_machine':
|
||||||
|
acc.arch = curr.Value;
|
||||||
|
break;
|
||||||
|
case 'version_compile_os':
|
||||||
|
acc.os = curr.Value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getProcesses () {
|
||||||
|
const sql = 'SELECT `ID`, `USER`, `HOST`, `DB`, `COMMAND`, `TIME`, `STATE`, LEFT(`INFO`, 51200) AS `INFO` FROM `information_schema`.`PROCESSLIST`';
|
||||||
|
|
||||||
|
const { rows } = await this.raw(sql);
|
||||||
|
|
||||||
|
return rows.map(row => {
|
||||||
|
return {
|
||||||
|
id: row.ID,
|
||||||
|
user: row.USER,
|
||||||
|
host: row.HOST,
|
||||||
|
db: row.DB,
|
||||||
|
command: row.COMMAND,
|
||||||
|
time: row.TIME,
|
||||||
|
state: row.STATE,
|
||||||
|
info: row.INFO
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CREATE TABLE
|
* CREATE TABLE
|
||||||
*
|
*
|
||||||
@@ -956,7 +1066,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
options
|
options
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
let sql = `ALTER TABLE \`${table}\` `;
|
let sql = `ALTER TABLE \`${this._schema}\`.\`${table}\` `;
|
||||||
const alterColumns = [];
|
const alterColumns = [];
|
||||||
|
|
||||||
// OPTIONS
|
// OPTIONS
|
||||||
@@ -1128,18 +1238,11 @@ export class MySQLClient extends AntaresCore {
|
|||||||
// INSERT
|
// INSERT
|
||||||
let insertRaw = '';
|
let insertRaw = '';
|
||||||
|
|
||||||
if (Object.keys(this._query.insert).length) {
|
if (this._query.insert.length) {
|
||||||
const fieldsList = [];
|
const fieldsList = Object.keys(this._query.insert[0]);
|
||||||
const valueList = [];
|
const rowsList = this._query.insert.map(el => `(${Object.values(el).join(', ')})`);
|
||||||
const fields = this._query.insert;
|
|
||||||
|
|
||||||
for (const key in fields) {
|
insertRaw = `(${fieldsList.join(', ')}) VALUES ${rowsList.join(', ')} `;
|
||||||
if (fields[key] === null) continue;
|
|
||||||
fieldsList.push(key);
|
|
||||||
valueList.push(fields[key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
insertRaw = `(${fieldsList.join(', ')}) VALUES (${valueList.join(', ')}) `;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GROUP BY
|
// GROUP BY
|
||||||
@@ -1181,10 +1284,13 @@ export class MySQLClient extends AntaresCore {
|
|||||||
|
|
||||||
for (const query of queries) {
|
for (const query of queries) {
|
||||||
if (!query) continue;
|
if (!query) continue;
|
||||||
|
const timeStart = new Date();
|
||||||
|
let timeStop;
|
||||||
let keysArr = [];
|
let keysArr = [];
|
||||||
|
|
||||||
const { rows, report, fields, keys } = await new Promise((resolve, reject) => {
|
const { rows, report, fields, keys, duration } = await new Promise((resolve, reject) => {
|
||||||
this._connection.query({ sql: query, nestTables }, async (err, response, fields) => {
|
this._connection.query({ sql: query, nestTables }, async (err, response, fields) => {
|
||||||
|
timeStop = new Date();
|
||||||
const queryResult = response;
|
const queryResult = response;
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
@@ -1201,10 +1307,9 @@ export class MySQLClient extends AntaresCore {
|
|||||||
name: field.orgName,
|
name: field.orgName,
|
||||||
alias: field.name,
|
alias: field.name,
|
||||||
orgName: field.orgName,
|
orgName: field.orgName,
|
||||||
schema: field.db,
|
schema: field.schema,
|
||||||
table: field.table,
|
table: field.table,
|
||||||
tableAlias: field.table,
|
tableAlias: field.table,
|
||||||
zerofill: field.zerofill,
|
|
||||||
orgTable: field.orgTable,
|
orgTable: field.orgTable,
|
||||||
type: type.name,
|
type: type.name,
|
||||||
length: type.length
|
length: type.length
|
||||||
@@ -1231,7 +1336,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
const response = await this.getTableColumns(paramObj);
|
const response = await this.getTableColumns(paramObj);
|
||||||
remappedFields = remappedFields.map(field => {
|
remappedFields = remappedFields.map(field => {
|
||||||
const detailedField = response.find(f => f.name === field.name);
|
const detailedField = response.find(f => f.name === field.name);
|
||||||
if (field.orgTable === paramObj.table && field.schema === paramObj.schema && detailedField.name === field.orgName)
|
if (detailedField && field.orgTable === paramObj.table && field.schema === paramObj.schema)
|
||||||
field = { ...detailedField, ...field };
|
field = { ...detailedField, ...field };
|
||||||
return field;
|
return field;
|
||||||
});
|
});
|
||||||
@@ -1252,6 +1357,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
|
duration: timeStop - timeStart,
|
||||||
rows: Array.isArray(queryResult) ? queryResult.some(el => Array.isArray(el)) ? [] : queryResult : false,
|
rows: Array.isArray(queryResult) ? queryResult.some(el => Array.isArray(el)) ? [] : queryResult : false,
|
||||||
report: !Array.isArray(queryResult) ? queryResult : false,
|
report: !Array.isArray(queryResult) ? queryResult : false,
|
||||||
fields: remappedFields,
|
fields: remappedFields,
|
||||||
@@ -1261,7 +1367,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
resultsArr.push({ rows, report, fields, keys });
|
resultsArr.push({ rows, report, fields, keys, duration });
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultsArr.length === 1 ? resultsArr[0] : resultsArr;
|
return resultsArr.length === 1 ? resultsArr[0] : resultsArr;
|
||||||
|
|||||||
1397
src/main/libs/clients/PostgreSQLClient.js
Normal file
1397
src/main/libs/clients/PostgreSQLClient.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="wrapper">
|
<div id="wrapper" :class="`theme-${applicationTheme}`">
|
||||||
<TheTitleBar />
|
<TheTitleBar />
|
||||||
<div id="window-content">
|
<div id="window-content">
|
||||||
<TheSettingBar />
|
<TheSettingBar />
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
<TheFooter />
|
<TheFooter />
|
||||||
<TheNotificationsBoard />
|
<TheNotificationsBoard />
|
||||||
<ModalNewConnection v-if="isNewConnModal" />
|
<ModalNewConnection v-if="isNewConnModal" />
|
||||||
|
<TheScratchpad v-if="isScratchpad" />
|
||||||
<ModalSettings v-if="isSettingModal" />
|
<ModalSettings v-if="isSettingModal" />
|
||||||
<ModalDiscardChanges v-if="isUnsavedDiscardModal" />
|
<ModalDiscardChanges v-if="isUnsavedDiscardModal" />
|
||||||
</div>
|
</div>
|
||||||
@@ -24,7 +25,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapActions, mapGetters } from 'vuex';
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer, remote } from 'electron';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
@@ -37,6 +38,7 @@ export default {
|
|||||||
Workspace: () => import(/* webpackChunkName: "Workspace" */'@/components/Workspace'),
|
Workspace: () => import(/* webpackChunkName: "Workspace" */'@/components/Workspace'),
|
||||||
ModalNewConnection: () => import(/* webpackChunkName: "ModalNewConnection" */'@/components/ModalNewConnection'),
|
ModalNewConnection: () => import(/* webpackChunkName: "ModalNewConnection" */'@/components/ModalNewConnection'),
|
||||||
ModalSettings: () => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings'),
|
ModalSettings: () => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings'),
|
||||||
|
TheScratchpad: () => import(/* webpackChunkName: "TheScratchpad" */'@/components/TheScratchpad'),
|
||||||
ModalDiscardChanges: () => import(/* webpackChunkName: "ModalDiscardChanges" */'@/components/ModalDiscardChanges')
|
ModalDiscardChanges: () => import(/* webpackChunkName: "ModalDiscardChanges" */'@/components/ModalDiscardChanges')
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
@@ -48,16 +50,59 @@ export default {
|
|||||||
isNewConnModal: 'application/isNewModal',
|
isNewConnModal: 'application/isNewModal',
|
||||||
isEditModal: 'application/isEditModal',
|
isEditModal: 'application/isEditModal',
|
||||||
isSettingModal: 'application/isSettingModal',
|
isSettingModal: 'application/isSettingModal',
|
||||||
|
isScratchpad: 'application/isScratchpad',
|
||||||
connections: 'connections/getConnections',
|
connections: 'connections/getConnections',
|
||||||
|
applicationTheme: 'settings/getApplicationTheme',
|
||||||
isUnsavedDiscardModal: 'workspaces/isUnsavedDiscardModal'
|
isUnsavedDiscardModal: 'workspaces/isUnsavedDiscardModal'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
ipcRenderer.send('check-for-updates');
|
ipcRenderer.send('check-for-updates');
|
||||||
|
this.checkVersionUpdate();
|
||||||
|
|
||||||
|
const Menu = remote.Menu;
|
||||||
|
|
||||||
|
const InputMenu = Menu.buildFromTemplate([
|
||||||
|
{
|
||||||
|
label: this.$t('word.cut'),
|
||||||
|
role: 'cut'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('word.copy'),
|
||||||
|
role: 'copy'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('word.paste'),
|
||||||
|
role: 'paste'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'separator'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('message.selectAll'),
|
||||||
|
role: 'selectall'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
document.body.addEventListener('contextmenu', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
let node = e.target;
|
||||||
|
|
||||||
|
while (node) {
|
||||||
|
if (node.nodeName.match(/^(input|textarea)$/i) || node.isContentEditable) {
|
||||||
|
InputMenu.popup(remote.getCurrentWindow());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
node = node.parentNode;
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
showNewConnModal: 'application/showNewConnModal'
|
showNewConnModal: 'application/showNewConnModal',
|
||||||
|
checkVersionUpdate: 'application/checkVersionUpdate'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
<slot name="body" />
|
<slot name="body" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div v-if="!hideFooter" class="modal-footer">
|
||||||
<button
|
<button
|
||||||
class="btn btn-primary mr-2"
|
class="btn btn-primary mr-2"
|
||||||
@click.stop="confirmModal"
|
@click.stop="confirmModal"
|
||||||
@@ -51,6 +51,10 @@ export default {
|
|||||||
validator: prop => ['small', 'medium', '400', 'large'].includes(prop),
|
validator: prop => ['small', 'medium', '400', 'large'].includes(prop),
|
||||||
default: 'small'
|
default: 'small'
|
||||||
},
|
},
|
||||||
|
hideFooter: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
confirmText: String,
|
confirmText: String,
|
||||||
cancelText: String
|
cancelText: String
|
||||||
},
|
},
|
||||||
@@ -74,6 +78,12 @@ export default {
|
|||||||
else return '';
|
else return '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
confirmModal () {
|
confirmModal () {
|
||||||
this.$emit('confirm');
|
this.$emit('confirm');
|
||||||
@@ -82,6 +92,11 @@ export default {
|
|||||||
|
|
||||||
hideModal () {
|
hideModal () {
|
||||||
this.$emit('hide');
|
this.$emit('hide');
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape')
|
||||||
|
this.hideModal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ export default {
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.context {
|
.context {
|
||||||
display: flex;
|
display: flex;
|
||||||
color: $body-font-color;
|
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
z-index: 400;
|
z-index: 400;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -87,7 +86,6 @@ export default {
|
|||||||
.context-container {
|
.context-container {
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
box-shadow: 0 0 2px 0 #000;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: #1d1d1d;
|
background: #1d1d1d;
|
||||||
border-radius: 0.1rem;
|
border-radius: 0.1rem;
|
||||||
@@ -111,14 +109,10 @@ export default {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
left: 100%;
|
left: 100%;
|
||||||
top: 0;
|
top: 0;
|
||||||
background: #1d1d1d;
|
|
||||||
box-shadow: 0 0 2px 0 #000;
|
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $primary-color;
|
|
||||||
|
|
||||||
.context-submenu {
|
.context-submenu {
|
||||||
display: block;
|
display: block;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
|
|||||||
117
src/renderer/components/BaseTextEditor.vue
Normal file
117
src/renderer/components/BaseTextEditor.vue
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
<template>
|
||||||
|
<div class="editor-wrapper">
|
||||||
|
<div
|
||||||
|
:id="`editor-${id}`"
|
||||||
|
class="editor"
|
||||||
|
:class="editorClass"
|
||||||
|
:style="{height: `${height}px`}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as ace from 'ace-builds';
|
||||||
|
import 'ace-builds/webpack-resolver';
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'BaseTextEditor',
|
||||||
|
props: {
|
||||||
|
value: String,
|
||||||
|
mode: { type: String, default: 'text' },
|
||||||
|
editorClass: { type: String, default: '' },
|
||||||
|
autoFocus: { type: Boolean, default: false },
|
||||||
|
readOnly: { type: Boolean, default: false },
|
||||||
|
showLineNumbers: { type: Boolean, default: true },
|
||||||
|
height: { type: Number, default: 200 }
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
editor: null,
|
||||||
|
id: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
editorTheme: 'settings/getEditorTheme',
|
||||||
|
autoComplete: 'settings/getAutoComplete',
|
||||||
|
lineWrap: 'settings/getLineWrap'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
mode () {
|
||||||
|
if (this.editor)
|
||||||
|
this.editor.session.setMode(`ace/mode/${this.mode}`);
|
||||||
|
},
|
||||||
|
editorTheme () {
|
||||||
|
if (this.editor)
|
||||||
|
this.editor.setTheme(`ace/theme/${this.editorTheme}`);
|
||||||
|
},
|
||||||
|
autoComplete () {
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.setOptions({
|
||||||
|
enableLiveAutocompletion: this.autoComplete
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lineWrap () {
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.setOptions({
|
||||||
|
wrap: this.lineWrap
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.id = this._uid;
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.editor = ace.edit(`editor-${this.id}`, {
|
||||||
|
mode: `ace/mode/${this.mode}`,
|
||||||
|
theme: `ace/theme/${this.editorTheme}`,
|
||||||
|
value: this.value || '',
|
||||||
|
fontSize: '14px',
|
||||||
|
printMargin: false,
|
||||||
|
readOnly: this.readOnly,
|
||||||
|
showLineNumbers: this.showLineNumbers,
|
||||||
|
showGutter: this.showLineNumbers
|
||||||
|
});
|
||||||
|
|
||||||
|
this.editor.setOptions({
|
||||||
|
enableBasicAutocompletion: false,
|
||||||
|
wrap: this.lineWrap,
|
||||||
|
enableSnippets: false,
|
||||||
|
enableLiveAutocompletion: false
|
||||||
|
});
|
||||||
|
|
||||||
|
this.editor.session.on('change', () => {
|
||||||
|
const content = this.editor.getValue();
|
||||||
|
this.$emit('update:value', content);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.autoFocus) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.editor.focus();
|
||||||
|
this.editor.resize();
|
||||||
|
}, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.editor.resize();
|
||||||
|
}, 20);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.editor-wrapper {
|
||||||
|
.editor {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ace_.mdi {
|
||||||
|
display: inline-block;
|
||||||
|
width: 17px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
107
src/renderer/components/BaseUploadInput.vue
Normal file
107
src/renderer/components/BaseUploadInput.vue
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<template>
|
||||||
|
<label :for="`id_${id}`" class="file-uploader">
|
||||||
|
<span class="file-uploader-message">
|
||||||
|
<i class="mdi mdi-folder-open mr-1" />{{ message }}
|
||||||
|
</span>
|
||||||
|
<span class="text-ellipsis file-uploader-value">
|
||||||
|
{{ value | lastPart }}
|
||||||
|
</span>
|
||||||
|
<i
|
||||||
|
v-if="value.length"
|
||||||
|
class="file-uploader-reset mdi mdi-close"
|
||||||
|
@click.prevent="clear"
|
||||||
|
/>
|
||||||
|
<form :ref="`form_${id}`">
|
||||||
|
<input
|
||||||
|
:id="`id_${id}`"
|
||||||
|
class="file-uploader-input"
|
||||||
|
type="file"
|
||||||
|
@change="$emit('change', $event)"
|
||||||
|
>
|
||||||
|
</form>
|
||||||
|
</label>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'BaseUploadInput',
|
||||||
|
filters: {
|
||||||
|
lastPart (string) {
|
||||||
|
if (!string) return '';
|
||||||
|
|
||||||
|
string = string.split(/[/\\]+/).pop();
|
||||||
|
if (string.length >= 19)
|
||||||
|
string = `...${string.slice(-19)}`;
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
message: {
|
||||||
|
default: 'Browse',
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
default: '',
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
id: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.id = this._uid;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
clear () {
|
||||||
|
this.$emit('clear');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.file-uploader {
|
||||||
|
border-radius: 0.1rem;
|
||||||
|
height: 1.8rem;
|
||||||
|
line-height: 1.2rem;
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s, border 0.2s, box-shadow 0.2s, color 0.2s;
|
||||||
|
position: relative;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
padding: 0.25rem 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-uploader-message {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-uploader-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-uploader-value {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-uploader-reset {
|
||||||
|
z-index: 1;
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
top: calc(50% - 8px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:disabled {
|
||||||
|
.file-uploader {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -41,14 +41,16 @@ export default {
|
|||||||
localScrollElement: null
|
localScrollElement: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
scrollElement () {
|
||||||
|
this.setScrollElement();
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this._checkScrollPosition = this.checkScrollPosition.bind(this);
|
this.setScrollElement();
|
||||||
this.localScrollElement = this.scrollElement ? this.scrollElement : this.$el;
|
|
||||||
this.updateWindow();
|
|
||||||
this.localScrollElement.addEventListener('scroll', this._checkScrollPosition);
|
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
this.localScrollElement.removeEventListener('scroll', this._checkScrollPosition);
|
this.localScrollElement.removeEventListener('scroll', this.checkScrollPosition);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
checkScrollPosition (e) {
|
checkScrollPosition (e) {
|
||||||
@@ -58,7 +60,7 @@ export default {
|
|||||||
this.updateWindow(e);
|
this.updateWindow(e);
|
||||||
}, 200);
|
}, 200);
|
||||||
},
|
},
|
||||||
updateWindow (e) {
|
updateWindow () {
|
||||||
const visibleItemsCount = Math.ceil(this.visibleHeight / this.itemHeight);
|
const visibleItemsCount = Math.ceil(this.visibleHeight / this.itemHeight);
|
||||||
const totalScrollHeight = this.items.length * this.itemHeight;
|
const totalScrollHeight = this.items.length * this.itemHeight;
|
||||||
const offset = 50;
|
const offset = 50;
|
||||||
@@ -74,6 +76,14 @@ export default {
|
|||||||
|
|
||||||
this.topHeight = firstCutIndex * this.itemHeight;
|
this.topHeight = firstCutIndex * this.itemHeight;
|
||||||
this.bottomHeight = totalScrollHeight - this.visibleItems.length * this.itemHeight - this.topHeight;
|
this.bottomHeight = totalScrollHeight - this.visibleItems.length * this.itemHeight - this.topHeight;
|
||||||
|
},
|
||||||
|
setScrollElement () {
|
||||||
|
if (this.localScrollElement)
|
||||||
|
this.localScrollElement.removeEventListener('scroll', this.checkScrollPosition);
|
||||||
|
|
||||||
|
this.localScrollElement = this.scrollElement ? this.scrollElement : this.$el;
|
||||||
|
this.updateWindow();
|
||||||
|
this.localScrollElement.addEventListener('scroll', this.checkScrollPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
235
src/renderer/components/FakerSelect.vue
Normal file
235
src/renderer/components/FakerSelect.vue
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
<template>
|
||||||
|
<fieldset class="input-group mb-0">
|
||||||
|
<select
|
||||||
|
v-model="selectedGroup"
|
||||||
|
class="form-select"
|
||||||
|
:disabled="!isChecked"
|
||||||
|
style="flex-grow: 0;"
|
||||||
|
@change="onChange"
|
||||||
|
>
|
||||||
|
<option value="manual">
|
||||||
|
{{ $t('message.manualValue') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="group in fakerGroups"
|
||||||
|
:key="group.name"
|
||||||
|
:value="group.name"
|
||||||
|
>
|
||||||
|
{{ $t(`faker.${group.name}`) }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<select
|
||||||
|
v-if="selectedGroup !== 'manual'"
|
||||||
|
v-model="selectedMethod"
|
||||||
|
class="form-select"
|
||||||
|
:disabled="!isChecked"
|
||||||
|
@change="onChange"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="method in fakerMethods"
|
||||||
|
:key="method.name"
|
||||||
|
:value="method.name"
|
||||||
|
>
|
||||||
|
{{ $t(`faker.${method.name}`) }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<ForeignKeySelect
|
||||||
|
v-else-if="foreignKeys.includes(field.name)"
|
||||||
|
ref="formInput"
|
||||||
|
class="form-select"
|
||||||
|
:value.sync="selectedValue"
|
||||||
|
:key-usage="getKeyUsage(field.name)"
|
||||||
|
:disabled="!isChecked"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
v-else-if="inputProps().mask"
|
||||||
|
ref="formInput"
|
||||||
|
v-model="selectedValue"
|
||||||
|
v-mask="inputProps().mask"
|
||||||
|
class="form-input"
|
||||||
|
:type="inputProps().type"
|
||||||
|
:disabled="!isChecked"
|
||||||
|
>
|
||||||
|
<BaseUploadInput
|
||||||
|
v-else-if="inputProps().type === 'file'"
|
||||||
|
:value="selectedValue"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="clearValue"
|
||||||
|
@change="filesChange($event)"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
v-else-if="inputProps().type === 'number'"
|
||||||
|
ref="formInput"
|
||||||
|
v-model="selectedValue"
|
||||||
|
class="form-input"
|
||||||
|
step="any"
|
||||||
|
:type="inputProps().type"
|
||||||
|
:disabled="!isChecked"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
v-else
|
||||||
|
ref="formInput"
|
||||||
|
v-model="selectedValue"
|
||||||
|
class="form-input"
|
||||||
|
:type="inputProps().type"
|
||||||
|
:disabled="!isChecked"
|
||||||
|
>
|
||||||
|
<template v-if="methodData && 'params' in methodData" class="columns">
|
||||||
|
<input
|
||||||
|
v-for="(option, key) in methodData.params"
|
||||||
|
:key="key"
|
||||||
|
v-model="methodParams[option]"
|
||||||
|
class="form-input column"
|
||||||
|
:type="inputProps().type"
|
||||||
|
:disabled="!isChecked"
|
||||||
|
:placeholder="option"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<slot />
|
||||||
|
</fieldset>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { VueMaskDirective } from 'v-mask';
|
||||||
|
import { TEXT, LONG_TEXT, NUMBER, FLOAT, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
|
||||||
|
import BaseUploadInput from '@/components/BaseUploadInput';
|
||||||
|
import ForeignKeySelect from '@/components/ForeignKeySelect';
|
||||||
|
import FakerMethods from 'common/FakerMethods';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FakerSelect',
|
||||||
|
components: {
|
||||||
|
ForeignKeySelect,
|
||||||
|
BaseUploadInput
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
mask: VueMaskDirective
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
type: String,
|
||||||
|
field: Object,
|
||||||
|
isChecked: Boolean,
|
||||||
|
foreignKeys: Array,
|
||||||
|
keyUsage: Array,
|
||||||
|
fieldLength: Number,
|
||||||
|
fieldObj: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
localType: null,
|
||||||
|
selectedGroup: 'manual',
|
||||||
|
selectedMethod: '',
|
||||||
|
selectedValue: '',
|
||||||
|
debounceTimeout: null,
|
||||||
|
methodParams: {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
fakerGroups () {
|
||||||
|
if ([...TEXT, ...LONG_TEXT].includes(this.type))
|
||||||
|
this.localType = 'string';
|
||||||
|
else if (NUMBER.includes(this.type))
|
||||||
|
this.localType = 'number';
|
||||||
|
else if (FLOAT.includes(this.type))
|
||||||
|
this.localType = 'float';
|
||||||
|
else if ([...DATE, ...DATETIME].includes(this.type))
|
||||||
|
this.localType = 'datetime';
|
||||||
|
else if (TIME.includes(this.type))
|
||||||
|
this.localType = 'time';
|
||||||
|
else
|
||||||
|
this.localType = 'none';
|
||||||
|
|
||||||
|
return FakerMethods.getGroupsByType(this.localType);
|
||||||
|
},
|
||||||
|
fakerMethods () {
|
||||||
|
return FakerMethods.getMethods({ type: this.localType, group: this.selectedGroup });
|
||||||
|
},
|
||||||
|
methodData () {
|
||||||
|
return this.fakerMethods.find(method => method.name === this.selectedMethod);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
fieldObj () {
|
||||||
|
if (this.fieldObj)
|
||||||
|
this.selectedValue = this.fieldObj.value;
|
||||||
|
},
|
||||||
|
selectedGroup () {
|
||||||
|
if (this.fakerMethods.length)
|
||||||
|
this.selectedMethod = this.fakerMethods[0].name;
|
||||||
|
else
|
||||||
|
this.selectedMethod = '';
|
||||||
|
},
|
||||||
|
selectedMethod () {
|
||||||
|
this.onChange();
|
||||||
|
},
|
||||||
|
selectedValue () {
|
||||||
|
clearTimeout(this.debounceTimeout);
|
||||||
|
this.debounceTimeout = null;
|
||||||
|
this.debounceTimeout = setTimeout(() => {
|
||||||
|
this.onChange();
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
inputProps () {
|
||||||
|
if ([...TEXT, ...LONG_TEXT].includes(this.type))
|
||||||
|
return { type: 'text', mask: false };
|
||||||
|
|
||||||
|
if ([...NUMBER, ...FLOAT].includes(this.type))
|
||||||
|
return { type: 'number', mask: false };
|
||||||
|
|
||||||
|
if (TIME.includes(this.type)) {
|
||||||
|
let timeMask = '##:##:##';
|
||||||
|
const precision = this.fieldLength;
|
||||||
|
|
||||||
|
for (let i = 0; i < precision; i++)
|
||||||
|
timeMask += i === 0 ? '.#' : '#';
|
||||||
|
|
||||||
|
return { type: 'text', mask: timeMask };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DATE.includes(this.type))
|
||||||
|
return { type: 'text', mask: '####-##-##' };
|
||||||
|
|
||||||
|
if (DATETIME.includes(this.type)) {
|
||||||
|
let datetimeMask = '####-##-## ##:##:##';
|
||||||
|
const precision = this.fieldLength;
|
||||||
|
|
||||||
|
for (let i = 0; i < precision; i++)
|
||||||
|
datetimeMask += i === 0 ? '.#' : '#';
|
||||||
|
|
||||||
|
return { type: 'text', mask: datetimeMask };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BLOB.includes(this.type))
|
||||||
|
return { type: 'file', mask: false };
|
||||||
|
|
||||||
|
if (BIT.includes(this.type))
|
||||||
|
return { type: 'text', mask: false };
|
||||||
|
|
||||||
|
return { type: 'text', mask: false };
|
||||||
|
},
|
||||||
|
getKeyUsage (keyName) {
|
||||||
|
return this.keyUsage.find(key => key.field === keyName);
|
||||||
|
},
|
||||||
|
filesChange (event) {
|
||||||
|
const { files } = event.target;
|
||||||
|
if (!files.length) return;
|
||||||
|
|
||||||
|
this.selectedValue = files[0].path;
|
||||||
|
},
|
||||||
|
clearValue () {
|
||||||
|
this.selectedValue = '';
|
||||||
|
},
|
||||||
|
onChange () {
|
||||||
|
this.$emit('update:value', {
|
||||||
|
group: this.selectedGroup,
|
||||||
|
method: this.selectedMethod,
|
||||||
|
params: this.methodParams,
|
||||||
|
value: this.selectedValue,
|
||||||
|
length: this.fieldLength
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -11,11 +11,11 @@
|
|||||||
</option>
|
</option>
|
||||||
<option
|
<option
|
||||||
v-for="row in foreignList"
|
v-for="row in foreignList"
|
||||||
:key="row.foreignColumn"
|
:key="row.foreign_column"
|
||||||
:value="row.foreignColumn"
|
:value="row.foreign_column"
|
||||||
:selected="row.foreignColumn === value"
|
:selected="row.foreign_column === value"
|
||||||
>
|
>
|
||||||
{{ row.foreignColumn }} {{ 'foreignDescription' in row ? ` - ${row.foreignDescription}` : '' | cutText }}
|
{{ row.foreign_column }} {{ 'foreign_description' in row ? ` - ${row.foreign_description}` : '' | cutText }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</template>
|
</template>
|
||||||
@@ -50,11 +50,12 @@ export default {
|
|||||||
selectedWorkspace: 'workspaces/getSelected'
|
selectedWorkspace: 'workspaces/getSelected'
|
||||||
}),
|
}),
|
||||||
isValidDefault () {
|
isValidDefault () {
|
||||||
return this.foreignList.some(foreign => foreign.foreignColumn.toString() === this.value.toString());
|
if (!this.foreignList.length) return true;
|
||||||
|
return this.value === null || this.foreignList.some(foreign => foreign.foreign_column.toString() === this.value.toString());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created () {
|
async created () {
|
||||||
let firstTextField;
|
let foreignDesc;
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
schema: this.keyUsage.refSchema,
|
schema: this.keyUsage.refSchema,
|
||||||
@@ -63,8 +64,10 @@ export default {
|
|||||||
|
|
||||||
try { // Field data
|
try { // Field data
|
||||||
const { status, response } = await Tables.getTableColumns(params);
|
const { status, response } = await Tables.getTableColumns(params);
|
||||||
if (status === 'success')
|
if (status === 'success') {
|
||||||
firstTextField = response.find(field => [...TEXT, ...LONG_TEXT].includes(field.type)).name || false;
|
const textField = response.find(field => [...TEXT, ...LONG_TEXT].includes(field.type));
|
||||||
|
foreignDesc = textField ? textField.name : false;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
this.addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
@@ -76,7 +79,7 @@ export default {
|
|||||||
const { status, response } = await Tables.getForeignList({
|
const { status, response } = await Tables.getForeignList({
|
||||||
...params,
|
...params,
|
||||||
column: this.keyUsage.refField,
|
column: this.keyUsage.refField,
|
||||||
description: firstTextField
|
description: foreignDesc
|
||||||
});
|
});
|
||||||
|
|
||||||
if (status === 'success')
|
if (status === 'success')
|
||||||
|
|||||||
129
src/renderer/components/ModalAskParameters.vue
Normal file
129
src/renderer/components/ModalAskParameters.vue
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.run')"
|
||||||
|
:cancel-text="$t('word.cancel')"
|
||||||
|
size="400"
|
||||||
|
@confirm="runRoutine"
|
||||||
|
@hide="closeModal"
|
||||||
|
>
|
||||||
|
<template slot="header">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-play mr-1" /> {{ $t('word.parameters') }}: {{ localRoutine.name }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div slot="body">
|
||||||
|
<div class="content">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div
|
||||||
|
v-for="(parameter, i) in inParameters"
|
||||||
|
:key="parameter._id"
|
||||||
|
class="form-group"
|
||||||
|
>
|
||||||
|
<div class="col-3">
|
||||||
|
<label class="form-label">{{ parameter.name }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-9">
|
||||||
|
<div class="input-group">
|
||||||
|
<input
|
||||||
|
:ref="i === 0 ? 'firstInput' : ''"
|
||||||
|
v-model="values[`${i}-${parameter.name}`]"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
<span class="input-group-addon field-type" :class="typeClass(parameter.type)">
|
||||||
|
{{ parameter.type }} {{ parameter.length | wrapNumber }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { NUMBER, FLOAT } from 'common/fieldTypes';
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalAskParameters',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
wrapNumber (num) {
|
||||||
|
if (!num) return '';
|
||||||
|
return `(${num})`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
localRoutine: Object,
|
||||||
|
client: String
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
values: {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
inParameters () {
|
||||||
|
return this.localRoutine.parameters.filter(param => param.context === 'IN');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput[0].focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
typeClass (type) {
|
||||||
|
if (type)
|
||||||
|
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
runRoutine () {
|
||||||
|
const valArr = Object.keys(this.values).reduce((acc, curr, i) => {
|
||||||
|
let qc;
|
||||||
|
switch (this.client) {
|
||||||
|
case 'maria':
|
||||||
|
case 'mysql':
|
||||||
|
qc = '"';
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
qc = '\'';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qc = '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
const param = this.localRoutine.parameters.find(param => `${i}-${param.name}` === curr);
|
||||||
|
|
||||||
|
const value = [...NUMBER, ...FLOAT].includes(param.type) ? this.values[curr] : `${qc}${this.values[curr]}${qc}`;
|
||||||
|
acc.push(value);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
this.$emit('confirm', valArr);
|
||||||
|
},
|
||||||
|
closeModal () {
|
||||||
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape')
|
||||||
|
this.closeModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.field-type {
|
||||||
|
font-size: 0.6rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -10,151 +10,267 @@
|
|||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click="closeModal" />
|
<a class="btn btn-clear c-hand" @click="closeModal" />
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body pb-0">
|
<div class="modal-body p-0">
|
||||||
<div class="content">
|
<div class="panel">
|
||||||
<form class="form-horizontal">
|
<div class="panel-nav">
|
||||||
<fieldset class="m-0" :disabled="isTesting">
|
<ul class="tab tab-block">
|
||||||
<div class="form-group">
|
<li
|
||||||
<div class="col-4 col-sm-12">
|
class="tab-item"
|
||||||
<label class="form-label">{{ $t('word.connectionName') }}</label>
|
:class="{'active': selectedTab === 'general'}"
|
||||||
|
@click="selectTab('general')"
|
||||||
|
>
|
||||||
|
<a class="c-hand">{{ $t('word.general') }}</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="tab-item"
|
||||||
|
:class="{'active': selectedTab === 'ssl'}"
|
||||||
|
@click="selectTab('ssl')"
|
||||||
|
>
|
||||||
|
<a class="c-hand">{{ $t('word.ssl') }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedTab === 'general'" class="panel-body py-0">
|
||||||
|
<div class="container">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<fieldset class="m-0" :disabled="isTesting">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.connectionName') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="localConnection.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.client') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<select v-model="localConnection.client" class="form-select">
|
||||||
|
<option value="mysql">
|
||||||
|
MySQL
|
||||||
|
</option>
|
||||||
|
<option value="maria">
|
||||||
|
MariaDB
|
||||||
|
</option>
|
||||||
|
<option value="pg">
|
||||||
|
PostgreSQL
|
||||||
|
</option>
|
||||||
|
<!-- <option value="mssql">
|
||||||
|
Microsoft SQL
|
||||||
|
</option>
|
||||||
|
<option value="oracledb">
|
||||||
|
Oracle DB
|
||||||
|
</option> -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.host"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.port') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.port"
|
||||||
|
class="form-input"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="65535"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.database" class="form-group">
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.database') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.database"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.user') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.user"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
:disabled="localConnection.ask"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.password') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.password"
|
||||||
|
class="form-input"
|
||||||
|
type="password"
|
||||||
|
:disabled="localConnection.ask"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4 col-sm-12" />
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<label class="form-checkbox form-inline">
|
||||||
|
<input v-model="localConnection.ask" type="checkbox"><i class="form-icon" /> {{ $t('message.askCredentials') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<BaseToast
|
||||||
|
class="mb-2"
|
||||||
|
:message="toast.message"
|
||||||
|
:status="toast.status"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedTab === 'ssl'" class="panel-body py-0">
|
||||||
|
<div class="container">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('message.enableSsl') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<label class="form-switch d-inline-block" @click.prevent="toggleSsl">
|
||||||
|
<input type="checkbox" :checked="localConnection.ssl">
|
||||||
|
<i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<fieldset class="m-0" :disabled="isTesting || !localConnection.ssl">
|
||||||
<input
|
<div class="form-group">
|
||||||
ref="firstInput"
|
<div class="col-4 col-sm-12">
|
||||||
v-model="localConnection.name"
|
<label class="form-label">{{ $t('word.privateKey') }}</label>
|
||||||
class="form-input"
|
</div>
|
||||||
type="text"
|
<div class="col-8 col-sm-12">
|
||||||
>
|
<BaseUploadInput
|
||||||
</div>
|
:value="localConnection.key"
|
||||||
</div>
|
:message="$t('word.browse')"
|
||||||
<div class="form-group">
|
@clear="pathClear('key')"
|
||||||
<div class="col-4 col-sm-12">
|
@change="pathSelection($event, 'key')"
|
||||||
<label class="form-label">{{ $t('word.client') }}</label>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
</div>
|
||||||
<select v-model="localConnection.client" class="form-select">
|
<div class="form-group">
|
||||||
<option value="mysql">
|
<div class="col-4 col-sm-12">
|
||||||
MySQL
|
<label class="form-label">{{ $t('word.certificate') }}</label>
|
||||||
</option>
|
</div>
|
||||||
<option value="maria">
|
<div class="col-8 col-sm-12">
|
||||||
MariaDB
|
<BaseUploadInput
|
||||||
</option>
|
:value="localConnection.cert"
|
||||||
<!-- <option value="mssql">
|
:message="$t('word.browse')"
|
||||||
Microsoft SQL
|
@clear="pathClear('cert')"
|
||||||
</option>
|
@change="pathSelection($event, 'cert')"
|
||||||
<option value="pg">
|
/>
|
||||||
PostgreSQL
|
</div>
|
||||||
</option>
|
</div>
|
||||||
<option value="oracledb">
|
<div class="form-group">
|
||||||
Oracle DB
|
<div class="col-4 col-sm-12">
|
||||||
</option> -->
|
<label class="form-label">{{ $t('word.caCertificate') }}</label>
|
||||||
</select>
|
</div>
|
||||||
</div>
|
<div class="col-8 col-sm-12">
|
||||||
</div>
|
<BaseUploadInput
|
||||||
<div class="form-group">
|
:value="localConnection.ca"
|
||||||
<div class="col-4 col-sm-12">
|
:message="$t('word.browse')"
|
||||||
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
@clear="pathClear('ca')"
|
||||||
</div>
|
@change="pathSelection($event, 'ca')"
|
||||||
<div class="col-8 col-sm-12">
|
/>
|
||||||
<input
|
</div>
|
||||||
v-model="localConnection.host"
|
</div>
|
||||||
class="form-input"
|
|
||||||
type="text"
|
<div class="form-group">
|
||||||
>
|
<div class="col-4 col-sm-12">
|
||||||
</div>
|
<label class="form-label">{{ $t('word.ciphers') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="col-8 col-sm-12">
|
||||||
<div class="col-4 col-sm-12">
|
<input
|
||||||
<label class="form-label">{{ $t('word.port') }}</label>
|
ref="firstInput"
|
||||||
</div>
|
v-model="localConnection.ciphers"
|
||||||
<div class="col-8 col-sm-12">
|
class="form-input"
|
||||||
<input
|
type="text"
|
||||||
v-model="localConnection.port"
|
>
|
||||||
class="form-input"
|
</div>
|
||||||
type="number"
|
</div>
|
||||||
min="1"
|
</fieldset>
|
||||||
max="65535"
|
</form>
|
||||||
>
|
</div>
|
||||||
</div>
|
<BaseToast
|
||||||
</div>
|
class="mb-2"
|
||||||
<div class="form-group">
|
:message="toast.message"
|
||||||
<div class="col-4 col-sm-12">
|
:status="toast.status"
|
||||||
<label class="form-label">{{ $t('word.user') }}</label>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<div class="modal-footer text-light">
|
||||||
<input
|
<button
|
||||||
v-model="localConnection.user"
|
class="btn btn-gray mr-2"
|
||||||
class="form-input"
|
:class="{'loading': isTesting}"
|
||||||
type="text"
|
@click="startTest"
|
||||||
:disabled="localConnection.ask"
|
>
|
||||||
>
|
{{ $t('message.testConnection') }}
|
||||||
</div>
|
</button>
|
||||||
</div>
|
<button class="btn btn-primary mr-2" @click="saveEditConnection">
|
||||||
<div class="form-group">
|
{{ $t('word.save') }}
|
||||||
<div class="col-4 col-sm-12">
|
</button>
|
||||||
<label class="form-label">{{ $t('word.password') }}</label>
|
<button class="btn btn-link" @click="closeModal">
|
||||||
</div>
|
{{ $t('word.close') }}
|
||||||
<div class="col-8 col-sm-12">
|
</button>
|
||||||
<input
|
</div>
|
||||||
v-model="localConnection.password"
|
|
||||||
class="form-input"
|
|
||||||
type="password"
|
|
||||||
:disabled="localConnection.ask"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12" />
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<label class="form-checkbox form-inline">
|
|
||||||
<input v-model="localConnection.ask" type="checkbox"><i class="form-icon" /> {{ $t('message.askCredentials') }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
<BaseToast
|
<ModalAskCredentials
|
||||||
class="mb-2"
|
v-if="isAsking"
|
||||||
:message="toast.message"
|
@close-asking="closeAsking"
|
||||||
:status="toast.status"
|
@credentials="continueTest"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer text-light">
|
|
||||||
<button
|
|
||||||
class="btn btn-gray mr-2"
|
|
||||||
:class="{'loading': isTesting}"
|
|
||||||
@click="startTest"
|
|
||||||
>
|
|
||||||
{{ $t('message.testConnection') }}
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-primary mr-2" @click="saveEditConnection">
|
|
||||||
{{ $t('word.save') }}
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-link" @click="closeModal">
|
|
||||||
{{ $t('word.close') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<ModalAskCredentials
|
|
||||||
v-if="isAsking"
|
|
||||||
@close-asking="closeAsking"
|
|
||||||
@credentials="continueTest"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapActions } from 'vuex';
|
import { mapActions } from 'vuex';
|
||||||
|
import customizations from 'common/customizations';
|
||||||
import Connection from '@/ipc-api/Connection';
|
import Connection from '@/ipc-api/Connection';
|
||||||
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
||||||
import BaseToast from '@/components/BaseToast';
|
import BaseToast from '@/components/BaseToast';
|
||||||
|
import BaseUploadInput from '@/components/BaseUploadInput';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ModalEditConnection',
|
name: 'ModalEditConnection',
|
||||||
components: {
|
components: {
|
||||||
ModalAskCredentials,
|
ModalAskCredentials,
|
||||||
BaseToast
|
BaseToast,
|
||||||
|
BaseUploadInput
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object
|
connection: Object
|
||||||
@@ -167,9 +283,15 @@ export default {
|
|||||||
},
|
},
|
||||||
isTesting: false,
|
isTesting: false,
|
||||||
isAsking: false,
|
isAsking: false,
|
||||||
localConnection: null
|
localConnection: null,
|
||||||
|
selectedTab: 'general'
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
customizations () {
|
||||||
|
return customizations[this.connection.client];
|
||||||
|
}
|
||||||
|
},
|
||||||
created () {
|
created () {
|
||||||
this.localConnection = Object.assign({}, this.connection);
|
this.localConnection = Object.assign({}, this.connection);
|
||||||
window.addEventListener('keydown', this.onKey);
|
window.addEventListener('keydown', this.onKey);
|
||||||
@@ -240,6 +362,21 @@ export default {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.key === 'Escape')
|
if (e.key === 'Escape')
|
||||||
this.closeModal();
|
this.closeModal();
|
||||||
|
},
|
||||||
|
selectTab (tab) {
|
||||||
|
this.selectedTab = tab;
|
||||||
|
},
|
||||||
|
toggleSsl () {
|
||||||
|
this.localConnection.ssl = !this.localConnection.ssl;
|
||||||
|
},
|
||||||
|
pathSelection (event, name) {
|
||||||
|
const { files } = event.target;
|
||||||
|
if (!files.length) return;
|
||||||
|
|
||||||
|
this.localConnection[name] = files[0].path;
|
||||||
|
},
|
||||||
|
pathClear (name) {
|
||||||
|
this.localConnection[name] = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -247,6 +384,8 @@ export default {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.modal-container {
|
.modal-container {
|
||||||
|
position: absolute;
|
||||||
max-width: 450px;
|
max-width: 450px;
|
||||||
|
top: 17.5vh;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-database-edit mr-1" /> {{ $t('message.editDatabase') }}
|
<i class="mdi mdi-24px mdi-database-edit mr-1" /> {{ $t('message.editSchema') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
class="form-input"
|
class="form-input"
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
:placeholder="$t('message.databaseName')"
|
:placeholder="$t('message.schemaName')"
|
||||||
readonly
|
readonly
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer text-light">
|
<div class="modal-footer text-light">
|
||||||
<button class="btn btn-primary mr-2" @click.stop="updateDatabase">
|
<button class="btn btn-primary mr-2" @click.stop="updateSchema">
|
||||||
{{ $t('word.update') }}
|
{{ $t('word.update') }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-link" @click.stop="closeModal">
|
<button class="btn btn-link" @click.stop="closeModal">
|
||||||
@@ -66,10 +66,10 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import Database from '@/ipc-api/Database';
|
import Schema from '@/ipc-api/Schema';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ModalEditDatabase',
|
name: 'ModalEditSchema',
|
||||||
props: {
|
props: {
|
||||||
selectedDatabase: String
|
selectedDatabase: String
|
||||||
},
|
},
|
||||||
@@ -98,7 +98,7 @@ export default {
|
|||||||
async created () {
|
async created () {
|
||||||
let actualCollation;
|
let actualCollation;
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Database.getDatabaseCollation({ uid: this.selectedWorkspace, database: this.selectedDatabase });
|
const { status, response } = await Schema.getDatabaseCollation({ uid: this.selectedWorkspace, database: this.selectedDatabase });
|
||||||
|
|
||||||
if (status === 'success')
|
if (status === 'success')
|
||||||
actualCollation = response;
|
actualCollation = response;
|
||||||
@@ -130,10 +130,10 @@ export default {
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification'
|
addNotification: 'notifications/addNotification'
|
||||||
}),
|
}),
|
||||||
async updateDatabase () {
|
async updateSchema () {
|
||||||
if (this.database.collation !== this.database.prevCollation) {
|
if (this.database.collation !== this.database.prevCollation) {
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Database.updateDatabase({
|
const { status, response } = await Schema.updateSchema({
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
...this.database
|
...this.database
|
||||||
});
|
});
|
||||||
392
src/renderer/components/ModalFakerRows.vue
Normal file
392
src/renderer/components/ModalFakerRows.vue
Normal file
@@ -0,0 +1,392 @@
|
|||||||
|
<template>
|
||||||
|
<div class="modal active">
|
||||||
|
<a class="modal-overlay" @click.stop="closeModal" />
|
||||||
|
<div class="modal-container p-0">
|
||||||
|
<div class="modal-header pl-2">
|
||||||
|
<div class="modal-title h6">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-playlist-plus mr-1" /> {{ $t('message.tableFiller') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
|
</div>
|
||||||
|
<div class="modal-body pb-0">
|
||||||
|
<div class="content">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<fieldset :disabled="isInserting">
|
||||||
|
<div
|
||||||
|
v-for="field in fields"
|
||||||
|
:key="field.name"
|
||||||
|
class="form-group"
|
||||||
|
>
|
||||||
|
<div class="col-3 col-sm-12">
|
||||||
|
<label class="form-label" :title="field.name">{{ field.name }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column columns col-sm-12">
|
||||||
|
<FakerSelect
|
||||||
|
:type="field.type"
|
||||||
|
class="column columns pr-0"
|
||||||
|
:is-checked="!fieldsToExclude.includes(field.name)"
|
||||||
|
:foreign-keys="foreignKeys"
|
||||||
|
:key-usage="keyUsage"
|
||||||
|
:field="field"
|
||||||
|
:field-length="fieldLength(field)"
|
||||||
|
:field-obj="localRow[field.name]"
|
||||||
|
:value.sync="localRow[field.name]"
|
||||||
|
>
|
||||||
|
<span class="input-group-addon field-type" :class="typeClass(field.type)">
|
||||||
|
{{ field.type }} {{ fieldLength(field) | wrapNumber }}
|
||||||
|
</span>
|
||||||
|
<label class="form-checkbox ml-3" :title="$t('word.insert')">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
:checked="!field.autoIncrement"
|
||||||
|
@change.prevent="toggleFields($event, field)"
|
||||||
|
><i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</FakerSelect>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer text-light columns">
|
||||||
|
<div class="column d-flex" :class="hasFakes ? 'col-4' : 'col-2'">
|
||||||
|
<div class="input-group tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')">
|
||||||
|
<input
|
||||||
|
v-model="nInserts"
|
||||||
|
type="number"
|
||||||
|
class="form-input"
|
||||||
|
min="1"
|
||||||
|
:disabled="isInserting"
|
||||||
|
>
|
||||||
|
<span class="input-group-addon">
|
||||||
|
<i class="mdi mdi-24px mdi-repeat" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="hasFakes"
|
||||||
|
class="tooltip tooltip-right ml-2"
|
||||||
|
:data-tooltip="$t('message.fakeDataLanguage')"
|
||||||
|
>
|
||||||
|
<select v-model="fakerLocale" class="form-select">
|
||||||
|
<option value="ar">
|
||||||
|
Arabic
|
||||||
|
</option><option value="az">
|
||||||
|
Azerbaijani
|
||||||
|
</option><option value="zh_CN">
|
||||||
|
Chinese
|
||||||
|
</option><option value="zh_TW">
|
||||||
|
Chinese (Taiwan)
|
||||||
|
</option><option value="cz">
|
||||||
|
Czech
|
||||||
|
</option><option value="nl">
|
||||||
|
Dutch
|
||||||
|
</option><option value="nl_BE">
|
||||||
|
Dutch (Belgium)
|
||||||
|
</option><option value="en">
|
||||||
|
English
|
||||||
|
</option><option value="en_AU_ocker">
|
||||||
|
English (Australia Ocker)
|
||||||
|
</option><option value="en_AU">
|
||||||
|
English (Australia)
|
||||||
|
</option><option value="en_BORK">
|
||||||
|
English (Bork)
|
||||||
|
</option><option value="en_CA">
|
||||||
|
English (Canada)
|
||||||
|
</option><option value="en_GB">
|
||||||
|
English (Great Britain)
|
||||||
|
</option><option value="en_IND">
|
||||||
|
English (India)
|
||||||
|
</option><option value="en_IE">
|
||||||
|
English (Ireland)
|
||||||
|
</option><option value="en_ZA">
|
||||||
|
English (South Africa)
|
||||||
|
</option><option value="en_US">
|
||||||
|
English (United States)
|
||||||
|
</option><option value="fa">
|
||||||
|
Farsi
|
||||||
|
</option><option value="fi">
|
||||||
|
Finnish
|
||||||
|
</option><option value="fr">
|
||||||
|
French
|
||||||
|
</option><option value="fr_CA">
|
||||||
|
French (Canada)
|
||||||
|
</option><option value="fr_CH">
|
||||||
|
French (Switzerland)
|
||||||
|
</option><option value="ge">
|
||||||
|
Georgian
|
||||||
|
</option><option value="de">
|
||||||
|
German
|
||||||
|
</option><option value="de_AT">
|
||||||
|
German (Austria)
|
||||||
|
</option><option value="de_CH">
|
||||||
|
German (Switzerland)
|
||||||
|
</option><option value="hr">
|
||||||
|
Hrvatski
|
||||||
|
</option><option value="id_ID">
|
||||||
|
Indonesia
|
||||||
|
</option><option value="it">
|
||||||
|
Italian
|
||||||
|
</option><option value="ja">
|
||||||
|
Japanese
|
||||||
|
</option><option value="ko">
|
||||||
|
Korean
|
||||||
|
</option><option value="nep">
|
||||||
|
Nepalese
|
||||||
|
</option><option value="nb_NO">
|
||||||
|
Norwegian
|
||||||
|
</option><option value="pl">
|
||||||
|
Polish
|
||||||
|
</option><option value="pt_BR">
|
||||||
|
Portuguese (Brazil)
|
||||||
|
</option><option value="pt_PT">
|
||||||
|
Portuguese (Portugal)
|
||||||
|
</option><option value="ro">
|
||||||
|
Romanian
|
||||||
|
</option><option value="ru">
|
||||||
|
Russian
|
||||||
|
</option><option value="sk">
|
||||||
|
Slovakian
|
||||||
|
</option><option value="es">
|
||||||
|
Spanish
|
||||||
|
</option><option value="es_MX">
|
||||||
|
Spanish (Mexico)
|
||||||
|
</option><option value="sv">
|
||||||
|
Swedish
|
||||||
|
</option><option value="tr">
|
||||||
|
Turkish
|
||||||
|
</option><option value="uk">
|
||||||
|
Ukrainian
|
||||||
|
</option><option value="vi">
|
||||||
|
Vietnamese
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-auto">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary mr-2"
|
||||||
|
:class="{'loading': isInserting}"
|
||||||
|
@click.stop="insertRows"
|
||||||
|
>
|
||||||
|
{{ $t('word.insert') }}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-link" @click.stop="closeModal">
|
||||||
|
{{ $t('word.close') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import moment from 'moment';
|
||||||
|
import { TEXT, LONG_TEXT, NUMBER, FLOAT, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import Tables from '@/ipc-api/Tables';
|
||||||
|
import FakerSelect from '@/components/FakerSelect';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalFakerRows',
|
||||||
|
components: {
|
||||||
|
FakerSelect
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
wrapNumber (num) {
|
||||||
|
if (!num) return '';
|
||||||
|
return `(${num})`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
tabUid: [String, Number],
|
||||||
|
fields: Array,
|
||||||
|
keyUsage: Array
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
localRow: {},
|
||||||
|
fieldsToExclude: [],
|
||||||
|
nInserts: 1,
|
||||||
|
isInserting: false,
|
||||||
|
fakerLocale: 'en'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
|
getWorkspace: 'workspaces/getWorkspace',
|
||||||
|
getWorkspaceTab: 'workspaces/getWorkspaceTab'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.selectedWorkspace);
|
||||||
|
},
|
||||||
|
foreignKeys () {
|
||||||
|
return this.keyUsage.map(key => key.field);
|
||||||
|
},
|
||||||
|
hasFakes () {
|
||||||
|
return Object.keys(this.localRow).some(field => 'group' in this.localRow[field] && this.localRow[field].group !== 'manual');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
nInserts (val) {
|
||||||
|
if (!val || val < 1)
|
||||||
|
this.nInserts = 1;
|
||||||
|
else if (val > 1000)
|
||||||
|
this.nInserts = 1000;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
const rowObj = {};
|
||||||
|
|
||||||
|
for (const field of this.fields) {
|
||||||
|
let fieldDefault;
|
||||||
|
|
||||||
|
if (field.default === 'NULL') fieldDefault = null;
|
||||||
|
else {
|
||||||
|
if ([...NUMBER, ...FLOAT].includes(field.type))
|
||||||
|
fieldDefault = +field.default;
|
||||||
|
|
||||||
|
if ([...TEXT, ...LONG_TEXT].includes(field.type))
|
||||||
|
fieldDefault = field.default ? field.default.substring(1, field.default.length - 1) : '';
|
||||||
|
|
||||||
|
if ([...TIME, ...DATE].includes(field.type))
|
||||||
|
fieldDefault = field.default;
|
||||||
|
|
||||||
|
if (BIT.includes(field.type))
|
||||||
|
fieldDefault = field.default.replaceAll('\'', '').replaceAll('b', '');
|
||||||
|
|
||||||
|
if (DATETIME.includes(field.type)) {
|
||||||
|
if (field.default && field.default.toLowerCase().includes('current_timestamp')) {
|
||||||
|
let datePrecision = '';
|
||||||
|
for (let i = 0; i < field.datePrecision; i++)
|
||||||
|
datePrecision += i === 0 ? '.S' : 'S';
|
||||||
|
fieldDefault = moment().format(`YYYY-MM-DD HH:mm:ss${datePrecision}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rowObj[field.name] = { value: fieldDefault };
|
||||||
|
|
||||||
|
if (field.autoIncrement)// Disable by default auto increment fields
|
||||||
|
this.fieldsToExclude = [...this.fieldsToExclude, field.name];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.localRow = { ...rowObj };
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification'
|
||||||
|
}),
|
||||||
|
typeClass (type) {
|
||||||
|
if (type)
|
||||||
|
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
async insertRows () {
|
||||||
|
this.isInserting = true;
|
||||||
|
const rowToInsert = this.localRow;
|
||||||
|
|
||||||
|
Object.keys(rowToInsert).forEach(key => {
|
||||||
|
if (this.fieldsToExclude.includes(key))
|
||||||
|
delete rowToInsert[key];
|
||||||
|
|
||||||
|
if (typeof rowToInsert[key] === 'undefined')
|
||||||
|
delete rowToInsert[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
const fieldTypes = {};
|
||||||
|
this.fields.forEach(field => {
|
||||||
|
fieldTypes[field.name] = field.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Tables.insertTableFakeRows({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
schema: this.workspace.breadcrumbs.schema,
|
||||||
|
table: this.workspace.breadcrumbs.table,
|
||||||
|
row: rowToInsert,
|
||||||
|
repeat: this.nInserts,
|
||||||
|
fields: fieldTypes,
|
||||||
|
locale: this.fakerLocale
|
||||||
|
});
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
this.closeModal();
|
||||||
|
this.$emit('reload');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isInserting = false;
|
||||||
|
},
|
||||||
|
closeModal () {
|
||||||
|
this.$emit('hide');
|
||||||
|
},
|
||||||
|
fieldLength (field) {
|
||||||
|
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
||||||
|
else if (TEXT.includes(field.type)) return field.charLength;
|
||||||
|
return field.length;
|
||||||
|
},
|
||||||
|
toggleFields (event, field) {
|
||||||
|
if (event.target.checked)
|
||||||
|
this.fieldsToExclude = this.fieldsToExclude.filter(f => f !== field.name);
|
||||||
|
else
|
||||||
|
this.fieldsToExclude = [...this.fieldsToExclude, field.name];
|
||||||
|
},
|
||||||
|
filesChange (event, field) {
|
||||||
|
const { files } = event.target;
|
||||||
|
if (!files.length) return;
|
||||||
|
|
||||||
|
this.localRow[field] = files[0].path;
|
||||||
|
},
|
||||||
|
getKeyUsage (keyName) {
|
||||||
|
return this.keyUsage.find(key => key.field === keyName);
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape')
|
||||||
|
this.closeModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.modal-container {
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: normal;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group-addon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-type {
|
||||||
|
font-size: 0.6rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -10,119 +10,232 @@
|
|||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click="closeModal" />
|
<a class="btn btn-clear c-hand" @click="closeModal" />
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body pb-0">
|
<div class="modal-body p-0">
|
||||||
<div class="content">
|
<div class="panel">
|
||||||
<form class="form-horizontal">
|
<div class="panel-nav">
|
||||||
<fieldset class="m-0" :disabled="isTesting">
|
<ul class="tab tab-block">
|
||||||
<div class="form-group">
|
<li
|
||||||
<div class="col-4 col-sm-12">
|
class="tab-item"
|
||||||
<label class="form-label">{{ $t('word.connectionName') }}</label>
|
:class="{'active': selectedTab === 'general'}"
|
||||||
|
@click="selectTab('general')"
|
||||||
|
>
|
||||||
|
<a class="c-hand">{{ $t('word.general') }}</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="tab-item"
|
||||||
|
:class="{'active': selectedTab === 'ssl'}"
|
||||||
|
@click="selectTab('ssl')"
|
||||||
|
>
|
||||||
|
<a class="c-hand">{{ $t('word.ssl') }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedTab === 'general'" class="panel-body py-0">
|
||||||
|
<div class="container">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<fieldset class="m-0" :disabled="isTesting">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.connectionName') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="connection.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.client') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<select
|
||||||
|
v-model="connection.client"
|
||||||
|
class="form-select"
|
||||||
|
@change="setDefaults"
|
||||||
|
>
|
||||||
|
<option value="mysql">
|
||||||
|
MySQL
|
||||||
|
</option>
|
||||||
|
<option value="maria">
|
||||||
|
MariaDB
|
||||||
|
</option>
|
||||||
|
<option value="pg">
|
||||||
|
PostgreSQL
|
||||||
|
</option>
|
||||||
|
<!-- <option value="mssql">
|
||||||
|
Microsoft SQL
|
||||||
|
</option>
|
||||||
|
<option value="oracledb">
|
||||||
|
Oracle DB
|
||||||
|
</option> -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.host"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.port') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.port"
|
||||||
|
class="form-input"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="65535"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.database" class="form-group">
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.database') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.database"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.user') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.user"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
:disabled="connection.ask"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.password') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.password"
|
||||||
|
class="form-input"
|
||||||
|
type="password"
|
||||||
|
:disabled="connection.ask"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4 col-sm-12" />
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<label class="form-checkbox form-inline">
|
||||||
|
<input v-model="connection.ask" type="checkbox"><i class="form-icon" /> {{ $t('message.askCredentials') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<BaseToast
|
||||||
|
class="mb-2"
|
||||||
|
:message="toast.message"
|
||||||
|
:status="toast.status"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedTab === 'ssl'" class="panel-body py-0">
|
||||||
|
<div class="container">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-4 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('message.enableSsl') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8 col-sm-12">
|
||||||
|
<label class="form-switch d-inline-block" @click.prevent="toggleSsl">
|
||||||
|
<input type="checkbox" :checked="connection.ssl">
|
||||||
|
<i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
<fieldset class="m-0" :disabled="isTesting || !connection.ssl">
|
||||||
<input
|
<div class="form-group">
|
||||||
ref="firstInput"
|
<div class="col-4 col-sm-12">
|
||||||
v-model="connection.name"
|
<label class="form-label">{{ $t('word.privateKey') }}</label>
|
||||||
class="form-input"
|
</div>
|
||||||
type="text"
|
<div class="col-8 col-sm-12">
|
||||||
>
|
<BaseUploadInput
|
||||||
</div>
|
:value="connection.key"
|
||||||
</div>
|
:message="$t('word.browse')"
|
||||||
<div class="form-group">
|
@clear="pathClear('key')"
|
||||||
<div class="col-4 col-sm-12">
|
@change="pathSelection($event, 'key')"
|
||||||
<label class="form-label">{{ $t('word.client') }}</label>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
</div>
|
||||||
<select
|
<div class="form-group">
|
||||||
v-model="connection.client"
|
<div class="col-4 col-sm-12">
|
||||||
class="form-select"
|
<label class="form-label">{{ $t('word.certificate') }}</label>
|
||||||
@change="setDefaults"
|
</div>
|
||||||
>
|
<div class="col-8 col-sm-12">
|
||||||
<option value="mysql">
|
<BaseUploadInput
|
||||||
MySQL
|
:value="connection.cert"
|
||||||
</option>
|
:message="$t('word.browse')"
|
||||||
<option value="maria">
|
@clear="pathClear('cert')"
|
||||||
MariaDB
|
@change="pathSelection($event, 'cert')"
|
||||||
</option>
|
/>
|
||||||
<!-- <option value="mssql">
|
</div>
|
||||||
Microsoft SQL
|
</div>
|
||||||
</option>
|
<div class="form-group">
|
||||||
<option value="pg">
|
<div class="col-4 col-sm-12">
|
||||||
PostgreSQL
|
<label class="form-label">{{ $t('word.caCertificate') }}</label>
|
||||||
</option>
|
</div>
|
||||||
<option value="oracledb">
|
<div class="col-8 col-sm-12">
|
||||||
Oracle DB
|
<BaseUploadInput
|
||||||
</option> -->
|
:value="connection.ca"
|
||||||
</select>
|
:message="$t('word.browse')"
|
||||||
</div>
|
@clear="pathClear('ca')"
|
||||||
</div>
|
@change="pathSelection($event, 'ca')"
|
||||||
<div class="form-group">
|
/>
|
||||||
<div class="col-4 col-sm-12">
|
</div>
|
||||||
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
<div class="form-group">
|
||||||
<input
|
<div class="col-4 col-sm-12">
|
||||||
v-model="connection.host"
|
<label class="form-label">{{ $t('word.ciphers') }}</label>
|
||||||
class="form-input"
|
</div>
|
||||||
type="text"
|
<div class="col-8 col-sm-12">
|
||||||
>
|
<input
|
||||||
</div>
|
ref="firstInput"
|
||||||
</div>
|
v-model="connection.ciphers"
|
||||||
<div class="form-group">
|
class="form-input"
|
||||||
<div class="col-4 col-sm-12">
|
type="text"
|
||||||
<label class="form-label">{{ $t('word.port') }}</label>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-8 col-sm-12">
|
</div>
|
||||||
<input
|
</fieldset>
|
||||||
v-model="connection.port"
|
</form>
|
||||||
class="form-input"
|
</div>
|
||||||
type="number"
|
<BaseToast
|
||||||
min="1"
|
class="mb-2"
|
||||||
max="65535"
|
:message="toast.message"
|
||||||
>
|
:status="toast.status"
|
||||||
</div>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.user') }}</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<input
|
|
||||||
v-model="connection.user"
|
|
||||||
class="form-input"
|
|
||||||
type="text"
|
|
||||||
:disabled="connection.ask"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.password') }}</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<input
|
|
||||||
v-model="connection.password"
|
|
||||||
class="form-input"
|
|
||||||
type="password"
|
|
||||||
:disabled="connection.ask"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12" />
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<label class="form-checkbox form-inline">
|
|
||||||
<input v-model="connection.ask" type="checkbox"><i class="form-icon" /> {{ $t('message.askCredentials') }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
<BaseToast
|
|
||||||
class="mb-2"
|
|
||||||
:message="toast.message"
|
|
||||||
:status="toast.status"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer text-light">
|
<div class="modal-footer text-light">
|
||||||
<button
|
<button
|
||||||
@@ -150,16 +263,19 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapActions } from 'vuex';
|
import { mapActions } from 'vuex';
|
||||||
|
import customizations from 'common/customizations';
|
||||||
import Connection from '@/ipc-api/Connection';
|
import Connection from '@/ipc-api/Connection';
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
||||||
import BaseToast from '@/components/BaseToast';
|
import BaseToast from '@/components/BaseToast';
|
||||||
|
import BaseUploadInput from '@/components/BaseUploadInput';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ModalNewConnection',
|
name: 'ModalNewConnection',
|
||||||
components: {
|
components: {
|
||||||
ModalAskCredentials,
|
ModalAskCredentials,
|
||||||
BaseToast
|
BaseToast,
|
||||||
|
BaseUploadInput
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -167,21 +283,35 @@ export default {
|
|||||||
name: '',
|
name: '',
|
||||||
client: 'mysql',
|
client: 'mysql',
|
||||||
host: '127.0.0.1',
|
host: '127.0.0.1',
|
||||||
port: '3306',
|
database: null,
|
||||||
user: 'root',
|
port: null,
|
||||||
|
user: null,
|
||||||
password: '',
|
password: '',
|
||||||
ask: false,
|
ask: false,
|
||||||
uid: uidGen('C')
|
uid: uidGen('C'),
|
||||||
|
ssl: false,
|
||||||
|
cert: '',
|
||||||
|
key: '',
|
||||||
|
ca: '',
|
||||||
|
ciphers: ''
|
||||||
|
|
||||||
},
|
},
|
||||||
toast: {
|
toast: {
|
||||||
status: '',
|
status: '',
|
||||||
message: ''
|
message: ''
|
||||||
},
|
},
|
||||||
isTesting: false,
|
isTesting: false,
|
||||||
isAsking: false
|
isAsking: false,
|
||||||
|
selectedTab: 'general'
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
customizations () {
|
||||||
|
return customizations[this.connection.client];
|
||||||
|
}
|
||||||
|
},
|
||||||
created () {
|
created () {
|
||||||
|
this.setDefaults();
|
||||||
window.addEventListener('keydown', this.onKey);
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -197,20 +327,9 @@ export default {
|
|||||||
addConnection: 'connections/addConnection'
|
addConnection: 'connections/addConnection'
|
||||||
}),
|
}),
|
||||||
setDefaults () {
|
setDefaults () {
|
||||||
switch (this.connection.client) {
|
this.connection.user = this.customizations.defaultUser;
|
||||||
case 'mysql':
|
this.connection.port = this.customizations.defaultPort;
|
||||||
this.connection.port = '3306';
|
this.connection.database = this.customizations.defaultDatabase;
|
||||||
break;
|
|
||||||
case 'mssql':
|
|
||||||
this.connection.port = '1433';
|
|
||||||
break;
|
|
||||||
case 'pg':
|
|
||||||
this.connection.port = '5432';
|
|
||||||
break;
|
|
||||||
case 'oracledb':
|
|
||||||
this.connection.port = '1521';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
async startTest () {
|
async startTest () {
|
||||||
this.isTesting = true;
|
this.isTesting = true;
|
||||||
@@ -265,6 +384,21 @@ export default {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.key === 'Escape')
|
if (e.key === 'Escape')
|
||||||
this.closeModal();
|
this.closeModal();
|
||||||
|
},
|
||||||
|
selectTab (tab) {
|
||||||
|
this.selectedTab = tab;
|
||||||
|
},
|
||||||
|
toggleSsl () {
|
||||||
|
this.connection.ssl = !this.connection.ssl;
|
||||||
|
},
|
||||||
|
pathSelection (event, name) {
|
||||||
|
const { files } = event.target;
|
||||||
|
if (!files.length) return;
|
||||||
|
|
||||||
|
this.connection[name] = files[0].path;
|
||||||
|
},
|
||||||
|
pathClear (name) {
|
||||||
|
this.connection[name] = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -272,6 +406,8 @@ export default {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.modal-container {
|
.modal-container {
|
||||||
|
position: absolute;
|
||||||
max-width: 450px;
|
max-width: 450px;
|
||||||
|
top: 17.5vh;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-plus mr-1" /> {{ $t('message.createNewRoutine') }}
|
<i class="mdi mdi-24px mdi-plus mr-1" /> {{ $t('message.createNewFunction') }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -25,7 +25,19 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.languages" class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.language') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="localFunction.language" class="form-select">
|
||||||
|
<option v-for="language in customizations.languages" :key="language">
|
||||||
|
{{ language }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.definer" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.definer') }}
|
{{ $t('word.definer') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -53,42 +65,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.comment" class="form-group">
|
||||||
<label class="form-label col-4">
|
|
||||||
{{ $t('word.returns') }}
|
|
||||||
</label>
|
|
||||||
<div class="column">
|
|
||||||
<div class="input-group">
|
|
||||||
<select
|
|
||||||
v-model="localFunction.returns"
|
|
||||||
class="form-select text-uppercase"
|
|
||||||
style="width: 0;"
|
|
||||||
>
|
|
||||||
<optgroup
|
|
||||||
v-for="group in workspace.dataTypes"
|
|
||||||
:key="group.group"
|
|
||||||
:label="group.group"
|
|
||||||
>
|
|
||||||
<option
|
|
||||||
v-for="type in group.types"
|
|
||||||
:key="type.name"
|
|
||||||
:selected="localFunction.returns === type.name"
|
|
||||||
:value="type.name"
|
|
||||||
>
|
|
||||||
{{ type.name }}
|
|
||||||
</option>
|
|
||||||
</optgroup>
|
|
||||||
</select>
|
|
||||||
<input
|
|
||||||
v-model="localFunction.returnsLength"
|
|
||||||
class="form-input"
|
|
||||||
type="number"
|
|
||||||
min="0"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.comment') }}
|
{{ $t('word.comment') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -111,7 +88,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.functionDataAccess" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('message.dataAccess') }}
|
{{ $t('message.dataAccess') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -124,7 +101,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.functionDeterministic" class="form-group">
|
||||||
<div class="col-4" />
|
<div class="col-4" />
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<label class="form-checkbox form-inline">
|
<label class="form-checkbox form-inline">
|
||||||
@@ -152,11 +129,12 @@ export default {
|
|||||||
return {
|
return {
|
||||||
localFunction: {
|
localFunction: {
|
||||||
definer: '',
|
definer: '',
|
||||||
sql: 'BEGIN\r\n RETURN NULL;\r\nEND',
|
sql: '',
|
||||||
parameters: [],
|
parameters: [],
|
||||||
name: '',
|
name: '',
|
||||||
comment: '',
|
comment: '',
|
||||||
returns: 'INT',
|
language: null,
|
||||||
|
returns: null,
|
||||||
returnsLength: 10,
|
returnsLength: 10,
|
||||||
security: 'DEFINER',
|
security: 'DEFINER',
|
||||||
deterministic: false,
|
deterministic: false,
|
||||||
@@ -168,9 +146,17 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
schema () {
|
schema () {
|
||||||
return this.workspace.breadcrumbs.schema;
|
return this.workspace.breadcrumbs.schema;
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace.customizations;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
|
if (this.customizations.languages)
|
||||||
|
this.localFunction.language = this.customizations.languages[0];
|
||||||
|
|
||||||
|
if (this.customizations.procedureSql)
|
||||||
|
this.localFunction.sql = this.customizations.procedureSql;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$refs.firstInput.focus();
|
this.$refs.firstInput.focus();
|
||||||
}, 20);
|
}, 20);
|
||||||
|
|||||||
@@ -25,7 +25,19 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.languages" class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.language') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="localRoutine.language" class="form-select">
|
||||||
|
<option v-for="language in customizations.languages" :key="language">
|
||||||
|
{{ language }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.definer" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.definer') }}
|
{{ $t('word.definer') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -53,7 +65,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.comment" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.comment') }}
|
{{ $t('word.comment') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -76,7 +88,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.comment" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('message.dataAccess') }}
|
{{ $t('message.dataAccess') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -89,7 +101,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.procedureDeterministic" class="form-group">
|
||||||
<div class="col-4" />
|
<div class="col-4" />
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<label class="form-checkbox form-inline">
|
<label class="form-checkbox form-inline">
|
||||||
@@ -117,10 +129,11 @@ export default {
|
|||||||
return {
|
return {
|
||||||
localRoutine: {
|
localRoutine: {
|
||||||
definer: '',
|
definer: '',
|
||||||
sql: 'BEGIN\r\n\r\nEND',
|
sql: '',
|
||||||
parameters: [],
|
parameters: [],
|
||||||
name: '',
|
name: '',
|
||||||
comment: '',
|
comment: '',
|
||||||
|
language: null,
|
||||||
security: 'DEFINER',
|
security: 'DEFINER',
|
||||||
deterministic: false,
|
deterministic: false,
|
||||||
dataAccess: 'CONTAINS SQL'
|
dataAccess: 'CONTAINS SQL'
|
||||||
@@ -131,9 +144,17 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
schema () {
|
schema () {
|
||||||
return this.workspace.breadcrumbs.schema;
|
return this.workspace.breadcrumbs.schema;
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace.customizations;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
|
if (this.customizations.languages)
|
||||||
|
this.localRoutine.language = this.customizations.languages[0];
|
||||||
|
|
||||||
|
if (this.customizations.procedureSql)
|
||||||
|
this.localRoutine.sql = this.customizations.procedureSql;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$refs.firstInput.focus();
|
this.$refs.firstInput.focus();
|
||||||
}, 20);
|
}, 20);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-database-plus mr-1" /> {{ $t('message.createNewDatabase') }}
|
<i class="mdi mdi-24px mdi-database-plus mr-1" /> {{ $t('message.createNewSchema') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
@@ -24,11 +24,11 @@
|
|||||||
class="form-input"
|
class="form-input"
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
:placeholder="$t('message.databaseName')"
|
:placeholder="$t('message.schemaName')"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.collations" class="form-group">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<label class="form-label">{{ $t('word.collation') }}</label>
|
<label class="form-label">{{ $t('word.collation') }}</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,7 +49,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer text-light">
|
<div class="modal-footer text-light">
|
||||||
<button class="btn btn-primary mr-2" @click.stop="createDatabase">
|
<button
|
||||||
|
class="btn btn-primary mr-2"
|
||||||
|
:class="{'loading': isLoading}"
|
||||||
|
@click.stop="createSchema"
|
||||||
|
>
|
||||||
{{ $t('word.add') }}
|
{{ $t('word.add') }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-link" @click.stop="closeModal">
|
<button class="btn btn-link" @click.stop="closeModal">
|
||||||
@@ -62,12 +66,13 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import Database from '@/ipc-api/Database';
|
import Schema from '@/ipc-api/Schema';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ModalNewDatabase',
|
name: 'ModalNewSchema',
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
isLoading: false,
|
||||||
database: {
|
database: {
|
||||||
name: '',
|
name: '',
|
||||||
collation: ''
|
collation: ''
|
||||||
@@ -83,8 +88,11 @@ export default {
|
|||||||
collations () {
|
collations () {
|
||||||
return this.getWorkspace(this.selectedWorkspace).collations;
|
return this.getWorkspace(this.selectedWorkspace).collations;
|
||||||
},
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.getWorkspace(this.selectedWorkspace).customizations;
|
||||||
|
},
|
||||||
defaultCollation () {
|
defaultCollation () {
|
||||||
return this.getDatabaseVariable(this.selectedWorkspace, 'collation_server').value || '';
|
return this.getDatabaseVariable(this.selectedWorkspace, 'collation_server') ? this.getDatabaseVariable(this.selectedWorkspace, 'collation_server').value : '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
@@ -101,9 +109,10 @@ export default {
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification'
|
addNotification: 'notifications/addNotification'
|
||||||
}),
|
}),
|
||||||
async createDatabase () {
|
async createSchema () {
|
||||||
|
this.isLoading = true;
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Database.createDatabase({
|
const { status, response } = await Schema.createSchema({
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
...this.database
|
...this.database
|
||||||
});
|
});
|
||||||
@@ -118,6 +127,7 @@ export default {
|
|||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
this.isLoading = false;
|
||||||
},
|
},
|
||||||
closeModal () {
|
closeModal () {
|
||||||
this.$emit('close');
|
this.$emit('close');
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="workspace.customizations.comment" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.comment') }}
|
{{ $t('word.comment') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="workspace.customizations.collations" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.collation') }}
|
{{ $t('word.collation') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="workspace.customizations.engines" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.engine') }}
|
{{ $t('word.engine') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -103,10 +103,14 @@ export default {
|
|||||||
getDatabaseVariable: 'workspaces/getDatabaseVariable'
|
getDatabaseVariable: 'workspaces/getDatabaseVariable'
|
||||||
}),
|
}),
|
||||||
defaultCollation () {
|
defaultCollation () {
|
||||||
return this.getDatabaseVariable(this.selectedWorkspace, 'collation_server').value || '';
|
if (this.workspace.customizations.collations)
|
||||||
|
return this.getDatabaseVariable(this.selectedWorkspace, 'collation_server').value || '';
|
||||||
|
return '';
|
||||||
},
|
},
|
||||||
defaultEngine () {
|
defaultEngine () {
|
||||||
return this.workspace.engines.find(engine => engine.isDefault).name;
|
if (this.workspace.customizations.engines)
|
||||||
|
return this.workspace.engines.find(engine => engine.isDefault).name;
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
|
|||||||
@@ -50,6 +50,16 @@
|
|||||||
:tabindex="key+1"
|
:tabindex="key+1"
|
||||||
@change="filesChange($event,field.name)"
|
@change="filesChange($event,field.name)"
|
||||||
>
|
>
|
||||||
|
<input
|
||||||
|
v-else-if="inputProps(field).type === 'number'"
|
||||||
|
ref="formInput"
|
||||||
|
v-model="localRow[field.name]"
|
||||||
|
class="form-input"
|
||||||
|
step="any"
|
||||||
|
:type="inputProps(field).type"
|
||||||
|
:disabled="fieldsToExclude.includes(field.name)"
|
||||||
|
:tabindex="key+1"
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
v-else
|
v-else
|
||||||
ref="formInput"
|
ref="formInput"
|
||||||
@@ -59,7 +69,7 @@
|
|||||||
: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.toLowerCase()}`">
|
<span class="input-group-addon" :class="typeCLass(field.type)">
|
||||||
{{ 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')">
|
||||||
@@ -107,8 +117,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { TEXT, LONG_TEXT, NUMBER, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
|
import { TEXT, LONG_TEXT, NUMBER, FLOAT, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
|
||||||
import { mask } from 'vue-the-mask';
|
import { VueMaskDirective } from 'v-mask';
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import Tables from '@/ipc-api/Tables';
|
import Tables from '@/ipc-api/Tables';
|
||||||
import ForeignKeySelect from '@/components/ForeignKeySelect';
|
import ForeignKeySelect from '@/components/ForeignKeySelect';
|
||||||
@@ -119,7 +129,7 @@ export default {
|
|||||||
ForeignKeySelect
|
ForeignKeySelect
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
mask
|
mask: VueMaskDirective
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
wrapNumber (num) {
|
wrapNumber (num) {
|
||||||
@@ -157,6 +167,8 @@ export default {
|
|||||||
nInserts (val) {
|
nInserts (val) {
|
||||||
if (!val || val < 1)
|
if (!val || val < 1)
|
||||||
this.nInserts = 1;
|
this.nInserts = 1;
|
||||||
|
else if (val > 1000)
|
||||||
|
this.nInserts = 1000;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
@@ -170,7 +182,7 @@ export default {
|
|||||||
|
|
||||||
if (field.default === 'NULL') fieldDefault = null;
|
if (field.default === 'NULL') fieldDefault = null;
|
||||||
else {
|
else {
|
||||||
if (NUMBER.includes(field.type))
|
if ([...NUMBER, ...FLOAT].includes(field.type))
|
||||||
fieldDefault = +field.default;
|
fieldDefault = +field.default;
|
||||||
|
|
||||||
if ([...TEXT, ...LONG_TEXT].includes(field.type))
|
if ([...TEXT, ...LONG_TEXT].includes(field.type))
|
||||||
@@ -210,6 +222,11 @@ export default {
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification'
|
addNotification: 'notifications/addNotification'
|
||||||
}),
|
}),
|
||||||
|
typeClass (type) {
|
||||||
|
if (type)
|
||||||
|
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||||
|
return '';
|
||||||
|
},
|
||||||
async insertRows () {
|
async insertRows () {
|
||||||
this.isInserting = true;
|
this.isInserting = true;
|
||||||
const rowToInsert = this.localRow;
|
const rowToInsert = this.localRow;
|
||||||
@@ -253,13 +270,14 @@ export default {
|
|||||||
},
|
},
|
||||||
fieldLength (field) {
|
fieldLength (field) {
|
||||||
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
||||||
|
else if (TEXT.includes(field.type)) return field.charLength;
|
||||||
return field.length;
|
return field.length;
|
||||||
},
|
},
|
||||||
inputProps (field) {
|
inputProps (field) {
|
||||||
if ([...TEXT, ...LONG_TEXT].includes(field.type))
|
if ([...TEXT, ...LONG_TEXT].includes(field.type))
|
||||||
return { type: 'text', mask: false };
|
return { type: 'text', mask: false };
|
||||||
|
|
||||||
if (NUMBER.includes(field.type))
|
if ([...NUMBER, ...FLOAT].includes(field.type))
|
||||||
return { type: 'number', mask: false };
|
return { type: 'number', mask: false };
|
||||||
|
|
||||||
if (TIME.includes(field.type)) {
|
if (TIME.includes(field.type)) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-6">
|
<div class="column col-6">
|
||||||
<div class="form-group">
|
<div v-if="workspace.customizations.definer" class="form-group">
|
||||||
<label class="form-label">{{ $t('word.definer') }}</label>
|
<label class="form-label">{{ $t('word.definer') }}</label>
|
||||||
<select v-model="localView.definer" class="form-select">
|
<select v-model="localView.definer" class="form-select">
|
||||||
<option value="">
|
<option value="">
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column col-4">
|
<div class="column col-4">
|
||||||
<div class="form-group">
|
<div v-if="workspace.customizations.viewSqlSecurity" class="form-group">
|
||||||
<label class="form-label">{{ $t('message.sqlSecurity') }}</label>
|
<label class="form-label">{{ $t('message.sqlSecurity') }}</label>
|
||||||
<label class="form-radio">
|
<label class="form-radio">
|
||||||
<input
|
<input
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-4">
|
<div class="column col-4">
|
||||||
<div class="form-group">
|
<div v-if="workspace.customizations.viewAlgorithm" class="form-group">
|
||||||
<label class="form-label">{{ $t('word.algorithm') }}</label>
|
<label class="form-label">{{ $t('word.algorithm') }}</label>
|
||||||
<label class="form-radio">
|
<label class="form-radio">
|
||||||
<input
|
<input
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-4">
|
<div class="column col-4">
|
||||||
<div class="form-group">
|
<div v-if="workspace.customizations.viewUpdateOption" class="form-group">
|
||||||
<label class="form-label">{{ $t('message.updateOption') }}</label>
|
<label class="form-label">{{ $t('message.updateOption') }}</label>
|
||||||
<label class="form-radio">
|
<label class="form-radio">
|
||||||
<input
|
<input
|
||||||
|
|||||||
310
src/renderer/components/ModalProcessesList.vue
Normal file
310
src/renderer/components/ModalProcessesList.vue
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
<template>
|
||||||
|
<div class="modal active">
|
||||||
|
<a class="modal-overlay" @click.stop="closeModal" />
|
||||||
|
<div class="modal-container p-0 pb-4">
|
||||||
|
<div class="modal-header pl-2">
|
||||||
|
<div class="modal-title h6">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-memory mr-1" /> {{ $t('message.processesList') }}: {{ connectionName }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
|
</div>
|
||||||
|
<div class="processes-toolbar py-2 px-4">
|
||||||
|
<div class="dropdown">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button
|
||||||
|
class="btn btn-dark btn-sm mr-0 pr-1 d-flex"
|
||||||
|
:class="{'loading':isQuering}"
|
||||||
|
title="F5"
|
||||||
|
@click="getProcessesList"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.refresh') }}</span>
|
||||||
|
<i v-if="!+autorefreshTimer" class="mdi mdi-24px mdi-refresh ml-1" />
|
||||||
|
<i v-else class="mdi mdi-24px mdi-history mdi-flip-h ml-1" />
|
||||||
|
</button>
|
||||||
|
<div class="btn btn-dark btn-sm dropdown-toggle pl-0 pr-0" tabindex="0">
|
||||||
|
<i class="mdi mdi-24px mdi-menu-down" />
|
||||||
|
</div>
|
||||||
|
<div class="menu px-3">
|
||||||
|
<span>{{ $t('word.autoRefresh') }}: <b>{{ +autorefreshTimer ? `${autorefreshTimer}s` : 'OFF' }}</b></span>
|
||||||
|
<input
|
||||||
|
v-model="autorefreshTimer"
|
||||||
|
class="slider no-border"
|
||||||
|
type="range"
|
||||||
|
min="0"
|
||||||
|
max="15"
|
||||||
|
step="0.5"
|
||||||
|
@change="setRefreshInterval"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="workspace-query-info">
|
||||||
|
<div v-if="sortedResults.length">
|
||||||
|
{{ $t('word.processes') }}: <b>{{ sortedResults.length.toLocaleString() }}</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body py-0 workspace-query-results">
|
||||||
|
<div
|
||||||
|
ref="tableWrapper"
|
||||||
|
class="vscroll"
|
||||||
|
:style="{'height': resultsSize+'px'}"
|
||||||
|
>
|
||||||
|
<div ref="table" class="table table-hover">
|
||||||
|
<div class="thead">
|
||||||
|
<div class="tr">
|
||||||
|
<div
|
||||||
|
v-for="(field, index) in fields"
|
||||||
|
:key="index"
|
||||||
|
class="th c-hand"
|
||||||
|
>
|
||||||
|
<div ref="columnResize" class="column-resizable">
|
||||||
|
<div class="table-column-title" @click="sort(field)">
|
||||||
|
<span>{{ field.toUpperCase() }}</span>
|
||||||
|
<i
|
||||||
|
v-if="currentSort === field"
|
||||||
|
class="mdi sort-icon"
|
||||||
|
:class="currentSortDir === 'asc' ? 'mdi-sort-ascending':'mdi-sort-descending'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<BaseVirtualScroll
|
||||||
|
ref="resultTable"
|
||||||
|
:items="sortedResults"
|
||||||
|
:item-height="22"
|
||||||
|
class="tbody"
|
||||||
|
:visible-height="resultsSize"
|
||||||
|
:scroll-element="scrollElement"
|
||||||
|
>
|
||||||
|
<template slot-scope="{ items }">
|
||||||
|
<ProcessesListRow
|
||||||
|
v-for="row in items"
|
||||||
|
:key="row._id"
|
||||||
|
class="process-row"
|
||||||
|
:row="row"
|
||||||
|
@contextmenu="contextMenu"
|
||||||
|
@stop-refresh="stopRefresh"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</BaseVirtualScroll>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import Schema from '@/ipc-api/Schema';
|
||||||
|
import BaseVirtualScroll from '@/components/BaseVirtualScroll';
|
||||||
|
import ProcessesListRow from '@/components/ProcessesListRow';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalProcessesList',
|
||||||
|
components: {
|
||||||
|
BaseVirtualScroll,
|
||||||
|
ProcessesListRow
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
connection: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
resultsSize: 1000,
|
||||||
|
isQuering: false,
|
||||||
|
autorefreshTimer: 0,
|
||||||
|
refreshInterval: null,
|
||||||
|
results: [],
|
||||||
|
fields: [],
|
||||||
|
currentSort: '',
|
||||||
|
currentSortDir: 'asc',
|
||||||
|
scrollElement: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
getConnectionName: 'connections/getConnectionName'
|
||||||
|
}),
|
||||||
|
connectionName () {
|
||||||
|
return this.getConnectionName(this.connection.uid);
|
||||||
|
},
|
||||||
|
sortedResults () {
|
||||||
|
if (this.currentSort) {
|
||||||
|
return [...this.results].sort((a, b) => {
|
||||||
|
let modifier = 1;
|
||||||
|
const valA = typeof a[this.currentSort] === 'string' ? a[this.currentSort].toLowerCase() : a[this.currentSort];
|
||||||
|
const valB = typeof b[this.currentSort] === 'string' ? b[this.currentSort].toLowerCase() : b[this.currentSort];
|
||||||
|
if (this.currentSortDir === 'desc') modifier = -1;
|
||||||
|
if (valA < valB) return -1 * modifier;
|
||||||
|
if (valA > valB) return 1 * modifier;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return this.results;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey, { capture: true });
|
||||||
|
},
|
||||||
|
updated () {
|
||||||
|
if (this.$refs.table)
|
||||||
|
this.refreshScroller();
|
||||||
|
|
||||||
|
if (this.$refs.tableWrapper)
|
||||||
|
this.scrollElement = this.$refs.tableWrapper;
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.getProcessesList();
|
||||||
|
window.addEventListener('resize', this.resizeResults);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey, { capture: true });
|
||||||
|
window.removeEventListener('resize', this.resizeResults);
|
||||||
|
clearInterval(this.refreshInterval);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification'
|
||||||
|
}),
|
||||||
|
async getProcessesList () {
|
||||||
|
this.isQuering = true;
|
||||||
|
|
||||||
|
// if table changes clear cached values
|
||||||
|
if (this.lastTable !== this.table)
|
||||||
|
this.results = [];
|
||||||
|
|
||||||
|
try { // Table data
|
||||||
|
const { status, response } = await Schema.getProcesses(this.connection.uid);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
this.results = response;
|
||||||
|
this.fields = response.length ? Object.keys(response[0]) : [];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isQuering = false;
|
||||||
|
},
|
||||||
|
setRefreshInterval () {
|
||||||
|
this.clearRefresh();
|
||||||
|
|
||||||
|
if (+this.autorefreshTimer) {
|
||||||
|
this.refreshInterval = setInterval(() => {
|
||||||
|
if (!this.isQuering)
|
||||||
|
this.getProcessesList();
|
||||||
|
}, this.autorefreshTimer * 1000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearRefresh () {
|
||||||
|
if (this.refreshInterval)
|
||||||
|
clearInterval(this.refreshInterval);
|
||||||
|
},
|
||||||
|
resizeResults () {
|
||||||
|
if (this.$refs.resultTable) {
|
||||||
|
const el = this.$refs.tableWrapper.parentElement;
|
||||||
|
|
||||||
|
if (el) {
|
||||||
|
const size = el.offsetHeight;
|
||||||
|
this.resultsSize = size;
|
||||||
|
}
|
||||||
|
this.$refs.resultTable.updateWindow();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refreshScroller () {
|
||||||
|
this.resizeResults();
|
||||||
|
},
|
||||||
|
sort (field) {
|
||||||
|
if (field === this.currentSort) {
|
||||||
|
if (this.currentSortDir === 'asc')
|
||||||
|
this.currentSortDir = 'desc';
|
||||||
|
else
|
||||||
|
this.resetSort();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.currentSortDir = 'asc';
|
||||||
|
this.currentSort = field;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetSort () {
|
||||||
|
this.currentSort = '';
|
||||||
|
this.currentSortDir = 'asc';
|
||||||
|
},
|
||||||
|
stopRefresh () {
|
||||||
|
this.autorefreshTimer = 0;
|
||||||
|
this.clearRefresh();
|
||||||
|
},
|
||||||
|
contextMenu () {},
|
||||||
|
closeModal () {
|
||||||
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape')
|
||||||
|
this.closeModal();
|
||||||
|
if (e.key === 'F5')
|
||||||
|
this.getProcessesList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vscroll {
|
||||||
|
height: 1000px;
|
||||||
|
overflow: auto;
|
||||||
|
overflow-anchor: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-resizable {
|
||||||
|
&:hover,
|
||||||
|
&:active {
|
||||||
|
resize: horizontal;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-column-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-icon {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
line-height: 1;
|
||||||
|
margin-left: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-tabs {
|
||||||
|
background: transparent !important;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
.modal-container {
|
||||||
|
max-width: 75vw;
|
||||||
|
margin-top: 10vh;
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
height: 80vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.processes-toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -30,12 +30,20 @@
|
|||||||
<a class="c-hand">{{ $t('word.themes') }}</a>
|
<a class="c-hand">{{ $t('word.themes') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
|
v-if="updateStatus !== 'disabled'"
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{'active': selectedTab === 'update'}"
|
:class="{'active': selectedTab === 'update'}"
|
||||||
@click="selectTab('update')"
|
@click="selectTab('update')"
|
||||||
>
|
>
|
||||||
<a class="c-hand" :class="{'badge badge-update': hasUpdates}">{{ $t('word.update') }}</a>
|
<a class="c-hand" :class="{'badge badge-update': hasUpdates}">{{ $t('word.update') }}</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li
|
||||||
|
class="tab-item"
|
||||||
|
:class="{'active': selectedTab === 'changelog'}"
|
||||||
|
@click="selectTab('changelog')"
|
||||||
|
>
|
||||||
|
<a class="c-hand">{{ $t('word.changelog') }}</a>
|
||||||
|
</li>
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{'active': selectedTab === 'about'}"
|
:class="{'active': selectedTab === 'about'}"
|
||||||
@@ -45,7 +53,7 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="selectedTab === 'general'" class="panel-body py-4">
|
<div v-show="selectedTab === 'general'" class="panel-body py-4">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<form class="form-horizontal columns">
|
<form class="form-horizontal columns">
|
||||||
<div class="column col-12 h6 text-uppercase mb-1">
|
<div class="column col-12 h6 text-uppercase mb-1">
|
||||||
@@ -133,26 +141,35 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="selectedTab === 'themes'" class="panel-body py-4">
|
<div v-show="selectedTab === 'themes'" class="panel-body py-4">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column col-12 h6 text-uppercase mb-2">
|
<div class="column col-12 h6 text-uppercase mb-2">
|
||||||
{{ $t('message.applicationTheme') }}
|
{{ $t('message.applicationTheme') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-6 c-hand theme-block" :class="{'selected': applicationTheme === 'dark'}">
|
<div
|
||||||
|
class="column col-6 c-hand theme-block"
|
||||||
|
:class="{'selected': applicationTheme === 'dark'}"
|
||||||
|
@click="changeApplicationTheme('dark')"
|
||||||
|
>
|
||||||
<img :src="require('@/images/dark.png').default" class="img-responsive img-fit-cover s-rounded">
|
<img :src="require('@/images/dark.png').default" class="img-responsive img-fit-cover s-rounded">
|
||||||
<div class="theme-name">
|
<div class="theme-name text-light">
|
||||||
<i class="mdi mdi-moon-waning-crescent mdi-48px" />
|
<i class="mdi mdi-moon-waning-crescent mdi-48px" />
|
||||||
<div class="h6 mt-4">
|
<div class="h6 mt-4">
|
||||||
{{ $t('word.dark') }}
|
{{ $t('word.dark') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-6 theme-block disabled" :class="{'selected': applicationTheme === 'light'}">
|
<div
|
||||||
<div class="theme-name">
|
class="column col-6 c-hand theme-block"
|
||||||
|
:class="{'selected': applicationTheme === 'light'}"
|
||||||
|
@click="changeApplicationTheme('light')"
|
||||||
|
>
|
||||||
|
<img :src="require('@/images/light.png').default" class="img-responsive img-fit-cover s-rounded">
|
||||||
|
<div class="theme-name text-dark">
|
||||||
<i class="mdi mdi-white-balance-sunny mdi-48px" />
|
<i class="mdi mdi-white-balance-sunny mdi-48px" />
|
||||||
<div class="h6 mt-4">
|
<div class="h6 mt-4">
|
||||||
{{ $t('word.light') }} (Coming)
|
{{ $t('word.light') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -185,8 +202,9 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-12">
|
<div class="column col-12">
|
||||||
<QueryEditor
|
<BaseTextEditor
|
||||||
:value="exampleQuery"
|
:value="exampleQuery"
|
||||||
|
mode="sql"
|
||||||
:workspace="workspace"
|
:workspace="workspace"
|
||||||
:read-only="true"
|
:read-only="true"
|
||||||
:height="270"
|
:height="270"
|
||||||
@@ -196,17 +214,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="selectedTab === 'update'" class="panel-body py-4">
|
<div v-show="selectedTab === 'update'" class="panel-body py-4">
|
||||||
<ModalSettingsUpdate />
|
<ModalSettingsUpdate />
|
||||||
</div>
|
</div>
|
||||||
|
<div v-show="selectedTab === 'changelog'" class="panel-body py-4">
|
||||||
|
<ModalSettingsChangelog />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="selectedTab === 'about'" class="panel-body py-4">
|
<div v-show="selectedTab === 'about'" class="panel-body py-4">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<img :src="require('@/images/logo.svg').default" width="128">
|
<img :src="require('@/images/logo.svg').default" width="128">
|
||||||
<h4>{{ appName }}</h4>
|
<h4>{{ appName }}</h4>
|
||||||
<p>
|
<p>
|
||||||
{{ $t('word.version') }} {{ appVersion }}<br>
|
{{ $t('word.version') }} {{ appVersion }}<br>
|
||||||
<a class="c-hand" @click="openOutside('https://github.com/Fabio286/antares')">GitHub</a> | <a class="c-hand" @click="openOutside('https://github.com/Fabio286/antares/blob/master/CHANGELOG.md')">CHANGELOG</a><br>
|
<a class="c-hand" @click="openOutside('https://github.com/Fabio286/antares')">GitHub</a> | <a class="c-hand" @click="openOutside('https://antares-sql.app/')">Website</a><br>
|
||||||
<small>{{ $t('word.author') }} <a class="c-hand" @click="openOutside('https://github.com/Fabio286')">Fabio Di Stasio</a></small><br>
|
<small>{{ $t('word.author') }} <a class="c-hand" @click="openOutside('https://github.com/Fabio286')">Fabio Di Stasio</a></small><br>
|
||||||
<small>{{ $t('message.madeWithJS') }}</small>
|
<small>{{ $t('message.madeWithJS') }}</small>
|
||||||
</p>
|
</p>
|
||||||
@@ -222,14 +243,16 @@
|
|||||||
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';
|
import ModalSettingsChangelog from '@/components/ModalSettingsChangelog';
|
||||||
|
import BaseTextEditor from '@/components/BaseTextEditor';
|
||||||
const { shell } = require('electron');
|
const { shell } = require('electron');
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ModalSettings',
|
name: 'ModalSettings',
|
||||||
components: {
|
components: {
|
||||||
ModalSettingsUpdate,
|
ModalSettingsUpdate,
|
||||||
QueryEditor
|
ModalSettingsChangelog,
|
||||||
|
BaseTextEditor
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -350,6 +373,7 @@ ORDER BY
|
|||||||
changeLocale: 'settings/changeLocale',
|
changeLocale: 'settings/changeLocale',
|
||||||
changeAutoComplete: 'settings/changeAutoComplete',
|
changeAutoComplete: 'settings/changeAutoComplete',
|
||||||
changeLineWrap: 'settings/changeLineWrap',
|
changeLineWrap: 'settings/changeLineWrap',
|
||||||
|
changeApplicationTheme: 'settings/changeApplicationTheme',
|
||||||
changeEditorTheme: 'settings/changeEditorTheme',
|
changeEditorTheme: 'settings/changeEditorTheme',
|
||||||
updateNotificationsTimeout: 'settings/updateNotificationsTimeout'
|
updateNotificationsTimeout: 'settings/updateNotificationsTimeout'
|
||||||
}),
|
}),
|
||||||
@@ -382,54 +406,59 @@ ORDER BY
|
|||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
#settings {
|
#settings {
|
||||||
.modal-body {
|
.modal-container {
|
||||||
overflow: hidden;
|
position: absolute;
|
||||||
|
top: 17.5vh;
|
||||||
|
|
||||||
.panel-body {
|
.modal-body {
|
||||||
height: calc(70vh - 70px);
|
overflow: hidden;
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
.theme-block {
|
.panel-body {
|
||||||
position: relative;
|
min-height: calc(25vh - 70px);
|
||||||
text-align: center;
|
max-height: 65vh;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
&.selected {
|
.theme-block {
|
||||||
img {
|
position: relative;
|
||||||
box-shadow: 0 0 0 3px $primary-color;
|
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%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.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 {
|
||||||
background: #32b643;
|
background: #32b643;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-update::after {
|
.badge-update::after {
|
||||||
bottom: initial;
|
bottom: initial;
|
||||||
background: $primary-color;
|
background: $primary-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-label {
|
.form-label {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
82
src/renderer/components/ModalSettingsChangelog.vue
Normal file
82
src/renderer/components/ModalSettingsChangelog.vue
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-relative">
|
||||||
|
<BaseLoader v-if="isLoading" />
|
||||||
|
<div
|
||||||
|
id="changelog"
|
||||||
|
class="container"
|
||||||
|
v-html="changelog"
|
||||||
|
/>
|
||||||
|
<div v-if="isError" class="empty">
|
||||||
|
<div class="empty-icon">
|
||||||
|
<i class="mdi mdi-48px mdi-alert-outline" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import marked from 'marked';
|
||||||
|
import BaseLoader from '@/components/BaseLoader';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalSettingsChangelog',
|
||||||
|
components: {
|
||||||
|
BaseLoader
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
changelog: '',
|
||||||
|
isLoading: true,
|
||||||
|
error: '',
|
||||||
|
isError: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({ appVersion: 'application/appVersion' })
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.getChangelog();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async getChangelog () {
|
||||||
|
try {
|
||||||
|
const apiRes = await fetch(`https://api.github.com/repos/Fabio286/antares/releases/tags/v${this.appVersion}`, {
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
|
||||||
|
const { body } = await apiRes.json();
|
||||||
|
const markdown = body.substr(0, body.indexOf('### Download'));
|
||||||
|
const renderer = {
|
||||||
|
link (href, title, text) {
|
||||||
|
return text;
|
||||||
|
},
|
||||||
|
listitem (text) {
|
||||||
|
return `<li>${text.replace(/ *\([^)]*\) */g, '')}</li>`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
marked.use({ renderer });
|
||||||
|
|
||||||
|
this.changelog = marked(markdown);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.error = err.message;
|
||||||
|
this.isError = true;
|
||||||
|
}
|
||||||
|
this.isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
#changelog {
|
||||||
|
h3 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -36,11 +36,17 @@
|
|||||||
{{ $t('message.restartToInstall') }}
|
{{ $t('message.restartToInstall') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group mt-4">
|
||||||
|
<label class="form-switch d-inline-block disabled" @click.prevent="toggleAllowPrerelease">
|
||||||
|
<input type="checkbox" :checked="allowPrerelease">
|
||||||
|
<i class="form-icon" /> {{ $t('message.includeBetaUpdates') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -48,7 +54,8 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
updateStatus: 'application/getUpdateStatus',
|
updateStatus: 'application/getUpdateStatus',
|
||||||
downloadPercentage: 'application/getDownloadProgress'
|
downloadPercentage: 'application/getDownloadProgress',
|
||||||
|
allowPrerelease: 'settings/getAllowPrerelease'
|
||||||
}),
|
}),
|
||||||
updateMessage () {
|
updateMessage () {
|
||||||
switch (this.updateStatus) {
|
switch (this.updateStatus) {
|
||||||
@@ -70,18 +77,18 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
changeAllowPrerelease: 'settings/changeAllowPrerelease'
|
||||||
|
}),
|
||||||
checkForUpdates () {
|
checkForUpdates () {
|
||||||
ipcRenderer.send('check-for-updates');
|
ipcRenderer.send('check-for-updates');
|
||||||
},
|
},
|
||||||
restartToUpdate () {
|
restartToUpdate () {
|
||||||
ipcRenderer.send('restart-to-update');
|
ipcRenderer.send('restart-to-update');
|
||||||
|
},
|
||||||
|
toggleAllowPrerelease () {
|
||||||
|
this.changeAllowPrerelease(!this.allowPrerelease);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.empty {
|
|
||||||
color: $body-font-color;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
155
src/renderer/components/ProcessesListRow.vue
Normal file
155
src/renderer/components/ProcessesListRow.vue
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tr" @click="selectRow($event, row._id)">
|
||||||
|
<div
|
||||||
|
v-for="(col, cKey) in row"
|
||||||
|
v-show="cKey !== '_id'"
|
||||||
|
:key="cKey"
|
||||||
|
class="td p-0"
|
||||||
|
tabindex="0"
|
||||||
|
@contextmenu.prevent="openContext($event, { id: row._id, field: cKey })"
|
||||||
|
>
|
||||||
|
<template v-if="cKey !== '_id'">
|
||||||
|
<span
|
||||||
|
v-if="!isInlineEditor[cKey]"
|
||||||
|
class="cell-content px-2"
|
||||||
|
:class="`${isNull(col)} type-${typeof col === 'number' ? 'int' : 'varchar'}`"
|
||||||
|
@dblclick="dblClick(cKey)"
|
||||||
|
>{{ col | cutText }}</span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<ConfirmModal
|
||||||
|
v-if="isInfoModal"
|
||||||
|
:confirm-text="$t('word.update')"
|
||||||
|
:cancel-text="$t('word.close')"
|
||||||
|
size="medium"
|
||||||
|
:hide-footer="true"
|
||||||
|
@hide="hideInfoModal"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-information-outline mr-1" /> {{ $t('message.processInfo') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<TextEditor
|
||||||
|
:value="row.info || ''"
|
||||||
|
editor-class="textarea-editor"
|
||||||
|
:mode="editorMode"
|
||||||
|
:read-only="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
import TextEditor from '@/components/BaseTextEditor';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ProcessesListRow',
|
||||||
|
components: {
|
||||||
|
ConfirmModal,
|
||||||
|
TextEditor
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
cutText (val) {
|
||||||
|
if (typeof val !== 'string') return val;
|
||||||
|
return val.length > 250 ? `${val.substring(0, 250)}[...]` : val;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
row: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isInlineEditor: {},
|
||||||
|
isInfoModal: false,
|
||||||
|
editorMode: 'sql'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {},
|
||||||
|
watch: {
|
||||||
|
fields () {
|
||||||
|
Object.keys(this.fields).forEach(field => {
|
||||||
|
this.isInlineEditor[field.name] = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isNull (value) {
|
||||||
|
return value === null ? ' is-null' : '';
|
||||||
|
},
|
||||||
|
selectRow (event, row) {
|
||||||
|
this.$emit('select-row', event, row);
|
||||||
|
},
|
||||||
|
openContext (event, payload) {
|
||||||
|
if (this.isEditable) {
|
||||||
|
payload.field = this.fields[payload.field].name;// Ensures field name only
|
||||||
|
this.$emit('contextmenu', event, payload);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hideInfoModal () {
|
||||||
|
this.isInfoModal = false;
|
||||||
|
},
|
||||||
|
dblClick (col) {
|
||||||
|
if (col !== 'info') return;
|
||||||
|
this.$emit('stop-refresh');
|
||||||
|
this.isInfoModal = true;
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
this.isInlineEditor[this.editingField] = false;
|
||||||
|
this.editingField = null;
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.editable-field {
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
line-height: 1;
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-content {
|
||||||
|
display: block;
|
||||||
|
min-height: 0.8rem;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textarea-editor {
|
||||||
|
height: 50vh !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-field-info {
|
||||||
|
margin-top: 0.4rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="editor-wrapper">
|
<div class="editor-wrapper">
|
||||||
<div
|
<div
|
||||||
ref="editor"
|
:id="`editor-${id}`"
|
||||||
class="editor"
|
class="editor"
|
||||||
:style="{height: `${height}px`}"
|
:style="{height: `${height}px`}"
|
||||||
/>
|
/>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
import * as ace from 'ace-builds';
|
import * as ace from 'ace-builds';
|
||||||
import 'ace-builds/webpack-resolver';
|
import 'ace-builds/webpack-resolver';
|
||||||
import '../libs/ext-language_tools';
|
import '../libs/ext-language_tools';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import Tables from '@/ipc-api/Tables';
|
import Tables from '@/ipc-api/Tables';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -20,6 +20,7 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
value: String,
|
value: String,
|
||||||
workspace: Object,
|
workspace: Object,
|
||||||
|
isSelected: Boolean,
|
||||||
schema: { type: String, default: '' },
|
schema: { type: String, default: '' },
|
||||||
autoFocus: { type: Boolean, default: false },
|
autoFocus: { type: Boolean, default: false },
|
||||||
readOnly: { type: Boolean, default: false },
|
readOnly: { type: Boolean, default: false },
|
||||||
@@ -29,14 +30,17 @@ export default {
|
|||||||
return {
|
return {
|
||||||
editor: null,
|
editor: null,
|
||||||
fields: [],
|
fields: [],
|
||||||
baseCompleter: []
|
customCompleter: [],
|
||||||
|
id: null,
|
||||||
|
lastSchema: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
editorTheme: 'settings/getEditorTheme',
|
editorTheme: 'settings/getEditorTheme',
|
||||||
autoComplete: 'settings/getAutoComplete',
|
autoComplete: 'settings/getAutoComplete',
|
||||||
lineWrap: 'settings/getLineWrap'
|
lineWrap: 'settings/getLineWrap',
|
||||||
|
baseCompleter: 'application/getBaseCompleter'
|
||||||
}),
|
}),
|
||||||
tables () {
|
tables () {
|
||||||
return this.workspace
|
return this.workspace
|
||||||
@@ -122,9 +126,13 @@ export default {
|
|||||||
return 'sql';
|
return 'sql';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
cursorPosition () {
|
||||||
|
return this.editor.session.doc.positionToIndex(this.editor.getCursorPosition());
|
||||||
|
},
|
||||||
lastWord () {
|
lastWord () {
|
||||||
const words = this.value.split(' ');
|
const charsBefore = this.value.slice(0, this.cursorPosition);
|
||||||
return words[words.length - 1];
|
const words = charsBefore.replaceAll('\n', ' ').split(' ').filter(Boolean);
|
||||||
|
return words.pop();
|
||||||
},
|
},
|
||||||
isLastWordATable () {
|
isLastWordATable () {
|
||||||
return /\w+\.\w*/gm.test(this.lastWord);
|
return /\w+\.\w*/gm.test(this.lastWord);
|
||||||
@@ -163,10 +171,24 @@ export default {
|
|||||||
wrap: this.lineWrap
|
wrap: this.lineWrap
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
isSelected () {
|
||||||
|
if (this.isSelected)
|
||||||
|
this.lastSchema = this.schema;
|
||||||
|
},
|
||||||
|
lastSchema () {
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.completers = this.baseCompleter.map(el => Object.assign({}, el));
|
||||||
|
this.setCustomCompleter();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created () {
|
||||||
|
this.id = this._uid;
|
||||||
|
this.lastSchema = this.schema;
|
||||||
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.editor = ace.edit(this.$refs.editor, {
|
this.editor = ace.edit(`editor-${this.id}`, {
|
||||||
mode: `ace/mode/${this.mode}`,
|
mode: `ace/mode/${this.mode}`,
|
||||||
theme: `ace/theme/${this.editorTheme}`,
|
theme: `ace/theme/${this.editorTheme}`,
|
||||||
value: this.value,
|
value: this.value,
|
||||||
@@ -182,32 +204,16 @@ export default {
|
|||||||
enableLiveAutocompletion: this.autoComplete
|
enableLiveAutocompletion: this.autoComplete
|
||||||
});
|
});
|
||||||
|
|
||||||
this.editor.completers.push({
|
if (!this.baseCompleter.length)
|
||||||
getCompletions: (editor, session, pos, prefix, callback) => {
|
this.setBaseCompleters(this.editor.completers.map(el => Object.assign({}, el)));
|
||||||
const completions = [];
|
|
||||||
[
|
|
||||||
...this.tables,
|
|
||||||
...this.triggers,
|
|
||||||
...this.procedures,
|
|
||||||
...this.functions,
|
|
||||||
...this.schedulers
|
|
||||||
].forEach(el => {
|
|
||||||
completions.push({
|
|
||||||
value: el.name,
|
|
||||||
meta: el.type
|
|
||||||
});
|
|
||||||
});
|
|
||||||
callback(null, completions);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.baseCompleter = this.editor.completers;
|
this.setCustomCompleter();
|
||||||
|
|
||||||
this.editor.commands.on('afterExec', e => {
|
this.editor.commands.on('afterExec', e => {
|
||||||
if (['insertstring', 'backspace', 'del'].includes(e.command.name)) {
|
if (['insertstring', 'backspace', 'del'].includes(e.command.name)) {
|
||||||
if (this.isLastWordATable || e.args === '.') {
|
if (this.isLastWordATable || e.args === '.') {
|
||||||
if (e.args !== ' ') {
|
if (e.args !== ' ') {
|
||||||
const table = this.tables.find(t => t.name === this.lastWord.split('.').pop());
|
const table = this.tables.find(t => t.name === this.lastWord.split('.').pop().trim());
|
||||||
|
|
||||||
if (table) {
|
if (table) {
|
||||||
const params = {
|
const params = {
|
||||||
@@ -224,13 +230,13 @@ export default {
|
|||||||
}).catch(console.log);
|
}).catch(console.log);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.editor.completers = this.baseCompleter;
|
this.editor.completers = this.customCompleter;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.editor.completers = this.baseCompleter;
|
this.editor.completers = this.customCompleter;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.editor.completers = this.baseCompleter;
|
this.editor.completers = this.customCompleter;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -239,6 +245,22 @@ export default {
|
|||||||
this.$emit('update:value', content);
|
this.$emit('update:value', content);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.editor.on('guttermousedown', e => {
|
||||||
|
const target = e.domEvent.target;
|
||||||
|
if (target.className.indexOf('ace_gutter-cell') === -1)
|
||||||
|
return;
|
||||||
|
if (e.clientX > 25 + target.getBoundingClientRect().left)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const row = e.getDocumentPosition().row;
|
||||||
|
const breakpoints = e.editor.session.getBreakpoints(row, 0);
|
||||||
|
if (typeof breakpoints[row] === typeof undefined)
|
||||||
|
e.editor.session.setBreakpoint(row);
|
||||||
|
else
|
||||||
|
e.editor.session.clearBreakpoint(row);
|
||||||
|
e.stop();
|
||||||
|
});
|
||||||
|
|
||||||
if (this.autoFocus) {
|
if (this.autoFocus) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.editor.focus();
|
this.editor.focus();
|
||||||
@@ -249,6 +271,33 @@ export default {
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.editor.resize();
|
this.editor.resize();
|
||||||
}, 20);
|
}, 20);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
setBaseCompleters: 'application/setBaseCompleter'
|
||||||
|
}),
|
||||||
|
setCustomCompleter () {
|
||||||
|
this.editor.completers.push({
|
||||||
|
getCompletions: (editor, session, pos, prefix, callback) => {
|
||||||
|
const completions = [];
|
||||||
|
[
|
||||||
|
...this.tables,
|
||||||
|
...this.triggers,
|
||||||
|
...this.procedures,
|
||||||
|
...this.functions,
|
||||||
|
...this.schedulers
|
||||||
|
].forEach(el => {
|
||||||
|
completions.push({
|
||||||
|
value: el.name,
|
||||||
|
meta: el.type
|
||||||
|
});
|
||||||
|
});
|
||||||
|
callback(null, completions);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.customCompleter = this.editor.completers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -267,16 +316,20 @@ export default {
|
|||||||
width: 17px;
|
width: 17px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ace_dark.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {
|
.ace_gutter-cell.ace_breakpoint {
|
||||||
background-color: #c9561a99;
|
&::before {
|
||||||
}
|
content: '\F0403';
|
||||||
|
position: absolute;
|
||||||
.ace_dark.ace_editor.ace_autocomplete .ace_marker-layer .ace_line-hover {
|
left: 3px;
|
||||||
background-color: #c9571a33;
|
top: 2px;
|
||||||
border: none;
|
color: $primary-color;
|
||||||
}
|
display: inline-block;
|
||||||
|
font: normal normal normal 24px/1 "Material Design Icons", sans-serif;
|
||||||
.ace_dark.ace_editor.ace_autocomplete .ace_completion-highlight {
|
font-size: inherit;
|
||||||
color: #e0d00c;
|
text-rendering: auto;
|
||||||
|
line-height: inherit;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -2,18 +2,18 @@
|
|||||||
<div id="footer" class="text-light">
|
<div id="footer" class="text-light">
|
||||||
<div class="footer-left-elements">
|
<div class="footer-left-elements">
|
||||||
<ul class="footer-elements">
|
<ul class="footer-elements">
|
||||||
<li class="footer-element" :title="$t('word.version')">
|
<li class="footer-element">
|
||||||
<i class="mdi mdi-18px mdi-memory mr-1" />
|
<i class="mdi mdi-18px mdi-database mr-1" />
|
||||||
<small>{{ appVersion }}</small>
|
<small>{{ versionString }}</small>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="footer-right-elements">
|
<div class="footer-right-elements">
|
||||||
<ul class="footer-elements">
|
<ul class="footer-elements">
|
||||||
<li class="footer-element footer-link" @click="openOutside('https://github.com/sponsors/Fabio286')">
|
<li class="footer-element footer-link" @click="openOutside('https://www.treedom.net/en/user/fabio-di-stasio/event/antares-for-the-planet')">
|
||||||
<i class="mdi mdi-18px mdi-coffee mr-1" />
|
<i class="mdi mdi-18px mdi-tree mr-1" />
|
||||||
<small>{{ $t('word.donate') }}</small>
|
<small>{{ $t('message.plantATree') }}</small>
|
||||||
</li>
|
</li>
|
||||||
<li class="footer-element footer-link" @click="openOutside('https://github.com/Fabio286/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" />
|
||||||
@@ -34,9 +34,18 @@ export default {
|
|||||||
name: 'TheFooter',
|
name: 'TheFooter',
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
appName: 'application/appName',
|
workspace: 'workspaces/getSelected',
|
||||||
|
getWorkspace: 'workspaces/getWorkspace',
|
||||||
appVersion: 'application/appVersion'
|
appVersion: 'application/appVersion'
|
||||||
})
|
}),
|
||||||
|
version () {
|
||||||
|
return this.getWorkspace(this.workspace) ? this.getWorkspace(this.workspace).version : null;
|
||||||
|
},
|
||||||
|
versionString () {
|
||||||
|
if (this.version)
|
||||||
|
return `${this.version.name} ${this.version.number} (${this.version.arch} ${this.version.os})`;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
@@ -55,13 +64,11 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: $primary-color;
|
|
||||||
padding: 0 0.2rem;
|
padding: 0 0.2rem;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
box-shadow: 0 0 1px 0 #000;
|
|
||||||
|
|
||||||
.footer-elements {
|
.footer-elements {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
@@ -79,10 +86,6 @@ export default {
|
|||||||
&.footer-link {
|
&.footer-link {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba($color: #fff, $alpha: 0.1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
75
src/renderer/components/TheScratchpad.vue
Normal file
75
src/renderer/components/TheScratchpad.vue
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.update')"
|
||||||
|
:cancel-text="$t('word.close')"
|
||||||
|
size="large"
|
||||||
|
:hide-footer="true"
|
||||||
|
@hide="hideScratchpad"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-notebook-edit-outline mr-1" /> {{ $t('word.scratchpad') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<TextEditor
|
||||||
|
:value.sync="localNotes"
|
||||||
|
editor-class="textarea-editor"
|
||||||
|
mode="markdown"
|
||||||
|
:auto-focus="true"
|
||||||
|
:show-line-numbers="false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<small class="text-gray">{{ $t('message.markdownSupported') }}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
import TextEditor from '@/components/BaseTextEditor';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TheScratchpad',
|
||||||
|
components: {
|
||||||
|
ConfirmModal,
|
||||||
|
TextEditor
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
localNotes: '',
|
||||||
|
debounceTimeout: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
notes: 'scratchpad/getNotes'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
localNotes () {
|
||||||
|
clearTimeout(this.debounceTimeout);
|
||||||
|
|
||||||
|
this.debounceTimeout = setTimeout(() => {
|
||||||
|
this.changeNotes(this.localNotes);
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.localNotes = this.notes;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
hideScratchpad: 'application/hideScratchpad',
|
||||||
|
changeNotes: 'scratchpad/changeNotes'
|
||||||
|
}),
|
||||||
|
hideModal () {
|
||||||
|
this.$emit('hide');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
@contextmenu.prevent="contextMenu($event, connection)"
|
@contextmenu.prevent="contextMenu($event, connection)"
|
||||||
@mouseover.self="tooltipPosition"
|
@mouseover.self="tooltipPosition"
|
||||||
>
|
>
|
||||||
<i class="settingbar-element-icon dbi" :class="`dbi-${connection.client} ${connected.includes(connection.uid) ? 'badge' : ''}`" />
|
<i class="settingbar-element-icon dbi" :class="`dbi-${connection.client} ${getStatusBadge(connection.uid)}`" />
|
||||||
<span class="ex-tooltip-content">{{ getConnectionName(connection.uid) }}</span>
|
<span class="ex-tooltip-content">{{ getConnectionName(connection.uid) }}</span>
|
||||||
</li>
|
</li>
|
||||||
</draggable>
|
</draggable>
|
||||||
@@ -36,6 +36,10 @@
|
|||||||
|
|
||||||
<div class="settingbar-bottom-elements">
|
<div class="settingbar-bottom-elements">
|
||||||
<ul class="settingbar-elements">
|
<ul class="settingbar-elements">
|
||||||
|
<li class="settingbar-element btn btn-link ex-tooltip" @click="showScratchpad">
|
||||||
|
<i class="settingbar-element-icon mdi mdi-24px mdi-notebook-edit-outline text-light" />
|
||||||
|
<span class="ex-tooltip-content">{{ $t('word.scratchpad') }}</span>
|
||||||
|
</li>
|
||||||
<li class="settingbar-element btn btn-link ex-tooltip" @click="showSettingModal('general')">
|
<li class="settingbar-element btn btn-link ex-tooltip" @click="showSettingModal('general')">
|
||||||
<i class="settingbar-element-icon mdi mdi-24px mdi-cog text-light" :class="{' badge badge-update': hasUpdates}" />
|
<i class="settingbar-element-icon mdi mdi-24px mdi-cog text-light" :class="{' badge badge-update': hasUpdates}" />
|
||||||
<span class="ex-tooltip-content">{{ $t('word.settings') }}</span>
|
<span class="ex-tooltip-content">{{ $t('word.settings') }}</span>
|
||||||
@@ -69,7 +73,7 @@ export default {
|
|||||||
...mapGetters({
|
...mapGetters({
|
||||||
getConnections: 'connections/getConnections',
|
getConnections: 'connections/getConnections',
|
||||||
getConnectionName: 'connections/getConnectionName',
|
getConnectionName: 'connections/getConnectionName',
|
||||||
connected: 'workspaces/getConnected',
|
getWorkspace: 'workspaces/getWorkspace',
|
||||||
selectedWorkspace: 'workspaces/getSelected',
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
updateStatus: 'application/getUpdateStatus'
|
updateStatus: 'application/getUpdateStatus'
|
||||||
}),
|
}),
|
||||||
@@ -90,6 +94,7 @@ export default {
|
|||||||
updateConnections: 'connections/updateConnections',
|
updateConnections: 'connections/updateConnections',
|
||||||
showNewConnModal: 'application/showNewConnModal',
|
showNewConnModal: 'application/showNewConnModal',
|
||||||
showSettingModal: 'application/showSettingModal',
|
showSettingModal: 'application/showSettingModal',
|
||||||
|
showScratchpad: 'application/showScratchpad',
|
||||||
selectWorkspace: 'workspaces/selectWorkspace'
|
selectWorkspace: 'workspaces/selectWorkspace'
|
||||||
}),
|
}),
|
||||||
contextMenu (event, connection) {
|
contextMenu (event, connection) {
|
||||||
@@ -104,6 +109,22 @@ export default {
|
|||||||
const el = e.target;
|
const el = e.target;
|
||||||
const fromTop = window.pageYOffset + el.getBoundingClientRect().top - (el.offsetHeight / 4);
|
const fromTop = window.pageYOffset + el.getBoundingClientRect().top - (el.offsetHeight / 4);
|
||||||
el.querySelector('.ex-tooltip-content').style.top = `${fromTop}px`;
|
el.querySelector('.ex-tooltip-content').style.top = `${fromTop}px`;
|
||||||
|
},
|
||||||
|
getStatusBadge (uid) {
|
||||||
|
if (this.getWorkspace(uid)) {
|
||||||
|
const status = this.getWorkspace(uid).connection_status;
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 'connected':
|
||||||
|
return 'badge badge-connected';
|
||||||
|
case 'connecting':
|
||||||
|
return 'badge badge-connecting';
|
||||||
|
case 'failed':
|
||||||
|
return 'badge badge-failed';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -117,9 +138,7 @@ export default {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: $bg-color-light;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-shadow: 0 0 1px 0 #000;
|
|
||||||
z-index: 9;
|
z-index: 9;
|
||||||
|
|
||||||
.settingbar-top-elements {
|
.settingbar-top-elements {
|
||||||
@@ -134,7 +153,6 @@ export default {
|
|||||||
|
|
||||||
.settingbar-bottom-elements {
|
.settingbar-bottom-elements {
|
||||||
padding-top: 0.5rem;
|
padding-top: 0.5rem;
|
||||||
background: $bg-color-light;
|
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +180,6 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
border-left-color: $body-font-color;
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,12 +188,10 @@ export default {
|
|||||||
bottom: -10px;
|
bottom: -10px;
|
||||||
right: 0;
|
right: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: $success-color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.badge-update::after {
|
&.badge-update::after {
|
||||||
bottom: initial;
|
bottom: initial;
|
||||||
background: $primary-color;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,12 +102,10 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
background: $bg-color-light;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: $titlebar-height;
|
height: $titlebar-height;
|
||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
box-shadow: 0 0 1px 0 #000;
|
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
|
|
||||||
.titlebar-resizer {
|
.titlebar-resizer {
|
||||||
@@ -149,11 +147,6 @@ export default {
|
|||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
background: rgba($color: #fff, $alpha: 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.close-button:hover {
|
|
||||||
background: red;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,58 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-show="isSelected" class="workspace column columns col-gapless">
|
<div v-show="isSelected" class="workspace column columns col-gapless">
|
||||||
<WorkspaceExploreBar :connection="connection" :is-selected="isSelected" />
|
<WorkspaceExploreBar :connection="connection" :is-selected="isSelected" />
|
||||||
<div v-if="workspace.connected" class="workspace-tabs column columns col-gapless">
|
<div v-if="workspace.connection_status === 'connected'" class="workspace-tabs column columns col-gapless">
|
||||||
<ul
|
<ul
|
||||||
id="tabWrap"
|
id="tabWrap"
|
||||||
ref="tabWrap"
|
ref="tabWrap"
|
||||||
class="tab tab-block column col-12"
|
class="tab tab-block column col-12"
|
||||||
>
|
>
|
||||||
<li class="tab-item d-none">
|
<li class="tab-item dropdown tools-dropdown">
|
||||||
<a class="tab-link workspace-tools-link">
|
<a
|
||||||
|
class="tab-link workspace-tools-link dropdown-toggle"
|
||||||
|
tabindex="0"
|
||||||
|
:title="$t('word.tools')"
|
||||||
|
>
|
||||||
<i class="mdi mdi-24px mdi-tools" />
|
<i class="mdi mdi-24px mdi-tools" />
|
||||||
</a>
|
</a>
|
||||||
|
<ul class="menu text-left text-uppercase">
|
||||||
|
<li v-if="workspace.customizations.processesList" class="menu-item">
|
||||||
|
<a class="c-hand p-vcentered" @click="showProcessesModal">
|
||||||
|
<i class="mdi mdi-memory mr-1 tool-icon" />
|
||||||
|
<span>{{ $t('message.processesList') }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
v-if="workspace.customizations.variables"
|
||||||
|
class="menu-item"
|
||||||
|
title="Coming..."
|
||||||
|
>
|
||||||
|
<a class="c-hand p-vcentered disabled">
|
||||||
|
<i class="mdi mdi-shape mr-1 tool-icon" />
|
||||||
|
<span>{{ $t('word.variables') }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
v-if="workspace.customizations.usersManagement"
|
||||||
|
class="menu-item"
|
||||||
|
title="Coming..."
|
||||||
|
>
|
||||||
|
<a class="c-hand p-vcentered disabled">
|
||||||
|
<i class="mdi mdi-account-group mr-1 tool-icon" />
|
||||||
|
<span>{{ $t('message.manageUsers') }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
v-if="schemaChild"
|
v-if="schemaChild && isSettingSupported"
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{'active': selectedTab === 'prop'}"
|
:class="{'active': selectedTab === 'prop'}"
|
||||||
@click="selectTab({uid: workspace.uid, tab: 'prop'})"
|
@click="selectTab({uid: workspace.uid, tab: 'prop'})"
|
||||||
>
|
>
|
||||||
<a class="tab-link">
|
<a class="tab-link">
|
||||||
<i class="mdi mdi-18px mdi-tune mr-1" />
|
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
|
||||||
<span :title="schemaChild">{{ $t('word.properties').toUpperCase() }}: {{ schemaChild }}</span>
|
<span :title="schemaChild">{{ $t('word.settings').toUpperCase() }}: {{ schemaChild }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
@@ -109,11 +141,16 @@
|
|||||||
<WorkspaceQueryTab
|
<WorkspaceQueryTab
|
||||||
v-for="tab of queryTabs"
|
v-for="tab of queryTabs"
|
||||||
:key="tab.uid"
|
:key="tab.uid"
|
||||||
:tab-uid="tab.uid"
|
:tab="tab"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<ModalProcessesList
|
||||||
|
v-if="isProcessesModal"
|
||||||
|
:connection="connection"
|
||||||
|
@close="hideProcessesModal"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -129,6 +166,7 @@ import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger';
|
|||||||
import WorkspacePropsTabRoutine from '@/components/WorkspacePropsTabRoutine';
|
import WorkspacePropsTabRoutine from '@/components/WorkspacePropsTabRoutine';
|
||||||
import WorkspacePropsTabFunction from '@/components/WorkspacePropsTabFunction';
|
import WorkspacePropsTabFunction from '@/components/WorkspacePropsTabFunction';
|
||||||
import WorkspacePropsTabScheduler from '@/components/WorkspacePropsTabScheduler';
|
import WorkspacePropsTabScheduler from '@/components/WorkspacePropsTabScheduler';
|
||||||
|
import ModalProcessesList from '@/components/ModalProcessesList';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Workspace',
|
name: 'Workspace',
|
||||||
@@ -141,14 +179,16 @@ export default {
|
|||||||
WorkspacePropsTabTrigger,
|
WorkspacePropsTabTrigger,
|
||||||
WorkspacePropsTabRoutine,
|
WorkspacePropsTabRoutine,
|
||||||
WorkspacePropsTabFunction,
|
WorkspacePropsTabFunction,
|
||||||
WorkspacePropsTabScheduler
|
WorkspacePropsTabScheduler,
|
||||||
|
ModalProcessesList
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object
|
connection: Object
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
hasWheelEvent: false
|
hasWheelEvent: false,
|
||||||
|
isProcessesModal: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -162,15 +202,31 @@ export default {
|
|||||||
isSelected () {
|
isSelected () {
|
||||||
return this.selectedWorkspace === this.connection.uid;
|
return this.selectedWorkspace === this.connection.uid;
|
||||||
},
|
},
|
||||||
|
isSettingSupported () {
|
||||||
|
if (this.workspace.breadcrumbs.table && this.workspace.customizations.tableSettings) return true;
|
||||||
|
if (this.workspace.breadcrumbs.view && this.workspace.customizations.viewSettings) return true;
|
||||||
|
if (this.workspace.breadcrumbs.trigger && this.workspace.customizations.triggerSettings) return true;
|
||||||
|
if (this.workspace.breadcrumbs.procedure && this.workspace.customizations.routineSettings) return true;
|
||||||
|
if (this.workspace.breadcrumbs.function && this.workspace.customizations.functionSettings) return true;
|
||||||
|
if (this.workspace.breadcrumbs.scheduler && this.workspace.customizations.schedulerSettings) return true;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
selectedTab () {
|
selectedTab () {
|
||||||
if (
|
if (
|
||||||
this.workspace.breadcrumbs.table === null &&
|
(
|
||||||
this.workspace.breadcrumbs.view === null &&
|
this.workspace.breadcrumbs.table === null &&
|
||||||
this.workspace.breadcrumbs.trigger === null &&
|
this.workspace.breadcrumbs.view === null &&
|
||||||
this.workspace.breadcrumbs.procedure === null &&
|
this.workspace.breadcrumbs.trigger === null &&
|
||||||
this.workspace.breadcrumbs.function === null &&
|
this.workspace.breadcrumbs.procedure === null &&
|
||||||
this.workspace.breadcrumbs.scheduler === null &&
|
this.workspace.breadcrumbs.function === null &&
|
||||||
['data', 'prop'].includes(this.workspace.selected_tab)
|
this.workspace.breadcrumbs.scheduler === null &&
|
||||||
|
['data', 'prop'].includes(this.workspace.selected_tab)
|
||||||
|
) ||
|
||||||
|
(
|
||||||
|
this.workspace.breadcrumbs.table === null &&
|
||||||
|
this.workspace.breadcrumbs.view === null &&
|
||||||
|
this.workspace.selected_tab === 'data'
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return this.queryTabs[0].uid;
|
return this.queryTabs[0].uid;
|
||||||
|
|
||||||
@@ -214,7 +270,7 @@ export default {
|
|||||||
removeTab: 'workspaces/removeTab'
|
removeTab: 'workspaces/removeTab'
|
||||||
}),
|
}),
|
||||||
addTab () {
|
addTab () {
|
||||||
this.newTab(this.connection.uid);
|
this.newTab({ uid: this.connection.uid });
|
||||||
|
|
||||||
if (!this.hasWheelEvent) {
|
if (!this.hasWheelEvent) {
|
||||||
this.$refs.tabWrap.addEventListener('wheel', e => {
|
this.$refs.tabWrap.addEventListener('wheel', e => {
|
||||||
@@ -227,6 +283,12 @@ export default {
|
|||||||
closeTab (tUid) {
|
closeTab (tUid) {
|
||||||
if (this.queryTabs.length === 1) return;
|
if (this.queryTabs.length === 1) return;
|
||||||
this.removeTab({ uid: this.connection.uid, tab: tUid });
|
this.removeTab({ uid: this.connection.uid, tab: tUid });
|
||||||
|
},
|
||||||
|
showProcessesModal () {
|
||||||
|
this.isProcessesModal = true;
|
||||||
|
},
|
||||||
|
hideProcessesModal () {
|
||||||
|
this.isProcessesModal = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -242,7 +304,6 @@ export default {
|
|||||||
height: calc(100vh - #{$excluding-size});
|
height: calc(100vh - #{$excluding-size});
|
||||||
|
|
||||||
.tab-block {
|
.tab-block {
|
||||||
background: $bg-color-light;
|
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
@@ -262,7 +323,6 @@ export default {
|
|||||||
|
|
||||||
> a {
|
> a {
|
||||||
padding: 0.2rem 0.8rem;
|
padding: 0.2rem 0.8rem;
|
||||||
color: $body-font-color;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -291,6 +351,42 @@ export default {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.tools-dropdown {
|
||||||
|
.tab-link:focus {
|
||||||
|
opacity: 1;
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
min-width: 100%;
|
||||||
|
|
||||||
|
.menu-item a {
|
||||||
|
border-radius: 0.1rem;
|
||||||
|
color: inherit;
|
||||||
|
display: block;
|
||||||
|
margin: 0 -0.4rem;
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
.tool-icon {
|
||||||
|
line-height: 1;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
z-index: 9;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tools-dropdown + .tab-item {
|
||||||
|
margin-left: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
.workspace-tools-link {
|
.workspace-tools-link {
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
padding-top: 0.3rem;
|
padding-top: 0.3rem;
|
||||||
@@ -310,11 +406,9 @@ export default {
|
|||||||
.th {
|
.th {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
background: $bg-color;
|
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
border-left: none;
|
border-left: none;
|
||||||
border-bottom-width: 2px;
|
border-bottom-width: 2px;
|
||||||
border-color: $bg-color-light;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
@@ -329,7 +423,6 @@ export default {
|
|||||||
.td {
|
.td {
|
||||||
border-right: 1px solid;
|
border-right: 1px solid;
|
||||||
border-bottom: 1px solid;
|
border-bottom: 1px solid;
|
||||||
border-color: $bg-color-light;
|
|
||||||
padding: 0 0.4rem;
|
padding: 0 0.4rem;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
@@ -339,8 +432,6 @@ export default {
|
|||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: inset 0 0 0 1px $body-font-color;
|
|
||||||
background: rgba($color: #000, $alpha: 0.3);
|
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column col-12 empty text-light">
|
<div class="column col-12 empty">
|
||||||
<div class="empty-icon">
|
<div class="empty-icon">
|
||||||
<i class="mdi mdi-48px mdi-power-plug-off" />
|
<i class="mdi mdi-48px mdi-power-plug-off" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
>
|
>
|
||||||
<div class="workspace-explorebar-header">
|
<div class="workspace-explorebar-header">
|
||||||
<span class="workspace-explorebar-title">{{ connectionName }}</span>
|
<span class="workspace-explorebar-title">{{ connectionName }}</span>
|
||||||
<span v-if="workspace.connected" class="workspace-explorebar-tools">
|
<span v-if="workspace.connection_status === 'connected'" class="workspace-explorebar-tools">
|
||||||
<i
|
<i
|
||||||
class="mdi mdi-18px mdi-database-plus c-hand mr-2"
|
class="mdi mdi-18px mdi-database-plus c-hand mr-2"
|
||||||
:title="$t('message.createNewDatabase')"
|
:title="$t('message.createNewSchema')"
|
||||||
@click="showNewDBModal"
|
@click="showNewDBModal"
|
||||||
/>
|
/>
|
||||||
<i
|
<i
|
||||||
@@ -27,24 +27,35 @@
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="workspace-explorebar-search">
|
||||||
|
<div v-if="workspace.connection_status === 'connected'" class="has-icon-right">
|
||||||
|
<input
|
||||||
|
v-model="searchTerm"
|
||||||
|
class="form-input input-sm"
|
||||||
|
type="text"
|
||||||
|
:placeholder="$t('message.searchForElements')"
|
||||||
|
>
|
||||||
|
<i class="form-icon mdi mdi-magnify mdi-18px" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<WorkspaceConnectPanel
|
<WorkspaceConnectPanel
|
||||||
v-if="!workspace.connected"
|
v-if="workspace.connection_status !== 'connected'"
|
||||||
class="workspace-explorebar-body"
|
class="workspace-explorebar-body"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
/>
|
/>
|
||||||
<div v-else class="workspace-explorebar-body">
|
<div v-else class="workspace-explorebar-body">
|
||||||
<WorkspaceExploreBarDatabase
|
<WorkspaceExploreBarSchema
|
||||||
v-for="db of workspace.structure"
|
v-for="db of workspace.structure"
|
||||||
:key="db.name"
|
:key="db.name"
|
||||||
:database="db"
|
:database="db"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
@show-database-context="openDatabaseContext"
|
@show-schema-context="openSchemaContext"
|
||||||
@show-table-context="openTableContext"
|
@show-table-context="openTableContext"
|
||||||
@show-misc-context="openMiscContext"
|
@show-misc-context="openMiscContext"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ModalNewDatabase
|
<ModalNewSchema
|
||||||
v-if="isNewDBModal"
|
v-if="isNewDBModal"
|
||||||
@close="hideNewDBModal"
|
@close="hideNewDBModal"
|
||||||
@reload="refresh"
|
@reload="refresh"
|
||||||
@@ -117,7 +128,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import _ from 'lodash';// TODO: remove
|
|
||||||
|
|
||||||
import Tables from '@/ipc-api/Tables';
|
import Tables from '@/ipc-api/Tables';
|
||||||
import Views from '@/ipc-api/Views';
|
import Views from '@/ipc-api/Views';
|
||||||
@@ -127,11 +137,11 @@ import Functions from '@/ipc-api/Functions';
|
|||||||
import Schedulers from '@/ipc-api/Schedulers';
|
import Schedulers from '@/ipc-api/Schedulers';
|
||||||
|
|
||||||
import WorkspaceConnectPanel from '@/components/WorkspaceConnectPanel';
|
import WorkspaceConnectPanel from '@/components/WorkspaceConnectPanel';
|
||||||
import WorkspaceExploreBarDatabase from '@/components/WorkspaceExploreBarDatabase';
|
import WorkspaceExploreBarSchema from '@/components/WorkspaceExploreBarSchema';
|
||||||
import DatabaseContext from '@/components/WorkspaceExploreBarDatabaseContext';
|
import DatabaseContext from '@/components/WorkspaceExploreBarSchemaContext';
|
||||||
import TableContext from '@/components/WorkspaceExploreBarTableContext';
|
import TableContext from '@/components/WorkspaceExploreBarTableContext';
|
||||||
import MiscContext from '@/components/WorkspaceExploreBarMiscContext';
|
import MiscContext from '@/components/WorkspaceExploreBarMiscContext';
|
||||||
import ModalNewDatabase from '@/components/ModalNewDatabase';
|
import ModalNewSchema from '@/components/ModalNewSchema';
|
||||||
import ModalNewTable from '@/components/ModalNewTable';
|
import ModalNewTable from '@/components/ModalNewTable';
|
||||||
import ModalNewView from '@/components/ModalNewView';
|
import ModalNewView from '@/components/ModalNewView';
|
||||||
import ModalNewTrigger from '@/components/ModalNewTrigger';
|
import ModalNewTrigger from '@/components/ModalNewTrigger';
|
||||||
@@ -143,11 +153,11 @@ export default {
|
|||||||
name: 'WorkspaceExploreBar',
|
name: 'WorkspaceExploreBar',
|
||||||
components: {
|
components: {
|
||||||
WorkspaceConnectPanel,
|
WorkspaceConnectPanel,
|
||||||
WorkspaceExploreBarDatabase,
|
WorkspaceExploreBarSchema,
|
||||||
DatabaseContext,
|
DatabaseContext,
|
||||||
TableContext,
|
TableContext,
|
||||||
MiscContext,
|
MiscContext,
|
||||||
ModalNewDatabase,
|
ModalNewSchema,
|
||||||
ModalNewTable,
|
ModalNewTable,
|
||||||
ModalNewView,
|
ModalNewView,
|
||||||
ModalNewTrigger,
|
ModalNewTrigger,
|
||||||
@@ -172,6 +182,8 @@ export default {
|
|||||||
isNewSchedulerModal: false,
|
isNewSchedulerModal: false,
|
||||||
|
|
||||||
localWidth: null,
|
localWidth: null,
|
||||||
|
explorebarWidthInterval: null,
|
||||||
|
searchTermInterval: null,
|
||||||
isDatabaseContext: false,
|
isDatabaseContext: false,
|
||||||
isTableContext: false,
|
isTableContext: false,
|
||||||
isMiscContext: false,
|
isMiscContext: false,
|
||||||
@@ -182,7 +194,8 @@ export default {
|
|||||||
|
|
||||||
selectedDatabase: '',
|
selectedDatabase: '',
|
||||||
selectedTable: null,
|
selectedTable: null,
|
||||||
selectedMisc: null
|
selectedMisc: null,
|
||||||
|
searchTerm: ''
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -199,11 +212,22 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
localWidth: _.debounce(function (val) {
|
localWidth (val) {
|
||||||
this.changeExplorebarSize(val);
|
clearTimeout(this.explorebarWidthInterval);
|
||||||
}, 500),
|
|
||||||
|
this.explorebarWidthInterval = setTimeout(() => {
|
||||||
|
this.changeExplorebarSize(val);
|
||||||
|
}, 500);
|
||||||
|
},
|
||||||
isSelected (val) {
|
isSelected (val) {
|
||||||
if (val) this.localWidth = this.explorebarSize;
|
if (val) this.localWidth = this.explorebarSize;
|
||||||
|
},
|
||||||
|
searchTerm () {
|
||||||
|
clearTimeout(this.searchTermInterval);
|
||||||
|
|
||||||
|
this.searchTermInterval = setTimeout(() => {
|
||||||
|
this.setSearchTerm(this.searchTerm);
|
||||||
|
}, 200);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
@@ -212,7 +236,7 @@ export default {
|
|||||||
mounted () {
|
mounted () {
|
||||||
const resizer = this.$refs.resizer;
|
const resizer = this.$refs.resizer;
|
||||||
|
|
||||||
resizer.addEventListener('mousedown', (e) => {
|
resizer.addEventListener('mousedown', e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
window.addEventListener('mousemove', this.resize);
|
window.addEventListener('mousemove', this.resize);
|
||||||
@@ -225,6 +249,7 @@ export default {
|
|||||||
refreshStructure: 'workspaces/refreshStructure',
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
selectTab: 'workspaces/selectTab',
|
selectTab: 'workspaces/selectTab',
|
||||||
|
setSearchTerm: 'workspaces/setSearchTerm',
|
||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
changeExplorebarSize: 'settings/changeExplorebarSize'
|
changeExplorebarSize: 'settings/changeExplorebarSize'
|
||||||
}),
|
}),
|
||||||
@@ -274,8 +299,8 @@ export default {
|
|||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
this.addNotification({ status: 'error', message: response });
|
||||||
},
|
},
|
||||||
openDatabaseContext (payload) {
|
openSchemaContext (payload) {
|
||||||
this.selectedDatabase = payload.database;
|
this.selectedDatabase = payload.schema;
|
||||||
this.databaseContextEvent = payload.event;
|
this.databaseContextEvent = payload.event;
|
||||||
this.isDatabaseContext = true;
|
this.isDatabaseContext = true;
|
||||||
},
|
},
|
||||||
@@ -435,8 +460,6 @@ export default {
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
background: $bg-color-gray;
|
|
||||||
box-shadow: 0 0 1px 0 #000;
|
|
||||||
z-index: 8;
|
z-index: 8;
|
||||||
flex: initial;
|
flex: initial;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -477,9 +500,40 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.workspace-explorebar-search {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 0.6rem;
|
||||||
|
height: 28px;
|
||||||
|
|
||||||
|
.has-icon-right {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.1rem;
|
||||||
|
|
||||||
|
.form-icon {
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
height: 1.2rem;
|
||||||
|
padding-left: 0.2rem;
|
||||||
|
|
||||||
|
&:focus + .form-icon {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.workspace-explorebar-body {
|
.workspace-explorebar-body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc((100vh - 30px) - #{$excluding-size});
|
height: calc((100vh - 58px) - #{$excluding-size});
|
||||||
overflow: overlay;
|
overflow: overlay;
|
||||||
padding: 0 0.1rem;
|
padding: 0 0.1rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="['procedure', 'function'].includes(selectedMisc.type)"
|
v-if="['procedure', 'function'].includes(selectedMisc.type)"
|
||||||
class="context-element disabled"
|
class="context-element"
|
||||||
@click="showRunModal"
|
@click="runElementCheck"
|
||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ $t('word.run') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ $t('word.run') }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -29,6 +29,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
|
<ModalAskParameters
|
||||||
|
v-if="isAskingParameters"
|
||||||
|
:local-routine="localElement"
|
||||||
|
:client="workspace.client"
|
||||||
|
@confirm="runElement"
|
||||||
|
@close="hideAskParamsModal"
|
||||||
|
/>
|
||||||
</BaseContextMenu>
|
</BaseContextMenu>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -36,6 +43,7 @@
|
|||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import BaseContextMenu from '@/components/BaseContextMenu';
|
import BaseContextMenu from '@/components/BaseContextMenu';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
import ModalAskParameters from '@/components/ModalAskParameters';
|
||||||
import Triggers from '@/ipc-api/Triggers';
|
import Triggers from '@/ipc-api/Triggers';
|
||||||
import Routines from '@/ipc-api/Routines';
|
import Routines from '@/ipc-api/Routines';
|
||||||
import Functions from '@/ipc-api/Functions';
|
import Functions from '@/ipc-api/Functions';
|
||||||
@@ -45,7 +53,8 @@ export default {
|
|||||||
name: 'WorkspaceExploreBarMiscContext',
|
name: 'WorkspaceExploreBarMiscContext',
|
||||||
components: {
|
components: {
|
||||||
BaseContextMenu,
|
BaseContextMenu,
|
||||||
ConfirmModal
|
ConfirmModal,
|
||||||
|
ModalAskParameters
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
contextEvent: MouseEvent,
|
contextEvent: MouseEvent,
|
||||||
@@ -54,7 +63,9 @@ export default {
|
|||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
isDeleteModal: false,
|
isDeleteModal: false,
|
||||||
isRunModal: false
|
isRunModal: false,
|
||||||
|
isAskingParameters: false,
|
||||||
|
localElement: {}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -83,7 +94,8 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
|
newTab: 'workspaces/newTab'
|
||||||
}),
|
}),
|
||||||
showCreateTableModal () {
|
showCreateTableModal () {
|
||||||
this.$emit('show-create-table-modal');
|
this.$emit('show-create-table-modal');
|
||||||
@@ -94,11 +106,12 @@ export default {
|
|||||||
hideDeleteModal () {
|
hideDeleteModal () {
|
||||||
this.isDeleteModal = false;
|
this.isDeleteModal = false;
|
||||||
},
|
},
|
||||||
showRunModal () {
|
showAskParamsModal () {
|
||||||
this.isRunModal = true;
|
this.isAskingParameters = true;
|
||||||
},
|
},
|
||||||
hideRunModal () {
|
hideAskParamsModal () {
|
||||||
this.isRunModal = false;
|
this.isAskingParameters = false;
|
||||||
|
this.closeContext();
|
||||||
},
|
},
|
||||||
closeContext () {
|
closeContext () {
|
||||||
this.$emit('close-context');
|
this.$emit('close-context');
|
||||||
@@ -148,6 +161,107 @@ export default {
|
|||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
runElementCheck () {
|
||||||
|
if (this.selectedMisc.type === 'procedure')
|
||||||
|
this.runRoutineCheck();
|
||||||
|
else if (this.selectedMisc.type === 'function')
|
||||||
|
this.runFunctionCheck();
|
||||||
|
},
|
||||||
|
runElement (params) {
|
||||||
|
if (this.selectedMisc.type === 'procedure')
|
||||||
|
this.runRoutine(params);
|
||||||
|
else if (this.selectedMisc.type === 'function')
|
||||||
|
this.runFunction(params);
|
||||||
|
},
|
||||||
|
async runRoutineCheck () {
|
||||||
|
const params = {
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
schema: this.workspace.breadcrumbs.schema,
|
||||||
|
routine: this.workspace.breadcrumbs.procedure
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Routines.getRoutineInformations(params);
|
||||||
|
if (status === 'success')
|
||||||
|
this.localElement = response;
|
||||||
|
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.localElement.parameters.length)
|
||||||
|
this.showAskParamsModal();
|
||||||
|
else
|
||||||
|
this.runRoutine();
|
||||||
|
},
|
||||||
|
runRoutine (params) {
|
||||||
|
if (!params) params = [];
|
||||||
|
|
||||||
|
let sql;
|
||||||
|
switch (this.workspace.client) { // TODO: move in a better place
|
||||||
|
case 'maria':
|
||||||
|
case 'mysql':
|
||||||
|
case 'pg':
|
||||||
|
sql = `CALL ${this.localElement.name}(${params.join(',')})`;
|
||||||
|
break;
|
||||||
|
case 'mssql':
|
||||||
|
sql = `EXEC ${this.localElement.name} ${params.join(',')}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sql = `CALL \`${this.localElement.name}\`(${params.join(',')})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.newTab({ uid: this.workspace.uid, content: sql, autorun: true });
|
||||||
|
this.closeContext();
|
||||||
|
},
|
||||||
|
async runFunctionCheck () {
|
||||||
|
const params = {
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
schema: this.workspace.breadcrumbs.schema,
|
||||||
|
func: this.workspace.breadcrumbs.function
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Functions.getFunctionInformations(params);
|
||||||
|
if (status === 'success')
|
||||||
|
this.localElement = response;
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.localElement.parameters.length)
|
||||||
|
this.showAskParamsModal();
|
||||||
|
else
|
||||||
|
this.runFunction();
|
||||||
|
},
|
||||||
|
runFunction (params) {
|
||||||
|
if (!params) params = [];
|
||||||
|
|
||||||
|
let sql;
|
||||||
|
switch (this.workspace.client) { // TODO: move in a better place
|
||||||
|
case 'maria':
|
||||||
|
case 'mysql':
|
||||||
|
sql = `SELECT \`${this.localElement.name}\` (${params.join(',')})`;
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
sql = `SELECT ${this.localElement.name}(${params.join(',')})`;
|
||||||
|
break;
|
||||||
|
case 'mssql':
|
||||||
|
sql = `SELECT ${this.localElement.name} ${params.join(',')}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sql = `SELECT \`${this.localElement.name}\` (${params.join(',')})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.newTab({ uid: this.workspace.uid, content: sql, autorun: true });
|
||||||
|
this.closeContext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
class="accordion-header database-name"
|
class="accordion-header database-name"
|
||||||
:class="{'text-bold': breadcrumbs.schema === database.name}"
|
:class="{'text-bold': breadcrumbs.schema === database.name}"
|
||||||
@click="selectSchema(database.name)"
|
@click="selectSchema(database.name)"
|
||||||
@contextmenu.prevent="showDatabaseContext($event, database.name)"
|
@contextmenu.prevent="showSchemaContext($event, database.name)"
|
||||||
>
|
>
|
||||||
<div v-if="isLoading" class="icon loading" />
|
<div v-if="isLoading" class="icon loading" />
|
||||||
<i v-else class="icon mdi mdi-18px mdi-chevron-right" />
|
<i v-else class="icon mdi mdi-18px mdi-chevron-right" />
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<div class="database-tables">
|
<div class="database-tables">
|
||||||
<ul class="menu menu-nav pt-0">
|
<ul class="menu menu-nav pt-0">
|
||||||
<li
|
<li
|
||||||
v-for="table of database.tables"
|
v-for="table of filteredTables"
|
||||||
:key="table.name"
|
:key="table.name"
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="{'text-bold': breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name)}"
|
:class="{'text-bold': breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name)}"
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
>
|
>
|
||||||
<a class="table-name">
|
<a class="table-name">
|
||||||
<i class="table-icon mdi mdi-18px mr-1" :class="table.type === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
|
<i class="table-icon mdi mdi-18px mr-1" :class="table.type === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
|
||||||
<span>{{ table.name }}</span>
|
<span v-html="highlightWord(table.name)" />
|
||||||
</a>
|
</a>
|
||||||
<div
|
<div
|
||||||
v-if="table.type === 'table'"
|
v-if="table.type === 'table'"
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="database.triggers.length" class="database-misc">
|
<div v-if="filteredTriggers.length && customizations.triggers" class="database-misc">
|
||||||
<details class="accordion">
|
<details class="accordion">
|
||||||
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger}">
|
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger}">
|
||||||
<i class="misc-icon mdi mdi-18px mdi-folder-cog mr-1" />
|
<i class="misc-icon mdi mdi-18px mdi-folder-cog mr-1" />
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<ul class="menu menu-nav pt-0">
|
<ul class="menu menu-nav pt-0">
|
||||||
<li
|
<li
|
||||||
v-for="trigger of database.triggers"
|
v-for="trigger of filteredTriggers"
|
||||||
:key="trigger.name"
|
:key="trigger.name"
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name}"
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name}"
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
>
|
>
|
||||||
<a class="table-name">
|
<a class="table-name">
|
||||||
<i class="table-icon mdi mdi-table-cog mdi-18px mr-1" />
|
<i class="table-icon mdi mdi-table-cog mdi-18px mr-1" />
|
||||||
<span>{{ trigger.name }}</span>
|
<span v-html="highlightWord(trigger.name)" />
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="database.procedures.length" class="database-misc">
|
<div v-if="filteredProcedures.length && customizations.routines" class="database-misc">
|
||||||
<details class="accordion">
|
<details class="accordion">
|
||||||
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure}">
|
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure}">
|
||||||
<i class="misc-icon mdi mdi-18px mdi-folder-sync mr-1" />
|
<i class="misc-icon mdi mdi-18px mdi-folder-sync mr-1" />
|
||||||
@@ -75,8 +75,8 @@
|
|||||||
<div>
|
<div>
|
||||||
<ul class="menu menu-nav pt-0">
|
<ul class="menu menu-nav pt-0">
|
||||||
<li
|
<li
|
||||||
v-for="procedure of database.procedures"
|
v-for="(procedure, i) of filteredProcedures"
|
||||||
:key="procedure.name"
|
:key="`${procedure.name}-${i}`"
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure === procedure.name}"
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure === procedure.name}"
|
||||||
@click="setBreadcrumbs({schema: database.name, procedure: procedure.name})"
|
@click="setBreadcrumbs({schema: database.name, procedure: procedure.name})"
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
>
|
>
|
||||||
<a class="table-name">
|
<a class="table-name">
|
||||||
<i class="table-icon mdi mdi-sync-circle mdi-18px mr-1" />
|
<i class="table-icon mdi mdi-sync-circle mdi-18px mr-1" />
|
||||||
<span>{{ procedure.name }}</span>
|
<span v-html="highlightWord(procedure.name)" />
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="database.functions.length" class="database-misc">
|
<div v-if="filteredFunctions.length && customizations.functions" class="database-misc">
|
||||||
<details class="accordion">
|
<details class="accordion">
|
||||||
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function}">
|
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function}">
|
||||||
<i class="misc-icon mdi mdi-18px mdi-folder-move mr-1" />
|
<i class="misc-icon mdi mdi-18px mdi-folder-move mr-1" />
|
||||||
@@ -103,8 +103,8 @@
|
|||||||
<div>
|
<div>
|
||||||
<ul class="menu menu-nav pt-0">
|
<ul class="menu menu-nav pt-0">
|
||||||
<li
|
<li
|
||||||
v-for="func of database.functions"
|
v-for="(func, i) of filteredFunctions"
|
||||||
:key="func.name"
|
:key="`${func.name}-${i}`"
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}"
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}"
|
||||||
@click="setBreadcrumbs({schema: database.name, function: func.name})"
|
@click="setBreadcrumbs({schema: database.name, function: func.name})"
|
||||||
@@ -112,7 +112,7 @@
|
|||||||
>
|
>
|
||||||
<a class="table-name">
|
<a class="table-name">
|
||||||
<i class="table-icon mdi mdi-arrow-right-bold-box mdi-18px mr-1" />
|
<i class="table-icon mdi mdi-arrow-right-bold-box mdi-18px mr-1" />
|
||||||
<span>{{ func.name }}</span>
|
<span v-html="highlightWord(func.name)" />
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="database.schedulers.length" class="database-misc">
|
<div v-if="filteredSchedulers.length && customizations.schedulers" class="database-misc">
|
||||||
<details class="accordion">
|
<details class="accordion">
|
||||||
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler}">
|
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler}">
|
||||||
<i class="misc-icon mdi mdi-18px mdi-folder-clock mr-1" />
|
<i class="misc-icon mdi mdi-18px mdi-folder-clock mr-1" />
|
||||||
@@ -131,7 +131,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<ul class="menu menu-nav pt-0">
|
<ul class="menu menu-nav pt-0">
|
||||||
<li
|
<li
|
||||||
v-for="scheduler of database.schedulers"
|
v-for="scheduler of filteredSchedulers"
|
||||||
:key="scheduler.name"
|
:key="scheduler.name"
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name}"
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name}"
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
>
|
>
|
||||||
<a class="table-name">
|
<a class="table-name">
|
||||||
<i class="table-icon mdi mdi-calendar-clock mdi-18px mr-1" />
|
<i class="table-icon mdi mdi-calendar-clock mdi-18px mr-1" />
|
||||||
<span>{{ scheduler.name }}</span>
|
<span v-html="highlightWord(scheduler.name)" />
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -157,7 +157,7 @@ import { mapActions, mapGetters } from 'vuex';
|
|||||||
import { formatBytes } from 'common/libs/formatBytes';
|
import { formatBytes } from 'common/libs/formatBytes';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WorkspaceExploreBarDatabase',
|
name: 'WorkspaceExploreBarSchema',
|
||||||
props: {
|
props: {
|
||||||
database: Object,
|
database: Object,
|
||||||
connection: Object
|
connection: Object
|
||||||
@@ -170,11 +170,34 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
getLoadedSchemas: 'workspaces/getLoadedSchemas',
|
getLoadedSchemas: 'workspaces/getLoadedSchemas',
|
||||||
getWorkspace: 'workspaces/getWorkspace'
|
getWorkspace: 'workspaces/getWorkspace',
|
||||||
|
getSearchTerm: 'workspaces/getSearchTerm',
|
||||||
|
applicationTheme: 'settings/getApplicationTheme'
|
||||||
}),
|
}),
|
||||||
|
searchTerm () {
|
||||||
|
return this.getSearchTerm(this.connection.uid);
|
||||||
|
},
|
||||||
|
filteredTables () {
|
||||||
|
return this.database.tables.filter(table => table.name.search(this.searchTerm) >= 0);
|
||||||
|
},
|
||||||
|
filteredTriggers () {
|
||||||
|
return this.database.triggers.filter(trigger => trigger.name.search(this.searchTerm) >= 0);
|
||||||
|
},
|
||||||
|
filteredProcedures () {
|
||||||
|
return this.database.procedures.filter(procedure => procedure.name.search(this.searchTerm) >= 0);
|
||||||
|
},
|
||||||
|
filteredFunctions () {
|
||||||
|
return this.database.functions.filter(func => func.name.search(this.searchTerm) >= 0);
|
||||||
|
},
|
||||||
|
filteredSchedulers () {
|
||||||
|
return this.database.schedulers.filter(scheduler => scheduler.name.search(this.searchTerm) >= 0);
|
||||||
|
},
|
||||||
breadcrumbs () {
|
breadcrumbs () {
|
||||||
return this.getWorkspace(this.connection.uid).breadcrumbs;
|
return this.getWorkspace(this.connection.uid).breadcrumbs;
|
||||||
},
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.getWorkspace(this.connection.uid).customizations;
|
||||||
|
},
|
||||||
loadedSchemas () {
|
loadedSchemas () {
|
||||||
return this.getLoadedSchemas(this.connection.uid);
|
return this.getLoadedSchemas(this.connection.uid);
|
||||||
},
|
},
|
||||||
@@ -203,9 +226,9 @@ export default {
|
|||||||
|
|
||||||
this.changeBreadcrumbs({ schema, table: null });
|
this.changeBreadcrumbs({ schema, table: null });
|
||||||
},
|
},
|
||||||
showDatabaseContext (event, database) {
|
showSchemaContext (event, schema) {
|
||||||
this.changeBreadcrumbs({ schema: database, table: null });
|
this.selectSchema(schema);
|
||||||
this.$emit('show-database-context', { event, database });
|
this.$emit('show-schema-context', { event, schema });
|
||||||
},
|
},
|
||||||
showTableContext (event, table) {
|
showTableContext (event, table) {
|
||||||
this.setBreadcrumbs({ schema: this.database.name, [table.type]: table.name });
|
this.setBreadcrumbs({ schema: this.database.name, [table.type]: table.name });
|
||||||
@@ -217,11 +240,22 @@ export default {
|
|||||||
},
|
},
|
||||||
piePercentage (val) {
|
piePercentage (val) {
|
||||||
const perc = val / this.maxSize * 100;
|
const perc = val / this.maxSize * 100;
|
||||||
return { background: `conic-gradient(lime ${perc}%, white 0)` };
|
if (this.applicationTheme === 'dark')
|
||||||
|
return { background: `conic-gradient(lime ${perc}%, white 0)` };
|
||||||
|
else
|
||||||
|
return { background: `conic-gradient(teal ${perc}%, silver 0)` };
|
||||||
},
|
},
|
||||||
setBreadcrumbs (payload) {
|
setBreadcrumbs (payload) {
|
||||||
if (this.breadcrumbs.schema === payload.schema && this.breadcrumbs.table === payload.table) return;
|
if (this.breadcrumbs.schema === payload.schema && this.breadcrumbs.table === payload.table) return;
|
||||||
this.changeBreadcrumbs(payload);
|
this.changeBreadcrumbs(payload);
|
||||||
|
},
|
||||||
|
highlightWord (string) {
|
||||||
|
if (this.searchTerm) {
|
||||||
|
const regexp = new RegExp(`(${this.searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
||||||
|
return string.replace(regexp, '<span class="text-primary">$1</span>');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -229,6 +263,12 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.workspace-explorebar-database {
|
.workspace-explorebar-database {
|
||||||
|
.database-name {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
.database-name,
|
.database-name,
|
||||||
.misc-name,
|
.misc-name,
|
||||||
a.table-name {
|
a.table-name {
|
||||||
@@ -271,26 +311,15 @@ export default {
|
|||||||
.database-name,
|
.database-name,
|
||||||
.misc-name {
|
.misc-name {
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $body-font-color;
|
|
||||||
background: rgba($color: #fff, $alpha: 0.05);
|
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.table-name {
|
|
||||||
&:hover {
|
|
||||||
color: inherit;
|
|
||||||
background: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-item {
|
.menu-item {
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $body-font-color;
|
|
||||||
background: rgba($color: #fff, $alpha: 0.05);
|
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,27 +7,55 @@
|
|||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-plus text-light pr-1" /> {{ $t('word.add') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-plus text-light pr-1" /> {{ $t('word.add') }}</span>
|
||||||
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
|
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
|
||||||
<div class="context-submenu">
|
<div class="context-submenu">
|
||||||
<div class="context-element" @click="showCreateTableModal">
|
<div
|
||||||
|
v-if="workspace.customizations.tableAdd"
|
||||||
|
class="context-element"
|
||||||
|
@click="showCreateTableModal"
|
||||||
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table text-light pr-1" /> {{ $t('word.table') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table text-light pr-1" /> {{ $t('word.table') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element" @click="showCreateViewModal">
|
<div
|
||||||
|
v-if="workspace.customizations.viewAdd"
|
||||||
|
class="context-element"
|
||||||
|
@click="showCreateViewModal"
|
||||||
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table-eye text-light pr-1" /> {{ $t('word.view') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table-eye text-light pr-1" /> {{ $t('word.view') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element" @click="showCreateTriggerModal">
|
<div
|
||||||
|
v-if="workspace.customizations.triggerAdd"
|
||||||
|
class="context-element"
|
||||||
|
@click="showCreateTriggerModal"
|
||||||
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ $tc('word.trigger', 1) }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ $tc('word.trigger', 1) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element" @click="showCreateRoutineModal">
|
<div
|
||||||
|
v-if="workspace.customizations.routineAdd"
|
||||||
|
class="context-element"
|
||||||
|
@click="showCreateRoutineModal"
|
||||||
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-sync-circle pr-1" /> {{ $tc('word.storedRoutine', 1) }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-sync-circle pr-1" /> {{ $tc('word.storedRoutine', 1) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element" @click="showCreateFunctionModal">
|
<div
|
||||||
|
v-if="workspace.customizations.functionAdd"
|
||||||
|
class="context-element"
|
||||||
|
@click="showCreateFunctionModal"
|
||||||
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.function', 1) }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.function', 1) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element" @click="showCreateSchedulerModal">
|
<div
|
||||||
|
v-if="workspace.customizations.schedulerAdd"
|
||||||
|
class="context-element"
|
||||||
|
@click="showCreateSchedulerModal"
|
||||||
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ $tc('word.scheduler', 1) }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ $tc('word.scheduler', 1) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element" @click="showEditModal">
|
<div
|
||||||
|
v-if="workspace.customizations.schemaEdit"
|
||||||
|
class="context-element"
|
||||||
|
@click="showEditModal"
|
||||||
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-database-edit text-light pr-1" /> {{ $t('word.edit') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-database-edit text-light pr-1" /> {{ $t('word.edit') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element" @click="showDeleteModal">
|
<div class="context-element" @click="showDeleteModal">
|
||||||
@@ -36,12 +64,12 @@
|
|||||||
|
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
v-if="isDeleteModal"
|
v-if="isDeleteModal"
|
||||||
@confirm="deleteDatabase"
|
@confirm="deleteSchema"
|
||||||
@hide="hideDeleteModal"
|
@hide="hideDeleteModal"
|
||||||
>
|
>
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-database-remove mr-1" /> {{ $t('message.deleteDatabase') }}
|
<i class="mdi mdi-24px mdi-database-remove mr-1" /> {{ $t('message.deleteSchema') }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div slot="body">
|
<div slot="body">
|
||||||
@@ -50,7 +78,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
<ModalEditDatabase
|
<ModalEditSchema
|
||||||
v-if="isEditModal"
|
v-if="isEditModal"
|
||||||
:selected-database="selectedDatabase"
|
:selected-database="selectedDatabase"
|
||||||
@close="hideEditModal"
|
@close="hideEditModal"
|
||||||
@@ -62,15 +90,15 @@
|
|||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import BaseContextMenu from '@/components/BaseContextMenu';
|
import BaseContextMenu from '@/components/BaseContextMenu';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
import ModalEditDatabase from '@/components/ModalEditDatabase';
|
import ModalEditSchema from '@/components/ModalEditSchema';
|
||||||
import Database from '@/ipc-api/Database';
|
import Schema from '@/ipc-api/Schema';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WorkspaceExploreBarDatabaseContext',
|
name: 'WorkspaceExploreBarSchemaContext',
|
||||||
components: {
|
components: {
|
||||||
BaseContextMenu,
|
BaseContextMenu,
|
||||||
ConfirmModal,
|
ConfirmModal,
|
||||||
ModalEditDatabase
|
ModalEditSchema
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
contextEvent: MouseEvent,
|
contextEvent: MouseEvent,
|
||||||
@@ -130,9 +158,9 @@ export default {
|
|||||||
closeContext () {
|
closeContext () {
|
||||||
this.$emit('close-context');
|
this.$emit('close-context');
|
||||||
},
|
},
|
||||||
async deleteDatabase () {
|
async deleteSchema () {
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Database.deleteDatabase({
|
const { status, response } = await Schema.deleteSchema({
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
database: this.selectedDatabase
|
database: this.selectedDatabase
|
||||||
});
|
});
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
:confirm-text="$t('word.confirm')"
|
:confirm-text="$t('word.confirm')"
|
||||||
size="medium"
|
size="medium"
|
||||||
|
class="options-modal"
|
||||||
@confirm="confirmForeignsChange"
|
@confirm="confirmForeignsChange"
|
||||||
@hide="$emit('hide')"
|
@hide="$emit('hide')"
|
||||||
>
|
>
|
||||||
@@ -36,7 +37,7 @@
|
|||||||
v-for="foreign in foreignProxy"
|
v-for="foreign in foreignProxy"
|
||||||
:key="foreign._id"
|
:key="foreign._id"
|
||||||
class="tile tile-centered c-hand mb-1 p-1"
|
class="tile tile-centered c-hand mb-1 p-1"
|
||||||
:class="{'selected-foreign': selectedForeignID === foreign._id}"
|
:class="{'selected-element': selectedForeignID === foreign._id}"
|
||||||
@click="selectForeign($event, foreign._id)"
|
@click="selectForeign($event, foreign._id)"
|
||||||
>
|
>
|
||||||
<div class="tile-icon">
|
<div class="tile-icon">
|
||||||
@@ -268,8 +269,10 @@ export default {
|
|||||||
this.$emit('foreigns-update', this.foreignProxy);
|
this.$emit('foreigns-update', this.foreignProxy);
|
||||||
},
|
},
|
||||||
selectForeign (event, id) {
|
selectForeign (event, id) {
|
||||||
if (this.selectedForeignID !== id && !event.target.classList.contains('remove-field'))
|
if (this.selectedForeignID !== id && !event.target.classList.contains('remove-field')) {
|
||||||
this.selectedForeignID = id;
|
this.selectedForeignID = id;
|
||||||
|
this.getRefFields();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getModalInnerHeight () {
|
getModalInnerHeight () {
|
||||||
const modalBody = document.querySelector('.modal-body');
|
const modalBody = document.querySelector('.modal-body');
|
||||||
@@ -369,23 +372,16 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $bg-color-light;
|
|
||||||
|
|
||||||
.tile-action {
|
.tile-action {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected-foreign {
|
&.selected-element {
|
||||||
background: $bg-color-light;
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-col {
|
|
||||||
border-left: 2px solid $bg-color-light;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fields-list {
|
.fields-list {
|
||||||
max-height: 80px;
|
max-height: 80px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|||||||
@@ -26,7 +26,19 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.languages" class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.language') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="optionsProxy.language" class="form-select">
|
||||||
|
<option v-for="language in customizations.languages" :key="language">
|
||||||
|
{{ language }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.definer" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.definer') }}
|
{{ $t('word.definer') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -65,6 +77,12 @@
|
|||||||
class="form-select text-uppercase"
|
class="form-select text-uppercase"
|
||||||
style="width: 0;"
|
style="width: 0;"
|
||||||
>
|
>
|
||||||
|
<option v-if="localOptions.returns === 'VOID'">
|
||||||
|
VOID
|
||||||
|
</option>
|
||||||
|
<option v-if="!isInDataTypes">
|
||||||
|
{{ localOptions.returns }}
|
||||||
|
</option>
|
||||||
<optgroup
|
<optgroup
|
||||||
v-for="group in workspace.dataTypes"
|
v-for="group in workspace.dataTypes"
|
||||||
:key="group.group"
|
:key="group.group"
|
||||||
@@ -81,6 +99,7 @@
|
|||||||
</optgroup>
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
<input
|
<input
|
||||||
|
v-if="customizations.parametersLength"
|
||||||
v-model="optionsProxy.returnsLength"
|
v-model="optionsProxy.returnsLength"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
type="number"
|
type="number"
|
||||||
@@ -89,7 +108,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.comment" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.comment') }}
|
{{ $t('word.comment') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -112,7 +131,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.functionDataAccess" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('message.dataAccess') }}
|
{{ $t('message.dataAccess') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -125,7 +144,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.functionDeterministic" class="form-group">
|
||||||
<div class="col-4" />
|
<div class="col-4" />
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<label class="form-checkbox form-inline">
|
<label class="form-checkbox form-inline">
|
||||||
@@ -159,6 +178,19 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
isTableNameValid () {
|
isTableNameValid () {
|
||||||
return this.optionsProxy.name !== '';
|
return this.optionsProxy.name !== '';
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace.customizations;
|
||||||
|
},
|
||||||
|
isInDataTypes () {
|
||||||
|
let typeNames = [];
|
||||||
|
for (const group of this.workspace.dataTypes) {
|
||||||
|
typeNames = group.types.reduce((acc, curr) => {
|
||||||
|
acc.push(curr.name);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
return typeNames.includes(this.localOptions.returns);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
:confirm-text="$t('word.confirm')"
|
:confirm-text="$t('word.confirm')"
|
||||||
size="medium"
|
size="medium"
|
||||||
@confirm="confirmIndexesChange"
|
class="options-modal"
|
||||||
|
@confirm="confirmParametersChange"
|
||||||
@hide="$emit('hide')"
|
@hide="$emit('hide')"
|
||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
@@ -34,27 +35,27 @@
|
|||||||
<div ref="parametersPanel" class="panel-body p-0 pr-1">
|
<div ref="parametersPanel" class="panel-body p-0 pr-1">
|
||||||
<div
|
<div
|
||||||
v-for="param in parametersProxy"
|
v-for="param in parametersProxy"
|
||||||
:key="param.name"
|
:key="param._id"
|
||||||
class="tile tile-centered c-hand mb-1 p-1"
|
class="tile tile-centered c-hand mb-1 p-1"
|
||||||
:class="{'selected-param': selectedParam === param.name}"
|
:class="{'selected-element': selectedParam === param._id}"
|
||||||
@click="selectParameter($event, param.name)"
|
@click="selectParameter($event, param._id)"
|
||||||
>
|
>
|
||||||
<div class="tile-icon">
|
<div class="tile-icon">
|
||||||
<div>
|
<div>
|
||||||
<i class="mdi mdi-hexagon mdi-24px" :class="`type-${param.type.toLowerCase()}`" />
|
<i class="mdi mdi-hexagon mdi-24px" :class="typeClass(param.type)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tile-content">
|
<div class="tile-content">
|
||||||
<div class="tile-title">
|
<div class="tile-title">
|
||||||
{{ param.name }}
|
{{ param.name }}
|
||||||
</div>
|
</div>
|
||||||
<small class="tile-subtitle text-gray">{{ param.type }}{{ param.length ? `(${param.length})` : '' }}</small>
|
<small class="tile-subtitle text-gray">{{ param.type }}{{ param.length ? `(${param.length})` : '' }} · {{ param.context }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="tile-action">
|
<div class="tile-action">
|
||||||
<button
|
<button
|
||||||
class="btn btn-link remove-field p-0 mr-2"
|
class="btn btn-link remove-field p-0 mr-2"
|
||||||
:title="$t('word.delete')"
|
:title="$t('word.delete')"
|
||||||
@click.prevent="removeParameter(param.name)"
|
@click.prevent="removeParameter(param._id)"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-close" />
|
<i class="mdi mdi-close" />
|
||||||
</button>
|
</button>
|
||||||
@@ -105,7 +106,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.parametersLength" class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.length') }}
|
{{ $t('word.length') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -118,6 +119,37 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="customizations.functionContext" class="form-group">
|
||||||
|
<label class="form-label col-3">
|
||||||
|
{{ $t('word.context') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="selectedParamObj.context"
|
||||||
|
type="radio"
|
||||||
|
name="context"
|
||||||
|
value="IN"
|
||||||
|
> <i class="form-icon" /> IN
|
||||||
|
</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="selectedParamObj.context"
|
||||||
|
type="radio"
|
||||||
|
value="OUT"
|
||||||
|
name="context"
|
||||||
|
> <i class="form-icon" /> OUT
|
||||||
|
</label>
|
||||||
|
<label class="form-radio">
|
||||||
|
<input
|
||||||
|
v-model="selectedParamObj.context"
|
||||||
|
type="radio"
|
||||||
|
value="INOUT"
|
||||||
|
name="context"
|
||||||
|
> <i class="form-icon" /> INOUT
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div v-if="!parametersProxy.length" class="empty">
|
<div v-if="!parametersProxy.length" class="empty">
|
||||||
<div class="empty-icon">
|
<div class="empty-icon">
|
||||||
@@ -139,6 +171,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -162,10 +195,13 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
selectedParamObj () {
|
selectedParamObj () {
|
||||||
return this.parametersProxy.find(param => param.name === this.selectedParam);
|
return this.parametersProxy.find(param => param._id === this.selectedParam);
|
||||||
},
|
},
|
||||||
isChanged () {
|
isChanged () {
|
||||||
return JSON.stringify(this.localParameters) !== JSON.stringify(this.parametersProxy);
|
return JSON.stringify(this.localParameters) !== JSON.stringify(this.parametersProxy);
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace.customizations;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
@@ -182,12 +218,17 @@ export default {
|
|||||||
window.removeEventListener('resize', this.getModalInnerHeight);
|
window.removeEventListener('resize', this.getModalInnerHeight);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
confirmIndexesChange () {
|
typeClass (type) {
|
||||||
|
if (type)
|
||||||
|
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
confirmParametersChange () {
|
||||||
this.$emit('parameters-update', this.parametersProxy);
|
this.$emit('parameters-update', this.parametersProxy);
|
||||||
},
|
},
|
||||||
selectParameter (event, name) {
|
selectParameter (event, uid) {
|
||||||
if (this.selectedParam !== name && !event.target.classList.contains('remove-field'))
|
if (this.selectedParam !== uid && !event.target.classList.contains('remove-field'))
|
||||||
this.selectedParam = name;
|
this.selectedParam = uid;
|
||||||
},
|
},
|
||||||
getModalInnerHeight () {
|
getModalInnerHeight () {
|
||||||
const modalBody = document.querySelector('.modal-body');
|
const modalBody = document.querySelector('.modal-body');
|
||||||
@@ -196,6 +237,7 @@ export default {
|
|||||||
},
|
},
|
||||||
addParameter () {
|
addParameter () {
|
||||||
this.parametersProxy = [...this.parametersProxy, {
|
this.parametersProxy = [...this.parametersProxy, {
|
||||||
|
_id: uidGen(),
|
||||||
name: `Param${this.i++}`,
|
name: `Param${this.i++}`,
|
||||||
type: 'INT',
|
type: 'INT',
|
||||||
context: 'IN',
|
context: 'IN',
|
||||||
@@ -209,8 +251,8 @@ export default {
|
|||||||
this.$refs.parametersPanel.scrollTop = this.$refs.parametersPanel.scrollHeight + 60;
|
this.$refs.parametersPanel.scrollTop = this.$refs.parametersPanel.scrollHeight + 60;
|
||||||
}, 20);
|
}, 20);
|
||||||
},
|
},
|
||||||
removeParameter (name) {
|
removeParameter (uid) {
|
||||||
this.parametersProxy = this.parametersProxy.filter(param => param.name !== name);
|
this.parametersProxy = this.parametersProxy.filter(param => param._id !== uid);
|
||||||
|
|
||||||
if (this.selectedParam === name && this.parametersProxy.length)
|
if (this.selectedParam === name && this.parametersProxy.length)
|
||||||
this.resetSelectedID();
|
this.resetSelectedID();
|
||||||
@@ -223,7 +265,7 @@ export default {
|
|||||||
this.resetSelectedID();
|
this.resetSelectedID();
|
||||||
},
|
},
|
||||||
resetSelectedID () {
|
resetSelectedID () {
|
||||||
this.selectedParam = this.parametersProxy.length ? this.parametersProxy[0].name : '';
|
this.selectedParam = this.parametersProxy.length ? this.parametersProxy[0]._id : '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -242,23 +284,16 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $bg-color-light;
|
|
||||||
|
|
||||||
.tile-action {
|
.tile-action {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected-param {
|
&.selected-element {
|
||||||
background: $bg-color-light;
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-col {
|
|
||||||
border-left: 2px solid $bg-color-light;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fields-list {
|
.fields-list {
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
:confirm-text="$t('word.confirm')"
|
:confirm-text="$t('word.confirm')"
|
||||||
size="medium"
|
size="medium"
|
||||||
|
class="options-modal"
|
||||||
@confirm="confirmIndexesChange"
|
@confirm="confirmIndexesChange"
|
||||||
@hide="$emit('hide')"
|
@hide="$emit('hide')"
|
||||||
>
|
>
|
||||||
@@ -36,7 +37,7 @@
|
|||||||
v-for="index in indexesProxy"
|
v-for="index in indexesProxy"
|
||||||
:key="index._id"
|
:key="index._id"
|
||||||
class="tile tile-centered c-hand mb-1 p-1"
|
class="tile tile-centered c-hand mb-1 p-1"
|
||||||
:class="{'selected-index': selectedIndexID === index._id}"
|
:class="{'selected-element': selectedIndexID === index._id}"
|
||||||
@click="selectIndex($event, index._id)"
|
@click="selectIndex($event, index._id)"
|
||||||
>
|
>
|
||||||
<div class="tile-icon">
|
<div class="tile-icon">
|
||||||
@@ -256,23 +257,16 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $bg-color-light;
|
|
||||||
|
|
||||||
.tile-action {
|
.tile-action {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected-index {
|
&.selected-element {
|
||||||
background: $bg-color-light;
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-col {
|
|
||||||
border-left: 2px solid $bg-color-light;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fields-list {
|
.fields-list {
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="workspace.customizations.comment" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.comment') }}
|
{{ $t('word.comment') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="workspace.customizations.autoIncrement" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.autoIncrement') }}
|
{{ $t('word.autoIncrement') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="workspace.customizations.collations" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.collation') }}
|
{{ $t('word.collation') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="workspace.customizations.engines" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.engine') }}
|
{{ $t('word.engine') }}
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -26,7 +26,19 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.languages" class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.language') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="optionsProxy.language" class="form-select">
|
||||||
|
<option v-for="language in customizations.languages" :key="language">
|
||||||
|
{{ language }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.definer" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.definer') }}
|
{{ $t('word.definer') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -54,7 +66,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.comment" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.comment') }}
|
{{ $t('word.comment') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -77,7 +89,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.procedureDataAccess" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('message.dataAccess') }}
|
{{ $t('message.dataAccess') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -90,7 +102,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.procedureDeterministic" class="form-group">
|
||||||
<div class="col-4" />
|
<div class="col-4" />
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<label class="form-checkbox form-inline">
|
<label class="form-checkbox form-inline">
|
||||||
@@ -124,6 +136,9 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
isTableNameValid () {
|
isTableNameValid () {
|
||||||
return this.optionsProxy.name !== '';
|
return this.optionsProxy.name !== '';
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace.customizations;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
:confirm-text="$t('word.confirm')"
|
:confirm-text="$t('word.confirm')"
|
||||||
size="medium"
|
size="medium"
|
||||||
@confirm="confirmIndexesChange"
|
class="options-modal"
|
||||||
|
@confirm="confirmParametersChange"
|
||||||
@hide="$emit('hide')"
|
@hide="$emit('hide')"
|
||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
@@ -34,14 +35,14 @@
|
|||||||
<div ref="parametersPanel" class="panel-body p-0 pr-1">
|
<div ref="parametersPanel" class="panel-body p-0 pr-1">
|
||||||
<div
|
<div
|
||||||
v-for="param in parametersProxy"
|
v-for="param in parametersProxy"
|
||||||
:key="param.name"
|
:key="param._id"
|
||||||
class="tile tile-centered c-hand mb-1 p-1"
|
class="tile tile-centered c-hand mb-1 p-1"
|
||||||
:class="{'selected-param': selectedParam === param.name}"
|
:class="{'selected-element': selectedParam === param._id}"
|
||||||
@click="selectParameter($event, param.name)"
|
@click="selectParameter($event, param._id)"
|
||||||
>
|
>
|
||||||
<div class="tile-icon">
|
<div class="tile-icon">
|
||||||
<div>
|
<div>
|
||||||
<i class="mdi mdi-hexagon mdi-24px" :class="`type-${param.type.toLowerCase()}`" />
|
<i class="mdi mdi-hexagon mdi-24px" :class="typeClass(param.type)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tile-content">
|
<div class="tile-content">
|
||||||
@@ -54,7 +55,7 @@
|
|||||||
<button
|
<button
|
||||||
class="btn btn-link remove-field p-0 mr-2"
|
class="btn btn-link remove-field p-0 mr-2"
|
||||||
:title="$t('word.delete')"
|
:title="$t('word.delete')"
|
||||||
@click.prevent="removeParameter(param.name)"
|
@click.prevent="removeParameter(param._id)"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-close" />
|
<i class="mdi mdi-close" />
|
||||||
</button>
|
</button>
|
||||||
@@ -105,7 +106,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.parametersLength" class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.length') }}
|
{{ $t('word.length') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -118,7 +119,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.procedureContext" class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.context') }}
|
{{ $t('word.context') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -170,6 +171,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -193,10 +195,13 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
selectedParamObj () {
|
selectedParamObj () {
|
||||||
return this.parametersProxy.find(param => param.name === this.selectedParam);
|
return this.parametersProxy.find(param => param._id === this.selectedParam);
|
||||||
},
|
},
|
||||||
isChanged () {
|
isChanged () {
|
||||||
return JSON.stringify(this.localParameters) !== JSON.stringify(this.parametersProxy);
|
return JSON.stringify(this.localParameters) !== JSON.stringify(this.parametersProxy);
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace.customizations;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
@@ -213,12 +218,17 @@ export default {
|
|||||||
window.removeEventListener('resize', this.getModalInnerHeight);
|
window.removeEventListener('resize', this.getModalInnerHeight);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
confirmIndexesChange () {
|
typeClass (type) {
|
||||||
|
if (type)
|
||||||
|
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
confirmParametersChange () {
|
||||||
this.$emit('parameters-update', this.parametersProxy);
|
this.$emit('parameters-update', this.parametersProxy);
|
||||||
},
|
},
|
||||||
selectParameter (event, name) {
|
selectParameter (event, uid) {
|
||||||
if (this.selectedParam !== name && !event.target.classList.contains('remove-field'))
|
if (this.selectedParam !== uid && !event.target.classList.contains('remove-field'))
|
||||||
this.selectedParam = name;
|
this.selectedParam = uid;
|
||||||
},
|
},
|
||||||
getModalInnerHeight () {
|
getModalInnerHeight () {
|
||||||
const modalBody = document.querySelector('.modal-body');
|
const modalBody = document.querySelector('.modal-body');
|
||||||
@@ -227,10 +237,11 @@ export default {
|
|||||||
},
|
},
|
||||||
addParameter () {
|
addParameter () {
|
||||||
this.parametersProxy = [...this.parametersProxy, {
|
this.parametersProxy = [...this.parametersProxy, {
|
||||||
name: `Param${this.i++}`,
|
_id: uidGen(),
|
||||||
type: 'INT',
|
name: `param${this.i++}`,
|
||||||
|
type: this.workspace.dataTypes[0].types[0].name,
|
||||||
context: 'IN',
|
context: 'IN',
|
||||||
length: 10
|
length: ''
|
||||||
}];
|
}];
|
||||||
|
|
||||||
if (this.parametersProxy.length === 1)
|
if (this.parametersProxy.length === 1)
|
||||||
@@ -240,8 +251,8 @@ export default {
|
|||||||
this.$refs.parametersPanel.scrollTop = this.$refs.parametersPanel.scrollHeight + 60;
|
this.$refs.parametersPanel.scrollTop = this.$refs.parametersPanel.scrollHeight + 60;
|
||||||
}, 20);
|
}, 20);
|
||||||
},
|
},
|
||||||
removeParameter (name) {
|
removeParameter (uid) {
|
||||||
this.parametersProxy = this.parametersProxy.filter(param => param.name !== name);
|
this.parametersProxy = this.parametersProxy.filter(param => param._id !== uid);
|
||||||
|
|
||||||
if (this.selectedParam === name && this.parametersProxy.length)
|
if (this.selectedParam === name && this.parametersProxy.length)
|
||||||
this.resetSelectedID();
|
this.resetSelectedID();
|
||||||
@@ -254,7 +265,7 @@ export default {
|
|||||||
this.resetSelectedID();
|
this.resetSelectedID();
|
||||||
},
|
},
|
||||||
resetSelectedID () {
|
resetSelectedID () {
|
||||||
this.selectedParam = this.parametersProxy.length ? this.parametersProxy[0].name : '';
|
this.selectedParam = this.parametersProxy.length ? this.parametersProxy[0]._id : '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -273,23 +284,16 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $bg-color-light;
|
|
||||||
|
|
||||||
.tile-action {
|
.tile-action {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected-param {
|
&.selected-element {
|
||||||
background: $bg-color-light;
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-col {
|
|
||||||
border-left: 2px solid $bg-color-light;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fields-list {
|
.fields-list {
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|||||||
@@ -138,7 +138,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
import { mask } from 'vue-the-mask';
|
import { VueMaskDirective } from 'v-mask';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -147,7 +147,7 @@ export default {
|
|||||||
ConfirmModal
|
ConfirmModal
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
mask
|
mask: VueMaskDirective
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
localOptions: Object,
|
localOptions: Object,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
|
title="CTRL+S"
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ $t('word.save') }}</span>
|
||||||
@@ -68,6 +69,7 @@
|
|||||||
@remove-field="removeField"
|
@remove-field="removeField"
|
||||||
@add-new-index="addNewIndex"
|
@add-new-index="addNewIndex"
|
||||||
@add-to-index="addToIndex"
|
@add-to-index="addToIndex"
|
||||||
|
@rename-field="renameField"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkspacePropsOptionsModal
|
<WorkspacePropsOptionsModal
|
||||||
@@ -149,6 +151,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
getWorkspace: 'workspaces/getWorkspace',
|
getWorkspace: 'workspaces/getWorkspace',
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
getDatabaseVariable: 'workspaces/getDatabaseVariable'
|
getDatabaseVariable: 'workspaces/getDatabaseVariable'
|
||||||
}),
|
}),
|
||||||
workspace () {
|
workspace () {
|
||||||
@@ -162,7 +165,7 @@ export default {
|
|||||||
return this.getDatabaseVariable(this.connection.uid, 'default_storage_engine').value || '';
|
return this.getDatabaseVariable(this.connection.uid, 'default_storage_engine').value || '';
|
||||||
},
|
},
|
||||||
isSelected () {
|
isSelected () {
|
||||||
return this.workspace.selected_tab === 'prop';
|
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.table;
|
||||||
},
|
},
|
||||||
schema () {
|
schema () {
|
||||||
return this.workspace.breadcrumbs.schema;
|
return this.workspace.breadcrumbs.schema;
|
||||||
@@ -199,6 +202,12 @@ export default {
|
|||||||
this.setUnsavedChanges(val);
|
this.setUnsavedChanges(val);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
@@ -433,11 +442,11 @@ export default {
|
|||||||
_id: uidGen(),
|
_id: uidGen(),
|
||||||
name: `${this.$tc('word.field', 1)}_${++this.newFieldsCounter}`,
|
name: `${this.$tc('word.field', 1)}_${++this.newFieldsCounter}`,
|
||||||
key: '',
|
key: '',
|
||||||
type: 'int',
|
type: this.workspace.dataTypes[0].types[0].name,
|
||||||
schema: this.schema,
|
schema: this.schema,
|
||||||
table: this.table,
|
table: this.table,
|
||||||
numPrecision: null,
|
numPrecision: null,
|
||||||
numLength: null,
|
numLength: this.workspace.dataTypes[0].types[0].length,
|
||||||
datePrecision: null,
|
datePrecision: null,
|
||||||
charLength: null,
|
charLength: null,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
@@ -457,6 +466,20 @@ export default {
|
|||||||
scrollable.scrollTop = scrollable.scrollHeight + 30;
|
scrollable.scrollTop = scrollable.scrollHeight + 30;
|
||||||
}, 20);
|
}, 20);
|
||||||
},
|
},
|
||||||
|
renameField (payload) {
|
||||||
|
this.localIndexes = this.localIndexes.map(index => {
|
||||||
|
const fi = index.fields.findIndex(field => field === payload.old);
|
||||||
|
if (fi !== -1)
|
||||||
|
index.fields[fi] = payload.new;
|
||||||
|
return index;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.localKeyUsage = this.localKeyUsage.map(key => {
|
||||||
|
if (key.field === payload.old)
|
||||||
|
key.field = payload.new;
|
||||||
|
return key;
|
||||||
|
});
|
||||||
|
},
|
||||||
removeField (uid) {
|
removeField (uid) {
|
||||||
this.localFields = this.localFields.filter(field => field._id !== uid);
|
this.localFields = this.localFields.filter(field => field._id !== uid);
|
||||||
},
|
},
|
||||||
@@ -504,6 +527,15 @@ export default {
|
|||||||
},
|
},
|
||||||
foreignsUpdate (foreigns) {
|
foreignsUpdate (foreigns) {
|
||||||
this.localKeyUsage = foreigns;
|
this.localKeyUsage = foreigns;
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
if (this.isSelected) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
||||||
|
if (this.isChanged)
|
||||||
|
this.saveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
|
title="CTRL+S"
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ $t('word.save') }}</span>
|
||||||
@@ -25,9 +26,9 @@
|
|||||||
<div class="divider-vert py-3" />
|
<div class="divider-vert py-3" />
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm disabled"
|
class="btn btn-dark btn-sm"
|
||||||
:disabled="isChanged"
|
:disabled="isChanged"
|
||||||
@click="false"
|
@click="runFunctionCheck"
|
||||||
>
|
>
|
||||||
<span>{{ $t('word.run') }}</span>
|
<span>{{ $t('word.run') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-play ml-1" />
|
<i class="mdi mdi-24px mdi-play ml-1" />
|
||||||
@@ -47,7 +48,7 @@
|
|||||||
<BaseLoader v-if="isLoading" />
|
<BaseLoader v-if="isLoading" />
|
||||||
<label class="form-label ml-2">{{ $t('message.functionBody') }}</label>
|
<label class="form-label ml-2">{{ $t('message.functionBody') }}</label>
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
v-if="isSelected"
|
v-show="isSelected"
|
||||||
ref="queryEditor"
|
ref="queryEditor"
|
||||||
:value.sync="localFunction.sql"
|
:value.sync="localFunction.sql"
|
||||||
:workspace="workspace"
|
:workspace="workspace"
|
||||||
@@ -70,15 +71,24 @@
|
|||||||
@hide="hideParamsModal"
|
@hide="hideParamsModal"
|
||||||
@parameters-update="parametersUpdate"
|
@parameters-update="parametersUpdate"
|
||||||
/>
|
/>
|
||||||
|
<ModalAskParameters
|
||||||
|
v-if="isAskingParameters"
|
||||||
|
:local-routine="localFunction"
|
||||||
|
:client="workspace.client"
|
||||||
|
@confirm="runFunction"
|
||||||
|
@close="hideAskParamsModal"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import BaseLoader from '@/components/BaseLoader';
|
import BaseLoader from '@/components/BaseLoader';
|
||||||
import QueryEditor from '@/components/QueryEditor';
|
import QueryEditor from '@/components/QueryEditor';
|
||||||
import WorkspacePropsFunctionOptionsModal from '@/components/WorkspacePropsFunctionOptionsModal';
|
import WorkspacePropsFunctionOptionsModal from '@/components/WorkspacePropsFunctionOptionsModal';
|
||||||
import WorkspacePropsFunctionParamsModal from '@/components/WorkspacePropsFunctionParamsModal';
|
import WorkspacePropsFunctionParamsModal from '@/components/WorkspacePropsFunctionParamsModal';
|
||||||
|
import ModalAskParameters from '@/components/ModalAskParameters';
|
||||||
import Functions from '@/ipc-api/Functions';
|
import Functions from '@/ipc-api/Functions';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -87,7 +97,8 @@ export default {
|
|||||||
BaseLoader,
|
BaseLoader,
|
||||||
QueryEditor,
|
QueryEditor,
|
||||||
WorkspacePropsFunctionOptionsModal,
|
WorkspacePropsFunctionOptionsModal,
|
||||||
WorkspacePropsFunctionParamsModal
|
WorkspacePropsFunctionParamsModal,
|
||||||
|
ModalAskParameters
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object,
|
connection: Object,
|
||||||
@@ -100,6 +111,7 @@ export default {
|
|||||||
isSaving: false,
|
isSaving: false,
|
||||||
isOptionsModal: false,
|
isOptionsModal: false,
|
||||||
isParamsModal: false,
|
isParamsModal: false,
|
||||||
|
isAskingParameters: false,
|
||||||
originalFunction: null,
|
originalFunction: null,
|
||||||
localFunction: { sql: '' },
|
localFunction: { sql: '' },
|
||||||
lastFunction: null,
|
lastFunction: null,
|
||||||
@@ -109,13 +121,14 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
getWorkspace: 'workspaces/getWorkspace'
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
}),
|
}),
|
||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
},
|
},
|
||||||
isSelected () {
|
isSelected () {
|
||||||
return this.workspace.selected_tab === 'prop';
|
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.function;
|
||||||
},
|
},
|
||||||
schema () {
|
schema () {
|
||||||
return this.workspace.breadcrumbs.schema;
|
return this.workspace.breadcrumbs.schema;
|
||||||
@@ -160,12 +173,19 @@ export default {
|
|||||||
destroyed () {
|
destroyed () {
|
||||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
},
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
refreshStructure: 'workspaces/refreshStructure',
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
||||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
|
newTab: 'workspaces/newTab'
|
||||||
}),
|
}),
|
||||||
async getFunctionData () {
|
async getFunctionData () {
|
||||||
if (!this.function) return;
|
if (!this.function) return;
|
||||||
@@ -183,6 +203,12 @@ export default {
|
|||||||
const { status, response } = await Functions.getFunctionInformations(params);
|
const { status, response } = await Functions.getFunctionInformations(params);
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.originalFunction = response;
|
this.originalFunction = response;
|
||||||
|
|
||||||
|
this.originalFunction.parameters = [...this.originalFunction.parameters.map(param => {
|
||||||
|
param._id = uidGen();
|
||||||
|
return param;
|
||||||
|
})];
|
||||||
|
|
||||||
this.localFunction = JSON.parse(JSON.stringify(this.originalFunction));
|
this.localFunction = JSON.parse(JSON.stringify(this.originalFunction));
|
||||||
this.sqlProxy = this.localFunction.sql;
|
this.sqlProxy = this.localFunction.sql;
|
||||||
}
|
}
|
||||||
@@ -250,6 +276,33 @@ export default {
|
|||||||
parametersUpdate (parameters) {
|
parametersUpdate (parameters) {
|
||||||
this.localFunction = { ...this.localFunction, parameters };
|
this.localFunction = { ...this.localFunction, parameters };
|
||||||
},
|
},
|
||||||
|
runFunctionCheck () {
|
||||||
|
if (this.localFunction.parameters.length)
|
||||||
|
this.showAskParamsModal();
|
||||||
|
else
|
||||||
|
this.runFunction();
|
||||||
|
},
|
||||||
|
runFunction (params) {
|
||||||
|
if (!params) params = [];
|
||||||
|
|
||||||
|
let sql;
|
||||||
|
switch (this.connection.client) { // TODO: move in a better place
|
||||||
|
case 'maria':
|
||||||
|
case 'mysql':
|
||||||
|
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
sql = `SELECT ${this.originalFunction.name}(${params.join(',')})`;
|
||||||
|
break;
|
||||||
|
case 'mssql':
|
||||||
|
sql = `SELECT ${this.originalFunction.name} ${params.join(',')}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.newTab({ uid: this.connection.uid, content: sql, autorun: true });
|
||||||
|
},
|
||||||
showOptionsModal () {
|
showOptionsModal () {
|
||||||
this.isOptionsModal = true;
|
this.isOptionsModal = true;
|
||||||
},
|
},
|
||||||
@@ -261,6 +314,21 @@ export default {
|
|||||||
},
|
},
|
||||||
hideParamsModal () {
|
hideParamsModal () {
|
||||||
this.isParamsModal = false;
|
this.isParamsModal = false;
|
||||||
|
},
|
||||||
|
showAskParamsModal () {
|
||||||
|
this.isAskingParameters = true;
|
||||||
|
},
|
||||||
|
hideAskParamsModal () {
|
||||||
|
this.isAskingParameters = false;
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
if (this.isSelected) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
||||||
|
if (this.isChanged)
|
||||||
|
this.saveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
|
title="CTRL+S"
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ $t('word.save') }}</span>
|
||||||
@@ -25,9 +26,9 @@
|
|||||||
<div class="divider-vert py-3" />
|
<div class="divider-vert py-3" />
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm disabled"
|
class="btn btn-dark btn-sm"
|
||||||
:disabled="isChanged"
|
:disabled="isChanged"
|
||||||
@click="false"
|
@click="runRoutineCheck"
|
||||||
>
|
>
|
||||||
<span>{{ $t('word.run') }}</span>
|
<span>{{ $t('word.run') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-play ml-1" />
|
<i class="mdi mdi-24px mdi-play ml-1" />
|
||||||
@@ -47,7 +48,8 @@
|
|||||||
<BaseLoader v-if="isLoading" />
|
<BaseLoader v-if="isLoading" />
|
||||||
<label class="form-label ml-2">{{ $t('message.routineBody') }}</label>
|
<label class="form-label ml-2">{{ $t('message.routineBody') }}</label>
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
v-if="isSelected"
|
v-show="isSelected"
|
||||||
|
:key="`${routine}-${_uid}`"
|
||||||
ref="queryEditor"
|
ref="queryEditor"
|
||||||
:value.sync="localRoutine.sql"
|
:value.sync="localRoutine.sql"
|
||||||
:workspace="workspace"
|
:workspace="workspace"
|
||||||
@@ -70,15 +72,24 @@
|
|||||||
@hide="hideParamsModal"
|
@hide="hideParamsModal"
|
||||||
@parameters-update="parametersUpdate"
|
@parameters-update="parametersUpdate"
|
||||||
/>
|
/>
|
||||||
|
<ModalAskParameters
|
||||||
|
v-if="isAskingParameters"
|
||||||
|
:local-routine="localRoutine"
|
||||||
|
:client="workspace.client"
|
||||||
|
@confirm="runRoutine"
|
||||||
|
@close="hideAskParamsModal"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import QueryEditor from '@/components/QueryEditor';
|
import QueryEditor from '@/components/QueryEditor';
|
||||||
import BaseLoader from '@/components/BaseLoader';
|
import BaseLoader from '@/components/BaseLoader';
|
||||||
import WorkspacePropsRoutineOptionsModal from '@/components/WorkspacePropsRoutineOptionsModal';
|
import WorkspacePropsRoutineOptionsModal from '@/components/WorkspacePropsRoutineOptionsModal';
|
||||||
import WorkspacePropsRoutineParamsModal from '@/components/WorkspacePropsRoutineParamsModal';
|
import WorkspacePropsRoutineParamsModal from '@/components/WorkspacePropsRoutineParamsModal';
|
||||||
|
import ModalAskParameters from '@/components/ModalAskParameters';
|
||||||
import Routines from '@/ipc-api/Routines';
|
import Routines from '@/ipc-api/Routines';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -87,7 +98,8 @@ export default {
|
|||||||
QueryEditor,
|
QueryEditor,
|
||||||
BaseLoader,
|
BaseLoader,
|
||||||
WorkspacePropsRoutineOptionsModal,
|
WorkspacePropsRoutineOptionsModal,
|
||||||
WorkspacePropsRoutineParamsModal
|
WorkspacePropsRoutineParamsModal,
|
||||||
|
ModalAskParameters
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object,
|
connection: Object,
|
||||||
@@ -100,6 +112,7 @@ export default {
|
|||||||
isSaving: false,
|
isSaving: false,
|
||||||
isOptionsModal: false,
|
isOptionsModal: false,
|
||||||
isParamsModal: false,
|
isParamsModal: false,
|
||||||
|
isAskingParameters: false,
|
||||||
originalRoutine: null,
|
originalRoutine: null,
|
||||||
localRoutine: { sql: '' },
|
localRoutine: { sql: '' },
|
||||||
lastRoutine: null,
|
lastRoutine: null,
|
||||||
@@ -109,13 +122,14 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
getWorkspace: 'workspaces/getWorkspace'
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
}),
|
}),
|
||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
},
|
},
|
||||||
isSelected () {
|
isSelected () {
|
||||||
return this.workspace.selected_tab === 'prop';
|
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.routine;
|
||||||
},
|
},
|
||||||
schema () {
|
schema () {
|
||||||
return this.workspace.breadcrumbs.schema;
|
return this.workspace.breadcrumbs.schema;
|
||||||
@@ -160,12 +174,19 @@ export default {
|
|||||||
destroyed () {
|
destroyed () {
|
||||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
},
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
refreshStructure: 'workspaces/refreshStructure',
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
||||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
|
newTab: 'workspaces/newTab'
|
||||||
}),
|
}),
|
||||||
async getRoutineData () {
|
async getRoutineData () {
|
||||||
if (!this.routine) return;
|
if (!this.routine) return;
|
||||||
@@ -182,6 +203,12 @@ export default {
|
|||||||
const { status, response } = await Routines.getRoutineInformations(params);
|
const { status, response } = await Routines.getRoutineInformations(params);
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.originalRoutine = response;
|
this.originalRoutine = response;
|
||||||
|
|
||||||
|
this.originalRoutine.parameters = [...this.originalRoutine.parameters.map(param => {
|
||||||
|
param._id = uidGen();
|
||||||
|
return param;
|
||||||
|
})];
|
||||||
|
|
||||||
this.localRoutine = JSON.parse(JSON.stringify(this.originalRoutine));
|
this.localRoutine = JSON.parse(JSON.stringify(this.originalRoutine));
|
||||||
this.sqlProxy = this.localRoutine.sql;
|
this.sqlProxy = this.localRoutine.sql;
|
||||||
}
|
}
|
||||||
@@ -249,6 +276,31 @@ export default {
|
|||||||
parametersUpdate (parameters) {
|
parametersUpdate (parameters) {
|
||||||
this.localRoutine = { ...this.localRoutine, parameters };
|
this.localRoutine = { ...this.localRoutine, parameters };
|
||||||
},
|
},
|
||||||
|
runRoutineCheck () {
|
||||||
|
if (this.localRoutine.parameters.length)
|
||||||
|
this.showAskParamsModal();
|
||||||
|
else
|
||||||
|
this.runRoutine();
|
||||||
|
},
|
||||||
|
runRoutine (params) {
|
||||||
|
if (!params) params = [];
|
||||||
|
|
||||||
|
let sql;
|
||||||
|
switch (this.connection.client) { // TODO: move in a better place
|
||||||
|
case 'maria':
|
||||||
|
case 'mysql':
|
||||||
|
case 'pg':
|
||||||
|
sql = `CALL ${this.originalRoutine.name}(${params.join(',')})`;
|
||||||
|
break;
|
||||||
|
case 'mssql':
|
||||||
|
sql = `EXEC ${this.originalRoutine.name} ${params.join(',')}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sql = `CALL \`${this.originalRoutine.name}\`(${params.join(',')})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.newTab({ uid: this.connection.uid, content: sql, autorun: true });
|
||||||
|
},
|
||||||
showOptionsModal () {
|
showOptionsModal () {
|
||||||
this.isOptionsModal = true;
|
this.isOptionsModal = true;
|
||||||
},
|
},
|
||||||
@@ -260,6 +312,21 @@ export default {
|
|||||||
},
|
},
|
||||||
hideParamsModal () {
|
hideParamsModal () {
|
||||||
this.isParamsModal = false;
|
this.isParamsModal = false;
|
||||||
|
},
|
||||||
|
showAskParamsModal () {
|
||||||
|
this.isAskingParameters = true;
|
||||||
|
},
|
||||||
|
hideAskParamsModal () {
|
||||||
|
this.isAskingParameters = false;
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
if (this.isSelected) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
||||||
|
if (this.isChanged)
|
||||||
|
this.saveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
|
title="CTRL+S"
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ $t('word.save') }}</span>
|
||||||
@@ -32,7 +33,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="columns mb-4">
|
<div class="columns mb-4">
|
||||||
<div class="column col-3">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.name') }}</label>
|
<label class="form-label">{{ $t('word.name') }}</label>
|
||||||
<input
|
<input
|
||||||
@@ -42,7 +43,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-3">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.definer') }}</label>
|
<label class="form-label">{{ $t('word.definer') }}</label>
|
||||||
<select
|
<select
|
||||||
@@ -71,7 +72,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-6">
|
<div class="column col-4">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.comment') }}</label>
|
<label class="form-label">{{ $t('word.comment') }}</label>
|
||||||
<input
|
<input
|
||||||
@@ -118,7 +119,7 @@
|
|||||||
<BaseLoader v-if="isLoading" />
|
<BaseLoader v-if="isLoading" />
|
||||||
<label class="form-label ml-2">{{ $t('message.schedulerBody') }}</label>
|
<label class="form-label ml-2">{{ $t('message.schedulerBody') }}</label>
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
v-if="isSelected"
|
v-show="isSelected"
|
||||||
ref="queryEditor"
|
ref="queryEditor"
|
||||||
:value.sync="localScheduler.sql"
|
:value.sync="localScheduler.sql"
|
||||||
:workspace="workspace"
|
:workspace="workspace"
|
||||||
@@ -169,13 +170,14 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
getWorkspace: 'workspaces/getWorkspace'
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
}),
|
}),
|
||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
},
|
},
|
||||||
isSelected () {
|
isSelected () {
|
||||||
return this.workspace.selected_tab === 'prop';
|
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.scheduler;
|
||||||
},
|
},
|
||||||
schema () {
|
schema () {
|
||||||
return this.workspace.breadcrumbs.schema;
|
return this.workspace.breadcrumbs.schema;
|
||||||
@@ -220,6 +222,12 @@ export default {
|
|||||||
destroyed () {
|
destroyed () {
|
||||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
},
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
@@ -310,6 +318,15 @@ export default {
|
|||||||
},
|
},
|
||||||
timingUpdate (options) {
|
timingUpdate (options) {
|
||||||
this.localScheduler = options;
|
this.localScheduler = options;
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
if (this.isSelected) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
||||||
|
if (this.isChanged)
|
||||||
|
this.saveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
|
title="CTRL+S"
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ $t('word.save') }}</span>
|
||||||
@@ -26,7 +27,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="columns mb-4">
|
<div class="columns mb-4">
|
||||||
<div class="column col-3">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.name') }}</label>
|
<label class="form-label">{{ $t('word.name') }}</label>
|
||||||
<input
|
<input
|
||||||
@@ -36,7 +37,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-3">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.definer') }}</label>
|
<label class="form-label">{{ $t('word.definer') }}</label>
|
||||||
<select
|
<select
|
||||||
@@ -67,7 +68,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column col-3">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.table') }}</label>
|
<label class="form-label">{{ $t('word.table') }}</label>
|
||||||
<select v-model="localTrigger.table" class="form-select">
|
<select v-model="localTrigger.table" class="form-select">
|
||||||
@@ -77,7 +78,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-3">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.event') }}</label>
|
<label class="form-label">{{ $t('word.event') }}</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@@ -99,7 +100,7 @@
|
|||||||
<BaseLoader v-if="isLoading" />
|
<BaseLoader v-if="isLoading" />
|
||||||
<label class="form-label ml-2">{{ $t('message.triggerStatement') }}</label>
|
<label class="form-label ml-2">{{ $t('message.triggerStatement') }}</label>
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
v-if="isSelected"
|
v-show="isSelected"
|
||||||
ref="queryEditor"
|
ref="queryEditor"
|
||||||
:value.sync="localTrigger.sql"
|
:value.sync="localTrigger.sql"
|
||||||
:workspace="workspace"
|
:workspace="workspace"
|
||||||
@@ -140,13 +141,14 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
getWorkspace: 'workspaces/getWorkspace'
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
}),
|
}),
|
||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
},
|
},
|
||||||
isSelected () {
|
isSelected () {
|
||||||
return this.workspace.selected_tab === 'prop';
|
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.trigger;
|
||||||
},
|
},
|
||||||
schema () {
|
schema () {
|
||||||
return this.workspace.breadcrumbs.schema;
|
return this.workspace.breadcrumbs.schema;
|
||||||
@@ -191,6 +193,12 @@ export default {
|
|||||||
destroyed () {
|
destroyed () {
|
||||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
},
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
@@ -274,6 +282,15 @@ export default {
|
|||||||
this.editorHeight = size;
|
this.editorHeight = size;
|
||||||
this.$refs.queryEditor.editor.resize();
|
this.$refs.queryEditor.editor.resize();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
if (this.isSelected) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
||||||
|
if (this.isChanged)
|
||||||
|
this.saveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
|
title="CTRL+S"
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ $t('word.save') }}</span>
|
||||||
@@ -26,7 +27,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="columns mb-4">
|
<div class="columns mb-4">
|
||||||
<div class="column col-3">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('word.name') }}</label>
|
<label class="form-label">{{ $t('word.name') }}</label>
|
||||||
<input
|
<input
|
||||||
@@ -36,8 +37,8 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-3">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div v-if="workspace.customizations.definer" class="form-group">
|
||||||
<label class="form-label">{{ $t('word.definer') }}</label>
|
<label class="form-label">{{ $t('word.definer') }}</label>
|
||||||
<select
|
<select
|
||||||
v-if="workspace.users.length"
|
v-if="workspace.users.length"
|
||||||
@@ -67,8 +68,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column col-2">
|
<div class="column col-auto mr-2">
|
||||||
<div class="form-group">
|
<div v-if="workspace.customizations.viewSqlSecurity" class="form-group">
|
||||||
<label class="form-label">{{ $t('message.sqlSecurity') }}</label>
|
<label class="form-label">{{ $t('message.sqlSecurity') }}</label>
|
||||||
<label class="form-radio">
|
<label class="form-radio">
|
||||||
<input
|
<input
|
||||||
@@ -90,8 +91,8 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-2">
|
<div class="column col-auto mr-2">
|
||||||
<div class="form-group">
|
<div v-if="workspace.customizations.viewAlgorithm" class="form-group">
|
||||||
<label class="form-label">{{ $t('word.algorithm') }}</label>
|
<label class="form-label">{{ $t('word.algorithm') }}</label>
|
||||||
<label class="form-radio">
|
<label class="form-radio">
|
||||||
<input
|
<input
|
||||||
@@ -122,7 +123,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-2">
|
<div v-if="workspace.customizations.viewUpdateOption" class="column col-auto mr-2">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">{{ $t('message.updateOption') }}</label>
|
<label class="form-label">{{ $t('message.updateOption') }}</label>
|
||||||
<label class="form-radio">
|
<label class="form-radio">
|
||||||
@@ -201,13 +202,14 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
getWorkspace: 'workspaces/getWorkspace'
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
}),
|
}),
|
||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
},
|
},
|
||||||
isSelected () {
|
isSelected () {
|
||||||
return this.workspace.selected_tab === 'prop';
|
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.view;
|
||||||
},
|
},
|
||||||
schema () {
|
schema () {
|
||||||
return this.workspace.breadcrumbs.schema;
|
return this.workspace.breadcrumbs.schema;
|
||||||
@@ -245,6 +247,12 @@ export default {
|
|||||||
destroyed () {
|
destroyed () {
|
||||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
},
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
@@ -327,6 +335,15 @@ export default {
|
|||||||
this.editorHeight = size;
|
this.editorHeight = size;
|
||||||
this.$refs.queryEditor.editor.resize();
|
this.$refs.queryEditor.editor.resize();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
if (this.isSelected) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
||||||
|
if (this.isChanged)
|
||||||
|
this.saveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -42,6 +42,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="customizations.tableArray" class="th">
|
||||||
|
<div class="column-resizable">
|
||||||
|
<div class="table-column-title">
|
||||||
|
{{ $t('word.array') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="th">
|
<div class="th">
|
||||||
<div class="column-resizable">
|
<div class="column-resizable">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
@@ -49,21 +56,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="th">
|
<div v-if="customizations.unsigned" class="th">
|
||||||
<div class="column-resizable">
|
<div class="column-resizable">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('word.unsigned') }}
|
{{ $t('word.unsigned') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="th">
|
<div v-if="customizations.nullable" class="th">
|
||||||
<div class="column-resizable">
|
<div class="column-resizable">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('message.allowNull') }}
|
{{ $t('message.allowNull') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="th">
|
<div v-if="customizations.zerofill" class="th">
|
||||||
<div class="column-resizable">
|
<div class="column-resizable">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('message.zeroFill') }}
|
{{ $t('message.zeroFill') }}
|
||||||
@@ -77,14 +84,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="th">
|
<div v-if="customizations.comment" class="th">
|
||||||
<div class="column-resizable">
|
<div class="column-resizable">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('word.comment') }}
|
{{ $t('word.comment') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="th">
|
<div v-if="customizations.collation" class="th">
|
||||||
<div class="column-resizable min-100">
|
<div class="column-resizable min-100">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
{{ $t('word.collation') }}
|
{{ $t('word.collation') }}
|
||||||
@@ -106,7 +113,9 @@
|
|||||||
:indexes="getIndexes(row.name)"
|
:indexes="getIndexes(row.name)"
|
||||||
:foreigns="getForeigns(row.name)"
|
:foreigns="getForeigns(row.name)"
|
||||||
:data-types="dataTypes"
|
:data-types="dataTypes"
|
||||||
|
:customizations="customizations"
|
||||||
@contextmenu="contextMenu"
|
@contextmenu="contextMenu"
|
||||||
|
@rename-field="$emit('rename-field', $event)"
|
||||||
/>
|
/>
|
||||||
</draggable>
|
</draggable>
|
||||||
</div>
|
</div>
|
||||||
@@ -154,6 +163,9 @@ export default {
|
|||||||
workspaceSchema () {
|
workspaceSchema () {
|
||||||
return this.getWorkspace(this.connUid).breadcrumbs.schema;
|
return this.getWorkspace(this.connUid).breadcrumbs.schema;
|
||||||
},
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.getWorkspace(this.connUid).customizations;
|
||||||
|
},
|
||||||
dataTypes () {
|
dataTypes () {
|
||||||
return this.getWorkspace(this.connUid).dataTypes;
|
return this.getWorkspace(this.connUid).dataTypes;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<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" tabindex="0">
|
<div class="td" tabindex="0">
|
||||||
<div class="row-draggable">
|
<div :class="customizations.sortableFields ? 'row-draggable' : 'text-center'">
|
||||||
<i class="mdi mdi-drag-horizontal row-draggable-icon" />
|
<i v-if="customizations.sortableFields" class="mdi mdi-drag-horizontal row-draggable-icon" />
|
||||||
{{ localRow.order }}
|
{{ localRow.order }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
<span
|
<span
|
||||||
v-if="!isInlineEditor.type"
|
v-if="!isInlineEditor.type"
|
||||||
class="cell-content text-left"
|
class="cell-content text-left"
|
||||||
:class="`type-${lowerCase(localRow.type)}`"
|
:class="typeClass(localRow.type)"
|
||||||
@click="editON($event, localRow.type.toUpperCase(), 'type')"
|
@click="editON($event, localRow.type.toUpperCase(), 'type')"
|
||||||
>
|
>
|
||||||
{{ localRow.type }}
|
{{ localRow.type }}
|
||||||
@@ -60,6 +60,9 @@
|
|||||||
class="form-select editable-field small-select text-uppercase"
|
class="form-select editable-field small-select text-uppercase"
|
||||||
@blur="editOFF"
|
@blur="editOFF"
|
||||||
>
|
>
|
||||||
|
<option v-if="!isInDataTypes">
|
||||||
|
{{ row.type }}
|
||||||
|
</option>
|
||||||
<optgroup
|
<optgroup
|
||||||
v-for="group in dataTypes"
|
v-for="group in dataTypes"
|
||||||
:key="group.group"
|
:key="group.group"
|
||||||
@@ -68,7 +71,7 @@
|
|||||||
<option
|
<option
|
||||||
v-for="type in group.types"
|
v-for="type in group.types"
|
||||||
:key="type.name"
|
:key="type.name"
|
||||||
:selected="localRow.type.toUpperCase() === type.name"
|
:selected="localRow.type === type.name"
|
||||||
:value="type.name"
|
:value="type.name"
|
||||||
>
|
>
|
||||||
{{ type.name }}
|
{{ type.name }}
|
||||||
@@ -76,6 +79,16 @@
|
|||||||
</optgroup>
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="customizations.tableArray"
|
||||||
|
class="td"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<label class="form-checkbox">
|
||||||
|
<input v-model="localRow.isArray" type="checkbox">
|
||||||
|
<i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div class="td type-int" tabindex="0">
|
<div class="td type-int" tabindex="0">
|
||||||
<template v-if="fieldType.length">
|
<template v-if="fieldType.length">
|
||||||
<span
|
<span
|
||||||
@@ -96,7 +109,11 @@
|
|||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="td" tabindex="0">
|
<div
|
||||||
|
v-if="customizations.unsigned"
|
||||||
|
class="td"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
<label class="form-checkbox">
|
<label class="form-checkbox">
|
||||||
<input
|
<input
|
||||||
v-model="localRow.unsigned"
|
v-model="localRow.unsigned"
|
||||||
@@ -106,7 +123,11 @@
|
|||||||
<i class="form-icon" />
|
<i class="form-icon" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="td" tabindex="0">
|
<div
|
||||||
|
v-if="customizations.nullable"
|
||||||
|
class="td"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
<label class="form-checkbox">
|
<label class="form-checkbox">
|
||||||
<input
|
<input
|
||||||
v-model="localRow.nullable"
|
v-model="localRow.nullable"
|
||||||
@@ -116,7 +137,11 @@
|
|||||||
<i class="form-icon" />
|
<i class="form-icon" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="td" tabindex="0">
|
<div
|
||||||
|
v-if="customizations.zerofill"
|
||||||
|
class="td"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
<label class="form-checkbox">
|
<label class="form-checkbox">
|
||||||
<input
|
<input
|
||||||
v-model="localRow.zerofill"
|
v-model="localRow.zerofill"
|
||||||
@@ -131,7 +156,11 @@
|
|||||||
{{ fieldDefault }}
|
{{ fieldDefault }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="td type-varchar" tabindex="0">
|
<div
|
||||||
|
v-if="customizations.comment"
|
||||||
|
class="td type-varchar"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
<span
|
<span
|
||||||
v-if="!isInlineEditor.comment"
|
v-if="!isInlineEditor.comment"
|
||||||
class="cell-content"
|
class="cell-content"
|
||||||
@@ -149,7 +178,11 @@
|
|||||||
@blur="editOFF"
|
@blur="editOFF"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="td" tabindex="0">
|
<div
|
||||||
|
v-if="customizations.collation"
|
||||||
|
class="td"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
<template v-if="fieldType.collation">
|
<template v-if="fieldType.collation">
|
||||||
<span
|
<span
|
||||||
v-if="!isInlineEditor.collation"
|
v-if="!isInlineEditor.collation"
|
||||||
@@ -220,7 +253,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-2">
|
<div v-if="customizations.nullable" class="mb-2">
|
||||||
<label class="form-radio form-inline">
|
<label class="form-radio form-inline">
|
||||||
<input
|
<input
|
||||||
v-model="defaultValue.type"
|
v-model="defaultValue.type"
|
||||||
@@ -230,7 +263,7 @@
|
|||||||
><i class="form-icon" /> NULL
|
><i class="form-icon" /> NULL
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-2">
|
<div v-if="customizations.autoIncrement" class="mb-2">
|
||||||
<label class="form-radio form-inline">
|
<label class="form-radio form-inline">
|
||||||
<input
|
<input
|
||||||
v-model="defaultValue.type"
|
v-model="defaultValue.type"
|
||||||
@@ -261,7 +294,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div v-if="customizations.onUpdate">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('message.onUpdate') }}
|
{{ $t('message.onUpdate') }}
|
||||||
@@ -294,7 +327,8 @@ export default {
|
|||||||
row: Object,
|
row: Object,
|
||||||
dataTypes: Array,
|
dataTypes: Array,
|
||||||
indexes: Array,
|
indexes: Array,
|
||||||
foreigns: Array
|
foreigns: Array,
|
||||||
|
customizations: Object
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -318,7 +352,7 @@ export default {
|
|||||||
getWorkspace: 'workspaces/getWorkspace'
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
}),
|
}),
|
||||||
localLength () {
|
localLength () {
|
||||||
return this.localRow.numLength || this.localRow.charLength || this.localRow.datePrecision || 0;
|
return this.localRow.numLength || this.localRow.charLength || this.localRow.datePrecision || this.localRow.numPrecision || 0;
|
||||||
},
|
},
|
||||||
fieldType () {
|
fieldType () {
|
||||||
const fieldType = this.dataTypes.reduce((acc, group) => [...acc, ...group.types], []).filter(type =>
|
const fieldType = this.dataTypes.reduce((acc, group) => [...acc, ...group.types], []).filter(type =>
|
||||||
@@ -343,6 +377,16 @@ export default {
|
|||||||
},
|
},
|
||||||
isNullable () {
|
isNullable () {
|
||||||
return !this.indexes.some(index => ['PRIMARY'].includes(index.type));
|
return !this.indexes.some(index => ['PRIMARY'].includes(index.type));
|
||||||
|
},
|
||||||
|
isInDataTypes () {
|
||||||
|
let typeNames = [];
|
||||||
|
for (const group of this.dataTypes) {
|
||||||
|
typeNames = group.types.reduce((acc, curr) => {
|
||||||
|
acc.push(curr.name);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
return typeNames.includes(this.row.type);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -378,10 +422,10 @@ export default {
|
|||||||
return 'UNKNOWN ' + key;
|
return 'UNKNOWN ' + key;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
lowerCase (val) {
|
typeClass (type) {
|
||||||
if (val)
|
if (type)
|
||||||
return val.toLowerCase();
|
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||||
return val;
|
return '';
|
||||||
},
|
},
|
||||||
initLocalRow () {
|
initLocalRow () {
|
||||||
Object.keys(this.localRow).forEach(key => {
|
Object.keys(this.localRow).forEach(key => {
|
||||||
@@ -400,7 +444,7 @@ export default {
|
|||||||
this.defaultValue.type = 'custom';
|
this.defaultValue.type = 'custom';
|
||||||
this.defaultValue.custom = this.localRow.default.replace(/(^')|('$)/g, '');
|
this.defaultValue.custom = this.localRow.default.replace(/(^')|('$)/g, '');
|
||||||
}
|
}
|
||||||
else if (!isNaN(this.localRow.default)) {
|
else if (!isNaN(this.localRow.default.replace(/[:.-\s]/g, ''))) {
|
||||||
this.defaultValue.type = 'custom';
|
this.defaultValue.type = 'custom';
|
||||||
this.defaultValue.custom = this.localRow.default;
|
this.defaultValue.custom = this.localRow.default;
|
||||||
}
|
}
|
||||||
@@ -409,9 +453,6 @@ export default {
|
|||||||
this.defaultValue.expression = this.localRow.default;
|
this.defaultValue.expression = this.localRow.default;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateRow () {
|
|
||||||
this.$emit('input', this.localRow);
|
|
||||||
},
|
|
||||||
editON (event, content, field) {
|
editON (event, content, field) {
|
||||||
if (field === 'length') {
|
if (field === 'length') {
|
||||||
if (['integer', 'float', 'binary', 'spatial'].includes(this.fieldType.group)) this.editingField = 'numLength';
|
if (['integer', 'float', 'binary', 'spatial'].includes(this.fieldType.group)) this.editingField = 'numLength';
|
||||||
@@ -438,6 +479,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
editOFF () {
|
editOFF () {
|
||||||
|
if (this.editingField === 'name')
|
||||||
|
this.$emit('rename-field', { old: this.localRow[this.editingField], new: this.editingContent });
|
||||||
|
|
||||||
this.localRow[this.editingField] = this.editingContent;
|
this.localRow[this.editingField] = this.editingContent;
|
||||||
|
|
||||||
if (this.editingField === 'type' && this.editingContent !== this.originalContent) {
|
if (this.editingField === 'type' && this.editingContent !== this.originalContent) {
|
||||||
@@ -460,8 +504,7 @@ export default {
|
|||||||
if (!this.fieldType.zerofill)
|
if (!this.fieldType.zerofill)
|
||||||
this.localRow.zerofill = false;
|
this.localRow.zerofill = false;
|
||||||
}
|
}
|
||||||
|
else if (this.editingField === 'default') {
|
||||||
if (this.editingField === 'default') {
|
|
||||||
switch (this.defaultValue.type) {
|
switch (this.defaultValue.type) {
|
||||||
case 'autoincrement':
|
case 'autoincrement':
|
||||||
this.localRow.autoIncrement = true;
|
this.localRow.autoIncrement = true;
|
||||||
|
|||||||
@@ -1,27 +1,82 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
|
<div
|
||||||
|
v-show="isSelected"
|
||||||
|
class="workspace-query-tab column col-12 columns col-gapless no-outline"
|
||||||
|
tabindex="0"
|
||||||
|
@keydown.116="runQuery(query)"
|
||||||
|
@keydown.ctrl.87="clear"
|
||||||
|
@keydown.ctrl.119="beautify"
|
||||||
|
>
|
||||||
<div class="workspace-query-runner column col-12">
|
<div class="workspace-query-runner column col-12">
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
v-if="isSelected"
|
v-show="isSelected"
|
||||||
|
ref="queryEditor"
|
||||||
:auto-focus="true"
|
:auto-focus="true"
|
||||||
:value.sync="query"
|
:value.sync="query"
|
||||||
:workspace="workspace"
|
:workspace="workspace"
|
||||||
:schema="schema"
|
:schema="schema"
|
||||||
|
:is-selected="isSelected"
|
||||||
|
:height="editorHeight"
|
||||||
/>
|
/>
|
||||||
|
<div ref="resizer" class="query-area-resizer" />
|
||||||
<div class="workspace-query-runner-footer">
|
<div class="workspace-query-runner-footer">
|
||||||
<div class="workspace-query-buttons">
|
<div class="workspace-query-buttons">
|
||||||
<button
|
<button
|
||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:class="{'loading':isQuering}"
|
:class="{'loading':isQuering}"
|
||||||
:disabled="!query"
|
:disabled="!query"
|
||||||
title="F9"
|
title="F5"
|
||||||
@click="runQuery(query)"
|
@click="runQuery(query)"
|
||||||
>
|
>
|
||||||
<span>{{ $t('word.run') }}</span>
|
<span>{{ $t('word.run') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-play" />
|
<i class="mdi mdi-24px mdi-play" />
|
||||||
</button>
|
</button>
|
||||||
|
<div class="dropdown export-dropdown pr-2">
|
||||||
|
<button
|
||||||
|
:disabled="!results.length || isQuering"
|
||||||
|
class="btn btn-dark btn-sm dropdown-toggle mr-0 pr-0"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.export') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-file-export ml-1" />
|
||||||
|
<i class="mdi mdi-24px mdi-menu-down" />
|
||||||
|
</button>
|
||||||
|
<ul class="menu text-left">
|
||||||
|
<li class="menu-item">
|
||||||
|
<a class="c-hand" @click="downloadTable('json')">JSON</a>
|
||||||
|
</li>
|
||||||
|
<li class="menu-item">
|
||||||
|
<a class="c-hand" @click="downloadTable('csv')">CSV</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="btn btn-dark btn-sm"
|
||||||
|
:disabled="!query || isQuering"
|
||||||
|
title="CTRL+F8"
|
||||||
|
@click="beautify()"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.format') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-brush pl-1" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-link btn-sm"
|
||||||
|
:disabled="!query || isQuering"
|
||||||
|
title="CTRL+W"
|
||||||
|
@click="clear()"
|
||||||
|
>
|
||||||
|
<span>{{ $t('word.clear') }}</span>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep pl-1" />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-info">
|
<div class="workspace-query-info">
|
||||||
|
<div
|
||||||
|
v-if="results.length"
|
||||||
|
class="d-flex"
|
||||||
|
:title="$t('message.queryDuration')"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-timer-sand mdi-rotate-180 pr-1" /> <b>{{ durationsCount / 1000 }}s</b>
|
||||||
|
</div>
|
||||||
<div v-if="resultsCount">
|
<div v-if="resultsCount">
|
||||||
{{ $t('word.results') }}: <b>{{ resultsCount.toLocaleString() }}</b>
|
{{ $t('word.results') }}: <b>{{ resultsCount.toLocaleString() }}</b>
|
||||||
</div>
|
</div>
|
||||||
@@ -38,14 +93,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-results column col-12">
|
<div class="workspace-query-results p-relative column col-12">
|
||||||
|
<BaseLoader v-if="isQuering" />
|
||||||
<WorkspaceQueryTable
|
<WorkspaceQueryTable
|
||||||
v-if="results"
|
v-if="results"
|
||||||
v-show="!isQuering"
|
v-show="!isQuering"
|
||||||
ref="queryTable"
|
ref="queryTable"
|
||||||
:results="results"
|
:results="results"
|
||||||
:tab-uid="tabUid"
|
:tab-uid="tab.uid"
|
||||||
:conn-uid="connection.uid"
|
:conn-uid="connection.uid"
|
||||||
|
:is-selected="isSelected"
|
||||||
mode="query"
|
mode="query"
|
||||||
@update-field="updateField"
|
@update-field="updateField"
|
||||||
@delete-selected="deleteSelected"
|
@delete-selected="deleteSelected"
|
||||||
@@ -55,8 +112,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Database from '@/ipc-api/Database';
|
import { format } from 'sql-formatter';
|
||||||
|
import Schema from '@/ipc-api/Schema';
|
||||||
import QueryEditor from '@/components/QueryEditor';
|
import QueryEditor from '@/components/QueryEditor';
|
||||||
|
import BaseLoader from '@/components/BaseLoader';
|
||||||
import WorkspaceQueryTable from '@/components/WorkspaceQueryTable';
|
import WorkspaceQueryTable from '@/components/WorkspaceQueryTable';
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import tableTabs from '@/mixins/tableTabs';
|
import tableTabs from '@/mixins/tableTabs';
|
||||||
@@ -64,13 +123,14 @@ import tableTabs from '@/mixins/tableTabs';
|
|||||||
export default {
|
export default {
|
||||||
name: 'WorkspaceQueryTab',
|
name: 'WorkspaceQueryTab',
|
||||||
components: {
|
components: {
|
||||||
|
BaseLoader,
|
||||||
QueryEditor,
|
QueryEditor,
|
||||||
WorkspaceQueryTable
|
WorkspaceQueryTable
|
||||||
},
|
},
|
||||||
mixins: [tableTabs],
|
mixins: [tableTabs],
|
||||||
props: {
|
props: {
|
||||||
connection: Object,
|
connection: Object,
|
||||||
tabUid: String,
|
tab: Object,
|
||||||
isSelected: Boolean
|
isSelected: Boolean
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
@@ -80,20 +140,41 @@ export default {
|
|||||||
isQuering: false,
|
isQuering: false,
|
||||||
results: [],
|
results: [],
|
||||||
resultsCount: 0,
|
resultsCount: 0,
|
||||||
affectedCount: 0
|
durationsCount: 0,
|
||||||
|
affectedCount: 0,
|
||||||
|
editorHeight: 200
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
getWorkspace: 'workspaces/getWorkspace'
|
getWorkspace: 'workspaces/getWorkspace',
|
||||||
|
selectedWorkspace: 'workspaces/getSelected'
|
||||||
}),
|
}),
|
||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
|
},
|
||||||
|
isWorkspaceSelected () {
|
||||||
|
return this.workspace.uid === this.selectedWorkspace;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
|
this.query = this.tab.content;
|
||||||
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
window.addEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
|
mounted () {
|
||||||
|
const resizer = this.$refs.resizer;
|
||||||
|
|
||||||
|
resizer.addEventListener('mousedown', e => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
window.addEventListener('mousemove', this.resize);
|
||||||
|
window.addEventListener('mouseup', this.stopResize);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.tab.autorun)
|
||||||
|
this.runQuery(this.query);
|
||||||
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
window.removeEventListener('keydown', this.onKey);
|
window.removeEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
@@ -114,11 +195,12 @@ export default {
|
|||||||
query
|
query
|
||||||
};
|
};
|
||||||
|
|
||||||
const { status, response } = await Database.rawQuery(params);
|
const { status, response } = await Schema.rawQuery(params);
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.results = Array.isArray(response) ? response : [response];
|
this.results = Array.isArray(response) ? response : [response];
|
||||||
this.resultsCount += this.results.reduce((acc, curr) => acc + (curr.rows ? curr.rows.length : 0), 0);
|
this.resultsCount += this.results.reduce((acc, curr) => acc + (curr.rows ? curr.rows.length : 0), 0);
|
||||||
|
this.durationsCount += this.results.reduce((acc, curr) => acc + curr.duration, 0);
|
||||||
this.affectedCount += this.results.reduce((acc, curr) => acc + (curr.report ? curr.report.affectedRows : 0), 0);
|
this.affectedCount += this.results.reduce((acc, curr) => acc + (curr.report ? curr.report.affectedRows : 0), 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -137,14 +219,54 @@ export default {
|
|||||||
clearTabData () {
|
clearTabData () {
|
||||||
this.results = [];
|
this.results = [];
|
||||||
this.resultsCount = 0;
|
this.resultsCount = 0;
|
||||||
|
this.durationsCount = 0;
|
||||||
this.affectedCount = 0;
|
this.affectedCount = 0;
|
||||||
},
|
},
|
||||||
onKey (e) {
|
resize (e) {
|
||||||
if (this.isSelected) {
|
const el = this.$refs.queryEditor.$el;
|
||||||
e.stopPropagation();
|
let editorHeight = e.pageY - el.getBoundingClientRect().top;
|
||||||
if (e.key === 'F9')
|
if (editorHeight > 400) editorHeight = 400;
|
||||||
this.runQuery(this.query);
|
if (editorHeight < 50) editorHeight = 50;
|
||||||
|
this.editorHeight = editorHeight;
|
||||||
|
},
|
||||||
|
stopResize () {
|
||||||
|
window.removeEventListener('mousemove', this.resize);
|
||||||
|
if (this.$refs.queryTable && this.results.length)
|
||||||
|
this.$refs.queryTable.resizeResults();
|
||||||
|
|
||||||
|
if (this.$refs.queryEditor)
|
||||||
|
this.$refs.queryEditor.editor.resize();
|
||||||
|
},
|
||||||
|
beautify () {
|
||||||
|
if (this.$refs.queryEditor) {
|
||||||
|
let language = 'sql';
|
||||||
|
|
||||||
|
switch (this.workspace.client) {
|
||||||
|
case 'mysql':
|
||||||
|
language = 'mysql';
|
||||||
|
break;
|
||||||
|
case 'maria':
|
||||||
|
language = 'mariadb';
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
language = 'postgresql';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formattedQuery = format(this.query, {
|
||||||
|
language,
|
||||||
|
uppercase: true
|
||||||
|
});
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(formattedQuery);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
clear () {
|
||||||
|
if (this.$refs.queryEditor)
|
||||||
|
this.$refs.queryEditor.editor.session.setValue('');
|
||||||
|
this.clearTabData();
|
||||||
|
},
|
||||||
|
downloadTable (format) {
|
||||||
|
this.$refs.queryTable.downloadTable(format, `${this.tab.type}-${this.tab.index}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -155,11 +277,23 @@ export default {
|
|||||||
align-content: baseline;
|
align-content: baseline;
|
||||||
|
|
||||||
.workspace-query-runner {
|
.workspace-query-runner {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.query-area-resizer {
|
||||||
|
position: absolute;
|
||||||
|
height: 5px;
|
||||||
|
bottom: 40px;
|
||||||
|
width: 100%;
|
||||||
|
cursor: ns-resize;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
|
||||||
.workspace-query-runner-footer {
|
.workspace-query-runner-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 0.3rem 0.6rem 0.4rem;
|
padding: 0.3rem 0.6rem 0.4rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
height: 42px;
|
||||||
|
|
||||||
.workspace-query-buttons {
|
.workspace-query-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -167,7 +301,6 @@ export default {
|
|||||||
.btn {
|
.btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
color: $body-font-color;
|
|
||||||
margin-right: 0.4rem;
|
margin-right: 0.4rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,6 +314,15 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.workspace-query-results {
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.export-dropdown {
|
||||||
|
.menu {
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
ref="tableWrapper"
|
ref="tableWrapper"
|
||||||
class="vscroll"
|
class="vscroll no-outline"
|
||||||
|
tabindex="0"
|
||||||
:style="{'height': resultsSize+'px'}"
|
:style="{'height': resultsSize+'px'}"
|
||||||
|
@keyup.46="showDeleteConfirmModal"
|
||||||
>
|
>
|
||||||
<TableContext
|
<TableContext
|
||||||
v-if="isContext"
|
v-if="isContext"
|
||||||
:context-event="contextEvent"
|
:context-event="contextEvent"
|
||||||
:selected-rows="selectedRows"
|
:selected-rows="selectedRows"
|
||||||
@delete-selected="deleteSelected"
|
@show-delete-modal="showDeleteConfirmModal"
|
||||||
@close-context="isContext = false"
|
@set-null="setNull"
|
||||||
|
@close-context="closeContext"
|
||||||
/>
|
/>
|
||||||
<ul v-if="resultsWithRows.length > 1" class="tab tab-block result-tabs">
|
<ul v-if="resultsWithRows.length > 1" class="tab tab-block result-tabs">
|
||||||
<li
|
<li
|
||||||
@@ -41,7 +44,7 @@
|
|||||||
/>
|
/>
|
||||||
<span>{{ field.alias || field.name }}</span>
|
<span>{{ field.alias || field.name }}</span>
|
||||||
<i
|
<i
|
||||||
v-if="currentSort === field.name || currentSort === `${field.table}.${field.name}`"
|
v-if="isSortable && currentSort === field.name || currentSort === `${field.table}.${field.name}`"
|
||||||
class="mdi sort-icon"
|
class="mdi sort-icon"
|
||||||
:class="currentSortDir === 'asc' ? 'mdi-sort-ascending':'mdi-sort-descending'"
|
:class="currentSortDir === 'asc' ? 'mdi-sort-ascending':'mdi-sort-descending'"
|
||||||
/>
|
/>
|
||||||
@@ -64,47 +67,67 @@
|
|||||||
v-for="row in items"
|
v-for="row in items"
|
||||||
:key="row._id"
|
:key="row._id"
|
||||||
:row="row"
|
:row="row"
|
||||||
:fields="fields"
|
:fields="fieldsObj"
|
||||||
:key-usage="keyUsage"
|
:key-usage="keyUsage"
|
||||||
class="tr"
|
|
||||||
:class="{'selected': selectedRows.includes(row._id)}"
|
:class="{'selected': selectedRows.includes(row._id)}"
|
||||||
@select-row="selectRow($event, row._id)"
|
@select-row="selectRow($event, row._id)"
|
||||||
@update-field="updateField($event, getPrimaryValue(row))"
|
@update-field="updateField($event, row)"
|
||||||
@contextmenu="contextMenu"
|
@contextmenu="contextMenu"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</BaseVirtualScroll>
|
</BaseVirtualScroll>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ConfirmModal
|
||||||
|
v-if="isDeleteConfirmModal"
|
||||||
|
@confirm="deleteSelected"
|
||||||
|
@hide="hideDeleteConfirmModal"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-delete mr-1" /> {{ $tc('message.deleteRows', selectedRows.length) }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<div class="mb-2">
|
||||||
|
{{ $tc('message.confirmToDeleteRows', selectedRows.length) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import arrayToFile from '../libs/arrayToFile';
|
import arrayToFile from '../libs/arrayToFile';
|
||||||
import { LONG_TEXT, BLOB } from 'common/fieldTypes';
|
import { TEXT, LONG_TEXT, BLOB } from 'common/fieldTypes';
|
||||||
import BaseVirtualScroll from '@/components/BaseVirtualScroll';
|
import BaseVirtualScroll from '@/components/BaseVirtualScroll';
|
||||||
import WorkspaceQueryTableRow from '@/components/WorkspaceQueryTableRow';
|
import WorkspaceQueryTableRow from '@/components/WorkspaceQueryTableRow';
|
||||||
import TableContext from '@/components/WorkspaceQueryTableContext';
|
import TableContext from '@/components/WorkspaceQueryTableContext';
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
import { mapActions, mapGetters } from 'vuex';
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WorkspaceQueryTable',
|
name: 'WorkspaceQueryTable',
|
||||||
components: {
|
components: {
|
||||||
BaseVirtualScroll,
|
BaseVirtualScroll,
|
||||||
WorkspaceQueryTableRow,
|
WorkspaceQueryTableRow,
|
||||||
TableContext
|
TableContext,
|
||||||
|
ConfirmModal
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
results: Array,
|
results: Array,
|
||||||
tabUid: [String, Number],
|
|
||||||
connUid: String,
|
connUid: String,
|
||||||
mode: String
|
mode: String,
|
||||||
|
isSelected: Boolean
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
resultsSize: 1000,
|
resultsSize: 1000,
|
||||||
localResults: [],
|
localResults: [],
|
||||||
isContext: false,
|
isContext: false,
|
||||||
|
isDeleteConfirmModal: false,
|
||||||
contextEvent: null,
|
contextEvent: null,
|
||||||
selectedCell: null,
|
selectedCell: null,
|
||||||
selectedRows: [],
|
selectedRows: [],
|
||||||
@@ -122,7 +145,16 @@ export default {
|
|||||||
return this.getWorkspace(this.connUid).breadcrumbs.schema;
|
return this.getWorkspace(this.connUid).breadcrumbs.schema;
|
||||||
},
|
},
|
||||||
primaryField () {
|
primaryField () {
|
||||||
return this.fields.filter(field => ['pri', 'uni'].includes(field.key))[0] || false;
|
const primaryFields = this.fields.filter(field => field.key === 'pri');
|
||||||
|
const uniqueFields = this.fields.filter(field => field.key === 'uni');
|
||||||
|
|
||||||
|
if ((primaryFields.length > 1 || !primaryFields.length) && (uniqueFields.length > 1 || !uniqueFields.length))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return primaryFields[0] || uniqueFields[0];
|
||||||
|
},
|
||||||
|
isSortable () {
|
||||||
|
return this.fields.every(field => field.name);
|
||||||
},
|
},
|
||||||
isHardSort () {
|
isHardSort () {
|
||||||
return this.mode === 'table' && this.localResults.length === 1000;
|
return this.mode === 'table' && this.localResults.length === 1000;
|
||||||
@@ -150,6 +182,37 @@ export default {
|
|||||||
},
|
},
|
||||||
keyUsage () {
|
keyUsage () {
|
||||||
return this.resultsWithRows.length ? this.resultsWithRows[this.resultsetIndex].keys : [];
|
return this.resultsWithRows.length ? this.resultsWithRows[this.resultsetIndex].keys : [];
|
||||||
|
},
|
||||||
|
fieldsObj () {
|
||||||
|
if (this.sortedResults.length) {
|
||||||
|
const fieldsObj = {};
|
||||||
|
for (const key in this.sortedResults[0]) {
|
||||||
|
if (key === '_id') continue;
|
||||||
|
|
||||||
|
const fieldObj = this.fields.find(field => {
|
||||||
|
let fieldNames = [
|
||||||
|
field.name,
|
||||||
|
field.alias,
|
||||||
|
`${field.table}.${field.name}`,
|
||||||
|
`${field.table}.${field.alias}`,
|
||||||
|
`${field.tableAlias}.${field.name}`,
|
||||||
|
`${field.tableAlias}.${field.alias}`
|
||||||
|
];
|
||||||
|
|
||||||
|
if (field.table)
|
||||||
|
fieldNames = [...fieldNames, `${field.table.toLowerCase()}.${field.name}`, `${field.table.toLowerCase()}.${field.alias}`];
|
||||||
|
|
||||||
|
if (field.tableAlias)
|
||||||
|
fieldNames = [...fieldNames, `${field.tableAlias.toLowerCase()}.${field.name}`, `${field.tableAlias.toLowerCase()}.${field.alias}`];
|
||||||
|
|
||||||
|
return fieldNames.includes(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
fieldsObj[key] = fieldObj;
|
||||||
|
}
|
||||||
|
return fieldsObj;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -196,6 +259,7 @@ export default {
|
|||||||
},
|
},
|
||||||
fieldLength (field) {
|
fieldLength (field) {
|
||||||
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
||||||
|
else if (TEXT.includes(field.type)) return field.charLength;
|
||||||
return field.length;
|
return field.length;
|
||||||
},
|
},
|
||||||
keyName (key) {
|
keyName (key) {
|
||||||
@@ -239,7 +303,7 @@ export default {
|
|||||||
: [];
|
: [];
|
||||||
},
|
},
|
||||||
resizeResults () {
|
resizeResults () {
|
||||||
if (this.$refs.resultTable) {
|
if (this.$refs.resultTable && this.isSelected) {
|
||||||
const el = this.$refs.tableWrapper;
|
const el = this.$refs.tableWrapper;
|
||||||
|
|
||||||
if (el) {
|
if (el) {
|
||||||
@@ -253,37 +317,72 @@ export default {
|
|||||||
refreshScroller () {
|
refreshScroller () {
|
||||||
this.resizeResults();
|
this.resizeResults();
|
||||||
},
|
},
|
||||||
updateField (payload, id) {
|
updateField (payload, row) {
|
||||||
if (!this.primaryField)
|
const orgRow = this.localResults.find(lr => lr._id === row._id);
|
||||||
this.addNotification({ status: 'warning', message: this.$t('message.unableEditFieldWithoutPrimary') });
|
delete row._id;
|
||||||
else {
|
delete orgRow._id;
|
||||||
const params = {
|
|
||||||
primary: this.primaryField.name,
|
Object.keys(orgRow).forEach(key => { // remap the row
|
||||||
schema: this.getSchema(this.resultsetIndex),
|
if (orgRow[key] instanceof Date && moment(orgRow[key]).isValid()) { // if datetime
|
||||||
table: this.getTable(this.resultsetIndex),
|
let datePrecision = '';
|
||||||
id,
|
const precision = this.fields.find(field => field.name === key).datePrecision;
|
||||||
...payload
|
for (let i = 0; i < precision; i++)
|
||||||
};
|
datePrecision += i === 0 ? '.S' : 'S';
|
||||||
this.$emit('update-field', params);
|
|
||||||
}
|
orgRow[key] = moment(orgRow[key]).format(`YYYY-MM-DD HH:mm:ss${datePrecision}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
primary: this.primaryField.name,
|
||||||
|
schema: this.getSchema(this.resultsetIndex),
|
||||||
|
table: this.getTable(this.resultsetIndex),
|
||||||
|
id: this.getPrimaryValue(orgRow),
|
||||||
|
row,
|
||||||
|
orgRow,
|
||||||
|
...payload
|
||||||
|
};
|
||||||
|
this.$emit('update-field', params);
|
||||||
|
},
|
||||||
|
closeContext () {
|
||||||
|
this.isContext = false;
|
||||||
|
},
|
||||||
|
showDeleteConfirmModal () {
|
||||||
|
this.isDeleteConfirmModal = true;
|
||||||
|
},
|
||||||
|
hideDeleteConfirmModal () {
|
||||||
|
this.isDeleteConfirmModal = false;
|
||||||
},
|
},
|
||||||
deleteSelected () {
|
deleteSelected () {
|
||||||
if (!this.primaryField)
|
this.closeContext();
|
||||||
this.addNotification({ status: 'warning', message: this.$t('message.unableEditFieldWithoutPrimary') });
|
const rows = this.localResults.filter(row => this.selectedRows.includes(row._id)).map(row => {
|
||||||
else {
|
delete row._id;
|
||||||
const rowIDs = this.localResults.filter(row => this.selectedRows.includes(row._id)).map(row =>
|
return row;
|
||||||
row[this.primaryField.name] ||
|
});
|
||||||
row[`${this.primaryField.table}.${this.primaryField.name}`] ||
|
|
||||||
row[`${this.primaryField.tableAlias}.${this.primaryField.name}`]
|
const params = {
|
||||||
);
|
primary: this.primaryField.name,
|
||||||
const params = {
|
schema: this.getSchema(this.resultsetIndex),
|
||||||
primary: this.primaryField.name,
|
table: this.getTable(this.resultsetIndex),
|
||||||
schema: this.getSchema(this.resultsetIndex),
|
rows
|
||||||
table: this.getTable(this.resultsetIndex),
|
};
|
||||||
rows: rowIDs
|
this.$emit('delete-selected', params);
|
||||||
};
|
},
|
||||||
this.$emit('delete-selected', params);
|
setNull () {
|
||||||
}
|
const row = this.localResults.find(row => this.selectedRows.includes(row._id));
|
||||||
|
delete row._id;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
primary: this.primaryField.name,
|
||||||
|
schema: this.getSchema(this.resultsetIndex),
|
||||||
|
table: this.getTable(this.resultsetIndex),
|
||||||
|
id: this.getPrimaryValue(row),
|
||||||
|
row,
|
||||||
|
orgRow: row,
|
||||||
|
field: this.selectedCell.field,
|
||||||
|
content: null
|
||||||
|
};
|
||||||
|
this.$emit('update-field', params);
|
||||||
},
|
},
|
||||||
applyUpdate (params) {
|
applyUpdate (params) {
|
||||||
const { primary, id, field, table, content } = params;
|
const { primary, id, field, table, content } = params;
|
||||||
@@ -325,6 +424,8 @@ export default {
|
|||||||
this.selectedRows = [row];
|
this.selectedRows = [row];
|
||||||
},
|
},
|
||||||
contextMenu (event, cell) {
|
contextMenu (event, cell) {
|
||||||
|
if (event.target.localName === 'input') return;
|
||||||
|
|
||||||
this.selectedCell = cell;
|
this.selectedCell = cell;
|
||||||
if (!this.selectedRows.includes(cell.id))
|
if (!this.selectedRows.includes(cell.id))
|
||||||
this.selectedRows = [cell.id];
|
this.selectedRows = [cell.id];
|
||||||
@@ -332,6 +433,8 @@ export default {
|
|||||||
this.isContext = true;
|
this.isContext = true;
|
||||||
},
|
},
|
||||||
sort (field) {
|
sort (field) {
|
||||||
|
if (!this.isSortable) return;
|
||||||
|
|
||||||
if (this.mode === 'query')
|
if (this.mode === 'query')
|
||||||
field = `${this.getTable(this.resultsetIndex)}.${field}`;
|
field = `${this.getTable(this.resultsetIndex)}.${field}`;
|
||||||
|
|
||||||
|
|||||||
@@ -3,62 +3,46 @@
|
|||||||
:context-event="contextEvent"
|
:context-event="contextEvent"
|
||||||
@close-context="closeContext"
|
@close-context="closeContext"
|
||||||
>
|
>
|
||||||
<div class="context-element" @click="showConfirmModal">
|
<div
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $tc('message.deleteRows', selectedRows.length) }}</span>
|
v-if="selectedRows.length === 1"
|
||||||
</div>
|
class="context-element"
|
||||||
|
@click="setNull"
|
||||||
<ConfirmModal
|
|
||||||
v-if="isConfirmModal"
|
|
||||||
@confirm="deleteRows"
|
|
||||||
@hide="hideConfirmModal"
|
|
||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<span class="d-flex">
|
||||||
<div class="d-flex">
|
<i class="mdi mdi-18px mdi-null text-light pr-1" /> {{ $t('message.setNull') }}
|
||||||
<i class="mdi mdi-24px mdi-delete mr-1" /> {{ $tc('message.deleteRows', selectedRows.length) }}
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
<div class="context-element" @click="showConfirmModal">
|
||||||
<div :slot="'body'">
|
<span class="d-flex">
|
||||||
<div class="mb-2">
|
<i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $tc('message.deleteRows', selectedRows.length) }}
|
||||||
{{ $tc('message.confirmToDeleteRows', selectedRows.length) }}
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</ConfirmModal>
|
|
||||||
</BaseContextMenu>
|
</BaseContextMenu>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import BaseContextMenu from '@/components/BaseContextMenu';
|
import BaseContextMenu from '@/components/BaseContextMenu';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WorkspaceQueryTableContext',
|
name: 'WorkspaceQueryTableContext',
|
||||||
components: {
|
components: {
|
||||||
BaseContextMenu,
|
BaseContextMenu
|
||||||
ConfirmModal
|
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
contextEvent: MouseEvent,
|
contextEvent: MouseEvent,
|
||||||
selectedRows: Array
|
selectedRows: Array
|
||||||
},
|
},
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
isConfirmModal: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
computed: {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
showConfirmModal () {
|
showConfirmModal () {
|
||||||
this.isConfirmModal = true;
|
this.$emit('show-delete-modal');
|
||||||
},
|
|
||||||
hideConfirmModal () {
|
|
||||||
this.isConfirmModal = false;
|
|
||||||
},
|
},
|
||||||
closeContext () {
|
closeContext () {
|
||||||
this.$emit('close-context');
|
this.$emit('close-context');
|
||||||
},
|
},
|
||||||
deleteRows () {
|
setNull () {
|
||||||
this.$emit('delete-selected');
|
this.$emit('set-null');
|
||||||
this.closeContext();
|
this.closeContext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,17 +6,17 @@
|
|||||||
:key="cKey"
|
:key="cKey"
|
||||||
class="td p-0"
|
class="td p-0"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@contextmenu.prevent="$emit('contextmenu', $event, {id: row._id, field: cKey})"
|
@contextmenu.prevent="openContext($event, { id: row._id, field: cKey })"
|
||||||
>
|
>
|
||||||
<template v-if="cKey !== '_id'">
|
<template v-if="cKey !== '_id'">
|
||||||
<span
|
<span
|
||||||
v-if="!isInlineEditor[cKey]"
|
v-if="!isInlineEditor[cKey]"
|
||||||
class="cell-content px-2"
|
class="cell-content px-2"
|
||||||
:class="`${isNull(col)} type-${getFieldType(cKey)}`"
|
:class="`${isNull(col)} ${typeClass(fields[cKey].type)}`"
|
||||||
@dblclick="editON($event, col, cKey)"
|
@dblclick="editON($event, col, cKey)"
|
||||||
>{{ col | typeFormat(getFieldType(cKey), getFieldPrecision(cKey)) | cutText }}</span>
|
>{{ col | typeFormat(fields[cKey].type.toLowerCase(), fields[cKey].length) | cutText }}</span>
|
||||||
<ForeignKeySelect
|
<ForeignKeySelect
|
||||||
v-else-if="foreignKeys.includes(cKey)"
|
v-else-if="isForeignKey(cKey)"
|
||||||
class="editable-field"
|
class="editable-field"
|
||||||
:value.sync="editingContent"
|
:value.sync="editingContent"
|
||||||
:key-usage="getKeyUsage(cKey)"
|
:key-usage="getKeyUsage(cKey)"
|
||||||
@@ -34,6 +34,15 @@
|
|||||||
class="editable-field px-2"
|
class="editable-field px-2"
|
||||||
@blur="editOFF"
|
@blur="editOFF"
|
||||||
>
|
>
|
||||||
|
<select
|
||||||
|
v-else-if="inputProps.type === 'boolean'"
|
||||||
|
v-model="editingContent"
|
||||||
|
class="form-select small-select editable-field"
|
||||||
|
@blur="editOFF"
|
||||||
|
>
|
||||||
|
<option>true</option>
|
||||||
|
<option>false</option>
|
||||||
|
</select>
|
||||||
<input
|
<input
|
||||||
v-else
|
v-else
|
||||||
ref="editField"
|
ref="editField"
|
||||||
@@ -61,14 +70,52 @@
|
|||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<div>
|
<div>
|
||||||
<textarea
|
<TextEditor
|
||||||
v-model="editingContent"
|
:value.sync="editingContent"
|
||||||
class="form-input textarea-editor"
|
editor-class="textarea-editor"
|
||||||
|
:mode="editorMode"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="editor-field-info">
|
<div class="editor-field-info p-vcentered">
|
||||||
<div><b>{{ $t('word.size') }}</b>: {{ editingContent ? editingContent.length : 0 }}</div>
|
<div class="d-flex p-vcentered">
|
||||||
<div><b>{{ $t('word.type') }}</b>: {{ editingType.toUpperCase() }}</div>
|
<label for="editorMode" class="form-label mr-2">
|
||||||
|
<b>{{ $t('word.content') }}</b>:
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="editorMode"
|
||||||
|
v-model="editorMode"
|
||||||
|
class="form-select select-sm"
|
||||||
|
>
|
||||||
|
<option value="text">
|
||||||
|
TEXT
|
||||||
|
</option>
|
||||||
|
<option value="html">
|
||||||
|
HTML
|
||||||
|
</option>
|
||||||
|
<option value="xml">
|
||||||
|
XML
|
||||||
|
</option>
|
||||||
|
<option value="json">
|
||||||
|
JSON
|
||||||
|
</option>
|
||||||
|
<option value="svg">
|
||||||
|
SVG
|
||||||
|
</option>
|
||||||
|
<option value="yaml">
|
||||||
|
YAML
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex">
|
||||||
|
<div class="p-vcentered">
|
||||||
|
<div class="mr-4">
|
||||||
|
<b>{{ $t('word.size') }}</b>: {{ editingContent ? editingContent.length : 0 }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<b>{{ $t('word.type') }}</b>: {{ editingType.toUpperCase() }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -135,19 +182,21 @@ import { mimeFromHex } from 'common/libs/mimeFromHex';
|
|||||||
import { formatBytes } from 'common/libs/formatBytes';
|
import { formatBytes } from 'common/libs/formatBytes';
|
||||||
import { bufferToBase64 } from 'common/libs/bufferToBase64';
|
import { bufferToBase64 } from 'common/libs/bufferToBase64';
|
||||||
import hexToBinary from 'common/libs/hexToBinary';
|
import hexToBinary from 'common/libs/hexToBinary';
|
||||||
import { TEXT, LONG_TEXT, NUMBER, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
|
import { TEXT, LONG_TEXT, ARRAY, TEXT_SEARCH, NUMBER, FLOAT, BOOLEAN, DATE, TIME, DATETIME, BLOB, BIT, HAS_TIMEZONE } from 'common/fieldTypes';
|
||||||
import { mask } from 'vue-the-mask';
|
import { VueMaskDirective } from 'v-mask';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
import TextEditor from '@/components/BaseTextEditor';
|
||||||
import ForeignKeySelect from '@/components/ForeignKeySelect';
|
import ForeignKeySelect from '@/components/ForeignKeySelect';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WorkspaceQueryTableRow',
|
name: 'WorkspaceQueryTableRow',
|
||||||
components: {
|
components: {
|
||||||
ConfirmModal,
|
ConfirmModal,
|
||||||
|
TextEditor,
|
||||||
ForeignKeySelect
|
ForeignKeySelect
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
mask
|
mask: VueMaskDirective
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
formatBytes,
|
formatBytes,
|
||||||
@@ -164,6 +213,9 @@ export default {
|
|||||||
return moment(val).isValid() ? moment(val).format('YYYY-MM-DD') : val;
|
return moment(val).isValid() ? moment(val).format('YYYY-MM-DD') : val;
|
||||||
|
|
||||||
if (DATETIME.includes(type)) {
|
if (DATETIME.includes(type)) {
|
||||||
|
if (typeof val === 'string')
|
||||||
|
return val;
|
||||||
|
|
||||||
let datePrecision = '';
|
let datePrecision = '';
|
||||||
for (let i = 0; i < precision; i++)
|
for (let i = 0; i < precision; i++)
|
||||||
datePrecision += i === 0 ? '.S' : 'S';
|
datePrecision += i === 0 ? '.S' : 'S';
|
||||||
@@ -185,15 +237,18 @@ export default {
|
|||||||
return hexToBinary(hex);
|
return hexToBinary(hex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ARRAY.includes(type)) {
|
||||||
|
if (Array.isArray(val))
|
||||||
|
return JSON.stringify(val).replaceAll('[', '{').replaceAll(']', '}');
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
row: Object,
|
row: Object,
|
||||||
fields: {
|
fields: Object,
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
keyUsage: Array
|
keyUsage: Array
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
@@ -206,6 +261,8 @@ export default {
|
|||||||
editingContent: null,
|
editingContent: null,
|
||||||
editingType: null,
|
editingType: null,
|
||||||
editingField: null,
|
editingField: null,
|
||||||
|
editingLength: null,
|
||||||
|
editorMode: 'text',
|
||||||
contentInfo: {
|
contentInfo: {
|
||||||
ext: '',
|
ext: '',
|
||||||
mime: '',
|
mime: '',
|
||||||
@@ -219,16 +276,19 @@ export default {
|
|||||||
if ([...TEXT, ...LONG_TEXT].includes(this.editingType))
|
if ([...TEXT, ...LONG_TEXT].includes(this.editingType))
|
||||||
return { type: 'text', mask: false };
|
return { type: 'text', mask: false };
|
||||||
|
|
||||||
if (NUMBER.includes(this.editingType))
|
if ([...NUMBER, ...FLOAT].includes(this.editingType))
|
||||||
return { type: 'number', mask: false };
|
return { type: 'number', mask: false };
|
||||||
|
|
||||||
if (TIME.includes(this.editingType)) {
|
if (TIME.includes(this.editingType)) {
|
||||||
let timeMask = '##:##:##';
|
let timeMask = '##:##:##';
|
||||||
const precision = this.getFieldPrecision(this.editingField);
|
const precision = this.fields[this.editingField].length;
|
||||||
|
|
||||||
for (let i = 0; i < precision; i++)
|
for (let i = 0; i < precision; i++)
|
||||||
timeMask += i === 0 ? '.#' : '#';
|
timeMask += i === 0 ? '.#' : '#';
|
||||||
|
|
||||||
|
if (HAS_TIMEZONE.includes(this.editingType))
|
||||||
|
timeMask += 'X##';
|
||||||
|
|
||||||
return { type: 'text', mask: timeMask };
|
return { type: 'text', mask: timeMask };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,19 +297,22 @@ export default {
|
|||||||
|
|
||||||
if (DATETIME.includes(this.editingType)) {
|
if (DATETIME.includes(this.editingType)) {
|
||||||
let datetimeMask = '####-##-## ##:##:##';
|
let datetimeMask = '####-##-## ##:##:##';
|
||||||
const precision = this.getFieldPrecision(this.editingField);
|
const precision = this.fields[this.editingField].length;
|
||||||
|
|
||||||
for (let i = 0; i < precision; i++)
|
for (let i = 0; i < precision; i++)
|
||||||
datetimeMask += i === 0 ? '.#' : '#';
|
datetimeMask += i === 0 ? '.#' : '#';
|
||||||
|
|
||||||
|
if (HAS_TIMEZONE.includes(this.editingType))
|
||||||
|
datetimeMask += 'X##';
|
||||||
|
|
||||||
return { type: 'text', mask: datetimeMask };
|
return { type: 'text', mask: datetimeMask };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BLOB.includes(this.editingType))
|
if (BLOB.includes(this.editingType))
|
||||||
return { type: 'file', mask: false };
|
return { type: 'file', mask: false };
|
||||||
|
|
||||||
if (BIT.includes(this.editingType))
|
if (BOOLEAN.includes(this.editingType))
|
||||||
return { type: 'text', mask: false };
|
return { type: 'boolean', mask: false };
|
||||||
|
|
||||||
return { type: 'text', mask: false };
|
return { type: 'text', mask: false };
|
||||||
},
|
},
|
||||||
@@ -260,80 +323,70 @@ export default {
|
|||||||
return this.keyUsage.map(key => key.field);
|
return this.keyUsage.map(key => key.field);
|
||||||
},
|
},
|
||||||
isEditable () {
|
isEditable () {
|
||||||
return this.fields ? !!(this.fields[0].schema && this.fields[0].table) : false;
|
if (this.fields) {
|
||||||
|
const nElements = Object.keys(this.fields).reduce((acc, curr) => {
|
||||||
|
acc.add(this.fields[curr].table);
|
||||||
|
acc.add(this.fields[curr].schema);
|
||||||
|
return acc;
|
||||||
|
}, new Set([]));
|
||||||
|
|
||||||
|
if (nElements.size > 2) return false;
|
||||||
|
|
||||||
|
return !!(this.fields[Object.keys(this.fields)[0]].schema && this.fields[Object.keys(this.fields)[0]].table);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
fields () {
|
fields () {
|
||||||
this.fields.forEach(field => {
|
Object.keys(this.fields).forEach(field => {
|
||||||
this.isInlineEditor[field.name] = false;
|
this.isInlineEditor[field.name] = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getFieldType (cKey) {
|
isForeignKey (key) {
|
||||||
let type = 'unknown';
|
if (key.includes('.'))
|
||||||
const field = this.getFieldObj(cKey);
|
key = key.split('.').pop();
|
||||||
if (field)
|
|
||||||
type = field.type;
|
|
||||||
|
|
||||||
return type.toLowerCase();
|
return this.foreignKeys.includes(key);
|
||||||
},
|
|
||||||
getFieldPrecision (cKey) {
|
|
||||||
let length = 0;
|
|
||||||
const field = this.getFieldObj(cKey);
|
|
||||||
if (field)
|
|
||||||
length = field.datePrecision;
|
|
||||||
|
|
||||||
return length;
|
|
||||||
},
|
|
||||||
getFieldObj (cKey) {
|
|
||||||
return this.fields.filter(field => {
|
|
||||||
let fieldNames = [
|
|
||||||
field.name,
|
|
||||||
field.alias,
|
|
||||||
`${field.table}.${field.name}`,
|
|
||||||
`${field.table}.${field.alias}`,
|
|
||||||
`${field.tableAlias}.${field.name}`,
|
|
||||||
`${field.tableAlias}.${field.alias}`
|
|
||||||
];
|
|
||||||
|
|
||||||
if (field.table)
|
|
||||||
fieldNames = [...fieldNames, `${field.table.toLowerCase()}.${field.name}`, `${field.table.toLowerCase()}.${field.alias}`];
|
|
||||||
|
|
||||||
if (field.tableAlias)
|
|
||||||
fieldNames = [...fieldNames, `${field.tableAlias.toLowerCase()}.${field.name}`, `${field.tableAlias.toLowerCase()}.${field.alias}`];
|
|
||||||
|
|
||||||
return fieldNames.includes(cKey);
|
|
||||||
})[0];
|
|
||||||
},
|
},
|
||||||
isNull (value) {
|
isNull (value) {
|
||||||
return value === null ? ' is-null' : '';
|
return value === null ? ' is-null' : '';
|
||||||
},
|
},
|
||||||
|
typeClass (type) {
|
||||||
|
if (type)
|
||||||
|
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||||
|
return '';
|
||||||
|
},
|
||||||
bufferToBase64 (val) {
|
bufferToBase64 (val) {
|
||||||
return bufferToBase64(val);
|
return bufferToBase64(val);
|
||||||
},
|
},
|
||||||
editON (event, content, field) {
|
editON (event, content, field) {
|
||||||
if (!this.isEditable) return;
|
if (!this.isEditable) return;
|
||||||
|
|
||||||
const type = this.getFieldType(field).toUpperCase(); ;
|
window.addEventListener('keydown', this.onKey);
|
||||||
this.originalContent = content;
|
|
||||||
|
const type = this.fields[field].type.toUpperCase(); ;
|
||||||
|
this.originalContent = this.$options.filters.typeFormat(content, type, this.fields[field].length);
|
||||||
this.editingType = type;
|
this.editingType = type;
|
||||||
this.editingField = field;
|
this.editingField = field;
|
||||||
|
this.editingLength = this.fields[field].length;
|
||||||
|
|
||||||
if (LONG_TEXT.includes(type)) {
|
if ([...LONG_TEXT, ...ARRAY, ...TEXT_SEARCH].includes(type)) {
|
||||||
this.isTextareaEditor = true;
|
this.isTextareaEditor = true;
|
||||||
this.editingContent = this.$options.filters.typeFormat(this.originalContent, type);
|
this.editingContent = this.$options.filters.typeFormat(content, type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BLOB.includes(type)) {
|
if (BLOB.includes(type)) {
|
||||||
this.isBlobEditor = true;
|
this.isBlobEditor = true;
|
||||||
this.editingContent = this.originalContent || '';
|
this.editingContent = content || '';
|
||||||
this.fileToUpload = null;
|
this.fileToUpload = null;
|
||||||
this.willBeDeleted = false;
|
this.willBeDeleted = false;
|
||||||
|
|
||||||
if (this.originalContent !== null) {
|
if (content !== null) {
|
||||||
const buff = Buffer.from(this.editingContent);
|
const buff = Buffer.from(this.editingContent);
|
||||||
if (buff.length) {
|
if (buff.length) {
|
||||||
const hex = buff.toString('hex').substring(0, 8).toUpperCase();
|
const hex = buff.toString('hex').substring(0, 8).toUpperCase();
|
||||||
@@ -349,7 +402,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Inline editable fields
|
// Inline editable fields
|
||||||
this.editingContent = this.$options.filters.typeFormat(this.originalContent, type, this.getFieldPrecision(field));
|
this.editingContent = this.originalContent;
|
||||||
this.$nextTick(() => { // Focus on input
|
this.$nextTick(() => { // Focus on input
|
||||||
event.target.blur();
|
event.target.blur();
|
||||||
|
|
||||||
@@ -360,10 +413,18 @@ export default {
|
|||||||
this.isInlineEditor = { ...this.isInlineEditor, ...obj };
|
this.isInlineEditor = { ...this.isInlineEditor, ...obj };
|
||||||
},
|
},
|
||||||
editOFF () {
|
editOFF () {
|
||||||
|
if (!this.editingField) return;
|
||||||
|
|
||||||
this.isInlineEditor[this.editingField] = false;
|
this.isInlineEditor[this.editingField] = false;
|
||||||
let content;
|
let content;
|
||||||
if (!BLOB.includes(this.editingType)) {
|
if (!BLOB.includes(this.editingType)) {
|
||||||
if (this.editingContent === this.$options.filters.typeFormat(this.originalContent, this.editingType)) return;// If not changed
|
if ([...DATETIME, ...TIME].includes(this.editingType)) {
|
||||||
|
if (this.editingContent.substring(this.editingContent.length - 1) === '.')
|
||||||
|
this.editingContent = this.editingContent.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.editingContent === this.$options.filters.typeFormat(this.originalContent, this.editingType, this.editingLength)) return;// If not changed
|
||||||
|
|
||||||
content = this.editingContent;
|
content = this.editingContent;
|
||||||
}
|
}
|
||||||
else { // Handle file upload
|
else { // Handle file upload
|
||||||
@@ -378,13 +439,14 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.$emit('update-field', {
|
this.$emit('update-field', {
|
||||||
field: this.getFieldObj(this.editingField).name,
|
field: this.fields[this.editingField].name,
|
||||||
type: this.editingType,
|
type: this.editingType,
|
||||||
content
|
content
|
||||||
});
|
});
|
||||||
|
|
||||||
this.editingType = null;
|
this.editingType = null;
|
||||||
this.editingField = null;
|
this.editingField = null;
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
hideEditorModal () {
|
hideEditorModal () {
|
||||||
this.isTextareaEditor = false;
|
this.isTextareaEditor = false;
|
||||||
@@ -416,14 +478,27 @@ export default {
|
|||||||
};
|
};
|
||||||
this.willBeDeleted = true;
|
this.willBeDeleted = true;
|
||||||
},
|
},
|
||||||
contextMenu (event, cell) {
|
|
||||||
this.$emit('update-field', event, cell);
|
|
||||||
},
|
|
||||||
selectRow (event, row) {
|
selectRow (event, row) {
|
||||||
this.$emit('select-row', event, row);
|
this.$emit('select-row', event, row);
|
||||||
},
|
},
|
||||||
getKeyUsage (keyName) {
|
getKeyUsage (keyName) {
|
||||||
|
if (keyName.includes('.'))
|
||||||
|
return this.keyUsage.find(key => key.field === keyName.split('.').pop());
|
||||||
return this.keyUsage.find(key => key.field === keyName);
|
return this.keyUsage.find(key => key.field === keyName);
|
||||||
|
},
|
||||||
|
openContext (event, payload) {
|
||||||
|
if (this.isEditable) {
|
||||||
|
payload.field = this.fields[payload.field].name;// Ensures field name only
|
||||||
|
this.$emit('contextmenu', event, payload);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
this.isInlineEditor[this.editingField] = false;
|
||||||
|
this.editingField = null;
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -453,7 +528,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.editor-field-info {
|
.editor-field-info {
|
||||||
margin-top: 0.6rem;
|
margin-top: 0.4rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
|
|||||||
@@ -33,11 +33,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn btn-dark btn-sm" @click="showAddModal">
|
<button
|
||||||
<span>{{ $t('word.add') }}</span>
|
v-if="isTable"
|
||||||
|
class="btn btn-dark btn-sm"
|
||||||
|
:disabled="isQuering"
|
||||||
|
@click="showFakerModal"
|
||||||
|
>
|
||||||
|
<span>{{ $t('message.tableFiller') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-playlist-plus ml-1" />
|
<i class="mdi mdi-24px mdi-playlist-plus ml-1" />
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown export-dropdown">
|
|
||||||
|
<div class="dropdown export-dropdown pr-2">
|
||||||
<button
|
<button
|
||||||
:disabled="isQuering"
|
:disabled="isQuering"
|
||||||
class="btn btn-dark btn-sm dropdown-toggle mr-0 pr-0"
|
class="btn btn-dark btn-sm dropdown-toggle mr-0 pr-0"
|
||||||
@@ -58,10 +64,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-info">
|
<div class="workspace-query-info">
|
||||||
|
<div
|
||||||
|
v-if="results.length"
|
||||||
|
class="d-flex"
|
||||||
|
:title="$t('message.queryDuration')"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-timer-sand mdi-rotate-180 pr-1" /> <b>{{ results[0].duration / 1000 }}s</b>
|
||||||
|
</div>
|
||||||
<div v-if="results.length && results[0].rows">
|
<div v-if="results.length && results[0].rows">
|
||||||
{{ $t('word.results') }}: <b>{{ results[0].rows.length.toLocaleString() }}</b>
|
{{ $t('word.results') }}: <b>{{ results[0].rows.length.toLocaleString() }}</b>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="results.length && results[0].rows && tableInfo && results[0].rows.length < tableInfo.rows">
|
<div v-if="hasApproximately">
|
||||||
{{ $t('word.total') }}: <b>{{ tableInfo.rows.toLocaleString() }}</b> <small>({{ $t('word.approximately') }})</small>
|
{{ $t('word.total') }}: <b>{{ tableInfo.rows.toLocaleString() }}</b> <small>({{ $t('word.approximately') }})</small>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="workspace.breadcrumbs.database">
|
<div v-if="workspace.breadcrumbs.database">
|
||||||
@@ -70,13 +83,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-results column col-12">
|
<div class="workspace-query-results p-relative column col-12">
|
||||||
|
<BaseLoader v-if="isQuering" />
|
||||||
<WorkspaceQueryTable
|
<WorkspaceQueryTable
|
||||||
v-if="results"
|
v-if="results"
|
||||||
ref="queryTable"
|
ref="queryTable"
|
||||||
:results="results"
|
:results="results"
|
||||||
:tab-uid="tabUid"
|
:tab-uid="tabUid"
|
||||||
:conn-uid="connection.uid"
|
:conn-uid="connection.uid"
|
||||||
|
:is-selected="isSelected"
|
||||||
mode="table"
|
mode="table"
|
||||||
@update-field="updateField"
|
@update-field="updateField"
|
||||||
@delete-selected="deleteSelected"
|
@delete-selected="deleteSelected"
|
||||||
@@ -91,21 +106,33 @@
|
|||||||
@hide="hideAddModal"
|
@hide="hideAddModal"
|
||||||
@reload="reloadTable"
|
@reload="reloadTable"
|
||||||
/>
|
/>
|
||||||
|
<ModalFakerRows
|
||||||
|
v-if="isFakerModal"
|
||||||
|
:fields="fields"
|
||||||
|
:key-usage="keyUsage"
|
||||||
|
:tab-uid="tabUid"
|
||||||
|
@hide="hideFakerModal"
|
||||||
|
@reload="reloadTable"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Tables from '@/ipc-api/Tables';
|
import Tables from '@/ipc-api/Tables';
|
||||||
|
import BaseLoader from '@/components/BaseLoader';
|
||||||
import WorkspaceQueryTable from '@/components/WorkspaceQueryTable';
|
import WorkspaceQueryTable from '@/components/WorkspaceQueryTable';
|
||||||
import ModalNewTableRow from '@/components/ModalNewTableRow';
|
import ModalNewTableRow from '@/components/ModalNewTableRow';
|
||||||
|
import ModalFakerRows from '@/components/ModalFakerRows';
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import tableTabs from '@/mixins/tableTabs';
|
import tableTabs from '@/mixins/tableTabs';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WorkspaceTableTab',
|
name: 'WorkspaceTableTab',
|
||||||
components: {
|
components: {
|
||||||
|
BaseLoader,
|
||||||
WorkspaceQueryTable,
|
WorkspaceQueryTable,
|
||||||
ModalNewTableRow
|
ModalNewTableRow,
|
||||||
|
ModalFakerRows
|
||||||
},
|
},
|
||||||
mixins: [tableTabs],
|
mixins: [tableTabs],
|
||||||
props: {
|
props: {
|
||||||
@@ -119,6 +146,7 @@ export default {
|
|||||||
results: [],
|
results: [],
|
||||||
lastTable: null,
|
lastTable: null,
|
||||||
isAddModal: false,
|
isAddModal: false,
|
||||||
|
isFakerModal: false,
|
||||||
autorefreshTimer: 0,
|
autorefreshTimer: 0,
|
||||||
refreshInterval: null,
|
refreshInterval: null,
|
||||||
sortParams: {}
|
sortParams: {}
|
||||||
@@ -126,13 +154,17 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
getWorkspace: 'workspaces/getWorkspace'
|
getWorkspace: 'workspaces/getWorkspace',
|
||||||
|
selectedWorkspace: 'workspaces/getSelected'
|
||||||
}),
|
}),
|
||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
},
|
},
|
||||||
isSelected () {
|
isSelected () {
|
||||||
return this.workspace.selected_tab === 'data';
|
return this.workspace.selected_tab === 'data' && this.workspace.uid === this.selectedWorkspace;
|
||||||
|
},
|
||||||
|
isTable () {
|
||||||
|
return !!this.workspace.breadcrumbs.table;
|
||||||
},
|
},
|
||||||
fields () {
|
fields () {
|
||||||
return this.results.length ? this.results[0].fields : [];
|
return this.results.length ? this.results[0].fields : [];
|
||||||
@@ -147,6 +179,13 @@ export default {
|
|||||||
catch (err) {
|
catch (err) {
|
||||||
return { rows: 0 };
|
return { rows: 0 };
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
hasApproximately () {
|
||||||
|
return this.results.length &&
|
||||||
|
this.results[0].rows &&
|
||||||
|
this.tableInfo &&
|
||||||
|
this.results[0].rows.length === 1000 &&
|
||||||
|
this.results[0].rows.length < this.tableInfo.rows;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -222,6 +261,13 @@ export default {
|
|||||||
hideAddModal () {
|
hideAddModal () {
|
||||||
this.isAddModal = false;
|
this.isAddModal = false;
|
||||||
},
|
},
|
||||||
|
showFakerModal () {
|
||||||
|
if (this.isQuering) return;
|
||||||
|
this.isFakerModal = true;
|
||||||
|
},
|
||||||
|
hideFakerModal () {
|
||||||
|
this.isFakerModal = false;
|
||||||
|
},
|
||||||
onKey (e) {
|
onKey (e) {
|
||||||
if (this.isSelected) {
|
if (this.isSelected) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -250,10 +296,6 @@ export default {
|
|||||||
.export-dropdown {
|
.export-dropdown {
|
||||||
.menu {
|
.menu {
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
|
|
||||||
.menu-item a:hover {
|
|
||||||
background: $bg-color-gray;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -69,19 +69,5 @@ module.exports = {
|
|||||||
uploadFile: 'رفع ملف',
|
uploadFile: 'رفع ملف',
|
||||||
addNewRow: 'إضافة صف جديد',
|
addNewRow: 'إضافة صف جديد',
|
||||||
numberOfInserts: 'عدد الإدراجات'
|
numberOfInserts: 'عدد الإدراجات'
|
||||||
},
|
|
||||||
// Date and Time
|
|
||||||
short: {
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'short',
|
|
||||||
day: 'numeric'
|
|
||||||
},
|
|
||||||
long: {
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'short',
|
|
||||||
day: 'numeric',
|
|
||||||
weekday: 'short',
|
|
||||||
hour: 'numeric',
|
|
||||||
minute: 'numeric'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -85,7 +85,27 @@ module.exports = {
|
|||||||
state: 'State',
|
state: 'State',
|
||||||
execution: 'Execution',
|
execution: 'Execution',
|
||||||
starts: 'Starts',
|
starts: 'Starts',
|
||||||
ends: 'Ends'
|
ends: 'Ends',
|
||||||
|
ssl: 'SSL',
|
||||||
|
privateKey: 'Private key',
|
||||||
|
certificate: 'Certificate',
|
||||||
|
caCertificate: 'CA certificate',
|
||||||
|
ciphers: 'Ciphers',
|
||||||
|
upload: 'Upload',
|
||||||
|
browse: 'Browse',
|
||||||
|
faker: 'Faker',
|
||||||
|
content: 'Content',
|
||||||
|
cut: 'Cut',
|
||||||
|
copy: 'Copy',
|
||||||
|
paste: 'Paste',
|
||||||
|
tools: 'Tools',
|
||||||
|
variables: 'Variables',
|
||||||
|
processes: 'Processes',
|
||||||
|
database: 'Database',
|
||||||
|
scratchpad: 'Scratchpad',
|
||||||
|
array: 'Array',
|
||||||
|
changelog: 'Changelog',
|
||||||
|
format: 'Format'
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
appWelcome: 'Welcome to Antares SQL Client!',
|
appWelcome: 'Welcome to Antares SQL Client!',
|
||||||
@@ -172,20 +192,188 @@ module.exports = {
|
|||||||
schedulerBody: 'Scheduler body',
|
schedulerBody: 'Scheduler body',
|
||||||
createNewScheduler: 'Create new scheduler',
|
createNewScheduler: 'Create new scheduler',
|
||||||
deleteScheduler: 'Delete scheduler',
|
deleteScheduler: 'Delete scheduler',
|
||||||
preserveOnCompletion: 'Preserve on completion'
|
preserveOnCompletion: 'Preserve on completion',
|
||||||
|
enableSsl: 'Enable SSL',
|
||||||
|
manualValue: 'Manual value',
|
||||||
|
tableFiller: 'Table Filler',
|
||||||
|
fakeDataLanguage: 'Fake data language',
|
||||||
|
searchForElements: 'Search for elements',
|
||||||
|
selectAll: 'Select all',
|
||||||
|
queryDuration: 'Query duration',
|
||||||
|
includeBetaUpdates: 'Include beta updates',
|
||||||
|
setNull: 'Set NULL',
|
||||||
|
processesList: 'Processes list',
|
||||||
|
processInfo: 'Process info',
|
||||||
|
manageUsers: 'Manage users',
|
||||||
|
createNewSchema: 'Create new schema',
|
||||||
|
schemaName: 'Schema name',
|
||||||
|
editSchema: 'Edit schema',
|
||||||
|
deleteSchema: 'Delete schema',
|
||||||
|
markdownSupported: 'Markdown supported',
|
||||||
|
plantATree: 'Plant a Tree'
|
||||||
},
|
},
|
||||||
// Date and Time
|
faker: {
|
||||||
short: {
|
address: 'Address',
|
||||||
year: 'numeric',
|
commerce: 'Commerce',
|
||||||
month: 'short',
|
company: 'Company',
|
||||||
day: 'numeric'
|
database: 'Database',
|
||||||
},
|
date: 'Date',
|
||||||
long: {
|
finance: 'Finance',
|
||||||
year: 'numeric',
|
git: 'Git',
|
||||||
month: 'short',
|
hacker: 'Hacker',
|
||||||
day: 'numeric',
|
internet: 'Internet',
|
||||||
weekday: 'short',
|
lorem: 'Lorem',
|
||||||
hour: 'numeric',
|
name: 'Name',
|
||||||
minute: 'numeric'
|
music: 'Music',
|
||||||
|
phone: 'Phone',
|
||||||
|
random: 'Random',
|
||||||
|
system: 'System',
|
||||||
|
time: 'Time',
|
||||||
|
vehicle: 'Vehicle',
|
||||||
|
zipCode: 'Zip code',
|
||||||
|
zipCodeByState: 'Zip code by state',
|
||||||
|
city: 'City',
|
||||||
|
cityPrefix: 'City prefix',
|
||||||
|
citySuffix: 'City suffix',
|
||||||
|
streetName: 'Street name',
|
||||||
|
streetAddress: 'Street address',
|
||||||
|
streetSuffix: 'Street suffix',
|
||||||
|
streetPrefix: 'Street prefix',
|
||||||
|
secondaryAddress: 'Secondary address',
|
||||||
|
county: 'County',
|
||||||
|
country: 'Country',
|
||||||
|
countryCode: 'Country code',
|
||||||
|
state: 'State',
|
||||||
|
stateAbbr: 'State abbreviation',
|
||||||
|
latitude: 'Latitude',
|
||||||
|
longitude: 'Longitude',
|
||||||
|
direction: 'Direction',
|
||||||
|
cardinalDirection: 'Cardinal direction',
|
||||||
|
ordinalDirection: 'Ordinal direction',
|
||||||
|
nearbyGPSCoordinate: 'Nearby GPS coordinate',
|
||||||
|
timeZone: 'Time zone',
|
||||||
|
color: 'Color',
|
||||||
|
department: 'Department',
|
||||||
|
productName: 'Product name',
|
||||||
|
price: 'Price',
|
||||||
|
productAdjective: 'Product adjective',
|
||||||
|
productMaterial: 'Product material',
|
||||||
|
product: 'Product',
|
||||||
|
productDescription: 'Product description',
|
||||||
|
suffixes: 'Suffixes',
|
||||||
|
companyName: 'Company name',
|
||||||
|
companySuffix: 'Company suffix',
|
||||||
|
catchPhrase: 'Catch phrase',
|
||||||
|
bs: 'BS',
|
||||||
|
catchPhraseAdjective: 'Catch phrase adjective',
|
||||||
|
catchPhraseDescriptor: 'Catch phrase descriptor',
|
||||||
|
catchPhraseNoun: 'Catch phrase noun',
|
||||||
|
bsAdjective: 'BS adjective',
|
||||||
|
bsBuzz: 'BS buzz',
|
||||||
|
bsNoun: 'BS noun',
|
||||||
|
column: 'Column',
|
||||||
|
type: 'Type',
|
||||||
|
collation: 'Collation',
|
||||||
|
engine: 'Engine',
|
||||||
|
past: 'Past',
|
||||||
|
future: 'Future',
|
||||||
|
between: 'Between',
|
||||||
|
recent: 'Recent',
|
||||||
|
soon: 'Soon',
|
||||||
|
month: 'Month',
|
||||||
|
weekday: 'Weekday',
|
||||||
|
account: 'Account',
|
||||||
|
accountName: 'Account name',
|
||||||
|
routingNumber: 'Routing number',
|
||||||
|
mask: 'Mask',
|
||||||
|
amount: 'Amount',
|
||||||
|
transactionType: 'Transaction type',
|
||||||
|
currencyCode: 'Currency code',
|
||||||
|
currencyName: 'Currency name',
|
||||||
|
currencySymbol: 'Currency symbol',
|
||||||
|
bitcoinAddress: 'Bitcoin address',
|
||||||
|
litecoinAddress: 'Litecoin address',
|
||||||
|
creditCardNumber: 'Credit card number',
|
||||||
|
creditCardCVV: 'Credit card CVV',
|
||||||
|
ethereumAddress: 'Ethereum address',
|
||||||
|
iban: 'Iban',
|
||||||
|
bic: 'Bic',
|
||||||
|
transactionDescription: 'Transaction description',
|
||||||
|
branch: 'Branch',
|
||||||
|
commitEntry: 'Commit entry',
|
||||||
|
commitMessage: 'Commit message',
|
||||||
|
commitSha: 'Commit SHA',
|
||||||
|
shortSha: 'Short SHA',
|
||||||
|
abbreviation: 'Abbreviation',
|
||||||
|
adjective: 'Adjective',
|
||||||
|
noun: 'Noun',
|
||||||
|
verb: 'Verb',
|
||||||
|
ingverb: 'Ingverb',
|
||||||
|
phrase: 'Phrase',
|
||||||
|
avatar: 'Avatar',
|
||||||
|
email: 'Email',
|
||||||
|
exampleEmail: 'Example email',
|
||||||
|
userName: 'Username',
|
||||||
|
protocol: 'Protocol',
|
||||||
|
url: 'Url',
|
||||||
|
domainName: 'Domin name',
|
||||||
|
domainSuffix: 'Domain suffix',
|
||||||
|
domainWord: 'Domain word',
|
||||||
|
ip: 'Ip',
|
||||||
|
ipv6: 'Ipv6',
|
||||||
|
userAgent: 'User agent',
|
||||||
|
mac: 'Mac',
|
||||||
|
password: 'Password',
|
||||||
|
word: 'Word',
|
||||||
|
words: 'Words',
|
||||||
|
sentence: 'Sentence',
|
||||||
|
slug: 'Slug',
|
||||||
|
sentences: 'Sentences',
|
||||||
|
paragraph: 'Paragraph',
|
||||||
|
paragraphs: 'Paragraphs',
|
||||||
|
text: 'Text',
|
||||||
|
lines: 'Lines',
|
||||||
|
genre: 'Genre',
|
||||||
|
firstName: 'First name',
|
||||||
|
lastName: 'Last name',
|
||||||
|
middleName: 'Middle name',
|
||||||
|
findName: 'Full name',
|
||||||
|
jobTitle: 'Job title',
|
||||||
|
gender: 'Gender',
|
||||||
|
prefix: 'Prefix',
|
||||||
|
suffix: 'Suffix',
|
||||||
|
title: 'Title',
|
||||||
|
jobDescriptor: 'Job descriptor',
|
||||||
|
jobArea: 'Job area',
|
||||||
|
jobType: 'Job type',
|
||||||
|
phoneNumber: 'Phone number',
|
||||||
|
phoneNumberFormat: 'Phone number format',
|
||||||
|
phoneFormats: 'Phone formats',
|
||||||
|
number: 'Number',
|
||||||
|
float: 'Float',
|
||||||
|
arrayElement: 'Array element',
|
||||||
|
arrayElements: 'Array elements',
|
||||||
|
objectElement: 'Object element',
|
||||||
|
uuid: 'Uuid',
|
||||||
|
boolean: 'Boolean',
|
||||||
|
image: 'Image',
|
||||||
|
locale: 'Locale',
|
||||||
|
alpha: 'Alpha',
|
||||||
|
alphaNumeric: 'Alphanumeric',
|
||||||
|
hexaDecimal: 'Hexadecimal',
|
||||||
|
fileName: 'File name',
|
||||||
|
commonFileName: 'Common file name',
|
||||||
|
mimeType: 'Mime type',
|
||||||
|
commonFileType: 'Common file type',
|
||||||
|
commonFileExt: 'Common file extension',
|
||||||
|
fileType: 'File type',
|
||||||
|
fileExt: 'File extension',
|
||||||
|
directoryPath: 'Directory path',
|
||||||
|
filePath: 'File path',
|
||||||
|
semver: 'Semver',
|
||||||
|
manufacturer: 'Manufacturer',
|
||||||
|
model: 'Model',
|
||||||
|
fuel: 'Fuel',
|
||||||
|
vin: 'Vin'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -72,19 +72,5 @@ module.exports = {
|
|||||||
numberOfInserts: 'Numero de inserciones',
|
numberOfInserts: 'Numero de inserciones',
|
||||||
openNewTab: 'Abrir nueva pestaña',
|
openNewTab: 'Abrir nueva pestaña',
|
||||||
affectedRows: 'Filas afectadas'
|
affectedRows: 'Filas afectadas'
|
||||||
},
|
|
||||||
// Date and Time
|
|
||||||
short: {
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'short',
|
|
||||||
day: 'numeric'
|
|
||||||
},
|
|
||||||
long: {
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'short',
|
|
||||||
day: 'numeric',
|
|
||||||
weekday: 'short',
|
|
||||||
hour: 'numeric',
|
|
||||||
minute: 'numeric'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
352
src/renderer/i18n/fr-FR.js
Normal file
352
src/renderer/i18n/fr-FR.js
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
module.exports = {
|
||||||
|
word: {
|
||||||
|
edit: 'Éditer',
|
||||||
|
save: 'Enregistrer',
|
||||||
|
close: 'Fermer',
|
||||||
|
delete: 'Supprimer',
|
||||||
|
confirm: 'Confirmer',
|
||||||
|
cancel: 'Annuler',
|
||||||
|
send: 'Envoyer',
|
||||||
|
connectionName: 'Nom de connexion',
|
||||||
|
client: 'Client',
|
||||||
|
hostName: 'Nom d\'hôte',
|
||||||
|
port: 'Port',
|
||||||
|
user: 'Utilisateur',
|
||||||
|
password: 'Mot de passe',
|
||||||
|
credentials: 'Identifiants',
|
||||||
|
connect: 'Se connecter',
|
||||||
|
connected: 'Connecté',
|
||||||
|
disconnect: 'Se déconnecter',
|
||||||
|
disconnected: 'Déconnecté',
|
||||||
|
refresh: 'Rafraichir',
|
||||||
|
settings: 'Paramètres',
|
||||||
|
general: 'Général',
|
||||||
|
themes: 'Thèmes',
|
||||||
|
update: 'Mise à jour',
|
||||||
|
about: 'À propos',
|
||||||
|
language: 'Langue',
|
||||||
|
version: 'Version',
|
||||||
|
donate: 'Faire un don',
|
||||||
|
run: 'Exécuter',
|
||||||
|
schema: 'Schéma',
|
||||||
|
results: 'Résutats',
|
||||||
|
size: 'Taille',
|
||||||
|
seconds: 'Secondes',
|
||||||
|
type: 'Type',
|
||||||
|
mimeType: 'Mime-Type',
|
||||||
|
download: 'Télécharger',
|
||||||
|
add: 'Ajouter',
|
||||||
|
data: 'Données',
|
||||||
|
properties: 'Propriétés',
|
||||||
|
insert: 'Insérer',
|
||||||
|
connecting: 'Connexion',
|
||||||
|
name: 'Nom',
|
||||||
|
collation: 'Collation',
|
||||||
|
clear: 'Effacer',
|
||||||
|
options: 'Options',
|
||||||
|
autoRefresh: 'Auto-rafraichissement',
|
||||||
|
indexes: 'Index',
|
||||||
|
foreignKeys: 'Clés étrangères',
|
||||||
|
length: 'Taille',
|
||||||
|
unsigned: 'Non-signé',
|
||||||
|
default: 'Défaut',
|
||||||
|
comment: 'Commentaire',
|
||||||
|
key: 'Clé | Clés',
|
||||||
|
order: 'Ordre',
|
||||||
|
expression: 'Expression',
|
||||||
|
autoIncrement: 'Auto Increment',
|
||||||
|
engine: 'Engine',
|
||||||
|
field: 'Champ | Champs',
|
||||||
|
approximately: 'Approximativement',
|
||||||
|
total: 'Totale',
|
||||||
|
table: 'Table',
|
||||||
|
discard: 'Abandonner',
|
||||||
|
stay: 'Rester',
|
||||||
|
author: 'Auteur',
|
||||||
|
light: 'Clair',
|
||||||
|
dark: 'Sombre',
|
||||||
|
autoCompletion: 'Completion auto',
|
||||||
|
application: 'Application',
|
||||||
|
editor: 'Editeur',
|
||||||
|
view: 'Vue',
|
||||||
|
definer: 'Définisseur',
|
||||||
|
algorithm: 'Algorithme',
|
||||||
|
trigger: 'Déclencheur | Déclencheurs',
|
||||||
|
storedRoutine: 'Procedure stockée | Procedures stockées',
|
||||||
|
scheduler: 'Opération planifiée | Opérations planifiées',
|
||||||
|
event: 'Evenement',
|
||||||
|
parameters: 'Paramètres',
|
||||||
|
function: 'Fonction | Fonctions',
|
||||||
|
deterministic: 'Déterministe',
|
||||||
|
context: 'Contextz',
|
||||||
|
export: 'Exporter',
|
||||||
|
returns: 'Retourner',
|
||||||
|
timing: 'Horaire',
|
||||||
|
state: 'État',
|
||||||
|
execution: 'Exécution',
|
||||||
|
starts: 'Débuts',
|
||||||
|
ends: 'Fins',
|
||||||
|
ssl: 'SSL',
|
||||||
|
privateKey: 'Clé privée',
|
||||||
|
certificate: 'Certificat',
|
||||||
|
caCertificate: 'CA certificat',
|
||||||
|
ciphers: 'Chiffrement',
|
||||||
|
upload: 'Charger',
|
||||||
|
browse: 'Parcourir',
|
||||||
|
faker: 'Faker'
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
appWelcome: 'Bienvenu sur le client SQL Antares!',
|
||||||
|
appFirstStep: 'Première étape: Créer une nouvelle connexion à une base de données.',
|
||||||
|
addConnection: 'Ajouter une connexion',
|
||||||
|
createConnection: 'Créer une connexion',
|
||||||
|
createNewConnection: 'Créer une nouvelle connexion',
|
||||||
|
askCredentials: 'Demander les identifiants',
|
||||||
|
testConnection: 'Tester la connexion',
|
||||||
|
editConnection: 'Editer la connexion',
|
||||||
|
deleteConnection: 'Supprimer la connexion',
|
||||||
|
deleteCorfirm: 'Êtes-vous sûr de vouloir annuler',
|
||||||
|
connectionSuccessfullyMade: 'Connexion établie avec succès!',
|
||||||
|
madeWithJS: 'Créé avec 💛 et JavaScript!',
|
||||||
|
checkForUpdates: 'Rechercher des mises à jour',
|
||||||
|
noUpdatesAvailable: 'Aucune mise à jour disponible',
|
||||||
|
checkingForUpdate: 'Recherche de mise à jour',
|
||||||
|
checkFailure: 'Erreur lors de la recherche, essayez plus tard',
|
||||||
|
updateAvailable: 'Une mise à jour est disponible',
|
||||||
|
downloadingUpdate: 'Téléchargement de la mise à jour',
|
||||||
|
updateDownloaded: 'Mise à jour téléchargée',
|
||||||
|
restartToInstall: 'Redémarrer Antares pour l\'installer',
|
||||||
|
unableEditFieldWithoutPrimary: 'Impossible de modifier un champ sans clé primaire dans l\'ensemble de résultats',
|
||||||
|
editCell: 'Modifier une cellule',
|
||||||
|
deleteRows: 'Supprimer une ligne | Supprimer {count} lignes',
|
||||||
|
confirmToDeleteRows: 'Êtes-vous sûr de vouloir supprimer une ligne? | Êtes-vous sûr de vouloir supprimer {count} lignes?',
|
||||||
|
notificationsTimeout: 'Délai d\'expiration des notifications',
|
||||||
|
uploadFile: 'Charger un fichier',
|
||||||
|
addNewRow: 'Ajouter une ligne',
|
||||||
|
numberOfInserts: 'Nombre d\'insertions',
|
||||||
|
openNewTab: 'Ouvrir un nouvel onglet',
|
||||||
|
affectedRows: 'Lignes concernées',
|
||||||
|
createNewDatabase: 'Créer une nouvelle base de données',
|
||||||
|
databaseName: 'Nom par défaut',
|
||||||
|
serverDefault: 'Serveur par défaut',
|
||||||
|
deleteDatabase: 'Supprimer la base de données',
|
||||||
|
editDatabase: 'Modifier la base de données',
|
||||||
|
clearChanges: 'Effacer les modifications',
|
||||||
|
addNewField: 'Ajouter un champ',
|
||||||
|
manageIndexes: 'Gérer les index',
|
||||||
|
manageForeignKeys: 'Gérer les clés étrangères',
|
||||||
|
allowNull: 'NULL autorisé',
|
||||||
|
zeroFill: 'Remplissage zéro',
|
||||||
|
customValue: 'Valeur personnalisée',
|
||||||
|
onUpdate: 'Lors d\'une mise à jour',
|
||||||
|
deleteField: 'Supprimer le champ',
|
||||||
|
createNewIndex: 'Créer un index',
|
||||||
|
addToIndex: 'Ajouter à l\'index',
|
||||||
|
createNewTable: 'Créer une nouvelle table',
|
||||||
|
emptyTable: 'Table vide',
|
||||||
|
deleteTable: 'Supprimer la table',
|
||||||
|
emptyCorfirm: 'Êtes-vous sûr de vouloir videz',
|
||||||
|
unsavedChanges: 'Changements non sauvegardés',
|
||||||
|
discardUnsavedChanges: 'Vous avez des modifications non sauvegardées. En quittant cet onglet, ces changements seront supprimés.',
|
||||||
|
thereAreNoIndexes: 'Il n\'y a pas d\'indexes',
|
||||||
|
thereAreNoForeign: 'Il n\'y a pas de clés étrangères',
|
||||||
|
createNewForeign: 'Créer une clés étrangère',
|
||||||
|
referenceTable: 'Table de référence',
|
||||||
|
referenceField: 'CHamp de référence',
|
||||||
|
foreignFields: 'Champ étrangé',
|
||||||
|
invalidDefault: 'Valeur par défaut invalide',
|
||||||
|
onDelete: 'Lors de la suppression',
|
||||||
|
applicationTheme: 'Thème de l\'application',
|
||||||
|
editorTheme: 'Thème de l\'éditeur',
|
||||||
|
wrapLongLines: 'Retour à la ligne automatique',
|
||||||
|
selectStatement: 'Sélectionnez la déclaration',
|
||||||
|
triggerStatement: 'Déclaration de déclencheur',
|
||||||
|
sqlSecurity: 'Sécurité SQL',
|
||||||
|
updateOption: 'Options de mises à jour',
|
||||||
|
deleteView: 'Supprimer la vue',
|
||||||
|
createNewView: 'Créer une nouvelle vue',
|
||||||
|
deleteTrigger: 'Supprimer le déclencheur',
|
||||||
|
createNewTrigger: 'Créer un nouveau déclencheur',
|
||||||
|
currentUser: 'Utilisateur actuel',
|
||||||
|
routineBody: 'Contenu de la procédure',
|
||||||
|
dataAccess: 'Accès aux données',
|
||||||
|
thereAreNoParameters: 'Il n\'y a pas de paramètres',
|
||||||
|
createNewParameter: 'Créer un nouveau paramètre',
|
||||||
|
createNewRoutine: 'Créer une nouvelle procédure stockée',
|
||||||
|
deleteRoutine: 'Supprimer une procédure stockée',
|
||||||
|
functionBody: 'Contenu de la fonction',
|
||||||
|
createNewFunction: 'Créer une nouvelle fonction',
|
||||||
|
deleteFunction: 'Supprimer la fonction',
|
||||||
|
schedulerBody: 'Contenu du opération planifiée',
|
||||||
|
createNewScheduler: 'Créere une nouvelle opération planifiée',
|
||||||
|
deleteScheduler: 'Supprimer l\'opération planifiée',
|
||||||
|
preserveOnCompletion: 'Préserver à l\'achèvement',
|
||||||
|
enableSsl: 'Activer le SSL',
|
||||||
|
manualValue: 'Valeur manuelle',
|
||||||
|
tableFiller: 'Remplisseur de table'
|
||||||
|
},
|
||||||
|
faker: {
|
||||||
|
address: 'Adresse',
|
||||||
|
commerce: 'Commerce',
|
||||||
|
company: 'Entreprise',
|
||||||
|
database: 'Base de données',
|
||||||
|
date: 'Date',
|
||||||
|
finance: 'Finance',
|
||||||
|
git: 'Git',
|
||||||
|
hacker: 'Hacker',
|
||||||
|
internet: 'Internet',
|
||||||
|
lorem: 'Lorem',
|
||||||
|
name: 'Nom',
|
||||||
|
music: 'Musique',
|
||||||
|
phone: 'Téléphone',
|
||||||
|
random: 'Aléatoire',
|
||||||
|
system: 'Système',
|
||||||
|
time: 'Temps',
|
||||||
|
vehicle: 'Véhicle',
|
||||||
|
zipCode: 'Code postal',
|
||||||
|
zipCodeByState: 'Code postal par région',
|
||||||
|
city: 'Ville',
|
||||||
|
cityPrefix: 'Préfixe de la ville',
|
||||||
|
citySuffix: 'Suffixe de la ville',
|
||||||
|
streetName: 'Ne de la rue',
|
||||||
|
streetAddress: 'Adresse',
|
||||||
|
streetSuffix: 'Suffixe de la rue',
|
||||||
|
streetPrefix: 'Préfixe de la rue',
|
||||||
|
secondaryAddress: 'Adresse secondaire',
|
||||||
|
county: 'Comté',
|
||||||
|
country: 'Pays',
|
||||||
|
countryCode: 'Code du pays',
|
||||||
|
state: 'Région',
|
||||||
|
stateAbbr: 'Abbreviation de la région',
|
||||||
|
latitude: 'Latitude',
|
||||||
|
longitude: 'Longitude',
|
||||||
|
direction: 'Direction',
|
||||||
|
cardinalDirection: 'Orientation cardinale',
|
||||||
|
ordinalDirection: 'Orientation originale',
|
||||||
|
nearbyGPSCoordinate: 'Coordonnées GPS des environs',
|
||||||
|
timeZone: 'Fuseau horaire',
|
||||||
|
color: 'Couleur',
|
||||||
|
department: 'Département',
|
||||||
|
productName: 'Nom de produit',
|
||||||
|
price: 'Prix',
|
||||||
|
productAdjective: 'Adjectif du produit',
|
||||||
|
productMaterial: 'Matériau du produit',
|
||||||
|
product: 'Produit',
|
||||||
|
productDescription: 'Description du produit',
|
||||||
|
suffixes: 'Suffixes',
|
||||||
|
companyName: 'Nom de l\'entreprise',
|
||||||
|
companySuffix: 'Suffixe de l\'entreprise',
|
||||||
|
catchPhrase: 'Slogan',
|
||||||
|
bs: 'BS',
|
||||||
|
catchPhraseAdjective: 'Adjectif du slogan',
|
||||||
|
catchPhraseDescriptor: 'Descripteur de slogan',
|
||||||
|
catchPhraseNoun: 'Nom de la phrase d\'accroche',
|
||||||
|
bsAdjective: 'Adjectif BS',
|
||||||
|
bsBuzz: 'BS buzz',
|
||||||
|
bsNoun: 'Nom BS',
|
||||||
|
column: 'Colonne',
|
||||||
|
type: 'Type',
|
||||||
|
collation: 'Collation',
|
||||||
|
engine: 'Engine',
|
||||||
|
past: 'Passé',
|
||||||
|
future: 'Futur',
|
||||||
|
between: 'Entre',
|
||||||
|
recent: 'Récent',
|
||||||
|
soon: 'Bientôt',
|
||||||
|
month: 'Mois',
|
||||||
|
weekday: 'Mercredi',
|
||||||
|
account: 'Compte',
|
||||||
|
accountName: 'Nom de compte',
|
||||||
|
routingNumber: 'Numéros de routage',
|
||||||
|
mask: 'Masque',
|
||||||
|
amount: 'Quantité',
|
||||||
|
transactionType: 'Type de transaction',
|
||||||
|
currencyCode: 'Code de la devise',
|
||||||
|
currencyName: 'Nom de la devise',
|
||||||
|
currencySymbol: 'Symbole de la devise',
|
||||||
|
bitcoinAddress: 'Adresse Bitcoin',
|
||||||
|
litecoinAddress: 'Adresse Litecoin',
|
||||||
|
creditCardNumber: 'Numero de carte de crédit',
|
||||||
|
creditCardCVV: 'Cryptogramme',
|
||||||
|
ethereumAddress: 'Adresse Ethereum',
|
||||||
|
iban: 'Iban',
|
||||||
|
bic: 'Bic',
|
||||||
|
transactionDescription: 'Description de la transaction',
|
||||||
|
branch: 'Branche',
|
||||||
|
commitEntry: 'Valider l\'entrée',
|
||||||
|
commitMessage: 'Valider le message',
|
||||||
|
commitSha: 'Valider le SHA',
|
||||||
|
shortSha: 'SHA court',
|
||||||
|
abbreviation: 'Abbréviation',
|
||||||
|
adjective: 'Adjectif',
|
||||||
|
noun: 'Nom',
|
||||||
|
verb: 'Verbe',
|
||||||
|
ingverb: 'Ingverb',
|
||||||
|
phrase: 'Phrase',
|
||||||
|
avatar: 'Avatar',
|
||||||
|
email: 'Email',
|
||||||
|
exampleEmail: 'Exemple d\'email',
|
||||||
|
userName: 'Nom d\'utilisateur',
|
||||||
|
protocol: 'Protocole',
|
||||||
|
url: 'Url',
|
||||||
|
domainName: 'Nom de domaine',
|
||||||
|
domainSuffix: 'Suffixe du nom de domaine',
|
||||||
|
domainWord: 'Mot de domaine',
|
||||||
|
ip: 'Ip',
|
||||||
|
ipv6: 'Ipv6',
|
||||||
|
userAgent: 'User agent',
|
||||||
|
mac: 'Mac',
|
||||||
|
password: 'Mot de passe',
|
||||||
|
word: 'Mot',
|
||||||
|
words: 'Mots',
|
||||||
|
sentence: 'Phrase',
|
||||||
|
slug: 'Slug',
|
||||||
|
sentences: 'Phrases',
|
||||||
|
paragraph: 'Paragraphe',
|
||||||
|
paragraphs: 'Paragraphes',
|
||||||
|
text: 'Texte',
|
||||||
|
lines: 'Lignes',
|
||||||
|
genre: 'Genre',
|
||||||
|
firstName: 'Prénom',
|
||||||
|
lastName: 'Nom',
|
||||||
|
middleName: 'Deuxième prénom',
|
||||||
|
findName: 'Nom et prénom',
|
||||||
|
jobTitle: 'Intitulé du poste',
|
||||||
|
gender: 'Genre',
|
||||||
|
prefix: 'Préfixe',
|
||||||
|
suffix: 'Suffixe',
|
||||||
|
title: 'Titre',
|
||||||
|
jobDescriptor: 'Descripteur de poste',
|
||||||
|
jobArea: 'Domaine d\'activité',
|
||||||
|
jobType: 'Type de poste',
|
||||||
|
phoneNumber: 'Numéro de téléphone',
|
||||||
|
phoneNumberFormat: 'Format du numéro de téléphone',
|
||||||
|
phoneFormats: 'Formats de téléphone',
|
||||||
|
number: 'Numéro',
|
||||||
|
float: 'Nombre décimaux',
|
||||||
|
arrayElement: 'Élément de Liste',
|
||||||
|
arrayElements: 'Éléments de liste',
|
||||||
|
objectElement: 'Élément d\'objet',
|
||||||
|
uuid: 'Uuid',
|
||||||
|
boolean: 'Boolean',
|
||||||
|
image: 'Image',
|
||||||
|
locale: 'Locale',
|
||||||
|
alpha: 'Alpha',
|
||||||
|
alphaNumeric: 'Alphanumerique',
|
||||||
|
hexaDecimal: 'Hexadecimale',
|
||||||
|
fileName: 'Nom deu fichier',
|
||||||
|
commonFileName: 'Nom de fichier commun',
|
||||||
|
mimeType: 'Mime type',
|
||||||
|
commonFileType: 'Type de dossier commun',
|
||||||
|
commonFileExt: 'Extension de fichier commun',
|
||||||
|
fileType: 'Type de fichier',
|
||||||
|
fileExt: 'Extension de fichier',
|
||||||
|
directoryPath: 'Chemin du répertoire',
|
||||||
|
filePath: 'Chemin d\'accès au fichier',
|
||||||
|
semver: 'Semver',
|
||||||
|
manufacturer: 'Fabricant',
|
||||||
|
model: 'Modèle',
|
||||||
|
fuel: 'Carburant',
|
||||||
|
vin: 'Vin'
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -8,7 +8,8 @@ const i18n = new VueI18n({
|
|||||||
'en-US': require('./en-US'),
|
'en-US': require('./en-US'),
|
||||||
'it-IT': require('./it-IT'),
|
'it-IT': require('./it-IT'),
|
||||||
'ar-SA': require('./ar-SA'),
|
'ar-SA': require('./ar-SA'),
|
||||||
'es-ES': require('./es-ES')
|
'es-ES': require('./es-ES'),
|
||||||
|
'fr-FR': require('./fr-FR')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
export default i18n;
|
export default i18n;
|
||||||
|
|||||||
@@ -72,19 +72,5 @@ module.exports = {
|
|||||||
numberOfInserts: 'Numero di insert',
|
numberOfInserts: 'Numero di insert',
|
||||||
openNewTab: 'Apri nuova scheda',
|
openNewTab: 'Apri nuova scheda',
|
||||||
affectedRows: 'Righe interessate'
|
affectedRows: 'Righe interessate'
|
||||||
},
|
|
||||||
// Date and Time
|
|
||||||
short: {
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'short',
|
|
||||||
day: 'numeric'
|
|
||||||
},
|
|
||||||
long: {
|
|
||||||
year: 'numeric',
|
|
||||||
month: 'short',
|
|
||||||
day: 'numeric',
|
|
||||||
weekday: 'short',
|
|
||||||
hour: 'numeric',
|
|
||||||
minute: 'numeric'
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ export default {
|
|||||||
'en-US': 'English',
|
'en-US': 'English',
|
||||||
'it-IT': 'Italiano',
|
'it-IT': 'Italiano',
|
||||||
'ar-SA': 'العربية',
|
'ar-SA': 'العربية',
|
||||||
'es-ES': 'Español'
|
'es-ES': 'Español',
|
||||||
|
'fr-FR': 'Français'
|
||||||
};
|
};
|
||||||
|
|||||||
BIN
src/renderer/images/light.png
Normal file
BIN
src/renderer/images/light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
@@ -2,20 +2,20 @@
|
|||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
export default class {
|
export default class {
|
||||||
static createDatabase (params) {
|
static createSchema (params) {
|
||||||
return ipcRenderer.invoke('create-database', params);
|
return ipcRenderer.invoke('create-schema', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateDatabase (params) {
|
static updateSchema (params) {
|
||||||
return ipcRenderer.invoke('update-database', params);
|
return ipcRenderer.invoke('update-schema', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDatabaseCollation (params) {
|
static getDatabaseCollation (params) {
|
||||||
return ipcRenderer.invoke('get-database-collation', params);
|
return ipcRenderer.invoke('get-schema-collation', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
static deleteDatabase (params) {
|
static deleteSchema (params) {
|
||||||
return ipcRenderer.invoke('delete-database', params);
|
return ipcRenderer.invoke('delete-schema', params);
|
||||||
}
|
}
|
||||||
|
|
||||||
static getStructure (params) {
|
static getStructure (params) {
|
||||||
@@ -34,6 +34,14 @@ export default class {
|
|||||||
return ipcRenderer.invoke('get-engines', uid);
|
return ipcRenderer.invoke('get-engines', uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getVersion (uid) {
|
||||||
|
return ipcRenderer.invoke('get-version', uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getProcesses (uid) {
|
||||||
|
return ipcRenderer.invoke('get-processes', uid);
|
||||||
|
}
|
||||||
|
|
||||||
static useSchema (params) {
|
static useSchema (params) {
|
||||||
return ipcRenderer.invoke('use-schema', params);
|
return ipcRenderer.invoke('use-schema', params);
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user