Compare commits
192 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 14c64c537c | |||
| 97b3563e25 | |||
| e834fe31ac | |||
| 065de3a0a2 | |||
| 1573de5b1f | |||
| 04fc1bbee0 | |||
| dea378014d | |||
| 3abff36136 | |||
| 70354aa828 | |||
| 372049ae64 | |||
| 5d271be062 | |||
| 07ee1ae828 | |||
| cbe0e2980a | |||
| 0fba50c6ec | |||
| 420255cdd4 | |||
| a8a47ed5f7 | |||
| 425f0663f9 | |||
| e106d100b5 | |||
| d7fdf53932 | |||
| 62f7e57d0c | |||
| 14577d14bb | |||
| f5f2a697e8 | |||
| a0105cf1c3 | |||
| 77c5d28032 | |||
| d1d8592f79 | |||
| 1f828f69a0 | |||
| adc5477673 | |||
| 1e543aa6b0 | |||
| c41e059b0b | |||
| e6ef5ffa56 | |||
| d67d122270 | |||
| b837e2fc68 | |||
| a73a2f483e | |||
| 0a9983d30d | |||
| 58b91ebfe0 | |||
| e78ca2417e | |||
| e1855a262d | |||
| 6b725b1d40 | |||
| f6faad98f8 | |||
| 320aa8ba04 | |||
| 9f0280b991 | |||
| 04fa320820 | |||
| f7c3aa883d | |||
| f7a74df009 | |||
| 003c02b1fb | |||
| ef21ea7448 | |||
| 525c964c62 | |||
| 7845e3e501 | |||
| 0c29e0d566 | |||
| d38097d056 | |||
| c87b8dc738 | |||
| ed6e7fa72d | |||
| f0fa7c81b7 | |||
| 5bb4e496f2 | |||
| 01057332b0 | |||
| ab382dfbcd | |||
| 88c4cdc8e2 | |||
| 15ff211a41 | |||
| 5c855a520a | |||
| 7488bc7a17 | |||
|
|
fd85cf43a2 | ||
| a87079cd17 | |||
| 14d5842056 | |||
| f19f9e23a2 | |||
| 0252a064d9 | |||
| 439356a019 | |||
| c6897af22d | |||
| 7e40dbfba3 | |||
| 27a153ef43 | |||
| 56fcc2650b | |||
| 9622dbec6b | |||
| a0ab63bdb5 | |||
| 8cd76e711c | |||
| 9af71a6e34 | |||
| e7b3c28826 | |||
| 7570b0add8 | |||
|
|
1801bef019 | ||
| 0db5ebd7bf | |||
| 5c21b67b3d | |||
| 08d5b1b329 | |||
| 61a42d51f5 | |||
| d96907ca2d | |||
| 75bbd5f66e | |||
| faa07a077c | |||
| 7ca69e51ae | |||
| d868c772b9 | |||
| a69bdeb20d | |||
| 51db2795bb | |||
| 2d278aa14e | |||
| f74bca7bb4 | |||
| 978a7c5f5b | |||
| 0110756204 | |||
| 7df0cf8389 | |||
| 3aef7e953e | |||
| a975df38dd | |||
| a0a025e450 | |||
| 7cc5e7b67f | |||
| e2ebb04a90 | |||
| e579f37438 | |||
| 3829b94bf7 | |||
| 5c8ee66f43 | |||
| b77e77f3bb | |||
| 1d5614278c | |||
| 8a20befd09 | |||
| 4133fc452f | |||
| 690a4541f9 | |||
| 71910ca5c8 | |||
| cce5adbac7 | |||
| e6d67fcb0c | |||
| e83b80afc9 | |||
| 8742fa10f0 | |||
| ab54eb086f | |||
| 308f69f12e | |||
| 2f30bafa33 | |||
|
|
1716077182 | ||
| 5562e73e75 | |||
| 8a7cc2a14f | |||
| 9ca059d979 | |||
| c9ccf786cd | |||
| 5e9c88a7fd | |||
| e0e6483d1f | |||
| 66227569f4 | |||
| faa799c8ea | |||
| acc1eeb094 | |||
| 865dad80df | |||
| f502620779 | |||
| f61d7eeaf4 | |||
| e71c7568c0 | |||
| 79f033e524 | |||
| 310cfaa3c2 | |||
| 6f93e1f9ab | |||
| 04bdd085a5 | |||
| c568acae14 | |||
| 7a46a119d8 | |||
| f1636f1528 | |||
| f839d5cd3c | |||
| 8f4f1d393c | |||
| ab17c6d4bf | |||
| b4b2f3d0cf | |||
| 7a766f04e6 | |||
| f6b77d1243 | |||
| 840241c3cc | |||
| 0f10c9e824 | |||
| 1e37f2a96f | |||
| 0d6137195d | |||
| d26f168250 | |||
| 5efcad4d3f | |||
| 0a872e7023 | |||
| 0e425a9c6d | |||
| 434711a360 | |||
| 854472c7a3 | |||
| 9a7cd90d5c | |||
| 026400d242 | |||
| 4bd8cbbf6c | |||
| 1382bc9300 | |||
| ae103e5477 | |||
| 6b0b8b19d7 | |||
| 475397ca34 | |||
| 7a62131cc7 | |||
| c7663be338 | |||
| 496490b14a | |||
|
|
672573d8c2 | ||
| d923453919 | |||
| 0435279604 | |||
| 79cef61fea | |||
| dec334737f | |||
| 84c3692e95 | |||
| 5dc0b0bea4 | |||
| 19d16e46bb | |||
| 3baf6fa173 | |||
| 29e2d92b5b | |||
| bebba64d06 | |||
| 9dfe7cca22 | |||
| 35ef070725 | |||
| 370ad6a536 | |||
| 5822b3df43 | |||
| 5208ec171b | |||
| 6e332da425 | |||
| bf3367b41d | |||
| ecfb732c26 | |||
| fd4f032a6f | |||
| 1b09909126 | |||
| 04cd806954 | |||
| 773cb36ca1 | |||
| ccacd3e2c3 | |||
| 15948b30c9 | |||
| 3dcb5d4f14 | |||
|
|
64d93d7c40 | ||
|
|
2d85295093 | ||
|
|
cd58e2d8ca | ||
| d87495822e | |||
|
|
8720bcdad6 |
24
.eslintrc
@@ -9,15 +9,18 @@
|
|||||||
"plugin:vue/recommended"
|
"plugin:vue/recommended"
|
||||||
],
|
],
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"parser": "babel-eslint",
|
"parser": "@babel/eslint-parser",
|
||||||
"ecmaVersion": 9,
|
"ecmaVersion": 9,
|
||||||
"sourceType": "module"
|
"sourceType": "module",
|
||||||
|
"requireConfigFile": false
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"indent": [
|
"indent": [
|
||||||
"error",
|
"error",
|
||||||
3,
|
3,
|
||||||
{ "SwitchCase": 1 }
|
{
|
||||||
|
"SwitchCase": 1
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"linebreak-style": [
|
"linebreak-style": [
|
||||||
"error",
|
"error",
|
||||||
@@ -44,18 +47,25 @@
|
|||||||
"vue/no-side-effects-in-computed-properties": "off",
|
"vue/no-side-effects-in-computed-properties": "off",
|
||||||
"vue/require-default-prop": "off",
|
"vue/require-default-prop": "off",
|
||||||
"vue/no-v-html": "off",
|
"vue/no-v-html": "off",
|
||||||
"vue/html-indent": ["error", 3, {
|
"vue/html-indent": [
|
||||||
|
"error",
|
||||||
|
3,
|
||||||
|
{
|
||||||
"attribute": 1,
|
"attribute": 1,
|
||||||
"baseIndent": 1,
|
"baseIndent": 1,
|
||||||
"closeBracket": 0,
|
"closeBracket": 0,
|
||||||
"ignores": []
|
"ignores": []
|
||||||
}],
|
}
|
||||||
"vue/max-attributes-per-line": ["error", {
|
],
|
||||||
|
"vue/max-attributes-per-line": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
"singleline": 2,
|
"singleline": 2,
|
||||||
"multiline": {
|
"multiline": {
|
||||||
"max": 1,
|
"max": 1,
|
||||||
"allowFirstLine": false
|
"allowFirstLine": false
|
||||||
}
|
}
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
7
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -12,6 +12,7 @@ A clear and concise description of what the bug is.
|
|||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
|
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
2. Click on '....'
|
2. Click on '....'
|
||||||
3. Scroll down to '....'
|
3. Scroll down to '....'
|
||||||
@@ -23,7 +24,13 @@ A clear and concise description of what you expected to happen.
|
|||||||
**Screenshots**
|
**Screenshots**
|
||||||
If applicable, add screenshots to help explain your problem.
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Application (please complete the following information):**
|
||||||
|
|
||||||
|
- Version [e.g. 0.14.0]
|
||||||
|
- Distribution: [e.g. exe, Linux Store, AppImage, dmg]
|
||||||
|
|
||||||
**Desktop (please complete the following information):**
|
**Desktop (please complete the following information):**
|
||||||
|
|
||||||
- OS: [e.g. iOS]
|
- OS: [e.g. iOS]
|
||||||
- Version [e.g. 22]
|
- Version [e.g. 22]
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Build/release
|
name: Build/release [linux]
|
||||||
|
|
||||||
on: push
|
on: push
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
os: [ubuntu-latest]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out Git repository
|
- name: Check out Git repository
|
||||||
@@ -17,7 +17,7 @@ jobs:
|
|||||||
- name: Install Node.js, NPM and Yarn
|
- name: Install Node.js, NPM and Yarn
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 10
|
node-version: 14
|
||||||
|
|
||||||
- name: Build/release Electron app
|
- name: Build/release Electron app
|
||||||
uses: samuelmeuli/action-electron-builder@v1
|
uses: samuelmeuli/action-electron-builder@v1
|
||||||
26
.github/workflows/build-mac.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
name: Build/release [mac]
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos-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: 14
|
||||||
|
|
||||||
|
- name: Build/release Electron app
|
||||||
|
uses: samuelmeuli/action-electron-builder@v1
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.github_token }}
|
||||||
|
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
26
.github/workflows/build-win.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
name: Build/release [windows]
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [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: 14
|
||||||
|
|
||||||
|
- name: Build/release Electron app
|
||||||
|
uses: samuelmeuli/action-electron-builder@v1
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.github_token }}
|
||||||
|
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
@@ -9,7 +9,8 @@
|
|||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"at-rule-no-unknown": null,
|
"at-rule-no-unknown": null,
|
||||||
"no-descending-specificity": null
|
"no-descending-specificity": null,
|
||||||
|
"declaration-colon-newline-after": "always-multi-line"
|
||||||
},
|
},
|
||||||
"syntax": "scss"
|
"syntax": "scss"
|
||||||
}
|
}
|
||||||
257
CHANGELOG.md
@@ -2,6 +2,263 @@
|
|||||||
|
|
||||||
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.3.2](https://github.com/Fabio286/antares/compare/v0.3.1...v0.3.2) (2021-08-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* contextual menu option to duplicate table fields ([3abff36](https://github.com/Fabio286/antares/commit/3abff3613618ddc86e1d6c898f83bcb360e8e5d9))
|
||||||
|
* **UI:** automatic scroll on selected tab ([e834fe3](https://github.com/Fabio286/antares/commit/e834fe31ac8b73f249e3ad8654e97da834b28cfe))
|
||||||
|
* **UI:** automatic scroll to selected tab element in left bar ([04fc1bb](https://github.com/Fabio286/antares/commit/04fc1bbee0cdc3b02288ff5d3b2c402633cae1f1))
|
||||||
|
* **UI:** button to clear sidebar search input ([1573de5](https://github.com/Fabio286/antares/commit/1573de5b1f545328523aaaaac541f5a8046617be))
|
||||||
|
* **UI:** query tab name based on content ([065de3a](https://github.com/Fabio286/antares/commit/065de3a0a28a9e6667df4e41ac1c5fa5561d2171))
|
||||||
|
* **UI:** shortcuts info on empty query tab ([70354aa](https://github.com/Fabio286/antares/commit/70354aa828121004f70e91d65cb9f0a4811dce57))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* tab selected when clicking closing cross ([07ee1ae](https://github.com/Fabio286/antares/commit/07ee1ae828bb5f9971a263f2e39e73c760fed50a))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* approximate table total updated on table refresh ([dea3780](https://github.com/Fabio286/antares/commit/dea378014dd45bf90f51a599b4a801e0bc22059f))
|
||||||
|
* **UI:** loading animation on tables and table context menu improvements ([372049a](https://github.com/Fabio286/antares/commit/372049ae64232e8e61a974edc8be5b319c5c0811))
|
||||||
|
* update italian translation ([cbe0e29](https://github.com/Fabio286/antares/commit/cbe0e2980a9d4562941aec37a57cc936a46f41d8))
|
||||||
|
|
||||||
|
### [0.3.1](https://github.com/Fabio286/antares/compare/v0.3.0...v0.3.1) (2021-07-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **UI:** tabs or explorebar elements selected with mouse wheel or right button ([a8a47ed](https://github.com/Fabio286/antares/commit/a8a47ed5f7d5d8cbbdd6c33ef8307d9dce5b193b))
|
||||||
|
|
||||||
|
## [0.3.0](https://github.com/Fabio286/antares/compare/v0.2.1...v0.3.0) (2021-07-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* close tabs if element deleted ([320aa8b](https://github.com/Fabio286/antares/commit/320aa8ba04d464f628b938b812165cd1ec786492))
|
||||||
|
* new data tabs ([ab382df](https://github.com/Fabio286/antares/commit/ab382dfbcd89f8ab38a8cbdb4d87e1705618fd0d))
|
||||||
|
* new function, procedure and scheduler tabs ([0a9983d](https://github.com/Fabio286/antares/commit/0a9983d30d6aeb9aee1c68b31a31f8c605deea45))
|
||||||
|
* new trigger function tabs ([58b91eb](https://github.com/Fabio286/antares/commit/58b91ebfe0ab200d76d4cfd442b9f2970ae97384))
|
||||||
|
* new trigger setting tabs ([f6faad9](https://github.com/Fabio286/antares/commit/f6faad98f88222500dfb7265b74c1be914b894cd))
|
||||||
|
* new unsaved change reminder ([f7a74df](https://github.com/Fabio286/antares/commit/f7a74df0097867a82a2a2d8b3c278a81897d7898))
|
||||||
|
* new view setting tabs ([003c02b](https://github.com/Fabio286/antares/commit/003c02b1fbe5aba7f53f3faa5610b0c2f7706793))
|
||||||
|
* option to restore session on startup ([adc5477](https://github.com/Fabio286/antares/commit/adc5477673603cd63fabf77a53db5397e3774e0e))
|
||||||
|
* option to select schema in query tabs ([a73a2f4](https://github.com/Fabio286/antares/commit/a73a2f483ef6927def4ba635e306277c34ae4b53))
|
||||||
|
* **UI:** empty workspace view ([e1855a2](https://github.com/Fabio286/antares/commit/e1855a262dc24363fc143a38a70154938308bd71))
|
||||||
|
* rename tabs if element is renamed ([ef21ea7](https://github.com/Fabio286/antares/commit/ef21ea74481839ba67ca79d40d5f47e1c12aebc0))
|
||||||
|
* **MySQL:** improved schema detection for queries ([5bb4e49](https://github.com/Fabio286/antares/commit/5bb4e496f289f3ae8a46f24d1a2fbb896d9da86e))
|
||||||
|
* **UI:** close temp data tabs ([88c4cdc](https://github.com/Fabio286/antares/commit/88c4cdc8e2a60ffbe26b0f831184c3a6dd9a1637))
|
||||||
|
* **UI:** display schema in data tabs ([0105733](https://github.com/Fabio286/antares/commit/01057332b090997c107bf395bb1fc3b9195e8218))
|
||||||
|
* **UI:** new table settings tabs ([7845e3e](https://github.com/Fabio286/antares/commit/7845e3e501bb7f890d07b42d4f3eb5f9f4bc8586))
|
||||||
|
* **UI:** sortable tabs ([d38097d](https://github.com/Fabio286/antares/commit/d38097d0567020b5265b5a0b347f5e1f38e0b1d4))
|
||||||
|
* **UI:** temporary table data tabs ([a87079c](https://github.com/Fabio286/antares/commit/a87079cd179033cebb6fd228ad7f1b991f3b6c46))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* clear empty indexes and foreign keys on confirm respective modals ([04fa320](https://github.com/Fabio286/antares/commit/04fa320820df1f70a4ef05e4a6e4cbcd4081d047))
|
||||||
|
* enabled copy context on non editable rows ([525c964](https://github.com/Fabio286/antares/commit/525c964c623e7574f6abe732a2b315b8805d5eee))
|
||||||
|
* issues with trigger temp tabs ([6b725b1](https://github.com/Fabio286/antares/commit/6b725b1d40753ad89bfad6f1df57c6fb737e5262))
|
||||||
|
* manual page input not disabled when only one page is available ([62f7e57](https://github.com/Fabio286/antares/commit/62f7e57d0ccf6041b5469bec6a1864aa5b045609))
|
||||||
|
* new field default value unknown instead 'noval' ([77c5d28](https://github.com/Fabio286/antares/commit/77c5d280325792e20befc845f3a6834837131e39))
|
||||||
|
* reload twice after element rename ([1e543aa](https://github.com/Fabio286/antares/commit/1e543aa6b0b63e9134bf544682a26bd98573b794))
|
||||||
|
* solved a vulnerability in table names ([5c855a5](https://github.com/Fabio286/antares/commit/5c855a520a6bc66cc00b0b8afc6d2c03c75c0fab))
|
||||||
|
* sort order of tables is lost switching pages ([14577d1](https://github.com/Fabio286/antares/commit/14577d14bb337898a1fd0fdee1c3760812b9d21f))
|
||||||
|
* wrong editor height with some conditions ([d7fdf53](https://github.com/Fabio286/antares/commit/d7fdf53932a4d0609a88641c40d451c71b12c242))
|
||||||
|
* **UI:** multiple trigger tabs open on single click on explore bar ([e78ca24](https://github.com/Fabio286/antares/commit/e78ca2417e8f996fa0507c5a5586a75393dfe8ee))
|
||||||
|
* **UI:** not disabled buttons during save table setting tabs ([a0105cf](https://github.com/Fabio286/antares/commit/a0105cf1c37144ea27b212029d39275918f4f95e))
|
||||||
|
* tab won't open after table or view creation ([f7c3aa8](https://github.com/Fabio286/antares/commit/f7c3aa883dfcad0a28222c53cf22ab49d381559c))
|
||||||
|
* wrong loaded schema change ([c41e059](https://github.com/Fabio286/antares/commit/c41e059b0ba6d8c6d545c3b86a21a9a2abb6f537))
|
||||||
|
* **UI:** table icon in view data tabs ([f0fa7c8](https://github.com/Fabio286/antares/commit/f0fa7c81b7aa2a05833ce0b243afed39db98d66b))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **UI:** improvements in setting bar connections sort ([0c29e0d](https://github.com/Fabio286/antares/commit/0c29e0d566c792ffd1b2b7045124e170b9c51985))
|
||||||
|
|
||||||
|
### [0.2.1](https://github.com/Fabio286/antares/compare/v0.2.0...v0.2.1) (2021-07-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **UI:** contextual menu shortcuts to create new elements on folders ([0252a06](https://github.com/Fabio286/antares/commit/0252a064d99df09b01c020c7d10c76dd43d4ede8))
|
||||||
|
* context menu option to duplicate connections ([439356a](https://github.com/Fabio286/antares/commit/439356a01993a6a248f5a7d7df305ac5e0e63775))
|
||||||
|
* **MySQL:** possibility to set a default schema in connection parameters ([c6897af](https://github.com/Fabio286/antares/commit/c6897af22d04ed930289a55124b3e8d080fbae3a))
|
||||||
|
* **UI:** new connection add panel ([8cd76e7](https://github.com/Fabio286/antares/commit/8cd76e711c9328254d498b4f9afa221311f5a487))
|
||||||
|
* **UI:** new connection edit panel ([9af71a6](https://github.com/Fabio286/antares/commit/9af71a6e343deda1bf79d8410511cf861af3c304))
|
||||||
|
* SSH Tunnel functionality ([#81](https://github.com/Fabio286/antares/issues/81)) ([1801bef](https://github.com/Fabio286/antares/commit/1801bef019cee77a99df7e3822145ad952465abb))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* clear corrupted configurations to avoid exceptions ([56fcc26](https://github.com/Fabio286/antares/commit/56fcc2650b93ece398118f39f027dc9520dd8a6a))
|
||||||
|
* **UI:** connection tab indicator when scrolling ([a0ab63b](https://github.com/Fabio286/antares/commit/a0ab63bdb533aac9b2bdc2ee07a3b1f2b70ea227))
|
||||||
|
* avoid to trigger schema loading multiple times ([7570b0a](https://github.com/Fabio286/antares/commit/7570b0add8cb9130f15cf8cb807a96dbfd2837d0))
|
||||||
|
|
||||||
|
## [0.2.0](https://github.com/Fabio286/antares/compare/v0.1.13...v0.2.0) (2021-07-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* contextual option to duplicate tables ([08d5b1b](https://github.com/Fabio286/antares/commit/08d5b1b3299adc5c53419f50892aee3b58e6fe72))
|
||||||
|
* **PostgreSQL:** trigger functions support ([75bbd5f](https://github.com/Fabio286/antares/commit/75bbd5f66e38a971482acff9dde636cf89858c8e))
|
||||||
|
* **PostgreSQL:** triggers creation ([7df0cf8](https://github.com/Fabio286/antares/commit/7df0cf83895ba784e30efa7814433cf329175364))
|
||||||
|
* **UI:** active tab animation ([978a7c5](https://github.com/Fabio286/antares/commit/978a7c5f5b314f2ba4067c86471755fdcc341b3f))
|
||||||
|
* **UI:** context option to copy cell or row value ([d868c77](https://github.com/Fabio286/antares/commit/d868c772b9e7c70e173d1f80ad8de51aa40991ef))
|
||||||
|
* **UI:** resizer border mouse hover animation ([a69bdeb](https://github.com/Fabio286/antares/commit/a69bdeb20db7d67613dace554ae37a0c5e112ec2))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **PostgreSQL:** error opening setting tab for some stored routines ([d96907c](https://github.com/Fabio286/antares/commit/d96907ca2de344114e3edae52843f6258468934b))
|
||||||
|
* **UI:** contextual sub-menu alignment when close to the lower edge of the window ([61a42d5](https://github.com/Fabio286/antares/commit/61a42d51f55ec3010c4ee67bad2f699ff14c9f7c))
|
||||||
|
* fields default not correctly set in table filler ([faa07a0](https://github.com/Fabio286/antares/commit/faa07a077c899af5c71124e0c20869876e57fd9a))
|
||||||
|
* rows loses internal id after export ([2d278aa](https://github.com/Fabio286/antares/commit/2d278aa14e0e5080725196538b7265ec00a7aebb))
|
||||||
|
* **UI:** editor blinking on first connection with a dark theme ([0110756](https://github.com/Fabio286/antares/commit/0110756204b79388535a74017b303e1ba41e8fd8))
|
||||||
|
* unhandled exception in connection test ([a975df3](https://github.com/Fabio286/antares/commit/a975df38dd471ab0ac8645a1021470a4600e6598))
|
||||||
|
* vulnerability in server error toast messages ([3aef7e9](https://github.com/Fabio286/antares/commit/3aef7e953ea82a9105d470cc62c68aacfc97f9d9))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* improved contextual menu appearance ([f74bca7](https://github.com/Fabio286/antares/commit/f74bca7bb4fc6802143446a44663fbf12319bf29))
|
||||||
|
* **UI:** increased application border-radius ([a0a025e](https://github.com/Fabio286/antares/commit/a0a025e4502141e95394ca9717b1ec65df2728c1))
|
||||||
|
|
||||||
|
### [0.1.13](https://github.com/Fabio286/antares/compare/v0.1.12...v0.1.13) (2021-06-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **PostgreSQL:** alter trigger support ([5c8ee66](https://github.com/Fabio286/antares/commit/5c8ee66f432585db7bd72103fa814558bf406bcc))
|
||||||
|
* **UI:** ability to manually insert page number in DATA tabs, closes [#65](https://github.com/Fabio286/antares/issues/65) ([e2ebb04](https://github.com/Fabio286/antares/commit/e2ebb04a9047d25187889644aa625fe675de808b))
|
||||||
|
* **UI:** option to change query editors font size, closes [#77](https://github.com/Fabio286/antares/issues/77) ([e579f37](https://github.com/Fabio286/antares/commit/e579f374381b329422b646b8f7ab5acf298db981))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** pool connections not released after MySQL errors, causing endless load animation ([4133fc4](https://github.com/Fabio286/antares/commit/4133fc452fc2e961eb587590e010f4968675db7e))
|
||||||
|
* **UI:** various fixes in displaying content with small window size ([8a20bef](https://github.com/Fabio286/antares/commit/8a20befd0941cb2e6d5e29552606454bd871b092))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* remove comments from queries before execution ([690a454](https://github.com/Fabio286/antares/commit/690a4541f96eaf831dea07b777a421729173b654))
|
||||||
|
|
||||||
|
### [0.1.12](https://github.com/Fabio286/antares/compare/v0.1.11...v0.1.12) (2021-06-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **PostgreSQL:** trigger rename and delete ([cce5adb](https://github.com/Fabio286/antares/commit/cce5adbac75170186956211b54f4db5be25aba07))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* internal exceptions ([8742fa1](https://github.com/Fabio286/antares/commit/8742fa10f08c92a98325df50be9a7df8e36b0f2c))
|
||||||
|
* page offset not reset when selected table changes ([5562e73](https://github.com/Fabio286/antares/commit/5562e73e75c659051a49d61041d1eee9d66ff6e2))
|
||||||
|
* **MySQL:** view's data tab doesn't work with some views, closes [#71](https://github.com/Fabio286/antares/issues/71) ([9ca059d](https://github.com/Fabio286/antares/commit/9ca059d979161c0132f64db372b4ed7ecc844b2d))
|
||||||
|
* **UI:** unable to browse view's result pages ([8a7cc2a](https://github.com/Fabio286/antares/commit/8a7cc2a14fa289c2f3d9e9eb1a5435ffbbaab006))
|
||||||
|
|
||||||
|
### [0.1.11](https://github.com/Fabio286/antares/compare/v0.1.10...v0.1.11) (2021-06-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **UI:** prevent canc key to trigger delete modal when editing a row ([5e9c88a](https://github.com/Fabio286/antares/commit/5e9c88a7fd656dd69b92ba4384ca81f4e8cdd422))
|
||||||
|
* table row loses internal id after cell update ([6622756](https://github.com/Fabio286/antares/commit/66227569f4c032298e20d44c253d30109ffde0b2))
|
||||||
|
* **MySQL:** missing schema altering tables in some conditions ([faa799c](https://github.com/Fabio286/antares/commit/faa799c8ea2329e7ca1b54c9414a060bc8a2a840))
|
||||||
|
* empty offset in cell update queries ([acc1eeb](https://github.com/Fabio286/antares/commit/acc1eeb0948db35f2c9a4d85d70fb654b41b15e5))
|
||||||
|
|
||||||
|
### [0.1.10](https://github.com/Fabio286/antares/compare/v0.1.9...v0.1.10) (2021-05-29)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* key shortcuts to change DATA tab page ([f61d7ee](https://github.com/Fabio286/antares/commit/f61d7eeaf42406eb88b3285ac4606794f56e88d3))
|
||||||
|
* option to set DATA tab page size ([e71c756](https://github.com/Fabio286/antares/commit/e71c7568c09bf8ad184753d3d0f14046f1e386d0))
|
||||||
|
* prev/next buttons to browse the results pages of data tab ([79f033e](https://github.com/Fabio286/antares/commit/79f033e5245e0e744c35540d25a30b1eadc4d603))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** wrong schema in view data tab select, closes [#71](https://github.com/Fabio286/antares/issues/71) ([310cfaa](https://github.com/Fabio286/antares/commit/310cfaa3c2324b1159a46be566b6af2555dc9732))
|
||||||
|
* wrong detection of field default expressions in some cases ([04bdd08](https://github.com/Fabio286/antares/commit/04bdd085a56240273133586d23196e52341d5f27))
|
||||||
|
|
||||||
|
### [0.1.9](https://github.com/Fabio286/antares/compare/v0.1.8...v0.1.9) (2021-05-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** can't access tables having UNIQUE KEY, closes [#69](https://github.com/Fabio286/antares/issues/69) ([f1636f1](https://github.com/Fabio286/antares/commit/f1636f1528b5765423535f84027fdc853c58ce80))
|
||||||
|
|
||||||
|
### [0.1.8](https://github.com/Fabio286/antares/compare/v0.1.7...v0.1.8) (2021-05-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* internal id not removed before row update ([0f10c9e](https://github.com/Fabio286/antares/commit/0f10c9e824b679cfebd8a31085236da3d38a7953))
|
||||||
|
* unable to add new ENUM fields ([1e37f2a](https://github.com/Fabio286/antares/commit/1e37f2a96f660a36f2739fea57ca298059e9c31a))
|
||||||
|
* unable to delete table rows ([0d61371](https://github.com/Fabio286/antares/commit/0d6137195da9e0e9afe68f6d3ebcd50e913a888e))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* improved the way how field default value are handled ([7a766f0](https://github.com/Fabio286/antares/commit/7a766f04e668868e8844aac1d7d445bc2da21558))
|
||||||
|
|
||||||
|
### [0.1.7](https://github.com/Fabio286/antares/compare/v0.1.6...v0.1.7) (2021-05-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* row loses internal id after cell update ([854472c](https://github.com/Fabio286/antares/commit/854472c7a3d8442b1eede12f978cad5aa684094e))
|
||||||
|
* **MySQL:** connection loses schema in some conditions ([6b0b8b1](https://github.com/Fabio286/antares/commit/6b0b8b19d7176ef8647d6f401a33315f8732fdf3))
|
||||||
|
* issue with ENUM and SET fields on table filler modal ([475397c](https://github.com/Fabio286/antares/commit/475397ca34c5b7d8925e2d97d32216d5b80d8211))
|
||||||
|
* issue with ENUM and SET length when creating a new field ([7a62131](https://github.com/Fabio286/antares/commit/7a62131cc707aa1c98bb12513de76229472b7c38))
|
||||||
|
* multiple row select on sorted tables not work properly ([c7663be](https://github.com/Fabio286/antares/commit/c7663be338ccb5ff31e241e824593531ad95bd32))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **core:** increased connection pool size to improve performance ([ae103e5](https://github.com/Fabio286/antares/commit/ae103e5477ad190c541d0f29e277c41de6947063))
|
||||||
|
* **MySQL:** improved connections pool handling ([434711a](https://github.com/Fabio286/antares/commit/434711a360bd7539d2ec3adec92e7ccc96fa828e))
|
||||||
|
|
||||||
|
### [0.1.6](https://github.com/Fabio286/antares/compare/v0.1.5...v0.1.6) (2021-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **MySQL:** ENUM and SET fields support, closes [#61](https://github.com/Fabio286/antares/issues/61) ([bebba64](https://github.com/Fabio286/antares/commit/bebba64d06532990405763284e27cb768dc050f7))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* better detection and handling of field default type ([3baf6fa](https://github.com/Fabio286/antares/commit/3baf6fa1736a405c95fb02d17c11514861ca9e04))
|
||||||
|
* no quotes around strings in field default custom value ([29e2d92](https://github.com/Fabio286/antares/commit/29e2d92b5bf66ff9e80bb1fee1274967c4418601))
|
||||||
|
* **UI:** data type figure twice on type select ([9dfe7cc](https://github.com/Fabio286/antares/commit/9dfe7cca2234ce6de512bb4c21205c2217e7f765))
|
||||||
|
* support to mDNS/zeroconf in snap build, closes [#58](https://github.com/Fabio286/antares/issues/58) ([35ef070](https://github.com/Fabio286/antares/commit/35ef070725f5779923bf3ad428b44accddf22dbe))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* italian translation updated ([5dc0b0b](https://github.com/Fabio286/antares/commit/5dc0b0bea40f9d5a5944af2f1c5dc6ff3e60c396))
|
||||||
|
|
||||||
|
### [0.1.5](https://github.com/Fabio286/antares/compare/v0.1.4...v0.1.5) (2021-04-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** multiple queries non properly split in some cases ([5208ec1](https://github.com/Fabio286/antares/commit/5208ec171b44da0e6bfa93f15bfedd03ef2aa868))
|
||||||
|
* % character not properly escaped, closes [#60](https://github.com/Fabio286/antares/issues/60) ([ecfb732](https://github.com/Fabio286/antares/commit/ecfb732c265a5485e131e75f3d20ff07d9409753))
|
||||||
|
* semicolon inside strings breaks queries, closes [#59](https://github.com/Fabio286/antares/issues/59) ([1b09909](https://github.com/Fabio286/antares/commit/1b0990912627a3a4a4e8d62b4593f8a7aa3a7fe5))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **UI:** new application icon ([5822b3d](https://github.com/Fabio286/antares/commit/5822b3df432e0a2b305d0ff37a20dc466c3a3992))
|
||||||
|
|
||||||
### [0.1.4](https://github.com/Fabio286/antares/compare/v0.1.3...v0.1.4) (2021-04-22)
|
### [0.1.4](https://github.com/Fabio286/antares/compare/v0.1.3...v0.1.4) (2021-04-22)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
108
README.md
@@ -4,48 +4,61 @@
|
|||||||
|
|
||||||
# Antares SQL Client
|
# Antares SQL Client
|
||||||
|
|
||||||
   [](https://snapcraft.io/antares) [](https://snapcraft.io/antares) [](https://www.treedom.net/en/user/fabio-di-stasio/event/antares-for-the-planet)
|
 [](https://actions-badge.atrox.dev/fabio286/antares/goto)   [](https://snapcraft.io/antares) [](https://snapcraft.io/antares) [](https://twitter.com/AntaresSQL) [](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.
|
Our target is to support as many databases as possible, and all major operating systems, including the ARM versions.
|
||||||
|
|
||||||
**At the moment this application is in development state, many features will come in future updates**, and supports only MySQL/MariaDB and PostgreSQL (partially).
|
**At the moment this application is in development state, many features will come in future updates**, and supports only MySQL/MariaDB and PostgreSQL.
|
||||||
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.
|
At the moment, however, there are all the features necessary to have a pleasant database management experience, so give it a chance and send us your feedback, we would really appreciate it.
|
||||||
I'm actively working on it, hoping to provide cool features and fixes as soon as possible.
|
We are actively working on it, hoping to provide new cool features, improvements and fixes as soon as possible.
|
||||||
|
|
||||||
🔗 If you are curious to try Antares you can download and install the [latest release](https://github.com/Fabio286/antares/releases/latest).
|
🔗 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 [follow Antares SQL](https://twitter.com/AntaresSQL) on Twitter.
|
||||||
🌟 Don't forget to **leave a star** if you appreciate this project.
|
🌟 Don't forget to **leave a star** if you appreciate this project.
|
||||||
|
|
||||||
## Philosophy
|
## Current key features
|
||||||
|
|
||||||
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.
|
|
||||||
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
|
|
||||||
|
|
||||||
- [Translate Antares](https://github.com/Fabio286/antares/wiki/Translate-Antares)
|
|
||||||
|
|
||||||
## Current main features
|
|
||||||
|
|
||||||
- Multiple database connections at same time.
|
- Multiple database connections at same time.
|
||||||
- Database management (add/edit/delete).
|
- Database management (add/edit/delete).
|
||||||
- Full tables management, including indexes and foreign keys.
|
- Full tables management, including indexes and foreign keys.
|
||||||
- Views, triggers, stored routines, functions and schedulers management (add/edit/delete).
|
- Views, triggers, stored routines, functions and schedulers management (add/edit/delete).
|
||||||
- Fake table data filler.
|
- A modern and friendly tab system; keep open every kind of tab you need in your workspace.
|
||||||
- Run queries on multiple tabs.
|
- Fake table data filler to generate tons of data for test purpose.
|
||||||
- Query suggestions and auto complete.
|
- Query suggestions and auto complete.
|
||||||
|
- SSH tunnel support.
|
||||||
- Dark and light theme.
|
- Dark and light theme.
|
||||||
|
- Editor themes.
|
||||||
- Scratchpad.
|
- Scratchpad.
|
||||||
- Multi language.
|
|
||||||
- Secure password storage.
|
- Secure password storage.
|
||||||
- Auto updates.
|
|
||||||
|
## Philosophy
|
||||||
|
|
||||||
|
Why are we 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.
|
||||||
|
A modern application created with minimalism and semplicity in mind, with features in the right places, not hundreds of tiny buttons, nested tabs or submenu; productivity comes first.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Based on your operating system you can have one or more distribution formats to choose based on your preferences.
|
||||||
|
Since Antares SQL is a free software we haven't a budget to spend in annual licenses or certificates. This can result that on some platforms you need some additional passages to install this app.
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
On Linux you can simply download and run `.AppImage` distributions, install from Snap Store or from AUR.
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
On Windows you can choose between Microsoft Store and download `.exe` distribution. The latter lacks of a certificate, so to install you need to click on "More info" and then "Run anyway" on SmartScreen prompt.
|
||||||
|
|
||||||
|
### MacOS
|
||||||
|
|
||||||
|
On macOS you can run `.dmg` distribution following [this guide](https://support.apple.com/guide/mac-help/mh40616/mac) to install apps from unknown developers.
|
||||||
|
|
||||||
|
## Download
|
||||||
|
|
||||||
|
[](https://snapcraft.io/antares) [](https://aur.archlinux.org/packages/antares-sql/) [](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)**
|
||||||
|
|
||||||
## Coming soon
|
## Coming soon
|
||||||
|
|
||||||
@@ -53,32 +66,19 @@ This is a roadmap with major features will come in near future.
|
|||||||
|
|
||||||
- Support for other databases.
|
- Support for other databases.
|
||||||
- Database tools.
|
- Database tools.
|
||||||
- SSH tunnel support.
|
|
||||||
- Users management (add/edit/delete).
|
- Users management (add/edit/delete).
|
||||||
- UI/UX improvements.
|
- Query history and bookmarks.
|
||||||
- Query history.
|
|
||||||
- More context menu shortcuts.
|
- More context menu shortcuts.
|
||||||
- More keyboard shortcuts.
|
- More keyboard shortcuts.
|
||||||
- Query logs console.
|
|
||||||
- Import/export and migration.
|
- Import/export and migration.
|
||||||
|
- Apple Silicon distribution
|
||||||
## 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
|
||||||
- [x] PostgreSQL (partially, work in progress)
|
- [x] PostgreSQL
|
||||||
- [ ] SQLite
|
- [ ] SQLite
|
||||||
- [ ] MSSQL
|
- [ ] MSSQL
|
||||||
- [ ] OracleDB
|
- [ ] OracleDB
|
||||||
@@ -90,21 +90,25 @@ Depending on your distribution, you will need to run the following command:
|
|||||||
|
|
||||||
- [x] Windows
|
- [x] Windows
|
||||||
- [x] Linux
|
- [x] Linux
|
||||||
- [x] MacOS (not tested due lack of hardware)
|
- [x] MacOS
|
||||||
|
|
||||||
#### • ARM
|
#### • ARM
|
||||||
|
|
||||||
- [ ] Windows
|
- [ ] Windows
|
||||||
- [ ] Linux
|
- [x] Linux
|
||||||
- [ ] MacOS
|
- [ ] MacOS
|
||||||
|
|
||||||
|
## How to contribute
|
||||||
|
|
||||||
|
- 🌍 [Translate Antares](https://github.com/Fabio286/antares/wiki/Translate-Antares)
|
||||||
|
- 📖 [Contributors Guide](https://github.com/Fabio286/antares/wiki/Contributors-Guide)
|
||||||
|
- 🚧 [Project Board](https://github.com/users/Fabio286/projects/1)
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
**Italian Translation** (46%) / [Giuseppe Gigliotti](https://github.com/ReverbOD) [[#20](https://github.com/Fabio286/antares/pull/20)]
|
**Italian Translation** / [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** (needs updates) / [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** (needs updates) / [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)]
|
**French Translation** (needs updates) / [MrAnyx](https://github.com/MrAnyx) [[#44](https://github.com/Fabio286/antares/pull/44)]
|
||||||
|
**Portugues (Brasil)** / [Daniel Eduardo](https://github.com/daeleduardo) [[#54](https://github.com/Fabio286/antares/pull/54)]
|
||||||
## Reviews
|
**Deutsch (Deutschland)** / [Christian Ratz](https://github.com/digitalgopnik) [[#74](https://github.com/Fabio286/antares/pull/74)]
|
||||||
|
|
||||||
<a target="_blank" href="https://www.softx64.com/windows/antares-sql-client.html" title="Antares SQL Client review"><img src="https://www.softx64.com/softx64-review.png" alt="Antares SQL Client review" /></a>
|
|
||||||
|
|||||||
BIN
build/appx/LargeTile.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
build/appx/SmallTile.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 17 KiB |
BIN
build/icon.icns
BIN
build/icon.ico
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 50 KiB |
77
docs/aur-badge.svg
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Gravit.io --><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
style="isolation:isolate" viewBox="0 0 182 56" width="182px" height="56px">
|
||||||
|
<defs>
|
||||||
|
<clipPath id="_clipPath_tR1uglJ1Zei76xP861DY1TsjAiQWS9qF">
|
||||||
|
<rect width="182" height="56" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<g clip-path="url(#_clipPath_tR1uglJ1Zei76xP861DY1TsjAiQWS9qF)">
|
||||||
|
<path
|
||||||
|
d="M 2.5 0.5 L 180.5 0.5 C 181.604 0.5 182.5 1.396 182.5 2.5 L 182.5 54.5 C 182.5 55.604 181.604 56.5 180.5 56.5 L 2.5 56.5 C 1.396 56.5 0.5 55.604 0.5 54.5 L 0.5 2.5 C 0.5 1.396 1.396 0.5 2.5 0.5 Z"
|
||||||
|
style="stroke:none;fill:#252525;stroke-miterlimit:10;" />
|
||||||
|
<path
|
||||||
|
d="M 2.5 0.5 L 180.5 0.5 C 181.604 0.5 182.5 1.396 182.5 2.5 L 182.5 54.5 C 182.5 55.604 181.604 56.5 180.5 56.5 L 2.5 56.5 C 1.396 56.5 0.5 55.604 0.5 54.5 L 0.5 2.5 C 0.5 1.396 1.396 0.5 2.5 0.5 Z"
|
||||||
|
style="fill:none;stroke:#CDCDCD;stroke-width:1;stroke-miterlimit:2;" />
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
d=" M 60.898 13.777 C 58.555 13.774 56.61 14.254 55.858 14.516 L 55.083 18.697 C 55.081 18.712 58.937 17.669 60.635 17.73 C 63.447 17.831 63.706 18.805 63.656 20.119 C 63.704 20.196 62.931 18.931 60.498 18.889 C 57.43 18.836 53.098 19.976 53.104 24.608 C 53.022 29.818 56.997 31.351 59.704 31.379 C 62.138 31.335 63.279 30.458 63.904 29.988 C 64.726 29.129 65.665 28.265 66.561 27.229 C 65.714 28.77 64.978 29.835 64.213 30.651 L 64.213 31.339 L 67.913 30.716 L 67.938 20.66 C 67.901 19.237 68.754 13.791 60.898 13.777 Z M 60.367 22.533 C 61.9 22.554 63.659 23.31 63.662 25.129 C 63.669 26.784 61.589 27.674 60.235 27.66 C 58.881 27.646 57.085 26.596 57.077 24.982 C 57.103 23.54 58.771 22.496 60.367 22.533 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(255,255,255)" />
|
||||||
|
<path
|
||||||
|
d=" M 70.378 14.707 L 70.352 31.36 L 74.662 30.529 L 74.67 21.087 C 74.671 19.681 76.679 18.039 79.198 18.065 C 79.733 17.097 80.738 14.625 80.983 14.062 C 75.354 14.049 75.282 15.68 74.303 16.483 C 74.293 14.952 74.3 14.033 74.3 14.033 L 70.378 14.707 L 70.378 14.707 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(255,255,255)" />
|
||||||
|
<path
|
||||||
|
d=" M 94.632 16.893 C 94.591 16.873 92.385 14.312 87.949 14.292 C 83.795 14.223 79.135 15.834 79.061 22.8 C 79.097 28.925 83.537 31.318 87.973 31.365 C 92.72 31.414 94.609 28.396 94.722 28.322 C 94.156 27.83 92.034 25.728 92.034 25.728 C 92.034 25.728 90.709 27.615 88.138 27.639 C 85.566 27.664 83.331 25.651 83.299 22.844 C 83.266 20.036 85.354 18.515 88.157 18.392 C 90.584 18.392 91.984 19.959 91.984 19.959 L 94.632 16.893 L 94.632 16.893 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(255,255,255)" />
|
||||||
|
<path
|
||||||
|
d=" M 100.065 8.879 L 95.996 9.835 L 96.026 31.526 L 100.034 30.802 L 100.08 20.595 C 100.089 19.524 101.628 17.88 104.2 17.933 C 106.658 17.958 107.207 19.571 107.201 19.775 L 107.272 31.592 L 111.224 30.894 L 111.239 18.363 C 111.265 17.157 108.598 14.61 104.311 14.592 C 102.273 14.596 101.145 15.057 100.571 15.397 C 99.588 16.156 98.466 16.883 97.362 17.811 C 98.382 16.501 99.239 15.595 100.075 14.921 L 100.065 8.879 L 100.065 8.879 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(255,255,255)" />
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
d=" M 114.673 9.441 L 116.508 8.982 L 116.595 30.85 L 114.73 31.168 L 114.673 9.441 L 114.673 9.441 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||||
|
<path
|
||||||
|
d=" M 119.663 15.968 L 121.271 15.252 L 121.285 30.932 L 119.731 31.253 L 119.663 15.968 L 119.663 15.968 Z M 119.28 10.314 L 120.577 9.255 L 121.655 10.454 L 120.357 11.54 L 119.28 10.314 L 119.28 10.314 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||||
|
<path
|
||||||
|
d=" M 124.296 15.682 L 126.131 15.308 L 126.14 18.586 C 126.14 18.727 127.148 14.924 132.008 15.009 C 136.727 15.035 137.499 18.688 137.473 19.507 L 137.531 31.034 L 135.922 31.384 L 135.913 19.998 C 135.932 19.665 135.178 16.853 131.843 16.843 C 128.509 16.833 126.199 19.265 126.203 20.818 L 126.229 30.848 L 124.365 31.335 L 124.296 15.682 L 124.296 15.682 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||||
|
<path
|
||||||
|
d=" M 153.547 31.117 L 151.711 31.492 L 151.703 28.214 C 151.703 28.073 150.694 31.876 145.835 31.791 C 141.116 31.765 140.344 28.112 140.37 27.293 L 140.311 15.765 L 142.261 15.372 L 142.292 26.758 C 142.292 27.069 142.665 29.947 145.999 29.957 C 149.334 29.967 151.669 27.949 151.686 24.911 L 151.662 15.928 L 153.477 15.464 L 153.547 31.117 L 153.547 31.117 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||||
|
<path
|
||||||
|
d=" M 157.144 15.553 L 155.857 16.56 L 160.792 23.018 L 155.529 30.478 L 156.894 31.492 L 161.841 24.563 L 166.948 31.656 L 168.211 30.649 L 162.738 23.065 L 167.104 16.933 L 165.762 15.797 L 161.785 21.472 L 157.144 15.553 L 157.144 15.553 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
d=" M 33.112 3.879 C 30.965 9.143 29.67 12.587 27.279 17.695 C 28.745 19.248 30.544 21.058 33.466 23.101 C 30.325 21.809 28.182 20.511 26.581 19.164 C 23.521 25.549 18.728 34.643 9 52.121 C 16.645 47.707 22.572 44.986 28.095 43.948 C 27.858 42.928 27.723 41.824 27.733 40.673 L 27.742 40.428 C 27.863 35.53 30.411 31.763 33.43 32.019 C 36.448 32.274 38.794 36.455 38.673 41.353 C 38.65 42.275 38.546 43.162 38.364 43.984 C 43.828 45.053 49.691 47.767 57.233 52.121 C 55.746 49.383 54.419 46.915 53.151 44.565 C 51.154 43.017 49.072 41.003 44.823 38.822 C 47.743 39.581 49.834 40.456 51.464 41.435 C 38.575 17.439 37.532 14.251 33.112 3.879 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
d=" M 170.614 30.156 L 170.614 28.802 L 170.109 28.802 L 170.109 28.621 L 171.325 28.621 L 171.325 28.802 L 170.817 28.802 L 170.817 30.156 L 170.614 30.156 Z "
|
||||||
|
fill="rgb(23,147,209)" />
|
||||||
|
<path
|
||||||
|
d=" M 171.536 30.156 L 171.536 28.621 L 171.842 28.621 L 172.205 29.708 C 172.238 29.809 172.263 29.884 172.278 29.935 C 172.295 29.879 172.323 29.797 172.36 29.689 L 172.727 28.621 L 173 28.621 L 173 30.156 L 172.804 30.156 L 172.804 28.871 L 172.358 30.156 L 172.175 30.156 L 171.732 28.849 L 171.732 30.156 L 171.536 30.156 Z "
|
||||||
|
fill="rgb(23,147,209)" />
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
d=" M 57.471 47.815 L 57.471 46.493 L 56.977 46.493 L 56.977 46.316 L 58.166 46.316 L 58.166 46.493 L 57.67 46.493 L 57.67 47.815 L 57.471 47.815 Z "
|
||||||
|
fill="rgb(23,147,209)" />
|
||||||
|
<path
|
||||||
|
d=" M 58.372 47.815 L 58.372 46.316 L 58.671 46.316 L 59.026 47.377 C 59.059 47.476 59.083 47.55 59.098 47.599 C 59.115 47.545 59.141 47.465 59.177 47.359 L 59.536 46.316 L 59.803 46.316 L 59.803 47.815 L 59.612 47.815 L 59.612 46.56 L 59.176 47.815 L 58.997 47.815 L 58.564 46.539 L 58.564 47.815 L 58.372 47.815"
|
||||||
|
fill="rgb(23,147,209)" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g clip-path="url(#_clipPath_NvFIpNfWUS6M4fZAtfyVzggsKR3URDoi)"><text transform="matrix(1,0,0,1,87.023,43.899)"
|
||||||
|
style="font-family:'Open Sans';font-weight:700;font-size:11px;font-style:normal;fill:#ffffff;stroke:none;">user
|
||||||
|
repository</text></g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="_clipPath_NvFIpNfWUS6M4fZAtfyVzggsKR3URDoi">
|
||||||
|
<rect x="0" y="0" width="86" height="14.98" transform="matrix(1,0,0,1,87,32.142)" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.5 KiB |
BIN
docs/gh-logo-2.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
docs/gh-logo.png
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 304 KiB |
BIN
docs/logo.png
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 172 KiB |
112
package.json
@@ -1,20 +1,22 @@
|
|||||||
{
|
{
|
||||||
"name": "antares",
|
"name": "antares",
|
||||||
"productName": "Antares",
|
"productName": "Antares",
|
||||||
"version": "0.1.4",
|
"version": "0.3.2",
|
||||||
"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",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"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",
|
||||||
"build:appx": "npm run build -- --win appx",
|
"build:local": "npm run build && electron-builder",
|
||||||
|
"build:appx": "npm run build:local -- --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",
|
||||||
"lint": "eslint . --ext .js,.vue && stylelint \"./src/**/*.{css,scss,sass,vue}\"",
|
"lint": "eslint . --ext .js,.vue && stylelint \"./src/**/*.{css,scss,sass,vue}\"",
|
||||||
"lint:fix": "eslint . --ext .js,.vue --fix && stylelint \"./src/**/*.{css,scss,sass,vue}\" --fix"
|
"lint:fix": "eslint . --ext .js,.vue --fix && stylelint \"./src/**/*.{css,scss,sass,vue}\" --fix",
|
||||||
|
"postinstall": "electron-builder install-app-deps"
|
||||||
},
|
},
|
||||||
"author": "Fabio Di Stasio <fabio286@gmail.com>",
|
"author": "Fabio Di Stasio <fabio286@gmail.com>",
|
||||||
"build": {
|
"build": {
|
||||||
@@ -26,24 +28,28 @@
|
|||||||
"portable"
|
"portable"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dmg": {
|
"mac": {
|
||||||
"contents": [
|
"target": {
|
||||||
{
|
"target": "default",
|
||||||
"x": 130,
|
"arch": [
|
||||||
"y": 220
|
"x64"
|
||||||
},
|
|
||||||
{
|
|
||||||
"x": 410,
|
|
||||||
"y": 220,
|
|
||||||
"type": "link",
|
|
||||||
"path": "/Applications"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"linux": {
|
"linux": {
|
||||||
"target": [
|
"target": [
|
||||||
"deb",
|
{
|
||||||
"AppImage"
|
"target": "deb",
|
||||||
|
"arch": "x64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "AppImage",
|
||||||
|
"arch": [
|
||||||
|
"x64",
|
||||||
|
"armv7l",
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"category": "Development"
|
"category": "Development"
|
||||||
},
|
},
|
||||||
@@ -59,6 +65,20 @@
|
|||||||
"identityName": "62514FabioDiStasio.AntaresSQLClient",
|
"identityName": "62514FabioDiStasio.AntaresSQLClient",
|
||||||
"publisher": "CN=1A2729ED-865C-41D2-9038-39AE2A63AA52",
|
"publisher": "CN=1A2729ED-865C-41D2-9038-39AE2A63AA52",
|
||||||
"applicationId": "FabioDiStasio.AntaresSQLClient"
|
"applicationId": "FabioDiStasio.AntaresSQLClient"
|
||||||
|
},
|
||||||
|
"dmg": {
|
||||||
|
"contents": [
|
||||||
|
{
|
||||||
|
"x": 130,
|
||||||
|
"y": 220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 410,
|
||||||
|
"y": 220,
|
||||||
|
"type": "link",
|
||||||
|
"path": "/Applications"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electronWebpack": {
|
"electronWebpack": {
|
||||||
@@ -67,49 +87,49 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@electron/remote": "^1.2.1",
|
||||||
"@mdi/font": "^5.9.55",
|
"@mdi/font": "^5.9.55",
|
||||||
"ace-builds": "^1.4.12",
|
"ace-builds": "^1.4.12",
|
||||||
"electron-log": "^4.3.0",
|
"electron-log": "^4.4.1",
|
||||||
"electron-store": "^7.0.0",
|
"electron-store": "^8.0.0",
|
||||||
"electron-updater": "^4.3.5",
|
"electron-updater": "^4.3.9",
|
||||||
"faker": "^5.3.1",
|
"faker": "^5.5.3",
|
||||||
"marked": "^2.0.2",
|
"marked": "^2.1.1",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"mssql": "^6.2.3",
|
"mysql2": "^2.3.0",
|
||||||
"mysql2": "^2.2.5",
|
"pg": "^8.7.1",
|
||||||
"node-sql-parser": "^3.1.0",
|
"pgsql-ast-parser": "^7.2.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",
|
||||||
"sql-formatter": "^4.0.2",
|
"sql-formatter": "^4.0.2",
|
||||||
|
"ssh2-promise": "^0.2.0",
|
||||||
"v-mask": "^2.2.4",
|
"v-mask": "^2.2.4",
|
||||||
"vue-i18n": "^8.22.4",
|
"vue-i18n": "^8.24.4",
|
||||||
"vuedraggable": "^2.24.3",
|
"vuedraggable": "^2.24.3",
|
||||||
"vuex": "^3.6.0"
|
"vuex": "^3.6.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-eslint": "^10.1.0",
|
"@babel/eslint-parser": "^7.15.0",
|
||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"electron": "^11.4.3",
|
"electron": "^13.1.8",
|
||||||
"electron-builder": "^22.9.1",
|
"electron-builder": "^22.11.7",
|
||||||
"electron-devtools-installer": "^3.1.1",
|
"electron-devtools-installer": "^3.2.0",
|
||||||
"electron-webpack": "^2.8.2",
|
"electron-webpack": "^2.8.2",
|
||||||
"electron-webpack-vue": "^2.4.0",
|
"electron-webpack-vue": "^2.4.0",
|
||||||
"eslint": "^7.24.0",
|
"eslint": "^7.32.0",
|
||||||
"eslint-config-standard": "^16.0.2",
|
"eslint-config-standard": "^16.0.3",
|
||||||
"eslint-plugin-import": "^2.22.1",
|
"eslint-plugin-import": "^2.23.4",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^5.1.0",
|
"eslint-plugin-promise": "^5.1.0",
|
||||||
"eslint-plugin-vue": "^7.9.0",
|
"eslint-plugin-vue": "^7.15.1",
|
||||||
"node-sass": "^5.0.0",
|
"sass": "^1.37.5",
|
||||||
"sass-loader": "^10.1.1",
|
"sass-loader": "^10.2.0",
|
||||||
"standard-version": "^9.2.0",
|
"standard-version": "^9.3.1",
|
||||||
"stylelint": "^13.12.0",
|
"stylelint": "^13.13.1",
|
||||||
"stylelint-config-standard": "^21.0.0",
|
"stylelint-config-standard": "^22.0.0",
|
||||||
"stylelint-scss": "^3.19.0",
|
"stylelint-scss": "^3.20.1",
|
||||||
"vue": "^2.6.12",
|
"vue": "^2.6.14",
|
||||||
"vue-template-compiler": "^2.6.12",
|
"vue-template-compiler": "^2.6.14",
|
||||||
"webpack": "^4.46.0"
|
"webpack": "^4.46.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ summary: Open source SQL client made to be simple and complete.
|
|||||||
description: |
|
description: |
|
||||||
Antares is an SQL client that aims to become an useful and complete tool, especially for developers.
|
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.
|
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.
|
base: core20
|
||||||
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
|
grade: stable
|
||||||
confinement: strict
|
confinement: strict
|
||||||
@@ -14,6 +12,9 @@ confinement: strict
|
|||||||
architectures:
|
architectures:
|
||||||
- build-on: amd64
|
- build-on: amd64
|
||||||
compression: lzo
|
compression: lzo
|
||||||
|
layout:
|
||||||
|
/etc/nsswitch.conf:
|
||||||
|
bind-file: $SNAP/etc/nsswitch.conf
|
||||||
|
|
||||||
parts:
|
parts:
|
||||||
antares:
|
antares:
|
||||||
@@ -57,6 +58,7 @@ parts:
|
|||||||
- fcitx-frontend-gtk3
|
- fcitx-frontend-gtk3
|
||||||
- libappindicator3-1
|
- libappindicator3-1
|
||||||
- libasound2
|
- libasound2
|
||||||
|
- libcurl4
|
||||||
- libgconf-2-4
|
- libgconf-2-4
|
||||||
- libgtk-3-0
|
- libgtk-3-0
|
||||||
- libnotify4
|
- libnotify4
|
||||||
@@ -68,20 +70,83 @@ parts:
|
|||||||
- libsecret-1-0
|
- libsecret-1-0
|
||||||
- libxtst6
|
- libxtst6
|
||||||
- libxkbfile1
|
- libxkbfile1
|
||||||
|
- gcc-10-base
|
||||||
|
- libapparmor1
|
||||||
|
- libblkid1
|
||||||
|
- libbsd0
|
||||||
|
- libcom-err2
|
||||||
|
- libcrypt1
|
||||||
|
- libdb5.3
|
||||||
|
- libdbus-1-3
|
||||||
|
- libexpat1
|
||||||
|
- libffi7
|
||||||
|
- libgcc-s1
|
||||||
|
- libgcrypt20
|
||||||
|
- libglib2.0-0
|
||||||
|
- libgmp10
|
||||||
|
- libgnutls30
|
||||||
|
- libgpg-error0
|
||||||
|
- libgssapi-krb5-2
|
||||||
|
- libhogweed5
|
||||||
|
- libidn2-0
|
||||||
|
- libjson-c4
|
||||||
|
- libk5crypto3
|
||||||
|
- libkeyutils1
|
||||||
|
- libkrb5-3
|
||||||
|
- libkrb5support0
|
||||||
|
- liblz4-1
|
||||||
|
- liblzma5
|
||||||
|
- libmount1
|
||||||
|
- libnettle7
|
||||||
|
- libp11-kit0
|
||||||
|
- libpcre2-8-0
|
||||||
|
- libselinux1
|
||||||
|
- libsqlite3-0
|
||||||
|
- libssl1.1
|
||||||
|
- libstdc++6
|
||||||
|
- libsystemd0
|
||||||
|
- libtasn1-6
|
||||||
|
- libudev1
|
||||||
|
- libunistring2
|
||||||
|
- libuuid1
|
||||||
|
- libwrap0
|
||||||
|
- libzstd1
|
||||||
|
- zlib1g
|
||||||
|
- libx11-xcb1
|
||||||
|
- libdrm2
|
||||||
|
- libgbm1
|
||||||
|
- libxcb-dri3-0
|
||||||
cleanup:
|
cleanup:
|
||||||
after: [antares]
|
after: [antares]
|
||||||
plugin: nil
|
plugin: nil
|
||||||
build-snaps: [ gnome-3-28-1804 ]
|
build-snaps: [gnome-3-38-2004]
|
||||||
override-prime: |
|
override-prime: |
|
||||||
set -eux
|
set -eux
|
||||||
cd /snap/gnome-3-28-1804/current
|
cd /snap/gnome-3-38-2004/current
|
||||||
find . -type f,l -exec rm -f $SNAPCRAFT_PRIME/{} \;
|
find . -type f,l -exec rm -f $SNAPCRAFT_PRIME/{} \;
|
||||||
|
|
||||||
|
mdns-lookup:
|
||||||
|
# Make resolution of ".local" host names (Zero-Conf/mDNS/DNS-SD)
|
||||||
|
# working: Take the original nsswitch.conf file from the base
|
||||||
|
# Snap and add "mdns4_minimal [NOTFOUND=return]" to its "hosts:" line
|
||||||
|
# Also install corresponding mdns4_minimal plug-in
|
||||||
|
# See: https://forum.snapcraft.io/t/no-mdns-support-in-snaps-should-core-have-a-modified-nsswitch-conf/
|
||||||
|
plugin: nil
|
||||||
|
stage-packages:
|
||||||
|
- libnss-mdns
|
||||||
|
override-prime: |
|
||||||
|
set -eux
|
||||||
|
sed -Ee 's/^\s*hosts:(\s+)files/hosts:\1files mdns4_minimal \[NOTFOUND=return\]/' /snap/core20/current/etc/nsswitch.conf > $SNAPCRAFT_STAGE/etc/nsswitch.conf
|
||||||
|
snapcraftctl prime
|
||||||
|
prime:
|
||||||
|
- lib/$SNAPCRAFT_ARCH_TRIPLET/libnss_mdns4_minimal*
|
||||||
|
- etc/nsswitch.conf
|
||||||
|
|
||||||
apps:
|
apps:
|
||||||
antares:
|
antares:
|
||||||
command: opt/Antares/antares --no-sandbox
|
command: opt/Antares/antares --no-sandbox
|
||||||
desktop: usr/share/applications/antares.desktop
|
desktop: usr/share/applications/antares.desktop
|
||||||
extensions: [gnome-3-28]
|
extensions: [gnome-3-38]
|
||||||
environment:
|
environment:
|
||||||
# Fallback to XWayland if running in a Wayland session.
|
# Fallback to XWayland if running in a Wayland session.
|
||||||
DISABLE_WAYLAND: 1
|
DISABLE_WAYLAND: 1
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ module.exports = {
|
|||||||
database: false,
|
database: false,
|
||||||
collations: false,
|
collations: false,
|
||||||
engines: false,
|
engines: false,
|
||||||
|
connectionSchema: false,
|
||||||
// Tools
|
// Tools
|
||||||
processesList: false,
|
processesList: false,
|
||||||
usersManagement: false,
|
usersManagement: false,
|
||||||
@@ -62,8 +63,14 @@ module.exports = {
|
|||||||
functionSql: false,
|
functionSql: false,
|
||||||
functionContext: false,
|
functionContext: false,
|
||||||
functionLanguage: false,
|
functionLanguage: false,
|
||||||
triggerMiltipleEvents: false,
|
triggerSql: false,
|
||||||
|
triggerStatementInCreation: false,
|
||||||
|
triggerMultipleEvents: false,
|
||||||
|
triggerTableInName: false,
|
||||||
triggerUpdateColumns: false,
|
triggerUpdateColumns: false,
|
||||||
|
triggerOnlyRename: false,
|
||||||
|
triggerFunctionSql: false,
|
||||||
|
triggerFunctionlanguages: false,
|
||||||
parametersLength: false,
|
parametersLength: false,
|
||||||
languages: false
|
languages: false
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ module.exports = {
|
|||||||
defaultUser: 'root',
|
defaultUser: 'root',
|
||||||
defaultDatabase: null,
|
defaultDatabase: null,
|
||||||
// Core
|
// Core
|
||||||
|
connectionSchema: true,
|
||||||
collations: true,
|
collations: true,
|
||||||
engines: true,
|
engines: true,
|
||||||
// Tools
|
// Tools
|
||||||
@@ -51,6 +52,7 @@ module.exports = {
|
|||||||
procedureDataAccess: true,
|
procedureDataAccess: true,
|
||||||
procedureSql: 'BEGIN\r\n\r\nEND',
|
procedureSql: 'BEGIN\r\n\r\nEND',
|
||||||
procedureContext: true,
|
procedureContext: true,
|
||||||
|
triggerSql: 'BEGIN\r\n\r\nEND',
|
||||||
functionDeterministic: true,
|
functionDeterministic: true,
|
||||||
functionDataAccess: true,
|
functionDataAccess: true,
|
||||||
functionSql: 'BEGIN\r\n\r\nEND',
|
functionSql: 'BEGIN\r\n\r\nEND',
|
||||||
|
|||||||
@@ -13,30 +13,40 @@ module.exports = {
|
|||||||
// Structure
|
// Structure
|
||||||
tables: true,
|
tables: true,
|
||||||
views: true,
|
views: true,
|
||||||
triggers: false,
|
triggers: true,
|
||||||
|
triggerFunctions: true,
|
||||||
routines: true,
|
routines: true,
|
||||||
functions: true,
|
functions: true,
|
||||||
// Settings
|
// Settings
|
||||||
tableAdd: true,
|
tableAdd: true,
|
||||||
viewAdd: true,
|
viewAdd: true,
|
||||||
triggerAdd: false,
|
triggerAdd: true,
|
||||||
|
triggerFunctionAdd: true,
|
||||||
routineAdd: true,
|
routineAdd: true,
|
||||||
functionAdd: true,
|
functionAdd: true,
|
||||||
databaseEdit: false,
|
databaseEdit: false,
|
||||||
tableSettings: true,
|
tableSettings: true,
|
||||||
viewSettings: true,
|
viewSettings: true,
|
||||||
triggerSettings: false,
|
triggerSettings: true,
|
||||||
|
triggerFunctionSettings: true,
|
||||||
routineSettings: true,
|
routineSettings: true,
|
||||||
functionSettings: true,
|
functionSettings: true,
|
||||||
indexes: true,
|
indexes: true,
|
||||||
foreigns: true,
|
foreigns: true,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
tableArray: true,
|
tableArray: true,
|
||||||
procedureSql: '$BODY$\r\n\r\n$BODY$',
|
procedureSql: '$procedure$\r\n\r\n$procedure$',
|
||||||
procedureContext: true,
|
procedureContext: true,
|
||||||
procedureLanguage: true,
|
procedureLanguage: true,
|
||||||
functionSql: '$BODY$\r\n\r\n$BODY$',
|
functionSql: '$function$\r\n\r\n$function$',
|
||||||
|
triggerFunctionSql: '$function$\r\nBEGIN\r\n\r\nEND\r\n$function$',
|
||||||
|
triggerFunctionlanguages: ['plpgsql'],
|
||||||
functionContext: true,
|
functionContext: true,
|
||||||
functionLanguage: true,
|
functionLanguage: true,
|
||||||
|
triggerSql: 'EXECUTE PROCEDURE ',
|
||||||
|
triggerStatementInCreation: true,
|
||||||
|
triggerMultipleEvents: true,
|
||||||
|
triggerTableInName: true,
|
||||||
|
triggerOnlyRename: false,
|
||||||
languages: ['sql', 'plpgsql', 'c', 'internal']
|
languages: ['sql', 'plpgsql', 'c', 'internal']
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -277,13 +277,6 @@ module.exports = [
|
|||||||
{
|
{
|
||||||
group: 'other',
|
group: 'other',
|
||||||
types: [
|
types: [
|
||||||
{
|
|
||||||
name: 'UNKNOWN',
|
|
||||||
length: false,
|
|
||||||
collation: false,
|
|
||||||
unsigned: false,
|
|
||||||
zerofill: false
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'ENUM',
|
name: 'ENUM',
|
||||||
length: true,
|
length: true,
|
||||||
@@ -299,5 +292,17 @@ module.exports = [
|
|||||||
zerofill: false
|
zerofill: false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'unknown',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'UNKNOWN',
|
||||||
|
length: false,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const regex = new RegExp(pattern);
|
|||||||
function sqlEscaper (string) {
|
function sqlEscaper (string) {
|
||||||
return string.replace(regex, char => {
|
return string.replace(regex, char => {
|
||||||
const m = ['\\0', '\\x08', '\\x09', '\\x1a', '\\n', '\\r', '\'', '\"', '\\', '\\\\', '%'];
|
const m = ['\\0', '\\x08', '\\x09', '\\x1a', '\\n', '\\r', '\'', '\"', '\\', '\\\\', '%'];
|
||||||
const r = ['\\\\0', '\\\\b', '\\\\t', '\\\\z', '\\\\n', '\\\\r', '\\\'', '\\\"', '\\\\', '\\\\\\\\', '\\%'];
|
const r = ['\\\\0', '\\\\b', '\\\\t', '\\\\z', '\\\\n', '\\\\r', '\\\'', '\\\"', '\\\\', '\\\\\\\\', '\%'];
|
||||||
return r[m.indexOf(char)] || char;
|
return r[m.indexOf(char)] || char;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { app, BrowserWindow, nativeImage } from 'electron';
|
import { app, BrowserWindow, /* session, */ nativeImage, Menu } from 'electron';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { format as formatUrl } from 'url';
|
|
||||||
import Store from 'electron-store';
|
import Store from 'electron-store';
|
||||||
|
|
||||||
import ipcHandlers from './ipc-handlers';
|
import ipcHandlers from './ipc-handlers';
|
||||||
@@ -29,6 +28,7 @@ async function createMainWindow () {
|
|||||||
icon: nativeImage.createFromDataURL(icon.default),
|
icon: nativeImage.createFromDataURL(icon.default),
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false,
|
||||||
'web-security': false,
|
'web-security': false,
|
||||||
enableRemoteModule: true,
|
enableRemoteModule: true,
|
||||||
spellcheck: false
|
spellcheck: false
|
||||||
@@ -37,26 +37,22 @@ async function createMainWindow () {
|
|||||||
backgroundColor: '#1d1d1d'
|
backgroundColor: '#1d1d1d'
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isDevelopment) {
|
try {
|
||||||
|
if (isDevelopment) { //
|
||||||
await window.loadURL(`http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`);
|
await window.loadURL(`http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`);
|
||||||
|
|
||||||
const { default: installExtension, VUEJS_DEVTOOLS } = require('electron-devtools-installer');
|
// const { default: installExtension, VUEJS3_DEVTOOLS } = require('electron-devtools-installer');
|
||||||
window.webContents.openDevTools();
|
|
||||||
|
|
||||||
installExtension(VUEJS_DEVTOOLS)
|
// const oldDevToolsID = session.defaultSession.getAllExtensions().find(ext => ext.name === 'Vue.js devtools').id;
|
||||||
.then(name => {
|
// session.defaultSession.removeExtension(oldDevToolsID);
|
||||||
console.log(name, 'installed');
|
// const toolName = await installExtension(VUEJS3_DEVTOOLS);
|
||||||
})
|
// console.log(toolName, 'installed');
|
||||||
.catch(err => {
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
await window.loadURL(formatUrl({
|
await window.loadURL(new URL(`file:///${path.join(__dirname, 'index.html')}`).href);
|
||||||
pathname: path.join(__dirname, 'index.html'),
|
}
|
||||||
protocol: 'file',
|
catch (err) {
|
||||||
slashes: true
|
console.log(err);
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.on('closed', () => {
|
window.on('closed', () => {
|
||||||
@@ -76,6 +72,8 @@ async function createMainWindow () {
|
|||||||
if (!gotTheLock)
|
if (!gotTheLock)
|
||||||
app.quit();
|
app.quit();
|
||||||
else {
|
else {
|
||||||
|
require('@electron/remote/main').initialize();
|
||||||
|
|
||||||
// Initialize ipcHandlers
|
// Initialize ipcHandlers
|
||||||
ipcHandlers();
|
ipcHandlers();
|
||||||
|
|
||||||
@@ -86,14 +84,20 @@ else {
|
|||||||
app.quit();
|
app.quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('activate', () => {
|
app.on('activate', async () => {
|
||||||
// on macOS it is common to re-create a window even after all windows have been closed
|
// on macOS it is common to re-create a window even after all windows have been closed
|
||||||
if (mainWindow === null)
|
if (mainWindow === null) {
|
||||||
mainWindow = createMainWindow();
|
mainWindow = await createMainWindow();
|
||||||
|
if (isDevelopment)
|
||||||
|
mainWindow.webContents.openDevTools();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// create main BrowserWindow when electron is ready
|
// create main BrowserWindow when electron is ready
|
||||||
app.on('ready', async () => {
|
app.on('ready', async () => {
|
||||||
mainWindow = createMainWindow();
|
mainWindow = await createMainWindow();
|
||||||
|
Menu.setApplicationMenu(null);
|
||||||
|
if (isDevelopment)
|
||||||
|
mainWindow.webContents.openDevTools();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,14 +24,23 @@ export default connections => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const connection = ClientsFactory.getConnection({
|
if (conn.ssh) {
|
||||||
|
params.ssh = {
|
||||||
|
host: conn.sshHost,
|
||||||
|
username: conn.sshUser,
|
||||||
|
password: conn.sshPass,
|
||||||
|
port: conn.sshPort ? conn.sshPort : 22,
|
||||||
|
identity: conn.sshKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const connection = await ClientsFactory.getConnection({
|
||||||
client: conn.client,
|
client: conn.client,
|
||||||
params
|
params
|
||||||
});
|
});
|
||||||
|
|
||||||
await connection.connect();
|
await connection.connect();
|
||||||
|
|
||||||
try {
|
|
||||||
await connection.select('1+1').run();
|
await connection.select('1+1').run();
|
||||||
connection.destroy();
|
connection.destroy();
|
||||||
|
|
||||||
@@ -58,6 +67,9 @@ export default connections => {
|
|||||||
if (conn.database)
|
if (conn.database)
|
||||||
params.database = conn.database;
|
params.database = conn.database;
|
||||||
|
|
||||||
|
if (conn.schema)
|
||||||
|
params.schema = conn.schema;
|
||||||
|
|
||||||
if (conn.ssl) {
|
if (conn.ssl) {
|
||||||
params.ssl = {
|
params.ssl = {
|
||||||
key: conn.key ? fs.readFileSync(conn.key) : null,
|
key: conn.key ? fs.readFileSync(conn.key) : null,
|
||||||
@@ -67,11 +79,21 @@ export default connections => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (conn.ssh) {
|
||||||
|
params.ssh = {
|
||||||
|
host: conn.sshHost,
|
||||||
|
username: conn.sshUser,
|
||||||
|
password: conn.sshPass,
|
||||||
|
port: conn.sshPort ? conn.sshPort : 22,
|
||||||
|
identity: conn.sshKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const connection = ClientsFactory.getConnection({
|
const connection = ClientsFactory.getConnection({
|
||||||
client: conn.client,
|
client: conn.client,
|
||||||
params,
|
params,
|
||||||
poolSize: 1
|
poolSize: 5
|
||||||
});
|
});
|
||||||
|
|
||||||
await connection.connect();
|
await connection.connect();
|
||||||
|
|||||||
@@ -31,6 +31,16 @@ export default (connections) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('alter-trigger-function', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].alterTriggerFunction(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.handle('create-function', async (event, params) => {
|
ipcMain.handle('create-function', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
await connections[params.uid].createFunction(params);
|
await connections[params.uid].createFunction(params);
|
||||||
@@ -40,4 +50,14 @@ export default (connections) => {
|
|||||||
return { status: 'error', response: err.toString() };
|
return { status: 'error', response: err.toString() };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('create-trigger-function', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].createTriggerFunction(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -128,7 +128,12 @@ export default connections => {
|
|||||||
if (!query) return;
|
if (!query) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await connections[uid].raw(query, { nest: true, details: true });
|
const result = await connections[uid].raw(query, {
|
||||||
|
nest: true,
|
||||||
|
details: true,
|
||||||
|
schema,
|
||||||
|
comments: false
|
||||||
|
});
|
||||||
|
|
||||||
return { status: 'success', response: result };
|
return { status: 'success', response: result };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,18 +16,20 @@ export default (connections) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('get-table-data', async (event, { uid, schema, table, sortParams }) => {
|
ipcMain.handle('get-table-data', async (event, { uid, schema, table, limit, page, sortParams }) => {
|
||||||
try {
|
try {
|
||||||
|
const offset = (page - 1) * limit;
|
||||||
const query = connections[uid]
|
const query = connections[uid]
|
||||||
.select('*')
|
.select('*')
|
||||||
.schema(schema)
|
.schema(schema)
|
||||||
.from(table)
|
.from(table)
|
||||||
.limit(1000);
|
.limit(limit)
|
||||||
|
.offset(offset);
|
||||||
|
|
||||||
if (sortParams && sortParams.field && sortParams.dir)
|
if (sortParams && sortParams.field && sortParams.dir)
|
||||||
query.orderBy({ [sortParams.field]: sortParams.dir.toUpperCase() });
|
query.orderBy({ [sortParams.field]: sortParams.dir.toUpperCase() });
|
||||||
|
|
||||||
const result = await query.run({ details: true });
|
const result = await query.run({ details: true, schema });
|
||||||
|
|
||||||
return { status: 'success', response: result };
|
return { status: 'success', response: result };
|
||||||
}
|
}
|
||||||
@@ -36,6 +38,16 @@ export default (connections) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('get-table-count', async (event, params) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[params.uid].getTableApproximateCount(params);
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.handle('get-table-indexes', async (event, params) => {
|
ipcMain.handle('get-table-indexes', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
const result = await connections[params.uid].getTableIndexes(params);
|
const result = await connections[params.uid].getTableIndexes(params);
|
||||||
@@ -59,6 +71,8 @@ export default (connections) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('update-table-cell', async (event, params) => {
|
ipcMain.handle('update-table-cell', async (event, params) => {
|
||||||
|
delete params.row._id;
|
||||||
|
|
||||||
try { // TODO: move to client classes
|
try { // TODO: move to client classes
|
||||||
let escapedParam;
|
let escapedParam;
|
||||||
let reload = false;
|
let reload = false;
|
||||||
@@ -420,6 +434,16 @@ export default (connections) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('duplicate-table', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].duplicateTable(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.handle('truncate-table', async (event, params) => {
|
ipcMain.handle('truncate-table', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
await connections[params.uid].truncateTable(params);
|
await connections[params.uid].truncateTable(params);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export class AntaresCore {
|
|||||||
groupBy: [],
|
groupBy: [],
|
||||||
orderBy: [],
|
orderBy: [],
|
||||||
limit: [],
|
limit: [],
|
||||||
|
offset: [],
|
||||||
join: [],
|
join: [],
|
||||||
update: [],
|
update: [],
|
||||||
insert: [],
|
insert: [],
|
||||||
@@ -109,6 +110,11 @@ export class AntaresCore {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offset (...args) {
|
||||||
|
this._query.offset = args;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String | Array} args field = value
|
* @param {String | Array} args field = value
|
||||||
* @returns
|
* @returns
|
||||||
@@ -134,7 +140,7 @@ export class AntaresCore {
|
|||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
* @memberof AntaresCore
|
* @memberof AntaresCore
|
||||||
*/
|
*/
|
||||||
async run (args) {
|
run (args) {
|
||||||
const rawQuery = this.getSQL();
|
const rawQuery = this.getSQL();
|
||||||
this._resetQuery();
|
this._resetQuery();
|
||||||
return this.raw(rawQuery, args);
|
return this.raw(rawQuery, args);
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ export class ClientsFactory {
|
|||||||
* @param {String} args.params.host
|
* @param {String} args.params.host
|
||||||
* @param {Number} args.params.port
|
* @param {Number} args.params.port
|
||||||
* @param {String} args.params.password
|
* @param {String} args.params.password
|
||||||
|
* @param {String=} args.params.database
|
||||||
|
* @param {String=} args.params.schema
|
||||||
|
* @param {String} args.params.ssh.host
|
||||||
|
* @param {String} args.params.ssh.username
|
||||||
|
* @param {String} args.params.ssh.password
|
||||||
|
* @param {Number} args.params.ssh.port
|
||||||
* @param {Number=} args.poolSize
|
* @param {Number=} args.poolSize
|
||||||
* @returns Database Connection
|
* @returns Database Connection
|
||||||
* @memberof ClientsFactory
|
* @memberof ClientsFactory
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import mysql from 'mysql2';
|
import mysql from 'mysql2/promise';
|
||||||
import { AntaresCore } from '../AntaresCore';
|
import { AntaresCore } from '../AntaresCore';
|
||||||
import dataTypes from 'common/data-types/mysql';
|
import dataTypes from 'common/data-types/mysql';
|
||||||
|
import * as SSH2Promise from 'ssh2-promise';
|
||||||
|
|
||||||
export class MySQLClient extends AntaresCore {
|
export class MySQLClient extends AntaresCore {
|
||||||
constructor (args) {
|
constructor (args) {
|
||||||
@@ -102,11 +103,35 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async connect () {
|
async connect () {
|
||||||
|
delete this._params.application_name;
|
||||||
|
|
||||||
|
const dbConfig = {
|
||||||
|
host: this._params.host,
|
||||||
|
port: this._params.port,
|
||||||
|
user: this._params.user,
|
||||||
|
password: this._params.password,
|
||||||
|
ssl: null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this._params.schema?.length) dbConfig.database = this._params.schema;
|
||||||
|
|
||||||
|
if (this._params.ssl) dbConfig.ssl = { ...this._params.ssl };
|
||||||
|
|
||||||
|
if (this._params.ssh) {
|
||||||
|
this._ssh = new SSH2Promise({ ...this._params.ssh });
|
||||||
|
|
||||||
|
this._tunnel = await this._ssh.addTunnel({
|
||||||
|
remoteAddr: this._params.host,
|
||||||
|
remotePort: this._params.port
|
||||||
|
});
|
||||||
|
dbConfig.port = this._tunnel.localPort;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this._poolSize)
|
if (!this._poolSize)
|
||||||
this._connection = mysql.createConnection(this._params);
|
this._connection = await mysql.createConnection(dbConfig);
|
||||||
else {
|
else {
|
||||||
this._connection = mysql.createPool({
|
this._connection = mysql.createPool({
|
||||||
...this._params,
|
...dbConfig,
|
||||||
connectionLimit: this._poolSize,
|
connectionLimit: this._poolSize,
|
||||||
typeCast: (field, next) => {
|
typeCast: (field, next) => {
|
||||||
if (field.type === 'DATETIME')
|
if (field.type === 'DATETIME')
|
||||||
@@ -123,6 +148,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
*/
|
*/
|
||||||
destroy () {
|
destroy () {
|
||||||
this._connection.end();
|
this._connection.end();
|
||||||
|
if (this._ssh) this._ssh.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -143,6 +169,12 @@ export class MySQLClient extends AntaresCore {
|
|||||||
*/
|
*/
|
||||||
async getStructure (schemas) {
|
async getStructure (schemas) {
|
||||||
const { rows: databases } = await this.raw('SHOW DATABASES');
|
const { rows: databases } = await this.raw('SHOW DATABASES');
|
||||||
|
|
||||||
|
let filteredDatabases = databases;
|
||||||
|
|
||||||
|
if (this._params.schema)
|
||||||
|
filteredDatabases = filteredDatabases.filter(db => db.Database === this._params.schema);
|
||||||
|
|
||||||
const { rows: functions } = await this.raw('SHOW FUNCTION STATUS');
|
const { rows: functions } = await this.raw('SHOW FUNCTION STATUS');
|
||||||
const { rows: procedures } = await this.raw('SHOW PROCEDURE STATUS');
|
const { rows: procedures } = await this.raw('SHOW PROCEDURE STATUS');
|
||||||
const { rows: schedulers } = await this.raw('SELECT *, EVENT_SCHEMA AS `Db`, EVENT_NAME AS `Name` FROM information_schema.`EVENTS`');
|
const { rows: schedulers } = await this.raw('SELECT *, EVENT_SCHEMA AS `Db`, EVENT_NAME AS `Name` FROM information_schema.`EVENTS`');
|
||||||
@@ -150,7 +182,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
const tablesArr = [];
|
const tablesArr = [];
|
||||||
const triggersArr = [];
|
const triggersArr = [];
|
||||||
|
|
||||||
for (const db of databases) {
|
for (const db of filteredDatabases) {
|
||||||
if (!schemas.has(db.Database)) continue;
|
if (!schemas.has(db.Database)) continue;
|
||||||
|
|
||||||
let { rows: tables } = await this.raw(`SHOW TABLE STATUS FROM \`${db.Database}\``);
|
let { rows: tables } = await this.raw(`SHOW TABLE STATUS FROM \`${db.Database}\``);
|
||||||
@@ -172,7 +204,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return databases.map(db => {
|
return filteredDatabases.map(db => {
|
||||||
if (schemas.has(db.Database)) {
|
if (schemas.has(db.Database)) {
|
||||||
// TABLES
|
// TABLES
|
||||||
const remappedTables = tablesArr.filter(table => table.Db === db.Database).map(table => {
|
const remappedTables = tablesArr.filter(table => table.Db === db.Database).map(table => {
|
||||||
@@ -307,25 +339,82 @@ export class MySQLClient extends AntaresCore {
|
|||||||
.orderBy({ ORDINAL_POSITION: 'ASC' })
|
.orderBy({ ORDINAL_POSITION: 'ASC' })
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
|
const { rows: fields } = await this.raw(`SHOW CREATE TABLE \`${schema}\`.\`${table}\``);
|
||||||
|
|
||||||
|
const remappedFields = fields.map(row => {
|
||||||
|
if (!row['Create Table']) return false;
|
||||||
|
|
||||||
|
let n = 0;
|
||||||
|
return row['Create Table']
|
||||||
|
.split('')
|
||||||
|
.reduce((acc, curr) => {
|
||||||
|
if (curr === ')') n--;
|
||||||
|
if (n !== 0) acc += curr;
|
||||||
|
if (curr === '(') n++;
|
||||||
|
return acc;
|
||||||
|
}, '')
|
||||||
|
.replaceAll('\n', '')
|
||||||
|
.split(',')
|
||||||
|
.map(f => {
|
||||||
|
try {
|
||||||
|
const fieldArr = f.trim().split(' ');
|
||||||
|
const nameAndType = fieldArr.slice(0, 2);
|
||||||
|
if (nameAndType[0].charAt(0) !== '`') return false;
|
||||||
|
|
||||||
|
const details = fieldArr.slice(2).join(' ');
|
||||||
|
let defaultValue = null;
|
||||||
|
if (details.includes('DEFAULT'))
|
||||||
|
defaultValue = details.match(/(?<=DEFAULT ).*?$/gs)[0].split(' COMMENT')[0];
|
||||||
|
// const defaultValueArr = defaultValue.split('');
|
||||||
|
// if (defaultValueArr[0] === '\'') {
|
||||||
|
// defaultValueArr.shift();
|
||||||
|
// defaultValueArr.pop();
|
||||||
|
// defaultValue = defaultValueArr.join('');
|
||||||
|
// }
|
||||||
|
|
||||||
|
const typeAndLength = nameAndType[1].replace(')', '').split('(');
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: nameAndType[0].replaceAll('`', ''),
|
||||||
|
type: typeAndLength[0].toUpperCase(),
|
||||||
|
length: typeAndLength[1] ? typeAndLength[1] : null,
|
||||||
|
default: defaultValue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.reduce((acc, curr) => {
|
||||||
|
acc[curr.name] = curr;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
})[0];
|
||||||
|
|
||||||
return rows.map(field => {
|
return rows.map(field => {
|
||||||
let numLength = field.COLUMN_TYPE.match(/int\(([^)]+)\)/);
|
let numLength = field.COLUMN_TYPE.match(/int\(([^)]+)\)/);
|
||||||
numLength = numLength ? +numLength.pop() : null;
|
numLength = numLength ? +numLength.pop() : null;
|
||||||
|
const enumValues = /(enum|set)/.test(field.COLUMN_TYPE)
|
||||||
|
? field.COLUMN_TYPE.match(/\(([^)]+)\)/)[0].slice(1, -1)
|
||||||
|
: null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: field.COLUMN_NAME,
|
name: field.COLUMN_NAME,
|
||||||
key: field.COLUMN_KEY.toLowerCase(),
|
key: field.COLUMN_KEY.toLowerCase(),
|
||||||
type: field.DATA_TYPE.toUpperCase(),
|
type: remappedFields ? remappedFields[field.COLUMN_NAME].type : field.DATA_TYPE,
|
||||||
schema: field.TABLE_SCHEMA,
|
schema: field.TABLE_SCHEMA,
|
||||||
table: field.TABLE_NAME,
|
table: field.TABLE_NAME,
|
||||||
numPrecision: field.NUMERIC_PRECISION,
|
numPrecision: field.NUMERIC_PRECISION,
|
||||||
numLength,
|
numLength,
|
||||||
|
enumValues,
|
||||||
datePrecision: field.DATETIME_PRECISION,
|
datePrecision: field.DATETIME_PRECISION,
|
||||||
charLength: field.CHARACTER_MAXIMUM_LENGTH,
|
charLength: field.CHARACTER_MAXIMUM_LENGTH,
|
||||||
nullable: field.IS_NULLABLE.includes('YES'),
|
nullable: field.IS_NULLABLE.includes('YES'),
|
||||||
unsigned: field.COLUMN_TYPE.includes('unsigned'),
|
unsigned: field.COLUMN_TYPE.includes('unsigned'),
|
||||||
zerofill: field.COLUMN_TYPE.includes('zerofill'),
|
zerofill: field.COLUMN_TYPE.includes('zerofill'),
|
||||||
order: field.ORDINAL_POSITION,
|
order: field.ORDINAL_POSITION,
|
||||||
default: field.COLUMN_DEFAULT,
|
default: remappedFields ? remappedFields[field.COLUMN_NAME].default : field.COLUMN_DEFAULT,
|
||||||
charset: field.CHARACTER_SET_NAME,
|
charset: field.CHARACTER_SET_NAME,
|
||||||
collation: field.COLLATION_NAME,
|
collation: field.COLLATION_NAME,
|
||||||
autoIncrement: field.EXTRA.includes('auto_increment'),
|
autoIncrement: field.EXTRA.includes('auto_increment'),
|
||||||
@@ -335,6 +424,19 @@ export class MySQLClient extends AntaresCore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.schema
|
||||||
|
* @param {String} params.table
|
||||||
|
* @returns {Object} table row count
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async getTableApproximateCount ({ schema, table }) {
|
||||||
|
const { rows } = await this.raw(`SELECT table_rows "count" FROM information_schema.tables WHERE table_name = "${table}" AND table_schema = "${schema}"`);
|
||||||
|
|
||||||
|
return rows.length ? rows[0].count : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} params
|
* @param {Object} params
|
||||||
* @param {String} params.schema
|
* @param {String} params.schema
|
||||||
@@ -484,7 +586,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async dropView (params) {
|
async dropView (params) {
|
||||||
const sql = `DROP VIEW \`${params.view}\``;
|
const sql = `DROP VIEW \`${params.schema}\`.\`${params.view}\``;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,10 +598,10 @@ export class MySQLClient extends AntaresCore {
|
|||||||
*/
|
*/
|
||||||
async alterView (params) {
|
async alterView (params) {
|
||||||
const { view } = params;
|
const { view } = params;
|
||||||
let sql = `ALTER ALGORITHM = ${view.algorithm}${view.definer ? ` DEFINER=${view.definer}` : ''} SQL SECURITY ${view.security} VIEW \`${view.oldName}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}`;
|
let sql = `ALTER ALGORITHM = ${view.algorithm}${view.definer ? ` DEFINER=${view.definer}` : ''} SQL SECURITY ${view.security} VIEW \`${view.schema}\`.\`${view.oldName}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}`;
|
||||||
|
|
||||||
if (view.name !== view.oldName)
|
if (view.name !== view.oldName)
|
||||||
sql += `; RENAME TABLE \`${view.oldName}\` TO \`${view.name}\``;
|
sql += `; RENAME TABLE \`${view.schema}\`.\`${view.oldName}\` TO \`${view.schema}\`.\`${view.name}\``;
|
||||||
|
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
@@ -510,8 +612,8 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @returns {Array.<Object>} parameters
|
* @returns {Array.<Object>} parameters
|
||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async createView (view) {
|
async createView (params) {
|
||||||
const sql = `CREATE ALGORITHM = ${view.algorithm} ${view.definer ? `DEFINER=${view.definer} ` : ''}SQL SECURITY ${view.security} VIEW \`${view.name}\` AS ${view.sql} ${view.updateOption ? `WITH ${view.updateOption} CHECK OPTION` : ''}`;
|
const sql = `CREATE ALGORITHM = ${params.algorithm} ${params.definer ? `DEFINER=${params.definer} ` : ''}SQL SECURITY ${params.security} VIEW \`${params.schema}\`.\`${params.name}\` AS ${params.sql} ${params.updateOption ? `WITH ${params.updateOption} CHECK OPTION` : ''}`;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,8 +633,8 @@ export class MySQLClient extends AntaresCore {
|
|||||||
sql: row['SQL Original Statement'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
sql: row['SQL Original Statement'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
||||||
name: row.Trigger,
|
name: row.Trigger,
|
||||||
table: row['SQL Original Statement'].match(/(?<=ON `).*?(?=`)/gs)[0],
|
table: row['SQL Original Statement'].match(/(?<=ON `).*?(?=`)/gs)[0],
|
||||||
event1: row['SQL Original Statement'].match(/(BEFORE|AFTER)/gs)[0],
|
activation: row['SQL Original Statement'].match(/(BEFORE|AFTER)/gs)[0],
|
||||||
event2: row['SQL Original Statement'].match(/(INSERT|UPDATE|DELETE)/gs)[0]
|
event: row['SQL Original Statement'].match(/(INSERT|UPDATE|DELETE)/gs)[0]
|
||||||
};
|
};
|
||||||
})[0];
|
})[0];
|
||||||
}
|
}
|
||||||
@@ -544,7 +646,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async dropTrigger (params) {
|
async dropTrigger (params) {
|
||||||
const sql = `DROP TRIGGER \`${params.trigger}\``;
|
const sql = `DROP TRIGGER \`${params.schema}\`.\`${params.trigger}\``;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -561,8 +663,8 @@ export class MySQLClient extends AntaresCore {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.createTrigger(tempTrigger);
|
await this.createTrigger(tempTrigger);
|
||||||
await this.dropTrigger({ trigger: tempTrigger.name });
|
await this.dropTrigger({ schema: trigger.schema, trigger: tempTrigger.name });
|
||||||
await this.dropTrigger({ trigger: trigger.oldName });
|
await this.dropTrigger({ schema: trigger.schema, trigger: trigger.oldName });
|
||||||
await this.createTrigger(trigger);
|
await this.createTrigger(trigger);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
@@ -576,8 +678,8 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @returns {Array.<Object>} parameters
|
* @returns {Array.<Object>} parameters
|
||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async createTrigger (trigger) {
|
async createTrigger (params) {
|
||||||
const sql = `CREATE ${trigger.definer ? `DEFINER=${trigger.definer} ` : ''}TRIGGER \`${trigger.name}\` ${trigger.event1} ${trigger.event2} ON \`${trigger.table}\` FOR EACH ROW ${trigger.sql}`;
|
const sql = `CREATE ${params.definer ? `DEFINER=${params.definer} ` : ''}TRIGGER \`${params.schema}\`.\`${params.name}\` ${params.activation} ${params.event} ON \`${params.table}\` FOR EACH ROW ${params.sql}`;
|
||||||
return await this.raw(sql, { split: false });
|
return await this.raw(sql, { split: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -651,7 +753,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async dropRoutine (params) {
|
async dropRoutine (params) {
|
||||||
const sql = `DROP PROCEDURE \`${params.routine}\``;
|
const sql = `DROP PROCEDURE \`${params.schema}\`.\`${params.routine}\``;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,8 +770,8 @@ export class MySQLClient extends AntaresCore {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.createRoutine(tempProcedure);
|
await this.createRoutine(tempProcedure);
|
||||||
await this.dropRoutine({ routine: tempProcedure.name });
|
await this.dropRoutine({ schema: routine.schema, routine: tempProcedure.name });
|
||||||
await this.dropRoutine({ routine: routine.oldName });
|
await this.dropRoutine({ schema: routine.schema, routine: routine.oldName });
|
||||||
await this.createRoutine(routine);
|
await this.createRoutine(routine);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
@@ -683,21 +785,21 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @returns {Array.<Object>} parameters
|
* @returns {Array.<Object>} parameters
|
||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async createRoutine (routine) {
|
async createRoutine (params) {
|
||||||
const parameters = 'parameters' in routine
|
const parameters = 'parameters' in params
|
||||||
? routine.parameters.reduce((acc, curr) => {
|
? params.parameters.reduce((acc, curr) => {
|
||||||
acc.push(`${curr.context} \`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
|
acc.push(`${curr.context} \`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
|
||||||
return acc;
|
return acc;
|
||||||
}, []).join(',')
|
}, []).join(',')
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
const sql = `CREATE ${routine.definer ? `DEFINER=${routine.definer} ` : ''}PROCEDURE \`${routine.name}\`(${parameters})
|
const sql = `CREATE ${params.definer ? `DEFINER=${params.definer} ` : ''}PROCEDURE \`${params.schema}\`.\`${params.name}\`(${parameters})
|
||||||
LANGUAGE SQL
|
LANGUAGE SQL
|
||||||
${routine.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
|
${params.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
|
||||||
${routine.dataAccess}
|
${params.dataAccess}
|
||||||
SQL SECURITY ${routine.security}
|
SQL SECURITY ${params.security}
|
||||||
COMMENT '${routine.comment}'
|
COMMENT '${params.comment}'
|
||||||
${routine.sql}`;
|
${params.sql}`;
|
||||||
|
|
||||||
return await this.raw(sql, { split: false });
|
return await this.raw(sql, { split: false });
|
||||||
}
|
}
|
||||||
@@ -778,7 +880,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async dropFunction (params) {
|
async dropFunction (params) {
|
||||||
const sql = `DROP FUNCTION \`${params.func}\``;
|
const sql = `DROP FUNCTION \`${params.schema}\`.\`${params.func}\``;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -795,8 +897,8 @@ export class MySQLClient extends AntaresCore {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.createFunction(tempProcedure);
|
await this.createFunction(tempProcedure);
|
||||||
await this.dropFunction({ func: tempProcedure.name });
|
await this.dropFunction({ schema: func.schema, func: tempProcedure.name });
|
||||||
await this.dropFunction({ func: func.oldName });
|
await this.dropFunction({ schema: func.schema, func: func.oldName });
|
||||||
await this.createFunction(func);
|
await this.createFunction(func);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
@@ -810,20 +912,20 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @returns {Array.<Object>} parameters
|
* @returns {Array.<Object>} parameters
|
||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async createFunction (func) {
|
async createFunction (params) {
|
||||||
const parameters = func.parameters.reduce((acc, curr) => {
|
const parameters = params.parameters.reduce((acc, curr) => {
|
||||||
acc.push(`\`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
|
acc.push(`\`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
|
||||||
return acc;
|
return acc;
|
||||||
}, []).join(',');
|
}, []).join(',');
|
||||||
|
|
||||||
const body = func.returns ? func.sql : 'BEGIN\n RETURN 0;\nEND';
|
const body = params.returns ? params.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})` : ''}
|
const sql = `CREATE ${params.definer ? `DEFINER=${params.definer} ` : ''}FUNCTION \`${params.schema}\`.\`${params.name}\`(${parameters}) RETURNS ${params.returns || 'SMALLINT'}${params.returnsLength ? `(${params.returnsLength})` : ''}
|
||||||
LANGUAGE SQL
|
LANGUAGE SQL
|
||||||
${func.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
|
${params.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
|
||||||
${func.dataAccess}
|
${params.dataAccess}
|
||||||
SQL SECURITY ${func.security}
|
SQL SECURITY ${params.security}
|
||||||
COMMENT '${func.comment}'
|
COMMENT '${params.comment}'
|
||||||
${body}`;
|
${body}`;
|
||||||
|
|
||||||
return await this.raw(sql, { split: false });
|
return await this.raw(sql, { split: false });
|
||||||
@@ -870,7 +972,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async dropEvent (params) {
|
async dropEvent (params) {
|
||||||
const sql = `DROP EVENT \`${params.scheduler}\``;
|
const sql = `DROP EVENT \`${params.schema}\`.\`${params.scheduler}\``;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -886,13 +988,13 @@ export class MySQLClient extends AntaresCore {
|
|||||||
if (scheduler.execution === 'EVERY' && scheduler.every[0].includes('-'))
|
if (scheduler.execution === 'EVERY' && scheduler.every[0].includes('-'))
|
||||||
scheduler.every[0] = `'${scheduler.every[0]}'`;
|
scheduler.every[0] = `'${scheduler.every[0]}'`;
|
||||||
|
|
||||||
const sql = `ALTER ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${scheduler.oldName}\`
|
const sql = `ALTER ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${scheduler.schema}\`.\`${scheduler.oldName}\`
|
||||||
ON SCHEDULE
|
ON SCHEDULE
|
||||||
${scheduler.execution === 'EVERY'
|
${scheduler.execution === 'EVERY'
|
||||||
? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}`
|
? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}`
|
||||||
: `AT '${scheduler.at}'`}
|
: `AT '${scheduler.at}'`}
|
||||||
ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE
|
ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE
|
||||||
${scheduler.name !== scheduler.oldName ? `RENAME TO \`${scheduler.name}\`` : ''}
|
${scheduler.name !== scheduler.oldName ? `RENAME TO \`${scheduler.schema}\`.\`${scheduler.name}\`` : ''}
|
||||||
${scheduler.state}
|
${scheduler.state}
|
||||||
COMMENT '${scheduler.comment}'
|
COMMENT '${scheduler.comment}'
|
||||||
DO ${scheduler.sql}`;
|
DO ${scheduler.sql}`;
|
||||||
@@ -906,16 +1008,16 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @returns {Array.<Object>} parameters
|
* @returns {Array.<Object>} parameters
|
||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async createEvent (scheduler) {
|
async createEvent (params) {
|
||||||
const sql = `CREATE ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${scheduler.name}\`
|
const sql = `CREATE ${params.definer ? ` DEFINER=${params.definer}` : ''} EVENT \`${params.schema}\`.\`${params.name}\`
|
||||||
ON SCHEDULE
|
ON SCHEDULE
|
||||||
${scheduler.execution === 'EVERY'
|
${params.execution === 'EVERY'
|
||||||
? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}`
|
? `EVERY ${params.every.join(' ')}${params.starts ? ` STARTS '${params.starts}'` : ''}${params.ends ? ` ENDS '${params.ends}'` : ''}`
|
||||||
: `AT '${scheduler.at}'`}
|
: `AT '${params.at}'`}
|
||||||
ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE
|
ON COMPLETION${!params.preserve ? ' NOT' : ''} PRESERVE
|
||||||
${scheduler.state}
|
${params.state}
|
||||||
COMMENT '${scheduler.comment}'
|
COMMENT '${params.comment}'
|
||||||
DO ${scheduler.sql}`;
|
DO ${params.sql}`;
|
||||||
|
|
||||||
return await this.raw(sql, { split: false });
|
return await this.raw(sql, { split: false });
|
||||||
}
|
}
|
||||||
@@ -1037,14 +1139,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async createTable (params) {
|
async createTable (params) {
|
||||||
const {
|
const sql = `CREATE TABLE \`${params.schema}\`.\`${params.name}\` (\`${params.name}_ID\` INT NULL) COMMENT='${params.comment}', COLLATE='${params.collation}', ENGINE=${params.engine}`;
|
||||||
name,
|
|
||||||
collation,
|
|
||||||
comment,
|
|
||||||
engine
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
const sql = `CREATE TABLE \`${name}\` (\`${name}_ID\` INT NULL) COMMENT='${comment}', COLLATE='${collation}', ENGINE=${engine}`;
|
|
||||||
|
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
@@ -1058,6 +1153,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
async alterTable (params) {
|
async alterTable (params) {
|
||||||
const {
|
const {
|
||||||
table,
|
table,
|
||||||
|
schema,
|
||||||
additions,
|
additions,
|
||||||
deletions,
|
deletions,
|
||||||
changes,
|
changes,
|
||||||
@@ -1066,7 +1162,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
options
|
options
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
let sql = `ALTER TABLE \`${this._schema}\`.\`${table}\` `;
|
let sql = `ALTER TABLE \`${schema}\`.\`${table}\` `;
|
||||||
const alterColumns = [];
|
const alterColumns = [];
|
||||||
|
|
||||||
// OPTIONS
|
// OPTIONS
|
||||||
@@ -1078,7 +1174,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
// ADD FIELDS
|
// ADD FIELDS
|
||||||
additions.forEach(addition => {
|
additions.forEach(addition => {
|
||||||
const typeInfo = this._getTypeInfo(addition.type);
|
const typeInfo = this._getTypeInfo(addition.type);
|
||||||
const length = typeInfo.length ? addition.numLength || addition.charLength || addition.datePrecision : false;
|
const length = typeInfo.length ? addition.enumValues || addition.numLength || addition.charLength || addition.datePrecision : false;
|
||||||
|
|
||||||
alterColumns.push(`ADD COLUMN \`${addition.name}\`
|
alterColumns.push(`ADD COLUMN \`${addition.name}\`
|
||||||
${addition.type.toUpperCase()}${length ? `(${length})` : ''}
|
${addition.type.toUpperCase()}${length ? `(${length})` : ''}
|
||||||
@@ -1116,7 +1212,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
// CHANGE FIELDS
|
// CHANGE FIELDS
|
||||||
changes.forEach(change => {
|
changes.forEach(change => {
|
||||||
const typeInfo = this._getTypeInfo(change.type);
|
const typeInfo = this._getTypeInfo(change.type);
|
||||||
const length = typeInfo.length ? change.numLength || change.charLength || change.datePrecision : false;
|
const length = typeInfo.length ? change.enumValues || change.numLength || change.charLength || change.datePrecision : false;
|
||||||
|
|
||||||
alterColumns.push(`CHANGE COLUMN \`${change.orgName}\` \`${change.name}\`
|
alterColumns.push(`CHANGE COLUMN \`${change.orgName}\` \`${change.name}\`
|
||||||
${change.type.toUpperCase()}${length ? `(${length})` : ''}
|
${change.type.toUpperCase()}${length ? `(${length})` : ''}
|
||||||
@@ -1178,11 +1274,22 @@ export class MySQLClient extends AntaresCore {
|
|||||||
sql += alterColumns.join(', ');
|
sql += alterColumns.join(', ');
|
||||||
|
|
||||||
// RENAME
|
// RENAME
|
||||||
if (options.name) sql += `; RENAME TABLE \`${table}\` TO \`${options.name}\``;
|
if (options.name) sql += `; RENAME TABLE \`${schema}\`.\`${table}\` TO \`${schema}\`.\`${options.name}\``;
|
||||||
|
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DUPLICATE TABLE
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof MySQLClient
|
||||||
|
*/
|
||||||
|
async duplicateTable (params) {
|
||||||
|
const sql = `CREATE TABLE \`${params.schema}\`.\`${params.table}_copy\` LIKE \`${params.schema}\`.\`${params.table}\``;
|
||||||
|
return await this.raw(sql);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TRUNCATE TABLE
|
* TRUNCATE TABLE
|
||||||
*
|
*
|
||||||
@@ -1190,7 +1297,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async truncateTable (params) {
|
async truncateTable (params) {
|
||||||
const sql = `TRUNCATE TABLE \`${params.table}\``;
|
const sql = `TRUNCATE TABLE \`${params.schema}\`.\`${params.table}\``;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1201,7 +1308,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async dropTable (params) {
|
async dropTable (params) {
|
||||||
const sql = `DROP TABLE \`${params.table}\``;
|
const sql = `DROP TABLE \`${params.schema}\`.\`${params.table}\``;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1256,7 +1363,10 @@ export class MySQLClient extends AntaresCore {
|
|||||||
// LIMIT
|
// LIMIT
|
||||||
const limitRaw = this._query.limit.length ? `LIMIT ${this._query.limit.join(', ')} ` : '';
|
const limitRaw = this._query.limit.length ? `LIMIT ${this._query.limit.join(', ')} ` : '';
|
||||||
|
|
||||||
return `${selectRaw}${updateRaw ? 'UPDATE' : ''}${insertRaw ? 'INSERT ' : ''}${this._query.delete ? 'DELETE ' : ''}${fromRaw}${updateRaw}${whereRaw}${groupByRaw}${orderByRaw}${limitRaw}${insertRaw}`;
|
// OFFSET
|
||||||
|
const offsetRaw = this._query.offset.length ? `OFFSET ${this._query.offset.join(', ')} ` : '';
|
||||||
|
|
||||||
|
return `${selectRaw}${updateRaw ? 'UPDATE' : ''}${insertRaw ? 'INSERT ' : ''}${this._query.delete ? 'DELETE ' : ''}${fromRaw}${updateRaw}${whereRaw}${groupByRaw}${orderByRaw}${limitRaw}${offsetRaw}${insertRaw}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1269,18 +1379,32 @@ export class MySQLClient extends AntaresCore {
|
|||||||
* @memberof MySQLClient
|
* @memberof MySQLClient
|
||||||
*/
|
*/
|
||||||
async raw (sql, args) {
|
async raw (sql, args) {
|
||||||
|
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
nest: false,
|
nest: false,
|
||||||
details: false,
|
details: false,
|
||||||
split: true,
|
split: true,
|
||||||
|
comments: true,
|
||||||
...args
|
...args
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!args.comments)
|
||||||
|
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments
|
||||||
|
|
||||||
const nestTables = args.nest ? '.' : false;
|
const nestTables = args.nest ? '.' : false;
|
||||||
const resultsArr = [];
|
const resultsArr = [];
|
||||||
let paramsArr = [];
|
let paramsArr = [];
|
||||||
const queries = args.split ? sql.split(';') : [sql];
|
const queries = args.split
|
||||||
|
? sql.split(/((?:[^;'"]*(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*')[^;'"]*)+)|;/gm)
|
||||||
|
.filter(Boolean)
|
||||||
|
.map(q => q.trim())
|
||||||
|
: [sql];
|
||||||
|
const isPool = typeof this._connection.getConnection === 'function';
|
||||||
|
const connection = isPool ? await this._connection.getConnection() : this._connection;
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
|
if (args.schema)
|
||||||
|
await connection.query(`USE \`${args.schema}\``);
|
||||||
|
|
||||||
for (const query of queries) {
|
for (const query of queries) {
|
||||||
if (!query) continue;
|
if (!query) continue;
|
||||||
@@ -1289,13 +1413,10 @@ export class MySQLClient extends AntaresCore {
|
|||||||
let keysArr = [];
|
let keysArr = [];
|
||||||
|
|
||||||
const { rows, report, fields, keys, duration } = 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) => {
|
connection.query({ sql: query, nestTables }).then(async ([response, fields]) => {
|
||||||
timeStop = new Date();
|
timeStop = new Date();
|
||||||
const queryResult = response;
|
const queryResult = response;
|
||||||
|
|
||||||
if (err)
|
|
||||||
reject(err);
|
|
||||||
else {
|
|
||||||
let remappedFields = fields
|
let remappedFields = fields
|
||||||
? fields.map(field => {
|
? fields.map(field => {
|
||||||
if (!field || Array.isArray(field))
|
if (!field || Array.isArray(field))
|
||||||
@@ -1307,7 +1428,7 @@ 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.schema,
|
schema: args.schema || field.schema,
|
||||||
table: field.table,
|
table: field.table,
|
||||||
tableAlias: field.table,
|
tableAlias: field.table,
|
||||||
orgTable: field.orgTable,
|
orgTable: field.orgTable,
|
||||||
@@ -1337,11 +1458,12 @@ export class MySQLClient extends AntaresCore {
|
|||||||
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 (detailedField && field.orgTable === paramObj.table && field.schema === paramObj.schema)
|
if (detailedField && field.orgTable === paramObj.table && field.schema === paramObj.schema)
|
||||||
field = { ...detailedField, ...field };
|
field = { ...field, ...detailedField };
|
||||||
return field;
|
return field;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
|
if (isPool) connection.release();
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1350,6 +1472,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
keysArr = keysArr ? [...keysArr, ...response] : response;
|
keysArr = keysArr ? [...keysArr, ...response] : response;
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
|
if (isPool) connection.release();
|
||||||
reject(err);
|
reject(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1363,13 +1486,17 @@ export class MySQLClient extends AntaresCore {
|
|||||||
fields: remappedFields,
|
fields: remappedFields,
|
||||||
keys: keysArr
|
keys: keysArr
|
||||||
});
|
});
|
||||||
}
|
}).catch((err) => {
|
||||||
|
if (isPool) connection.release();
|
||||||
|
reject(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
resultsArr.push({ rows, report, fields, keys, duration });
|
resultsArr.push({ rows, report, fields, keys, duration });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isPool) connection.release();
|
||||||
|
|
||||||
return resultsArr.length === 1 ? resultsArr[0] : resultsArr;
|
return resultsArr.length === 1 ? resultsArr[0] : resultsArr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Pool, Client, types } from 'pg';
|
|||||||
import { parse } from 'pgsql-ast-parser';
|
import { parse } from 'pgsql-ast-parser';
|
||||||
import { AntaresCore } from '../AntaresCore';
|
import { AntaresCore } from '../AntaresCore';
|
||||||
import dataTypes from 'common/data-types/postgresql';
|
import dataTypes from 'common/data-types/postgresql';
|
||||||
|
import * as SSH2Promise from 'ssh2-promise';
|
||||||
|
|
||||||
function pgToString (value) {
|
function pgToString (value) {
|
||||||
return value.toString();
|
return value.toString();
|
||||||
@@ -51,13 +52,35 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
* @memberof PostgreSQLClient
|
* @memberof PostgreSQLClient
|
||||||
*/
|
*/
|
||||||
async connect () {
|
async connect () {
|
||||||
|
const dbConfig = {
|
||||||
|
host: this._params.host,
|
||||||
|
port: this._params.port,
|
||||||
|
user: this._params.user,
|
||||||
|
password: this._params.password,
|
||||||
|
ssl: null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this._params.database?.length) dbConfig.database = this._params.database;
|
||||||
|
|
||||||
|
if (this._params.ssl) dbConfig.ssl = { ...this._params.ssl };
|
||||||
|
|
||||||
|
if (this._params.ssh) {
|
||||||
|
this._ssh = new SSH2Promise({ ...this._params.ssh });
|
||||||
|
|
||||||
|
this._tunnel = await this._ssh.addTunnel({
|
||||||
|
remoteAddr: this._params.host,
|
||||||
|
remotePort: this._params.port
|
||||||
|
});
|
||||||
|
dbConfig.port = this._tunnel.localPort;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this._poolSize) {
|
if (!this._poolSize) {
|
||||||
const client = new Client(this._params);
|
const client = new Client(dbConfig);
|
||||||
await client.connect();
|
await client.connect();
|
||||||
this._connection = client;
|
this._connection = client;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const pool = new Pool({ ...this._params, max: this._poolSize });
|
const pool = new Pool({ ...dbConfig, max: this._poolSize });
|
||||||
this._connection = pool;
|
this._connection = pool;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,6 +90,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
*/
|
*/
|
||||||
destroy () {
|
destroy () {
|
||||||
this._connection.end();
|
this._connection.end();
|
||||||
|
if (this._ssh) this._ssh.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -215,6 +239,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
functions: [],
|
functions: [],
|
||||||
procedures: [],
|
procedures: [],
|
||||||
triggers: [],
|
triggers: [],
|
||||||
|
triggerFunctions: [],
|
||||||
schedulers: []
|
schedulers: []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -268,6 +293,19 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} params
|
||||||
|
* @param {String} params.schema
|
||||||
|
* @param {String} params.table
|
||||||
|
* @returns {Object} table row count
|
||||||
|
* @memberof PostgreSQLClient
|
||||||
|
*/
|
||||||
|
async getTableApproximateCount ({ schema, table }) {
|
||||||
|
const { rows } = await this.raw(`SELECT reltuples AS count FROM pg_class WHERE relname = '${table}'`);
|
||||||
|
|
||||||
|
return rows.length ? rows[0].count : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} params
|
* @param {Object} params
|
||||||
* @param {String} params.schema
|
* @param {String} params.schema
|
||||||
@@ -458,7 +496,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
* @memberof PostgreSQLClient
|
* @memberof PostgreSQLClient
|
||||||
*/
|
*/
|
||||||
async dropView (params) {
|
async dropView (params) {
|
||||||
const sql = `DROP VIEW ${this._schema}.${params.view}`;
|
const sql = `DROP VIEW ${params.schema}.${params.view}`;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -470,10 +508,10 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
*/
|
*/
|
||||||
async alterView (params) {
|
async alterView (params) {
|
||||||
const { view } = params;
|
const { view } = params;
|
||||||
let sql = `CREATE OR REPLACE VIEW ${this._schema}.${view.oldName} AS ${view.sql}`;
|
let sql = `CREATE OR REPLACE VIEW ${view.schema}.${view.oldName} AS ${view.sql}`;
|
||||||
|
|
||||||
if (view.name !== view.oldName)
|
if (view.name !== view.oldName)
|
||||||
sql += `; ALTER VIEW ${this._schema}.${view.oldName} RENAME TO ${view.name}`;
|
sql += `; ALTER VIEW ${view.schema}.${view.oldName} RENAME TO ${view.name}`;
|
||||||
|
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
@@ -484,8 +522,8 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
* @returns {Array.<Object>} parameters
|
* @returns {Array.<Object>} parameters
|
||||||
* @memberof PostgreSQLClient
|
* @memberof PostgreSQLClient
|
||||||
*/
|
*/
|
||||||
async createView (view) {
|
async createView (params) {
|
||||||
const sql = `CREATE VIEW ${this._schema}.${view.name} AS ${view.sql}`;
|
const sql = `CREATE VIEW ${params.schema}.${params.name} AS ${params.sql}`;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,17 +534,33 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
* @memberof PostgreSQLClient
|
* @memberof PostgreSQLClient
|
||||||
*/
|
*/
|
||||||
async getTriggerInformations ({ schema, trigger }) {
|
async getTriggerInformations ({ schema, trigger }) {
|
||||||
const sql = `SHOW CREATE TRIGGER \`${schema}\`.\`${trigger}\``;
|
const [table, triggerName] = trigger.split('.');
|
||||||
const results = await this.raw(sql);
|
|
||||||
|
const results = await this.raw(`
|
||||||
|
SELECT event_object_schema AS table_schema,
|
||||||
|
event_object_table AS table_name,
|
||||||
|
trigger_schema,
|
||||||
|
trigger_name,
|
||||||
|
string_agg(event_manipulation, ',') AS event,
|
||||||
|
action_timing AS activation,
|
||||||
|
action_condition AS condition,
|
||||||
|
action_statement AS definition
|
||||||
|
FROM information_schema.triggers
|
||||||
|
WHERE trigger_schema = '${schema}'
|
||||||
|
AND trigger_name = '${triggerName}'
|
||||||
|
AND event_object_table = '${table}'
|
||||||
|
GROUP BY 1,2,3,4,6,7,8
|
||||||
|
ORDER BY table_schema,
|
||||||
|
table_name
|
||||||
|
`);
|
||||||
|
|
||||||
return results.rows.map(row => {
|
return results.rows.map(row => {
|
||||||
return {
|
return {
|
||||||
definer: row['SQL Original Statement'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
sql: row.definition,
|
||||||
sql: row['SQL Original Statement'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
name: row.trigger_name,
|
||||||
name: row.Trigger,
|
table: row.table_name,
|
||||||
table: row['SQL Original Statement'].match(/(?<=ON `).*?(?=`)/gs)[0],
|
event: row.event.split(','),
|
||||||
event1: row['SQL Original Statement'].match(/(BEFORE|AFTER)/gs)[0],
|
activation: row.activation
|
||||||
event2: row['SQL Original Statement'].match(/(INSERT|UPDATE|DELETE)/gs)[0]
|
|
||||||
};
|
};
|
||||||
})[0];
|
})[0];
|
||||||
}
|
}
|
||||||
@@ -519,7 +573,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
*/
|
*/
|
||||||
async dropTrigger (params) {
|
async dropTrigger (params) {
|
||||||
const triggerParts = params.trigger.split('.');
|
const triggerParts = params.trigger.split('.');
|
||||||
const sql = `DROP TRIGGER ${triggerParts[1]} ON ${triggerParts[0]}`;
|
const sql = `DROP TRIGGER "${triggerParts[1]}" ON "${params.schema}"."${triggerParts[0]}"`;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,8 +590,8 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.createTrigger(tempTrigger);
|
await this.createTrigger(tempTrigger);
|
||||||
await this.dropTrigger({ trigger: tempTrigger.name });
|
await this.dropTrigger({ schema: trigger.schema, trigger: `${tempTrigger.table}.${tempTrigger.name}` });
|
||||||
await this.dropTrigger({ trigger: trigger.oldName });
|
await this.dropTrigger({ schema: trigger.schema, trigger: `${trigger.table}.${trigger.oldName}` });
|
||||||
await this.createTrigger(trigger);
|
await this.createTrigger(trigger);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
@@ -551,8 +605,9 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
* @returns {Array.<Object>} parameters
|
* @returns {Array.<Object>} parameters
|
||||||
* @memberof PostgreSQLClient
|
* @memberof PostgreSQLClient
|
||||||
*/
|
*/
|
||||||
async createTrigger (trigger) {
|
async createTrigger (params) {
|
||||||
const sql = `CREATE ${trigger.definer ? `DEFINER=${trigger.definer} ` : ''}TRIGGER \`${trigger.name}\` ${trigger.event1} ${trigger.event2} ON \`${trigger.table}\` FOR EACH ROW ${trigger.sql}`;
|
const eventsString = Array.isArray(params.event) ? params.event.join(' OR ') : params.event;
|
||||||
|
const sql = `CREATE TRIGGER "${params.name}" ${params.activation} ${eventsString} ON "${params.schema}"."${params.table}" FOR EACH ROW ${params.sql}`;
|
||||||
return await this.raw(sql, { split: false });
|
return await this.raw(sql, { split: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -595,6 +650,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
AND proc.routine_type = 'PROCEDURE'
|
AND proc.routine_type = 'PROCEDURE'
|
||||||
AND proc.routine_name = '${routine}'
|
AND proc.routine_name = '${routine}'
|
||||||
AND proc.specific_schema = '${schema}'
|
AND proc.specific_schema = '${schema}'
|
||||||
|
AND args.data_type != NULL
|
||||||
ORDER BY procedure_schema,
|
ORDER BY procedure_schema,
|
||||||
specific_name,
|
specific_name,
|
||||||
procedure_name,
|
procedure_name,
|
||||||
@@ -606,7 +662,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
const parameters = results.rows.map(row => {
|
const parameters = results.rows.map(row => {
|
||||||
return {
|
return {
|
||||||
name: row.parameter_name,
|
name: row.parameter_name,
|
||||||
type: row.data_type.toUpperCase(),
|
type: row.data_type ? row.data_type.toUpperCase() : '',
|
||||||
length: '',
|
length: '',
|
||||||
context: row.parameter_mode
|
context: row.parameter_mode
|
||||||
};
|
};
|
||||||
@@ -633,7 +689,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
* @memberof PostgreSQLClient
|
* @memberof PostgreSQLClient
|
||||||
*/
|
*/
|
||||||
async dropRoutine (params) {
|
async dropRoutine (params) {
|
||||||
const sql = `DROP PROCEDURE ${this._schema}.${params.routine}`;
|
const sql = `DROP PROCEDURE "${params.schema}"."${params.routine}"`;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -650,8 +706,8 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.createRoutine(tempProcedure);
|
await this.createRoutine(tempProcedure);
|
||||||
await this.dropRoutine({ routine: tempProcedure.name });
|
await this.dropRoutine({ schema: routine.schema, routine: tempProcedure.name });
|
||||||
await this.dropRoutine({ routine: routine.oldName });
|
await this.dropRoutine({ schema: routine.schema, routine: routine.oldName });
|
||||||
await this.createRoutine(routine);
|
await this.createRoutine(routine);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
@@ -673,10 +729,10 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
}, []).join(',')
|
}, []).join(',')
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
if (this._schema !== 'public')
|
if (routine.schema !== 'public')
|
||||||
await this.use(this._schema);
|
await this.use(routine.schema);
|
||||||
|
|
||||||
const sql = `CREATE PROCEDURE ${this._schema}.${routine.name}(${parameters})
|
const sql = `CREATE PROCEDURE "${routine.schema}"."${routine.name}"(${parameters})
|
||||||
LANGUAGE ${routine.language}
|
LANGUAGE ${routine.language}
|
||||||
SECURITY ${routine.security}
|
SECURITY ${routine.security}
|
||||||
AS ${routine.sql}`;
|
AS ${routine.sql}`;
|
||||||
@@ -762,7 +818,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
* @memberof PostgreSQLClient
|
* @memberof PostgreSQLClient
|
||||||
*/
|
*/
|
||||||
async dropFunction (params) {
|
async dropFunction (params) {
|
||||||
const sql = `DROP FUNCTION ${this._schema}.${params.func}`;
|
const sql = `DROP FUNCTION "${params.schema}"."${params.func}"`;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -779,8 +835,8 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.createFunction(tempProcedure);
|
await this.createFunction(tempProcedure);
|
||||||
await this.dropFunction({ func: tempProcedure.name });
|
await this.dropFunction({ schema: func.schema, func: tempProcedure.name });
|
||||||
await this.dropFunction({ func: func.oldName });
|
await this.dropFunction({ schema: func.schema, func: func.oldName });
|
||||||
await this.createFunction(func);
|
await this.createFunction(func);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
@@ -797,17 +853,17 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
async createFunction (func) {
|
async createFunction (func) {
|
||||||
const parameters = 'parameters' in func
|
const parameters = 'parameters' in func
|
||||||
? func.parameters.reduce((acc, curr) => {
|
? func.parameters.reduce((acc, curr) => {
|
||||||
acc.push(`${curr.context} ${curr.name} ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
|
acc.push(`${curr.context} ${curr.name || ''} ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
|
||||||
return acc;
|
return acc;
|
||||||
}, []).join(',')
|
}, []).join(',')
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
if (this._schema !== 'public')
|
if (func.schema !== 'public')
|
||||||
await this.use(this._schema);
|
await this.use(func.schema);
|
||||||
|
|
||||||
const body = func.returns ? func.sql : '$BODY$\n$BODY$';
|
const body = func.returns ? func.sql : '$function$\n$function$';
|
||||||
|
|
||||||
const sql = `CREATE FUNCTION ${this._schema}.${func.name}(${parameters})
|
const sql = `CREATE FUNCTION "${func.schema}"."${func.name}" (${parameters})
|
||||||
RETURNS ${func.returns || 'void'}
|
RETURNS ${func.returns || 'void'}
|
||||||
LANGUAGE ${func.language}
|
LANGUAGE ${func.language}
|
||||||
SECURITY ${func.security}
|
SECURITY ${func.security}
|
||||||
@@ -817,95 +873,46 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SHOW CREATE EVENT
|
* ALTER TRIGGER FUNCTION
|
||||||
*
|
|
||||||
* @returns {Array.<Object>} view informations
|
|
||||||
* @memberof PostgreSQLClient
|
|
||||||
*/
|
|
||||||
// async getEventInformations ({ schema, scheduler }) {
|
|
||||||
// const sql = `SHOW CREATE EVENT \`${schema}\`.\`${scheduler}\``;
|
|
||||||
// const results = await this.raw(sql);
|
|
||||||
|
|
||||||
// return results.rows.map(row => {
|
|
||||||
// const schedule = row['Create Event'];
|
|
||||||
// const execution = schedule.includes('EVERY') ? 'EVERY' : 'ONCE';
|
|
||||||
// const every = execution === 'EVERY' ? row['Create Event'].match(/(?<=EVERY )(\s*([^\s]+)){0,2}/gs)[0].replaceAll('\'', '').split(' ') : [];
|
|
||||||
// const starts = execution === 'EVERY' && schedule.includes('STARTS') ? schedule.match(/(?<=STARTS ').*?(?='\s)/gs)[0] : '';
|
|
||||||
// const ends = execution === 'EVERY' && schedule.includes('ENDS') ? schedule.match(/(?<=ENDS ').*?(?='\s)/gs)[0] : '';
|
|
||||||
// const at = execution === 'ONCE' && schedule.includes('AT') ? schedule.match(/(?<=AT ').*?(?='\s)/gs)[0] : '';
|
|
||||||
|
|
||||||
// return {
|
|
||||||
// definer: row['Create Event'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
|
||||||
// sql: row['Create Event'].match(/(?<=DO )(.*)/gs)[0],
|
|
||||||
// name: row.Event,
|
|
||||||
// comment: row['Create Event'].match(/(?<=COMMENT ').*?(?=')/gs) ? row['Create Event'].match(/(?<=COMMENT ').*?(?=')/gs)[0] : '',
|
|
||||||
// state: row['Create Event'].includes('ENABLE') ? 'ENABLE' : row['Create Event'].includes('DISABLE ON SLAVE') ? 'DISABLE ON SLAVE' : 'DISABLE',
|
|
||||||
// preserve: row['Create Event'].includes('ON COMPLETION PRESERVE'),
|
|
||||||
// execution,
|
|
||||||
// every,
|
|
||||||
// starts,
|
|
||||||
// ends,
|
|
||||||
// at
|
|
||||||
// };
|
|
||||||
// })[0];
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DROP EVENT
|
|
||||||
*
|
*
|
||||||
* @returns {Array.<Object>} parameters
|
* @returns {Array.<Object>} parameters
|
||||||
* @memberof PostgreSQLClient
|
* @memberof PostgreSQLClient
|
||||||
*/
|
*/
|
||||||
// async dropEvent (params) {
|
async alterTriggerFunction (params) {
|
||||||
// const sql = `DROP EVENT \`${params.scheduler}\``;
|
const { func } = params;
|
||||||
// return await this.raw(sql);
|
|
||||||
// }
|
if (func.schema !== 'public')
|
||||||
|
await this.use(func.schema);
|
||||||
|
|
||||||
|
const body = func.returns ? func.sql : '$function$\n$function$';
|
||||||
|
|
||||||
|
const sql = `CREATE OR REPLACE FUNCTION "${func.schema}"."${func.name}" ()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE ${func.language}
|
||||||
|
AS ${body}`;
|
||||||
|
|
||||||
|
return await this.raw(sql, { split: false });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ALTER EVENT
|
* CREATE TRIGGER FUNCTION
|
||||||
*
|
*
|
||||||
* @returns {Array.<Object>} parameters
|
* @returns {Array.<Object>} parameters
|
||||||
* @memberof PostgreSQLClient
|
* @memberof PostgreSQLClient
|
||||||
*/
|
*/
|
||||||
// async alterEvent (params) {
|
async createTriggerFunction (func) {
|
||||||
// const { scheduler } = params;
|
if (func.schema !== 'public')
|
||||||
|
await this.use(func.schema);
|
||||||
|
|
||||||
// if (scheduler.execution === 'EVERY' && scheduler.every[0].includes('-'))
|
const body = func.returns ? func.sql : '$function$\r\nBEGIN\r\n\r\nEND\r\n$function$';
|
||||||
// scheduler.every[0] = `'${scheduler.every[0]}'`;
|
|
||||||
|
|
||||||
// const sql = `ALTER ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${scheduler.oldName}\`
|
const sql = `CREATE FUNCTION "${func.schema}"."${func.name}" ()
|
||||||
// ON SCHEDULE
|
RETURNS TRIGGER
|
||||||
// ${scheduler.execution === 'EVERY'
|
LANGUAGE ${func.language}
|
||||||
// ? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}`
|
AS ${body}`;
|
||||||
// : `AT '${scheduler.at}'`}
|
|
||||||
// ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE
|
|
||||||
// ${scheduler.name !== scheduler.oldName ? `RENAME TO \`${scheduler.name}\`` : ''}
|
|
||||||
// ${scheduler.state}
|
|
||||||
// COMMENT '${scheduler.comment}'
|
|
||||||
// DO ${scheduler.sql}`;
|
|
||||||
|
|
||||||
// return await this.raw(sql, { split: false });
|
return await this.raw(sql, { split: false });
|
||||||
// }
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* CREATE EVENT
|
|
||||||
*
|
|
||||||
* @returns {Array.<Object>} parameters
|
|
||||||
* @memberof PostgreSQLClient
|
|
||||||
*/
|
|
||||||
// async createEvent (scheduler) {
|
|
||||||
// const sql = `CREATE ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${scheduler.name}\`
|
|
||||||
// ON SCHEDULE
|
|
||||||
// ${scheduler.execution === 'EVERY'
|
|
||||||
// ? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}`
|
|
||||||
// : `AT '${scheduler.at}'`}
|
|
||||||
// ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE
|
|
||||||
// ${scheduler.state}
|
|
||||||
// COMMENT '${scheduler.comment}'
|
|
||||||
// DO ${scheduler.sql}`;
|
|
||||||
|
|
||||||
// return await this.raw(sql, { split: false });
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SELECT * FROM pg_collation
|
* SELECT * FROM pg_collation
|
||||||
@@ -995,11 +1002,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
* @memberof PostgreSQLClient
|
* @memberof PostgreSQLClient
|
||||||
*/
|
*/
|
||||||
async createTable (params) {
|
async createTable (params) {
|
||||||
const {
|
const sql = `CREATE TABLE "${params.schema}"."${params.name}" (${params.name}_id INTEGER NULL); ALTER TABLE "${params.schema}"."${params.name}" DROP COLUMN ${params.name}_id`;
|
||||||
name
|
|
||||||
} = params;
|
|
||||||
|
|
||||||
const sql = `CREATE TABLE ${this._schema}.${name} (${name}_id INTEGER NULL); ALTER TABLE ${this._schema}.${name} DROP COLUMN ${name}_id`;
|
|
||||||
|
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
@@ -1013,6 +1016,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
async alterTable (params) {
|
async alterTable (params) {
|
||||||
const {
|
const {
|
||||||
table,
|
table,
|
||||||
|
schema,
|
||||||
additions,
|
additions,
|
||||||
deletions,
|
deletions,
|
||||||
changes,
|
changes,
|
||||||
@@ -1021,8 +1025,8 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
options
|
options
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
if (this._schema !== 'public')
|
if (schema !== 'public')
|
||||||
await this.use(this._schema);
|
await this.use(schema);
|
||||||
|
|
||||||
let sql = '';
|
let sql = '';
|
||||||
const alterColumns = [];
|
const alterColumns = [];
|
||||||
@@ -1063,7 +1067,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
else if (type === 'UNIQUE')
|
else if (type === 'UNIQUE')
|
||||||
alterColumns.push(`ADD CONSTRAINT ${addition.name} UNIQUE (${fields})`);
|
alterColumns.push(`ADD CONSTRAINT ${addition.name} UNIQUE (${fields})`);
|
||||||
else
|
else
|
||||||
manageIndexes.push(`CREATE INDEX ${addition.name} ON ${this._schema}.${table}(${fields})`);
|
manageIndexes.push(`CREATE INDEX ${addition.name} ON "${schema}"."${table}" (${fields})`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ADD FOREIGN KEYS
|
// ADD FOREIGN KEYS
|
||||||
@@ -1101,7 +1105,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (change.orgName !== change.name)
|
if (change.orgName !== change.name)
|
||||||
renameColumns.push(`ALTER TABLE "${this._schema}"."${table}" RENAME COLUMN "${change.orgName}" TO "${change.name}"`);
|
renameColumns.push(`ALTER TABLE "${schema}"."${table}" RENAME COLUMN "${change.orgName}" TO "${change.name}"`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// CHANGE INDEX
|
// CHANGE INDEX
|
||||||
@@ -1119,7 +1123,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
else if (type === 'UNIQUE')
|
else if (type === 'UNIQUE')
|
||||||
alterColumns.push(`ADD CONSTRAINT ${change.name} UNIQUE (${fields})`);
|
alterColumns.push(`ADD CONSTRAINT ${change.name} UNIQUE (${fields})`);
|
||||||
else
|
else
|
||||||
manageIndexes.push(`CREATE INDEX ${change.name} ON ${this._schema}.${table}(${fields})`);
|
manageIndexes.push(`CREATE INDEX ${change.name} ON "${schema}"."${table}" (${fields})`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// CHANGE FOREIGN KEYS
|
// CHANGE FOREIGN KEYS
|
||||||
@@ -1146,10 +1150,10 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
alterColumns.push(`DROP CONSTRAINT ${deletion.constraintName}`);
|
alterColumns.push(`DROP CONSTRAINT ${deletion.constraintName}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (alterColumns.length) sql += `ALTER TABLE "${this._schema}"."${table}" ${alterColumns.join(', ')}; `;
|
if (alterColumns.length) sql += `ALTER TABLE "${schema}"."${table}" ${alterColumns.join(', ')}; `;
|
||||||
if (createSequences.length) sql = `${createSequences.join(';')}; ${sql}`;
|
if (createSequences.length) sql = `${createSequences.join(';')}; ${sql}`;
|
||||||
if (manageIndexes.length) sql = `${manageIndexes.join(';')}; ${sql}`;
|
if (manageIndexes.length) sql = `${manageIndexes.join(';')}; ${sql}`;
|
||||||
if (options.name) sql += `ALTER TABLE "${this._schema}"."${table}" RENAME TO "${options.name}"; `;
|
if (options.name) sql += `ALTER TABLE "${schema}"."${table}" RENAME TO "${options.name}"; `;
|
||||||
|
|
||||||
// RENAME
|
// RENAME
|
||||||
if (renameColumns.length) sql = `${renameColumns.join(';')}; ${sql}`;
|
if (renameColumns.length) sql = `${renameColumns.join(';')}; ${sql}`;
|
||||||
@@ -1157,6 +1161,17 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DUPLICATE TABLE
|
||||||
|
*
|
||||||
|
* @returns {Array.<Object>} parameters
|
||||||
|
* @memberof PostgreSQLClient
|
||||||
|
*/
|
||||||
|
async duplicateTable (params) {
|
||||||
|
const sql = `CREATE TABLE ${params.schema}.${params.table}_copy (LIKE ${params.schema}.${params.table} INCLUDING ALL)`;
|
||||||
|
return await this.raw(sql);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TRUNCATE TABLE
|
* TRUNCATE TABLE
|
||||||
*
|
*
|
||||||
@@ -1164,7 +1179,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
* @memberof PostgreSQLClient
|
* @memberof PostgreSQLClient
|
||||||
*/
|
*/
|
||||||
async truncateTable (params) {
|
async truncateTable (params) {
|
||||||
const sql = `TRUNCATE TABLE ${this._schema}.${params.table}`;
|
const sql = `TRUNCATE TABLE ${params.schema}.${params.table}`;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1175,7 +1190,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
* @memberof PostgreSQLClient
|
* @memberof PostgreSQLClient
|
||||||
*/
|
*/
|
||||||
async dropTable (params) {
|
async dropTable (params) {
|
||||||
const sql = `DROP TABLE ${this._schema}.${params.table}`;
|
const sql = `DROP TABLE ${params.schema}.${params.table}`;
|
||||||
return await this.raw(sql);
|
return await this.raw(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1230,7 +1245,10 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
// LIMIT
|
// LIMIT
|
||||||
const limitRaw = selectArray.length && this._query.limit.length ? `LIMIT ${this._query.limit.join(', ')} ` : '';
|
const limitRaw = selectArray.length && this._query.limit.length ? `LIMIT ${this._query.limit.join(', ')} ` : '';
|
||||||
|
|
||||||
return `${selectRaw}${updateRaw ? 'UPDATE' : ''}${insertRaw ? 'INSERT ' : ''}${this._query.delete ? 'DELETE ' : ''}${fromRaw}${updateRaw}${whereRaw}${groupByRaw}${orderByRaw}${limitRaw}${insertRaw}`;
|
// OFFSET
|
||||||
|
const offsetRaw = selectArray.length && this._query.offset.length ? `OFFSET ${this._query.offset.join(', ')} ` : '';
|
||||||
|
|
||||||
|
return `${selectRaw}${updateRaw ? 'UPDATE' : ''}${insertRaw ? 'INSERT ' : ''}${this._query.delete ? 'DELETE ' : ''}${fromRaw}${updateRaw}${whereRaw}${groupByRaw}${orderByRaw}${limitRaw}${offsetRaw}${insertRaw}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1247,15 +1265,23 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
nest: false,
|
nest: false,
|
||||||
details: false,
|
details: false,
|
||||||
split: true,
|
split: true,
|
||||||
|
comments: true,
|
||||||
...args
|
...args
|
||||||
};
|
};
|
||||||
|
|
||||||
if (args.nest && this._schema !== 'public')
|
if (args.schema && args.schema !== 'public')
|
||||||
await this.use(this._schema);
|
await this.use(args.schema);
|
||||||
|
|
||||||
|
if (!args.comments)
|
||||||
|
sql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '');// Remove comments
|
||||||
|
|
||||||
const resultsArr = [];
|
const resultsArr = [];
|
||||||
let paramsArr = [];
|
let paramsArr = [];
|
||||||
const queries = args.split ? sql.split(';') : [sql];
|
const queries = args.split
|
||||||
|
? sql.split(/(?!\B'[^']*);(?![^']*'\B)/gm)
|
||||||
|
.filter(Boolean)
|
||||||
|
.map(q => q.trim())
|
||||||
|
: [sql];
|
||||||
|
|
||||||
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
|
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
|
||||||
|
|
||||||
|
|||||||
@@ -4,28 +4,28 @@
|
|||||||
<div id="window-content">
|
<div id="window-content">
|
||||||
<TheSettingBar />
|
<TheSettingBar />
|
||||||
<div id="main-content" class="container">
|
<div id="main-content" class="container">
|
||||||
<TheAppWelcome v-if="!connections.length" @new-conn="showNewConnModal" />
|
<div class="columns col-gapless">
|
||||||
<div v-else class="columns col-gapless">
|
|
||||||
<Workspace
|
<Workspace
|
||||||
v-for="connection in connections"
|
v-for="connection in connections"
|
||||||
:key="connection.uid"
|
:key="connection.uid"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
/>
|
/>
|
||||||
</div>
|
<WorkspaceAddConnectionPanel v-if="selectedWorkspace === 'NEW'" />
|
||||||
</div>
|
</div>
|
||||||
<TheFooter />
|
<TheFooter />
|
||||||
<TheNotificationsBoard />
|
<TheNotificationsBoard />
|
||||||
<ModalNewConnection v-if="isNewConnModal" />
|
|
||||||
<TheScratchpad v-if="isScratchpad" />
|
<TheScratchpad v-if="isScratchpad" />
|
||||||
<ModalSettings v-if="isSettingModal" />
|
<ModalSettings v-if="isSettingModal" />
|
||||||
<ModalDiscardChanges v-if="isUnsavedDiscardModal" />
|
<BaseTextEditor class="d-none" value="" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapActions, mapGetters } from 'vuex';
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
import { ipcRenderer, remote } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
|
import { Menu, getCurrentWindow } from '@electron/remote';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
@@ -34,21 +34,19 @@ export default {
|
|||||||
TheSettingBar: () => import(/* webpackChunkName: "TheSettingBar" */'@/components/TheSettingBar'),
|
TheSettingBar: () => import(/* webpackChunkName: "TheSettingBar" */'@/components/TheSettingBar'),
|
||||||
TheFooter: () => import(/* webpackChunkName: "TheFooter" */'@/components/TheFooter'),
|
TheFooter: () => import(/* webpackChunkName: "TheFooter" */'@/components/TheFooter'),
|
||||||
TheNotificationsBoard: () => import(/* webpackChunkName: "TheNotificationsBoard" */'@/components/TheNotificationsBoard'),
|
TheNotificationsBoard: () => import(/* webpackChunkName: "TheNotificationsBoard" */'@/components/TheNotificationsBoard'),
|
||||||
TheAppWelcome: () => import(/* webpackChunkName: "TheAppWelcome" */'@/components/TheAppWelcome'),
|
|
||||||
Workspace: () => import(/* webpackChunkName: "Workspace" */'@/components/Workspace'),
|
Workspace: () => import(/* webpackChunkName: "Workspace" */'@/components/Workspace'),
|
||||||
ModalNewConnection: () => import(/* webpackChunkName: "ModalNewConnection" */'@/components/ModalNewConnection'),
|
WorkspaceAddConnectionPanel: () => import(/* webpackChunkName: "WorkspaceAddConnectionPanel" */'@/components/WorkspaceAddConnectionPanel'),
|
||||||
ModalSettings: () => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings'),
|
ModalSettings: () => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings'),
|
||||||
TheScratchpad: () => import(/* webpackChunkName: "TheScratchpad" */'@/components/TheScratchpad'),
|
TheScratchpad: () => import(/* webpackChunkName: "TheScratchpad" */'@/components/TheScratchpad'),
|
||||||
ModalDiscardChanges: () => import(/* webpackChunkName: "ModalDiscardChanges" */'@/components/ModalDiscardChanges')
|
BaseTextEditor: () => import(/* webpackChunkName: "BaseTextEditor" */'@/components/BaseTextEditor')
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
isLoading: 'application/isLoading',
|
isLoading: 'application/isLoading',
|
||||||
isNewConnModal: 'application/isNewModal',
|
|
||||||
isEditModal: 'application/isEditModal',
|
|
||||||
isSettingModal: 'application/isSettingModal',
|
isSettingModal: 'application/isSettingModal',
|
||||||
isScratchpad: 'application/isScratchpad',
|
isScratchpad: 'application/isScratchpad',
|
||||||
connections: 'connections/getConnections',
|
connections: 'connections/getConnections',
|
||||||
@@ -60,8 +58,6 @@ export default {
|
|||||||
ipcRenderer.send('check-for-updates');
|
ipcRenderer.send('check-for-updates');
|
||||||
this.checkVersionUpdate();
|
this.checkVersionUpdate();
|
||||||
|
|
||||||
const Menu = remote.Menu;
|
|
||||||
|
|
||||||
const InputMenu = Menu.buildFromTemplate([
|
const InputMenu = Menu.buildFromTemplate([
|
||||||
{
|
{
|
||||||
label: this.$t('word.cut'),
|
label: this.$t('word.cut'),
|
||||||
@@ -92,7 +88,7 @@ export default {
|
|||||||
|
|
||||||
while (node) {
|
while (node) {
|
||||||
if (node.nodeName.match(/^(input|textarea)$/i) || node.isContentEditable) {
|
if (node.nodeName.match(/^(input|textarea)$/i) || node.isContentEditable) {
|
||||||
InputMenu.popup(remote.getCurrentWindow());
|
InputMenu.popup(getCurrentWindow());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
node = node.parentNode;
|
node = node.parentNode;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="context">
|
<div class="context" :class="{'bottom': isBottom}">
|
||||||
<a
|
<a
|
||||||
class="context-overlay"
|
class="context-overlay"
|
||||||
@click="close"
|
@click="close"
|
||||||
@@ -23,22 +23,30 @@ export default {
|
|||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
contextSize: null
|
contextSize: null,
|
||||||
|
isBottom: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
position () {
|
position () {
|
||||||
|
let topCord = 0;
|
||||||
|
let leftCord = 0;
|
||||||
|
|
||||||
|
if (this.contextEvent) {
|
||||||
const { clientY, clientX } = this.contextEvent;
|
const { clientY, clientX } = this.contextEvent;
|
||||||
let topCord = `${clientY + 5}px`;
|
topCord = `${clientY + 2}px`;
|
||||||
let leftCord = `${clientX + 5}px`;
|
leftCord = `${clientX + 5}px`;
|
||||||
|
|
||||||
if (this.contextSize) {
|
if (this.contextSize) {
|
||||||
if (clientY + this.contextSize.height + 5 >= window.innerHeight)
|
if (clientY + (this.contextSize.height < 200 ? 200 : this.contextSize.height) + 5 >= window.innerHeight) {
|
||||||
topCord = `${clientY - this.contextSize.height}px`;
|
topCord = `${clientY + 3 - this.contextSize.height}px`;
|
||||||
|
this.isBottom = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (clientX + this.contextSize.width + 5 >= window.innerWidth)
|
if (clientX + this.contextSize.width + 5 >= window.innerWidth)
|
||||||
leftCord = `${clientX - this.contextSize.width}px`;
|
leftCord = `${clientX - this.contextSize.width}px`;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
top: topCord,
|
top: topCord,
|
||||||
@@ -50,6 +58,7 @@ export default {
|
|||||||
window.addEventListener('keydown', this.onKey);
|
window.addEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
|
if (this.$refs.contextContent)
|
||||||
this.contextSize = this.$refs.contextContent.getBoundingClientRect();
|
this.contextSize = this.$refs.contextContent.getBoundingClientRect();
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
@@ -83,12 +92,21 @@ export default {
|
|||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
||||||
|
&:not(.bottom) .context-submenu {
|
||||||
|
top: -0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.bottom .context-submenu {
|
||||||
|
bottom: -0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.context-container {
|
.context-container {
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: #1d1d1d;
|
background: #1d1d1d;
|
||||||
border-radius: 0.1rem;
|
border-radius: $border-radius;
|
||||||
|
border: 1px solid $bg-color-light-dark;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -97,19 +115,24 @@ export default {
|
|||||||
.context-element {
|
.context-element {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin: 0.2rem;
|
||||||
padding: 0.1rem 0.3rem;
|
padding: 0.1rem 0.3rem;
|
||||||
|
border-radius: $border-radius;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
.context-submenu {
|
.context-submenu {
|
||||||
|
border-radius: $border-radius;
|
||||||
|
border: 1px solid $bg-color-light-dark;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 100%;
|
left: 100%;
|
||||||
top: 0;
|
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
|
background: #1d1d1d;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
editorTheme: 'settings/getEditorTheme',
|
editorTheme: 'settings/getEditorTheme',
|
||||||
|
editorFontSize: 'settings/getEditorFontSize',
|
||||||
autoComplete: 'settings/getAutoComplete',
|
autoComplete: 'settings/getAutoComplete',
|
||||||
lineWrap: 'settings/getLineWrap'
|
lineWrap: 'settings/getLineWrap'
|
||||||
})
|
})
|
||||||
@@ -47,6 +48,19 @@ export default {
|
|||||||
if (this.editor)
|
if (this.editor)
|
||||||
this.editor.setTheme(`ace/theme/${this.editorTheme}`);
|
this.editor.setTheme(`ace/theme/${this.editorTheme}`);
|
||||||
},
|
},
|
||||||
|
editorFontSize () {
|
||||||
|
const sizes = {
|
||||||
|
small: '12px',
|
||||||
|
medium: '14px',
|
||||||
|
large: '16px'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.setOptions({
|
||||||
|
fontSize: sizes[this.editorFontSize]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
autoComplete () {
|
autoComplete () {
|
||||||
if (this.editor) {
|
if (this.editor) {
|
||||||
this.editor.setOptions({
|
this.editor.setOptions({
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
class="toast mt-2"
|
class="toast mt-2"
|
||||||
:class="toastStatus.className"
|
:class="toastStatus.className"
|
||||||
>
|
>
|
||||||
<span class="p-vcentered text-left" v-html="`${toastStatus.iconTag} ${message}`" />
|
<span class="p-vcentered text-left"><i class="mdi mdi-24px mr-1" :class="toastStatus.iconName" /> {{ message }}</span>
|
||||||
<button class="btn btn-clear" @click="hideToast" />
|
<button class="btn btn-clear" @click="hideToast" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -30,27 +30,27 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
toastStatus () {
|
toastStatus () {
|
||||||
let className = '';
|
let className = '';
|
||||||
let iconTag = '';
|
let iconName = '';
|
||||||
switch (this.status) {
|
switch (this.status) {
|
||||||
case 'success':
|
case 'success':
|
||||||
className = 'toast-success';
|
className = 'toast-success';
|
||||||
iconTag = '<i class="mdi mdi-24px mdi-check mr-1"></i>';
|
iconName = 'mdi-check';
|
||||||
break;
|
break;
|
||||||
case 'error':
|
case 'error':
|
||||||
className = 'toast-error';
|
className = 'toast-error';
|
||||||
iconTag = '<i class="mdi mdi-24px mdi-alert-rhombus mr-1"></i>';
|
iconName = 'mdi-alert-rhombus';
|
||||||
break;
|
break;
|
||||||
case 'warning':
|
case 'warning':
|
||||||
className = 'toast-warning';
|
className = 'toast-warning';
|
||||||
iconTag = '<i class="mdi mdi-24px mdi-alert mr-1"></i>';
|
iconName = 'mdi-alert';
|
||||||
break;
|
break;
|
||||||
case 'primary':
|
case 'primary':
|
||||||
className = 'toast-primary';
|
className = 'toast-primary';
|
||||||
iconTag = '<i class="mdi mdi-24px mdi-information-outline mr-1"></i>';
|
iconName = 'mdi-information-outline';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { className, iconTag };
|
return { className, iconName };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.file-uploader {
|
.file-uploader {
|
||||||
border-radius: 0.1rem;
|
border-radius: $border-radius;
|
||||||
height: 1.8rem;
|
height: 1.8rem;
|
||||||
line-height: 1.2rem;
|
line-height: 1.2rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -66,6 +66,21 @@
|
|||||||
:type="inputProps().type"
|
:type="inputProps().type"
|
||||||
:disabled="!isChecked"
|
:disabled="!isChecked"
|
||||||
>
|
>
|
||||||
|
<select
|
||||||
|
v-else-if="enumArray"
|
||||||
|
v-model="selectedValue"
|
||||||
|
class="form-select"
|
||||||
|
:disabled="!isChecked"
|
||||||
|
@change="onChange"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="val in enumArray"
|
||||||
|
:key="val"
|
||||||
|
:value="val"
|
||||||
|
>
|
||||||
|
{{ val }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
<input
|
<input
|
||||||
v-else
|
v-else
|
||||||
ref="formInput"
|
ref="formInput"
|
||||||
@@ -121,7 +136,8 @@ export default {
|
|||||||
selectedMethod: '',
|
selectedMethod: '',
|
||||||
selectedValue: '',
|
selectedValue: '',
|
||||||
debounceTimeout: null,
|
debounceTimeout: null,
|
||||||
methodParams: {}
|
methodParams: {},
|
||||||
|
enumArray: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -150,8 +166,14 @@ export default {
|
|||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
fieldObj () {
|
fieldObj () {
|
||||||
if (this.fieldObj)
|
if (this.fieldObj) {
|
||||||
|
if (Array.isArray(this.fieldObj.value)) {
|
||||||
|
this.enumArray = this.fieldObj.value;
|
||||||
|
this.selectedValue = this.fieldObj.value[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
this.selectedValue = this.fieldObj.value;
|
this.selectedValue = this.fieldObj.value;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
selectedGroup () {
|
selectedGroup () {
|
||||||
if (this.fakerMethods.length)
|
if (this.fakerMethods.length)
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ export default {
|
|||||||
}),
|
}),
|
||||||
isValidDefault () {
|
isValidDefault () {
|
||||||
if (!this.foreignList.length) return true;
|
if (!this.foreignList.length) return true;
|
||||||
return this.value === null || this.foreignList.some(foreign => foreign.foreign_column.toString() === this.value.toString());
|
if (this.value === null) return false;
|
||||||
|
return this.foreignList.some(foreign => foreign.foreign_column.toString() === this.value.toString());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created () {
|
async created () {
|
||||||
@@ -65,7 +66,7 @@ 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') {
|
||||||
const textField = response.find(field => [...TEXT, ...LONG_TEXT].includes(field.type));
|
const textField = response.find(field => [...TEXT, ...LONG_TEXT].includes(field.type) && field.name !== this.keyUsage.refField);
|
||||||
foreignDesc = textField ? textField.name : false;
|
foreignDesc = textField ? textField.name : false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer text-light">
|
<div class="modal-footer">
|
||||||
<button class="btn btn-primary mr-2" @click.stop="sendCredentials">
|
<button class="btn btn-primary mr-2" @click.stop="sendCredentials">
|
||||||
{{ $t('word.send') }}
|
{{ $t('word.send') }}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
>
|
>
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-play mr-1" /> {{ $t('word.parameters') }}: {{ localRoutine.name }}
|
<i class="mdi mdi-24px mdi-play mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.parameters') }}: {{ localRoutine.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div slot="body">
|
<div slot="body">
|
||||||
@@ -19,10 +20,10 @@
|
|||||||
:key="parameter._id"
|
:key="parameter._id"
|
||||||
class="form-group"
|
class="form-group"
|
||||||
>
|
>
|
||||||
<div class="col-3">
|
<div class="col-4">
|
||||||
<label class="form-label">{{ parameter.name }}</label>
|
<label class="form-label">{{ parameter.name }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-8">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input
|
<input
|
||||||
:ref="i === 0 ? 'firstInput' : ''"
|
:ref="i === 0 ? 'firstInput' : ''"
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
:confirm-text="$t('word.discard')"
|
:confirm-text="$t('word.discard')"
|
||||||
:cancel-text="$t('word.stay')"
|
:cancel-text="$t('word.stay')"
|
||||||
@confirm="discardUnsavedChanges"
|
@confirm="$emit('confirm')"
|
||||||
@hide="closeUnsavedChangesModal"
|
@hide="$emit('close')"
|
||||||
>
|
>
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapActions } from 'vuex';
|
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -34,13 +33,6 @@ export default {
|
|||||||
window.removeEventListener('keydown', this.onKey);
|
window.removeEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
|
||||||
discardUnsavedChanges: 'workspaces/discardUnsavedChanges',
|
|
||||||
closeUnsavedChangesModal: 'workspaces/closeUnsavedChangesModal'
|
|
||||||
}),
|
|
||||||
closeModal () {
|
|
||||||
this.$emit('close');
|
|
||||||
},
|
|
||||||
onKey (e) {
|
onKey (e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.key === 'Escape')
|
if (e.key === 'Escape')
|
||||||
|
|||||||
@@ -1,391 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="modal active">
|
|
||||||
<a class="modal-overlay c-hand" @click="closeModal" />
|
|
||||||
<div class="modal-container">
|
|
||||||
<div class="modal-header pl-2">
|
|
||||||
<div class="modal-title h6">
|
|
||||||
<div class="d-flex">
|
|
||||||
<i class="mdi mdi-24px mdi-server mr-1" /> {{ $t('message.editConnection') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<a class="btn btn-clear c-hand" @click="closeModal" />
|
|
||||||
</div>
|
|
||||||
<div class="modal-body p-0">
|
|
||||||
<div class="panel">
|
|
||||||
<div class="panel-nav">
|
|
||||||
<ul class="tab tab-block">
|
|
||||||
<li
|
|
||||||
class="tab-item"
|
|
||||||
: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>
|
|
||||||
<fieldset class="m-0" :disabled="isTesting || !localConnection.ssl">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.privateKey') }}</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<BaseUploadInput
|
|
||||||
:value="localConnection.key"
|
|
||||||
:message="$t('word.browse')"
|
|
||||||
@clear="pathClear('key')"
|
|
||||||
@change="pathSelection($event, 'key')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.certificate') }}</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<BaseUploadInput
|
|
||||||
:value="localConnection.cert"
|
|
||||||
:message="$t('word.browse')"
|
|
||||||
@clear="pathClear('cert')"
|
|
||||||
@change="pathSelection($event, 'cert')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.caCertificate') }}</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<BaseUploadInput
|
|
||||||
:value="localConnection.ca"
|
|
||||||
:message="$t('word.browse')"
|
|
||||||
@clear="pathClear('ca')"
|
|
||||||
@change="pathSelection($event, 'ca')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.ciphers') }}</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<input
|
|
||||||
ref="firstInput"
|
|
||||||
v-model="localConnection.ciphers"
|
|
||||||
class="form-input"
|
|
||||||
type="text"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<BaseToast
|
|
||||||
class="mb-2"
|
|
||||||
:message="toast.message"
|
|
||||||
:status="toast.status"
|
|
||||||
/>
|
|
||||||
</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>
|
|
||||||
<ModalAskCredentials
|
|
||||||
v-if="isAsking"
|
|
||||||
@close-asking="closeAsking"
|
|
||||||
@credentials="continueTest"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapActions } from 'vuex';
|
|
||||||
import customizations from 'common/customizations';
|
|
||||||
import Connection from '@/ipc-api/Connection';
|
|
||||||
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
|
||||||
import BaseToast from '@/components/BaseToast';
|
|
||||||
import BaseUploadInput from '@/components/BaseUploadInput';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ModalEditConnection',
|
|
||||||
components: {
|
|
||||||
ModalAskCredentials,
|
|
||||||
BaseToast,
|
|
||||||
BaseUploadInput
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
connection: Object
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
toast: {
|
|
||||||
status: '',
|
|
||||||
message: ''
|
|
||||||
},
|
|
||||||
isTesting: false,
|
|
||||||
isAsking: false,
|
|
||||||
localConnection: null,
|
|
||||||
selectedTab: 'general'
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
customizations () {
|
|
||||||
return customizations[this.connection.client];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created () {
|
|
||||||
this.localConnection = Object.assign({}, this.connection);
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$refs.firstInput.focus();
|
|
||||||
}, 20);
|
|
||||||
},
|
|
||||||
beforeDestroy () {
|
|
||||||
window.removeEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions({
|
|
||||||
editConnection: 'connections/editConnection'
|
|
||||||
}),
|
|
||||||
async startTest () {
|
|
||||||
this.isTesting = true;
|
|
||||||
this.toast = {
|
|
||||||
status: '',
|
|
||||||
message: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.localConnection.ask)
|
|
||||||
this.isAsking = true;
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
const res = await Connection.makeTest(this.localConnection);
|
|
||||||
if (res.status === 'error')
|
|
||||||
this.toast = { status: 'error', message: res.response.message };
|
|
||||||
else
|
|
||||||
this.toast = { status: 'success', message: this.$t('message.connectionSuccessfullyMade') };
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
this.toast = { status: 'error', message: err.stack };
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isTesting = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async continueTest (credentials) { // if "Ask for credentials" is true
|
|
||||||
this.isAsking = false;
|
|
||||||
const params = Object.assign({}, this.localConnection, credentials);
|
|
||||||
try {
|
|
||||||
const res = await Connection.makeTest(params);
|
|
||||||
if (res.status === 'error')
|
|
||||||
this.toast = { status: 'error', message: res.response.message };
|
|
||||||
else
|
|
||||||
this.toast = { status: 'success', message: this.$t('message.connectionSuccessfullyMade') };
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
this.toast = { status: 'error', message: err.stack };
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isTesting = false;
|
|
||||||
},
|
|
||||||
saveEditConnection () {
|
|
||||||
this.editConnection(this.localConnection);
|
|
||||||
this.closeModal();
|
|
||||||
},
|
|
||||||
closeAsking () {
|
|
||||||
this.isTesting = false;
|
|
||||||
this.isAsking = false;
|
|
||||||
},
|
|
||||||
closeModal () {
|
|
||||||
this.$emit('close');
|
|
||||||
},
|
|
||||||
onKey (e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
if (e.key === 'Escape')
|
|
||||||
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] = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.modal-container {
|
|
||||||
position: absolute;
|
|
||||||
max-width: 450px;
|
|
||||||
top: 17.5vh;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -5,7 +5,8 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-database-edit mr-1" /> {{ $t('message.editSchema') }}
|
<i class="mdi mdi-24px mdi-database-edit mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.editSchema') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
@@ -52,7 +53,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer text-light">
|
<div class="modal-footer">
|
||||||
<button class="btn btn-primary mr-2" @click.stop="updateSchema">
|
<button class="btn btn-primary mr-2" @click.stop="updateSchema">
|
||||||
{{ $t('word.update') }}
|
{{ $t('word.update') }}
|
||||||
</button>
|
</button>
|
||||||
@@ -71,7 +72,7 @@ import Schema from '@/ipc-api/Schema';
|
|||||||
export default {
|
export default {
|
||||||
name: 'ModalEditSchema',
|
name: 'ModalEditSchema',
|
||||||
props: {
|
props: {
|
||||||
selectedDatabase: String
|
selectedSchema: String
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -98,7 +99,7 @@ export default {
|
|||||||
async created () {
|
async created () {
|
||||||
let actualCollation;
|
let actualCollation;
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Schema.getDatabaseCollation({ uid: this.selectedWorkspace, database: this.selectedDatabase });
|
const { status, response } = await Schema.getDatabaseCollation({ uid: this.selectedWorkspace, database: this.selectedSchema });
|
||||||
|
|
||||||
if (status === 'success')
|
if (status === 'success')
|
||||||
actualCollation = response;
|
actualCollation = response;
|
||||||
@@ -111,8 +112,8 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.database = {
|
this.database = {
|
||||||
name: this.selectedDatabase,
|
name: this.selectedSchema,
|
||||||
prevName: this.selectedDatabase,
|
prevName: this.selectedSchema,
|
||||||
collation: actualCollation || this.defaultCollation,
|
collation: actualCollation || this.defaultCollation,
|
||||||
prevCollation: actualCollation || this.defaultCollation
|
prevCollation: actualCollation || this.defaultCollation
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-playlist-plus mr-1" /> {{ $t('message.tableFiller') }}
|
<i class="mdi mdi-24px mdi-playlist-plus mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.tableFiller') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
@@ -51,7 +52,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer text-light columns">
|
<div class="modal-footer columns">
|
||||||
<div class="column d-flex" :class="hasFakes ? 'col-4' : 'col-2'">
|
<div class="column d-flex" :class="hasFakes ? 'col-4' : 'col-2'">
|
||||||
<div class="input-group tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')">
|
<div class="input-group tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')">
|
||||||
<input
|
<input
|
||||||
@@ -250,25 +251,32 @@ export default {
|
|||||||
if (field.default === 'NULL') fieldDefault = null;
|
if (field.default === 'NULL') fieldDefault = null;
|
||||||
else {
|
else {
|
||||||
if ([...NUMBER, ...FLOAT].includes(field.type))
|
if ([...NUMBER, ...FLOAT].includes(field.type))
|
||||||
fieldDefault = +field.default;
|
fieldDefault = Number.isNaN(+field.default) ? null : +field.default;
|
||||||
|
else if ([...TEXT, ...LONG_TEXT].includes(field.type)) {
|
||||||
if ([...TEXT, ...LONG_TEXT].includes(field.type))
|
fieldDefault = field.default
|
||||||
fieldDefault = field.default ? field.default.substring(1, field.default.length - 1) : '';
|
? field.default.includes('\'')
|
||||||
|
? field.default.split('\'')[1]
|
||||||
if ([...TIME, ...DATE].includes(field.type))
|
: field.default
|
||||||
|
: '';
|
||||||
|
}
|
||||||
|
else if ([...TIME, ...DATE].includes(field.type))
|
||||||
fieldDefault = field.default;
|
fieldDefault = field.default;
|
||||||
|
else if (BIT.includes(field.type))
|
||||||
if (BIT.includes(field.type))
|
|
||||||
fieldDefault = field.default.replaceAll('\'', '').replaceAll('b', '');
|
fieldDefault = field.default.replaceAll('\'', '').replaceAll('b', '');
|
||||||
|
else if (DATETIME.includes(field.type)) {
|
||||||
if (DATETIME.includes(field.type)) {
|
if (field.default && ['current_timestamp', 'now()'].includes(field.default.toLowerCase())) {
|
||||||
if (field.default && field.default.toLowerCase().includes('current_timestamp')) {
|
|
||||||
let datePrecision = '';
|
let datePrecision = '';
|
||||||
for (let i = 0; i < field.datePrecision; i++)
|
for (let i = 0; i < field.datePrecision; i++)
|
||||||
datePrecision += i === 0 ? '.S' : 'S';
|
datePrecision += i === 0 ? '.S' : 'S';
|
||||||
fieldDefault = moment().format(`YYYY-MM-DD HH:mm:ss${datePrecision}`);
|
fieldDefault = moment().format(`YYYY-MM-DD HH:mm:ss${datePrecision}`);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
fieldDefault = field.default;
|
||||||
}
|
}
|
||||||
|
else if (field.enumValues)
|
||||||
|
fieldDefault = field.enumValues.replaceAll('\'', '').split(',');
|
||||||
|
else
|
||||||
|
fieldDefault = field.default;
|
||||||
}
|
}
|
||||||
|
|
||||||
rowObj[field.name] = { value: fieldDefault };
|
rowObj[field.name] = { value: fieldDefault };
|
||||||
|
|||||||
@@ -1,413 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="modal active">
|
|
||||||
<a class="modal-overlay c-hand" @click="closeModal" />
|
|
||||||
<div class="modal-container">
|
|
||||||
<div class="modal-header pl-2">
|
|
||||||
<div class="modal-title h6">
|
|
||||||
<div class="d-flex">
|
|
||||||
<i class="mdi mdi-24px mdi-server-plus mr-1" /> {{ $t('message.createNewConnection') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<a class="btn btn-clear c-hand" @click="closeModal" />
|
|
||||||
</div>
|
|
||||||
<div class="modal-body p-0">
|
|
||||||
<div class="panel">
|
|
||||||
<div class="panel-nav">
|
|
||||||
<ul class="tab tab-block">
|
|
||||||
<li
|
|
||||||
class="tab-item"
|
|
||||||
: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>
|
|
||||||
<fieldset class="m-0" :disabled="isTesting || !connection.ssl">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.privateKey') }}</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<BaseUploadInput
|
|
||||||
:value="connection.key"
|
|
||||||
:message="$t('word.browse')"
|
|
||||||
@clear="pathClear('key')"
|
|
||||||
@change="pathSelection($event, 'key')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.certificate') }}</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<BaseUploadInput
|
|
||||||
:value="connection.cert"
|
|
||||||
:message="$t('word.browse')"
|
|
||||||
@clear="pathClear('cert')"
|
|
||||||
@change="pathSelection($event, 'cert')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.caCertificate') }}</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<BaseUploadInput
|
|
||||||
:value="connection.ca"
|
|
||||||
:message="$t('word.browse')"
|
|
||||||
@clear="pathClear('ca')"
|
|
||||||
@change="pathSelection($event, 'ca')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.ciphers') }}</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<input
|
|
||||||
ref="firstInput"
|
|
||||||
v-model="connection.ciphers"
|
|
||||||
class="form-input"
|
|
||||||
type="text"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<BaseToast
|
|
||||||
class="mb-2"
|
|
||||||
:message="toast.message"
|
|
||||||
:status="toast.status"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</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="saveNewConnection">
|
|
||||||
{{ $t('word.save') }}
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-link" @click="closeModal">
|
|
||||||
{{ $t('word.close') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ModalAskCredentials
|
|
||||||
v-if="isAsking"
|
|
||||||
@close-asking="closeAsking"
|
|
||||||
@credentials="continueTest"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapActions } from 'vuex';
|
|
||||||
import customizations from 'common/customizations';
|
|
||||||
import Connection from '@/ipc-api/Connection';
|
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
|
||||||
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
|
||||||
import BaseToast from '@/components/BaseToast';
|
|
||||||
import BaseUploadInput from '@/components/BaseUploadInput';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ModalNewConnection',
|
|
||||||
components: {
|
|
||||||
ModalAskCredentials,
|
|
||||||
BaseToast,
|
|
||||||
BaseUploadInput
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
connection: {
|
|
||||||
name: '',
|
|
||||||
client: 'mysql',
|
|
||||||
host: '127.0.0.1',
|
|
||||||
database: null,
|
|
||||||
port: null,
|
|
||||||
user: null,
|
|
||||||
password: '',
|
|
||||||
ask: false,
|
|
||||||
uid: uidGen('C'),
|
|
||||||
ssl: false,
|
|
||||||
cert: '',
|
|
||||||
key: '',
|
|
||||||
ca: '',
|
|
||||||
ciphers: ''
|
|
||||||
|
|
||||||
},
|
|
||||||
toast: {
|
|
||||||
status: '',
|
|
||||||
message: ''
|
|
||||||
},
|
|
||||||
isTesting: false,
|
|
||||||
isAsking: false,
|
|
||||||
selectedTab: 'general'
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
customizations () {
|
|
||||||
return customizations[this.connection.client];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created () {
|
|
||||||
this.setDefaults();
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$refs.firstInput.focus();
|
|
||||||
}, 20);
|
|
||||||
},
|
|
||||||
beforeDestroy () {
|
|
||||||
window.removeEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions({
|
|
||||||
closeModal: 'application/hideNewConnModal',
|
|
||||||
addConnection: 'connections/addConnection'
|
|
||||||
}),
|
|
||||||
setDefaults () {
|
|
||||||
this.connection.user = this.customizations.defaultUser;
|
|
||||||
this.connection.port = this.customizations.defaultPort;
|
|
||||||
this.connection.database = this.customizations.defaultDatabase;
|
|
||||||
},
|
|
||||||
async startTest () {
|
|
||||||
this.isTesting = true;
|
|
||||||
this.toast = {
|
|
||||||
status: '',
|
|
||||||
message: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.connection.ask)
|
|
||||||
this.isAsking = true;
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
const res = await Connection.makeTest(this.connection);
|
|
||||||
if (res.status === 'error')
|
|
||||||
this.toast = { status: 'error', message: res.response.message };
|
|
||||||
else
|
|
||||||
this.toast = { status: 'success', message: this.$t('message.connectionSuccessfullyMade') };
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
this.toast = { status: 'error', message: err.stack };
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isTesting = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async continueTest (credentials) { // if "Ask for credentials" is true
|
|
||||||
this.isAsking = false;
|
|
||||||
const params = Object.assign({}, this.connection, credentials);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await Connection.makeTest(params);
|
|
||||||
if (res.status === 'error')
|
|
||||||
this.toast = { status: 'error', message: res.response.message };
|
|
||||||
else
|
|
||||||
this.toast = { status: 'success', message: this.$t('message.connectionSuccessfullyMade') };
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
this.toast = { status: 'error', message: err.stack };
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isTesting = false;
|
|
||||||
},
|
|
||||||
saveNewConnection () {
|
|
||||||
this.addConnection(this.connection);
|
|
||||||
this.closeModal();
|
|
||||||
},
|
|
||||||
closeAsking () {
|
|
||||||
this.isAsking = false;
|
|
||||||
this.isTesting = false;
|
|
||||||
},
|
|
||||||
onKey (e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
if (e.key === 'Escape')
|
|
||||||
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] = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.modal-container {
|
|
||||||
position: absolute;
|
|
||||||
max-width: 450px;
|
|
||||||
top: 17.5vh;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -155,8 +155,8 @@ export default {
|
|||||||
if (this.customizations.languages)
|
if (this.customizations.languages)
|
||||||
this.localFunction.language = this.customizations.languages[0];
|
this.localFunction.language = this.customizations.languages[0];
|
||||||
|
|
||||||
if (this.customizations.procedureSql)
|
if (this.customizations.functionSql)
|
||||||
this.localFunction.sql = this.customizations.procedureSql;
|
this.localFunction.sql = this.customizations.functionSql;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$refs.firstInput.focus();
|
this.$refs.firstInput.focus();
|
||||||
}, 20);
|
}, 20);
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-database-plus mr-1" /> {{ $t('message.createNewSchema') }}
|
<i class="mdi mdi-24px mdi-database-plus mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.createNewSchema') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
@@ -48,7 +49,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer text-light">
|
<div class="modal-footer">
|
||||||
<button
|
<button
|
||||||
class="btn btn-primary mr-2"
|
class="btn btn-primary mr-2"
|
||||||
:class="{'loading': isLoading}"
|
:class="{'loading': isLoading}"
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-playlist-plus mr-1" /> {{ $t('message.addNewRow') }}
|
<i class="mdi mdi-24px mdi-playlist-plus mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.addNewRow') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
@@ -85,7 +86,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer text-light">
|
<div class="modal-footer">
|
||||||
<div class="input-group col-3 tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')">
|
<div class="input-group col-3 tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')">
|
||||||
<input
|
<input
|
||||||
v-model="nInserts"
|
v-model="nInserts"
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.definer" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.definer') }}
|
{{ $t('word.definer') }}
|
||||||
</label>
|
</label>
|
||||||
@@ -71,30 +71,56 @@
|
|||||||
</label>
|
</label>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<select v-model="localTrigger.event1" class="form-select">
|
<select v-model="localTrigger.activation" class="form-select">
|
||||||
<option>BEFORE</option>
|
<option>BEFORE</option>
|
||||||
<option>AFTER</option>
|
<option>AFTER</option>
|
||||||
</select>
|
</select>
|
||||||
<select v-model="localTrigger.event2" class="form-select">
|
<select
|
||||||
<option>INSERT</option>
|
v-if="!customizations.triggerMultipleEvents"
|
||||||
<option>UPDATE</option>
|
v-model="localTrigger.event"
|
||||||
<option>DELETE</option>
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option v-for="event in Object.keys(localEvents)" :key="event">
|
||||||
|
{{ event }}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
|
<div v-if="customizations.triggerMultipleEvents" class="px-4">
|
||||||
|
<label
|
||||||
|
v-for="event in Object.keys(localEvents)"
|
||||||
|
:key="event"
|
||||||
|
class="form-checkbox form-inline"
|
||||||
|
@change.prevent="changeEvents(event)"
|
||||||
|
>
|
||||||
|
<input :checked="localEvents[event]" type="checkbox"><i class="form-icon" /> {{ event }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<div v-if="customizations.triggerStatementInCreation" class="workspace-query-results column col-12 mt-2">
|
||||||
|
<label class="form-label ml-2">{{ $t('message.triggerStatement') }}</label>
|
||||||
|
<QueryEditor
|
||||||
|
ref="queryEditor"
|
||||||
|
:value.sync="localTrigger.sql"
|
||||||
|
:workspace="workspace"
|
||||||
|
:schema="schema"
|
||||||
|
:height="editorHeight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
import QueryEditor from '@/components/QueryEditor';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ModalNewTrigger',
|
name: 'ModalNewTrigger',
|
||||||
components: {
|
components: {
|
||||||
ConfirmModal
|
ConfirmModal,
|
||||||
|
QueryEditor
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
workspace: Object
|
workspace: Object
|
||||||
@@ -103,13 +129,15 @@ export default {
|
|||||||
return {
|
return {
|
||||||
localTrigger: {
|
localTrigger: {
|
||||||
definer: '',
|
definer: '',
|
||||||
sql: 'BEGIN\r\n\r\nEND',
|
sql: '',
|
||||||
name: '',
|
name: '',
|
||||||
table: '',
|
table: '',
|
||||||
event1: 'BEFORE',
|
activation: 'BEFORE',
|
||||||
event2: 'INSERT'
|
event: 'INSERT'
|
||||||
},
|
},
|
||||||
isOptionsChanging: false
|
isOptionsChanging: false,
|
||||||
|
localEvents: { INSERT: false, UPDATE: false, DELETE: false },
|
||||||
|
editorHeight: 150
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -122,11 +150,16 @@ export default {
|
|||||||
.map(schema => schema.tables);
|
.map(schema => schema.tables);
|
||||||
|
|
||||||
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace.customizations;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
created () {
|
||||||
this.localTrigger.table = this.schemaTables.length ? this.schemaTables[0].name : '';
|
this.localTrigger.table = this.schemaTables.length ? this.schemaTables[0].name : '';
|
||||||
|
this.localTrigger.sql = this.customizations.triggerSql;
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$refs.firstInput.focus();
|
this.$refs.firstInput.focus();
|
||||||
}, 20);
|
}, 20);
|
||||||
@@ -134,6 +167,16 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
confirmNewTrigger () {
|
confirmNewTrigger () {
|
||||||
this.$emit('open-create-trigger-editor', this.localTrigger);
|
this.$emit('open-create-trigger-editor', this.localTrigger);
|
||||||
|
},
|
||||||
|
changeEvents (event) {
|
||||||
|
if (this.customizations.triggerMultipleEvents) {
|
||||||
|
this.localEvents[event] = !this.localEvents[event];
|
||||||
|
this.localTrigger.event = [];
|
||||||
|
for (const key in this.localEvents) {
|
||||||
|
if (this.localEvents[key])
|
||||||
|
this.localTrigger.event.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
132
src/renderer/components/ModalNewTriggerFunction.vue
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.confirm')"
|
||||||
|
size="400"
|
||||||
|
@confirm="confirmNewFunction"
|
||||||
|
@hide="$emit('close')"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-plus mr-1" /> {{ $t('message.createNewFunction') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.name') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="localFunction.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.languages" class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.language') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="localFunction.language" class="form-select">
|
||||||
|
<option v-for="language in customizations.triggerFunctionlanguages" :key="language">
|
||||||
|
{{ language }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.definer" class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.definer') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select
|
||||||
|
v-if="workspace.users.length"
|
||||||
|
v-model="localFunction.definer"
|
||||||
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="user in workspace.users"
|
||||||
|
:key="`${user.name}@${user.host}`"
|
||||||
|
:value="`\`${user.name}\`@\`${user.host}\``"
|
||||||
|
>
|
||||||
|
{{ user.name }}@{{ user.host }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<select v-if="!workspace.users.length" class="form-select">
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.comment" class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.comment') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
v-model="localFunction.comment"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalNewTriggerFunction',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
localFunction: {
|
||||||
|
definer: '',
|
||||||
|
sql: '',
|
||||||
|
name: '',
|
||||||
|
comment: '',
|
||||||
|
language: null
|
||||||
|
},
|
||||||
|
isOptionsChanging: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
schema () {
|
||||||
|
return this.workspace.breadcrumbs.schema;
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace.customizations;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
if (this.customizations.triggerFunctionlanguages)
|
||||||
|
this.localFunction.language = this.customizations.triggerFunctionlanguages[0];
|
||||||
|
|
||||||
|
if (this.customizations.triggerFunctionSql)
|
||||||
|
this.localFunction.sql = this.customizations.triggerFunctionSql;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmNewFunction () {
|
||||||
|
this.$emit('open-create-function-editor', this.localFunction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -5,7 +5,8 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-memory mr-1" /> {{ $t('message.processesList') }}: {{ connectionName }}
|
<i class="mdi mdi-24px mdi-memory mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.processesList') }}: {{ connectionName }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
@@ -19,9 +20,9 @@
|
|||||||
title="F5"
|
title="F5"
|
||||||
@click="getProcessesList"
|
@click="getProcessesList"
|
||||||
>
|
>
|
||||||
|
<i v-if="!+autorefreshTimer" class="mdi mdi-24px mdi-refresh mr-1" />
|
||||||
|
<i v-else class="mdi mdi-24px mdi-history mdi-flip-h mr-1" />
|
||||||
<span>{{ $t('word.refresh') }}</span>
|
<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>
|
</button>
|
||||||
<div class="btn btn-dark btn-sm dropdown-toggle pl-0 pr-0" tabindex="0">
|
<div class="btn btn-dark btn-sm dropdown-toggle pl-0 pr-0" tabindex="0">
|
||||||
<i class="mdi mdi-24px mdi-menu-down" />
|
<i class="mdi mdi-24px mdi-menu-down" />
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-cog mr-1" />
|
<i class="mdi mdi-24px mdi-cog mr-1" />
|
||||||
{{ $t('word.settings') }}
|
<span class="cut-text">{{ $t('word.settings') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click="closeModal" />
|
<a class="btn btn-clear c-hand" @click="closeModal" />
|
||||||
@@ -16,40 +16,40 @@
|
|||||||
<div class="panel-nav">
|
<div class="panel-nav">
|
||||||
<ul class="tab tab-block">
|
<ul class="tab tab-block">
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'general'}"
|
:class="{'active': selectedTab === 'general'}"
|
||||||
@click="selectTab('general')"
|
@click="selectTab('general')"
|
||||||
>
|
>
|
||||||
<a class="c-hand">{{ $t('word.general') }}</a>
|
<a class="tab-link">{{ $t('word.general') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'themes'}"
|
:class="{'active': selectedTab === 'themes'}"
|
||||||
@click="selectTab('themes')"
|
@click="selectTab('themes')"
|
||||||
>
|
>
|
||||||
<a class="c-hand">{{ $t('word.themes') }}</a>
|
<a class="tab-link">{{ $t('word.themes') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
v-if="updateStatus !== 'disabled'"
|
v-if="updateStatus !== 'disabled'"
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'update'}"
|
:class="{'active': selectedTab === 'update'}"
|
||||||
@click="selectTab('update')"
|
@click="selectTab('update')"
|
||||||
>
|
>
|
||||||
<a class="c-hand" :class="{'badge badge-update': hasUpdates}">{{ $t('word.update') }}</a>
|
<a class="tab-link" :class="{'badge badge-update': hasUpdates}">{{ $t('word.update') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'changelog'}"
|
:class="{'active': selectedTab === 'changelog'}"
|
||||||
@click="selectTab('changelog')"
|
@click="selectTab('changelog')"
|
||||||
>
|
>
|
||||||
<a class="c-hand">{{ $t('word.changelog') }}</a>
|
<a class="tab-link">{{ $t('word.changelog') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'about'}"
|
:class="{'active': selectedTab === 'about'}"
|
||||||
@click="selectTab('about')"
|
@click="selectTab('about')"
|
||||||
>
|
>
|
||||||
<a class="c-hand">{{ $t('word.about') }}</a>
|
<a class="tab-link">{{ $t('word.about') }}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -59,15 +59,15 @@
|
|||||||
<div class="column col-12 h6 text-uppercase mb-1">
|
<div class="column col-12 h6 text-uppercase mb-1">
|
||||||
{{ $t('word.application') }}
|
{{ $t('word.application') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-8 col-sm-12 mb-2">
|
<div class="column col-9 col-sm-12 mb-2">
|
||||||
<div class="form-group mb-4">
|
<div class="form-group">
|
||||||
<div class="col-6 col-sm-12">
|
<div class="col-7 col-sm-12">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
<i class="mdi mdi-18px mdi-translate mr-1" />
|
<i class="mdi mdi-18px mdi-translate mr-1" />
|
||||||
{{ $t('word.language') }}
|
{{ $t('word.language') }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 col-sm-12">
|
<div class="col-5 col-sm-12">
|
||||||
<select
|
<select
|
||||||
v-model="localLocale"
|
v-model="localLocale"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
@@ -84,12 +84,46 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-6 col-sm-12">
|
<div class="col-7 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('message.dataTabPageSize') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-5 col-sm-12">
|
||||||
|
<select
|
||||||
|
v-model="localPageSize"
|
||||||
|
class="form-select"
|
||||||
|
@change="changePageSize(+localPageSize)"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="size in pageSizes"
|
||||||
|
:key="size"
|
||||||
|
>
|
||||||
|
{{ size }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<div class="col-7 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('message.restorePreviourSession') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-5 col-sm-12">
|
||||||
|
<label class="form-switch d-inline-block" @click.prevent="toggleRestoreSession">
|
||||||
|
<input type="checkbox" :checked="restoreTabs">
|
||||||
|
<i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-7 col-sm-12">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('message.notificationsTimeout') }}
|
{{ $t('message.notificationsTimeout') }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 col-sm-12">
|
<div class="col-5 col-sm-12">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input
|
<input
|
||||||
v-model="localTimeout"
|
v-model="localTimeout"
|
||||||
@@ -107,29 +141,27 @@
|
|||||||
<div class="column col-12 h6 mt-4 text-uppercase mb-1">
|
<div class="column col-12 h6 mt-4 text-uppercase mb-1">
|
||||||
{{ $t('word.editor') }}
|
{{ $t('word.editor') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-8 col-sm-12">
|
<div class="column col-9 col-sm-12">
|
||||||
<div class="form-group">
|
<div class="form-group mb-0">
|
||||||
<div class="col-6 col-sm-12">
|
<div class="col-7 col-sm-12">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.autoCompletion') }}
|
{{ $t('word.autoCompletion') }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 col-sm-12">
|
<div class="col-5 col-sm-12">
|
||||||
<label class="form-switch d-inline-block" @click.prevent="toggleAutoComplete">
|
<label class="form-switch d-inline-block" @click.prevent="toggleAutoComplete">
|
||||||
<input type="checkbox" :checked="selectedAutoComplete">
|
<input type="checkbox" :checked="selectedAutoComplete">
|
||||||
<i class="form-icon" />
|
<i class="form-icon" />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group mb-0">
|
||||||
<div class="column col-8 col-sm-12">
|
<div class="col-7 col-sm-12">
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-6 col-sm-12">
|
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('message.wrapLongLines') }}
|
{{ $t('message.wrapLongLines') }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 col-sm-12">
|
<div class="col-5 col-sm-12">
|
||||||
<label class="form-switch d-inline-block" @click.prevent="toggleLineWrap">
|
<label class="form-switch d-inline-block" @click.prevent="toggleLineWrap">
|
||||||
<input type="checkbox" :checked="selectedLineWrap">
|
<input type="checkbox" :checked="selectedLineWrap">
|
||||||
<i class="form-icon" />
|
<i class="form-icon" />
|
||||||
@@ -201,6 +233,31 @@
|
|||||||
</optgroup>
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="column col-6 mb-4">
|
||||||
|
<div class="btn-group btn-group-block">
|
||||||
|
<button
|
||||||
|
class="btn btn-dark cut-text"
|
||||||
|
:class="{'active': editorFontSize === 'small'}"
|
||||||
|
@click="changeEditorFontSize('small')"
|
||||||
|
>
|
||||||
|
{{ $t('word.small') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-dark cut-text"
|
||||||
|
:class="{'active': editorFontSize === 'medium'}"
|
||||||
|
@click="changeEditorFontSize('medium')"
|
||||||
|
>
|
||||||
|
{{ $t('word.medium') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-dark cut-text"
|
||||||
|
:class="{'active': editorFontSize === 'large'}"
|
||||||
|
@click="changeEditorFontSize('large')"
|
||||||
|
>
|
||||||
|
{{ $t('word.large') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="column col-12">
|
<div class="column col-12">
|
||||||
<BaseTextEditor
|
<BaseTextEditor
|
||||||
:value="exampleQuery"
|
:value="exampleQuery"
|
||||||
@@ -227,7 +284,7 @@
|
|||||||
<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://antares-sql.app/')">Website</a><br>
|
<a class="c-hand" @click="openOutside('https://github.com/Fabio286/antares')"><i class="mdi mdi-github d-inline" /> GitHub</a> • <a class="c-hand" @click="openOutside('https://twitter.com/AntaresSQL')"><i class="mdi mdi-twitter d-inline" /> Twitter</a> • <a class="c-hand" @click="openOutside('https://antares-sql.app/')"><i class="mdi mdi-web d-inline" /> 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>
|
||||||
@@ -257,9 +314,11 @@ export default {
|
|||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
localLocale: null,
|
localLocale: null,
|
||||||
|
localPageSize: null,
|
||||||
localTimeout: null,
|
localTimeout: null,
|
||||||
localEditorTheme: null,
|
localEditorTheme: null,
|
||||||
selectedTab: 'general',
|
selectedTab: 'general',
|
||||||
|
pageSizes: [30, 40, 100, 250, 500, 1000],
|
||||||
editorThemes: [
|
editorThemes: [
|
||||||
{
|
{
|
||||||
group: this.$t('word.light'),
|
group: this.$t('word.light'),
|
||||||
@@ -317,11 +376,14 @@ export default {
|
|||||||
appVersion: 'application/appVersion',
|
appVersion: 'application/appVersion',
|
||||||
selectedSettingTab: 'application/selectedSettingTab',
|
selectedSettingTab: 'application/selectedSettingTab',
|
||||||
selectedLocale: 'settings/getLocale',
|
selectedLocale: 'settings/getLocale',
|
||||||
|
pageSize: 'settings/getDataTabLimit',
|
||||||
selectedAutoComplete: 'settings/getAutoComplete',
|
selectedAutoComplete: 'settings/getAutoComplete',
|
||||||
selectedLineWrap: 'settings/getLineWrap',
|
selectedLineWrap: 'settings/getLineWrap',
|
||||||
notificationsTimeout: 'settings/getNotificationsTimeout',
|
notificationsTimeout: 'settings/getNotificationsTimeout',
|
||||||
|
restoreTabs: 'settings/getRestoreTabs',
|
||||||
applicationTheme: 'settings/getApplicationTheme',
|
applicationTheme: 'settings/getApplicationTheme',
|
||||||
editorTheme: 'settings/getEditorTheme',
|
editorTheme: 'settings/getEditorTheme',
|
||||||
|
editorFontSize: 'settings/getEditorFontSize',
|
||||||
updateStatus: 'application/getUpdateStatus',
|
updateStatus: 'application/getUpdateStatus',
|
||||||
selectedWorkspace: 'workspaces/getSelected',
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
getWorkspace: 'workspaces/getWorkspace'
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
@@ -359,6 +421,7 @@ ORDER BY
|
|||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.localLocale = this.selectedLocale;
|
this.localLocale = this.selectedLocale;
|
||||||
|
this.localPageSize = this.pageSize;
|
||||||
this.localTimeout = this.notificationsTimeout;
|
this.localTimeout = this.notificationsTimeout;
|
||||||
this.localEditorTheme = this.editorTheme;
|
this.localEditorTheme = this.editorTheme;
|
||||||
this.selectedTab = this.selectedSettingTab;
|
this.selectedTab = this.selectedSettingTab;
|
||||||
@@ -371,10 +434,13 @@ ORDER BY
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
closeModal: 'application/hideSettingModal',
|
closeModal: 'application/hideSettingModal',
|
||||||
changeLocale: 'settings/changeLocale',
|
changeLocale: 'settings/changeLocale',
|
||||||
|
changePageSize: 'settings/changePageSize',
|
||||||
|
changeRestoreTabs: 'settings/changeRestoreTabs',
|
||||||
changeAutoComplete: 'settings/changeAutoComplete',
|
changeAutoComplete: 'settings/changeAutoComplete',
|
||||||
changeLineWrap: 'settings/changeLineWrap',
|
changeLineWrap: 'settings/changeLineWrap',
|
||||||
changeApplicationTheme: 'settings/changeApplicationTheme',
|
changeApplicationTheme: 'settings/changeApplicationTheme',
|
||||||
changeEditorTheme: 'settings/changeEditorTheme',
|
changeEditorTheme: 'settings/changeEditorTheme',
|
||||||
|
changeEditorFontSize: 'settings/changeEditorFontSize',
|
||||||
updateNotificationsTimeout: 'settings/updateNotificationsTimeout'
|
updateNotificationsTimeout: 'settings/updateNotificationsTimeout'
|
||||||
}),
|
}),
|
||||||
selectTab (tab) {
|
selectTab (tab) {
|
||||||
@@ -394,6 +460,9 @@ ORDER BY
|
|||||||
if (e.key === 'Escape')
|
if (e.key === 'Escape')
|
||||||
this.closeModal();
|
this.closeModal();
|
||||||
},
|
},
|
||||||
|
toggleRestoreSession () {
|
||||||
|
this.changeRestoreTabs(!this.restoreTabs);
|
||||||
|
},
|
||||||
toggleAutoComplete () {
|
toggleAutoComplete () {
|
||||||
this.changeAutoComplete(!this.selectedAutoComplete);
|
this.changeAutoComplete(!this.selectedAutoComplete);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
editorTheme: 'settings/getEditorTheme',
|
editorTheme: 'settings/getEditorTheme',
|
||||||
|
editorFontSize: 'settings/getEditorFontSize',
|
||||||
autoComplete: 'settings/getAutoComplete',
|
autoComplete: 'settings/getAutoComplete',
|
||||||
lineWrap: 'settings/getLineWrap',
|
lineWrap: 'settings/getLineWrap',
|
||||||
baseCompleter: 'application/getBaseCompleter'
|
baseCompleter: 'application/getBaseCompleter'
|
||||||
@@ -158,6 +159,19 @@ export default {
|
|||||||
if (this.editor)
|
if (this.editor)
|
||||||
this.editor.setTheme(`ace/theme/${this.editorTheme}`);
|
this.editor.setTheme(`ace/theme/${this.editorTheme}`);
|
||||||
},
|
},
|
||||||
|
editorFontSize () {
|
||||||
|
const sizes = {
|
||||||
|
small: '12px',
|
||||||
|
medium: '14px',
|
||||||
|
large: '16px'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.setOptions({
|
||||||
|
fontSize: sizes[this.editorFontSize]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
autoComplete () {
|
autoComplete () {
|
||||||
if (this.editor) {
|
if (this.editor) {
|
||||||
this.editor.setOptions({
|
this.editor.setOptions({
|
||||||
@@ -173,8 +187,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
isSelected () {
|
isSelected () {
|
||||||
if (this.isSelected)
|
if (this.isSelected) {
|
||||||
this.lastSchema = this.schema;
|
this.lastSchema = this.schema;
|
||||||
|
this.editor.resize();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
height () {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.editor.resize();
|
||||||
|
}, 20);
|
||||||
},
|
},
|
||||||
lastSchema () {
|
lastSchema () {
|
||||||
if (this.editor) {
|
if (this.editor) {
|
||||||
|
|||||||
@@ -3,18 +3,13 @@
|
|||||||
:context-event="contextEvent"
|
:context-event="contextEvent"
|
||||||
@close-context="$emit('close-context')"
|
@close-context="$emit('close-context')"
|
||||||
>
|
>
|
||||||
<div class="context-element" @click="showEditModal(contextConnection)">
|
<div class="context-element" @click="duplicateConnection">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-pencil text-light pr-1" /> {{ $t('word.edit') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-content-duplicate text-light pr-1" /> {{ $t('word.duplicate') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element" @click="showConfirmModal">
|
<div class="context-element" @click="showConfirmModal">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $t('word.delete') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $t('word.delete') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ModalEditConnection
|
|
||||||
v-if="isEditModal"
|
|
||||||
:connection="contextConnection"
|
|
||||||
@close="hideEditModal"
|
|
||||||
/>
|
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
v-if="isConfirmModal"
|
v-if="isConfirmModal"
|
||||||
@confirm="confirmDeleteConnection"
|
@confirm="confirmDeleteConnection"
|
||||||
@@ -36,15 +31,14 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapActions, mapGetters } from 'vuex';
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import BaseContextMenu from '@/components/BaseContextMenu';
|
import BaseContextMenu from '@/components/BaseContextMenu';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
import ModalEditConnection from '@/components/ModalEditConnection';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SettingBarContext',
|
name: 'SettingBarContext',
|
||||||
components: {
|
components: {
|
||||||
BaseContextMenu,
|
BaseContextMenu,
|
||||||
ModalEditConnection,
|
|
||||||
ConfirmModal
|
ConfirmModal
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
@@ -59,7 +53,8 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
getConnectionName: 'connections/getConnectionName'
|
getConnectionName: 'connections/getConnectionName',
|
||||||
|
selectedWorkspace: 'workspaces/getSelected'
|
||||||
}),
|
}),
|
||||||
connectionName () {
|
connectionName () {
|
||||||
return this.getConnectionName(this.contextConnection.uid);
|
return this.getConnectionName(this.contextConnection.uid);
|
||||||
@@ -67,17 +62,25 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
deleteConnection: 'connections/deleteConnection'
|
addConnection: 'connections/addConnection',
|
||||||
|
deleteConnection: 'connections/deleteConnection',
|
||||||
|
selectWorkspace: 'workspaces/selectWorkspace'
|
||||||
}),
|
}),
|
||||||
confirmDeleteConnection () {
|
confirmDeleteConnection () {
|
||||||
|
if (this.selectedWorkspace === this.contextConnection.uid)
|
||||||
|
this.selectWorkspace();
|
||||||
this.deleteConnection(this.contextConnection);
|
this.deleteConnection(this.contextConnection);
|
||||||
this.closeContext();
|
this.closeContext();
|
||||||
},
|
},
|
||||||
showEditModal () {
|
duplicateConnection () {
|
||||||
this.isEditModal = true;
|
let connectionCopy = Object.assign({}, this.contextConnection);
|
||||||
},
|
connectionCopy = {
|
||||||
hideEditModal () {
|
...connectionCopy,
|
||||||
this.isEditModal = false;
|
uid: uidGen('C'),
|
||||||
|
name: connectionCopy.name ? `${connectionCopy.name}_copy` : ''
|
||||||
|
};
|
||||||
|
|
||||||
|
this.addConnection(connectionCopy);
|
||||||
this.closeContext();
|
this.closeContext();
|
||||||
},
|
},
|
||||||
showConfirmModal () {
|
showConfirmModal () {
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="columns">
|
|
||||||
<div class="column col-12 empty text-light">
|
|
||||||
<div class="empty-icon">
|
|
||||||
<i class="mdi mdi-48px mdi-emoticon" />
|
|
||||||
</div>
|
|
||||||
<p class="empty-title h5">
|
|
||||||
{{ $t('message.appWelcome') }}
|
|
||||||
</p>
|
|
||||||
<p class="empty-subtitle">
|
|
||||||
{{ $t('message.appFirstStep') }}
|
|
||||||
</p>
|
|
||||||
<div class="empty-action">
|
|
||||||
<button class="btn btn-primary" @click="$emit('new-conn')">
|
|
||||||
{{ $t('message.createConnection') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'TheAppWelcome'
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.empty {
|
|
||||||
height: 100%;
|
|
||||||
border-radius: 0;
|
|
||||||
background: transparent;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -8,24 +8,29 @@
|
|||||||
@close-context="isContext = false"
|
@close-context="isContext = false"
|
||||||
/>
|
/>
|
||||||
<ul class="settingbar-elements">
|
<ul class="settingbar-elements">
|
||||||
<draggable v-model="connections">
|
<Draggable
|
||||||
|
v-model="connections"
|
||||||
|
@start="isDragging = true"
|
||||||
|
@end="dragStop"
|
||||||
|
>
|
||||||
<li
|
<li
|
||||||
v-for="connection in connections"
|
v-for="connection in connections"
|
||||||
:key="connection.uid"
|
:key="connection.uid"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
class="settingbar-element btn btn-link ex-tooltip"
|
class="settingbar-element btn btn-link ex-tooltip"
|
||||||
:class="{'selected': connection.uid === selectedWorkspace}"
|
:class="{'selected': connection.uid === selectedWorkspace}"
|
||||||
@click="selectWorkspace(connection.uid)"
|
@click.stop="selectWorkspace(connection.uid)"
|
||||||
@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} ${getStatusBadge(connection.uid)}`" />
|
<i class="settingbar-element-icon dbi" :class="`dbi-${connection.client} ${getStatusBadge(connection.uid)}`" />
|
||||||
<span class="ex-tooltip-content">{{ getConnectionName(connection.uid) }}</span>
|
<span v-if="!isDragging" class="ex-tooltip-content">{{ getConnectionName(connection.uid) }}</span>
|
||||||
</li>
|
</li>
|
||||||
</draggable>
|
</Draggable>
|
||||||
<li
|
<li
|
||||||
class="settingbar-element btn btn-link ex-tooltip"
|
class="settingbar-element btn btn-link ex-tooltip"
|
||||||
@click="showNewConnModal"
|
:class="{'selected': 'NEW' === selectedWorkspace}"
|
||||||
|
@click="selectWorkspace('NEW')"
|
||||||
@mouseover.self="tooltipPosition"
|
@mouseover.self="tooltipPosition"
|
||||||
>
|
>
|
||||||
<i class="settingbar-element-icon mdi mdi-24px mdi-plus text-light" />
|
<i class="settingbar-element-icon mdi mdi-24px mdi-plus text-light" />
|
||||||
@@ -51,19 +56,20 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapActions, mapGetters } from 'vuex';
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
import draggable from 'vuedraggable';
|
import Draggable from 'vuedraggable';
|
||||||
import SettingBarContext from '@/components/SettingBarContext';
|
import SettingBarContext from '@/components/SettingBarContext';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TheSettingBar',
|
name: 'TheSettingBar',
|
||||||
components: {
|
components: {
|
||||||
draggable,
|
Draggable,
|
||||||
SettingBarContext
|
SettingBarContext
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
dragElement: null,
|
dragElement: null,
|
||||||
isContext: false,
|
isContext: false,
|
||||||
|
isDragging: false,
|
||||||
contextEvent: null,
|
contextEvent: null,
|
||||||
contextConnection: {},
|
contextConnection: {},
|
||||||
scale: 0
|
scale: 0
|
||||||
@@ -92,7 +98,6 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
updateConnections: 'connections/updateConnections',
|
updateConnections: 'connections/updateConnections',
|
||||||
showNewConnModal: 'application/showNewConnModal',
|
|
||||||
showSettingModal: 'application/showSettingModal',
|
showSettingModal: 'application/showSettingModal',
|
||||||
showScratchpad: 'application/showScratchpad',
|
showScratchpad: 'application/showScratchpad',
|
||||||
selectWorkspace: 'workspaces/selectWorkspace'
|
selectWorkspace: 'workspaces/selectWorkspace'
|
||||||
@@ -106,13 +111,13 @@ export default {
|
|||||||
return connection.ask ? '' : `${connection.user + '@'}${connection.host}:${connection.port}`;
|
return connection.ask ? '' : `${connection.user + '@'}${connection.host}:${connection.port}`;
|
||||||
},
|
},
|
||||||
tooltipPosition (e) {
|
tooltipPosition (e) {
|
||||||
const el = e.target;
|
const el = e.target ? e.target : e;
|
||||||
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) {
|
getStatusBadge (uid) {
|
||||||
if (this.getWorkspace(uid)) {
|
if (this.getWorkspace(uid)) {
|
||||||
const status = this.getWorkspace(uid).connection_status;
|
const status = this.getWorkspace(uid).connectionStatus;
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'connected':
|
case 'connected':
|
||||||
@@ -125,6 +130,13 @@ export default {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
dragStop (e) {
|
||||||
|
this.isDragging = false;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.tooltipPosition(e.originalEvent.target.parentNode);
|
||||||
|
}, 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -167,13 +179,13 @@ export default {
|
|||||||
height: $settingbar-width;
|
height: $settingbar-width;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border-left: 3px solid transparent;
|
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-content: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: flex-start;
|
||||||
flex-direction: column;
|
border-radius: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@@ -181,9 +193,24 @@ export default {
|
|||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
height: $settingbar-width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
height: 0;
|
||||||
|
width: 3px;
|
||||||
|
transition: height 0.2s;
|
||||||
|
background-color: $primary-color;
|
||||||
|
border-radius: $border-radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settingbar-element-icon {
|
.settingbar-element-icon {
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
&.badge::after {
|
&.badge::after {
|
||||||
bottom: -10px;
|
bottom: -10px;
|
||||||
right: 0;
|
right: 0;
|
||||||
@@ -211,7 +238,7 @@ export default {
|
|||||||
padding: 0.2rem 0.4rem;
|
padding: 0.2rem 0.4rem;
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
background: rgba(48, 55, 66, 0.95);
|
background: rgba(48, 55, 66, 0.95);
|
||||||
border-radius: 0.1rem;
|
border-radius: $border-radius;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
max-width: 320px;
|
max-width: 320px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@@ -220,7 +247,13 @@ export default {
|
|||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover .ex-tooltip-content {
|
&.sortable-chosen {
|
||||||
|
.ex-tooltip-content {
|
||||||
|
opacity: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(.selected) .ex-tooltip-content {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,15 +37,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { remote, ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
|
import { getCurrentWindow } from '@electron/remote';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TheTitleBar',
|
name: 'TheTitleBar',
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
w: remote.getCurrentWindow(),
|
w: getCurrentWindow(),
|
||||||
isMaximized: remote.getCurrentWindow().isMaximized(),
|
isMaximized: getCurrentWindow().isMaximized(),
|
||||||
isDevelopment: process.env.NODE_ENV === 'development'
|
isDevelopment: process.env.NODE_ENV === 'development'
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -57,6 +58,7 @@ export default {
|
|||||||
}),
|
}),
|
||||||
windowTitle () {
|
windowTitle () {
|
||||||
if (!this.selectedWorkspace) return '';
|
if (!this.selectedWorkspace) return '';
|
||||||
|
if (this.selectedWorkspace === 'NEW') return this.$t('message.createNewConnection');
|
||||||
|
|
||||||
const connectionName = this.getConnectionName(this.selectedWorkspace);
|
const connectionName = this.getConnectionName(this.selectedWorkspace);
|
||||||
const workspace = this.getWorkspace(this.selectedWorkspace);
|
const workspace = this.getWorkspace(this.selectedWorkspace);
|
||||||
@@ -132,7 +134,7 @@ export default {
|
|||||||
|
|
||||||
.titlebar-logo {
|
.titlebar-logo {
|
||||||
height: $titlebar-height;
|
height: $titlebar-height;
|
||||||
padding: 0 0.4rem;
|
padding: 0.3rem 0.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.titlebar-element {
|
.titlebar-element {
|
||||||
|
|||||||
@@ -1,13 +1,143 @@
|
|||||||
<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
|
||||||
<div v-if="workspace.connection_status === 'connected'" class="workspace-tabs column columns col-gapless">
|
v-if="workspace.connectionStatus === 'connected'"
|
||||||
<ul
|
:connection="connection"
|
||||||
id="tabWrap"
|
:is-selected="isSelected"
|
||||||
|
/>
|
||||||
|
<div v-if="workspace.connectionStatus === 'connected'" class="workspace-tabs column columns col-gapless">
|
||||||
|
<Draggable
|
||||||
ref="tabWrap"
|
ref="tabWrap"
|
||||||
|
v-model="draggableTabs"
|
||||||
|
tag="ul"
|
||||||
|
group="tabs"
|
||||||
class="tab tab-block column col-12"
|
class="tab tab-block column col-12"
|
||||||
|
draggable=".tab-draggable"
|
||||||
|
@mouseover.native="addWheelEvent"
|
||||||
>
|
>
|
||||||
<li class="tab-item dropdown tools-dropdown">
|
<li
|
||||||
|
v-for="(tab, i) of draggableTabs"
|
||||||
|
:key="i"
|
||||||
|
:ref="selectedTab === tab.uid ? 'tab-selected' : ''"
|
||||||
|
class="tab-item tab-draggable"
|
||||||
|
draggable="true"
|
||||||
|
:class="{'active': selectedTab === tab.uid}"
|
||||||
|
@mousedown.left="selectTab({uid: workspace.uid, tab: tab.uid})"
|
||||||
|
@mouseup.middle="closeTab(tab)"
|
||||||
|
>
|
||||||
|
<a v-if="tab.type === 'query'" class="tab-link">
|
||||||
|
<i class="mdi mdi-18px mdi-code-tags mr-1" />
|
||||||
|
<span>
|
||||||
|
<span>{{ tab.content || 'Query' | cutText }} #{{ tab.index }}</span>
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else-if="tab.type === 'temp-data'"
|
||||||
|
class="tab-link"
|
||||||
|
@dblclick="openAsPermanentTab(tab)"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-18px mr-1" :class="tab.elementType === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
|
||||||
|
<span :title="`${$t('word.data').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
<span class=" text-italic">{{ tab.elementName | cutText }}</span>
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a v-else-if="tab.type === 'data'" class="tab-link">
|
||||||
|
<i class="mdi mdi-18px mr-1" :class="tab.elementType === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
|
||||||
|
<span :title="`${$t('word.data').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
{{ tab.elementName | cutText }}
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else-if="tab.type === 'table-props'"
|
||||||
|
class="tab-link"
|
||||||
|
:class="{'badge': tab.isChanged}"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-tune-vertical-variant mdi-18px mr-1" />
|
||||||
|
<span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
{{ tab.elementName | cutText }}
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else-if="tab.type === 'view-props'"
|
||||||
|
class="tab-link"
|
||||||
|
:class="{'badge': tab.isChanged}"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-tune-vertical-variant mdi-18px mr-1" />
|
||||||
|
<span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
{{ tab.elementName | cutText }}
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else-if="tab.type.includes('temp-')"
|
||||||
|
class="tab-link"
|
||||||
|
:class="{'badge': tab.isChanged}"
|
||||||
|
@dblclick="openAsPermanentTab(tab)"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
|
||||||
|
<span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
<span class=" text-italic">{{ tab.elementName | cutText }}</span>
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else
|
||||||
|
class="tab-link"
|
||||||
|
:class="{'badge': tab.isChanged}"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
|
||||||
|
<span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
{{ tab.elementName | cutText }}
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li slot="header" class="tab-item dropdown tools-dropdown">
|
||||||
<a
|
<a
|
||||||
class="tab-link workspace-tools-link dropdown-toggle"
|
class="tab-link workspace-tools-link dropdown-toggle"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@@ -44,143 +174,153 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li slot="footer" class="tab-item">
|
||||||
v-if="schemaChild && isSettingSupported"
|
|
||||||
class="tab-item"
|
|
||||||
:class="{'active': selectedTab === 'prop'}"
|
|
||||||
@click="selectTab({uid: workspace.uid, tab: 'prop'})"
|
|
||||||
>
|
|
||||||
<a class="tab-link">
|
|
||||||
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
|
|
||||||
<span :title="schemaChild">{{ $t('word.settings').toUpperCase() }}: {{ schemaChild }}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
v-if="workspace.breadcrumbs.table || workspace.breadcrumbs.view"
|
|
||||||
class="tab-item"
|
|
||||||
:class="{'active': selectedTab === 'data'}"
|
|
||||||
@click="selectTab({uid: workspace.uid, tab: 'data'})"
|
|
||||||
>
|
|
||||||
<a class="tab-link">
|
|
||||||
<i class="mdi mdi-18px mr-1" :class="workspace.breadcrumbs.table ? 'mdi-table' : 'mdi-table-eye'" />
|
|
||||||
<span :title="schemaChild">{{ $t('word.data').toUpperCase() }}: {{ schemaChild }}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
v-for="tab of queryTabs"
|
|
||||||
:key="tab.uid"
|
|
||||||
class="tab-item"
|
|
||||||
:class="{'active': selectedTab === tab.uid}"
|
|
||||||
@click="selectTab({uid: workspace.uid, tab: tab.uid})"
|
|
||||||
@mouseup.middle="closeTab(tab.uid)"
|
|
||||||
>
|
|
||||||
<a class="tab-link">
|
|
||||||
<i class="mdi mdi-18px mdi-code-tags mr-1" />
|
|
||||||
<span>
|
|
||||||
Query #{{ tab.index }}
|
|
||||||
<span
|
|
||||||
v-if="queryTabs.length > 1"
|
|
||||||
class="btn btn-clear"
|
|
||||||
:title="$t('word.close')"
|
|
||||||
@click.stop="closeTab(tab.uid)"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="tab-item">
|
|
||||||
<a
|
<a
|
||||||
class="tab-add"
|
class="tab-add"
|
||||||
:title="$t('message.openNewTab')"
|
:title="$t('message.openNewTab')"
|
||||||
@click="addTab"
|
@click="addQueryTab"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-plus" />
|
<i class="mdi mdi-24px mdi-plus" />
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</Draggable>
|
||||||
<WorkspacePropsTab
|
<WorkspaceEmptyState v-if="!workspace.tabs.length" @new-tab="addQueryTab" />
|
||||||
v-show="selectedTab === 'prop' && workspace.breadcrumbs.table"
|
<template v-for="tab of workspace.tabs">
|
||||||
:is-selected="selectedTab === 'prop'"
|
|
||||||
:connection="connection"
|
|
||||||
:table="workspace.breadcrumbs.table"
|
|
||||||
/>
|
|
||||||
<WorkspacePropsTabView
|
|
||||||
v-show="selectedTab === 'prop' && workspace.breadcrumbs.view"
|
|
||||||
:is-selected="selectedTab === 'prop'"
|
|
||||||
:connection="connection"
|
|
||||||
:view="workspace.breadcrumbs.view"
|
|
||||||
/>
|
|
||||||
<WorkspacePropsTabTrigger
|
|
||||||
v-show="selectedTab === 'prop' && workspace.breadcrumbs.trigger"
|
|
||||||
:is-selected="selectedTab === 'prop'"
|
|
||||||
:connection="connection"
|
|
||||||
:trigger="workspace.breadcrumbs.trigger"
|
|
||||||
/>
|
|
||||||
<WorkspacePropsTabRoutine
|
|
||||||
v-show="selectedTab === 'prop' && workspace.breadcrumbs.procedure"
|
|
||||||
:is-selected="selectedTab === 'prop'"
|
|
||||||
:connection="connection"
|
|
||||||
:routine="workspace.breadcrumbs.procedure"
|
|
||||||
/>
|
|
||||||
<WorkspacePropsTabFunction
|
|
||||||
v-show="selectedTab === 'prop' && workspace.breadcrumbs.function"
|
|
||||||
:is-selected="selectedTab === 'prop'"
|
|
||||||
:connection="connection"
|
|
||||||
:function="workspace.breadcrumbs.function"
|
|
||||||
/>
|
|
||||||
<WorkspacePropsTabScheduler
|
|
||||||
v-show="selectedTab === 'prop' && workspace.breadcrumbs.scheduler"
|
|
||||||
:is-selected="selectedTab === 'prop'"
|
|
||||||
:connection="connection"
|
|
||||||
:scheduler="workspace.breadcrumbs.scheduler"
|
|
||||||
/>
|
|
||||||
<WorkspaceTableTab
|
|
||||||
v-show="selectedTab === 'data'"
|
|
||||||
:connection="connection"
|
|
||||||
:table="workspace.breadcrumbs.table || workspace.breadcrumbs.view"
|
|
||||||
/>
|
|
||||||
<WorkspaceQueryTab
|
<WorkspaceQueryTab
|
||||||
v-for="tab of queryTabs"
|
v-if="tab.type==='query'"
|
||||||
:key="tab.uid"
|
:key="tab.uid"
|
||||||
:tab="tab"
|
:tab="tab"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
/>
|
/>
|
||||||
|
<WorkspaceTableTab
|
||||||
|
v-else-if="['temp-data', 'data'].includes(tab.type)"
|
||||||
|
:key="tab.uid"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:table="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
:element-type="tab.elementType"
|
||||||
|
/>
|
||||||
|
<WorkspacePropsTab
|
||||||
|
v-else-if="tab.type === 'table-props'"
|
||||||
|
:key="tab.uid"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:table="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspacePropsTabView
|
||||||
|
v-else-if="tab.type === 'view-props'"
|
||||||
|
:key="tab.uid"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:connection="connection"
|
||||||
|
:view="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspacePropsTabTrigger
|
||||||
|
v-else-if="['temp-trigger-props', 'trigger-props'].includes(tab.type)"
|
||||||
|
:key="tab.uid"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:trigger="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspacePropsTabTriggerFunction
|
||||||
|
v-else-if="['temp-trigger-function-props', 'trigger-function-props'].includes(tab.type)"
|
||||||
|
:key="tab.uid"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:function="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspacePropsTabRoutine
|
||||||
|
v-else-if="['temp-routine-props', 'routine-props'].includes(tab.type)"
|
||||||
|
:key="tab.uid"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:routine="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspacePropsTabFunction
|
||||||
|
v-else-if="['temp-function-props', 'function-props'].includes(tab.type)"
|
||||||
|
:key="tab.uid"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:function="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspacePropsTabScheduler
|
||||||
|
v-else-if="['temp-scheduler-props', 'scheduler-props'].includes(tab.type)"
|
||||||
|
:key="tab.uid"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:scheduler="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
<WorkspaceEditConnectionPanel v-else :connection="connection" />
|
||||||
<ModalProcessesList
|
<ModalProcessesList
|
||||||
v-if="isProcessesModal"
|
v-if="isProcessesModal"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
@close="hideProcessesModal"
|
@close="hideProcessesModal"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ModalDiscardChanges
|
||||||
|
v-if="unsavedTab"
|
||||||
|
@confirm="closeTab(unsavedTab, true)"
|
||||||
|
@close="unsavedTab = null"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import Draggable from 'vuedraggable';
|
||||||
import Connection from '@/ipc-api/Connection';
|
import Connection from '@/ipc-api/Connection';
|
||||||
|
import WorkspaceEmptyState from '@/components/WorkspaceEmptyState';
|
||||||
import WorkspaceExploreBar from '@/components/WorkspaceExploreBar';
|
import WorkspaceExploreBar from '@/components/WorkspaceExploreBar';
|
||||||
|
import WorkspaceEditConnectionPanel from '@/components/WorkspaceEditConnectionPanel';
|
||||||
import WorkspaceQueryTab from '@/components/WorkspaceQueryTab';
|
import WorkspaceQueryTab from '@/components/WorkspaceQueryTab';
|
||||||
import WorkspaceTableTab from '@/components/WorkspaceTableTab';
|
import WorkspaceTableTab from '@/components/WorkspaceTableTab';
|
||||||
import WorkspacePropsTab from '@/components/WorkspacePropsTab';
|
import WorkspacePropsTab from '@/components/WorkspacePropsTab';
|
||||||
import WorkspacePropsTabView from '@/components/WorkspacePropsTabView';
|
import WorkspacePropsTabView from '@/components/WorkspacePropsTabView';
|
||||||
import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger';
|
import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger';
|
||||||
|
import WorkspacePropsTabTriggerFunction from '@/components/WorkspacePropsTabTriggerFunction';
|
||||||
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';
|
import ModalProcessesList from '@/components/ModalProcessesList';
|
||||||
|
import ModalDiscardChanges from '@/components/ModalDiscardChanges';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Workspace',
|
name: 'Workspace',
|
||||||
components: {
|
components: {
|
||||||
|
Draggable,
|
||||||
|
WorkspaceEmptyState,
|
||||||
WorkspaceExploreBar,
|
WorkspaceExploreBar,
|
||||||
|
WorkspaceEditConnectionPanel,
|
||||||
WorkspaceQueryTab,
|
WorkspaceQueryTab,
|
||||||
WorkspaceTableTab,
|
WorkspaceTableTab,
|
||||||
WorkspacePropsTab,
|
WorkspacePropsTab,
|
||||||
WorkspacePropsTabView,
|
WorkspacePropsTabView,
|
||||||
WorkspacePropsTabTrigger,
|
WorkspacePropsTabTrigger,
|
||||||
|
WorkspacePropsTabTriggerFunction,
|
||||||
WorkspacePropsTabRoutine,
|
WorkspacePropsTabRoutine,
|
||||||
WorkspacePropsTabFunction,
|
WorkspacePropsTabFunction,
|
||||||
WorkspacePropsTabScheduler,
|
WorkspacePropsTabScheduler,
|
||||||
ModalProcessesList
|
ModalProcessesList,
|
||||||
|
ModalDiscardChanges
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
cutText (string) {
|
||||||
|
const limit = 20;
|
||||||
|
const escapedString = string.replace(/\s{2,}/g, ' ');
|
||||||
|
if (escapedString.length > limit)
|
||||||
|
return `${escapedString.substr(0, limit)}...`;
|
||||||
|
return escapedString;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object
|
connection: Object
|
||||||
@@ -188,7 +328,8 @@ export default {
|
|||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
hasWheelEvent: false,
|
hasWheelEvent: false,
|
||||||
isProcessesModal: false
|
isProcessesModal: false,
|
||||||
|
unsavedTab: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -199,6 +340,14 @@ export default {
|
|||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
},
|
},
|
||||||
|
draggableTabs: {
|
||||||
|
get () {
|
||||||
|
return this.workspace.tabs;
|
||||||
|
},
|
||||||
|
set (val) {
|
||||||
|
this.updateTabs({ uid: this.connection.uid, tabs: val });
|
||||||
|
}
|
||||||
|
},
|
||||||
isSelected () {
|
isSelected () {
|
||||||
return this.selectedWorkspace === this.connection.uid;
|
return this.selectedWorkspace === this.connection.uid;
|
||||||
},
|
},
|
||||||
@@ -208,32 +357,12 @@ export default {
|
|||||||
if (this.workspace.breadcrumbs.trigger && this.workspace.customizations.triggerSettings) return true;
|
if (this.workspace.breadcrumbs.trigger && this.workspace.customizations.triggerSettings) return true;
|
||||||
if (this.workspace.breadcrumbs.procedure && this.workspace.customizations.routineSettings) return true;
|
if (this.workspace.breadcrumbs.procedure && this.workspace.customizations.routineSettings) return true;
|
||||||
if (this.workspace.breadcrumbs.function && this.workspace.customizations.functionSettings) return true;
|
if (this.workspace.breadcrumbs.function && this.workspace.customizations.functionSettings) return true;
|
||||||
|
if (this.workspace.breadcrumbs.triggerFunction && this.workspace.customizations.functionSettings) return true;
|
||||||
if (this.workspace.breadcrumbs.scheduler && this.workspace.customizations.schedulerSettings) return true;
|
if (this.workspace.breadcrumbs.scheduler && this.workspace.customizations.schedulerSettings) return true;
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
selectedTab () {
|
selectedTab () {
|
||||||
if (
|
return this.workspace ? this.workspace.selectedTab : null;
|
||||||
(
|
|
||||||
this.workspace.breadcrumbs.table === null &&
|
|
||||||
this.workspace.breadcrumbs.view === null &&
|
|
||||||
this.workspace.breadcrumbs.trigger === null &&
|
|
||||||
this.workspace.breadcrumbs.procedure === null &&
|
|
||||||
this.workspace.breadcrumbs.function === null &&
|
|
||||||
this.workspace.breadcrumbs.scheduler === null &&
|
|
||||||
['data', 'prop'].includes(this.workspace.selected_tab)
|
|
||||||
) ||
|
|
||||||
(
|
|
||||||
this.workspace.breadcrumbs.table === null &&
|
|
||||||
this.workspace.breadcrumbs.view === null &&
|
|
||||||
this.workspace.selected_tab === 'data'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return this.queryTabs[0].uid;
|
|
||||||
|
|
||||||
return this.queryTabs.find(tab => tab.uid === this.workspace.selected_tab) ||
|
|
||||||
['data', 'prop'].includes(this.workspace.selected_tab)
|
|
||||||
? this.workspace.selected_tab
|
|
||||||
: this.queryTabs[0].uid;
|
|
||||||
},
|
},
|
||||||
queryTabs () {
|
queryTabs () {
|
||||||
return this.workspace.tabs.filter(tab => tab.type === 'query');
|
return this.workspace.tabs.filter(tab => tab.type === 'query');
|
||||||
@@ -246,20 +375,26 @@ export default {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
selectedTab (newVal, oldVal) {
|
||||||
|
if (newVal !== oldVal) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const element = this.$refs['tab-selected'] ? this.$refs['tab-selected'][0] : null;
|
||||||
|
if (element) {
|
||||||
|
element.setAttribute('tabindex', '-1');
|
||||||
|
element.focus();
|
||||||
|
element.removeAttribute('tabindex');
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
async created () {
|
async created () {
|
||||||
await this.addWorkspace(this.connection.uid);
|
await this.addWorkspace(this.connection.uid);
|
||||||
const isInitiated = await Connection.checkConnection(this.connection.uid);
|
const isInitiated = await Connection.checkConnection(this.connection.uid);
|
||||||
if (isInitiated)
|
if (isInitiated)
|
||||||
this.connectWorkspace(this.connection);
|
this.connectWorkspace(this.connection);
|
||||||
},
|
},
|
||||||
mounted () {
|
|
||||||
if (this.$refs.tabWrap) {
|
|
||||||
this.$refs.tabWrap.addEventListener('wheel', e => {
|
|
||||||
if (e.deltaY > 0) this.$refs.tabWrap.scrollLeft += 50;
|
|
||||||
else this.$refs.tabWrap.scrollLeft -= 50;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
addWorkspace: 'workspaces/addWorkspace',
|
addWorkspace: 'workspaces/addWorkspace',
|
||||||
@@ -267,28 +402,55 @@ export default {
|
|||||||
removeConnected: 'workspaces/removeConnected',
|
removeConnected: 'workspaces/removeConnected',
|
||||||
selectTab: 'workspaces/selectTab',
|
selectTab: 'workspaces/selectTab',
|
||||||
newTab: 'workspaces/newTab',
|
newTab: 'workspaces/newTab',
|
||||||
removeTab: 'workspaces/removeTab'
|
removeTab: 'workspaces/removeTab',
|
||||||
|
updateTabs: 'workspaces/updateTabs'
|
||||||
}),
|
}),
|
||||||
addTab () {
|
addQueryTab () {
|
||||||
this.newTab({ uid: this.connection.uid });
|
this.newTab({ uid: this.connection.uid, type: 'query' });
|
||||||
|
|
||||||
if (!this.hasWheelEvent) {
|
|
||||||
this.$refs.tabWrap.addEventListener('wheel', e => {
|
|
||||||
if (e.deltaY > 0) this.$refs.tabWrap.scrollLeft += 50;
|
|
||||||
else this.$refs.tabWrap.scrollLeft -= 50;
|
|
||||||
});
|
|
||||||
this.hasWheelEvent = true;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
closeTab (tUid) {
|
openAsPermanentTab (tab) {
|
||||||
if (this.queryTabs.length === 1) return;
|
const permanentTabs = {
|
||||||
this.removeTab({ uid: this.connection.uid, tab: tUid });
|
table: 'data',
|
||||||
|
view: 'data',
|
||||||
|
trigger: 'trigger-props',
|
||||||
|
triggerFunction: 'trigger-function-props',
|
||||||
|
function: 'function-props',
|
||||||
|
routine: 'routine-props',
|
||||||
|
scheduler: 'scheduler-props'
|
||||||
|
};
|
||||||
|
|
||||||
|
this.newTab({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: tab.schema,
|
||||||
|
elementName: tab.elementName,
|
||||||
|
type: permanentTabs[tab.elementType],
|
||||||
|
elementType: tab.elementType
|
||||||
|
});
|
||||||
|
},
|
||||||
|
closeTab (tab, force) {
|
||||||
|
this.unsavedTab = null;
|
||||||
|
// if (tab.type === 'query' && this.queryTabs.length === 1) return;
|
||||||
|
if (!force && tab.isChanged) {
|
||||||
|
this.unsavedTab = tab;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.removeTab({ uid: this.connection.uid, tab: tab.uid });
|
||||||
},
|
},
|
||||||
showProcessesModal () {
|
showProcessesModal () {
|
||||||
this.isProcessesModal = true;
|
this.isProcessesModal = true;
|
||||||
},
|
},
|
||||||
hideProcessesModal () {
|
hideProcessesModal () {
|
||||||
this.isProcessesModal = false;
|
this.isProcessesModal = false;
|
||||||
|
},
|
||||||
|
addWheelEvent () {
|
||||||
|
if (!this.hasWheelEvent) {
|
||||||
|
this.$refs.tabWrap.$el.addEventListener('wheel', e => {
|
||||||
|
if (e.deltaY > 0) this.$refs.tabWrap.$el.scrollLeft += 50;
|
||||||
|
else this.$refs.tabWrap.$el.scrollLeft -= 50;
|
||||||
|
});
|
||||||
|
this.hasWheelEvent = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -317,20 +479,35 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tab-item {
|
.tab-item {
|
||||||
max-width: 12rem;
|
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
flex: initial;
|
flex: initial;
|
||||||
|
|
||||||
> a {
|
> a {
|
||||||
padding: 0.2rem 0.8rem;
|
padding: 0.2rem 0.6rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
|
|
||||||
|
&.badge::after {
|
||||||
|
position: absolute;
|
||||||
|
right: 35px;
|
||||||
|
top: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-clear {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
||||||
|
.btn-clear {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.tab-add {
|
&.tab-add {
|
||||||
@@ -349,9 +526,15 @@ export default {
|
|||||||
|
|
||||||
&.active a {
|
&.active a {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
||||||
|
.btn-clear {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.tools-dropdown {
|
&.tools-dropdown {
|
||||||
|
height: 34px;
|
||||||
|
|
||||||
.tab-link:focus {
|
.tab-link:focus {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
@@ -362,7 +545,7 @@ export default {
|
|||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
|
|
||||||
.menu-item a {
|
.menu-item a {
|
||||||
border-radius: 0.1rem;
|
border-radius: $border-radius;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0 -0.4rem;
|
margin: 0 -0.4rem;
|
||||||
|
|||||||
519
src/renderer/components/WorkspaceAddConnectionPanel.vue
Normal file
@@ -0,0 +1,519 @@
|
|||||||
|
<template>
|
||||||
|
<div class="connection-panel">
|
||||||
|
<div class="panel">
|
||||||
|
<div class="panel-nav">
|
||||||
|
<ul class="tab tab-block">
|
||||||
|
<li
|
||||||
|
class="tab-item c-hand"
|
||||||
|
:class="{'active': selectedTab === 'general'}"
|
||||||
|
@click="selectTab('general')"
|
||||||
|
>
|
||||||
|
<a class="tab-link">{{ $t('word.general') }}</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="tab-item c-hand"
|
||||||
|
:class="{'active': selectedTab === 'ssl'}"
|
||||||
|
@click="selectTab('ssl')"
|
||||||
|
>
|
||||||
|
<a class="tab-link">{{ $t('word.ssl') }}</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="tab-item c-hand"
|
||||||
|
:class="{'active': selectedTab === 'ssh'}"
|
||||||
|
@click="selectTab('ssh')"
|
||||||
|
>
|
||||||
|
<a class="tab-link">{{ $t('word.sshTunnel') }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedTab === 'general'" class="panel-body py-0">
|
||||||
|
<div>
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<fieldset class="m-0" :disabled="isBusy">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.connectionName') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="connection.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.client') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<select v-model="connection.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 columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.host"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.port') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column 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 columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.database') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.database"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.user') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.user"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
:disabled="connection.ask"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.password') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.password"
|
||||||
|
class="form-input"
|
||||||
|
type="password"
|
||||||
|
:disabled="connection.ask"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.connectionSchema" class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.schema') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.schema"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
:placeholder="$t('word.all')"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12" />
|
||||||
|
<div class="column 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>
|
||||||
|
<div v-if="selectedTab === 'ssl'" class="panel-body py-0">
|
||||||
|
<div>
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('message.enableSsl') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="column 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>
|
||||||
|
<fieldset class="m-0" :disabled="isBusy || !connection.ssl">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.privateKey') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<BaseUploadInput
|
||||||
|
:value="connection.key"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="pathClear('key')"
|
||||||
|
@change="pathSelection($event, 'key')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.certificate') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<BaseUploadInput
|
||||||
|
:value="connection.cert"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="pathClear('cert')"
|
||||||
|
@change="pathSelection($event, 'cert')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.caCertificate') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<BaseUploadInput
|
||||||
|
:value="connection.ca"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="pathClear('ca')"
|
||||||
|
@change="pathSelection($event, 'ca')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.ciphers') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="connection.ciphers"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedTab === 'ssh'" class="panel-body py-0">
|
||||||
|
<div>
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('message.enableSsh') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<label class="form-switch d-inline-block" @click.prevent="toggleSsh">
|
||||||
|
<input type="checkbox" :checked="connection.ssh">
|
||||||
|
<i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fieldset class="m-0" :disabled="isBusy || !connection.ssh">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.sshHost"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.user') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.sshUser"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.password') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.sshPass"
|
||||||
|
class="form-input"
|
||||||
|
type="password"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.port') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.sshPort"
|
||||||
|
class="form-input"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="65535"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.privateKey') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<BaseUploadInput
|
||||||
|
:value="connection.sshKey"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="pathClear('sshKey')"
|
||||||
|
@change="pathSelection($event, 'sshKey')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer">
|
||||||
|
<button
|
||||||
|
class="btn btn-gray mr-2 d-flex"
|
||||||
|
:class="{'loading': isTesting}"
|
||||||
|
:disabled="isBusy"
|
||||||
|
@click="startTest"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-lightning-bolt mr-1" />
|
||||||
|
{{ $t('message.testConnection') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-primary mr-2 d-flex"
|
||||||
|
:disabled="isBusy"
|
||||||
|
@click="saveConnection"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
|
{{ $t('word.save') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ModalAskCredentials
|
||||||
|
v-if="isAsking"
|
||||||
|
@close-asking="closeAsking"
|
||||||
|
@credentials="continueTest"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapActions } from 'vuex';
|
||||||
|
import customizations from 'common/customizations';
|
||||||
|
import Connection from '@/ipc-api/Connection';
|
||||||
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
|
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
||||||
|
import BaseUploadInput from '@/components/BaseUploadInput';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspaceAddConnectionPanel',
|
||||||
|
components: {
|
||||||
|
ModalAskCredentials,
|
||||||
|
BaseUploadInput
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
connection: {
|
||||||
|
name: '',
|
||||||
|
client: 'mysql',
|
||||||
|
host: '127.0.0.1',
|
||||||
|
database: null,
|
||||||
|
port: null,
|
||||||
|
user: null,
|
||||||
|
password: '',
|
||||||
|
ask: false,
|
||||||
|
uid: uidGen('C'),
|
||||||
|
ssl: false,
|
||||||
|
cert: '',
|
||||||
|
key: '',
|
||||||
|
ca: '',
|
||||||
|
ciphers: '',
|
||||||
|
ssh: false,
|
||||||
|
sshHost: '',
|
||||||
|
sshUser: '',
|
||||||
|
sshPass: '',
|
||||||
|
sshKey: '',
|
||||||
|
sshPort: 22
|
||||||
|
},
|
||||||
|
isConnecting: false,
|
||||||
|
isTesting: false,
|
||||||
|
isAsking: false,
|
||||||
|
selectedTab: 'general'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
customizations () {
|
||||||
|
return customizations[this.connection.client];
|
||||||
|
},
|
||||||
|
isBusy () {
|
||||||
|
return this.isConnecting || this.isTesting;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.setDefaults();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.$refs.firstInput) this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addConnection: 'connections/addConnection',
|
||||||
|
connectWorkspace: 'workspaces/connectWorkspace',
|
||||||
|
addNotification: 'notifications/addNotification',
|
||||||
|
selectWorkspace: 'workspaces/selectWorkspace'
|
||||||
|
}),
|
||||||
|
setDefaults () {
|
||||||
|
this.connection.user = this.customizations.defaultUser;
|
||||||
|
this.connection.port = this.customizations.defaultPort;
|
||||||
|
this.connection.database = this.customizations.defaultDatabase;
|
||||||
|
},
|
||||||
|
async startConnection () {
|
||||||
|
await this.saveConnection();
|
||||||
|
this.isConnecting = true;
|
||||||
|
|
||||||
|
if (this.connection.ask)
|
||||||
|
this.isAsking = true;
|
||||||
|
else {
|
||||||
|
await this.connectWorkspace(this.connection);
|
||||||
|
this.isConnecting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async startTest () {
|
||||||
|
this.isTesting = true;
|
||||||
|
|
||||||
|
if (this.connection.ask)
|
||||||
|
this.isAsking = true;
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
const res = await Connection.makeTest(this.connection);
|
||||||
|
if (res.status === 'error')
|
||||||
|
this.addNotification({ status: 'error', message: res.response.message });
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'success', message: this.$t('message.connectionSuccessfullyMade') });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isTesting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async continueTest (credentials) { // if "Ask for credentials" is true
|
||||||
|
this.isAsking = false;
|
||||||
|
const params = Object.assign({}, this.connection, credentials);
|
||||||
|
try {
|
||||||
|
if (this.isConnecting) {
|
||||||
|
const params = Object.assign({}, this.connection, credentials);
|
||||||
|
await this.connectWorkspace(params);
|
||||||
|
this.isConnecting = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const res = await Connection.makeTest(params);
|
||||||
|
if (res.status === 'error')
|
||||||
|
this.addNotification({ status: 'error', message: res.response.message });
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'success', message: this.$t('message.connectionSuccessfullyMade') });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isTesting = false;
|
||||||
|
},
|
||||||
|
saveConnection () {
|
||||||
|
this.selectWorkspace(this.connection.uid);
|
||||||
|
return this.addConnection(this.connection);
|
||||||
|
},
|
||||||
|
closeAsking () {
|
||||||
|
this.isTesting = false;
|
||||||
|
this.isAsking = false;
|
||||||
|
},
|
||||||
|
selectTab (tab) {
|
||||||
|
this.selectedTab = tab;
|
||||||
|
},
|
||||||
|
toggleSsl () {
|
||||||
|
this.connection.ssl = !this.connection.ssl;
|
||||||
|
},
|
||||||
|
toggleSsh () {
|
||||||
|
this.connection.ssh = !this.connection.ssh;
|
||||||
|
},
|
||||||
|
pathSelection (event, name) {
|
||||||
|
const { files } = event.target;
|
||||||
|
if (!files.length) return;
|
||||||
|
|
||||||
|
this.connection[name] = files[0].path;
|
||||||
|
},
|
||||||
|
pathClear (name) {
|
||||||
|
this.connection[name] = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.connection-panel {
|
||||||
|
margin-top: 15vh;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
width: 450px;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
|
||||||
|
.panel-body {
|
||||||
|
flex: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="columns">
|
|
||||||
<div class="column col-12 empty">
|
|
||||||
<div class="empty-icon">
|
|
||||||
<i class="mdi mdi-48px mdi-power-plug-off" />
|
|
||||||
</div>
|
|
||||||
<p class="empty-title h5">
|
|
||||||
{{ isConnecting ? $t('word.connecting') : $t('word.disconnected') }}
|
|
||||||
</p>
|
|
||||||
<div class="empty-action">
|
|
||||||
<button
|
|
||||||
class="btn btn-success"
|
|
||||||
:class="{'loading': isConnecting}"
|
|
||||||
@click="startConnection"
|
|
||||||
>
|
|
||||||
{{ $t('word.connect') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ModalAskCredentials
|
|
||||||
v-if="isAsking"
|
|
||||||
@close-asking="closeAsking"
|
|
||||||
@credentials="continueTest"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapActions } from 'vuex';
|
|
||||||
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'WorkspaceConnectPanel',
|
|
||||||
components: {
|
|
||||||
ModalAskCredentials
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
connection: Object
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
isConnecting: false,
|
|
||||||
isAsking: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions({
|
|
||||||
connectWorkspace: 'workspaces/connectWorkspace'
|
|
||||||
}),
|
|
||||||
async startConnection () {
|
|
||||||
this.isConnecting = true;
|
|
||||||
|
|
||||||
if (this.connection.ask)
|
|
||||||
this.isAsking = true;
|
|
||||||
else {
|
|
||||||
await this.connectWorkspace(this.connection);
|
|
||||||
this.isConnecting = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async continueTest (credentials) { // if "Ask for credentials" is true
|
|
||||||
this.isAsking = false;
|
|
||||||
const params = Object.assign({}, this.connection, credentials);
|
|
||||||
await this.connectWorkspace(params);
|
|
||||||
this.isConnecting = false;
|
|
||||||
},
|
|
||||||
closeAsking () {
|
|
||||||
this.isAsking = false;
|
|
||||||
this.isConnecting = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.empty {
|
|
||||||
height: 100%;
|
|
||||||
border-radius: 0;
|
|
||||||
background: transparent;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
500
src/renderer/components/WorkspaceEditConnectionPanel.vue
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
<template>
|
||||||
|
<div class="connection-panel">
|
||||||
|
<div class="panel">
|
||||||
|
<div class="panel-nav">
|
||||||
|
<ul class="tab tab-block">
|
||||||
|
<li
|
||||||
|
class="tab-item c-hand"
|
||||||
|
:class="{'active': selectedTab === 'general'}"
|
||||||
|
@click="selectTab('general')"
|
||||||
|
>
|
||||||
|
<a class="tab-link">{{ $t('word.general') }}</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="tab-item c-hand"
|
||||||
|
:class="{'active': selectedTab === 'ssl'}"
|
||||||
|
@click="selectTab('ssl')"
|
||||||
|
>
|
||||||
|
<a class="tab-link">{{ $t('word.ssl') }}</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="tab-item c-hand"
|
||||||
|
:class="{'active': selectedTab === 'ssh'}"
|
||||||
|
@click="selectTab('ssh')"
|
||||||
|
>
|
||||||
|
<a class="tab-link">{{ $t('word.sshTunnel') }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedTab === 'general'" class="panel-body py-0">
|
||||||
|
<div>
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<fieldset class="m-0" :disabled="isBusy">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.connectionName') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="localConnection.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.client') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column 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>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.host"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.port') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column 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 columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.database') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.database"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.user') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.user"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
:disabled="localConnection.ask"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.password') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.password"
|
||||||
|
class="form-input"
|
||||||
|
type="password"
|
||||||
|
:disabled="localConnection.ask"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.connectionSchema" class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.schema') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.schema"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
:placeholder="$t('word.all')"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12" />
|
||||||
|
<div class="column 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>
|
||||||
|
<div v-if="selectedTab === 'ssl'" class="panel-body py-0">
|
||||||
|
<div>
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('message.enableSsl') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="column 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>
|
||||||
|
<fieldset class="m-0" :disabled="isBusy || !localConnection.ssl">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.privateKey') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<BaseUploadInput
|
||||||
|
:value="localConnection.key"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="pathClear('key')"
|
||||||
|
@change="pathSelection($event, 'key')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.certificate') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<BaseUploadInput
|
||||||
|
:value="localConnection.cert"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="pathClear('cert')"
|
||||||
|
@change="pathSelection($event, 'cert')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.caCertificate') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<BaseUploadInput
|
||||||
|
:value="localConnection.ca"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="pathClear('ca')"
|
||||||
|
@change="pathSelection($event, 'ca')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.ciphers') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="localConnection.ciphers"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedTab === 'ssh'" class="panel-body py-0">
|
||||||
|
<div>
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('message.enableSsh') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<label class="form-switch d-inline-block" @click.prevent="toggleSsh">
|
||||||
|
<input type="checkbox" :checked="localConnection.ssh">
|
||||||
|
<i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fieldset class="m-0" :disabled="isBusy || !localConnection.ssh">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.sshHost"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.user') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.sshUser"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.password') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.sshPass"
|
||||||
|
class="form-input"
|
||||||
|
type="password"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.port') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.sshPort"
|
||||||
|
class="form-input"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="65535"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.privateKey') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<BaseUploadInput
|
||||||
|
:value="localConnection.sshKey"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="pathClear('sshKey')"
|
||||||
|
@change="pathSelection($event, 'sshKey')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer">
|
||||||
|
<button
|
||||||
|
class="btn btn-gray mr-2 d-flex"
|
||||||
|
:class="{'loading': isTesting}"
|
||||||
|
:disabled="isBusy"
|
||||||
|
@click="startTest"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-lightning-bolt mr-1" />
|
||||||
|
{{ $t('message.testConnection') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-primary mr-2 d-flex"
|
||||||
|
:disabled="isBusy || !hasChanges"
|
||||||
|
@click="saveConnection"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
|
{{ $t('word.save') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-success d-flex"
|
||||||
|
:class="{'loading': isConnecting}"
|
||||||
|
:disabled="isBusy"
|
||||||
|
@click="startConnection"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-connection mr-1" />
|
||||||
|
{{ $t('word.connect') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ModalAskCredentials
|
||||||
|
v-if="isAsking"
|
||||||
|
@close-asking="closeAsking"
|
||||||
|
@credentials="continueTest"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapActions } from 'vuex';
|
||||||
|
import customizations from 'common/customizations';
|
||||||
|
import Connection from '@/ipc-api/Connection';
|
||||||
|
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
||||||
|
import BaseUploadInput from '@/components/BaseUploadInput';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspaceEditConnectionPanel',
|
||||||
|
components: {
|
||||||
|
ModalAskCredentials,
|
||||||
|
BaseUploadInput
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
connection: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isConnecting: false,
|
||||||
|
isTesting: false,
|
||||||
|
isAsking: false,
|
||||||
|
localConnection: null,
|
||||||
|
selectedTab: 'general'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
customizations () {
|
||||||
|
return customizations[this.connection.client];
|
||||||
|
},
|
||||||
|
isBusy () {
|
||||||
|
return this.isConnecting || this.isTesting;
|
||||||
|
},
|
||||||
|
hasChanges () {
|
||||||
|
return JSON.stringify(this.connection) !== JSON.stringify(this.localConnection);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
connection () {
|
||||||
|
this.localConnection = JSON.parse(JSON.stringify(this.connection));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.localConnection = JSON.parse(JSON.stringify(this.connection));
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
editConnection: 'connections/editConnection',
|
||||||
|
connectWorkspace: 'workspaces/connectWorkspace',
|
||||||
|
addNotification: 'notifications/addNotification'
|
||||||
|
}),
|
||||||
|
async startConnection () {
|
||||||
|
await this.saveConnection();
|
||||||
|
this.isConnecting = true;
|
||||||
|
|
||||||
|
if (this.localConnection.ask)
|
||||||
|
this.isAsking = true;
|
||||||
|
else {
|
||||||
|
await this.connectWorkspace(this.localConnection);
|
||||||
|
this.isConnecting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async startTest () {
|
||||||
|
this.isTesting = true;
|
||||||
|
|
||||||
|
if (this.localConnection.ask)
|
||||||
|
this.isAsking = true;
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
const res = await Connection.makeTest(this.localConnection);
|
||||||
|
if (res.status === 'error')
|
||||||
|
this.addNotification({ status: 'error', message: res.response.message });
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'success', message: this.$t('message.connectionSuccessfullyMade') });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isTesting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async continueTest (credentials) { // if "Ask for credentials" is true
|
||||||
|
this.isAsking = false;
|
||||||
|
const params = Object.assign({}, this.localConnection, credentials);
|
||||||
|
try {
|
||||||
|
if (this.isConnecting) {
|
||||||
|
const params = Object.assign({}, this.connection, credentials);
|
||||||
|
await this.connectWorkspace(params);
|
||||||
|
this.isConnecting = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const res = await Connection.makeTest(params);
|
||||||
|
if (res.status === 'error')
|
||||||
|
this.addNotification({ status: 'error', message: res.response.message });
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'success', message: this.$t('message.connectionSuccessfullyMade') });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isTesting = false;
|
||||||
|
},
|
||||||
|
saveConnection () {
|
||||||
|
return this.editConnection(this.localConnection);
|
||||||
|
},
|
||||||
|
closeAsking () {
|
||||||
|
this.isTesting = false;
|
||||||
|
this.isAsking = false;
|
||||||
|
},
|
||||||
|
selectTab (tab) {
|
||||||
|
this.selectedTab = tab;
|
||||||
|
},
|
||||||
|
toggleSsl () {
|
||||||
|
this.localConnection.ssl = !this.localConnection.ssl;
|
||||||
|
},
|
||||||
|
toggleSsh () {
|
||||||
|
this.localConnection.ssh = !this.localConnection.ssh;
|
||||||
|
},
|
||||||
|
pathSelection (event, name) {
|
||||||
|
const { files } = event.target;
|
||||||
|
if (!files.length) return;
|
||||||
|
|
||||||
|
this.localConnection[name] = files[0].path;
|
||||||
|
},
|
||||||
|
pathClear (name) {
|
||||||
|
this.localConnection[name] = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.connection-panel {
|
||||||
|
margin-top: 15vh;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
width: 450px;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
|
||||||
|
.panel-body {
|
||||||
|
flex: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
55
src/renderer/components/WorkspaceEmptyState.vue
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<div class="column col-12 empty">
|
||||||
|
<div class="empty-icon">
|
||||||
|
<img :src="require(`@/images/logo-${applicationTheme}.svg`).default" width="200">
|
||||||
|
</div>
|
||||||
|
<p class="h6 empty-subtitle">
|
||||||
|
{{ $t('message.noOpenTabs') }}
|
||||||
|
</p>
|
||||||
|
<div class="empty-action">
|
||||||
|
<button class="btn btn-gray d-flex" @click="$emit('new-tab')">
|
||||||
|
<i class="mdi mdi-24px mdi-tab-plus mr-2" />
|
||||||
|
{{ $t('message.openNewTab') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
export default {
|
||||||
|
name: 'WorkspaceEmptyState',
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
applicationTheme: 'settings/getApplicationTheme',
|
||||||
|
getWorkspace: 'workspaces/getWorkspace',
|
||||||
|
selectedWorkspace: 'workspaces/getSelected'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.selectedWorkspace);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.changeBreadcrumbs({ schema: this.workspace.breadcrumbs.schema });
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.empty {
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
>
|
>
|
||||||
<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.connection_status === 'connected'" class="workspace-explorebar-tools">
|
<span v-if="workspace.connectionStatus === '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.createNewSchema')"
|
:title="$t('message.createNewSchema')"
|
||||||
@@ -21,29 +21,29 @@
|
|||||||
@click="refresh"
|
@click="refresh"
|
||||||
/>
|
/>
|
||||||
<i
|
<i
|
||||||
class="mdi mdi-18px mdi-power-plug-off c-hand"
|
class="mdi mdi-18px mdi-power c-hand"
|
||||||
:title="$t('word.disconnect')"
|
:title="$t('word.disconnect')"
|
||||||
@click="disconnectWorkspace(connection.uid)"
|
@click="disconnectWorkspace(connection.uid)"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-explorebar-search">
|
<div class="workspace-explorebar-search">
|
||||||
<div v-if="workspace.connection_status === 'connected'" class="has-icon-right">
|
<div v-if="workspace.connectionStatus === 'connected'" class="has-icon-right">
|
||||||
<input
|
<input
|
||||||
v-model="searchTerm"
|
v-model="searchTerm"
|
||||||
class="form-input input-sm"
|
class="form-input input-sm"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="$t('message.searchForElements')"
|
:placeholder="$t('message.searchForElements')"
|
||||||
>
|
>
|
||||||
<i class="form-icon mdi mdi-magnify mdi-18px" />
|
<i v-if="!searchTerm" class="form-icon mdi mdi-magnify mdi-18px" />
|
||||||
</div>
|
<i
|
||||||
</div>
|
v-else
|
||||||
<WorkspaceConnectPanel
|
class="form-icon c-hand mdi mdi-backspace mdi-18px pr-1"
|
||||||
v-if="workspace.connection_status !== 'connected'"
|
@click="searchTerm = ''"
|
||||||
class="workspace-explorebar-body"
|
|
||||||
:connection="connection"
|
|
||||||
/>
|
/>
|
||||||
<div v-else class="workspace-explorebar-body">
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="workspace-explorebar-body">
|
||||||
<WorkspaceExploreBarSchema
|
<WorkspaceExploreBarSchema
|
||||||
v-for="db of workspace.structure"
|
v-for="db of workspace.structure"
|
||||||
:key="db.name"
|
:key="db.name"
|
||||||
@@ -52,6 +52,7 @@
|
|||||||
@show-schema-context="openSchemaContext"
|
@show-schema-context="openSchemaContext"
|
||||||
@show-table-context="openTableContext"
|
@show-table-context="openTableContext"
|
||||||
@show-misc-context="openMiscContext"
|
@show-misc-context="openMiscContext"
|
||||||
|
@show-misc-folder-context="openMiscFolderContext"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -90,6 +91,12 @@
|
|||||||
@close="hideCreateFunctionModal"
|
@close="hideCreateFunctionModal"
|
||||||
@open-create-function-editor="openCreateFunctionEditor"
|
@open-create-function-editor="openCreateFunctionEditor"
|
||||||
/>
|
/>
|
||||||
|
<ModalNewTriggerFunction
|
||||||
|
v-if="isNewTriggerFunctionModal"
|
||||||
|
:workspace="workspace"
|
||||||
|
@close="hideCreateTriggerFunctionModal"
|
||||||
|
@open-create-function-editor="openCreateTriggerFunctionEditor"
|
||||||
|
/>
|
||||||
<ModalNewScheduler
|
<ModalNewScheduler
|
||||||
v-if="isNewSchedulerModal"
|
v-if="isNewSchedulerModal"
|
||||||
:workspace="workspace"
|
:workspace="workspace"
|
||||||
@@ -98,7 +105,7 @@
|
|||||||
/>
|
/>
|
||||||
<DatabaseContext
|
<DatabaseContext
|
||||||
v-if="isDatabaseContext"
|
v-if="isDatabaseContext"
|
||||||
:selected-database="selectedDatabase"
|
:selected-schema="selectedSchema"
|
||||||
:context-event="databaseContextEvent"
|
:context-event="databaseContextEvent"
|
||||||
@close-context="closeDatabaseContext"
|
@close-context="closeDatabaseContext"
|
||||||
@show-create-table-modal="showCreateTableModal"
|
@show-create-table-modal="showCreateTableModal"
|
||||||
@@ -106,11 +113,13 @@
|
|||||||
@show-create-trigger-modal="showCreateTriggerModal"
|
@show-create-trigger-modal="showCreateTriggerModal"
|
||||||
@show-create-routine-modal="showCreateRoutineModal"
|
@show-create-routine-modal="showCreateRoutineModal"
|
||||||
@show-create-function-modal="showCreateFunctionModal"
|
@show-create-function-modal="showCreateFunctionModal"
|
||||||
|
@show-create-trigger-function-modal="showCreateTriggerFunctionModal"
|
||||||
@show-create-scheduler-modal="showCreateSchedulerModal"
|
@show-create-scheduler-modal="showCreateSchedulerModal"
|
||||||
@reload="refresh"
|
@reload="refresh"
|
||||||
/>
|
/>
|
||||||
<TableContext
|
<TableContext
|
||||||
v-if="isTableContext"
|
v-show="isTableContext"
|
||||||
|
:selected-schema="selectedSchema"
|
||||||
:selected-table="selectedTable"
|
:selected-table="selectedTable"
|
||||||
:context-event="tableContextEvent"
|
:context-event="tableContextEvent"
|
||||||
@close-context="closeTableContext"
|
@close-context="closeTableContext"
|
||||||
@@ -119,10 +128,24 @@
|
|||||||
<MiscContext
|
<MiscContext
|
||||||
v-if="isMiscContext"
|
v-if="isMiscContext"
|
||||||
:selected-misc="selectedMisc"
|
:selected-misc="selectedMisc"
|
||||||
|
:selected-schema="selectedSchema"
|
||||||
:context-event="miscContextEvent"
|
:context-event="miscContextEvent"
|
||||||
@close-context="closeMiscContext"
|
@close-context="closeMiscContext"
|
||||||
@reload="refresh"
|
@reload="refresh"
|
||||||
/>
|
/>
|
||||||
|
<MiscFolderContext
|
||||||
|
v-if="isMiscFolderContext"
|
||||||
|
:selected-misc="selectedMisc"
|
||||||
|
:selected-schema="selectedSchema"
|
||||||
|
:context-event="miscContextEvent"
|
||||||
|
@show-create-trigger-modal="showCreateTriggerModal"
|
||||||
|
@show-create-routine-modal="showCreateRoutineModal"
|
||||||
|
@show-create-function-modal="showCreateFunctionModal"
|
||||||
|
@show-create-trigger-function-modal="showCreateTriggerFunctionModal"
|
||||||
|
@show-create-scheduler-modal="showCreateSchedulerModal"
|
||||||
|
@close-context="closeMiscFolderContext"
|
||||||
|
@reload="refresh"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -136,33 +159,35 @@ import Routines from '@/ipc-api/Routines';
|
|||||||
import Functions from '@/ipc-api/Functions';
|
import Functions from '@/ipc-api/Functions';
|
||||||
import Schedulers from '@/ipc-api/Schedulers';
|
import Schedulers from '@/ipc-api/Schedulers';
|
||||||
|
|
||||||
import WorkspaceConnectPanel from '@/components/WorkspaceConnectPanel';
|
|
||||||
import WorkspaceExploreBarSchema from '@/components/WorkspaceExploreBarSchema';
|
import WorkspaceExploreBarSchema from '@/components/WorkspaceExploreBarSchema';
|
||||||
import DatabaseContext from '@/components/WorkspaceExploreBarSchemaContext';
|
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 MiscFolderContext from '@/components/WorkspaceExploreBarMiscFolderContext';
|
||||||
import ModalNewSchema from '@/components/ModalNewSchema';
|
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';
|
||||||
import ModalNewRoutine from '@/components/ModalNewRoutine';
|
import ModalNewRoutine from '@/components/ModalNewRoutine';
|
||||||
import ModalNewFunction from '@/components/ModalNewFunction';
|
import ModalNewFunction from '@/components/ModalNewFunction';
|
||||||
|
import ModalNewTriggerFunction from '@/components/ModalNewTriggerFunction';
|
||||||
import ModalNewScheduler from '@/components/ModalNewScheduler';
|
import ModalNewScheduler from '@/components/ModalNewScheduler';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WorkspaceExploreBar',
|
name: 'WorkspaceExploreBar',
|
||||||
components: {
|
components: {
|
||||||
WorkspaceConnectPanel,
|
|
||||||
WorkspaceExploreBarSchema,
|
WorkspaceExploreBarSchema,
|
||||||
DatabaseContext,
|
DatabaseContext,
|
||||||
TableContext,
|
TableContext,
|
||||||
MiscContext,
|
MiscContext,
|
||||||
|
MiscFolderContext,
|
||||||
ModalNewSchema,
|
ModalNewSchema,
|
||||||
ModalNewTable,
|
ModalNewTable,
|
||||||
ModalNewView,
|
ModalNewView,
|
||||||
ModalNewTrigger,
|
ModalNewTrigger,
|
||||||
ModalNewRoutine,
|
ModalNewRoutine,
|
||||||
ModalNewFunction,
|
ModalNewFunction,
|
||||||
|
ModalNewTriggerFunction,
|
||||||
ModalNewScheduler
|
ModalNewScheduler
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
@@ -179,6 +204,7 @@ export default {
|
|||||||
isNewTriggerModal: false,
|
isNewTriggerModal: false,
|
||||||
isNewRoutineModal: false,
|
isNewRoutineModal: false,
|
||||||
isNewFunctionModal: false,
|
isNewFunctionModal: false,
|
||||||
|
isNewTriggerFunctionModal: false,
|
||||||
isNewSchedulerModal: false,
|
isNewSchedulerModal: false,
|
||||||
|
|
||||||
localWidth: null,
|
localWidth: null,
|
||||||
@@ -187,12 +213,13 @@ export default {
|
|||||||
isDatabaseContext: false,
|
isDatabaseContext: false,
|
||||||
isTableContext: false,
|
isTableContext: false,
|
||||||
isMiscContext: false,
|
isMiscContext: false,
|
||||||
|
isMiscFolderContext: false,
|
||||||
|
|
||||||
databaseContextEvent: null,
|
databaseContextEvent: null,
|
||||||
tableContextEvent: null,
|
tableContextEvent: null,
|
||||||
miscContextEvent: null,
|
miscContextEvent: null,
|
||||||
|
|
||||||
selectedDatabase: '',
|
selectedSchema: '',
|
||||||
selectedTable: null,
|
selectedTable: null,
|
||||||
selectedMisc: null,
|
selectedMisc: null,
|
||||||
searchTerm: ''
|
searchTerm: ''
|
||||||
@@ -209,6 +236,9 @@ export default {
|
|||||||
},
|
},
|
||||||
connectionName () {
|
connectionName () {
|
||||||
return this.getConnectionName(this.connection.uid);
|
return this.getConnectionName(this.connection.uid);
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace.customizations;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -249,6 +279,7 @@ export default {
|
|||||||
refreshStructure: 'workspaces/refreshStructure',
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
selectTab: 'workspaces/selectTab',
|
selectTab: 'workspaces/selectTab',
|
||||||
|
newTab: 'workspaces/newTab',
|
||||||
setSearchTerm: 'workspaces/setSearchTerm',
|
setSearchTerm: 'workspaces/setSearchTerm',
|
||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
changeExplorebarSize: 'settings/changeExplorebarSize'
|
changeExplorebarSize: 'settings/changeExplorebarSize'
|
||||||
@@ -286,6 +317,7 @@ export default {
|
|||||||
async openCreateTableEditor (payload) {
|
async openCreateTableEditor (payload) {
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
...payload
|
...payload
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -293,14 +325,19 @@ export default {
|
|||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
await this.refresh();
|
await this.refresh();
|
||||||
this.changeBreadcrumbs({ schema: this.selectedDatabase, table: payload.name });
|
this.newTab({
|
||||||
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
|
uid: this.workspace.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
elementName: payload.name,
|
||||||
|
elementType: 'table',
|
||||||
|
type: 'table-props'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
this.addNotification({ status: 'error', message: response });
|
||||||
},
|
},
|
||||||
openSchemaContext (payload) {
|
openSchemaContext (payload) {
|
||||||
this.selectedDatabase = payload.schema;
|
this.selectedSchema = payload.schema;
|
||||||
this.databaseContextEvent = payload.event;
|
this.databaseContextEvent = payload.event;
|
||||||
this.isDatabaseContext = true;
|
this.isDatabaseContext = true;
|
||||||
},
|
},
|
||||||
@@ -309,6 +346,7 @@ export default {
|
|||||||
},
|
},
|
||||||
openTableContext (payload) {
|
openTableContext (payload) {
|
||||||
this.selectedTable = payload.table;
|
this.selectedTable = payload.table;
|
||||||
|
this.selectedSchema = payload.schema;
|
||||||
this.tableContextEvent = payload.event;
|
this.tableContextEvent = payload.event;
|
||||||
this.isTableContext = true;
|
this.isTableContext = true;
|
||||||
},
|
},
|
||||||
@@ -317,14 +355,25 @@ export default {
|
|||||||
},
|
},
|
||||||
openMiscContext (payload) {
|
openMiscContext (payload) {
|
||||||
this.selectedMisc = payload.misc;
|
this.selectedMisc = payload.misc;
|
||||||
|
this.selectedSchema = payload.schema;
|
||||||
this.miscContextEvent = payload.event;
|
this.miscContextEvent = payload.event;
|
||||||
this.isMiscContext = true;
|
this.isMiscContext = true;
|
||||||
},
|
},
|
||||||
|
openMiscFolderContext (payload) {
|
||||||
|
this.selectedMisc = payload.type;
|
||||||
|
this.selectedSchema = payload.schema;
|
||||||
|
this.miscContextEvent = payload.event;
|
||||||
|
this.isMiscFolderContext = true;
|
||||||
|
},
|
||||||
closeMiscContext () {
|
closeMiscContext () {
|
||||||
this.isMiscContext = false;
|
this.isMiscContext = false;
|
||||||
},
|
},
|
||||||
|
closeMiscFolderContext () {
|
||||||
|
this.isMiscFolderContext = false;
|
||||||
|
},
|
||||||
showCreateViewModal () {
|
showCreateViewModal () {
|
||||||
this.closeDatabaseContext();
|
this.closeDatabaseContext();
|
||||||
|
this.closeMiscFolderContext();
|
||||||
this.isNewViewModal = true;
|
this.isNewViewModal = true;
|
||||||
},
|
},
|
||||||
hideCreateViewModal () {
|
hideCreateViewModal () {
|
||||||
@@ -333,6 +382,7 @@ export default {
|
|||||||
async openCreateViewEditor (payload) {
|
async openCreateViewEditor (payload) {
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
...payload
|
...payload
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -340,14 +390,22 @@ export default {
|
|||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
await this.refresh();
|
await this.refresh();
|
||||||
this.changeBreadcrumbs({ schema: this.selectedDatabase, view: payload.name });
|
this.changeBreadcrumbs({ schema: this.selectedSchema, view: payload.name });
|
||||||
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
|
|
||||||
|
this.newTab({
|
||||||
|
uid: this.workspace.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
elementName: payload.name,
|
||||||
|
elementType: 'view',
|
||||||
|
type: 'view-props'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
this.addNotification({ status: 'error', message: response });
|
||||||
},
|
},
|
||||||
showCreateTriggerModal () {
|
showCreateTriggerModal () {
|
||||||
this.closeDatabaseContext();
|
this.closeDatabaseContext();
|
||||||
|
this.closeMiscFolderContext();
|
||||||
this.isNewTriggerModal = true;
|
this.isNewTriggerModal = true;
|
||||||
},
|
},
|
||||||
hideCreateTriggerModal () {
|
hideCreateTriggerModal () {
|
||||||
@@ -356,6 +414,7 @@ export default {
|
|||||||
async openCreateTriggerEditor (payload) {
|
async openCreateTriggerEditor (payload) {
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
...payload
|
...payload
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -363,14 +422,23 @@ export default {
|
|||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
await this.refresh();
|
await this.refresh();
|
||||||
this.changeBreadcrumbs({ schema: this.selectedDatabase, trigger: payload.name });
|
const triggerName = this.customizations.triggerTableInName ? `${payload.table}.${payload.name}` : payload.name;
|
||||||
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
|
this.changeBreadcrumbs({ schema: this.selectedSchema, trigger: triggerName });
|
||||||
|
|
||||||
|
this.newTab({
|
||||||
|
uid: this.workspace.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
elementName: triggerName,
|
||||||
|
elementType: 'trigger',
|
||||||
|
type: 'trigger-props'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
this.addNotification({ status: 'error', message: response });
|
||||||
},
|
},
|
||||||
showCreateRoutineModal () {
|
showCreateRoutineModal () {
|
||||||
this.closeDatabaseContext();
|
this.closeDatabaseContext();
|
||||||
|
this.closeMiscFolderContext();
|
||||||
this.isNewRoutineModal = true;
|
this.isNewRoutineModal = true;
|
||||||
},
|
},
|
||||||
hideCreateRoutineModal () {
|
hideCreateRoutineModal () {
|
||||||
@@ -379,6 +447,7 @@ export default {
|
|||||||
async openCreateRoutineEditor (payload) {
|
async openCreateRoutineEditor (payload) {
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
...payload
|
...payload
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -386,21 +455,38 @@ export default {
|
|||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
await this.refresh();
|
await this.refresh();
|
||||||
this.changeBreadcrumbs({ schema: this.selectedDatabase, procedure: payload.name });
|
this.changeBreadcrumbs({ schema: this.selectedSchema, routine: payload.name });
|
||||||
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
|
|
||||||
|
this.newTab({
|
||||||
|
uid: this.workspace.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
elementName: payload.name,
|
||||||
|
elementType: 'routine',
|
||||||
|
type: 'routine-props'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
this.addNotification({ status: 'error', message: response });
|
||||||
},
|
},
|
||||||
showCreateFunctionModal () {
|
showCreateFunctionModal () {
|
||||||
this.closeDatabaseContext();
|
this.closeDatabaseContext();
|
||||||
|
this.closeMiscFolderContext();
|
||||||
this.isNewFunctionModal = true;
|
this.isNewFunctionModal = true;
|
||||||
},
|
},
|
||||||
hideCreateFunctionModal () {
|
hideCreateFunctionModal () {
|
||||||
this.isNewFunctionModal = false;
|
this.isNewFunctionModal = false;
|
||||||
},
|
},
|
||||||
|
showCreateTriggerFunctionModal () {
|
||||||
|
this.closeDatabaseContext();
|
||||||
|
this.closeMiscFolderContext();
|
||||||
|
this.isNewTriggerFunctionModal = true;
|
||||||
|
},
|
||||||
|
hideCreateTriggerFunctionModal () {
|
||||||
|
this.isNewTriggerFunctionModal = false;
|
||||||
|
},
|
||||||
showCreateSchedulerModal () {
|
showCreateSchedulerModal () {
|
||||||
this.closeDatabaseContext();
|
this.closeDatabaseContext();
|
||||||
|
this.closeMiscFolderContext();
|
||||||
this.isNewSchedulerModal = true;
|
this.isNewSchedulerModal = true;
|
||||||
},
|
},
|
||||||
hideCreateSchedulerModal () {
|
hideCreateSchedulerModal () {
|
||||||
@@ -409,6 +495,7 @@ export default {
|
|||||||
async openCreateFunctionEditor (payload) {
|
async openCreateFunctionEditor (payload) {
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
...payload
|
...payload
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -416,8 +503,39 @@ export default {
|
|||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
await this.refresh();
|
await this.refresh();
|
||||||
this.changeBreadcrumbs({ schema: this.selectedDatabase, function: payload.name });
|
this.changeBreadcrumbs({ schema: this.selectedSchema, function: payload.name });
|
||||||
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
|
|
||||||
|
this.newTab({
|
||||||
|
uid: this.workspace.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
elementName: payload.name,
|
||||||
|
elementType: 'function',
|
||||||
|
type: 'function-props'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
},
|
||||||
|
async openCreateTriggerFunctionEditor (payload) {
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
...payload
|
||||||
|
};
|
||||||
|
|
||||||
|
const { status, response } = await Functions.createTriggerFunction(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
await this.refresh();
|
||||||
|
this.changeBreadcrumbs({ schema: this.selectedSchema, triggerFunction: payload.name });
|
||||||
|
|
||||||
|
this.newTab({
|
||||||
|
uid: this.workspace.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
elementName: payload.name,
|
||||||
|
elementType: 'triggerFunction',
|
||||||
|
type: 'trigger-function-props'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
this.addNotification({ status: 'error', message: response });
|
||||||
@@ -425,6 +543,7 @@ export default {
|
|||||||
async openCreateSchedulerEditor (payload) {
|
async openCreateSchedulerEditor (payload) {
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
...payload
|
...payload
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -432,8 +551,15 @@ export default {
|
|||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
await this.refresh();
|
await this.refresh();
|
||||||
this.changeBreadcrumbs({ schema: this.selectedDatabase, scheduler: payload.name });
|
this.changeBreadcrumbs({ schema: this.selectedSchema, scheduler: payload.name });
|
||||||
this.selectTab({ uid: this.workspace.uid, tab: 'prop' });
|
|
||||||
|
this.newTab({
|
||||||
|
uid: this.workspace.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
elementName: payload.name,
|
||||||
|
elementType: 'scheduler',
|
||||||
|
type: 'scheduler-props'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
this.addNotification({ status: 'error', message: response });
|
||||||
@@ -451,6 +577,11 @@ export default {
|
|||||||
height: calc(100vh - #{$excluding-size});
|
height: calc(100vh - #{$excluding-size});
|
||||||
cursor: ew-resize;
|
cursor: ew-resize;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
|
transition: background 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba($primary-color, 50%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspace-explorebar {
|
.workspace-explorebar {
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
>
|
>
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-delete mr-1" /> {{ deleteMessage }}
|
<i class="mdi mdi-24px mdi-delete mr-1" />
|
||||||
|
<span class="cut-text">{{ deleteMessage }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div slot="body">
|
<div slot="body">
|
||||||
@@ -58,7 +59,8 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
contextEvent: MouseEvent,
|
contextEvent: MouseEvent,
|
||||||
selectedMisc: Object
|
selectedMisc: Object,
|
||||||
|
selectedSchema: String
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -83,6 +85,7 @@ export default {
|
|||||||
case 'procedure':
|
case 'procedure':
|
||||||
return this.$t('message.deleteRoutine');
|
return this.$t('message.deleteRoutine');
|
||||||
case 'function':
|
case 'function':
|
||||||
|
case 'triggerFunction':
|
||||||
return this.$t('message.deleteFunction');
|
return this.$t('message.deleteFunction');
|
||||||
case 'scheduler':
|
case 'scheduler':
|
||||||
return this.$t('message.deleteScheduler');
|
return this.$t('message.deleteScheduler');
|
||||||
@@ -95,6 +98,7 @@ export default {
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
|
removeTabs: 'workspaces/removeTabs',
|
||||||
newTab: 'workspaces/newTab'
|
newTab: 'workspaces/newTab'
|
||||||
}),
|
}),
|
||||||
showCreateTableModal () {
|
showCreateTableModal () {
|
||||||
@@ -124,24 +128,29 @@ export default {
|
|||||||
case 'trigger':
|
case 'trigger':
|
||||||
res = await Triggers.dropTrigger({
|
res = await Triggers.dropTrigger({
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
|
schema: this.selectedSchema,
|
||||||
trigger: this.selectedMisc.name
|
trigger: this.selectedMisc.name
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'procedure':
|
case 'procedure':
|
||||||
res = await Routines.dropRoutine({
|
res = await Routines.dropRoutine({
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
|
schema: this.selectedSchema,
|
||||||
routine: this.selectedMisc.name
|
routine: this.selectedMisc.name
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'function':
|
case 'function':
|
||||||
|
case 'triggerFunction':
|
||||||
res = await Functions.dropFunction({
|
res = await Functions.dropFunction({
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
|
schema: this.selectedSchema,
|
||||||
func: this.selectedMisc.name
|
func: this.selectedMisc.name
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'scheduler':
|
case 'scheduler':
|
||||||
res = await Schedulers.dropScheduler({
|
res = await Schedulers.dropScheduler({
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
|
schema: this.selectedSchema,
|
||||||
scheduler: this.selectedMisc.name
|
scheduler: this.selectedMisc.name
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
@@ -150,7 +159,12 @@ export default {
|
|||||||
const { status, response } = res;
|
const { status, response } = res;
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.changeBreadcrumbs({ [this.selectedMisc.type]: null });
|
this.removeTabs({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
elementName: this.selectedMisc.name,
|
||||||
|
elementType: this.selectedMisc.type,
|
||||||
|
schema: this.selectedSchema
|
||||||
|
});
|
||||||
|
|
||||||
this.closeContext();
|
this.closeContext();
|
||||||
this.$emit('reload');
|
this.$emit('reload');
|
||||||
@@ -177,8 +191,8 @@ export default {
|
|||||||
async runRoutineCheck () {
|
async runRoutineCheck () {
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
schema: this.workspace.breadcrumbs.schema,
|
schema: this.selectedSchema,
|
||||||
routine: this.workspace.breadcrumbs.procedure
|
routine: this.selectedMisc.name
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -215,14 +229,14 @@ export default {
|
|||||||
sql = `CALL \`${this.localElement.name}\`(${params.join(',')})`;
|
sql = `CALL \`${this.localElement.name}\`(${params.join(',')})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.newTab({ uid: this.workspace.uid, content: sql, autorun: true });
|
this.newTab({ uid: this.workspace.uid, content: sql, type: 'query', autorun: true });
|
||||||
this.closeContext();
|
this.closeContext();
|
||||||
},
|
},
|
||||||
async runFunctionCheck () {
|
async runFunctionCheck () {
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
schema: this.workspace.breadcrumbs.schema,
|
schema: this.selectedSchema,
|
||||||
func: this.workspace.breadcrumbs.function
|
func: this.selectedMisc.name
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -260,7 +274,7 @@ export default {
|
|||||||
sql = `SELECT \`${this.localElement.name}\` (${params.join(',')})`;
|
sql = `SELECT \`${this.localElement.name}\` (${params.join(',')})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.newTab({ uid: this.workspace.uid, content: sql, autorun: true });
|
this.newTab({ uid: this.workspace.uid, content: sql, type: 'query', autorun: true });
|
||||||
this.closeContext();
|
this.closeContext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
<template>
|
||||||
|
<BaseContextMenu
|
||||||
|
:context-event="contextEvent"
|
||||||
|
@close-context="closeContext"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="selectedMisc === 'trigger'"
|
||||||
|
class="context-element"
|
||||||
|
@click="$emit('show-create-trigger-modal')"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ $t('message.createNewTrigger') }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="selectedMisc === 'procedure'"
|
||||||
|
class="context-element"
|
||||||
|
@click="$emit('show-create-routine-modal')"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-sync-circle text-light pr-1" /> {{ $t('message.createNewRoutine') }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="selectedMisc === 'function'"
|
||||||
|
class="context-element"
|
||||||
|
@click="$emit('show-create-function-modal')"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box text-light pr-1" /> {{ $t('message.createNewFunction') }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="selectedMisc === 'triggerFunction'"
|
||||||
|
class="context-element"
|
||||||
|
@click="$emit('show-create-trigger-function-modal')"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-cog-clockwise text-light pr-1" /> {{ $t('message.createNewFunction') }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="selectedMisc === 'scheduler'"
|
||||||
|
class="context-element"
|
||||||
|
@click="$emit('show-create-scheduler-modal')"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ $t('message.createNewScheduler') }}</span>
|
||||||
|
</div>
|
||||||
|
</BaseContextMenu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import BaseContextMenu from '@/components/BaseContextMenu';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspaceExploreBarMiscContext',
|
||||||
|
components: {
|
||||||
|
BaseContextMenu
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
contextEvent: MouseEvent,
|
||||||
|
selectedMisc: String,
|
||||||
|
selectedSchema: String
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
localElement: {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.selectedWorkspace);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||||
|
}),
|
||||||
|
showCreateTableModal () {
|
||||||
|
this.$emit('show-create-table-modal');
|
||||||
|
},
|
||||||
|
showDeleteModal () {
|
||||||
|
this.isDeleteModal = true;
|
||||||
|
},
|
||||||
|
hideDeleteModal () {
|
||||||
|
this.isDeleteModal = false;
|
||||||
|
},
|
||||||
|
showAskParamsModal () {
|
||||||
|
this.isAskingParameters = true;
|
||||||
|
},
|
||||||
|
hideAskParamsModal () {
|
||||||
|
this.isAskingParameters = false;
|
||||||
|
this.closeContext();
|
||||||
|
},
|
||||||
|
closeContext () {
|
||||||
|
this.$emit('close-context');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -16,14 +16,21 @@
|
|||||||
<ul class="menu menu-nav pt-0">
|
<ul class="menu menu-nav pt-0">
|
||||||
<li
|
<li
|
||||||
v-for="table of filteredTables"
|
v-for="table of filteredTables"
|
||||||
|
:ref="breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name) ? 'explorebar-selected' : ''"
|
||||||
: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="{'selected': breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name)}"
|
||||||
@click="setBreadcrumbs({schema: database.name, [table.type]: table.name})"
|
@mousedown.left="selectTable({schema: database.name, table})"
|
||||||
|
@dblclick="openDataTab({schema: database.name, table})"
|
||||||
@contextmenu.prevent="showTableContext($event, table)"
|
@contextmenu.prevent="showTableContext($event, table)"
|
||||||
>
|
>
|
||||||
<a class="table-name">
|
<a class="table-name">
|
||||||
<i class="table-icon mdi mdi-18px mr-1" :class="table.type === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
|
<div v-if="checkLoadingStatus(table.name, 'table')" class="icon loading mr-1" />
|
||||||
|
<i
|
||||||
|
v-else
|
||||||
|
class="table-icon mdi mdi-18px mr-1"
|
||||||
|
:class="table.type === 'view' ? 'mdi-table-eye' : 'mdi-table'"
|
||||||
|
/>
|
||||||
<span v-html="highlightWord(table.name)" />
|
<span v-html="highlightWord(table.name)" />
|
||||||
</a>
|
</a>
|
||||||
<div
|
<div
|
||||||
@@ -39,7 +46,11 @@
|
|||||||
|
|
||||||
<div v-if="filteredTriggers.length && customizations.triggers" 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}"
|
||||||
|
@contextmenu.prevent="showMiscFolderContext($event, '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" />
|
||||||
{{ $tc('word.trigger', 2) }}
|
{{ $tc('word.trigger', 2) }}
|
||||||
</summary>
|
</summary>
|
||||||
@@ -49,9 +60,11 @@
|
|||||||
<li
|
<li
|
||||||
v-for="trigger of filteredTriggers"
|
v-for="trigger of filteredTriggers"
|
||||||
:key="trigger.name"
|
:key="trigger.name"
|
||||||
|
:ref="breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name ? 'explorebar-selected' : ''"
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name}"
|
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name}"
|
||||||
@click="setBreadcrumbs({schema: database.name, trigger: trigger.name})"
|
@mousedown="selectMisc({schema: database.name, misc: trigger, type: 'trigger'})"
|
||||||
|
@dblclick="openMiscPermanentTab({schema: database.name, misc: trigger, type: 'trigger'})"
|
||||||
@contextmenu.prevent="showMiscContext($event, {...trigger, type: 'trigger'})"
|
@contextmenu.prevent="showMiscContext($event, {...trigger, type: 'trigger'})"
|
||||||
>
|
>
|
||||||
<a class="table-name">
|
<a class="table-name">
|
||||||
@@ -67,7 +80,11 @@
|
|||||||
|
|
||||||
<div v-if="filteredProcedures.length && customizations.routines" 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.routine}"
|
||||||
|
@contextmenu.prevent="showMiscFolderContext($event, '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" />
|
||||||
{{ $tc('word.storedRoutine', 2) }}
|
{{ $tc('word.storedRoutine', 2) }}
|
||||||
</summary>
|
</summary>
|
||||||
@@ -77,9 +94,11 @@
|
|||||||
<li
|
<li
|
||||||
v-for="(procedure, i) of filteredProcedures"
|
v-for="(procedure, i) of filteredProcedures"
|
||||||
:key="`${procedure.name}-${i}`"
|
:key="`${procedure.name}-${i}`"
|
||||||
|
:ref="breadcrumbs.schema === database.name && breadcrumbs.routine === procedure.name ? 'explorebar-selected' : ''"
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure === procedure.name}"
|
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.routine === procedure.name}"
|
||||||
@click="setBreadcrumbs({schema: database.name, procedure: procedure.name})"
|
@mousedown="selectMisc({schema: database.name, misc: procedure, type: 'routine'})"
|
||||||
|
@dblclick="openMiscPermanentTab({schema: database.name, misc: procedure, type: 'routine'})"
|
||||||
@contextmenu.prevent="showMiscContext($event, {...procedure, type: 'procedure'})"
|
@contextmenu.prevent="showMiscContext($event, {...procedure, type: 'procedure'})"
|
||||||
>
|
>
|
||||||
<a class="table-name">
|
<a class="table-name">
|
||||||
@@ -93,9 +112,47 @@
|
|||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="filteredTriggerFunctions.length && customizations.triggerFunctions" class="database-misc">
|
||||||
|
<details class="accordion">
|
||||||
|
<summary
|
||||||
|
class="accordion-header misc-name"
|
||||||
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.triggerFunction}"
|
||||||
|
@contextmenu.prevent="showMiscFolderContext($event, 'triggerFunction')"
|
||||||
|
>
|
||||||
|
<i class="misc-icon mdi mdi-18px mdi-folder-refresh mr-1" />
|
||||||
|
{{ $tc('word.triggerFunction', 2) }}
|
||||||
|
</summary>
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div>
|
||||||
|
<ul class="menu menu-nav pt-0">
|
||||||
|
<li
|
||||||
|
v-for="(func, i) of filteredTriggerFunctions"
|
||||||
|
:key="`${func.name}-${i}`"
|
||||||
|
:ref="breadcrumbs.schema === database.name && breadcrumbs.triggerFunction === func.name ? 'explorebar-selected' : ''"
|
||||||
|
class="menu-item"
|
||||||
|
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.triggerFunction === func.name}"
|
||||||
|
@mousedown="selectMisc({schema: database.name, misc: func, type: 'triggerFunction'})"
|
||||||
|
@dblclick="openMiscPermanentTab({schema: database.name, misc: func, type: 'triggerFunction'})"
|
||||||
|
@contextmenu.prevent="showMiscContext($event, {...func, type: 'triggerFunction'})"
|
||||||
|
>
|
||||||
|
<a class="table-name">
|
||||||
|
<i class="table-icon mdi mdi-cog-clockwise mdi-18px mr-1" />
|
||||||
|
<span v-html="highlightWord(func.name)" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="filteredFunctions.length && customizations.functions" class="database-misc">
|
<div v-if="filteredFunctions.length && customizations.functions" class="database-misc">
|
||||||
<details class="accordion">
|
<details class="accordion">
|
||||||
<summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function}">
|
<summary
|
||||||
|
class="accordion-header misc-name"
|
||||||
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function}"
|
||||||
|
@contextmenu.prevent="showMiscFolderContext($event, '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" />
|
||||||
{{ $tc('word.function', 2) }}
|
{{ $tc('word.function', 2) }}
|
||||||
</summary>
|
</summary>
|
||||||
@@ -105,9 +162,11 @@
|
|||||||
<li
|
<li
|
||||||
v-for="(func, i) of filteredFunctions"
|
v-for="(func, i) of filteredFunctions"
|
||||||
:key="`${func.name}-${i}`"
|
:key="`${func.name}-${i}`"
|
||||||
|
:ref="breadcrumbs.schema === database.name && breadcrumbs.function === func.name ? 'explorebar-selected' : ''"
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}"
|
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}"
|
||||||
@click="setBreadcrumbs({schema: database.name, function: func.name})"
|
@mousedown="selectMisc({schema: database.name, misc: func, type: 'function'})"
|
||||||
|
@dblclick="openMiscPermanentTab({schema: database.name, misc: func, type: 'function'})"
|
||||||
@contextmenu.prevent="showMiscContext($event, {...func, type: 'function'})"
|
@contextmenu.prevent="showMiscContext($event, {...func, type: 'function'})"
|
||||||
>
|
>
|
||||||
<a class="table-name">
|
<a class="table-name">
|
||||||
@@ -123,7 +182,11 @@
|
|||||||
|
|
||||||
<div v-if="filteredSchedulers.length && customizations.schedulers" 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}"
|
||||||
|
@contextmenu.prevent="showMiscFolderContext($event, '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" />
|
||||||
{{ $tc('word.scheduler', 2) }}
|
{{ $tc('word.scheduler', 2) }}
|
||||||
</summary>
|
</summary>
|
||||||
@@ -133,9 +196,11 @@
|
|||||||
<li
|
<li
|
||||||
v-for="scheduler of filteredSchedulers"
|
v-for="scheduler of filteredSchedulers"
|
||||||
:key="scheduler.name"
|
:key="scheduler.name"
|
||||||
|
:ref="breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name ? 'explorebar-selected' : ''"
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name}"
|
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name}"
|
||||||
@click="setBreadcrumbs({schema: database.name, scheduler: scheduler.name})"
|
@mousedown="selectMisc({schema: database.name, misc: scheduler, type: 'scheduler'})"
|
||||||
|
@dblclick="openMiscPermanentTab({schema: database.name, misc: scheduler, type: 'scheduler'})"
|
||||||
@contextmenu.prevent="showMiscContext($event, {...scheduler, type: 'scheduler'})"
|
@contextmenu.prevent="showMiscContext($event, {...scheduler, type: 'scheduler'})"
|
||||||
>
|
>
|
||||||
<a class="table-name">
|
<a class="table-name">
|
||||||
@@ -189,14 +254,22 @@ export default {
|
|||||||
filteredFunctions () {
|
filteredFunctions () {
|
||||||
return this.database.functions.filter(func => func.name.search(this.searchTerm) >= 0);
|
return this.database.functions.filter(func => func.name.search(this.searchTerm) >= 0);
|
||||||
},
|
},
|
||||||
|
filteredTriggerFunctions () {
|
||||||
|
return this.database.triggerFunctions
|
||||||
|
? this.database.triggerFunctions.filter(func => func.name.search(this.searchTerm) >= 0)
|
||||||
|
: [];
|
||||||
|
},
|
||||||
filteredSchedulers () {
|
filteredSchedulers () {
|
||||||
return this.database.schedulers.filter(scheduler => scheduler.name.search(this.searchTerm) >= 0);
|
return this.database.schedulers.filter(scheduler => scheduler.name.search(this.searchTerm) >= 0);
|
||||||
},
|
},
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.connection.uid);
|
||||||
|
},
|
||||||
breadcrumbs () {
|
breadcrumbs () {
|
||||||
return this.getWorkspace(this.connection.uid).breadcrumbs;
|
return this.workspace.breadcrumbs;
|
||||||
},
|
},
|
||||||
customizations () {
|
customizations () {
|
||||||
return this.getWorkspace(this.connection.uid).customizations;
|
return this.workspace.customizations;
|
||||||
},
|
},
|
||||||
loadedSchemas () {
|
loadedSchemas () {
|
||||||
return this.getLoadedSchemas(this.connection.uid);
|
return this.getLoadedSchemas(this.connection.uid);
|
||||||
@@ -211,32 +284,101 @@ export default {
|
|||||||
return this.database.tables.reduce((acc, curr) => acc + curr.size, 0);
|
return this.database.tables.reduce((acc, curr) => acc + curr.size, 0);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
breadcrumbs (newVal, oldVal) {
|
||||||
|
if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const element = this.$refs['explorebar-selected'] ? this.$refs['explorebar-selected'][0] : null;
|
||||||
|
if (element) {
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
const elemTop = rect.top;
|
||||||
|
const elemBottom = rect.bottom;
|
||||||
|
const isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
|
||||||
|
|
||||||
|
if (!isVisible) {
|
||||||
|
element.setAttribute('tabindex', '-1');
|
||||||
|
element.focus();
|
||||||
|
element.removeAttribute('tabindex');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
|
addLoadedSchema: 'workspaces/addLoadedSchema',
|
||||||
|
newTab: 'workspaces/newTab',
|
||||||
refreshSchema: 'workspaces/refreshSchema'
|
refreshSchema: 'workspaces/refreshSchema'
|
||||||
}),
|
}),
|
||||||
formatBytes,
|
formatBytes,
|
||||||
async selectSchema (schema) {
|
async selectSchema (schema) {
|
||||||
if (!this.loadedSchemas.has(schema)) {
|
if (!this.loadedSchemas.has(schema) && !this.isLoading) {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
await this.refreshSchema({ uid: this.connection.uid, schema });
|
await this.refreshSchema({ uid: this.connection.uid, schema });
|
||||||
|
this.addLoadedSchema(schema);
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.changeBreadcrumbs({ schema, table: null });
|
this.changeBreadcrumbs({ schema, table: null });
|
||||||
},
|
},
|
||||||
|
selectTable ({ schema, table }) {
|
||||||
|
this.newTab({ uid: this.connection.uid, elementName: table.name, schema: this.database.name, type: 'temp-data', elementType: table.type });
|
||||||
|
this.setBreadcrumbs({ schema, [table.type]: table.name });
|
||||||
|
},
|
||||||
|
selectMisc ({ schema, misc, type }) {
|
||||||
|
const miscTempTabs = {
|
||||||
|
trigger: 'temp-trigger-props',
|
||||||
|
triggerFunction: 'temp-trigger-function-props',
|
||||||
|
function: 'temp-function-props',
|
||||||
|
routine: 'temp-routine-props',
|
||||||
|
scheduler: 'temp-scheduler-props'
|
||||||
|
};
|
||||||
|
|
||||||
|
this.newTab({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
elementName: misc.name,
|
||||||
|
schema: this.database.name,
|
||||||
|
type: miscTempTabs[type],
|
||||||
|
elementType: type
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setBreadcrumbs({ schema, [type]: misc.name });
|
||||||
|
},
|
||||||
|
openDataTab ({ schema, table }) {
|
||||||
|
this.newTab({ uid: this.connection.uid, elementName: table.name, schema: this.database.name, type: 'data', elementType: table.type });
|
||||||
|
this.setBreadcrumbs({ schema, [table.type]: table.name });
|
||||||
|
},
|
||||||
|
openMiscPermanentTab ({ schema, misc, type }) {
|
||||||
|
const miscTabs = {
|
||||||
|
trigger: 'trigger-props',
|
||||||
|
triggerFunction: 'trigger-function-props',
|
||||||
|
function: 'function-props',
|
||||||
|
routine: 'routine-props',
|
||||||
|
scheduler: 'scheduler-props'
|
||||||
|
};
|
||||||
|
|
||||||
|
this.newTab({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
elementName: misc.name,
|
||||||
|
schema: this.database.name,
|
||||||
|
type: miscTabs[type],
|
||||||
|
elementType: type
|
||||||
|
});
|
||||||
|
this.setBreadcrumbs({ schema, [type]: misc.name });
|
||||||
|
},
|
||||||
showSchemaContext (event, schema) {
|
showSchemaContext (event, schema) {
|
||||||
this.selectSchema(schema);
|
|
||||||
this.$emit('show-schema-context', { event, schema });
|
this.$emit('show-schema-context', { event, schema });
|
||||||
},
|
},
|
||||||
showTableContext (event, table) {
|
showTableContext (event, table) {
|
||||||
this.setBreadcrumbs({ schema: this.database.name, [table.type]: table.name });
|
this.$emit('show-table-context', { event, schema: this.database.name, table });
|
||||||
this.$emit('show-table-context', { event, table });
|
|
||||||
},
|
},
|
||||||
showMiscContext (event, misc) {
|
showMiscContext (event, misc) {
|
||||||
this.setBreadcrumbs({ schema: this.database.name, [misc.type]: misc.name });
|
this.$emit('show-misc-context', { event, schema: this.database.name, misc });
|
||||||
this.$emit('show-misc-context', { event, misc });
|
},
|
||||||
|
showMiscFolderContext (event, type) {
|
||||||
|
this.$emit('show-misc-folder-context', { event, schema: this.database.name, type });
|
||||||
},
|
},
|
||||||
piePercentage (val) {
|
piePercentage (val) {
|
||||||
const perc = val / this.maxSize * 100;
|
const perc = val / this.maxSize * 100;
|
||||||
@@ -250,12 +392,20 @@ export default {
|
|||||||
this.changeBreadcrumbs(payload);
|
this.changeBreadcrumbs(payload);
|
||||||
},
|
},
|
||||||
highlightWord (string) {
|
highlightWord (string) {
|
||||||
|
string = string.replaceAll('<', '<').replaceAll('>', '>');
|
||||||
|
|
||||||
if (this.searchTerm) {
|
if (this.searchTerm) {
|
||||||
const regexp = new RegExp(`(${this.searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
const regexp = new RegExp(`(${this.searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
||||||
return string.replace(regexp, '<span class="text-primary">$1</span>');
|
return string.replace(regexp, '<span class="text-primary">$1</span>');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return string;
|
return string;
|
||||||
|
},
|
||||||
|
checkLoadingStatus (name, type) {
|
||||||
|
return this.workspace.loadingElements.some(el =>
|
||||||
|
el.name === name &&
|
||||||
|
el.type === type &&
|
||||||
|
el.schema === this.database.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -263,6 +413,10 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.workspace-explorebar-database {
|
.workspace-explorebar-database {
|
||||||
|
.selected {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
.database-name {
|
.database-name {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -311,7 +465,7 @@ export default {
|
|||||||
.database-name,
|
.database-name,
|
||||||
.misc-name {
|
.misc-name {
|
||||||
&:hover {
|
&:hover {
|
||||||
border-radius: 2px;
|
border-radius: $border-radius;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,8 +473,9 @@ export default {
|
|||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&:hover {
|
&:hover,
|
||||||
border-radius: 2px;
|
&.selected {
|
||||||
|
border-radius: $border-radius;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,13 @@
|
|||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.function', 1) }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.function', 1) }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="workspace.customizations.triggerFunctionAdd"
|
||||||
|
class="context-element"
|
||||||
|
@click="showCreateTriggerFunctionModal"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-cog-clockwise pr-1" /> {{ $tc('word.triggerFunction', 1) }}</span>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="workspace.customizations.schedulerAdd"
|
v-if="workspace.customizations.schedulerAdd"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@@ -69,18 +76,19 @@
|
|||||||
>
|
>
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-database-remove mr-1" /> {{ $t('message.deleteSchema') }}
|
<i class="mdi mdi-24px mdi-database-remove mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.deleteSchema') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div slot="body">
|
<div slot="body">
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
{{ $t('message.deleteCorfirm') }} "<b>{{ selectedDatabase }}</b>"?
|
{{ $t('message.deleteCorfirm') }} "<b>{{ selectedSchema }}</b>"?
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
<ModalEditSchema
|
<ModalEditSchema
|
||||||
v-if="isEditModal"
|
v-if="isEditModal"
|
||||||
:selected-database="selectedDatabase"
|
:selected-schema="selectedSchema"
|
||||||
@close="hideEditModal"
|
@close="hideEditModal"
|
||||||
/>
|
/>
|
||||||
</BaseContextMenu>
|
</BaseContextMenu>
|
||||||
@@ -102,7 +110,7 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
contextEvent: MouseEvent,
|
contextEvent: MouseEvent,
|
||||||
selectedDatabase: String
|
selectedSchema: String
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -139,6 +147,9 @@ export default {
|
|||||||
showCreateFunctionModal () {
|
showCreateFunctionModal () {
|
||||||
this.$emit('show-create-function-modal');
|
this.$emit('show-create-function-modal');
|
||||||
},
|
},
|
||||||
|
showCreateTriggerFunctionModal () {
|
||||||
|
this.$emit('show-create-trigger-function-modal');
|
||||||
|
},
|
||||||
showCreateSchedulerModal () {
|
showCreateSchedulerModal () {
|
||||||
this.$emit('show-create-scheduler-modal');
|
this.$emit('show-create-scheduler-modal');
|
||||||
},
|
},
|
||||||
@@ -162,11 +173,11 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const { status, response } = await Schema.deleteSchema({
|
const { status, response } = await Schema.deleteSchema({
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
database: this.selectedDatabase
|
database: this.selectedSchema
|
||||||
});
|
});
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
if (this.selectedDatabase === this.workspace.breadcrumbs.schema)
|
if (this.selectedSchema === this.workspace.breadcrumbs.schema)
|
||||||
this.changeBreadcrumbs({ schema: null });
|
this.changeBreadcrumbs({ schema: null });
|
||||||
|
|
||||||
this.closeContext();
|
this.closeContext();
|
||||||
|
|||||||
@@ -4,7 +4,28 @@
|
|||||||
@close-context="closeContext"
|
@close-context="closeContext"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="selectedTable.type === 'table'"
|
v-if="selectedTable && selectedTable.type === 'table' && customizations.tableSettings"
|
||||||
|
class="context-element"
|
||||||
|
@click="openTableSettingTab"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-tune-vertical-variant text-light pr-1" /> {{ $t('word.settings') }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="selectedTable && selectedTable.type === 'view' && customizations.viewSettings"
|
||||||
|
class="context-element"
|
||||||
|
@click="openViewSettingTab"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-tune-vertical-variant text-light pr-1" /> {{ $t('word.settings') }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="selectedTable && selectedTable.type === 'table'"
|
||||||
|
class="context-element"
|
||||||
|
@click="duplicateTable"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table-multiple text-light pr-1" /> {{ $t('message.duplicateTable') }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="selectedTable && selectedTable.type === 'table'"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@click="showEmptyModal"
|
@click="showEmptyModal"
|
||||||
>
|
>
|
||||||
@@ -21,7 +42,7 @@
|
|||||||
>
|
>
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-table-off mr-1" /> {{ $t('message.emptyTable') }}
|
<i class="mdi mdi-24px mdi-table-off mr-1" /> <span class="cut-text">{{ $t('message.emptyTable') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div slot="body">
|
<div slot="body">
|
||||||
@@ -37,7 +58,8 @@
|
|||||||
>
|
>
|
||||||
<template slot="header">
|
<template slot="header">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-table-remove mr-1" /> {{ selectedTable.type === 'table' ? $t('message.deleteTable') : $t('message.deleteView') }}
|
<i class="mdi mdi-24px mdi-table-remove mr-1" />
|
||||||
|
<span class="cut-text">{{ selectedTable.type === 'table' ? $t('message.deleteTable') : $t('message.deleteView') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div slot="body">
|
<div slot="body">
|
||||||
@@ -64,7 +86,8 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
contextEvent: MouseEvent,
|
contextEvent: MouseEvent,
|
||||||
selectedTable: Object
|
selectedTable: Object,
|
||||||
|
selectedSchema: String
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -79,11 +102,18 @@ export default {
|
|||||||
}),
|
}),
|
||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.selectedWorkspace);
|
return this.getWorkspace(this.selectedWorkspace);
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace && this.workspace.customizations ? this.workspace.customizations : {};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
|
newTab: 'workspaces/newTab',
|
||||||
|
removeTabs: 'workspaces/removeTabs',
|
||||||
|
addLoadingElement: 'workspaces/addLoadingElement',
|
||||||
|
removeLoadingElement: 'workspaces/removeLoadingElement',
|
||||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||||
}),
|
}),
|
||||||
showCreateTableModal () {
|
showCreateTableModal () {
|
||||||
@@ -104,51 +134,137 @@ export default {
|
|||||||
closeContext () {
|
closeContext () {
|
||||||
this.$emit('close-context');
|
this.$emit('close-context');
|
||||||
},
|
},
|
||||||
async emptyTable () {
|
openTableSettingTab () {
|
||||||
try {
|
this.newTab({
|
||||||
const { status, response } = await Tables.truncateTable({
|
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
|
elementName: this.selectedTable.name,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
type: 'table-props',
|
||||||
|
elementType: 'table'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.changeBreadcrumbs({
|
||||||
|
schema: this.selectedSchema,
|
||||||
table: this.selectedTable.name
|
table: this.selectedTable.name
|
||||||
});
|
});
|
||||||
|
|
||||||
if (status === 'success') {
|
this.closeContext();
|
||||||
if (this.selectedTable.name === this.workspace.breadcrumbs.table)
|
},
|
||||||
this.changeBreadcrumbs({ table: null });
|
openViewSettingTab () {
|
||||||
|
this.newTab({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
elementType: 'table',
|
||||||
|
elementName: this.selectedTable.name,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
type: 'view-props'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.changeBreadcrumbs({
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
view: this.selectedTable.name
|
||||||
|
});
|
||||||
|
|
||||||
this.closeContext();
|
this.closeContext();
|
||||||
|
},
|
||||||
|
async duplicateTable () {
|
||||||
|
this.closeContext();
|
||||||
|
|
||||||
|
this.addLoadingElement({
|
||||||
|
name: this.selectedTable.name,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
type: 'table'
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Tables.duplicateTable({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
table: this.selectedTable.name,
|
||||||
|
schema: this.selectedSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
if (status === 'success')
|
||||||
this.$emit('reload');
|
this.$emit('reload');
|
||||||
}
|
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
this.addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.removeLoadingElement({
|
||||||
|
name: this.selectedTable.name,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
type: 'table'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async emptyTable () {
|
||||||
|
this.closeContext();
|
||||||
|
|
||||||
|
this.addLoadingElement({
|
||||||
|
name: this.selectedTable.name,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
type: 'table'
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Tables.truncateTable({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
table: this.selectedTable.name,
|
||||||
|
schema: this.selectedSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
if (status === 'success')
|
||||||
|
this.$emit('reload');
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.removeLoadingElement({
|
||||||
|
name: this.selectedTable.name,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
type: 'table'
|
||||||
|
});
|
||||||
},
|
},
|
||||||
async deleteTable () {
|
async deleteTable () {
|
||||||
|
this.closeContext();
|
||||||
|
|
||||||
|
this.addLoadingElement({
|
||||||
|
name: this.selectedTable.name,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
type: 'table'
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let res;
|
let res;
|
||||||
|
|
||||||
if (this.selectedTable.type === 'table') {
|
if (this.selectedTable.type === 'table') {
|
||||||
res = await Tables.dropTable({
|
res = await Tables.dropTable({
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
table: this.selectedTable.name
|
table: this.selectedTable.name,
|
||||||
|
schema: this.selectedSchema
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (this.selectedTable.type === 'view') {
|
else if (this.selectedTable.type === 'view') {
|
||||||
res = await Views.dropView({
|
res = await Views.dropView({
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
view: this.selectedTable.name
|
view: this.selectedTable.name,
|
||||||
|
schema: this.selectedSchema
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { status, response } = res;
|
const { status, response } = res;
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
if (this.selectedTable.name === this.workspace.breadcrumbs.table || this.selectedTable.name === this.workspace.breadcrumbs.view)
|
this.removeTabs({
|
||||||
this.changeBreadcrumbs({ table: null, view: null });
|
uid: this.selectedWorkspace,
|
||||||
|
elementName: this.selectedTable.name,
|
||||||
|
elementType: this.selectedTable.type,
|
||||||
|
schema: this.selectedSchema
|
||||||
|
});
|
||||||
|
|
||||||
this.closeContext();
|
|
||||||
this.$emit('reload');
|
this.$emit('reload');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -157,6 +273,12 @@ export default {
|
|||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.removeLoadingElement({
|
||||||
|
name: this.selectedTable.name,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
type: 'table'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-key-link mr-1" /> {{ $t('word.foreignKeys') }} "{{ table }}"
|
<i class="mdi mdi-24px mdi-key-link mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.foreignKeys') }} "{{ table }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -18,8 +19,8 @@
|
|||||||
<div class="panel-header pt-0 pl-0">
|
<div class="panel-header pt-0 pl-0">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<button class="btn btn-dark btn-sm d-flex" @click="addForeign">
|
<button class="btn btn-dark btn-sm d-flex" @click="addForeign">
|
||||||
|
<i class="mdi mdi-24px mdi-link-plus mr-1" />
|
||||||
<span>{{ $t('word.add') }}</span>
|
<span>{{ $t('word.add') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-link-plus ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
||||||
@@ -27,8 +28,8 @@
|
|||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
@click.prevent="clearChanges"
|
@click.prevent="clearChanges"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ $t('word.clear') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -266,6 +267,12 @@ export default {
|
|||||||
addNotification: 'notifications/addNotification'
|
addNotification: 'notifications/addNotification'
|
||||||
}),
|
}),
|
||||||
confirmForeignsChange () {
|
confirmForeignsChange () {
|
||||||
|
this.foreignProxy = this.foreignProxy.filter(foreign =>
|
||||||
|
foreign.field &&
|
||||||
|
foreign.refField &&
|
||||||
|
foreign.table &&
|
||||||
|
foreign.refTable
|
||||||
|
);
|
||||||
this.$emit('foreigns-update', this.foreignProxy);
|
this.$emit('foreigns-update', this.foreignProxy);
|
||||||
},
|
},
|
||||||
selectForeign (event, id) {
|
selectForeign (event, id) {
|
||||||
@@ -330,6 +337,8 @@ export default {
|
|||||||
this.selectedForeignID = this.foreignProxy.length ? this.foreignProxy[0]._id : '';
|
this.selectedForeignID = this.foreignProxy.length ? this.foreignProxy[0]._id : '';
|
||||||
},
|
},
|
||||||
async getRefFields () {
|
async getRefFields () {
|
||||||
|
if (!this.selectedForeignObj.refTable) return;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.selectedForeignObj.refSchema,
|
schema: this.selectedForeignObj.refSchema,
|
||||||
@@ -361,7 +370,7 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.tile {
|
.tile {
|
||||||
border-radius: 2px;
|
border-radius: $border-radius;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-cogs mr-1" /> {{ $t('word.options') }} "{{ localOptions.name }}"
|
<i class="mdi mdi-24px mdi-cogs mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.options') }} "{{ localOptions.name }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" /> {{ $t('word.parameters') }} "{{ func }}"
|
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.parameters') }} "{{ func }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -18,8 +19,8 @@
|
|||||||
<div class="panel-header pt-0 pl-0">
|
<div class="panel-header pt-0 pl-0">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<button class="btn btn-dark btn-sm d-flex" @click="addParameter">
|
<button class="btn btn-dark btn-sm d-flex" @click="addParameter">
|
||||||
|
<i class="mdi mdi-24px mdi-plus mr-1" />
|
||||||
<span>{{ $t('word.add') }}</span>
|
<span>{{ $t('word.add') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-plus ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
||||||
@@ -27,8 +28,8 @@
|
|||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
@click.prevent="clearChanges"
|
@click.prevent="clearChanges"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ $t('word.clear') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -273,7 +274,7 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.tile {
|
.tile {
|
||||||
border-radius: 2px;
|
border-radius: $border-radius;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-key mdi-rotate-45 mr-1" /> {{ $t('word.indexes') }} "{{ table }}"
|
<i class="mdi mdi-24px mdi-key mdi-rotate-45 mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.indexes') }} "{{ table }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -18,8 +19,8 @@
|
|||||||
<div class="panel-header pt-0 pl-0">
|
<div class="panel-header pt-0 pl-0">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<button class="btn btn-dark btn-sm d-flex" @click="addIndex">
|
<button class="btn btn-dark btn-sm d-flex" @click="addIndex">
|
||||||
|
<i class="mdi mdi-24px mdi-key-plus mr-1" />
|
||||||
<span>{{ $t('word.add') }}</span>
|
<span>{{ $t('word.add') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-key-plus ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
||||||
@@ -27,8 +28,8 @@
|
|||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
@click.prevent="clearChanges"
|
@click.prevent="clearChanges"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ $t('word.clear') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -185,6 +186,7 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
confirmIndexesChange () {
|
confirmIndexesChange () {
|
||||||
|
this.indexesProxy = this.indexesProxy.filter(index => index.fields.length);
|
||||||
this.$emit('indexes-update', this.indexesProxy);
|
this.$emit('indexes-update', this.indexesProxy);
|
||||||
},
|
},
|
||||||
selectIndex (event, id) {
|
selectIndex (event, id) {
|
||||||
@@ -246,7 +248,7 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.tile {
|
.tile {
|
||||||
border-radius: 2px;
|
border-radius: $border-radius;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-cogs mr-1" /> {{ $t('word.options') }} "{{ table }}"
|
<i class="mdi mdi-24px mdi-cogs mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.options') }} "{{ table }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -47,6 +48,7 @@
|
|||||||
v-model="optionsProxy.autoIncrement"
|
v-model="optionsProxy.autoIncrement"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
type="number"
|
type="number"
|
||||||
|
:disabled="optionsProxy.autoIncrement === null"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-cogs mr-1" /> {{ $t('word.options') }} "{{ localOptions.name }}"
|
<i class="mdi mdi-24px mdi-cogs mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.options') }} "{{ localOptions.name }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" /> {{ $t('word.parameters') }} "{{ routine }}"
|
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.parameters') }} "{{ routine }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -18,8 +19,8 @@
|
|||||||
<div class="panel-header pt-0 pl-0">
|
<div class="panel-header pt-0 pl-0">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<button class="btn btn-dark btn-sm d-flex" @click="addParameter">
|
<button class="btn btn-dark btn-sm d-flex" @click="addParameter">
|
||||||
|
<i class="mdi mdi-24px mdi-plus mr-1" />
|
||||||
<span>{{ $t('word.add') }}</span>
|
<span>{{ $t('word.add') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-plus ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
class="btn btn-dark btn-sm d-flex ml-2 mr-0"
|
||||||
@@ -27,8 +28,8 @@
|
|||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
@click.prevent="clearChanges"
|
@click.prevent="clearChanges"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ $t('word.clear') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -273,7 +274,7 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.tile {
|
.tile {
|
||||||
border-radius: 2px;
|
border-radius: $border-radius;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-timer mr-1" /> {{ $t('word.timing') }} "{{ localOptions.name }}"
|
<i class="mdi mdi-24px mdi-timer mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.timing') }} "{{ localOptions.name }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="workspace-query-tab column col-12 columns col-gapless">
|
<div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
|
||||||
<div class="workspace-query-runner column col-12">
|
<div class="workspace-query-runner column col-12">
|
||||||
<div class="workspace-query-runner-footer">
|
<div class="workspace-query-runner-footer">
|
||||||
<div class="workspace-query-buttons">
|
<div class="workspace-query-buttons">
|
||||||
@@ -10,46 +10,61 @@
|
|||||||
title="CTRL+S"
|
title="CTRL+S"
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ $t('word.save') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-content-save ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged || isSaving"
|
||||||
class="btn btn-link btn-sm mr-0"
|
class="btn btn-link btn-sm mr-0"
|
||||||
:title="$t('message.clearChanges')"
|
:title="$t('message.clearChanges')"
|
||||||
@click="clearChanges"
|
@click="clearChanges"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ $t('word.clear') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="divider-vert py-3" />
|
<div class="divider-vert py-3" />
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
:disabled="isSaving"
|
||||||
class="btn btn-dark btn-sm"
|
class="btn btn-dark btn-sm"
|
||||||
:title="$t('message.addNewField')"
|
:title="$t('message.addNewField')"
|
||||||
@click="addField"
|
@click="addField"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-playlist-plus mr-1" />
|
||||||
<span>{{ $t('word.add') }}</span>
|
<span>{{ $t('word.add') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-playlist-plus ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
:disabled="isSaving"
|
||||||
class="btn btn-dark btn-sm"
|
class="btn btn-dark btn-sm"
|
||||||
:title="$t('message.manageIndexes')"
|
:title="$t('message.manageIndexes')"
|
||||||
@click="showIntdexesModal"
|
@click="showIntdexesModal"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-key mdi-rotate-45 mr-1" />
|
||||||
<span>{{ $t('word.indexes') }}</span>
|
<span>{{ $t('word.indexes') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-key mdi-rotate-45 ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-dark btn-sm" @click="showForeignModal">
|
<button
|
||||||
|
class="btn btn-dark btn-sm"
|
||||||
|
:disabled="isSaving"
|
||||||
|
@click="showForeignModal"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-key-link mr-1" />
|
||||||
<span>{{ $t('word.foreignKeys') }}</span>
|
<span>{{ $t('word.foreignKeys') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-key-link ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-dark btn-sm" @click="showOptionsModal">
|
<button
|
||||||
|
class="btn btn-dark btn-sm"
|
||||||
|
:disabled="isSaving"
|
||||||
|
@click="showOptionsModal"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-cogs mr-1" />
|
||||||
<span>{{ $t('word.options') }}</span>
|
<span>{{ $t('word.options') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-cogs ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="workspace-query-info">
|
||||||
|
<div class="d-flex" :title="$t('word.schema')">
|
||||||
|
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-results column col-12 p-relative">
|
<div class="workspace-query-results column col-12 p-relative">
|
||||||
@@ -66,6 +81,7 @@
|
|||||||
:table="table"
|
:table="table"
|
||||||
:schema="schema"
|
:schema="schema"
|
||||||
mode="table"
|
mode="table"
|
||||||
|
@duplicate-field="duplicateField"
|
||||||
@remove-field="removeField"
|
@remove-field="removeField"
|
||||||
@add-new-index="addNewIndex"
|
@add-new-index="addNewIndex"
|
||||||
@add-to-index="addToIndex"
|
@add-to-index="addToIndex"
|
||||||
@@ -126,11 +142,12 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object,
|
connection: Object,
|
||||||
table: String
|
isSelected: Boolean,
|
||||||
|
table: String,
|
||||||
|
schema: String
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
tabUid: 'prop',
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isSaving: false,
|
isSaving: false,
|
||||||
isOptionsModal: false,
|
isOptionsModal: false,
|
||||||
@@ -157,6 +174,9 @@ export default {
|
|||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
},
|
},
|
||||||
|
tabUid () {
|
||||||
|
return this.$vnode.key;
|
||||||
|
},
|
||||||
tableOptions () {
|
tableOptions () {
|
||||||
const db = this.workspace.structure.find(db => db.name === this.schema);
|
const db = this.workspace.structure.find(db => db.name === this.schema);
|
||||||
return db && this.table ? db.tables.find(table => table.name === this.table) : {};
|
return db && this.table ? db.tables.find(table => table.name === this.table) : {};
|
||||||
@@ -164,12 +184,6 @@ export default {
|
|||||||
defaultEngine () {
|
defaultEngine () {
|
||||||
return this.getDatabaseVariable(this.connection.uid, 'default_storage_engine').value || '';
|
return this.getDatabaseVariable(this.connection.uid, 'default_storage_engine').value || '';
|
||||||
},
|
},
|
||||||
isSelected () {
|
|
||||||
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.table;
|
|
||||||
},
|
|
||||||
schema () {
|
|
||||||
return this.workspace.breadcrumbs.schema;
|
|
||||||
},
|
|
||||||
schemaTables () {
|
schemaTables () {
|
||||||
const schemaTables = this.workspace.structure
|
const schemaTables = this.workspace.structure
|
||||||
.filter(schema => schema.name === this.schema)
|
.filter(schema => schema.name === this.schema)
|
||||||
@@ -185,6 +199,12 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
schema () {
|
||||||
|
if (this.isSelected) {
|
||||||
|
this.getFieldsData();
|
||||||
|
this.lastTable = this.table;
|
||||||
|
}
|
||||||
|
},
|
||||||
table () {
|
table () {
|
||||||
if (this.isSelected) {
|
if (this.isSelected) {
|
||||||
this.getFieldsData();
|
this.getFieldsData();
|
||||||
@@ -192,17 +212,19 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
isSelected (val) {
|
isSelected (val) {
|
||||||
if (val && this.lastTable !== this.table) {
|
if (val) {
|
||||||
|
this.changeBreadcrumbs({ schema: this.schema, table: this.table });
|
||||||
|
|
||||||
|
if (this.lastTable !== this.table)
|
||||||
this.getFieldsData();
|
this.getFieldsData();
|
||||||
this.lastTable = this.table;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isChanged (val) {
|
isChanged (val) {
|
||||||
if (this.isSelected && this.lastTable === this.table && this.table !== null)
|
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
|
||||||
this.setUnsavedChanges(val);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
|
this.getFieldsData();
|
||||||
window.addEventListener('keydown', this.onKey);
|
window.addEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
@@ -213,26 +235,45 @@ export default {
|
|||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
refreshStructure: 'workspaces/refreshStructure',
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
||||||
|
renameTabs: 'workspaces/renameTabs',
|
||||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||||
}),
|
}),
|
||||||
async getFieldsData () {
|
async getFieldsData () {
|
||||||
if (!this.table) return;
|
if (!this.table) return;
|
||||||
|
|
||||||
this.localFields = [];
|
this.localFields = [];
|
||||||
|
this.lastTable = this.table;
|
||||||
this.newFieldsCounter = 0;
|
this.newFieldsCounter = 0;
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
try {
|
||||||
this.localOptions = JSON.parse(JSON.stringify(this.tableOptions));
|
this.localOptions = JSON.parse(JSON.stringify(this.tableOptions));
|
||||||
|
}
|
||||||
|
catch (err) {}
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.schema,
|
schema: this.schema,
|
||||||
table: this.workspace.breadcrumbs.table
|
table: this.table
|
||||||
};
|
};
|
||||||
|
|
||||||
try { // Columns data
|
try { // Columns data
|
||||||
const { status, response } = await Tables.getTableColumns(params);
|
const { status, response } = await Tables.getTableColumns(params);
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.originalFields = response.map(field => {
|
this.originalFields = response.map(field => {
|
||||||
|
if (field.autoIncrement)
|
||||||
|
field.defaultType = 'autoincrement';
|
||||||
|
else if (field.default === null)
|
||||||
|
field.defaultType = 'noval';
|
||||||
|
else if (field.default === 'NULL')
|
||||||
|
field.defaultType = 'null';
|
||||||
|
else if (isNaN(+field.default) && field.default.charAt(0) !== '\'')
|
||||||
|
field.defaultType = 'expression';
|
||||||
|
else {
|
||||||
|
field.defaultType = 'custom';
|
||||||
|
if (isNaN(+field.default) && !field.default.includes('\''))
|
||||||
|
field.default = `'${field.default}'`;
|
||||||
|
}
|
||||||
|
|
||||||
return { ...field, _id: uidGen() };
|
return { ...field, _id: uidGen() };
|
||||||
});
|
});
|
||||||
this.localFields = JSON.parse(JSON.stringify(this.originalFields));
|
this.localFields = JSON.parse(JSON.stringify(this.originalFields));
|
||||||
@@ -396,7 +437,7 @@ export default {
|
|||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.schema,
|
schema: this.schema,
|
||||||
table: this.workspace.breadcrumbs.table,
|
table: this.table,
|
||||||
additions,
|
additions,
|
||||||
changes,
|
changes,
|
||||||
deletions,
|
deletions,
|
||||||
@@ -414,10 +455,17 @@ export default {
|
|||||||
await this.refreshStructure(this.connection.uid);
|
await this.refreshStructure(this.connection.uid);
|
||||||
|
|
||||||
if (oldName !== this.localOptions.name) {
|
if (oldName !== this.localOptions.name) {
|
||||||
this.setUnsavedChanges(false);
|
this.renameTabs({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
elementName: oldName,
|
||||||
|
elementNewName: this.localOptions.name,
|
||||||
|
elementType: 'table'
|
||||||
|
});
|
||||||
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, table: this.localOptions.name });
|
this.changeBreadcrumbs({ schema: this.schema, table: this.localOptions.name });
|
||||||
}
|
}
|
||||||
|
else
|
||||||
this.getFieldsData();
|
this.getFieldsData();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -480,6 +528,13 @@ export default {
|
|||||||
return key;
|
return key;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
duplicateField (uid) {
|
||||||
|
const fieldToClone = Object.assign({}, this.localFields.find(field => field._id === uid));
|
||||||
|
fieldToClone._id = uidGen();
|
||||||
|
fieldToClone.name = `${fieldToClone.name}_copy`;
|
||||||
|
fieldToClone.order = this.localFields.length + 1;
|
||||||
|
this.localFields = [...this.localFields, fieldToClone];
|
||||||
|
},
|
||||||
removeField (uid) {
|
removeField (uid) {
|
||||||
this.localFields = this.localFields.filter(field => field._id !== uid);
|
this.localFields = this.localFields.filter(field => field._id !== uid);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="workspace-query-tab column col-12 columns col-gapless">
|
<div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
|
||||||
<div class="workspace-query-runner column col-12">
|
<div class="workspace-query-runner column col-12">
|
||||||
<div class="workspace-query-runner-footer">
|
<div class="workspace-query-runner-footer">
|
||||||
<div class="workspace-query-buttons">
|
<div class="workspace-query-buttons">
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
title="CTRL+S"
|
title="CTRL+S"
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ $t('word.save') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-content-save ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
@@ -19,8 +19,8 @@
|
|||||||
:title="$t('message.clearChanges')"
|
:title="$t('message.clearChanges')"
|
||||||
@click="clearChanges"
|
@click="clearChanges"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ $t('word.clear') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="divider-vert py-3" />
|
<div class="divider-vert py-3" />
|
||||||
@@ -30,18 +30,23 @@
|
|||||||
:disabled="isChanged"
|
:disabled="isChanged"
|
||||||
@click="runFunctionCheck"
|
@click="runFunctionCheck"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-play mr-1" />
|
||||||
<span>{{ $t('word.run') }}</span>
|
<span>{{ $t('word.run') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-play ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-dark btn-sm" @click="showParamsModal">
|
<button class="btn btn-dark btn-sm" @click="showParamsModal">
|
||||||
|
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
||||||
<span>{{ $t('word.parameters') }}</span>
|
<span>{{ $t('word.parameters') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-dots-horizontal ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-dark btn-sm" @click="showOptionsModal">
|
<button class="btn btn-dark btn-sm" @click="showOptionsModal">
|
||||||
|
<i class="mdi mdi-24px mdi-cogs mr-1" />
|
||||||
<span>{{ $t('word.options') }}</span>
|
<span>{{ $t('word.options') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-cogs ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="workspace-query-info">
|
||||||
|
<div class="d-flex" :title="$t('word.schema')">
|
||||||
|
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||||
@@ -102,11 +107,12 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object,
|
connection: Object,
|
||||||
function: String
|
function: String,
|
||||||
|
isSelected: Boolean,
|
||||||
|
schema: String
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
tabUid: 'prop',
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isSaving: false,
|
isSaving: false,
|
||||||
isOptionsModal: false,
|
isOptionsModal: false,
|
||||||
@@ -127,17 +133,16 @@ export default {
|
|||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
},
|
},
|
||||||
isSelected () {
|
tabUid () {
|
||||||
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.function;
|
return this.$vnode.key;
|
||||||
},
|
|
||||||
schema () {
|
|
||||||
return this.workspace.breadcrumbs.schema;
|
|
||||||
},
|
},
|
||||||
isChanged () {
|
isChanged () {
|
||||||
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
|
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
|
||||||
},
|
},
|
||||||
isDefinerInUsers () {
|
isDefinerInUsers () {
|
||||||
return this.originalFunction ? this.workspace.users.some(user => this.originalFunction.definer === `\`${user.name}\`@\`${user.host}\``) : true;
|
return this.originalFunction
|
||||||
|
? this.workspace.users.some(user => this.originalFunction.definer === `\`${user.name}\`@\`${user.host}\``)
|
||||||
|
: true;
|
||||||
},
|
},
|
||||||
schemaTables () {
|
schemaTables () {
|
||||||
const schemaTables = this.workspace.structure
|
const schemaTables = this.workspace.structure
|
||||||
@@ -148,6 +153,13 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
async schema () {
|
||||||
|
if (this.isSelected) {
|
||||||
|
await this.getFunctionData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
||||||
|
this.lastFunction = this.function;
|
||||||
|
}
|
||||||
|
},
|
||||||
async function () {
|
async function () {
|
||||||
if (this.isSelected) {
|
if (this.isSelected) {
|
||||||
await this.getFunctionData();
|
await this.getFunctionData();
|
||||||
@@ -156,26 +168,32 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async isSelected (val) {
|
async isSelected (val) {
|
||||||
if (val && this.lastFunction !== this.function) {
|
if (val) {
|
||||||
await this.getFunctionData();
|
this.changeBreadcrumbs({ schema: this.schema, function: this.function });
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
|
||||||
this.lastFunction = this.function;
|
setTimeout(() => {
|
||||||
|
this.resizeQueryEditor();
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
if (this.lastFunction !== this.function)
|
||||||
|
this.getRoutineData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isChanged (val) {
|
isChanged (val) {
|
||||||
if (this.isSelected && this.lastFunction === this.function && this.function !== null)
|
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
|
||||||
this.setUnsavedChanges(val);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async created () {
|
||||||
|
await this.getFunctionData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
window.addEventListener('resize', this.resizeQueryEditor);
|
window.addEventListener('resize', this.resizeQueryEditor);
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed () {
|
||||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
},
|
},
|
||||||
created () {
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
window.removeEventListener('keydown', this.onKey);
|
window.removeEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
@@ -183,20 +201,22 @@ export default {
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
refreshStructure: 'workspaces/refreshStructure',
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
renameTabs: 'workspaces/renameTabs',
|
||||||
|
newTab: 'workspaces/newTab',
|
||||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
newTab: 'workspaces/newTab'
|
setUnsavedChanges: 'workspaces/setUnsavedChanges'
|
||||||
}),
|
}),
|
||||||
async getFunctionData () {
|
async getFunctionData () {
|
||||||
if (!this.function) return;
|
if (!this.function) return;
|
||||||
|
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
this.localFunction = { sql: '' };
|
this.localFunction = { sql: '' };
|
||||||
|
this.lastFunction = this.function;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.schema,
|
schema: this.schema,
|
||||||
func: this.workspace.breadcrumbs.function
|
func: this.function
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -227,9 +247,9 @@ export default {
|
|||||||
this.isSaving = true;
|
this.isSaving = true;
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.schema,
|
|
||||||
func: {
|
func: {
|
||||||
...this.localFunction,
|
...this.localFunction,
|
||||||
|
schema: this.schema,
|
||||||
oldName: this.originalFunction.name
|
oldName: this.originalFunction.name
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -243,10 +263,17 @@ export default {
|
|||||||
await this.refreshStructure(this.connection.uid);
|
await this.refreshStructure(this.connection.uid);
|
||||||
|
|
||||||
if (oldName !== this.localFunction.name) {
|
if (oldName !== this.localFunction.name) {
|
||||||
this.setUnsavedChanges(false);
|
this.renameTabs({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
elementName: oldName,
|
||||||
|
elementNewName: this.localFunction.name,
|
||||||
|
elementType: 'function'
|
||||||
|
});
|
||||||
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, function: this.localFunction.name });
|
this.changeBreadcrumbs({ schema: this.schema, function: this.localFunction.name });
|
||||||
}
|
}
|
||||||
|
else
|
||||||
this.getFunctionData();
|
this.getFunctionData();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -301,7 +328,7 @@ export default {
|
|||||||
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
|
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.newTab({ uid: this.connection.uid, content: sql, autorun: true });
|
this.newTab({ uid: this.connection.uid, content: sql, type: 'query', autorun: true });
|
||||||
},
|
},
|
||||||
showOptionsModal () {
|
showOptionsModal () {
|
||||||
this.isOptionsModal = true;
|
this.isOptionsModal = true;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="workspace-query-tab column col-12 columns col-gapless">
|
<div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
|
||||||
<div class="workspace-query-runner column col-12">
|
<div class="workspace-query-runner column col-12">
|
||||||
<div class="workspace-query-runner-footer">
|
<div class="workspace-query-runner-footer">
|
||||||
<div class="workspace-query-buttons">
|
<div class="workspace-query-buttons">
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
title="CTRL+S"
|
title="CTRL+S"
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ $t('word.save') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-content-save ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
@@ -19,8 +19,8 @@
|
|||||||
:title="$t('message.clearChanges')"
|
:title="$t('message.clearChanges')"
|
||||||
@click="clearChanges"
|
@click="clearChanges"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ $t('word.clear') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="divider-vert py-3" />
|
<div class="divider-vert py-3" />
|
||||||
@@ -30,18 +30,23 @@
|
|||||||
:disabled="isChanged"
|
:disabled="isChanged"
|
||||||
@click="runRoutineCheck"
|
@click="runRoutineCheck"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-play mr-1" />
|
||||||
<span>{{ $t('word.run') }}</span>
|
<span>{{ $t('word.run') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-play ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-dark btn-sm" @click="showParamsModal">
|
<button class="btn btn-dark btn-sm" @click="showParamsModal">
|
||||||
|
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
||||||
<span>{{ $t('word.parameters') }}</span>
|
<span>{{ $t('word.parameters') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-dots-horizontal ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-dark btn-sm" @click="showOptionsModal">
|
<button class="btn btn-dark btn-sm" @click="showOptionsModal">
|
||||||
|
<i class="mdi mdi-24px mdi-cogs mr-1" />
|
||||||
<span>{{ $t('word.options') }}</span>
|
<span>{{ $t('word.options') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-cogs ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="workspace-query-info">
|
||||||
|
<div class="d-flex" :title="$t('word.schema')">
|
||||||
|
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||||
@@ -103,11 +108,12 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object,
|
connection: Object,
|
||||||
routine: String
|
routine: String,
|
||||||
|
isSelected: Boolean,
|
||||||
|
schema: String
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
tabUid: 'prop',
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isSaving: false,
|
isSaving: false,
|
||||||
isOptionsModal: false,
|
isOptionsModal: false,
|
||||||
@@ -128,11 +134,8 @@ export default {
|
|||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
},
|
},
|
||||||
isSelected () {
|
tabUid () {
|
||||||
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.routine;
|
return this.$vnode.key;
|
||||||
},
|
|
||||||
schema () {
|
|
||||||
return this.workspace.breadcrumbs.schema;
|
|
||||||
},
|
},
|
||||||
isChanged () {
|
isChanged () {
|
||||||
return JSON.stringify(this.originalRoutine) !== JSON.stringify(this.localRoutine);
|
return JSON.stringify(this.originalRoutine) !== JSON.stringify(this.localRoutine);
|
||||||
@@ -149,6 +152,13 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
async schema () {
|
||||||
|
if (this.isSelected) {
|
||||||
|
await this.getRoutineData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
|
||||||
|
this.lastRoutine = this.routine;
|
||||||
|
}
|
||||||
|
},
|
||||||
async routine () {
|
async routine () {
|
||||||
if (this.isSelected) {
|
if (this.isSelected) {
|
||||||
await this.getRoutineData();
|
await this.getRoutineData();
|
||||||
@@ -157,26 +167,32 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async isSelected (val) {
|
async isSelected (val) {
|
||||||
if (val && this.lastRoutine !== this.routine) {
|
if (val) {
|
||||||
await this.getRoutineData();
|
this.changeBreadcrumbs({ schema: this.schema, routine: this.routine });
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
|
|
||||||
this.lastRoutine = this.routine;
|
setTimeout(() => {
|
||||||
|
this.resizeQueryEditor();
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
if (this.lastRoutine !== this.routine)
|
||||||
|
this.getRoutineData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isChanged (val) {
|
isChanged (val) {
|
||||||
if (this.isSelected && this.lastRoutine === this.routine && this.routine !== null)
|
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
|
||||||
this.setUnsavedChanges(val);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async created () {
|
||||||
|
await this.getRoutineData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql);
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
window.addEventListener('resize', this.resizeQueryEditor);
|
window.addEventListener('resize', this.resizeQueryEditor);
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed () {
|
||||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
},
|
},
|
||||||
created () {
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
window.removeEventListener('keydown', this.onKey);
|
window.removeEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
@@ -184,19 +200,22 @@ export default {
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
refreshStructure: 'workspaces/refreshStructure',
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
renameTabs: 'workspaces/renameTabs',
|
||||||
|
newTab: 'workspaces/newTab',
|
||||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
newTab: 'workspaces/newTab'
|
setUnsavedChanges: 'workspaces/setUnsavedChanges'
|
||||||
}),
|
}),
|
||||||
async getRoutineData () {
|
async getRoutineData () {
|
||||||
if (!this.routine) return;
|
if (!this.routine) return;
|
||||||
|
|
||||||
this.localRoutine = { sql: '' };
|
this.localRoutine = { sql: '' };
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
this.lastRoutine = this.routine;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.schema,
|
schema: this.schema,
|
||||||
routine: this.workspace.breadcrumbs.procedure
|
routine: this.routine
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -227,9 +246,9 @@ export default {
|
|||||||
this.isSaving = true;
|
this.isSaving = true;
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.schema,
|
|
||||||
routine: {
|
routine: {
|
||||||
...this.localRoutine,
|
...this.localRoutine,
|
||||||
|
schema: this.schema,
|
||||||
oldName: this.originalRoutine.name
|
oldName: this.originalRoutine.name
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -243,10 +262,17 @@ export default {
|
|||||||
await this.refreshStructure(this.connection.uid);
|
await this.refreshStructure(this.connection.uid);
|
||||||
|
|
||||||
if (oldName !== this.localRoutine.name) {
|
if (oldName !== this.localRoutine.name) {
|
||||||
this.setUnsavedChanges(false);
|
this.renameTabs({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
elementName: oldName,
|
||||||
|
elementNewName: this.localRoutine.name,
|
||||||
|
elementType: 'procedure'
|
||||||
|
});
|
||||||
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, procedure: this.localRoutine.name });
|
this.changeBreadcrumbs({ schema: this.schema, procedure: this.localRoutine.name });
|
||||||
}
|
}
|
||||||
|
else
|
||||||
this.getRoutineData();
|
this.getRoutineData();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -299,7 +325,7 @@ export default {
|
|||||||
sql = `CALL \`${this.originalRoutine.name}\`(${params.join(',')})`;
|
sql = `CALL \`${this.originalRoutine.name}\`(${params.join(',')})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.newTab({ uid: this.connection.uid, content: sql, autorun: true });
|
this.newTab({ uid: this.connection.uid, content: sql, type: 'query', autorun: true });
|
||||||
},
|
},
|
||||||
showOptionsModal () {
|
showOptionsModal () {
|
||||||
this.isOptionsModal = true;
|
this.isOptionsModal = true;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="workspace-query-tab column col-12 columns col-gapless">
|
<div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
|
||||||
<div class="workspace-query-runner column col-12">
|
<div class="workspace-query-runner column col-12">
|
||||||
<div class="workspace-query-runner-footer">
|
<div class="workspace-query-runner-footer">
|
||||||
<div class="workspace-query-buttons">
|
<div class="workspace-query-buttons">
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
title="CTRL+S"
|
title="CTRL+S"
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ $t('word.save') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-content-save ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
@@ -19,16 +19,21 @@
|
|||||||
:title="$t('message.clearChanges')"
|
:title="$t('message.clearChanges')"
|
||||||
@click="clearChanges"
|
@click="clearChanges"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ $t('word.clear') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="divider-vert py-3" />
|
<div class="divider-vert py-3" />
|
||||||
<button class="btn btn-dark btn-sm" @click="showTimingModal">
|
<button class="btn btn-dark btn-sm" @click="showTimingModal">
|
||||||
|
<i class="mdi mdi-24px mdi-timer mr-1" />
|
||||||
<span>{{ $t('word.timing') }}</span>
|
<span>{{ $t('word.timing') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-timer ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="workspace-query-info">
|
||||||
|
<div class="d-flex" :title="$t('word.schema')">
|
||||||
|
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -153,11 +158,12 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object,
|
connection: Object,
|
||||||
scheduler: String
|
scheduler: String,
|
||||||
|
isSelected: Boolean,
|
||||||
|
schema: String
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
tabUid: 'prop',
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isSaving: false,
|
isSaving: false,
|
||||||
isTimingModal: false,
|
isTimingModal: false,
|
||||||
@@ -176,11 +182,8 @@ export default {
|
|||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
},
|
},
|
||||||
isSelected () {
|
tabUid () {
|
||||||
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.scheduler;
|
return this.$vnode.key;
|
||||||
},
|
|
||||||
schema () {
|
|
||||||
return this.workspace.breadcrumbs.schema;
|
|
||||||
},
|
},
|
||||||
isChanged () {
|
isChanged () {
|
||||||
return JSON.stringify(this.originalScheduler) !== JSON.stringify(this.localScheduler);
|
return JSON.stringify(this.originalScheduler) !== JSON.stringify(this.localScheduler);
|
||||||
@@ -197,6 +200,13 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
async schema () {
|
||||||
|
if (this.isSelected) {
|
||||||
|
await this.getSchedulerData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localScheduler.sql);
|
||||||
|
this.lastScheduler = this.scheduler;
|
||||||
|
}
|
||||||
|
},
|
||||||
async scheduler () {
|
async scheduler () {
|
||||||
if (this.isSelected) {
|
if (this.isSelected) {
|
||||||
await this.getSchedulerData();
|
await this.getSchedulerData();
|
||||||
@@ -205,26 +215,32 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async isSelected (val) {
|
async isSelected (val) {
|
||||||
if (val && this.lastScheduler !== this.scheduler) {
|
if (val) {
|
||||||
await this.getSchedulerData();
|
this.changeBreadcrumbs({ schema: this.schema, scheduler: this.scheduler });
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localScheduler.sql);
|
|
||||||
this.lastScheduler = this.scheduler;
|
setTimeout(() => {
|
||||||
|
this.resizeQueryEditor();
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
if (this.lastScheduler !== this.scheduler)
|
||||||
|
this.getSchedulerData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isChanged (val) {
|
isChanged (val) {
|
||||||
if (this.isSelected && this.lastScheduler === this.scheduler && this.scheduler !== null)
|
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
|
||||||
this.setUnsavedChanges(val);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async created () {
|
||||||
|
await this.getSchedulerData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localScheduler.sql);
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
window.addEventListener('resize', this.resizeQueryEditor);
|
window.addEventListener('resize', this.resizeQueryEditor);
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed () {
|
||||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
},
|
},
|
||||||
created () {
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
window.removeEventListener('keydown', this.onKey);
|
window.removeEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
@@ -232,17 +248,21 @@ export default {
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
refreshStructure: 'workspaces/refreshStructure',
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
renameTabs: 'workspaces/renameTabs',
|
||||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
newTab: 'workspaces/newTab',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
|
setUnsavedChanges: 'workspaces/setUnsavedChanges'
|
||||||
}),
|
}),
|
||||||
async getSchedulerData () {
|
async getSchedulerData () {
|
||||||
if (!this.scheduler) return;
|
if (!this.scheduler) return;
|
||||||
|
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
this.lastScheduler = this.scheduler;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.schema,
|
schema: this.schema,
|
||||||
scheduler: this.workspace.breadcrumbs.scheduler
|
scheduler: this.scheduler
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -267,9 +287,9 @@ export default {
|
|||||||
this.isSaving = true;
|
this.isSaving = true;
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.schema,
|
|
||||||
scheduler: {
|
scheduler: {
|
||||||
...this.localScheduler,
|
...this.localScheduler,
|
||||||
|
schema: this.schema,
|
||||||
oldName: this.originalScheduler.name
|
oldName: this.originalScheduler.name
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -283,10 +303,17 @@ export default {
|
|||||||
await this.refreshStructure(this.connection.uid);
|
await this.refreshStructure(this.connection.uid);
|
||||||
|
|
||||||
if (oldName !== this.localScheduler.name) {
|
if (oldName !== this.localScheduler.name) {
|
||||||
this.setUnsavedChanges(false);
|
this.renameTabs({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
elementName: oldName,
|
||||||
|
elementNewName: this.localScheduler.name,
|
||||||
|
elementType: 'scheduler'
|
||||||
|
});
|
||||||
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, scheduler: this.localScheduler.name });
|
this.changeBreadcrumbs({ schema: this.schema, scheduler: this.localScheduler.name });
|
||||||
}
|
}
|
||||||
|
else
|
||||||
this.getSchedulerData();
|
this.getSchedulerData();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="workspace-query-tab column col-12 columns col-gapless">
|
<div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
|
||||||
<div class="workspace-query-runner column col-12">
|
<div class="workspace-query-runner column col-12">
|
||||||
<div class="workspace-query-runner-footer">
|
<div class="workspace-query-runner-footer">
|
||||||
<div class="workspace-query-buttons">
|
<div class="workspace-query-buttons">
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
title="CTRL+S"
|
title="CTRL+S"
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ $t('word.save') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-content-save ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
@@ -19,10 +19,15 @@
|
|||||||
:title="$t('message.clearChanges')"
|
:title="$t('message.clearChanges')"
|
||||||
@click="clearChanges"
|
@click="clearChanges"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ $t('word.clear') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="workspace-query-info">
|
||||||
|
<div class="d-flex" :title="$t('word.schema')">
|
||||||
|
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -37,7 +42,7 @@
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-auto">
|
<div v-if="customizations.definer" 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 +72,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="columns">
|
<fieldset class="columns" :disabled="customizations.triggerOnlyRename">
|
||||||
<div class="column col-auto">
|
<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>
|
||||||
@@ -82,19 +87,33 @@
|
|||||||
<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">
|
||||||
<select v-model="localTrigger.event1" class="form-select">
|
<select v-model="localTrigger.activation" class="form-select">
|
||||||
<option>BEFORE</option>
|
<option>BEFORE</option>
|
||||||
<option>AFTER</option>
|
<option>AFTER</option>
|
||||||
</select>
|
</select>
|
||||||
<select v-model="localTrigger.event2" class="form-select">
|
<select
|
||||||
<option>INSERT</option>
|
v-if="!customizations.triggerMultipleEvents"
|
||||||
<option>UPDATE</option>
|
v-model="localTrigger.event"
|
||||||
<option>DELETE</option>
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option v-for="event in Object.keys(localEvents)" :key="event">
|
||||||
|
{{ event }}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
|
<div v-if="customizations.triggerMultipleEvents" class="px-4">
|
||||||
|
<label
|
||||||
|
v-for="event in Object.keys(localEvents)"
|
||||||
|
:key="event"
|
||||||
|
class="form-checkbox form-inline"
|
||||||
|
@change.prevent="changeEvents(event)"
|
||||||
|
>
|
||||||
|
<input :checked="localEvents[event]" type="checkbox"><i class="form-icon" /> {{ event }}
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||||
<BaseLoader v-if="isLoading" />
|
<BaseLoader v-if="isLoading" />
|
||||||
@@ -125,18 +144,20 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object,
|
connection: Object,
|
||||||
trigger: String
|
trigger: String,
|
||||||
|
isSelected: Boolean,
|
||||||
|
schema: String
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
tabUid: 'prop',
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isSaving: false,
|
isSaving: false,
|
||||||
originalTrigger: null,
|
originalTrigger: null,
|
||||||
localTrigger: { sql: '' },
|
localTrigger: { sql: '' },
|
||||||
lastTrigger: null,
|
lastTrigger: null,
|
||||||
sqlProxy: '',
|
sqlProxy: '',
|
||||||
editorHeight: 300
|
editorHeight: 300,
|
||||||
|
localEvents: { INSERT: false, UPDATE: false, DELETE: false }
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -147,11 +168,11 @@ export default {
|
|||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
},
|
},
|
||||||
isSelected () {
|
tabUid () {
|
||||||
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.trigger;
|
return this.$vnode.key;
|
||||||
},
|
},
|
||||||
schema () {
|
customizations () {
|
||||||
return this.workspace.breadcrumbs.schema;
|
return this.workspace.customizations;
|
||||||
},
|
},
|
||||||
isChanged () {
|
isChanged () {
|
||||||
return JSON.stringify(this.originalTrigger) !== JSON.stringify(this.localTrigger);
|
return JSON.stringify(this.originalTrigger) !== JSON.stringify(this.localTrigger);
|
||||||
@@ -168,6 +189,13 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
async schema () {
|
||||||
|
if (this.isSelected) {
|
||||||
|
await this.getTriggerData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
|
||||||
|
this.lastTrigger = this.trigger;
|
||||||
|
}
|
||||||
|
},
|
||||||
async trigger () {
|
async trigger () {
|
||||||
if (this.isSelected) {
|
if (this.isSelected) {
|
||||||
await this.getTriggerData();
|
await this.getTriggerData();
|
||||||
@@ -176,26 +204,32 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async isSelected (val) {
|
async isSelected (val) {
|
||||||
if (val && this.lastTrigger !== this.trigger) {
|
if (val) {
|
||||||
await this.getTriggerData();
|
this.changeBreadcrumbs({ schema: this.schema, trigger: this.trigger });
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
|
|
||||||
this.lastTrigger = this.trigger;
|
setTimeout(() => {
|
||||||
|
this.resizeQueryEditor();
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
if (this.lastTrigger !== this.trigger)
|
||||||
|
this.getTriggerData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isChanged (val) {
|
isChanged (val) {
|
||||||
if (this.isSelected && this.lastTrigger === this.trigger && this.trigger !== null)
|
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
|
||||||
this.setUnsavedChanges(val);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async created () {
|
||||||
|
await this.getTriggerData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
window.addEventListener('resize', this.resizeQueryEditor);
|
window.addEventListener('resize', this.resizeQueryEditor);
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed () {
|
||||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
},
|
},
|
||||||
created () {
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
window.removeEventListener('keydown', this.onKey);
|
window.removeEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
@@ -203,19 +237,26 @@ export default {
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
refreshStructure: 'workspaces/refreshStructure',
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
renameTabs: 'workspaces/renameTabs',
|
||||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
newTab: 'workspaces/newTab',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
|
setUnsavedChanges: 'workspaces/setUnsavedChanges'
|
||||||
}),
|
}),
|
||||||
async getTriggerData () {
|
async getTriggerData () {
|
||||||
if (!this.trigger) return;
|
if (!this.trigger) return;
|
||||||
|
|
||||||
|
Object.keys(this.localEvents).forEach(event => {
|
||||||
|
this.localEvents[event] = false;
|
||||||
|
});
|
||||||
|
|
||||||
this.localTrigger = { sql: '' };
|
this.localTrigger = { sql: '' };
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
|
this.lastTrigger = this.trigger;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.schema,
|
schema: this.schema,
|
||||||
trigger: this.workspace.breadcrumbs.trigger
|
trigger: this.trigger
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -224,6 +265,12 @@ export default {
|
|||||||
this.originalTrigger = response;
|
this.originalTrigger = response;
|
||||||
this.localTrigger = JSON.parse(JSON.stringify(this.originalTrigger));
|
this.localTrigger = JSON.parse(JSON.stringify(this.originalTrigger));
|
||||||
this.sqlProxy = this.localTrigger.sql;
|
this.sqlProxy = this.localTrigger.sql;
|
||||||
|
|
||||||
|
if (this.customizations.triggerMultipleEvents) {
|
||||||
|
this.originalTrigger.event.forEach(e => {
|
||||||
|
this.localEvents[e] = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
this.addNotification({ status: 'error', message: response });
|
||||||
@@ -235,14 +282,24 @@ export default {
|
|||||||
this.resizeQueryEditor();
|
this.resizeQueryEditor();
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
},
|
},
|
||||||
|
changeEvents (event) {
|
||||||
|
if (this.customizations.triggerMultipleEvents) {
|
||||||
|
this.localEvents[event] = !this.localEvents[event];
|
||||||
|
this.localTrigger.event = [];
|
||||||
|
for (const key in this.localEvents) {
|
||||||
|
if (this.localEvents[key])
|
||||||
|
this.localTrigger.event.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
async saveChanges () {
|
async saveChanges () {
|
||||||
if (this.isSaving) return;
|
if (this.isSaving) return;
|
||||||
this.isSaving = true;
|
this.isSaving = true;
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.schema,
|
|
||||||
trigger: {
|
trigger: {
|
||||||
...this.localTrigger,
|
...this.localTrigger,
|
||||||
|
schema: this.schema,
|
||||||
oldName: this.originalTrigger.name
|
oldName: this.originalTrigger.name
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -251,15 +308,23 @@ export default {
|
|||||||
const { status, response } = await Triggers.alterTrigger(params);
|
const { status, response } = await Triggers.alterTrigger(params);
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
const oldName = this.originalTrigger.name;
|
|
||||||
|
|
||||||
await this.refreshStructure(this.connection.uid);
|
await this.refreshStructure(this.connection.uid);
|
||||||
|
|
||||||
if (oldName !== this.localTrigger.name) {
|
if (this.originalTrigger.name !== this.localTrigger.name) {
|
||||||
this.setUnsavedChanges(false);
|
const triggerName = this.customizations.triggerTableInName ? `${this.localTrigger.table}.${this.localTrigger.name}` : this.localTrigger.name;
|
||||||
this.changeBreadcrumbs({ schema: this.schema, trigger: this.localTrigger.name });
|
const triggerOldName = this.customizations.triggerTableInName ? `${this.originalTrigger.table}.${this.originalTrigger.name}` : this.originalTrigger.name;
|
||||||
}
|
|
||||||
|
|
||||||
|
this.renameTabs({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
elementName: triggerOldName,
|
||||||
|
elementNewName: triggerName,
|
||||||
|
elementType: 'trigger'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.changeBreadcrumbs({ schema: this.schema, trigger: triggerName });
|
||||||
|
}
|
||||||
|
else
|
||||||
this.getTriggerData();
|
this.getTriggerData();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -274,6 +339,16 @@ export default {
|
|||||||
clearChanges () {
|
clearChanges () {
|
||||||
this.localTrigger = JSON.parse(JSON.stringify(this.originalTrigger));
|
this.localTrigger = JSON.parse(JSON.stringify(this.originalTrigger));
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
|
this.$refs.queryEditor.editor.session.setValue(this.localTrigger.sql);
|
||||||
|
|
||||||
|
Object.keys(this.localEvents).forEach(event => {
|
||||||
|
this.localEvents[event] = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.customizations.triggerMultipleEvents) {
|
||||||
|
this.originalTrigger.event.forEach(e => {
|
||||||
|
this.localEvents[e] = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
resizeQueryEditor () {
|
resizeQueryEditor () {
|
||||||
if (this.$refs.queryEditor) {
|
if (this.$refs.queryEditor) {
|
||||||
|
|||||||
335
src/renderer/components/WorkspacePropsTabTriggerFunction.vue
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
<template>
|
||||||
|
<div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
|
||||||
|
<div class="workspace-query-runner column col-12">
|
||||||
|
<div class="workspace-query-runner-footer">
|
||||||
|
<div class="workspace-query-buttons">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary btn-sm"
|
||||||
|
:disabled="!isChanged"
|
||||||
|
:class="{'loading':isSaving}"
|
||||||
|
title="CTRL+S"
|
||||||
|
@click="saveChanges"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
|
<span>{{ $t('word.save') }}</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
:disabled="!isChanged"
|
||||||
|
class="btn btn-link btn-sm mr-0"
|
||||||
|
:title="$t('message.clearChanges')"
|
||||||
|
@click="clearChanges"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
|
<span>{{ $t('word.clear') }}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="divider-vert py-3" />
|
||||||
|
|
||||||
|
<button class="btn btn-dark btn-sm" @click="showOptionsModal">
|
||||||
|
<i class="mdi mdi-24px mdi-cogs mr-1" />
|
||||||
|
<span>{{ $t('word.options') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="workspace-query-results column col-12 mt-2 p-relative">
|
||||||
|
<BaseLoader v-if="isLoading" />
|
||||||
|
<label class="form-label ml-2">{{ $t('message.functionBody') }}</label>
|
||||||
|
<QueryEditor
|
||||||
|
v-show="isSelected"
|
||||||
|
ref="queryEditor"
|
||||||
|
:value.sync="localFunction.sql"
|
||||||
|
:workspace="workspace"
|
||||||
|
:schema="schema"
|
||||||
|
:height="editorHeight"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<WorkspacePropsTriggerFunctionOptionsModal
|
||||||
|
v-if="isOptionsModal"
|
||||||
|
:local-options="localFunction"
|
||||||
|
:workspace="workspace"
|
||||||
|
@hide="hideOptionsModal"
|
||||||
|
@options-update="optionsUpdate"
|
||||||
|
/>
|
||||||
|
<ModalAskParameters
|
||||||
|
v-if="isAskingParameters"
|
||||||
|
:local-routine="localFunction"
|
||||||
|
:client="workspace.client"
|
||||||
|
@confirm="runFunction"
|
||||||
|
@close="hideAskParamsModal"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
|
import BaseLoader from '@/components/BaseLoader';
|
||||||
|
import QueryEditor from '@/components/QueryEditor';
|
||||||
|
import WorkspacePropsTriggerFunctionOptionsModal from '@/components/WorkspacePropsTriggerFunctionOptionsModal';
|
||||||
|
import ModalAskParameters from '@/components/ModalAskParameters';
|
||||||
|
import Functions from '@/ipc-api/Functions';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsTabTriggerFunction',
|
||||||
|
components: {
|
||||||
|
BaseLoader,
|
||||||
|
QueryEditor,
|
||||||
|
WorkspacePropsTriggerFunctionOptionsModal,
|
||||||
|
ModalAskParameters
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
connection: Object,
|
||||||
|
function: String,
|
||||||
|
isSelected: Boolean,
|
||||||
|
schema: String
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isLoading: false,
|
||||||
|
isSaving: false,
|
||||||
|
isOptionsModal: false,
|
||||||
|
isParamsModal: false,
|
||||||
|
isAskingParameters: false,
|
||||||
|
originalFunction: null,
|
||||||
|
localFunction: { sql: '' },
|
||||||
|
lastFunction: null,
|
||||||
|
sqlProxy: '',
|
||||||
|
editorHeight: 300
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.connection.uid);
|
||||||
|
},
|
||||||
|
tabUid () {
|
||||||
|
return this.$vnode.key;
|
||||||
|
},
|
||||||
|
isChanged () {
|
||||||
|
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
|
||||||
|
},
|
||||||
|
isDefinerInUsers () {
|
||||||
|
return this.originalFunction
|
||||||
|
? this.workspace.users.some(user => this.originalFunction.definer === `\`${user.name}\`@\`${user.host}\``)
|
||||||
|
: true;
|
||||||
|
},
|
||||||
|
schemaTables () {
|
||||||
|
const schemaTables = this.workspace.structure
|
||||||
|
.filter(schema => schema.name === this.schema)
|
||||||
|
.map(schema => schema.tables);
|
||||||
|
|
||||||
|
return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
async schema () {
|
||||||
|
if (this.isSelected) {
|
||||||
|
await this.getFunctionData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
||||||
|
this.lastFunction = this.function;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async function () {
|
||||||
|
if (this.isSelected) {
|
||||||
|
await this.getFunctionData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
||||||
|
this.lastFunction = this.function;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async isSelected (val) {
|
||||||
|
if (val) {
|
||||||
|
this.changeBreadcrumbs({ schema: this.schema, triggerFunction: this.function });
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.resizeQueryEditor();
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
if (this.lastFunction !== this.function)
|
||||||
|
await this.getFunctionData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isChanged (val) {
|
||||||
|
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async created () {
|
||||||
|
await this.getFunctionData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
window.addEventListener('resize', this.resizeQueryEditor);
|
||||||
|
},
|
||||||
|
destroyed () {
|
||||||
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification',
|
||||||
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
|
renameTabs: 'workspaces/renameTabs',
|
||||||
|
newTab: 'workspaces/newTab',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
|
setUnsavedChanges: 'workspaces/setUnsavedChanges'
|
||||||
|
}),
|
||||||
|
async getFunctionData () {
|
||||||
|
if (!this.function) return;
|
||||||
|
|
||||||
|
this.isLoading = true;
|
||||||
|
this.localFunction = { sql: '' };
|
||||||
|
this.lastFunction = this.function;
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
func: this.function
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Functions.getFunctionInformations(params);
|
||||||
|
if (status === 'success') {
|
||||||
|
this.originalFunction = response;
|
||||||
|
|
||||||
|
this.originalFunction.parameters = [...this.originalFunction.parameters.map(param => {
|
||||||
|
param._id = uidGen();
|
||||||
|
return param;
|
||||||
|
})];
|
||||||
|
|
||||||
|
this.localFunction = JSON.parse(JSON.stringify(this.originalFunction));
|
||||||
|
this.sqlProxy = this.localFunction.sql;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resizeQueryEditor();
|
||||||
|
this.isLoading = false;
|
||||||
|
},
|
||||||
|
async saveChanges () {
|
||||||
|
if (this.isSaving) return;
|
||||||
|
this.isSaving = true;
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
func: {
|
||||||
|
...this.localFunction,
|
||||||
|
schema: this.schema,
|
||||||
|
oldName: this.originalFunction.name
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Functions.alterTriggerFunction(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
const oldName = this.originalFunction.name;
|
||||||
|
|
||||||
|
await this.refreshStructure(this.connection.uid);
|
||||||
|
|
||||||
|
if (oldName !== this.localFunction.name) {
|
||||||
|
this.renameTabs({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
elementName: oldName,
|
||||||
|
elementNewName: this.localFunction.name,
|
||||||
|
elementType: 'triggerFunction'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.changeBreadcrumbs({ schema: this.schema, triggerFunction: this.localFunction.name });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.getFunctionData();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isSaving = false;
|
||||||
|
},
|
||||||
|
clearChanges () {
|
||||||
|
this.localFunction = JSON.parse(JSON.stringify(this.originalFunction));
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localFunction.sql);
|
||||||
|
},
|
||||||
|
resizeQueryEditor () {
|
||||||
|
if (this.$refs.queryEditor) {
|
||||||
|
const footer = document.getElementById('footer');
|
||||||
|
const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight;
|
||||||
|
this.editorHeight = size;
|
||||||
|
this.$refs.queryEditor.editor.resize();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
optionsUpdate (options) {
|
||||||
|
this.localFunction = options;
|
||||||
|
},
|
||||||
|
parametersUpdate (parameters) {
|
||||||
|
this.localFunction = { ...this.localFunction, parameters };
|
||||||
|
},
|
||||||
|
runFunctionCheck () {
|
||||||
|
if (this.localFunction.parameters.length)
|
||||||
|
this.showAskParamsModal();
|
||||||
|
else
|
||||||
|
this.runFunction();
|
||||||
|
},
|
||||||
|
runFunction (params) {
|
||||||
|
if (!params) params = [];
|
||||||
|
|
||||||
|
let sql;
|
||||||
|
switch (this.connection.client) { // TODO: move in a better place
|
||||||
|
case 'maria':
|
||||||
|
case 'mysql':
|
||||||
|
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
sql = `SELECT ${this.originalFunction.name}(${params.join(',')})`;
|
||||||
|
break;
|
||||||
|
case 'mssql':
|
||||||
|
sql = `SELECT ${this.originalFunction.name} ${params.join(',')}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.newTab({ uid: this.connection.uid, content: sql, type: 'query', autorun: true });
|
||||||
|
},
|
||||||
|
showOptionsModal () {
|
||||||
|
this.isOptionsModal = true;
|
||||||
|
},
|
||||||
|
hideOptionsModal () {
|
||||||
|
this.isOptionsModal = false;
|
||||||
|
},
|
||||||
|
showParamsModal () {
|
||||||
|
this.isParamsModal = true;
|
||||||
|
},
|
||||||
|
hideParamsModal () {
|
||||||
|
this.isParamsModal = false;
|
||||||
|
},
|
||||||
|
showAskParamsModal () {
|
||||||
|
this.isAskingParameters = true;
|
||||||
|
},
|
||||||
|
hideAskParamsModal () {
|
||||||
|
this.isAskingParameters = false;
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
if (this.isSelected) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
||||||
|
if (this.isChanged)
|
||||||
|
this.saveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="workspace-query-tab column col-12 columns col-gapless">
|
<div v-show="isSelected" class="workspace-query-tab column col-12 columns col-gapless">
|
||||||
<div class="workspace-query-runner column col-12">
|
<div class="workspace-query-runner column col-12">
|
||||||
<div class="workspace-query-runner-footer">
|
<div class="workspace-query-runner-footer">
|
||||||
<div class="workspace-query-buttons">
|
<div class="workspace-query-buttons">
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
title="CTRL+S"
|
title="CTRL+S"
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ $t('word.save') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-content-save ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
@@ -19,10 +19,15 @@
|
|||||||
:title="$t('message.clearChanges')"
|
:title="$t('message.clearChanges')"
|
||||||
@click="clearChanges"
|
@click="clearChanges"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ $t('word.clear') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="workspace-query-info">
|
||||||
|
<div class="d-flex" :title="$t('word.schema')">
|
||||||
|
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -161,7 +166,7 @@
|
|||||||
<BaseLoader v-if="isLoading" />
|
<BaseLoader v-if="isLoading" />
|
||||||
<label class="form-label ml-2">{{ $t('message.selectStatement') }}</label>
|
<label class="form-label ml-2">{{ $t('message.selectStatement') }}</label>
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
v-if="isSelected"
|
v-show="isSelected"
|
||||||
ref="queryEditor"
|
ref="queryEditor"
|
||||||
:value.sync="localView.sql"
|
:value.sync="localView.sql"
|
||||||
:workspace="workspace"
|
:workspace="workspace"
|
||||||
@@ -186,11 +191,12 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object,
|
connection: Object,
|
||||||
|
isSelected: Boolean,
|
||||||
|
schema: String,
|
||||||
view: String
|
view: String
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
tabUid: 'prop',
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isSaving: false,
|
isSaving: false,
|
||||||
originalView: null,
|
originalView: null,
|
||||||
@@ -208,11 +214,8 @@ export default {
|
|||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
},
|
},
|
||||||
isSelected () {
|
tabUid () {
|
||||||
return this.workspace.selected_tab === 'prop' && this.selectedWorkspace === this.workspace.uid && this.view;
|
return this.$vnode.key;
|
||||||
},
|
|
||||||
schema () {
|
|
||||||
return this.workspace.breadcrumbs.schema;
|
|
||||||
},
|
},
|
||||||
isChanged () {
|
isChanged () {
|
||||||
return JSON.stringify(this.originalView) !== JSON.stringify(this.localView);
|
return JSON.stringify(this.originalView) !== JSON.stringify(this.localView);
|
||||||
@@ -222,6 +225,13 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
async schema () {
|
||||||
|
if (this.isSelected) {
|
||||||
|
await this.getViewData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localView.sql);
|
||||||
|
this.lastView = this.view;
|
||||||
|
}
|
||||||
|
},
|
||||||
async view () {
|
async view () {
|
||||||
if (this.isSelected) {
|
if (this.isSelected) {
|
||||||
await this.getViewData();
|
await this.getViewData();
|
||||||
@@ -229,27 +239,33 @@ export default {
|
|||||||
this.lastView = this.view;
|
this.lastView = this.view;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async isSelected (val) {
|
isSelected (val) {
|
||||||
if (val && this.lastView !== this.view) {
|
if (val) {
|
||||||
await this.getViewData();
|
this.changeBreadcrumbs({ schema: this.schema, view: this.view });
|
||||||
this.$refs.queryEditor.editor.session.setValue(this.localView.sql);
|
|
||||||
this.lastView = this.view;
|
setTimeout(() => {
|
||||||
|
this.resizeQueryEditor();
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
if (this.lastView !== this.view)
|
||||||
|
this.getViewData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isChanged (val) {
|
isChanged (val) {
|
||||||
if (this.isSelected && this.lastView === this.view && this.view !== null)
|
this.setUnsavedChanges({ uid: this.connection.uid, tUid: this.tabUid, isChanged: val });
|
||||||
this.setUnsavedChanges(val);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async created () {
|
||||||
|
await this.getViewData();
|
||||||
|
this.$refs.queryEditor.editor.session.setValue(this.localView.sql);
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
window.addEventListener('resize', this.resizeQueryEditor);
|
window.addEventListener('resize', this.resizeQueryEditor);
|
||||||
},
|
},
|
||||||
destroyed () {
|
destroyed () {
|
||||||
window.removeEventListener('resize', this.resizeQueryEditor);
|
window.removeEventListener('resize', this.resizeQueryEditor);
|
||||||
},
|
},
|
||||||
created () {
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
|
||||||
},
|
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
window.removeEventListener('keydown', this.onKey);
|
window.removeEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
@@ -258,17 +274,19 @@ export default {
|
|||||||
addNotification: 'notifications/addNotification',
|
addNotification: 'notifications/addNotification',
|
||||||
refreshStructure: 'workspaces/refreshStructure',
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
setUnsavedChanges: 'workspaces/setUnsavedChanges',
|
||||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
|
renameTabs: 'workspaces/renameTabs'
|
||||||
}),
|
}),
|
||||||
async getViewData () {
|
async getViewData () {
|
||||||
if (!this.view) return;
|
if (!this.view) return;
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
this.localView = { sql: '' };
|
this.localView = { sql: '' };
|
||||||
|
this.lastView = this.view;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.schema,
|
schema: this.schema,
|
||||||
view: this.workspace.breadcrumbs.view
|
view: this.view
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -293,9 +311,9 @@ export default {
|
|||||||
this.isSaving = true;
|
this.isSaving = true;
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.schema,
|
|
||||||
view: {
|
view: {
|
||||||
...this.localView,
|
...this.localView,
|
||||||
|
schema: this.schema,
|
||||||
oldName: this.originalView.name
|
oldName: this.originalView.name
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -309,10 +327,17 @@ export default {
|
|||||||
await this.refreshStructure(this.connection.uid);
|
await this.refreshStructure(this.connection.uid);
|
||||||
|
|
||||||
if (oldName !== this.localView.name) {
|
if (oldName !== this.localView.name) {
|
||||||
this.setUnsavedChanges(false);
|
this.renameTabs({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
elementName: oldName,
|
||||||
|
elementNewName: this.localView.name,
|
||||||
|
elementType: 'view'
|
||||||
|
});
|
||||||
|
|
||||||
this.changeBreadcrumbs({ schema: this.schema, view: this.localView.name });
|
this.changeBreadcrumbs({ schema: this.schema, view: this.localView.name });
|
||||||
}
|
}
|
||||||
|
else
|
||||||
this.getViewData();
|
this.getViewData();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
:index-types="indexTypes"
|
:index-types="indexTypes"
|
||||||
:indexes="indexes"
|
:indexes="indexes"
|
||||||
@delete-selected="removeField"
|
@delete-selected="removeField"
|
||||||
|
@duplicate-selected="duplicateField"
|
||||||
@close-context="isContext = false"
|
@close-context="isContext = false"
|
||||||
@add-new-index="$emit('add-new-index', $event)"
|
@add-new-index="$emit('add-new-index', $event)"
|
||||||
@add-to-index="$emit('add-to-index', $event)"
|
@add-to-index="$emit('add-to-index', $event)"
|
||||||
@@ -100,7 +101,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<draggable
|
<Draggable
|
||||||
ref="resultTable"
|
ref="resultTable"
|
||||||
:list="fields"
|
:list="fields"
|
||||||
class="tbody"
|
class="tbody"
|
||||||
@@ -117,14 +118,14 @@
|
|||||||
@contextmenu="contextMenu"
|
@contextmenu="contextMenu"
|
||||||
@rename-field="$emit('rename-field', $event)"
|
@rename-field="$emit('rename-field', $event)"
|
||||||
/>
|
/>
|
||||||
</draggable>
|
</Draggable>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapActions, mapGetters } from 'vuex';
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
import draggable from 'vuedraggable';
|
import Draggable from 'vuedraggable';
|
||||||
import TableRow from '@/components/WorkspacePropsTableRow';
|
import TableRow from '@/components/WorkspacePropsTableRow';
|
||||||
import TableContext from '@/components/WorkspacePropsTableContext';
|
import TableContext from '@/components/WorkspacePropsTableContext';
|
||||||
|
|
||||||
@@ -133,7 +134,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
TableRow,
|
TableRow,
|
||||||
TableContext,
|
TableContext,
|
||||||
draggable
|
Draggable
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
fields: Array,
|
fields: Array,
|
||||||
@@ -220,6 +221,9 @@ export default {
|
|||||||
this.contextEvent = event;
|
this.contextEvent = event;
|
||||||
this.isContext = true;
|
this.isContext = true;
|
||||||
},
|
},
|
||||||
|
duplicateField () {
|
||||||
|
this.$emit('duplicate-field', this.selectedField._id);
|
||||||
|
},
|
||||||
removeField () {
|
removeField () {
|
||||||
this.$emit('remove-field', this.selectedField._id);
|
this.$emit('remove-field', this.selectedField._id);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -33,6 +33,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="context-element" @click="duplicateField">
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-content-duplicate text-light pr-1" /> {{ $t('word.duplicate') }}</span>
|
||||||
|
</div>
|
||||||
<div class="context-element" @click="deleteField">
|
<div class="context-element" @click="deleteField">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $t('message.deleteField') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $t('message.deleteField') }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -62,6 +65,10 @@ export default {
|
|||||||
closeContext () {
|
closeContext () {
|
||||||
this.$emit('close-context');
|
this.$emit('close-context');
|
||||||
},
|
},
|
||||||
|
duplicateField () {
|
||||||
|
this.$emit('duplicate-selected');
|
||||||
|
this.closeContext();
|
||||||
|
},
|
||||||
deleteField () {
|
deleteField () {
|
||||||
this.$emit('delete-selected');
|
this.$emit('delete-selected');
|
||||||
this.closeContext();
|
this.closeContext();
|
||||||
|
|||||||
@@ -96,8 +96,22 @@
|
|||||||
class="cell-content"
|
class="cell-content"
|
||||||
@dblclick="editON($event, localLength, 'length')"
|
@dblclick="editON($event, localLength, 'length')"
|
||||||
>
|
>
|
||||||
|
<span v-if="localRow.enumValues">
|
||||||
|
{{ localRow.enumValues }}
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
{{ localLength }}
|
{{ localLength }}
|
||||||
</span>
|
</span>
|
||||||
|
</span>
|
||||||
|
<input
|
||||||
|
v-else-if="localRow.enumValues"
|
||||||
|
ref="editField"
|
||||||
|
v-model="editingContent"
|
||||||
|
type="text"
|
||||||
|
autofocus
|
||||||
|
class="editable-field px-2"
|
||||||
|
@blur="editOFF"
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
v-else
|
v-else
|
||||||
ref="editField"
|
ref="editField"
|
||||||
@@ -218,7 +232,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" /> {{ $t('word.default') }} "{{ row.name }}"
|
<i class="mdi mdi-24px mdi-playlist-edit mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.default') }} "{{ row.name }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -381,10 +396,12 @@ export default {
|
|||||||
isInDataTypes () {
|
isInDataTypes () {
|
||||||
let typeNames = [];
|
let typeNames = [];
|
||||||
for (const group of this.dataTypes) {
|
for (const group of this.dataTypes) {
|
||||||
typeNames = group.types.reduce((acc, curr) => {
|
const groupTypeNames = group.types.reduce((acc, curr) => {
|
||||||
acc.push(curr.name);
|
acc.push(curr.name);
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
typeNames = [...groupTypeNames, ...typeNames];
|
||||||
}
|
}
|
||||||
return typeNames.includes(this.row.type);
|
return typeNames.includes(this.row.type);
|
||||||
}
|
}
|
||||||
@@ -433,37 +450,39 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.defaultValue.onUpdate = this.localRow.onUpdate;
|
this.defaultValue.onUpdate = this.localRow.onUpdate;
|
||||||
|
this.defaultValue.type = this.localRow.defaultType || 'noval';
|
||||||
if (this.localRow.autoIncrement)
|
if (this.defaultValue.type === 'custom') {
|
||||||
this.defaultValue.type = 'autoincrement';
|
this.defaultValue.custom = this.localRow.default
|
||||||
else if (this.localRow.default === null)
|
? this.localRow.default.includes('\'')
|
||||||
this.defaultValue.type = 'noval';
|
? this.localRow.default.split('\'')[1]
|
||||||
else if (this.localRow.default === 'NULL')
|
: this.localRow.default
|
||||||
this.defaultValue.type = 'null';
|
: '';
|
||||||
else if (this.localRow.default.match(/^'.*'$/g)) {
|
|
||||||
this.defaultValue.type = 'custom';
|
|
||||||
this.defaultValue.custom = this.localRow.default.replace(/(^')|('$)/g, '');
|
|
||||||
}
|
}
|
||||||
else if (!isNaN(this.localRow.default.replace(/[:.-\s]/g, ''))) {
|
else if (this.defaultValue.type === 'expression') {
|
||||||
this.defaultValue.type = 'custom';
|
if (this.localRow.default.toUpperCase().includes('ON UPDATE'))
|
||||||
this.defaultValue.custom = this.localRow.default;
|
this.defaultValue.expression = this.localRow.default.replace(/ on update.*$/i, '');
|
||||||
}
|
else
|
||||||
else {
|
|
||||||
this.defaultValue.type = 'expression';
|
|
||||||
this.defaultValue.expression = this.localRow.default;
|
this.defaultValue.expression = this.localRow.default;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
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';
|
||||||
if (['string', 'other'].includes(this.fieldType.group)) this.editingField = 'charLength';
|
else if (['string', 'unknown'].includes(this.fieldType.group)) this.editingField = 'charLength';
|
||||||
if (['time'].includes(this.fieldType.group)) this.editingField = 'datePrecision';
|
else if (['other'].includes(this.fieldType.group)) this.editingField = 'enumValues';
|
||||||
|
else if (['time'].includes(this.fieldType.group)) this.editingField = 'datePrecision';
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.editingField = field;
|
this.editingField = field;
|
||||||
|
|
||||||
|
if (this.localRow.enumValues && field === 'length') {
|
||||||
|
this.editingContent = this.localRow.enumValues;
|
||||||
|
this.originalContent = this.localRow.enumValues;
|
||||||
|
}
|
||||||
|
else {
|
||||||
this.editingContent = content;
|
this.editingContent = content;
|
||||||
this.originalContent = content;
|
this.originalContent = content;
|
||||||
|
}
|
||||||
|
|
||||||
const obj = { [field]: true };
|
const obj = { [field]: true };
|
||||||
this.isInlineEditor = { ...this.isInlineEditor, ...obj };
|
this.isInlineEditor = { ...this.isInlineEditor, ...obj };
|
||||||
@@ -488,11 +507,13 @@ export default {
|
|||||||
this.localRow.numLength = null;
|
this.localRow.numLength = null;
|
||||||
this.localRow.charLength = null;
|
this.localRow.charLength = null;
|
||||||
this.localRow.datePrecision = null;
|
this.localRow.datePrecision = null;
|
||||||
|
this.localRow.enumValues = '';
|
||||||
|
|
||||||
if (this.fieldType.length) {
|
if (this.fieldType.length) {
|
||||||
if (['integer', 'float', 'binary', 'spatial'].includes(this.fieldType.group)) this.localRow.numLength = 11;
|
if (['integer', 'float', 'binary', 'spatial'].includes(this.fieldType.group)) this.localRow.numLength = 11;
|
||||||
if (['string', 'other'].includes(this.fieldType.group)) this.localRow.charLength = 15;
|
if (['string'].includes(this.fieldType.group)) this.localRow.charLength = 15;
|
||||||
if (['time'].includes(this.fieldType.group)) this.localRow.datePrecision = 0;
|
if (['time'].includes(this.fieldType.group)) this.localRow.datePrecision = 0;
|
||||||
|
if (['other'].includes(this.fieldType.group)) this.localRow.enumValues = '\'valA\',\'valB\'';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.fieldType.collation)
|
if (!this.fieldType.collation)
|
||||||
@@ -519,7 +540,7 @@ export default {
|
|||||||
break;
|
break;
|
||||||
case 'custom':
|
case 'custom':
|
||||||
this.localRow.autoIncrement = false;
|
this.localRow.autoIncrement = false;
|
||||||
this.localRow.default = `'${this.defaultValue.custom}'`;
|
this.localRow.default = Number.isNaN(+this.defaultValue.custom) ? `'${this.defaultValue.custom}'` : this.defaultValue.custom;
|
||||||
break;
|
break;
|
||||||
case 'expression':
|
case 'expression':
|
||||||
this.localRow.autoIncrement = false;
|
this.localRow.autoIncrement = false;
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.confirm')"
|
||||||
|
size="400"
|
||||||
|
@confirm="confirmOptionsChange"
|
||||||
|
@hide="$emit('hide')"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-cogs mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.options') }} "{{ localOptions.name }}"</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div v-if="customizations.triggerFunctionlanguages" class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.language') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="optionsProxy.language" class="form-select">
|
||||||
|
<option v-for="language in customizations.triggerFunctionlanguages" :key="language">
|
||||||
|
{{ language }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.definer" class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.definer') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select
|
||||||
|
v-if="workspace.users.length"
|
||||||
|
v-model="optionsProxy.definer"
|
||||||
|
class="form-select"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="user in workspace.users"
|
||||||
|
:key="`${user.name}@${user.host}`"
|
||||||
|
:value="`\`${user.name}\`@\`${user.host}\``"
|
||||||
|
>
|
||||||
|
{{ user.name }}@{{ user.host }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<select v-if="!workspace.users.length" class="form-select">
|
||||||
|
<option value="">
|
||||||
|
{{ $t('message.currentUser') }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.comment" class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.comment') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<input
|
||||||
|
v-model="optionsProxy.comment"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspacePropsTriggerFunctionOptionsModal',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
localOptions: Object,
|
||||||
|
workspace: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
optionsProxy: {},
|
||||||
|
isOptionsChanging: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isTableNameValid () {
|
||||||
|
return this.optionsProxy.name !== '';
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace.customizations;
|
||||||
|
},
|
||||||
|
isInDataTypes () {
|
||||||
|
let typeNames = [];
|
||||||
|
for (const group of this.workspace.dataTypes) {
|
||||||
|
typeNames = group.types.reduce((acc, curr) => {
|
||||||
|
acc.push(curr.name);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
return typeNames.includes(this.localOptions.returns);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.optionsProxy = JSON.parse(JSON.stringify(this.localOptions));
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
confirmOptionsChange () {
|
||||||
|
if (!this.isTableNameValid)
|
||||||
|
this.optionsProxy.name = this.localOptions.name;
|
||||||
|
|
||||||
|
this.$emit('options-update', this.optionsProxy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
41
src/renderer/components/WorkspaceQueryEmptyState.vue
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column col-16 text-right">
|
||||||
|
<div class="mb-4">
|
||||||
|
{{ $t('message.runQuery') }}
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
{{ $t('word.format') }}
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
{{ $t('word.clear') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-16">
|
||||||
|
<div class="mb-4">
|
||||||
|
<code>F5</code>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<code>CTRL</code> + <code>F8</code>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<code>CTRL</code> + <code>W</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'WorkspaceQueryEmptyState'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
padding-top: 15vh;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-show="isSelected"
|
v-show="isSelected"
|
||||||
class="workspace-query-tab column col-12 columns col-gapless no-outline"
|
class="workspace-query-tab column col-12 columns col-gapless no-outline p-0"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@keydown.116="runQuery(query)"
|
@keydown.116="runQuery(query)"
|
||||||
@keydown.ctrl.87="clear"
|
@keydown.ctrl.87="clear"
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
:auto-focus="true"
|
:auto-focus="true"
|
||||||
:value.sync="query"
|
:value.sync="query"
|
||||||
:workspace="workspace"
|
:workspace="workspace"
|
||||||
:schema="schema"
|
:schema="breadcrumbsSchema"
|
||||||
:is-selected="isSelected"
|
:is-selected="isSelected"
|
||||||
:height="editorHeight"
|
:height="editorHeight"
|
||||||
/>
|
/>
|
||||||
@@ -28,17 +28,38 @@
|
|||||||
title="F5"
|
title="F5"
|
||||||
@click="runQuery(query)"
|
@click="runQuery(query)"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-play pr-1" />
|
||||||
<span>{{ $t('word.run') }}</span>
|
<span>{{ $t('word.run') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-play" />
|
|
||||||
</button>
|
</button>
|
||||||
<div class="dropdown export-dropdown pr-2">
|
<button
|
||||||
|
class="btn btn-dark btn-sm"
|
||||||
|
:disabled="!query || isQuering"
|
||||||
|
title="CTRL+F8"
|
||||||
|
@click="beautify()"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-brush pr-1" />
|
||||||
|
<span>{{ $t('word.format') }}</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-link btn-sm"
|
||||||
|
:disabled="!query || isQuering"
|
||||||
|
title="CTRL+W"
|
||||||
|
@click="clear()"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-delete-sweep pr-1" />
|
||||||
|
<span>{{ $t('word.clear') }}</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="divider-vert py-3" />
|
||||||
|
|
||||||
|
<div class="dropdown table-dropdown pr-2">
|
||||||
<button
|
<button
|
||||||
:disabled="!results.length || isQuering"
|
:disabled="!results.length || 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"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-file-export mr-1" />
|
||||||
<span>{{ $t('word.export') }}</span>
|
<span>{{ $t('word.export') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-file-export ml-1" />
|
|
||||||
<i class="mdi mdi-24px mdi-menu-down" />
|
<i class="mdi mdi-24px mdi-menu-down" />
|
||||||
</button>
|
</button>
|
||||||
<ul class="menu text-left">
|
<ul class="menu text-left">
|
||||||
@@ -50,24 +71,6 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</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
|
<div
|
||||||
@@ -83,16 +86,21 @@
|
|||||||
<div v-if="affectedCount">
|
<div v-if="affectedCount">
|
||||||
{{ $t('message.affectedRows') }}: <b>{{ affectedCount }}</b>
|
{{ $t('message.affectedRows') }}: <b>{{ affectedCount }}</b>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="input-group" :title="$t('word.schema')">
|
||||||
v-if="workspace.breadcrumbs.schema"
|
<i class="input-group-addon addon-sm mdi mdi-24px mdi-database" />
|
||||||
class="d-flex"
|
<select v-model="selectedSchema" class="form-select select-sm text-bold">
|
||||||
:title="$t('word.schema')"
|
<option :value="null">
|
||||||
>
|
{{ $t('message.noSchema') }}
|
||||||
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ workspace.breadcrumbs.schema }}</b>
|
</option>
|
||||||
|
<option v-for="schemaName in databaseSchemas" :key="schemaName">
|
||||||
|
{{ schemaName }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<WorkspaceQueryEmptyState v-if="!results.length && !isQuering" />
|
||||||
<div class="workspace-query-results p-relative column col-12">
|
<div class="workspace-query-results p-relative column col-12">
|
||||||
<BaseLoader v-if="isQuering" />
|
<BaseLoader v-if="isQuering" />
|
||||||
<WorkspaceQueryTable
|
<WorkspaceQueryTable
|
||||||
@@ -117,6 +125,7 @@ import Schema from '@/ipc-api/Schema';
|
|||||||
import QueryEditor from '@/components/QueryEditor';
|
import QueryEditor from '@/components/QueryEditor';
|
||||||
import BaseLoader from '@/components/BaseLoader';
|
import BaseLoader from '@/components/BaseLoader';
|
||||||
import WorkspaceQueryTable from '@/components/WorkspaceQueryTable';
|
import WorkspaceQueryTable from '@/components/WorkspaceQueryTable';
|
||||||
|
import WorkspaceQueryEmptyState from '@/components/WorkspaceQueryEmptyState';
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import tableTabs from '@/mixins/tableTabs';
|
import tableTabs from '@/mixins/tableTabs';
|
||||||
|
|
||||||
@@ -125,7 +134,8 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
BaseLoader,
|
BaseLoader,
|
||||||
QueryEditor,
|
QueryEditor,
|
||||||
WorkspaceQueryTable
|
WorkspaceQueryTable,
|
||||||
|
WorkspaceQueryEmptyState
|
||||||
},
|
},
|
||||||
mixins: [tableTabs],
|
mixins: [tableTabs],
|
||||||
props: {
|
props: {
|
||||||
@@ -139,6 +149,7 @@ export default {
|
|||||||
lastQuery: '',
|
lastQuery: '',
|
||||||
isQuering: false,
|
isQuering: false,
|
||||||
results: [],
|
results: [],
|
||||||
|
selectedSchema: null,
|
||||||
resultsCount: 0,
|
resultsCount: 0,
|
||||||
durationsCount: 0,
|
durationsCount: 0,
|
||||||
affectedCount: 0,
|
affectedCount: 0,
|
||||||
@@ -153,12 +164,32 @@ export default {
|
|||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
},
|
},
|
||||||
|
breadcrumbsSchema () {
|
||||||
|
return this.workspace.breadcrumbs.schema || null;
|
||||||
|
},
|
||||||
|
databaseSchemas () {
|
||||||
|
return this.workspace.structure.reduce((acc, curr) => {
|
||||||
|
acc.push(curr.name);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
},
|
||||||
isWorkspaceSelected () {
|
isWorkspaceSelected () {
|
||||||
return this.workspace.uid === this.selectedWorkspace;
|
return this.workspace.uid === this.selectedWorkspace;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
isSelected (val) {
|
||||||
|
if (val)
|
||||||
|
this.changeBreadcrumbs({ schema: this.selectedSchema, query: `Query #${this.tab.index}` });
|
||||||
|
},
|
||||||
|
selectedSchema () {
|
||||||
|
this.changeBreadcrumbs({ schema: this.selectedSchema, query: `Query #${this.tab.index}` });
|
||||||
|
}
|
||||||
|
},
|
||||||
created () {
|
created () {
|
||||||
this.query = this.tab.content;
|
this.query = this.tab.content;
|
||||||
|
this.selectedSchema = this.tab.schema || this.breadcrumbsSchema;
|
||||||
|
// this.changeBreadcrumbs({ schema: this.selectedSchema, query: `Query #${this.tab.index}` });
|
||||||
|
|
||||||
window.addEventListener('keydown', this.onKey);
|
window.addEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
@@ -180,7 +211,9 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification'
|
addNotification: 'notifications/addNotification',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
|
updateTabContent: 'workspaces/updateTabContent'
|
||||||
}),
|
}),
|
||||||
async runQuery (query) {
|
async runQuery (query) {
|
||||||
if (!query || this.isQuering) return;
|
if (!query || this.isQuering) return;
|
||||||
@@ -191,7 +224,7 @@ export default {
|
|||||||
try { // Query Data
|
try { // Query Data
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.schema,
|
schema: this.selectedSchema,
|
||||||
query
|
query
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -202,6 +235,8 @@ export default {
|
|||||||
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.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);
|
||||||
|
|
||||||
|
this.updateTabContent({ uid: this.connection.uid, tab: this.tab.uid, type: 'query', schema: this.selectedSchema, content: query });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
this.addNotification({ status: 'error', message: response });
|
||||||
@@ -281,11 +316,16 @@ export default {
|
|||||||
|
|
||||||
.query-area-resizer {
|
.query-area-resizer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 5px;
|
height: 4px;
|
||||||
bottom: 40px;
|
bottom: 40px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
cursor: ns-resize;
|
cursor: ns-resize;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
|
transition: background 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba($primary-color, 50%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspace-query-runner-footer {
|
.workspace-query-runner-footer {
|
||||||
@@ -295,8 +335,10 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
height: 42px;
|
height: 42px;
|
||||||
|
|
||||||
.workspace-query-buttons {
|
.workspace-query-buttons,
|
||||||
|
.workspace-query-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -306,7 +348,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.workspace-query-info {
|
.workspace-query-info {
|
||||||
display: flex;
|
overflow: visible;
|
||||||
|
|
||||||
> div + div {
|
> div + div {
|
||||||
padding-left: 0.6rem;
|
padding-left: 0.6rem;
|
||||||
@@ -319,10 +361,4 @@ export default {
|
|||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.export-dropdown {
|
|
||||||
.menu {
|
|
||||||
min-width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -10,8 +10,11 @@
|
|||||||
v-if="isContext"
|
v-if="isContext"
|
||||||
:context-event="contextEvent"
|
:context-event="contextEvent"
|
||||||
:selected-rows="selectedRows"
|
:selected-rows="selectedRows"
|
||||||
|
:selected-cell="selectedCell"
|
||||||
@show-delete-modal="showDeleteConfirmModal"
|
@show-delete-modal="showDeleteConfirmModal"
|
||||||
@set-null="setNull"
|
@set-null="setNull"
|
||||||
|
@copy-cell="copyCell"
|
||||||
|
@copy-row="copyRow"
|
||||||
@close-context="closeContext"
|
@close-context="closeContext"
|
||||||
/>
|
/>
|
||||||
<ul v-if="resultsWithRows.length > 1" class="tab tab-block result-tabs">
|
<ul v-if="resultsWithRows.length > 1" class="tab tab-block result-tabs">
|
||||||
@@ -69,6 +72,7 @@
|
|||||||
:row="row"
|
:row="row"
|
||||||
:fields="fieldsObj"
|
:fields="fieldsObj"
|
||||||
:key-usage="keyUsage"
|
:key-usage="keyUsage"
|
||||||
|
:element-type="elementType"
|
||||||
: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, row)"
|
@update-field="updateField($event, row)"
|
||||||
@@ -85,7 +89,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-delete mr-1" /> {{ $tc('message.deleteRows', selectedRows.length) }}
|
<i class="mdi mdi-24px mdi-delete mr-1" />
|
||||||
|
<span class="cut-text">{{ $tc('message.deleteRows', selectedRows.length) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -120,11 +125,12 @@ export default {
|
|||||||
results: Array,
|
results: Array,
|
||||||
connUid: String,
|
connUid: String,
|
||||||
mode: String,
|
mode: String,
|
||||||
isSelected: Boolean
|
isSelected: Boolean,
|
||||||
|
elementType: { type: String, default: 'table' }
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
resultsSize: 1000,
|
resultsSize: 0,
|
||||||
localResults: [],
|
localResults: [],
|
||||||
isContext: false,
|
isContext: false,
|
||||||
isDeleteConfirmModal: false,
|
isDeleteConfirmModal: false,
|
||||||
@@ -139,7 +145,8 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
getWorkspace: 'workspaces/getWorkspace'
|
getWorkspace: 'workspaces/getWorkspace',
|
||||||
|
pageSize: 'settings/getDataTabLimit'
|
||||||
}),
|
}),
|
||||||
workspaceSchema () {
|
workspaceSchema () {
|
||||||
return this.getWorkspace(this.connUid).breadcrumbs.schema;
|
return this.getWorkspace(this.connUid).breadcrumbs.schema;
|
||||||
@@ -157,7 +164,7 @@ export default {
|
|||||||
return this.fields.every(field => field.name);
|
return this.fields.every(field => field.name);
|
||||||
},
|
},
|
||||||
isHardSort () {
|
isHardSort () {
|
||||||
return this.mode === 'table' && this.localResults.length === 1000;
|
return this.mode === 'table' && this.localResults.length === this.pageSize;
|
||||||
},
|
},
|
||||||
sortedResults () {
|
sortedResults () {
|
||||||
if (this.currentSort && !this.isHardSort) {
|
if (this.currentSort && !this.isHardSort) {
|
||||||
@@ -222,6 +229,9 @@ export default {
|
|||||||
},
|
},
|
||||||
resultsetIndex () {
|
resultsetIndex () {
|
||||||
this.setLocalResults();
|
this.setLocalResults();
|
||||||
|
},
|
||||||
|
isSelected (val) {
|
||||||
|
if (val) this.refreshScroller();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updated () {
|
updated () {
|
||||||
@@ -319,8 +329,6 @@ export default {
|
|||||||
},
|
},
|
||||||
updateField (payload, row) {
|
updateField (payload, row) {
|
||||||
const orgRow = this.localResults.find(lr => lr._id === row._id);
|
const orgRow = this.localResults.find(lr => lr._id === row._id);
|
||||||
delete row._id;
|
|
||||||
delete orgRow._id;
|
|
||||||
|
|
||||||
Object.keys(orgRow).forEach(key => { // remap the row
|
Object.keys(orgRow).forEach(key => { // remap the row
|
||||||
if (orgRow[key] instanceof Date && moment(orgRow[key]).isValid()) { // if datetime
|
if (orgRow[key] instanceof Date && moment(orgRow[key]).isValid()) { // if datetime
|
||||||
@@ -347,7 +355,9 @@ export default {
|
|||||||
closeContext () {
|
closeContext () {
|
||||||
this.isContext = false;
|
this.isContext = false;
|
||||||
},
|
},
|
||||||
showDeleteConfirmModal () {
|
showDeleteConfirmModal (e) {
|
||||||
|
if (e && e.path && ['INPUT', 'TEXTAREA', 'SELECT'].includes(e.path[0].tagName))
|
||||||
|
return;
|
||||||
this.isDeleteConfirmModal = true;
|
this.isDeleteConfirmModal = true;
|
||||||
},
|
},
|
||||||
hideDeleteConfirmModal () {
|
hideDeleteConfirmModal () {
|
||||||
@@ -370,7 +380,6 @@ export default {
|
|||||||
},
|
},
|
||||||
setNull () {
|
setNull () {
|
||||||
const row = this.localResults.find(row => this.selectedRows.includes(row._id));
|
const row = this.localResults.find(row => this.selectedRows.includes(row._id));
|
||||||
delete row._id;
|
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
primary: this.primaryField.name,
|
primary: this.primaryField.name,
|
||||||
@@ -384,6 +393,22 @@ export default {
|
|||||||
};
|
};
|
||||||
this.$emit('update-field', params);
|
this.$emit('update-field', params);
|
||||||
},
|
},
|
||||||
|
copyCell () {
|
||||||
|
const row = this.localResults.find(row => this.selectedRows.includes(row._id));
|
||||||
|
const cellName = Object.keys(row).find(prop => [
|
||||||
|
this.selectedCell.field,
|
||||||
|
`${this.fields[0].table}.${this.selectedCell.field}`,
|
||||||
|
`${this.fields[0].tableAlias}.${this.selectedCell.field}`
|
||||||
|
].includes(prop));
|
||||||
|
const valueToCopy = row[cellName];
|
||||||
|
navigator.clipboard.writeText(valueToCopy);
|
||||||
|
},
|
||||||
|
copyRow () {
|
||||||
|
const row = this.localResults.find(row => this.selectedRows.includes(row._id));
|
||||||
|
const rowToCopy = JSON.parse(JSON.stringify(row));
|
||||||
|
delete rowToCopy._id;
|
||||||
|
navigator.clipboard.writeText(JSON.stringify(rowToCopy));
|
||||||
|
},
|
||||||
applyUpdate (params) {
|
applyUpdate (params) {
|
||||||
const { primary, id, field, table, content } = params;
|
const { primary, id, field, table, content } = params;
|
||||||
|
|
||||||
@@ -408,15 +433,15 @@ export default {
|
|||||||
this.selectedRows.push(row);
|
this.selectedRows.push(row);
|
||||||
else {
|
else {
|
||||||
const lastID = this.selectedRows.slice(-1)[0];
|
const lastID = this.selectedRows.slice(-1)[0];
|
||||||
const lastIndex = this.localResults.findIndex(el => el._id === lastID);
|
const lastIndex = this.sortedResults.findIndex(el => el._id === lastID);
|
||||||
const clickedIndex = this.localResults.findIndex(el => el._id === row);
|
const clickedIndex = this.sortedResults.findIndex(el => el._id === row);
|
||||||
if (lastIndex > clickedIndex) {
|
if (lastIndex > clickedIndex) {
|
||||||
for (let i = clickedIndex; i < lastIndex; i++)
|
for (let i = clickedIndex; i < lastIndex; i++)
|
||||||
this.selectedRows.push(this.localResults[i]._id);
|
this.selectedRows.push(this.sortedResults[i]._id);
|
||||||
}
|
}
|
||||||
else if (lastIndex < clickedIndex) {
|
else if (lastIndex < clickedIndex) {
|
||||||
for (let i = clickedIndex; i > lastIndex; i--)
|
for (let i = clickedIndex; i > lastIndex; i--)
|
||||||
this.selectedRows.push(this.localResults[i]._id);
|
this.selectedRows.push(this.sortedResults[i]._id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -435,6 +460,8 @@ export default {
|
|||||||
sort (field) {
|
sort (field) {
|
||||||
if (!this.isSortable) return;
|
if (!this.isSortable) return;
|
||||||
|
|
||||||
|
this.selectedRows = [];
|
||||||
|
|
||||||
if (this.mode === 'query')
|
if (this.mode === 'query')
|
||||||
field = `${this.getTable(this.resultsetIndex)}.${field}`;
|
field = `${this.getTable(this.resultsetIndex)}.${field}`;
|
||||||
|
|
||||||
@@ -462,7 +489,7 @@ export default {
|
|||||||
downloadTable (format, filename) {
|
downloadTable (format, filename) {
|
||||||
if (!this.sortedResults) return;
|
if (!this.sortedResults) return;
|
||||||
|
|
||||||
const rows = [...this.sortedResults].map(row => {
|
const rows = JSON.parse(JSON.stringify(this.sortedResults)).map(row => {
|
||||||
delete row._id;
|
delete row._id;
|
||||||
return row;
|
return row;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,16 +3,44 @@
|
|||||||
:context-event="contextEvent"
|
:context-event="contextEvent"
|
||||||
@close-context="closeContext"
|
@close-context="closeContext"
|
||||||
>
|
>
|
||||||
|
<div v-if="selectedRows.length === 1" class="context-element">
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-content-copy text-light pr-1" /> {{ $t('word.copy') }}</span>
|
||||||
|
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
|
||||||
|
<div class="context-submenu">
|
||||||
<div
|
<div
|
||||||
v-if="selectedRows.length === 1"
|
v-if="selectedRows.length === 1"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
|
@click="copyCell"
|
||||||
|
>
|
||||||
|
<span class="d-flex">
|
||||||
|
<i class="mdi mdi-18px mdi-numeric-0 mdi-rotate-90 text-light pr-1" /> {{ $tc('word.cell', 1) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="selectedRows.length === 1"
|
||||||
|
class="context-element"
|
||||||
|
@click="copyRow"
|
||||||
|
>
|
||||||
|
<span class="d-flex">
|
||||||
|
<i class="mdi mdi-18px mdi-table-row text-light pr-1" /> {{ $tc('word.row', 1) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="selectedRows.length === 1 && selectedCell.isEditable"
|
||||||
|
class="context-element"
|
||||||
@click="setNull"
|
@click="setNull"
|
||||||
>
|
>
|
||||||
<span class="d-flex">
|
<span class="d-flex">
|
||||||
<i class="mdi mdi-18px mdi-null text-light pr-1" /> {{ $t('message.setNull') }}
|
<i class="mdi mdi-18px mdi-null text-light pr-1" /> {{ $t('message.setNull') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element" @click="showConfirmModal">
|
<div
|
||||||
|
v-if="selectedCell.isEditable"
|
||||||
|
class="context-element"
|
||||||
|
@click="showConfirmModal"
|
||||||
|
>
|
||||||
<span class="d-flex">
|
<span class="d-flex">
|
||||||
<i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $tc('message.deleteRows', selectedRows.length) }}
|
<i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $tc('message.deleteRows', selectedRows.length) }}
|
||||||
</span>
|
</span>
|
||||||
@@ -30,7 +58,8 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
contextEvent: MouseEvent,
|
contextEvent: MouseEvent,
|
||||||
selectedRows: Array
|
selectedRows: Array,
|
||||||
|
selectedCell: Object
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
},
|
},
|
||||||
@@ -44,6 +73,14 @@ export default {
|
|||||||
setNull () {
|
setNull () {
|
||||||
this.$emit('set-null');
|
this.$emit('set-null');
|
||||||
this.closeContext();
|
this.closeContext();
|
||||||
|
},
|
||||||
|
copyCell () {
|
||||||
|
this.$emit('copy-cell');
|
||||||
|
this.closeContext();
|
||||||
|
},
|
||||||
|
copyRow () {
|
||||||
|
this.$emit('copy-row');
|
||||||
|
this.closeContext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
>
|
>
|
||||||
<template v-if="cKey !== '_id'">
|
<template v-if="cKey !== '_id'">
|
||||||
<span
|
<span
|
||||||
v-if="!isInlineEditor[cKey]"
|
v-if="!isInlineEditor[cKey] && fields[cKey]"
|
||||||
class="cell-content px-2"
|
class="cell-content px-2"
|
||||||
:class="`${isNull(col)} ${typeClass(fields[cKey].type)}`"
|
:class="`${isNull(col)} ${typeClass(fields[cKey].type)}`"
|
||||||
@dblclick="editON($event, col, cKey)"
|
@dblclick="editON($event, col, cKey)"
|
||||||
@@ -43,6 +43,16 @@
|
|||||||
<option>true</option>
|
<option>true</option>
|
||||||
<option>false</option>
|
<option>false</option>
|
||||||
</select>
|
</select>
|
||||||
|
<select
|
||||||
|
v-else-if="enumArray"
|
||||||
|
v-model="editingContent"
|
||||||
|
class="form-select small-select editable-field"
|
||||||
|
@blur="editOFF"
|
||||||
|
>
|
||||||
|
<option v-for="value in enumArray" :key="value">
|
||||||
|
{{ value }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
<input
|
<input
|
||||||
v-else
|
v-else
|
||||||
ref="editField"
|
ref="editField"
|
||||||
@@ -64,7 +74,7 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" /> {{ $t('word.edit') }} "{{ editingField }}"
|
<i class="mdi mdi-24px mdi-playlist-edit mr-1" /> <span class="cut-text">{{ $t('word.edit') }} "{{ editingField }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -128,7 +138,8 @@
|
|||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-playlist-edit mr-1" /> {{ $t('word.edit') }} "{{ editingField }}"
|
<i class="mdi mdi-24px mdi-playlist-edit mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.edit') }} "{{ editingField }}"</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div :slot="'body'">
|
<div :slot="'body'">
|
||||||
@@ -249,7 +260,8 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
row: Object,
|
row: Object,
|
||||||
fields: Object,
|
fields: Object,
|
||||||
keyUsage: Array
|
keyUsage: Array,
|
||||||
|
elementType: { type: String, default: 'table' }
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -323,6 +335,8 @@ export default {
|
|||||||
return this.keyUsage.map(key => key.field);
|
return this.keyUsage.map(key => key.field);
|
||||||
},
|
},
|
||||||
isEditable () {
|
isEditable () {
|
||||||
|
if (this.elementType === 'view') return false;
|
||||||
|
|
||||||
if (this.fields) {
|
if (this.fields) {
|
||||||
const nElements = Object.keys(this.fields).reduce((acc, curr) => {
|
const nElements = Object.keys(this.fields).reduce((acc, curr) => {
|
||||||
acc.add(this.fields[curr].table);
|
acc.add(this.fields[curr].table);
|
||||||
@@ -336,6 +350,11 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
},
|
||||||
|
enumArray () {
|
||||||
|
if (this.fields[this.editingField] && this.fields[this.editingField].enumValues)
|
||||||
|
return this.fields[this.editingField].enumValues.replaceAll('\'', '').split(',');
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -487,10 +506,9 @@ export default {
|
|||||||
return this.keyUsage.find(key => key.field === keyName);
|
return this.keyUsage.find(key => key.field === keyName);
|
||||||
},
|
},
|
||||||
openContext (event, payload) {
|
openContext (event, payload) {
|
||||||
if (this.isEditable) {
|
|
||||||
payload.field = this.fields[payload.field].name;// Ensures field name only
|
payload.field = this.fields[payload.field].name;// Ensures field name only
|
||||||
|
payload.isEditable = this.isEditable;
|
||||||
this.$emit('contextmenu', event, payload);
|
this.$emit('contextmenu', event, payload);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onKey (e) {
|
onKey (e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<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 p-0">
|
||||||
<div class="workspace-query-runner column col-12">
|
<div class="workspace-query-runner column col-12">
|
||||||
<div class="workspace-query-runner-footer">
|
<div class="workspace-query-runner-footer">
|
||||||
<div class="workspace-query-buttons">
|
<div class="workspace-query-buttons">
|
||||||
@@ -11,9 +11,9 @@
|
|||||||
title="F5"
|
title="F5"
|
||||||
@click="reloadTable"
|
@click="reloadTable"
|
||||||
>
|
>
|
||||||
|
<i v-if="!+autorefreshTimer" class="mdi mdi-24px mdi-refresh mr-1" />
|
||||||
|
<i v-else class="mdi mdi-24px mdi-history mdi-flip-h mr-1" />
|
||||||
<span>{{ $t('word.refresh') }}</span>
|
<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>
|
</button>
|
||||||
<div class="btn btn-dark btn-sm dropdown-toggle pl-0 pr-0" tabindex="0">
|
<div class="btn btn-dark btn-sm dropdown-toggle pl-0 pr-0" tabindex="0">
|
||||||
<i class="mdi mdi-24px mdi-menu-down" />
|
<i class="mdi mdi-24px mdi-menu-down" />
|
||||||
@@ -32,6 +32,44 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="btn-group">
|
||||||
|
<button
|
||||||
|
class="btn btn-dark btn-sm mr-0"
|
||||||
|
:disabled="isQuering || page === 1"
|
||||||
|
title="CTRL+ᐊ"
|
||||||
|
@click="pageChange('prev')"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-skip-previous" />
|
||||||
|
</button>
|
||||||
|
<div class="dropdown" :class="{'active': isPageMenu}">
|
||||||
|
<div @click="openPageMenu">
|
||||||
|
<div class="btn btn-dark btn-sm mr-0 no-radius dropdown-toggle text-bold px-3">
|
||||||
|
{{ page }}
|
||||||
|
</div>
|
||||||
|
<div class="menu px-3">
|
||||||
|
<span>{{ $t('message.pageNumber') }}</span>
|
||||||
|
<input
|
||||||
|
ref="pageSelect"
|
||||||
|
v-model="pageProxy"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
class="form-input"
|
||||||
|
@blur="setPageNumber"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="btn btn-dark btn-sm mr-0"
|
||||||
|
:disabled="isQuering || (results.length && results[0].rows.length < limit)"
|
||||||
|
title="CTRL+ᐅ"
|
||||||
|
@click="pageChange('next')"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-skip-next" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="divider-vert py-3" />
|
||||||
|
|
||||||
<button
|
<button
|
||||||
v-if="isTable"
|
v-if="isTable"
|
||||||
@@ -39,18 +77,18 @@
|
|||||||
:disabled="isQuering"
|
:disabled="isQuering"
|
||||||
@click="showFakerModal"
|
@click="showFakerModal"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-playlist-plus mr-1" />
|
||||||
<span>{{ $t('message.tableFiller') }}</span>
|
<span>{{ $t('message.tableFiller') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-playlist-plus ml-1" />
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="dropdown export-dropdown pr-2">
|
<div class="dropdown table-dropdown pr-2">
|
||||||
<button
|
<button
|
||||||
:disabled="isQuering"
|
:disabled="isQuering"
|
||||||
class="btn btn-dark btn-sm dropdown-toggle mr-0 pr-0"
|
class="btn btn-dark btn-sm dropdown-toggle mr-0 pr-0"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-file-export mr-1" />
|
||||||
<span>{{ $t('word.export') }}</span>
|
<span>{{ $t('word.export') }}</span>
|
||||||
<i class="mdi mdi-24px mdi-file-export ml-1" />
|
|
||||||
<i class="mdi mdi-24px mdi-menu-down" />
|
<i class="mdi mdi-24px mdi-menu-down" />
|
||||||
</button>
|
</button>
|
||||||
<ul class="menu text-left">
|
<ul class="menu text-left">
|
||||||
@@ -72,13 +110,13 @@
|
|||||||
<i class="mdi mdi-timer-sand mdi-rotate-180 pr-1" /> <b>{{ results[0].duration / 1000 }}s</b>
|
<i class="mdi mdi-timer-sand mdi-rotate-180 pr-1" /> <b>{{ results[0].duration / 1000 }}s</b>
|
||||||
</div>
|
</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 | localeString }}</b>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="hasApproximately">
|
<div v-if="hasApproximately || (page > 1 && approximateCount)">
|
||||||
{{ $t('word.total') }}: <b>{{ tableInfo.rows.toLocaleString() }}</b> <small>({{ $t('word.approximately') }})</small>
|
{{ $t('word.total') }}: <b :title="$t('word.approximately')">≈ {{ approximateCount | localeString }}</b>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="workspace.breadcrumbs.database">
|
<div class="d-flex" :title="$t('word.schema')">
|
||||||
{{ $t('word.schema') }}: <b>{{ workspace.breadcrumbs.database }}</b>
|
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -93,6 +131,7 @@
|
|||||||
:conn-uid="connection.uid"
|
:conn-uid="connection.uid"
|
||||||
:is-selected="isSelected"
|
:is-selected="isSelected"
|
||||||
mode="table"
|
mode="table"
|
||||||
|
:element-type="elementType"
|
||||||
@update-field="updateField"
|
@update-field="updateField"
|
||||||
@delete-selected="deleteSelected"
|
@delete-selected="deleteSelected"
|
||||||
@hard-sort="hardSort"
|
@hard-sort="hardSort"
|
||||||
@@ -134,35 +173,46 @@ export default {
|
|||||||
ModalNewTableRow,
|
ModalNewTableRow,
|
||||||
ModalFakerRows
|
ModalFakerRows
|
||||||
},
|
},
|
||||||
|
filters: {
|
||||||
|
localeString (val) {
|
||||||
|
if (val !== null)
|
||||||
|
return val.toLocaleString();
|
||||||
|
}
|
||||||
|
},
|
||||||
mixins: [tableTabs],
|
mixins: [tableTabs],
|
||||||
props: {
|
props: {
|
||||||
connection: Object,
|
connection: Object,
|
||||||
table: String
|
isSelected: Boolean,
|
||||||
|
table: String,
|
||||||
|
schema: String,
|
||||||
|
elementType: String
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
tabUid: 'data',
|
tabUid: 'data', // ???
|
||||||
isQuering: false,
|
isQuering: false,
|
||||||
|
isPageMenu: false,
|
||||||
results: [],
|
results: [],
|
||||||
lastTable: null,
|
lastTable: null,
|
||||||
isAddModal: false,
|
isAddModal: false,
|
||||||
isFakerModal: false,
|
isFakerModal: false,
|
||||||
autorefreshTimer: 0,
|
autorefreshTimer: 0,
|
||||||
refreshInterval: null,
|
refreshInterval: null,
|
||||||
sortParams: {}
|
sortParams: {},
|
||||||
|
page: 1,
|
||||||
|
pageProxy: 1,
|
||||||
|
approximateCount: 0
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
getWorkspace: 'workspaces/getWorkspace',
|
getWorkspace: 'workspaces/getWorkspace',
|
||||||
selectedWorkspace: 'workspaces/getSelected'
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
|
limit: 'settings/getDataTabLimit'
|
||||||
}),
|
}),
|
||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
},
|
},
|
||||||
isSelected () {
|
|
||||||
return this.workspace.selected_tab === 'data' && this.workspace.uid === this.selectedWorkspace;
|
|
||||||
},
|
|
||||||
isTable () {
|
isTable () {
|
||||||
return !!this.workspace.breadcrumbs.table;
|
return !!this.workspace.breadcrumbs.table;
|
||||||
},
|
},
|
||||||
@@ -183,24 +233,43 @@ export default {
|
|||||||
hasApproximately () {
|
hasApproximately () {
|
||||||
return this.results.length &&
|
return this.results.length &&
|
||||||
this.results[0].rows &&
|
this.results[0].rows &&
|
||||||
this.tableInfo &&
|
this.results[0].rows.length === this.limit &&
|
||||||
this.results[0].rows.length === 1000 &&
|
this.results[0].rows.length < this.approximateCount;
|
||||||
this.results[0].rows.length < this.tableInfo.rows;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
table () {
|
schema () {
|
||||||
if (this.isSelected) {
|
if (this.isSelected) {
|
||||||
|
this.page = 1;
|
||||||
|
this.approximateCount = 0;
|
||||||
this.sortParams = {};
|
this.sortParams = {};
|
||||||
this.getTableData();
|
this.getTableData();
|
||||||
this.lastTable = this.table;
|
this.lastTable = this.table;
|
||||||
this.$refs.queryTable.resetSort();
|
this.$refs.queryTable.resetSort();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isSelected (val) {
|
table () {
|
||||||
if (val && this.lastTable !== this.table) {
|
if (this.isSelected) {
|
||||||
|
this.page = 1;
|
||||||
|
this.approximateCount = 0;
|
||||||
|
this.sortParams = {};
|
||||||
this.getTableData();
|
this.getTableData();
|
||||||
this.lastTable = this.table;
|
this.lastTable = this.table;
|
||||||
|
this.$refs.queryTable.resetSort();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
page (val, oldVal) {
|
||||||
|
if (val && val > 0 && val !== oldVal) {
|
||||||
|
this.pageProxy = this.page;
|
||||||
|
this.getTableData();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isSelected (val) {
|
||||||
|
if (val) {
|
||||||
|
this.changeBreadcrumbs({ schema: this.schema, [this.elementType]: this.table });
|
||||||
|
|
||||||
|
if (this.lastTable !== this.table)
|
||||||
|
this.getTableData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -214,9 +283,10 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification'
|
addNotification: 'notifications/addNotification',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||||
}),
|
}),
|
||||||
async getTableData (sortParams) {
|
async getTableData () {
|
||||||
if (!this.table) return;
|
if (!this.table) return;
|
||||||
this.isQuering = true;
|
this.isQuering = true;
|
||||||
|
|
||||||
@@ -224,11 +294,15 @@ export default {
|
|||||||
if (this.lastTable !== this.table)
|
if (this.lastTable !== this.table)
|
||||||
this.results = [];
|
this.results = [];
|
||||||
|
|
||||||
|
this.lastTable = this.table;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.connection.uid,
|
uid: this.connection.uid,
|
||||||
schema: this.schema,
|
schema: this.schema,
|
||||||
table: this.workspace.breadcrumbs.table || this.workspace.breadcrumbs.view,
|
table: this.table,
|
||||||
sortParams
|
limit: this.limit,
|
||||||
|
page: this.page,
|
||||||
|
sortParams: this.sortParams
|
||||||
};
|
};
|
||||||
|
|
||||||
try { // Table data
|
try { // Table data
|
||||||
@@ -243,17 +317,58 @@ export default {
|
|||||||
this.addNotification({ status: 'error', message: err.stack });
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.results.length && this.results[0].rows.length === this.limit) {
|
||||||
|
try { // Table approximate count
|
||||||
|
const { status, response } = await Tables.getTableApproximateCount(params);
|
||||||
|
|
||||||
|
if (status === 'success')
|
||||||
|
this.approximateCount = response;
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.isQuering = false;
|
this.isQuering = false;
|
||||||
},
|
},
|
||||||
getTable () {
|
getTable () {
|
||||||
return this.table;
|
return this.table;
|
||||||
},
|
},
|
||||||
reloadTable () {
|
reloadTable () {
|
||||||
this.getTableData(this.sortParams);
|
this.getTableData();
|
||||||
},
|
},
|
||||||
hardSort (sortParams) {
|
hardSort (sortParams) {
|
||||||
this.sortParams = sortParams;
|
this.sortParams = sortParams;
|
||||||
this.getTableData(sortParams);
|
this.getTableData();
|
||||||
|
},
|
||||||
|
openPageMenu () {
|
||||||
|
if (this.isQuering || (this.results.length && this.results[0].rows.length < this.limit && this.page === 1)) return;
|
||||||
|
|
||||||
|
this.isPageMenu = true;
|
||||||
|
if (this.isPageMenu)
|
||||||
|
setTimeout(() => this.$refs.pageSelect.focus(), 20);
|
||||||
|
},
|
||||||
|
setPageNumber () {
|
||||||
|
this.isPageMenu = false;
|
||||||
|
|
||||||
|
if (this.pageProxy > 0)
|
||||||
|
this.page = this.pageProxy;
|
||||||
|
else
|
||||||
|
this.pageProxy = this.page;
|
||||||
|
},
|
||||||
|
pageChange (direction) {
|
||||||
|
if (this.isQuering) return;
|
||||||
|
|
||||||
|
if (direction === 'next' && (this.results.length && this.results[0].rows.length === this.limit)) {
|
||||||
|
if (!this.page)
|
||||||
|
this.page = 2;
|
||||||
|
else
|
||||||
|
this.page++;
|
||||||
|
}
|
||||||
|
else if (direction === 'prev' && this.page > 1)
|
||||||
|
this.page--;
|
||||||
},
|
},
|
||||||
showAddModal () {
|
showAddModal () {
|
||||||
this.isAddModal = true;
|
this.isAddModal = true;
|
||||||
@@ -273,6 +388,13 @@ export default {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.key === 'F5')
|
if (e.key === 'F5')
|
||||||
this.reloadTable();
|
this.reloadTable();
|
||||||
|
|
||||||
|
if (e.ctrlKey) {
|
||||||
|
if (e.key === 'ArrowRight')
|
||||||
|
this.pageChange('next');
|
||||||
|
if (e.key === 'ArrowLeft')
|
||||||
|
this.pageChange('prev');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setRefreshInterval () {
|
setRefreshInterval () {
|
||||||
@@ -292,10 +414,3 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
|
||||||
.export-dropdown {
|
|
||||||
.menu {
|
|
||||||
min-width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
381
src/renderer/i18n/de-DE.js
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
module.exports = {
|
||||||
|
word: {
|
||||||
|
edit: 'Bearbeiten',
|
||||||
|
save: 'Speichern',
|
||||||
|
close: 'Schließen',
|
||||||
|
delete: 'Löschen',
|
||||||
|
confirm: 'Bestätigen',
|
||||||
|
cancel: 'Abbrechen',
|
||||||
|
send: 'Senden',
|
||||||
|
connectionName: 'Verbindungsname',
|
||||||
|
client: 'Client',
|
||||||
|
hostName: 'Hostname',
|
||||||
|
port: 'Port',
|
||||||
|
user: 'Nutzer',
|
||||||
|
password: 'Kennwort',
|
||||||
|
credentials: 'Zugangsdaten',
|
||||||
|
connect: 'Verbinden',
|
||||||
|
connected: 'Verbunden',
|
||||||
|
disconnect: 'Trennen',
|
||||||
|
disconnected: 'Getrennt',
|
||||||
|
refresh: 'Aktualisieren',
|
||||||
|
settings: 'Einstellungen',
|
||||||
|
general: 'Allgemein',
|
||||||
|
themes: 'Designs',
|
||||||
|
update: 'Aktualisierung',
|
||||||
|
about: 'Über',
|
||||||
|
language: 'Sprache',
|
||||||
|
version: 'Version',
|
||||||
|
donate: 'Spenden',
|
||||||
|
run: 'Ausführen',
|
||||||
|
schema: 'Schema',
|
||||||
|
results: 'Ergebnisse',
|
||||||
|
size: 'Größe',
|
||||||
|
seconds: 'Sekunden',
|
||||||
|
type: 'Typ',
|
||||||
|
mimeType: 'Mime-Type',
|
||||||
|
download: 'Herunterladen',
|
||||||
|
add: 'Hinzufügen',
|
||||||
|
data: 'Daten',
|
||||||
|
properties: 'Eigenschaften',
|
||||||
|
insert: 'Einfügen',
|
||||||
|
connecting: 'Verbinden',
|
||||||
|
name: 'Name',
|
||||||
|
collation: 'Kollation',
|
||||||
|
clear: 'Leeren',
|
||||||
|
options: 'Optionen',
|
||||||
|
autoRefresh: 'Auto-Aktualisierung',
|
||||||
|
indexes: 'Indizes',
|
||||||
|
foreignKeys: 'Fremdschlüssel',
|
||||||
|
length: 'Länge',
|
||||||
|
unsigned: 'Unsigniert',
|
||||||
|
default: 'Standard',
|
||||||
|
comment: 'Kommentar',
|
||||||
|
key: 'Schlüssel',
|
||||||
|
order: 'Sortierung',
|
||||||
|
expression: 'Ausdruck',
|
||||||
|
autoIncrement: 'Auto Inkrement',
|
||||||
|
engine: 'Engine',
|
||||||
|
field: 'Feld | Felder',
|
||||||
|
approximately: 'Ungefähr',
|
||||||
|
total: 'Gesamt',
|
||||||
|
table: 'Tabelle',
|
||||||
|
discard: 'Verwerfen',
|
||||||
|
stay: 'Warten',
|
||||||
|
author: 'Autor',
|
||||||
|
light: 'Hell',
|
||||||
|
dark: 'Dunkel',
|
||||||
|
autoCompletion: 'Auto-Vervollständigung',
|
||||||
|
application: 'Anwendung',
|
||||||
|
editor: 'Editor',
|
||||||
|
view: 'Ansicht',
|
||||||
|
definer: 'Bestimmer',
|
||||||
|
algorithm: 'Algorithmus',
|
||||||
|
trigger: 'Auslöser',
|
||||||
|
storedRoutine: 'Gespeicherte Routine | Gespeicherte Routinen',
|
||||||
|
scheduler: 'Zeitplaner',
|
||||||
|
event: 'Ereignis',
|
||||||
|
parameters: 'Parameter',
|
||||||
|
function: 'Funktion | Funktionen',
|
||||||
|
deterministic: 'Deterministisch',
|
||||||
|
context: 'Kontext',
|
||||||
|
export: 'Export',
|
||||||
|
returns: 'Rückgabe',
|
||||||
|
timing: 'Dauer',
|
||||||
|
state: 'Stand',
|
||||||
|
execution: 'Ausführung',
|
||||||
|
starts: 'Startet',
|
||||||
|
ends: 'Endet',
|
||||||
|
ssl: 'SSL',
|
||||||
|
privateKey: 'Privater Schlüssel',
|
||||||
|
certificate: 'Zertifikat',
|
||||||
|
caCertificate: 'CA Zertifikat',
|
||||||
|
ciphers: 'Chiffren',
|
||||||
|
upload: 'Hochladen',
|
||||||
|
browse: 'Suchen',
|
||||||
|
faker: 'Faker',
|
||||||
|
content: 'Inhalt',
|
||||||
|
cut: 'Ausschneiden',
|
||||||
|
copy: 'Kopieren',
|
||||||
|
paste: 'Einfügen',
|
||||||
|
tools: 'Tools',
|
||||||
|
variables: 'Variablen',
|
||||||
|
processes: 'Prozesse',
|
||||||
|
database: 'Datenbank',
|
||||||
|
scratchpad: 'Scratchpad',
|
||||||
|
array: 'Array',
|
||||||
|
format: 'Formatierung',
|
||||||
|
sshTunnel: 'SSH Tunnel'
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
appWelcome: 'Willkommen im Antares SQL Client!',
|
||||||
|
appFirstStep: 'Dein erster Schritt: Erstelle eine neue Datenbankverbindung.',
|
||||||
|
addConnection: 'Verbindung hinzufügen',
|
||||||
|
createConnection: 'Verbindung erstellen',
|
||||||
|
createNewConnection: 'Neue Verbindung erstellen',
|
||||||
|
askCredentials: 'Frage nach Zugangsdaten',
|
||||||
|
testConnection: 'Verbindung testen',
|
||||||
|
editConnection: 'Verbindung bearbeiten',
|
||||||
|
deleteConnection: 'Verbindung löschen',
|
||||||
|
deleteCorfirm: 'Bestätige den Abbruch von',
|
||||||
|
connectionSuccessfullyMade: 'Verbindung erfolgreich erstellt!',
|
||||||
|
madeWithJS: 'Mit 💛 und JavaScript gemacht!',
|
||||||
|
checkForUpdates: 'Suche nach Aktualisierungen',
|
||||||
|
noUpdatesAvailable: 'Keine Aktualisierungen verfügbar',
|
||||||
|
checkingForUpdate: 'Suche nach Aktualisierungen',
|
||||||
|
checkFailure: 'Suche fehlgeschlagen, bitte versuche es später noch einmal',
|
||||||
|
updateAvailable: 'Aktualisierung verfügbar',
|
||||||
|
downloadingUpdate: 'Aktualisierung wird heruntergeladen',
|
||||||
|
updateDownloaded: 'Aktualisierung heruntergeladen',
|
||||||
|
restartToInstall: 'Starte Antares neu für die Installation',
|
||||||
|
unableEditFieldWithoutPrimary: 'Feld kann ohne Primärschlüssel in Ergebnisliste nicht bearbeitet werden',
|
||||||
|
editCell: 'Zelle bearbeiten',
|
||||||
|
deleteRows: 'Zeile löschen | Lösche {count} Zeilen',
|
||||||
|
confirmToDeleteRows: 'Eine Zeile wirklich löschen? | {count} Zeilen wirklich löschen?',
|
||||||
|
notificationsTimeout: 'Timeout für Benachrichtigungen',
|
||||||
|
uploadFile: 'Datei hochladen',
|
||||||
|
addNewRow: 'Neue Zeile hinzufügen',
|
||||||
|
numberOfInserts: 'Anzahl der eingefügten Zeilen',
|
||||||
|
openNewTab: 'Öffne einen neuen Tab',
|
||||||
|
affectedRows: 'Betroffene Zeilen',
|
||||||
|
createNewDatabase: 'Erstelle ein neue Datenbank',
|
||||||
|
databaseName: 'Datenbankname',
|
||||||
|
serverDefault: 'Server default',
|
||||||
|
deleteDatabase: 'Datenbank löschen',
|
||||||
|
editDatabase: 'Datenbank bearbeiten',
|
||||||
|
clearChanges: 'Änderungen leeren',
|
||||||
|
addNewField: 'Neues Feld hinzufügen',
|
||||||
|
manageIndexes: 'Indizes verwalten',
|
||||||
|
manageForeignKeys: 'Fremdschlüssel verwalten',
|
||||||
|
allowNull: 'Erlaube NULL',
|
||||||
|
zeroFill: 'Nullauffüllung',
|
||||||
|
customValue: 'Spezifischer Wert',
|
||||||
|
onUpdate: 'Bei Aktualisierung',
|
||||||
|
deleteField: 'Feld löschen',
|
||||||
|
createNewIndex: 'Neuen Index erstellen',
|
||||||
|
addToIndex: 'Zum Index hinzufügen',
|
||||||
|
createNewTable: 'Neue Tabelle erstellen',
|
||||||
|
emptyTable: 'Tabelle leeren',
|
||||||
|
deleteTable: 'Tabelle löschen',
|
||||||
|
emptyCorfirm: 'Wirklich leeren?',
|
||||||
|
unsavedChanges: 'Ungespeicherte Änderungen',
|
||||||
|
discardUnsavedChanges: 'Du hast ungespeicherte Änderungen. Wenn du den Tab verlässt, werden diese Änderungen verworfen.',
|
||||||
|
thereAreNoIndexes: 'Es gibt keine Indizes',
|
||||||
|
thereAreNoForeign: 'Es gibt keine Fremdschlüssel',
|
||||||
|
createNewForeign: 'Neuen Fremdschlüssel erstellen',
|
||||||
|
referenceTable: 'Referenztabelle',
|
||||||
|
referenceField: 'Referenzfeld',
|
||||||
|
foreignFields: 'Fremdfelder',
|
||||||
|
invalidDefault: 'Ungültiger Standard',
|
||||||
|
onDelete: 'Bei Löschung',
|
||||||
|
applicationTheme: 'Anwendungsdesign',
|
||||||
|
editorTheme: 'Editordesign',
|
||||||
|
wrapLongLines: 'Lange Zeilen umbrechen',
|
||||||
|
selectStatement: 'Select-Anweisung',
|
||||||
|
triggerStatement: 'Trigger-Anweisung',
|
||||||
|
sqlSecurity: 'SQL-Sicherheit',
|
||||||
|
updateOption: 'Aktualisierungsoption',
|
||||||
|
deleteView: 'View löschen',
|
||||||
|
createNewView: 'Neue View erstellen',
|
||||||
|
deleteTrigger: 'Trigger löschen',
|
||||||
|
createNewTrigger: 'Neuen Trigger erstellen',
|
||||||
|
currentUser: 'Aktueller Nutzer',
|
||||||
|
routineBody: 'Routineninhalt',
|
||||||
|
dataAccess: 'Datenzugriff',
|
||||||
|
thereAreNoParameters: 'Es gibt keine Parameter',
|
||||||
|
createNewParameter: 'Neue Parameter erstellen',
|
||||||
|
createNewRoutine: 'Neue Routine erstellen',
|
||||||
|
deleteRoutine: 'Routine löschen',
|
||||||
|
functionBody: 'Funktionsinhalt',
|
||||||
|
createNewFunction: 'Neue Funktion erstellen',
|
||||||
|
deleteFunction: 'Funktion löschen',
|
||||||
|
schedulerBody: 'Zeitplaner-Inhalt',
|
||||||
|
createNewScheduler: 'Neuen Zeitplaner erstellen',
|
||||||
|
deleteScheduler: 'Zeitplaner löschen',
|
||||||
|
preserveOnCompletion: 'Bei Vervollständigung erhalten',
|
||||||
|
enableSsl: 'Aktiviere SSL',
|
||||||
|
manualValue: 'Manueller Wert',
|
||||||
|
tableFiller: 'Tabellenfüller',
|
||||||
|
fakeDataLanguage: 'Fingierte Datensprache',
|
||||||
|
searchForElements: 'Suche nach Elemente',
|
||||||
|
selectAll: 'Alle auswählen',
|
||||||
|
queryDuration: 'Dauer der Abfrage',
|
||||||
|
includeBetaUpdates: 'Beta-Aktualisierungen berücksichtigen',
|
||||||
|
setNull: 'Setze NULL',
|
||||||
|
processesList: 'Prozessliste',
|
||||||
|
processInfo: 'Prozessinformationen',
|
||||||
|
manageUsers: 'Benutzer verwalten',
|
||||||
|
createNewSchema: 'Neues Schema erstellen',
|
||||||
|
schemaName: 'Schemaname',
|
||||||
|
editSchema: 'Schema bearbeiten',
|
||||||
|
deleteSchema: 'Schema löschen',
|
||||||
|
markdownSupported: 'Unterstützt Markdown',
|
||||||
|
plantATree: 'Pflanze einen Baum',
|
||||||
|
dataTabPageSize: 'Einträge pro Tab / Seite',
|
||||||
|
enableSsh: 'Aktiviere SSH'
|
||||||
|
},
|
||||||
|
faker: {
|
||||||
|
address: 'Adresse',
|
||||||
|
commerce: 'Handel',
|
||||||
|
company: 'Firma',
|
||||||
|
database: 'Datenbank',
|
||||||
|
date: 'Datum',
|
||||||
|
finance: 'Finanzen',
|
||||||
|
git: 'Git',
|
||||||
|
hacker: 'Hacker',
|
||||||
|
internet: 'Internet',
|
||||||
|
lorem: 'Lorem',
|
||||||
|
name: 'Name',
|
||||||
|
music: 'Musik',
|
||||||
|
phone: 'Telefon',
|
||||||
|
random: 'Zufällig',
|
||||||
|
system: 'System',
|
||||||
|
time: 'Zeit',
|
||||||
|
vehicle: 'Fahrzeug',
|
||||||
|
zipCode: 'Postleitzahl',
|
||||||
|
zipCodeByState: 'Postleitzahl nach Stadt',
|
||||||
|
city: 'Stadt',
|
||||||
|
cityPrefix: 'Stadtpräfix',
|
||||||
|
citySuffix: 'Stadtzusatz',
|
||||||
|
streetName: 'Straßenname',
|
||||||
|
streetAddress: 'Anschrift',
|
||||||
|
streetSuffix: 'Straßenzusatz',
|
||||||
|
streetPrefix: 'Straßenpräfix',
|
||||||
|
secondaryAddress: 'Zweite Anschrift',
|
||||||
|
county: 'Landkreis',
|
||||||
|
country: 'Land',
|
||||||
|
countryCode: 'Ländercode',
|
||||||
|
state: 'Bundesland',
|
||||||
|
stateAbbr: 'Bundeslandkürzel',
|
||||||
|
latitude: 'Breitengrad',
|
||||||
|
longitude: 'Längengrad',
|
||||||
|
direction: 'Richtung',
|
||||||
|
cardinalDirection: 'Himmelsrichtung',
|
||||||
|
ordinalDirection: 'Nebenhimmelsrichtung',
|
||||||
|
nearbyGPSCoordinate: 'Nächste GPS-Koordinate',
|
||||||
|
timeZone: 'Zeitzone',
|
||||||
|
color: 'Farbe',
|
||||||
|
department: 'Abteilung',
|
||||||
|
productName: 'Produktname',
|
||||||
|
price: 'Preis',
|
||||||
|
productAdjective: 'Produkteigenschaft',
|
||||||
|
productMaterial: 'Produktmaterial',
|
||||||
|
product: 'Produkt',
|
||||||
|
productDescription: 'Produktbeschreibung',
|
||||||
|
suffixes: 'Zusätze',
|
||||||
|
companyName: 'Firmenname',
|
||||||
|
companySuffix: 'Firmenzusatz',
|
||||||
|
catchPhrase: 'Slogan',
|
||||||
|
bs: 'BS',
|
||||||
|
catchPhraseAdjective: 'Sloganeigenschaft',
|
||||||
|
catchPhraseDescriptor: 'Sloganbeschreibung',
|
||||||
|
catchPhraseNoun: 'Slogannomen',
|
||||||
|
bsAdjective: 'BS-Eigenschaft',
|
||||||
|
bsBuzz: 'BS-Klatsch',
|
||||||
|
bsNoun: 'BS-Nomen',
|
||||||
|
column: 'Spalte',
|
||||||
|
type: 'Typ',
|
||||||
|
collation: 'Kollation',
|
||||||
|
engine: 'Engine',
|
||||||
|
past: 'Vergangenheit',
|
||||||
|
future: 'Zukunft',
|
||||||
|
between: 'Zwischen',
|
||||||
|
recent: 'Kürzlich',
|
||||||
|
soon: 'Bald',
|
||||||
|
month: 'Monat',
|
||||||
|
weekday: 'Wochentag',
|
||||||
|
account: 'Konto',
|
||||||
|
accountName: 'Kontoname',
|
||||||
|
routingNumber: 'Bankleitzahl',
|
||||||
|
mask: 'Maske',
|
||||||
|
amount: 'Wert',
|
||||||
|
transactionType: 'Vorgangstyp',
|
||||||
|
currencyCode: 'Währungscode',
|
||||||
|
currencyName: 'Währungsname',
|
||||||
|
currencySymbol: 'Währungssymbol',
|
||||||
|
bitcoinAddress: 'Bitcoin-Adresse',
|
||||||
|
litecoinAddress: 'Litecoin-Adresse',
|
||||||
|
creditCardNumber: 'Kreditkartennummer',
|
||||||
|
creditCardCVV: 'Kartenprüfnummer',
|
||||||
|
ethereumAddress: 'Ethereum-Adresse',
|
||||||
|
iban: 'IBAN',
|
||||||
|
bic: 'BIC',
|
||||||
|
transactionDescription: 'Vorgangsbeschreibung',
|
||||||
|
branch: 'Zweig',
|
||||||
|
commitEntry: 'Commit-Eintrag',
|
||||||
|
commitMessage: 'Commit-Nachricht',
|
||||||
|
commitSha: 'Commit-SHA',
|
||||||
|
shortSha: 'Kurzer SHA',
|
||||||
|
abbreviation: 'Kürzel',
|
||||||
|
adjective: 'Adjektiv',
|
||||||
|
noun: 'Nomen',
|
||||||
|
verb: 'Verb',
|
||||||
|
ingverb: 'Ingverb',
|
||||||
|
phrase: 'Phrase',
|
||||||
|
avatar: 'Avatar',
|
||||||
|
email: 'E-Mail',
|
||||||
|
exampleEmail: 'Beispiel-E-Mail',
|
||||||
|
userName: 'Benutzername',
|
||||||
|
protocol: 'Protokoll',
|
||||||
|
url: 'Url',
|
||||||
|
domainName: 'Domainname',
|
||||||
|
domainSuffix: 'Domainzusatz',
|
||||||
|
domainWord: 'Domainwort',
|
||||||
|
ip: 'IP',
|
||||||
|
ipv6: 'IPv6',
|
||||||
|
userAgent: 'Browserkennung',
|
||||||
|
mac: 'Mac',
|
||||||
|
password: 'Kennwort',
|
||||||
|
word: 'Wort',
|
||||||
|
words: 'Wörter',
|
||||||
|
sentence: 'Satz',
|
||||||
|
slug: 'Slug',
|
||||||
|
sentences: 'Sätze',
|
||||||
|
paragraph: 'Paragraph',
|
||||||
|
paragraphs: 'Paragraphen',
|
||||||
|
text: 'Text',
|
||||||
|
lines: 'Zeilen',
|
||||||
|
genre: 'Genre',
|
||||||
|
firstName: 'Vorname',
|
||||||
|
lastName: 'Nachname',
|
||||||
|
middleName: 'Zweitname',
|
||||||
|
findName: 'Vollständiger Name',
|
||||||
|
jobTitle: 'Berufsbezeichnung',
|
||||||
|
gender: 'Geschlecht',
|
||||||
|
prefix: 'Präfix',
|
||||||
|
suffix: 'Zusatz',
|
||||||
|
title: 'Titel',
|
||||||
|
jobDescriptor: 'Berufsbeschreibung',
|
||||||
|
jobArea: 'Berufsfeld',
|
||||||
|
jobType: 'Anstellungsart',
|
||||||
|
phoneNumber: 'Telefonnummer',
|
||||||
|
phoneNumberFormat: 'Telefonnummerformat',
|
||||||
|
phoneFormats: 'Telefonnummerformate',
|
||||||
|
number: 'Nummer',
|
||||||
|
float: 'Gleitkommazahl',
|
||||||
|
arrayElement: 'Array-Element',
|
||||||
|
arrayElements: 'Array-Elemente',
|
||||||
|
objectElement: 'Object-Element',
|
||||||
|
uuid: 'Uuid',
|
||||||
|
boolean: 'Boolean',
|
||||||
|
image: 'Grafik',
|
||||||
|
locale: 'Sprachumgebung',
|
||||||
|
alpha: 'Alpha',
|
||||||
|
alphaNumeric: 'Alphanumerisch',
|
||||||
|
hexaDecimal: 'Hexadezimal',
|
||||||
|
fileName: 'Dateiname',
|
||||||
|
commonFileName: 'Allgemeiner Dateiname',
|
||||||
|
mimeType: 'Mimetype',
|
||||||
|
commonFileType: 'Allgemeiner Dateityp',
|
||||||
|
commonFileExt: 'Allgemeine Dateiendung',
|
||||||
|
fileType: 'Dateityp',
|
||||||
|
fileExt: 'Dateiendung',
|
||||||
|
directoryPath: 'Verzeichnispfad',
|
||||||
|
filePath: 'Dateipfad',
|
||||||
|
semver: 'Semver',
|
||||||
|
manufacturer: 'Hersteller',
|
||||||
|
model: 'Modell',
|
||||||
|
fuel: 'Treibstoff',
|
||||||
|
vin: 'Wein'
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -105,7 +105,18 @@ module.exports = {
|
|||||||
scratchpad: 'Scratchpad',
|
scratchpad: 'Scratchpad',
|
||||||
array: 'Array',
|
array: 'Array',
|
||||||
changelog: 'Changelog',
|
changelog: 'Changelog',
|
||||||
format: 'Format'
|
format: 'Format',
|
||||||
|
sshTunnel: 'SSH tunnel',
|
||||||
|
structure: 'Structure',
|
||||||
|
small: 'Small',
|
||||||
|
medium: 'Medium',
|
||||||
|
large: 'Large',
|
||||||
|
row: 'Row | Rows',
|
||||||
|
cell: 'Cell | Cells',
|
||||||
|
triggerFunction: 'Trigger function | Trigger functions',
|
||||||
|
all: 'All',
|
||||||
|
duplicate: 'Duplicate',
|
||||||
|
routine: 'Routine'
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
appWelcome: 'Welcome to Antares SQL Client!',
|
appWelcome: 'Welcome to Antares SQL Client!',
|
||||||
@@ -159,7 +170,7 @@ module.exports = {
|
|||||||
deleteTable: 'Delete table',
|
deleteTable: 'Delete table',
|
||||||
emptyCorfirm: 'Do you confirm to empty',
|
emptyCorfirm: 'Do you confirm to empty',
|
||||||
unsavedChanges: 'Unsaved changes',
|
unsavedChanges: 'Unsaved changes',
|
||||||
discardUnsavedChanges: 'You have some unsaved changes. By leaving this tab these changes will be discarded.',
|
discardUnsavedChanges: 'You have some unsaved changes. Closing this tab these changes will be discarded.',
|
||||||
thereAreNoIndexes: 'There are no indexes',
|
thereAreNoIndexes: 'There are no indexes',
|
||||||
thereAreNoForeign: 'There are no foreign keys',
|
thereAreNoForeign: 'There are no foreign keys',
|
||||||
createNewForeign: 'Create new foreign key',
|
createNewForeign: 'Create new foreign key',
|
||||||
@@ -210,7 +221,15 @@ module.exports = {
|
|||||||
editSchema: 'Edit schema',
|
editSchema: 'Edit schema',
|
||||||
deleteSchema: 'Delete schema',
|
deleteSchema: 'Delete schema',
|
||||||
markdownSupported: 'Markdown supported',
|
markdownSupported: 'Markdown supported',
|
||||||
plantATree: 'Plant a Tree'
|
plantATree: 'Plant a Tree',
|
||||||
|
dataTabPageSize: 'DATA tab page size',
|
||||||
|
enableSsh: 'Enable SSH',
|
||||||
|
pageNumber: 'Page number',
|
||||||
|
duplicateTable: 'Duplicate table',
|
||||||
|
noOpenTabs: 'There are no open tabs, navigate on the left bar or:',
|
||||||
|
noSchema: 'No schema',
|
||||||
|
restorePreviourSession: 'Restore previous session',
|
||||||
|
runQuery: 'Run query'
|
||||||
},
|
},
|
||||||
faker: {
|
faker: {
|
||||||
address: 'Address',
|
address: 'Address',
|
||||||
|
|||||||