mirror of
https://github.com/Fabio286/antares.git
synced 2025-06-05 21:59:22 +02:00
Compare commits
113 Commits
Author | SHA1 | Date | |
---|---|---|---|
fe39c1d388 | |||
d114f8a651 | |||
84168d1d75 | |||
498a9b48e2 | |||
01f607cd40 | |||
efe134a059 | |||
40cb4dd98d | |||
a142d3c4d7 | |||
89da957a49 | |||
05c2f9836c | |||
ebc325ae0c | |||
39326eb52e | |||
df681147aa | |||
ffc645ba5e | |||
1fb1205319 | |||
c90ab0e880 | |||
9dc700e13e | |||
6950d0ce5a | |||
4df14c3693 | |||
c05be8304f | |||
31c575dad9 | |||
040657d5ca | |||
5043fafa93 | |||
8eb127e458 | |||
d044a02cb7 | |||
8cb2c197c8 | |||
c50d17e82b | |||
|
7c186d2dee | ||
0f219cf9b7 | |||
75c5a34095 | |||
48877534d1 | |||
c22413fde9 | |||
77ab8d8a03 | |||
4386c6ab95 | |||
19205e0736 | |||
4fc4ddd1d6 | |||
49b63bc6f2 | |||
44eb507a12 | |||
1590ffaff0 | |||
3c1bae540f | |||
44bb75bc60 | |||
8bb5bb93cf | |||
f64a12a8e9 | |||
da25823868 | |||
a9fcfd57ec | |||
e2307341f3 | |||
09a372e96d | |||
f4da28cca0 | |||
89745b7391 | |||
104b7c928b | |||
|
427360d826 | ||
e29d86b409 | |||
|
0bfa14e1c9 | ||
88ba55ec02 | |||
aaff4cf4fe | |||
35c54aee84 | |||
be2e9b21f5 | |||
2262278393 | |||
531e17889a | |||
a07ed58004 | |||
00dc59a76d | |||
2f883bfeb2 | |||
7ff16fccce | |||
|
3625fbc1b0 | ||
|
deee0d637b | ||
8c6950cebd | |||
46167e4473 | |||
c32a4415d1 | |||
1c3d7aa30b | |||
664d18efc1 | |||
cc941dfc04 | |||
1d151e9349 | |||
addd9fba28 | |||
a00c19d300 | |||
9551afbd2d | |||
1ead76c028 | |||
d3da15aa13 | |||
f3b5de38c4 | |||
|
d4b6d2e9d1 | ||
|
e2c106e4e0 | ||
eb60899e6e | |||
1d367d468d | |||
8ecaedbf6c | |||
|
dd1eebd4ec | ||
8c83b3f144 | |||
985e5d3527 | |||
78902639eb | |||
cb038b374a | |||
eafdb1cc3d | |||
91e0630513 | |||
bf768c3800 | |||
0b1aa3dd29 | |||
ec75f9546a | |||
9bc9adb7cf | |||
b4d14d98db | |||
c21bd6075c | |||
44647f5b55 | |||
3f9e6d85ca | |||
6a6f43a718 | |||
f12a04b052 | |||
abf829867e | |||
b71f04e5aa | |||
7725fafe85 | |||
ed3d35f131 | |||
e0946f04f7 | |||
0891e7be8c | |||
f312cf5f85 | |||
05e0d310ec | |||
|
1e0b2b4cae | ||
5ee728cfe4 | |||
a91fa8ff54 | |||
|
2c13433900 | ||
dcc2a4c51c |
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -6,6 +6,8 @@
|
|||||||
version: 2
|
version: 2
|
||||||
updates:
|
updates:
|
||||||
- package-ecosystem: "npm"
|
- package-ecosystem: "npm"
|
||||||
|
allow:
|
||||||
|
- dependency-type: "production"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "monthly"
|
interval: "monthly"
|
||||||
|
29
.github/workflows/build-linux.yml
vendored
29
.github/workflows/build-linux.yml
vendored
@@ -1,29 +0,0 @@
|
|||||||
name: Build/release [LINUX]
|
|
||||||
|
|
||||||
on: push
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out Git repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Install Node.js, NPM and Yarn
|
|
||||||
uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: 14
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm i
|
|
||||||
|
|
||||||
- name: Build/release Electron app
|
|
||||||
uses: samuelmeuli/action-electron-builder@v1
|
|
||||||
with:
|
|
||||||
github_token: ${{ secrets.github_token }}
|
|
||||||
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
|
29
.github/workflows/build-mac.yml
vendored
29
.github/workflows/build-mac.yml
vendored
@@ -1,29 +0,0 @@
|
|||||||
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@v2
|
|
||||||
|
|
||||||
- name: Install Node.js, NPM and Yarn
|
|
||||||
uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: 14
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm i
|
|
||||||
|
|
||||||
- name: Build/release Electron app
|
|
||||||
uses: samuelmeuli/action-electron-builder@v1
|
|
||||||
with:
|
|
||||||
github_token: ${{ secrets.github_token }}
|
|
||||||
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
|
29
.github/workflows/build-win.yml
vendored
29
.github/workflows/build-win.yml
vendored
@@ -1,29 +0,0 @@
|
|||||||
name: Build/release [WINDOWS]
|
|
||||||
|
|
||||||
on: push
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [windows-2019]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out Git repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Install Node.js, NPM and Yarn
|
|
||||||
uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: 14
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: npm i
|
|
||||||
|
|
||||||
- name: Build/release Electron app
|
|
||||||
uses: samuelmeuli/action-electron-builder@v1
|
|
||||||
with:
|
|
||||||
github_token: ${{ secrets.github_token }}
|
|
||||||
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
|
37
.github/workflows/build.yml
vendored
Normal file
37
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
name: Build & release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out Git repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm i
|
||||||
|
|
||||||
|
- name: "Build"
|
||||||
|
run: npm run build
|
||||||
|
|
||||||
|
- name: Release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
artifacts: "build/*.AppImage,build/*.yml,build/*.deb,build/*.dmg,build/*.blockmap,build/*.zip,build/*.exe"
|
||||||
|
allowUpdates: true
|
||||||
|
draft: true
|
||||||
|
generateReleaseNotes: true
|
7
.github/workflows/create-artifact-linux.yml
vendored
7
.github/workflows/create-artifact-linux.yml
vendored
@@ -10,10 +10,15 @@ jobs:
|
|||||||
- name: Check out Git repository
|
- name: Check out Git repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
|
||||||
- name: npm install & build
|
- name: npm install & build
|
||||||
run: |
|
run: |
|
||||||
npm install
|
npm install
|
||||||
npm run build:local
|
npm run build
|
||||||
|
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
31
.github/workflows/create-artifact-macos.yml
vendored
Normal file
31
.github/workflows/create-artifact-macos.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
name: Create artifact [MAC]
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out Git repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 16
|
||||||
|
|
||||||
|
- name: npm install & build
|
||||||
|
run: |
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
- name: Upload Artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: macos-build
|
||||||
|
retention-days: 3
|
||||||
|
path: |
|
||||||
|
build
|
||||||
|
!build/*-unpacked
|
||||||
|
!build/.icon-ico
|
8
.github/workflows/test-e2e-linux.yml
vendored
8
.github/workflows/test-e2e-linux.yml
vendored
@@ -12,12 +12,12 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out Git repository
|
- name: Check out Git repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install Node.js, NPM and Yarn
|
- name: Install Node.js
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 14
|
node-version: 16
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm i
|
run: npm i
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": [
|
"extends": [
|
||||||
"stylelint-config-standard"
|
"stylelint-config-standard",
|
||||||
|
"stylelint-config-recommended-vue"
|
||||||
],
|
],
|
||||||
"fix": true,
|
"fix": true,
|
||||||
"formatter": "verbose",
|
"formatter": "verbose",
|
||||||
@@ -10,6 +11,7 @@
|
|||||||
"rules": {
|
"rules": {
|
||||||
"at-rule-no-unknown": null,
|
"at-rule-no-unknown": null,
|
||||||
"no-descending-specificity": null,
|
"no-descending-specificity": null,
|
||||||
|
"font-family-no-missing-generic-family-keyword": null,
|
||||||
"declaration-colon-newline-after": "always-multi-line"
|
"declaration-colon-newline-after": "always-multi-line"
|
||||||
},
|
},
|
||||||
"syntax": "scss"
|
"syntax": "scss"
|
||||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -7,7 +7,8 @@
|
|||||||
"SQLite",
|
"SQLite",
|
||||||
"Windows",
|
"Windows",
|
||||||
"translation",
|
"translation",
|
||||||
"Linux"
|
"Linux",
|
||||||
|
"MacOS"
|
||||||
],
|
],
|
||||||
"svg.preview.background": "transparent"
|
"svg.preview.background": "transparent"
|
||||||
}
|
}
|
132
CHANGELOG.md
132
CHANGELOG.md
@@ -2,6 +2,138 @@
|
|||||||
|
|
||||||
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.5.17](https://github.com/antares-sql/antares/compare/v0.5.16...v0.5.17) (2022-09-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* added more editor font sizes, closes [#440](https://github.com/antares-sql/antares/issues/440) ([d114f8a](https://github.com/antares-sql/antares/commit/d114f8a65164f702b23175095e6fc2b021e0e038))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* "run or reload" shortcut triggers on all connections open ([01f607c](https://github.com/antares-sql/antares/commit/01f607cd40c18ab0f9761b2a05705a966aaae43a))
|
||||||
|
* cant run procedures with parameters from leftbar ([efe134a](https://github.com/antares-sql/antares/commit/efe134a059700ca87333dc6e66166d6ec8d289e8))
|
||||||
|
* editor font size doesn't change on new tabs, fixes [#442](https://github.com/antares-sql/antares/issues/442) ([84168d1](https://github.com/antares-sql/antares/commit/84168d1d75460acc2c844bfece7d85f0c977e74c))
|
||||||
|
* empty definer when editing a view, fixes [#437](https://github.com/antares-sql/antares/issues/437) ([498a9b4](https://github.com/antares-sql/antares/commit/498a9b48e25ee061960f5f649c953cdaf6ff1a58))
|
||||||
|
* **MacOS:** empty options on macos menubar ([a142d3c](https://github.com/antares-sql/antares/commit/a142d3c4d77e31375dfbea148eec54ce1f635192))
|
||||||
|
|
||||||
|
### [0.5.16](https://github.com/antares-sql/antares/compare/v0.5.15...v0.5.16) (2022-08-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* CTRL+Right/Left not working on text editor, closes [#427](https://github.com/antares-sql/antares/issues/427) ([ffc645b](https://github.com/antares-sql/antares/commit/ffc645ba5efb1c52670096e4f8c7f992b7335dae))
|
||||||
|
* issue updating datetime cells with null value, closes [#423](https://github.com/antares-sql/antares/issues/423) ([ebc325a](https://github.com/antares-sql/antares/commit/ebc325ae0c656dca2eb8f7544ab271beaee9b47e))
|
||||||
|
* ts exceptions ([df68114](https://github.com/antares-sql/antares/commit/df681147aaf0bfca69f3ffdc474cc1846541b1d8))
|
||||||
|
* **UI:** editor themes group not visible in select element ([9dc700e](https://github.com/antares-sql/antares/commit/9dc700e13ea65bb8c6feac4ff4ffeadd32053614))
|
||||||
|
* **UI:** wrong position of fields resizable area ([c90ab0e](https://github.com/antares-sql/antares/commit/c90ab0e8807ff30a9ab58e9aa3515cf427dd6e86))
|
||||||
|
* unable to set null or delete rows without primary key ([39326eb](https://github.com/antares-sql/antares/commit/39326eb52e038728b5419d4a8de8024c7ead3002))
|
||||||
|
|
||||||
|
### [0.5.15](https://github.com/antares-sql/antares/compare/v0.5.14...v0.5.15) (2022-08-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* ability to add new shortcuts ([d044a02](https://github.com/antares-sql/antares/commit/d044a02cb79a9d06aadc34cdbf6e81da84360559))
|
||||||
|
* ability to edit shortcuts ([8eb127e](https://github.com/antares-sql/antares/commit/8eb127e45838bc01ba12f0740fec077fcd975532))
|
||||||
|
* added more events in shortcuts setting ([5043faf](https://github.com/antares-sql/antares/commit/5043fafa934844ebc2f59cabcec830c6a4d5ca8e))
|
||||||
|
* delete shortcuts and restore defaults ([c22413f](https://github.com/antares-sql/antares/commit/c22413fde9dfe5501a5f220070cfe552a318c70b))
|
||||||
|
* dynamic shortcut suggestions on empty query tabs ([4df14c3](https://github.com/antares-sql/antares/commit/4df14c3693955bd7801b4b99103fca85f00f3e8c))
|
||||||
|
* list of available shortcuts in settings window ([44bb75b](https://github.com/antares-sql/antares/commit/44bb75bc60d7d31bbd99a9ba57f30fd354f7581c))
|
||||||
|
* **UI:** connection name on left bar, closes [#382](https://github.com/antares-sql/antares/issues/382) [#414](https://github.com/antares-sql/antares/issues/414) ([4887753](https://github.com/antares-sql/antares/commit/48877534d1a41d351b267c0dab925046ca984179))
|
||||||
|
* **UI:** shortcuts setting UI improved ([49b63bc](https://github.com/antares-sql/antares/commit/49b63bc6f28fc6031e6a892d0a48cd35ae2f26cd))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* startup exception ([c50d17e](https://github.com/antares-sql/antares/commit/c50d17e82b7fd337d4037ddf646cd1a8fc765bae))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* improved keypress detector ([0f219cf](https://github.com/antares-sql/antares/commit/0f219cf9b796b4369c609fb0e8e3b84346a30b07))
|
||||||
|
* **translation:** updated italian translation ([c05be83](https://github.com/antares-sql/antares/commit/c05be8304f3cf299cf338f67c00184305e022919))
|
||||||
|
|
||||||
|
### [0.5.14](https://github.com/antares-sql/antares/compare/v0.5.13...v0.5.14) (2022-08-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* unable to open settingbar context menu ([44eb507](https://github.com/antares-sql/antares/commit/44eb507a12bad028a4fa8a8bb0f6442a3e8dde91))
|
||||||
|
|
||||||
|
### [0.5.13](https://github.com/antares-sql/antares/compare/v0.5.12...v0.5.13) (2022-08-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* copy row as CSV, closes [#394](https://github.com/antares-sql/antares/issues/394) ([1c3d7aa](https://github.com/antares-sql/antares/commit/1c3d7aa30bb9c2bd900a764ee6b97960729e9263))
|
||||||
|
* new macos icon ([0bfa14e](https://github.com/antares-sql/antares/commit/0bfa14e1c90320578597df030941530b670a4131))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** error with ANSI sql_mode ([f64a12a](https://github.com/antares-sql/antares/commit/f64a12a8e9c5f764c3a692f1a032736e008058b5))
|
||||||
|
* set legacy: false ([104b7c9](https://github.com/antares-sql/antares/commit/104b7c928b9c2abfc056880f16c606a0b1fa7c67))
|
||||||
|
|
||||||
|
### [0.5.12](https://github.com/antares-sql/antares/compare/v0.5.11...v0.5.12) (2022-07-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* ability to copy multiple selected rows ([9551afb](https://github.com/antares-sql/antares/commit/9551afbd2d7e525c81f28e98e788b92609ce9de4))
|
||||||
|
* context menu option to duplicate a table row ([985e5d3](https://github.com/antares-sql/antares/commit/985e5d352793d1b3e1981d004b6f494bfbb049bf))
|
||||||
|
* copy row as SQL INSERT ([d3da15a](https://github.com/antares-sql/antares/commit/d3da15aa1377dcba73927047563f1d0c2d1284ca))
|
||||||
|
* execute selected query ([7890263](https://github.com/antares-sql/antares/commit/78902639ebb29a8c53f8aa0d2045c74e0646febc))
|
||||||
|
* export table content as SQL INSERT ([f3b5de3](https://github.com/antares-sql/antares/commit/f3b5de38c4abfd2c1d738e179fc22e6c8b6f9080))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* disable ctrl+alt+(left/right) shortcut on linux ([8ecaedb](https://github.com/antares-sql/antares/commit/8ecaedbf6c2fc0dc56ff2177a87dd6ede74bdd22))
|
||||||
|
* error on schema export ([1d151e9](https://github.com/antares-sql/antares/commit/1d151e9349fd97576ccd8ef7f88ca789a1f28b65))
|
||||||
|
* issue with logger on import/export ([cb038b3](https://github.com/antares-sql/antares/commit/cb038b374a4fe85ad569e42eee7af123c925e775))
|
||||||
|
* missing defaults on insert row window ([1ead76c](https://github.com/antares-sql/antares/commit/1ead76c02889f48bd91cae702820b082ca2ff54b))
|
||||||
|
* missing table on insert new records on session restored tabs ([8c83b3f](https://github.com/antares-sql/antares/commit/8c83b3f1447354ec63b2a308db05ad4d54659aa7))
|
||||||
|
* **MySQL:** missing quoted identifier for column names in table filter, closes [#380](https://github.com/antares-sql/antares/issues/380) ([eb60899](https://github.com/antares-sql/antares/commit/eb60899e6e17879c79a7ee7108061e9aca8596f7))
|
||||||
|
* prevent ctrl+a in console ([a00c19d](https://github.com/antares-sql/antares/commit/a00c19d3003cd43d3ee6e3132728122bb2b24c97))
|
||||||
|
|
||||||
|
### [0.5.11](https://github.com/antares-sql/antares/compare/v0.5.10...v0.5.11) (2022-07-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* console events disabled in production ([0b1aa3d](https://github.com/antares-sql/antares/commit/0b1aa3dd299db641df3d4c56c7ee56a187fc3ab3))
|
||||||
|
* filter persists switching temporary table tabs ([bf768c3](https://github.com/antares-sql/antares/commit/bf768c380087b65604b5b571a9858a7f07bd681d))
|
||||||
|
* unable to edit table fields content on tables with datetime fields ([91e0630](https://github.com/antares-sql/antares/commit/91e06305133c97ea02dcfdc4e739a4b0a7e7049d))
|
||||||
|
|
||||||
|
### [0.5.10](https://github.com/antares-sql/antares/compare/v0.5.9...v0.5.10) (2022-07-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* context menu to copy queries from console ([c21bd60](https://github.com/antares-sql/antares/commit/c21bd6075c1203607c05e45b76233d57e3008190))
|
||||||
|
* Ctrl+PgUp & Ctrl+PgDn to navigate between tabs ([abf8298](https://github.com/antares-sql/antares/commit/abf829867e567354e534cff3e02a6d43f4c7a262))
|
||||||
|
* field names suggestion for tables in the query ([b71f04e](https://github.com/antares-sql/antares/commit/b71f04e5aa3c37eaa160dfbc76d1b84789e3543e))
|
||||||
|
* initial console implementation ([6a6f43a](https://github.com/antares-sql/antares/commit/6a6f43a718561e0abd2cb89048b7fe45d08736ae))
|
||||||
|
* ipc event channel to send logs to renderer ([f12a04b](https://github.com/antares-sql/antares/commit/f12a04b0524f1172334c89afeb27675c19ff68d2))
|
||||||
|
* open/close console on single connection ([44647f5](https://github.com/antares-sql/antares/commit/44647f5b5508965bf5a7264add89e175c725e877))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* exception on QueryEditor with null modelValue ([9bc9adb](https://github.com/antares-sql/antares/commit/9bc9adb7cff19b86a99491d968485a4cd7b47b99))
|
||||||
|
* fields content language detection not working properly ([a91fa8f](https://github.com/antares-sql/antares/commit/a91fa8ff54bbf1f8475666efd3a268a3a4f07f0c))
|
||||||
|
* **Linux:** ctrl+space shortcut not working ([ed3d35f](https://github.com/antares-sql/antares/commit/ed3d35f1319a1e2edcb8104f2045a71b9e9754a2))
|
||||||
|
* unable to delete by select all in left bar search, closes [#368](https://github.com/antares-sql/antares/issues/368) ([7725faf](https://github.com/antares-sql/antares/commit/7725fafe852479720fa619ced0970f2fa0099191))
|
||||||
|
* unable to update data on tables missing primary or unique key ([e0946f0](https://github.com/antares-sql/antares/commit/e0946f04f792d25c187ea56d4714bdacc016ada3))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* improved resize of text editor resizing console height ([3f9e6d8](https://github.com/antares-sql/antares/commit/3f9e6d85ca445eea1028effa32418eee4980f87d))
|
||||||
|
* **UI:** improved visibility of explore bar tooltips ([f312cf5](https://github.com/antares-sql/antares/commit/f312cf5f855deddd562c26d1835f78d16499b93b))
|
||||||
|
|
||||||
### [0.5.9](https://github.com/antares-sql/antares/compare/v0.5.8...v0.5.9) (2022-07-06)
|
### [0.5.9](https://github.com/antares-sql/antares/compare/v0.5.8...v0.5.9) (2022-07-06)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -44,8 +44,7 @@ In this folder is located the structure of Vue frontend application.
|
|||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
The command to build Antares SQL locally is `npm run build:local`.
|
The command to build Antares SQL locally is `npm run build`.
|
||||||
`build` command (without `:local`) is used exclusively by the GitHub Action.
|
|
||||||
|
|
||||||
## Conventions
|
## Conventions
|
||||||
|
|
||||||
|
16
README.md
16
README.md
@@ -13,12 +13,13 @@ Antares is an SQL client based on [Electron.js](https://github.com/electron/elec
|
|||||||
Our 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, PostgreSQL and SQLite.
|
**At the moment this application is in development state, many features will come in future updates**, and supports only MySQL/MariaDB, PostgreSQL and SQLite.
|
||||||
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.
|
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.
|
||||||
We are actively working on it, hoping to provide new cool features, improvements 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 [follow Antares SQL](https://twitter.com/AntaresSQL) on Twitter.
|
👁 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.
|
||||||
|
🗳️ Poll: **[Which is the main OS you use Antares on?](https://github.com/antares-sql/antares/discussions/379)**
|
||||||
|
|
||||||
## Current key features
|
## Current key features
|
||||||
|
|
||||||
@@ -33,6 +34,7 @@ We are actively working on it, hoping to provide new cool features, improvements
|
|||||||
- SSH tunnel support.
|
- SSH tunnel support.
|
||||||
- Manual commit mode.
|
- Manual commit mode.
|
||||||
- Import and export database dumps.
|
- Import and export database dumps.
|
||||||
|
- Customizable keyboard shortcuts.
|
||||||
- Dark and light theme.
|
- Dark and light theme.
|
||||||
- Editor themes.
|
- Editor themes.
|
||||||
|
|
||||||
@@ -40,20 +42,20 @@ We are actively working on it, hoping to provide new cool features, improvements
|
|||||||
|
|
||||||
Why are we developing an SQL client when there are a lot of them on the market?
|
Why are we developing an SQL client when there are a lot of them on the market?
|
||||||
The main goal is to develop a **forever 100% free (without paid premium feature)**, full featured, as possible community driven, cross platform and open source alternative, empowered by JavaScript ecosystem.
|
The main goal is to develop a **forever 100% free (without paid premium feature)**, full featured, as possible community driven, cross platform and open source alternative, empowered by JavaScript 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.
|
A modern application created with minimalism and simplicity in mind, with features in the right places, not hundreds of tiny buttons, nested tabs or submenues; productivity comes first.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Based on your operating system you can have one or more distribution formats to choose based on your preferences.
|
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.
|
Since Antares SQL is a free software we don't have a budget to spend on annual licenses or certificates. This can result that on some platforms you might need to put in some additional work to install this app.
|
||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
On Linux you can simply download and run `.AppImage` distributions, install from Snap Store or from AUR.
|
On Linux you can simply download and run the `.AppImage` distribution, install from Snap Store, from AUR or from our [PPA repository](https://github.com/antares-sql/antares-ppa).
|
||||||
|
|
||||||
### Windows
|
### 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.
|
On Windows you can choose between downloading the app from Microsoft Store or downloading the `.exe` from our [website](https://antares-sql.app/downloads) or [this github repo](https://github.com/Fabio286/antares/releases/latest). Distributions that are not from Microsoft Store are not signed with a certificate, so to install you need to click on "More info" and then "Run anyway" on SmartScreen prompt.
|
||||||
|
|
||||||
### MacOS
|
### MacOS
|
||||||
|
|
||||||
@@ -61,7 +63,7 @@ On macOS you can run `.dmg` distribution following [this guide](https://support.
|
|||||||
|
|
||||||
## Download
|
## 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)
|
[](https://snapcraft.io/antares) [](https://aur.archlinux.org/packages/antares-sql/) [<img src="https://developer.microsoft.com/store/badges/images/English_get-it-from-MS.png" style="height: 56px">](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)**
|
🚀 **[Other Downloads](https://github.com/Fabio286/antares/releases/latest)**
|
||||||
|
|
||||||
## Coming soon
|
## Coming soon
|
||||||
|
BIN
assets/icon.icns
BIN
assets/icon.icns
Binary file not shown.
BIN
assets/linux/128x128.png
Normal file
BIN
assets/linux/128x128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
assets/linux/16x16.png
Normal file
BIN
assets/linux/16x16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 889 B |
BIN
assets/linux/256x256.png
Normal file
BIN
assets/linux/256x256.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
BIN
assets/linux/32x32.png
Normal file
BIN
assets/linux/32x32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
BIN
assets/linux/64x64.png
Normal file
BIN
assets/linux/64x64.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
13157
package-lock.json
generated
13157
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
38
package.json
38
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "antares",
|
"name": "antares",
|
||||||
"productName": "Antares",
|
"productName": "Antares",
|
||||||
"version": "0.5.9",
|
"version": "0.5.17",
|
||||||
"description": "A modern, fast and productivity driven SQL client with a focus in UX.",
|
"description": "A modern, fast and productivity driven SQL client with a focus in UX.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": "https://github.com/antares-sql/antares.git",
|
"repository": "https://github.com/antares-sql/antares.git",
|
||||||
@@ -12,14 +12,13 @@
|
|||||||
"compile:main": "webpack --mode=production --config webpack.main.config.js",
|
"compile:main": "webpack --mode=production --config webpack.main.config.js",
|
||||||
"compile:workers": "webpack --mode=production --config webpack.workers.config.js",
|
"compile:workers": "webpack --mode=production --config webpack.workers.config.js",
|
||||||
"compile:renderer": "webpack --mode=production --config webpack.renderer.config.js",
|
"compile:renderer": "webpack --mode=production --config webpack.renderer.config.js",
|
||||||
"build": "cross-env NODE_ENV=production npm run compile",
|
"build": "cross-env NODE_ENV=production npm run compile && electron-builder --publish never",
|
||||||
"build:local": "npm run build && electron-builder --publish never",
|
"build:appx": "npm run build -- --win appx",
|
||||||
"build:appx": "npm run build:local -- --win appx",
|
"rebuild:electron": "rimraf ./dist && npm run postinstall && npm run devtools:install",
|
||||||
"rebuild:electron": "rimraf ./dist && npm run postinstall",
|
|
||||||
"release": "standard-version",
|
"release": "standard-version",
|
||||||
"release:pre": "npm run release -- --prerelease alpha",
|
"release:pre": "npm run release -- --prerelease alpha",
|
||||||
"devtools:install": "node scripts/devtoolsInstaller",
|
"devtools:install": "node scripts/devtoolsInstaller",
|
||||||
"postinstall": "electron-builder install-app-deps && npm run devtools:install",
|
"postinstall": "electron-builder install-app-deps",
|
||||||
"test:e2e": "npm run compile && npm run test:e2e-dry",
|
"test:e2e": "npm run compile && npm run test:e2e-dry",
|
||||||
"test:e2e-dry": "xvfb-maybe -- playwright test",
|
"test:e2e-dry": "xvfb-maybe -- playwright test",
|
||||||
"lint": "eslint . --ext .js,.ts,.vue && stylelint \"./src/**/*.{css,scss,sass,vue}\"",
|
"lint": "eslint . --ext .js,.ts,.vue && stylelint \"./src/**/*.{css,scss,sass,vue}\"",
|
||||||
@@ -65,7 +64,11 @@
|
|||||||
"target": [
|
"target": [
|
||||||
{
|
{
|
||||||
"target": "deb",
|
"target": "deb",
|
||||||
"arch": "x64"
|
"arch": [
|
||||||
|
"x64",
|
||||||
|
"armv7l",
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"target": "AppImage",
|
"target": "AppImage",
|
||||||
@@ -76,6 +79,7 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"icon": "assets/linux",
|
||||||
"category": "Development"
|
"category": "Development"
|
||||||
},
|
},
|
||||||
"appImage": {
|
"appImage": {
|
||||||
@@ -117,11 +121,10 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/remote": "~2.0.1",
|
"@electron/remote": "~2.0.1",
|
||||||
"@faker-js/faker": "~6.1.2",
|
"@faker-js/faker": "~6.1.2",
|
||||||
"@mdi/font": "~6.9.96",
|
"@mdi/font": "~7.0.96",
|
||||||
"@turf/helpers": "~6.5.0",
|
"@turf/helpers": "~6.5.0",
|
||||||
"@vscode/vscode-languagedetection": "~1.0.21",
|
|
||||||
"@vueuse/core": "~8.7.5",
|
"@vueuse/core": "~8.7.5",
|
||||||
"ace-builds": "~1.4.13",
|
"ace-builds": "~1.8.1",
|
||||||
"better-sqlite3": "~7.5.1",
|
"better-sqlite3": "~7.5.1",
|
||||||
"electron-log": "~4.4.1",
|
"electron-log": "~4.4.1",
|
||||||
"electron-store": "~8.0.1",
|
"electron-store": "~8.0.1",
|
||||||
@@ -130,19 +133,20 @@
|
|||||||
"encoding": "~0.1.13",
|
"encoding": "~0.1.13",
|
||||||
"leaflet": "~1.7.1",
|
"leaflet": "~1.7.1",
|
||||||
"marked": "~4.0.0",
|
"marked": "~4.0.0",
|
||||||
"moment": "~2.29.1",
|
"moment": "~2.29.4",
|
||||||
"mysql2": "~2.3.2",
|
"mysql2": "~2.3.2",
|
||||||
"pg": "~8.7.1",
|
"pg": "~8.7.1",
|
||||||
|
"pg-connection-string": "~2.5.0",
|
||||||
"pg-query-stream": "~4.2.3",
|
"pg-query-stream": "~4.2.3",
|
||||||
"pgsql-ast-parser": "~7.2.1",
|
"pgsql-ast-parser": "~7.2.1",
|
||||||
"pinia": "~2.0.13",
|
"pinia": "~2.0.13",
|
||||||
"source-map-support": "~0.5.20",
|
"source-map-support": "~0.5.20",
|
||||||
"spectre.css": "~0.5.9",
|
"spectre.css": "~0.5.9",
|
||||||
"sql-formatter": "~4.0.2",
|
"sql-formatter": "~8.2.0",
|
||||||
"ssh2-promise": "~1.0.2",
|
"ssh2-promise": "~1.0.2",
|
||||||
"v-mask": "~2.3.0",
|
"v-mask": "~2.3.0",
|
||||||
"vue": "~3.2.37",
|
"vue": "~3.2.37",
|
||||||
"vue-i18n": "~9.1.9",
|
"vue-i18n": "~9.2.0",
|
||||||
"vuedraggable": "~4.1.0"
|
"vuedraggable": "~4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -177,15 +181,17 @@
|
|||||||
"node-loader": "~2.0.0",
|
"node-loader": "~2.0.0",
|
||||||
"playwright": "~1.21.1",
|
"playwright": "~1.21.1",
|
||||||
"playwright-core": "~1.21.1",
|
"playwright-core": "~1.21.1",
|
||||||
|
"postcss-html": "~1.5.0",
|
||||||
"progress-webpack-plugin": "~1.0.12",
|
"progress-webpack-plugin": "~1.0.12",
|
||||||
"rimraf": "~3.0.2",
|
"rimraf": "~3.0.2",
|
||||||
"sass": "~1.42.1",
|
"sass": "~1.42.1",
|
||||||
"sass-loader": "~12.3.0",
|
"sass-loader": "~12.3.0",
|
||||||
"standard-version": "~9.3.1",
|
"standard-version": "~9.3.1",
|
||||||
"style-loader": "~3.3.1",
|
"style-loader": "~3.3.1",
|
||||||
"stylelint": "~13.13.1",
|
"stylelint": "~14.9.1",
|
||||||
"stylelint-config-standard": "~22.0.0",
|
"stylelint-config-recommended-vue": "~1.4.0",
|
||||||
"stylelint-scss": "~3.21.0",
|
"stylelint-config-standard": "~26.0.0",
|
||||||
|
"stylelint-scss": "~4.3.0",
|
||||||
"tree-kill": "~1.2.2",
|
"tree-kill": "~1.2.2",
|
||||||
"ts-loader": "~9.2.8",
|
"ts-loader": "~9.2.8",
|
||||||
"typescript": "~4.6.3",
|
"typescript": "~4.6.3",
|
||||||
|
@@ -114,7 +114,6 @@ function startRenderer (callback) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const server = new WebpackDevServer(compiler, {
|
const server = new WebpackDevServer(compiler, {
|
||||||
hot: true,
|
|
||||||
port: 9080,
|
port: 9080,
|
||||||
client: {
|
client: {
|
||||||
overlay: true,
|
overlay: true,
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
// @ts-check
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-nocheck
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const https = require('https');
|
const https = require('https');
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
export default class {
|
export default class {
|
||||||
static get _methods () {
|
static get _methods () {
|
||||||
return [
|
return [
|
||||||
@@ -180,7 +181,7 @@ export default class {
|
|||||||
acc[curr.group] = new Set(curr.types);
|
acc[curr.group] = new Set(curr.types);
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {} as any);
|
||||||
|
|
||||||
const groupsArr = [];
|
const groupsArr = [];
|
||||||
|
|
||||||
@@ -198,12 +199,12 @@ export default class {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static getGroupsByType (type) {
|
static getGroupsByType (type: string) {
|
||||||
if (!type) return [];
|
if (!type) return [];
|
||||||
return this.getGroups().filter(group => group.types.includes(type));
|
return this.getGroups().filter(group => group.types.includes(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
static getMethods ({ type, group }) {
|
static getMethods ({ type, group }: {type: string; group: string}) {
|
||||||
return this._methods.filter(method => method.group === group && method.types.includes(type)).sort((a, b) => {
|
return this._methods.filter(method => method.group === group && method.types.includes(type)).sort((a, b) => {
|
||||||
if (a.name < b.name)
|
if (a.name < b.name)
|
||||||
return -1;
|
return -1;
|
||||||
|
@@ -25,7 +25,7 @@ export const customizations: Customizations = {
|
|||||||
functions: true,
|
functions: true,
|
||||||
schedulers: true,
|
schedulers: true,
|
||||||
// Settings
|
// Settings
|
||||||
elementsWrapper: '',
|
elementsWrapper: '`',
|
||||||
stringsWrapper: '"',
|
stringsWrapper: '"',
|
||||||
tableAdd: true,
|
tableAdd: true,
|
||||||
tableTruncateDisableFKCheck: true,
|
tableTruncateDisableFKCheck: true,
|
||||||
|
@@ -25,6 +25,7 @@ export interface IpcResponse<T = any> {
|
|||||||
*/
|
*/
|
||||||
export interface ClientParams {
|
export interface ClientParams {
|
||||||
client: ClientCode;
|
client: ClientCode;
|
||||||
|
uid?: string;
|
||||||
params:
|
params:
|
||||||
mysql.ConnectionOptions & {schema: string; ssl?: mysql.SslOptions; ssh?: SSHConfig; readonly: boolean}
|
mysql.ConnectionOptions & {schema: string; ssl?: mysql.SslOptions; ssh?: SSHConfig; readonly: boolean}
|
||||||
| pg.ClientConfig & {schema: string; ssl?: mysql.SslOptions; ssh?: SSHConfig; readonly: boolean}
|
| pg.ClientConfig & {schema: string; ssl?: mysql.SslOptions; ssh?: SSHConfig; readonly: boolean}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
export function bufferToBase64 (buf: Buffer) {
|
export function bufferToBase64 (buf: Buffer) {
|
||||||
const binstr = Array.prototype.map.call(buf, ch => {
|
const binstr = Array.prototype.map.call(buf, (ch: number) => {
|
||||||
return String.fromCharCode(ch);
|
return String.fromCharCode(ch);
|
||||||
}).join('');
|
}).join('');
|
||||||
return Buffer.from(binstr, 'binary').toString('base64');
|
return Buffer.from(binstr, 'binary').toString('base64');
|
||||||
|
192
src/common/libs/langDetector.ts
Normal file
192
src/common/libs/langDetector.ts
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
function isJSON (str: string) {
|
||||||
|
try {
|
||||||
|
if (!['{', '['].includes(str.trim()[0]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
JSON.parse(str);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isHTML (str: string) {
|
||||||
|
const tags = [
|
||||||
|
'a',
|
||||||
|
'abbr',
|
||||||
|
'address',
|
||||||
|
'area',
|
||||||
|
'article',
|
||||||
|
'aside',
|
||||||
|
'audio',
|
||||||
|
'b',
|
||||||
|
'base',
|
||||||
|
'bdi',
|
||||||
|
'bdo',
|
||||||
|
'blockquote',
|
||||||
|
'body',
|
||||||
|
'br',
|
||||||
|
'button',
|
||||||
|
'canvas',
|
||||||
|
'caption',
|
||||||
|
'cite',
|
||||||
|
'code',
|
||||||
|
'col',
|
||||||
|
'colgroup',
|
||||||
|
'data',
|
||||||
|
'datalist',
|
||||||
|
'dd',
|
||||||
|
'del',
|
||||||
|
'details',
|
||||||
|
'dfn',
|
||||||
|
'dialog',
|
||||||
|
'div',
|
||||||
|
'dl',
|
||||||
|
'dt',
|
||||||
|
'em',
|
||||||
|
'embed',
|
||||||
|
'fieldset',
|
||||||
|
'figcaption',
|
||||||
|
'figure',
|
||||||
|
'footer',
|
||||||
|
'form',
|
||||||
|
'h1',
|
||||||
|
'h2',
|
||||||
|
'h3',
|
||||||
|
'h4',
|
||||||
|
'h5',
|
||||||
|
'h6',
|
||||||
|
'head',
|
||||||
|
'header',
|
||||||
|
'hgroup',
|
||||||
|
'hr',
|
||||||
|
'html',
|
||||||
|
'i',
|
||||||
|
'iframe',
|
||||||
|
'img',
|
||||||
|
'input',
|
||||||
|
'ins',
|
||||||
|
'kbd',
|
||||||
|
'label',
|
||||||
|
'legend',
|
||||||
|
'li',
|
||||||
|
'link',
|
||||||
|
'main',
|
||||||
|
'map',
|
||||||
|
'mark',
|
||||||
|
'math',
|
||||||
|
'menu',
|
||||||
|
'menuitem',
|
||||||
|
'meta',
|
||||||
|
'meter',
|
||||||
|
'nav',
|
||||||
|
'noscript',
|
||||||
|
'object',
|
||||||
|
'ol',
|
||||||
|
'optgroup',
|
||||||
|
'option',
|
||||||
|
'output',
|
||||||
|
'p',
|
||||||
|
'param',
|
||||||
|
'picture',
|
||||||
|
'pre',
|
||||||
|
'progress',
|
||||||
|
'q',
|
||||||
|
'rb',
|
||||||
|
'rp',
|
||||||
|
'rt',
|
||||||
|
'rtc',
|
||||||
|
'ruby',
|
||||||
|
's',
|
||||||
|
'samp',
|
||||||
|
'script',
|
||||||
|
'section',
|
||||||
|
'select',
|
||||||
|
'slot',
|
||||||
|
'small',
|
||||||
|
'source',
|
||||||
|
'span',
|
||||||
|
'strong',
|
||||||
|
'style',
|
||||||
|
'sub',
|
||||||
|
'summary',
|
||||||
|
'sup',
|
||||||
|
'svg',
|
||||||
|
'table',
|
||||||
|
'tbody',
|
||||||
|
'td',
|
||||||
|
'template',
|
||||||
|
'textarea',
|
||||||
|
'tfoot',
|
||||||
|
'th',
|
||||||
|
'thead',
|
||||||
|
'time',
|
||||||
|
'title',
|
||||||
|
'tr',
|
||||||
|
'track',
|
||||||
|
'u',
|
||||||
|
'ul',
|
||||||
|
'var',
|
||||||
|
'video',
|
||||||
|
'wbr'
|
||||||
|
];
|
||||||
|
const doc = new DOMParser().parseFromString(str, 'text/html');
|
||||||
|
const lowerStr = str.toLowerCase();
|
||||||
|
if (Array.from(doc.body.childNodes).some(node => node.nodeType === 1))
|
||||||
|
return tags.some((tag) => lowerStr.includes(`<${tag}>`));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSVG (str: string) {
|
||||||
|
const doc = new DOMParser().parseFromString(str, 'text/xml');
|
||||||
|
const lowerStr = str.toLowerCase();
|
||||||
|
const errorNode = doc.querySelector('parsererror');
|
||||||
|
if (!errorNode)
|
||||||
|
return lowerStr.includes('<svg');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isXML (str: string) {
|
||||||
|
const doc = new DOMParser().parseFromString(str, 'text/xml');
|
||||||
|
const errorNode = doc.querySelector('parsererror');
|
||||||
|
return !errorNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMD (str: string) {
|
||||||
|
const mdChecks = [
|
||||||
|
'# ',
|
||||||
|
'`',
|
||||||
|
'- ',
|
||||||
|
'+ ',
|
||||||
|
'* ',
|
||||||
|
'1. ',
|
||||||
|
'**',
|
||||||
|
'__',
|
||||||
|
'~~',
|
||||||
|
'>> ',
|
||||||
|
'](http',
|
||||||
|
'![',
|
||||||
|
'[ ]',
|
||||||
|
'[x]'
|
||||||
|
];
|
||||||
|
|
||||||
|
return mdChecks.some((tag) => str.includes(tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function langDetector (str: string) {
|
||||||
|
if (!str.trim().length)
|
||||||
|
return 'text';
|
||||||
|
if (isJSON(str))
|
||||||
|
return 'json';
|
||||||
|
if (isHTML(str))
|
||||||
|
return 'html';
|
||||||
|
if (isSVG(str))
|
||||||
|
return 'svg';
|
||||||
|
if (isXML(str))
|
||||||
|
return 'xml';
|
||||||
|
if (isMD(str))
|
||||||
|
return 'markdown';
|
||||||
|
return 'text';
|
||||||
|
}
|
@@ -1,14 +0,0 @@
|
|||||||
/* eslint-disable no-useless-escape */
|
|
||||||
// eslint-disable-next-line no-control-regex
|
|
||||||
const pattern = /[\0\x08\x09\x1a\n\r"'\\\%]/gm;
|
|
||||||
const regex = new RegExp(pattern);
|
|
||||||
|
|
||||||
function sqlEscaper (string: string) {
|
|
||||||
return string.replace(regex, char => {
|
|
||||||
const m = ['\\0', '\\x08', '\\x09', '\\x1a', '\\n', '\\r', '\'', '\"', '\\', '\\\\', '%'];
|
|
||||||
const r = ['\\\\0', '\\\\b', '\\\\t', '\\\\z', '\\\\n', '\\\\r', '\\\'', '\\\"', '\\\\', '\\\\\\\\', '\%'];
|
|
||||||
return r[m.indexOf(char)] || char;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export { sqlEscaper };
|
|
162
src/common/libs/sqlUtils.ts
Normal file
162
src/common/libs/sqlUtils.ts
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
/* eslint-disable no-useless-escape */
|
||||||
|
import * as moment from 'moment';
|
||||||
|
import { lineString, point, polygon } from '@turf/helpers';
|
||||||
|
import customizations from '../customizations';
|
||||||
|
import { ClientCode } from '../interfaces/antares';
|
||||||
|
import { BLOB, BIT, DATE, DATETIME, FLOAT, SPATIAL, IS_MULTI_SPATIAL, NUMBER, TEXT_SEARCH } from 'common/fieldTypes';
|
||||||
|
import hexToBinary, { HexChar } from './hexToBinary';
|
||||||
|
import { getArrayDepth } from './getArrayDepth';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes a string fo SQL use
|
||||||
|
*
|
||||||
|
* @param { String } string
|
||||||
|
* @returns { String } Escaped string
|
||||||
|
*/
|
||||||
|
export const sqlEscaper = (string: string): string => {
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
|
const pattern = /[\0\x08\x09\x1a\n\r"'\\\%]/gm;
|
||||||
|
const regex = new RegExp(pattern);
|
||||||
|
return string.replace(regex, char => {
|
||||||
|
const m = ['\\0', '\\x08', '\\x09', '\\x1a', '\\n', '\\r', '\'', '\"', '\\', '\\\\', '%'];
|
||||||
|
const r = ['\\\\0', '\\\\b', '\\\\t', '\\\\z', '\\\\n', '\\\\r', '\\\'', '\\\"', '\\\\', '\\\\\\\\', '\%'];
|
||||||
|
return r[m.indexOf(char)] || char;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const objectToGeoJSON = (val: any) => {
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
if (getArrayDepth(val) === 1)
|
||||||
|
return lineString(val.reduce((acc, curr) => [...acc, [curr.x, curr.y]], []));
|
||||||
|
else
|
||||||
|
return polygon(val.map(arr => arr.reduce((acc: any, curr: any) => [...acc, [curr.x, curr.y]], [])));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return point([val.x, val.y]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const escapeAndQuote = (val: string, client: ClientCode) => {
|
||||||
|
const { stringsWrapper: sw } = customizations[client];
|
||||||
|
// eslint-disable-next-line no-control-regex
|
||||||
|
const CHARS_TO_ESCAPE = /[\0\b\t\n\r\x1a"'\\]/g;
|
||||||
|
const CHARS_ESCAPE_MAP: {[key: string]: string} = {
|
||||||
|
'\0': '\\0',
|
||||||
|
'\b': '\\b',
|
||||||
|
'\t': '\\t',
|
||||||
|
'\n': '\\n',
|
||||||
|
'\r': '\\r',
|
||||||
|
'\x1a': '\\Z',
|
||||||
|
'"': '\\"',
|
||||||
|
'\'': '\\\'',
|
||||||
|
'\\': '\\\\'
|
||||||
|
};
|
||||||
|
let chunkIndex = CHARS_TO_ESCAPE.lastIndex = 0;
|
||||||
|
let escapedVal = '';
|
||||||
|
let match;
|
||||||
|
|
||||||
|
while ((match = CHARS_TO_ESCAPE.exec(val))) {
|
||||||
|
escapedVal += val.slice(chunkIndex, match.index) + CHARS_ESCAPE_MAP[match[0]];
|
||||||
|
chunkIndex = CHARS_TO_ESCAPE.lastIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunkIndex === 0)
|
||||||
|
return `${sw}${val}${sw}`;
|
||||||
|
|
||||||
|
if (chunkIndex < val.length)
|
||||||
|
return `${sw}${escapedVal + val.slice(chunkIndex)}${sw}`;
|
||||||
|
|
||||||
|
return `${sw}${escapedVal}${sw}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const valueToSqlString = (args: {
|
||||||
|
val: any;
|
||||||
|
client: ClientCode;
|
||||||
|
field: {type: string; datePrecision: number};
|
||||||
|
}): string => {
|
||||||
|
let parsedValue;
|
||||||
|
const { val, client, field } = args;
|
||||||
|
const { stringsWrapper: sw } = customizations[client];
|
||||||
|
|
||||||
|
if (val === null)
|
||||||
|
parsedValue = 'NULL';
|
||||||
|
else if (DATE.includes(field.type)) {
|
||||||
|
parsedValue = moment(val).isValid()
|
||||||
|
? escapeAndQuote(moment(val).format('YYYY-MM-DD'), client)
|
||||||
|
: val;
|
||||||
|
}
|
||||||
|
else if (DATETIME.includes(field.type)) {
|
||||||
|
let datePrecision = '';
|
||||||
|
for (let i = 0; i < field.datePrecision; i++)
|
||||||
|
datePrecision += i === 0 ? '.S' : 'S';
|
||||||
|
|
||||||
|
parsedValue = moment(val).isValid()
|
||||||
|
? escapeAndQuote(moment(val).format(`YYYY-MM-DD HH:mm:ss${datePrecision}`), client)
|
||||||
|
: escapeAndQuote(val, client);
|
||||||
|
}
|
||||||
|
else if ('isArray' in field) {
|
||||||
|
let localVal;
|
||||||
|
if (Array.isArray(val))
|
||||||
|
localVal = JSON.stringify(val).replaceAll('[', '{').replaceAll(']', '}');
|
||||||
|
else
|
||||||
|
localVal = typeof val === 'string' ? val.replaceAll('[', '{').replaceAll(']', '}') : '';
|
||||||
|
parsedValue = `'${localVal}'`;
|
||||||
|
}
|
||||||
|
else if (TEXT_SEARCH.includes(field.type))
|
||||||
|
parsedValue = `'${val.replaceAll('\'', '\'\'')}'`;
|
||||||
|
else if (BIT.includes(field.type))
|
||||||
|
parsedValue = `b'${hexToBinary(Buffer.from(val).toString('hex') as undefined as HexChar[])}'`;
|
||||||
|
else if (BLOB.includes(field.type)) {
|
||||||
|
if (['mysql', 'maria'].includes(client))
|
||||||
|
parsedValue = `X'${val.toString('hex').toUpperCase()}'`;
|
||||||
|
else if (client === 'pg')
|
||||||
|
parsedValue = `decode('${val.toString('hex').toUpperCase()}', 'hex')`;
|
||||||
|
}
|
||||||
|
else if (NUMBER.includes(field.type))
|
||||||
|
parsedValue = val;
|
||||||
|
else if (FLOAT.includes(field.type))
|
||||||
|
parsedValue = parseFloat(val);
|
||||||
|
else if (SPATIAL.includes(field.type)) {
|
||||||
|
let geoJson;
|
||||||
|
if (IS_MULTI_SPATIAL.includes(field.type)) {
|
||||||
|
const features = [];
|
||||||
|
for (const element of val)
|
||||||
|
features.push(objectToGeoJSON(element));
|
||||||
|
|
||||||
|
geoJson = {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
geoJson = objectToGeoJSON(val);
|
||||||
|
|
||||||
|
parsedValue = `ST_GeomFromGeoJSON('${JSON.stringify(geoJson)}')`;
|
||||||
|
}
|
||||||
|
else if (val === '') parsedValue = `${sw}${sw}`;
|
||||||
|
else {
|
||||||
|
parsedValue = typeof val === 'string'
|
||||||
|
? escapeAndQuote(val, client)
|
||||||
|
: typeof val === 'object'
|
||||||
|
? escapeAndQuote(JSON.stringify(val), client)
|
||||||
|
: val;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parsedValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const jsonToSqlInsert = (args: {
|
||||||
|
json: { [key: string]: any};
|
||||||
|
client: ClientCode;
|
||||||
|
fields: { [key: string]: {type: string; datePrecision: number}};
|
||||||
|
table: string;
|
||||||
|
}) => {
|
||||||
|
const { client, json, fields, table } = args;
|
||||||
|
const { elementsWrapper: ew } = customizations[client];
|
||||||
|
const fieldNames = Object.keys(json).map(key => `${ew}${key}${ew}`);
|
||||||
|
const values = Object.keys(json).map(key => (
|
||||||
|
valueToSqlString({ val: json[key], client, field: fields[key] })
|
||||||
|
));
|
||||||
|
|
||||||
|
return `INSERT INTO ${ew}${table}${ew} (${fieldNames.join(', ')}) VALUES (${values.join(', ')});`;
|
||||||
|
};
|
138
src/common/shortcuts.ts
Normal file
138
src/common/shortcuts.ts
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
export const shortcutEvents: { [key: string]: { l18n: string; l18nParam?: string | number; context?: 'tab' }} = {
|
||||||
|
'run-or-reload': { l18n: 'message.runOrReload', context: 'tab' },
|
||||||
|
'open-new-tab': { l18n: 'message.openNewTab', context: 'tab' },
|
||||||
|
'close-tab': { l18n: 'message.closeTab', context: 'tab' },
|
||||||
|
'format-query': { l18n: 'message.formatQuery', context: 'tab' },
|
||||||
|
'kill-query': { l18n: 'message.killQuery', context: 'tab' },
|
||||||
|
'query-history': { l18n: 'message.queryHistory', context: 'tab' },
|
||||||
|
'clear-query': { l18n: 'message.clearQuery', context: 'tab' },
|
||||||
|
'next-tab': { l18n: 'message.nextTab' },
|
||||||
|
'prev-tab': { l18n: 'message.previousTab' },
|
||||||
|
'open-all-connections': { l18n: 'message.openAllConnections' },
|
||||||
|
'open-filter': { l18n: 'message.openFilter' },
|
||||||
|
'next-page': { l18n: 'message.nextResultsPage' },
|
||||||
|
'prev-page': { l18n: 'message.previousResultsPage' },
|
||||||
|
'toggle-console': { l18n: 'message.toggleConsole' },
|
||||||
|
'save-content': { l18n: 'message.saveContent' },
|
||||||
|
'create-connection': { l18n: 'message.createNewConnection' },
|
||||||
|
'open-settings': { l18n: 'message.openSettings' },
|
||||||
|
'open-scratchpad': { l18n: 'message.openScratchpad' }
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ShortcutRecord {
|
||||||
|
event: string;
|
||||||
|
keys: Electron.Accelerator[] | string[];
|
||||||
|
/** Needed for default shortcuts */
|
||||||
|
os: NodeJS.Platform[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default shortcuts
|
||||||
|
*/
|
||||||
|
const shortcuts: ShortcutRecord[] = [
|
||||||
|
{
|
||||||
|
event: 'run-or-reload',
|
||||||
|
keys: ['F5'],
|
||||||
|
os: ['darwin', 'linux', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'save-content',
|
||||||
|
keys: ['CommandOrControl+S'],
|
||||||
|
os: ['darwin', 'linux', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'kill-query',
|
||||||
|
keys: ['CommandOrControl+K'],
|
||||||
|
os: ['darwin', 'linux', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'format-query',
|
||||||
|
keys: ['CommandOrControl+B'],
|
||||||
|
os: ['darwin', 'linux', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'clear-query',
|
||||||
|
keys: ['CommandOrControl+Alt+W'],
|
||||||
|
os: ['darwin', 'linux', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'query-history',
|
||||||
|
keys: ['CommandOrControl+G'],
|
||||||
|
os: ['darwin', 'linux', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'open-new-tab',
|
||||||
|
keys: ['CommandOrControl+T'],
|
||||||
|
os: ['darwin', 'linux', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'close-tab',
|
||||||
|
keys: ['CommandOrControl+W'],
|
||||||
|
os: ['darwin', 'linux', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'next-tab',
|
||||||
|
keys: ['Alt+CommandOrControl+Right'],
|
||||||
|
os: ['darwin', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'prev-tab',
|
||||||
|
keys: ['Alt+CommandOrControl+Left'],
|
||||||
|
os: ['darwin', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'next-tab',
|
||||||
|
keys: ['CommandOrControl+PageDown'],
|
||||||
|
os: ['linux', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'prev-tab',
|
||||||
|
keys: ['CommandOrControl+PageUp'],
|
||||||
|
os: ['linux', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'open-filter',
|
||||||
|
keys: ['CommandOrControl+F'],
|
||||||
|
os: ['darwin', 'linux', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'next-page',
|
||||||
|
keys: ['CommandOrControl+Right'],
|
||||||
|
os: ['darwin', 'linux', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'prev-page',
|
||||||
|
keys: ['CommandOrControl+Left'],
|
||||||
|
os: ['darwin', 'linux', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'open-all-connections',
|
||||||
|
keys: ['Shift+CommandOrControl+Space'],
|
||||||
|
os: ['darwin', 'linux', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'toggle-console',
|
||||||
|
keys: ['CommandOrControl+F12'],
|
||||||
|
os: ['darwin', 'linux', 'win32']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'toggle-console',
|
||||||
|
keys: ['CommandOrControl+`'],
|
||||||
|
os: ['darwin', 'linux', 'win32']
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 1; i <= 9; i++) {
|
||||||
|
shortcutEvents[`select-tab-${i}`] = {
|
||||||
|
l18n: 'message.selectTabNumber',
|
||||||
|
l18nParam: i
|
||||||
|
};
|
||||||
|
|
||||||
|
shortcuts.push({
|
||||||
|
event: `select-tab-${i}`,
|
||||||
|
keys: [`CommandOrControl+${i}`],
|
||||||
|
os: ['darwin', 'linux', 'win32']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { shortcuts, ShortcutRecord };
|
@@ -1,4 +1,5 @@
|
|||||||
import { app, ipcMain, dialog } from 'electron';
|
import { app, ipcMain, dialog } from 'electron';
|
||||||
|
import { ShortcutRegister } from '../libs/ShortcutRegister';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
ipcMain.on('close-app', () => {
|
ipcMain.on('close-app', () => {
|
||||||
@@ -12,4 +13,24 @@ export default () => {
|
|||||||
ipcMain.handle('get-download-dir-path', () => {
|
ipcMain.handle('get-download-dir-path', () => {
|
||||||
return app.getPath('downloads');
|
return app.getPath('downloads');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('resotre-default-shortcuts', () => {
|
||||||
|
const shortCutRegister = ShortcutRegister.getInstance();
|
||||||
|
shortCutRegister.restoreDefaults();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('reload-shortcuts', () => {
|
||||||
|
const shortCutRegister = ShortcutRegister.getInstance();
|
||||||
|
shortCutRegister.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('update-shortcuts', (event, shortcuts) => {
|
||||||
|
const shortCutRegister = ShortcutRegister.getInstance();
|
||||||
|
shortCutRegister.updateShortcuts(shortcuts);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('unregister-shortcuts', () => {
|
||||||
|
const shortCutRegister = ShortcutRegister.getInstance();
|
||||||
|
shortCutRegister.unregister();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@@ -55,6 +55,7 @@ export default (connections: {[key: string]: antares.Client}) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const connection = await ClientsFactory.getClient({
|
const connection = await ClientsFactory.getClient({
|
||||||
|
uid: conn.uid,
|
||||||
client: conn.client,
|
client: conn.client,
|
||||||
params
|
params
|
||||||
});
|
});
|
||||||
@@ -128,6 +129,7 @@ export default (connections: {[key: string]: antares.Client}) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const connection = ClientsFactory.getClient({
|
const connection = ClientsFactory.getClient({
|
||||||
|
uid: conn.uid,
|
||||||
client: conn.client,
|
client: conn.client,
|
||||||
params,
|
params,
|
||||||
poolSize: 5
|
poolSize: 5
|
||||||
|
@@ -4,7 +4,7 @@ import { InsertRowsParams } from 'common/interfaces/tableApis';
|
|||||||
import { ipcMain } from 'electron';
|
import { ipcMain } from 'electron';
|
||||||
import { faker } from '@faker-js/faker';
|
import { faker } from '@faker-js/faker';
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { sqlEscaper } from 'common/libs/sqlEscaper';
|
import { sqlEscaper } from 'common/libs/sqlUtils';
|
||||||
import { TEXT, LONG_TEXT, ARRAY, TEXT_SEARCH, NUMBER, FLOAT, BLOB, BIT, DATE, DATETIME } from 'common/fieldTypes';
|
import { TEXT, LONG_TEXT, ARRAY, TEXT_SEARCH, NUMBER, FLOAT, BLOB, BIT, DATE, DATETIME } from 'common/fieldTypes';
|
||||||
import customizations from 'common/customizations';
|
import customizations from 'common/customizations';
|
||||||
|
|
||||||
@@ -177,7 +177,10 @@ export default (connections: {[key: string]: antares.Client}) => {
|
|||||||
if (typeof orgRow[key] === 'string')
|
if (typeof orgRow[key] === 'string')
|
||||||
orgRow[key] = `'${orgRow[key]}'`;
|
orgRow[key] = `'${orgRow[key]}'`;
|
||||||
|
|
||||||
orgRow[key] = `= ${orgRow[key]}`;
|
if (orgRow[key] === null)
|
||||||
|
orgRow[key] = `IS ${orgRow[key]}`;
|
||||||
|
else
|
||||||
|
orgRow[key] = `= ${orgRow[key]}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
await connections[params.uid]
|
await connections[params.uid]
|
||||||
|
@@ -3,10 +3,14 @@ import mysql from 'mysql2/promise';
|
|||||||
import * as pg from 'pg';
|
import * as pg from 'pg';
|
||||||
import SSH2Promise from 'ssh2-promise';
|
import SSH2Promise from 'ssh2-promise';
|
||||||
|
|
||||||
const queryLogger = (sql: string) => {
|
const queryLogger = ({ sql, cUid }: {sql: string; cUid: string}) => {
|
||||||
// Remove comments, newlines and multiple spaces
|
// Remove comments, newlines and multiple spaces
|
||||||
const escapedSql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '').replace(/\s\s+/g, ' ');
|
const escapedSql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '').replace(/\s\s+/g, ' ');
|
||||||
console.log(escapedSql);
|
if (process.type !== undefined) {
|
||||||
|
const mainWindow = require('electron').webContents.fromId(1);
|
||||||
|
mainWindow.send('query-log', { cUid, sql: escapedSql, date: new Date() });
|
||||||
|
}
|
||||||
|
if (process.env.NODE_ENV === 'development') console.log(escapedSql);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,15 +18,17 @@ const queryLogger = (sql: string) => {
|
|||||||
*/
|
*/
|
||||||
export class AntaresCore {
|
export class AntaresCore {
|
||||||
_client: antares.ClientCode;
|
_client: antares.ClientCode;
|
||||||
|
protected _cUid: string
|
||||||
protected _params: mysql.ConnectionOptions | pg.ClientConfig | { databasePath: string; readonly: boolean};
|
protected _params: mysql.ConnectionOptions | pg.ClientConfig | { databasePath: string; readonly: boolean};
|
||||||
protected _poolSize: number;
|
protected _poolSize: number;
|
||||||
protected _ssh?: SSH2Promise;
|
protected _ssh?: SSH2Promise;
|
||||||
protected _logger: (sql: string) => void;
|
protected _logger: (args: {sql: string; cUid: string}) => void;
|
||||||
protected _queryDefaults: antares.QueryBuilderObject;
|
protected _queryDefaults: antares.QueryBuilderObject;
|
||||||
protected _query: antares.QueryBuilderObject;
|
protected _query: antares.QueryBuilderObject;
|
||||||
|
|
||||||
constructor (args: antares.ClientParams) {
|
constructor (args: antares.ClientParams) {
|
||||||
this._client = args.client;
|
this._client = args.client;
|
||||||
|
this._cUid = args.uid;
|
||||||
this._params = args.params;
|
this._params = args.params;
|
||||||
this._poolSize = args.poolSize || undefined;
|
this._poolSize = args.poolSize || undefined;
|
||||||
this._logger = args.logger || queryLogger;
|
this._logger = args.logger || queryLogger;
|
||||||
|
139
src/main/libs/ShortcutRegister.ts
Normal file
139
src/main/libs/ShortcutRegister.ts
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import { BrowserWindow, globalShortcut, Menu, MenuItem, MenuItemConstructorOptions } from 'electron';
|
||||||
|
import * as Store from 'electron-store';
|
||||||
|
import { ShortcutRecord, shortcuts } from 'common/shortcuts';
|
||||||
|
|
||||||
|
const shortcutsStore = new Store({ name: 'shortcuts' });
|
||||||
|
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||||
|
const defaultShortcuts = shortcuts.filter(s => s.os.includes(process.platform));
|
||||||
|
|
||||||
|
export type ShortcutMode = 'local' | 'global'
|
||||||
|
export type OsMenu = {
|
||||||
|
[key in NodeJS.Platform]?: MenuItemConstructorOptions[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ShortcutRegister {
|
||||||
|
private _shortcuts: ShortcutRecord[];
|
||||||
|
private _mainWindow: BrowserWindow;
|
||||||
|
private _menu: Menu;
|
||||||
|
private _menuTemplate: OsMenu;
|
||||||
|
private _mode: ShortcutMode;
|
||||||
|
private static _instance: ShortcutRegister;
|
||||||
|
|
||||||
|
private constructor (args: { mainWindow: BrowserWindow; menuTemplate?: OsMenu; mode: ShortcutMode }) {
|
||||||
|
this._mainWindow = args.mainWindow;
|
||||||
|
this._menuTemplate = args.menuTemplate || {};
|
||||||
|
this._mode = args.mode;
|
||||||
|
this.shortcuts = shortcutsStore.get('shortcuts', defaultShortcuts) as ShortcutRecord[];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getInstance (args?: { mainWindow?: BrowserWindow; menuTemplate?: OsMenu; mode?: ShortcutMode }) {
|
||||||
|
if (!ShortcutRegister._instance && args.menuTemplate !== undefined && args.mode !== undefined) {
|
||||||
|
ShortcutRegister._instance = new ShortcutRegister({
|
||||||
|
mainWindow: args.mainWindow,
|
||||||
|
menuTemplate: args.menuTemplate,
|
||||||
|
mode: args.mode
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ShortcutRegister._instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
get shortcuts () {
|
||||||
|
return this._shortcuts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private set shortcuts (value: ShortcutRecord[]) {
|
||||||
|
this._shortcuts = value;
|
||||||
|
shortcutsStore.set('shortcuts', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
init () {
|
||||||
|
this._mainWindow.webContents.send('update-shortcuts', this.shortcuts);
|
||||||
|
|
||||||
|
this.buildBaseMenu();
|
||||||
|
|
||||||
|
if (this._mode === 'global')
|
||||||
|
this.setGlobalShortcuts();
|
||||||
|
else if (this._mode === 'local')
|
||||||
|
this.setLocalShortcuts();
|
||||||
|
else
|
||||||
|
throw new Error(`Unknown mode "${this._mode}"`);
|
||||||
|
|
||||||
|
Menu.setApplicationMenu(this._menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildBaseMenu () {
|
||||||
|
if (Object.keys(this._menuTemplate).includes(process.platform))
|
||||||
|
this._menu = Menu.buildFromTemplate(this._menuTemplate[process.platform]);
|
||||||
|
else
|
||||||
|
this._menu = new Menu();
|
||||||
|
}
|
||||||
|
|
||||||
|
private setLocalShortcuts () {
|
||||||
|
for (const shortcut of this.shortcuts) {
|
||||||
|
if (shortcut.os.includes(process.platform)) {
|
||||||
|
for (const key of shortcut.keys) {
|
||||||
|
try {
|
||||||
|
this._menu.append(new MenuItem({
|
||||||
|
label: 'Shortcuts',
|
||||||
|
visible: false,
|
||||||
|
submenu: [{
|
||||||
|
label: String(key),
|
||||||
|
accelerator: key,
|
||||||
|
visible: false,
|
||||||
|
click: () => {
|
||||||
|
this._mainWindow.webContents.send(shortcut.event);
|
||||||
|
if (isDevelopment) console.log('LOCAL EVENT:', shortcut);
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (isDevelopment) console.log(error);
|
||||||
|
this.restoreDefaults();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setGlobalShortcuts () {
|
||||||
|
for (const shortcut of this.shortcuts) {
|
||||||
|
if (shortcut.os.includes(process.platform)) {
|
||||||
|
for (const key of shortcut.keys) {
|
||||||
|
try {
|
||||||
|
globalShortcut.register(key, () => {
|
||||||
|
this._mainWindow.webContents.send(shortcut.event);
|
||||||
|
if (isDevelopment) console.log('GLOBAL EVENT:', shortcut);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (isDevelopment) console.log(error);
|
||||||
|
this.restoreDefaults();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reload () {
|
||||||
|
this.unregister();
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateShortcuts (shortcuts: ShortcutRecord[]) {
|
||||||
|
this.shortcuts = shortcuts;
|
||||||
|
this.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreDefaults () {
|
||||||
|
this.shortcuts = defaultShortcuts;
|
||||||
|
this.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
unregister () {
|
||||||
|
if (this._mode === 'global') globalShortcut.unregisterAll();
|
||||||
|
}
|
||||||
|
}
|
@@ -192,14 +192,14 @@ export class MySQLClient extends AntaresCore {
|
|||||||
|
|
||||||
// ANSI_QUOTES check
|
// ANSI_QUOTES check
|
||||||
const [response] = await connection.query<mysql.RowDataPacket[]>('SHOW GLOBAL VARIABLES LIKE \'%sql_mode%\'');
|
const [response] = await connection.query<mysql.RowDataPacket[]>('SHOW GLOBAL VARIABLES LIKE \'%sql_mode%\'');
|
||||||
const sqlMode = response[0]?.Value?.split(',');
|
const sqlMode: string[] = response[0]?.Value?.split(',');
|
||||||
const hasAnsiQuotes = sqlMode.includes('ANSI_QUOTES');
|
const hasAnsiQuotes = sqlMode.includes('ANSI') || sqlMode.includes('ANSI_QUOTES');
|
||||||
|
|
||||||
if (this._params.readonly)
|
if (this._params.readonly)
|
||||||
await connection.query('SET SESSION TRANSACTION READ ONLY');
|
await connection.query('SET SESSION TRANSACTION READ ONLY');
|
||||||
|
|
||||||
if (hasAnsiQuotes)
|
if (hasAnsiQuotes)
|
||||||
await connection.query(`SET SESSION sql_mode = "${sqlMode.filter((m: string) => m !== 'ANSI_QUOTES').join(',')}"`);
|
await connection.query(`SET SESSION sql_mode = '${sqlMode.filter((m: string) => !['ANSI', 'ANSI_QUOTES'].includes(m)).join(',')}'`);
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
@@ -219,18 +219,18 @@ export class MySQLClient extends AntaresCore {
|
|||||||
|
|
||||||
// ANSI_QUOTES check
|
// ANSI_QUOTES check
|
||||||
const [res] = await connection.query<mysql.RowDataPacket[]>('SHOW GLOBAL VARIABLES LIKE \'%sql_mode%\'');
|
const [res] = await connection.query<mysql.RowDataPacket[]>('SHOW GLOBAL VARIABLES LIKE \'%sql_mode%\'');
|
||||||
const sqlMode = res[0]?.Value?.split(',');
|
const sqlMode: string[] = res[0]?.Value?.split(',');
|
||||||
const hasAnsiQuotes = sqlMode.includes('ANSI_QUOTES');
|
const hasAnsiQuotes = sqlMode.includes('ANSI') || sqlMode.includes('ANSI_QUOTES');
|
||||||
|
|
||||||
if (hasAnsiQuotes)
|
if (hasAnsiQuotes)
|
||||||
await connection.query(`SET SESSION sql_mode = "${sqlMode.filter((m: string) => m !== 'ANSI_QUOTES').join(',')}"`);
|
await connection.query(`SET SESSION sql_mode = '${sqlMode.filter((m: string) => !['ANSI', 'ANSI_QUOTES'].includes(m)).join(',')}'`);
|
||||||
|
|
||||||
connection.on('connection', conn => {
|
connection.on('connection', conn => {
|
||||||
if (this._params.readonly)
|
if (this._params.readonly)
|
||||||
conn.query('SET SESSION TRANSACTION READ ONLY');
|
conn.query('SET SESSION TRANSACTION READ ONLY');
|
||||||
|
|
||||||
if (hasAnsiQuotes)
|
if (hasAnsiQuotes)
|
||||||
conn.query(`SET SESSION sql_mode = "${sqlMode.filter((m: string) => m !== 'ANSI_QUOTES').join(',')}"`);
|
conn.query(`SET SESSION sql_mode = '${sqlMode.filter((m: string) => !['ANSI', 'ANSI_QUOTES'].includes(m)).join(',')}'`);
|
||||||
});
|
});
|
||||||
|
|
||||||
return connection;
|
return connection;
|
||||||
@@ -940,7 +940,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
algorithm: algorithm[0]['Create View'].match(/(?<=CREATE ALGORITHM=).*?(?=\s)/gs)[0],
|
algorithm: algorithm[0]['Create View'].match(/(?<=CREATE ALGORITHM=).*?(?=\s)/gs)[0],
|
||||||
definer: viewInfo[0].DEFINER,
|
definer: viewInfo[0].DEFINER.split('@').map((str: string) => `\`${str}\``).join('@'),
|
||||||
security: viewInfo[0].SECURITY_TYPE,
|
security: viewInfo[0].SECURITY_TYPE,
|
||||||
updateOption: viewInfo[0].CHECK_OPTION === 'NONE' ? '' : viewInfo[0].CHECK_OPTION,
|
updateOption: viewInfo[0].CHECK_OPTION === 'NONE' ? '' : viewInfo[0].CHECK_OPTION,
|
||||||
sql: viewInfo[0].VIEW_DEFINITION,
|
sql: viewInfo[0].VIEW_DEFINITION,
|
||||||
@@ -1397,7 +1397,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getVersion () {
|
async getVersion () {
|
||||||
const sql = 'SHOW VARIABLES LIKE "%vers%"';
|
const sql = 'SHOW VARIABLES LIKE \'%vers%\'';
|
||||||
const { rows } = await this.raw(sql);
|
const { rows } = await this.raw(sql);
|
||||||
|
|
||||||
return rows.reduce((acc, curr) => {
|
return rows.reduce((acc, curr) => {
|
||||||
@@ -1536,7 +1536,7 @@ export class MySQLClient extends AntaresCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async raw<T = antares.QueryResult> (sql: string, args?: antares.QueryParams) {
|
async raw<T = antares.QueryResult> (sql: string, args?: antares.QueryParams) {
|
||||||
if (process.env.NODE_ENV === 'development') this._logger(sql);
|
this._logger({ cUid: this._cUid, sql });
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
nest: false,
|
nest: false,
|
||||||
@@ -1588,7 +1588,8 @@ export class MySQLClient extends AntaresCore {
|
|||||||
let timeStop: Date;
|
let timeStop: Date;
|
||||||
let keysArr: antares.QueryForeign[] = [];
|
let keysArr: antares.QueryForeign[] = [];
|
||||||
|
|
||||||
const { rows, report, fields, keys, duration } = await new Promise((resolve, reject) => {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const { rows, report, fields, keys, duration }: any = await new Promise((resolve, reject) => {
|
||||||
connection.query({ sql: query, nestTables }).then(async ([response, fields]) => {
|
connection.query({ sql: query, nestTables }).then(async ([response, fields]) => {
|
||||||
timeStop = new Date();
|
timeStop = new Date();
|
||||||
const queryResult = response;
|
const queryResult = response;
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import * as antares from 'common/interfaces/antares';
|
import * as antares from 'common/interfaces/antares';
|
||||||
import * as mysql from 'mysql2';
|
import * as mysql from 'mysql2';
|
||||||
import { builtinsTypes } from 'pg-types';
|
|
||||||
import * as pg from 'pg';
|
import * as pg from 'pg';
|
||||||
import * as pgAst from 'pgsql-ast-parser';
|
import * as pgAst from 'pgsql-ast-parser';
|
||||||
import { AntaresCore } from '../AntaresCore';
|
import { AntaresCore } from '../AntaresCore';
|
||||||
@@ -19,6 +18,68 @@ pg.types.setTypeParser(1114, pgToString); // timestamp
|
|||||||
pg.types.setTypeParser(1184, pgToString); // timestamptz
|
pg.types.setTypeParser(1184, pgToString); // timestamptz
|
||||||
pg.types.setTypeParser(1266, pgToString); // timetz
|
pg.types.setTypeParser(1266, pgToString); // timetz
|
||||||
|
|
||||||
|
// from pg-types
|
||||||
|
type builtinsTypes =
|
||||||
|
'BOOL' |
|
||||||
|
'BYTEA' |
|
||||||
|
'CHAR' |
|
||||||
|
'INT8' |
|
||||||
|
'INT2' |
|
||||||
|
'INT4' |
|
||||||
|
'REGPROC' |
|
||||||
|
'TEXT' |
|
||||||
|
'OID' |
|
||||||
|
'TID' |
|
||||||
|
'XID' |
|
||||||
|
'CID' |
|
||||||
|
'JSON' |
|
||||||
|
'XML' |
|
||||||
|
'PG_NODE_TREE' |
|
||||||
|
'SMGR' |
|
||||||
|
'PATH' |
|
||||||
|
'POLYGON' |
|
||||||
|
'CIDR' |
|
||||||
|
'FLOAT4' |
|
||||||
|
'FLOAT8' |
|
||||||
|
'ABSTIME' |
|
||||||
|
'RELTIME' |
|
||||||
|
'TINTERVAL' |
|
||||||
|
'CIRCLE' |
|
||||||
|
'MACADDR8' |
|
||||||
|
'MONEY' |
|
||||||
|
'MACADDR' |
|
||||||
|
'INET' |
|
||||||
|
'ACLITEM' |
|
||||||
|
'BPCHAR' |
|
||||||
|
'VARCHAR' |
|
||||||
|
'DATE' |
|
||||||
|
'TIME' |
|
||||||
|
'TIMESTAMP' |
|
||||||
|
'TIMESTAMPTZ' |
|
||||||
|
'INTERVAL' |
|
||||||
|
'TIMETZ' |
|
||||||
|
'BIT' |
|
||||||
|
'VARBIT' |
|
||||||
|
'NUMERIC' |
|
||||||
|
'REFCURSOR' |
|
||||||
|
'REGPROCEDURE' |
|
||||||
|
'REGOPER' |
|
||||||
|
'REGOPERATOR' |
|
||||||
|
'REGCLASS' |
|
||||||
|
'REGTYPE' |
|
||||||
|
'UUID' |
|
||||||
|
'TXID_SNAPSHOT' |
|
||||||
|
'PG_LSN' |
|
||||||
|
'PG_NDISTINCT' |
|
||||||
|
'PG_DEPENDENCIES' |
|
||||||
|
'TSVECTOR' |
|
||||||
|
'TSQUERY' |
|
||||||
|
'GTSVECTOR' |
|
||||||
|
'REGCONFIG' |
|
||||||
|
'REGDICTIONARY' |
|
||||||
|
'JSONB' |
|
||||||
|
'REGNAMESPACE' |
|
||||||
|
'REGROLE';
|
||||||
export class PostgreSQLClient extends AntaresCore {
|
export class PostgreSQLClient extends AntaresCore {
|
||||||
private _schema?: string;
|
private _schema?: string;
|
||||||
private _runningConnections: Map<string, number>;
|
private _runningConnections: Map<string, number>;
|
||||||
@@ -1314,7 +1375,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async raw<T = antares.QueryResult> (sql: string, args?: antares.QueryParams) {
|
async raw<T = antares.QueryResult> (sql: string, args?: antares.QueryParams) {
|
||||||
if (process.env.NODE_ENV === 'development') this._logger(sql);
|
this._logger({ cUid: this._cUid, sql });
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
nest: false,
|
nest: false,
|
||||||
@@ -1365,7 +1426,8 @@ export class PostgreSQLClient extends AntaresCore {
|
|||||||
let timeStop: Date;
|
let timeStop: Date;
|
||||||
let keysArr: antares.QueryForeign[] = [];
|
let keysArr: antares.QueryForeign[] = [];
|
||||||
|
|
||||||
const { rows, report, fields, keys, duration } = await new Promise((resolve, reject) => {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const { rows, report, fields, keys, duration }: any = await new Promise((resolve, reject) => {
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const res = await connection.query({ rowMode: args.nest ? 'array' : null, text: query });
|
const res = await connection.query({ rowMode: args.nest ? 'array' : null, text: query });
|
||||||
|
@@ -586,7 +586,7 @@ export class SQLiteClient extends AntaresCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async raw<T = antares.QueryResult> (sql: string, args?: antares.QueryParams) {
|
async raw<T = antares.QueryResult> (sql: string, args?: antares.QueryParams) {
|
||||||
if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder
|
this._logger({ cUid: this._cUid, sql });// TODO: replace BLOB content with a placeholder
|
||||||
|
|
||||||
args = {
|
args = {
|
||||||
nest: false,
|
nest: false,
|
||||||
@@ -628,7 +628,8 @@ export class SQLiteClient extends AntaresCore {
|
|||||||
let timeStop;
|
let timeStop;
|
||||||
const keysArr: antares.QueryForeign[] = [];
|
const keysArr: antares.QueryForeign[] = [];
|
||||||
|
|
||||||
const { rows, report, fields, keys, duration } = await new Promise((resolve, reject) => {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const { rows, report, fields, keys, duration }: any = await new Promise((resolve, reject) => {
|
||||||
(async () => {
|
(async () => {
|
||||||
let queryRunResult: sqlite.RunResult;
|
let queryRunResult: sqlite.RunResult;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
@@ -1,12 +1,8 @@
|
|||||||
import * as exporter from 'common/interfaces/exporter';
|
import * as exporter from 'common/interfaces/exporter';
|
||||||
import * as mysql from 'mysql2/promise';
|
import * as mysql from 'mysql2/promise';
|
||||||
import { SqlExporter } from './SqlExporter';
|
import { SqlExporter } from './SqlExporter';
|
||||||
import { BLOB, BIT, DATE, DATETIME, FLOAT, SPATIAL, IS_MULTI_SPATIAL, NUMBER } from 'common/fieldTypes';
|
|
||||||
import hexToBinary, { HexChar } from 'common/libs/hexToBinary';
|
|
||||||
import { getArrayDepth } from 'common/libs/getArrayDepth';
|
|
||||||
import * as moment from 'moment';
|
|
||||||
import { lineString, point, polygon } from '@turf/helpers';
|
|
||||||
import { MySQLClient } from '../../clients/MySQLClient';
|
import { MySQLClient } from '../../clients/MySQLClient';
|
||||||
|
import { valueToSqlString } from 'common/libs/sqlUtils';
|
||||||
|
|
||||||
export default class MysqlExporter extends SqlExporter {
|
export default class MysqlExporter extends SqlExporter {
|
||||||
protected _client: MySQLClient;
|
protected _client: MySQLClient;
|
||||||
@@ -122,54 +118,7 @@ ${footer}
|
|||||||
const column = notGeneratedColumns[i];
|
const column = notGeneratedColumns[i];
|
||||||
const val = row[column.name];
|
const val = row[column.name];
|
||||||
|
|
||||||
if (val === null) sqlInsertString += 'NULL';
|
sqlInsertString += valueToSqlString({ val, client: 'mysql', field: column });
|
||||||
else if (DATE.includes(column.type)) {
|
|
||||||
sqlInsertString += moment(val).isValid()
|
|
||||||
? this.escapeAndQuote(moment(val).format('YYYY-MM-DD'))
|
|
||||||
: val;
|
|
||||||
}
|
|
||||||
else if (DATETIME.includes(column.type)) {
|
|
||||||
let datePrecision = '';
|
|
||||||
for (let i = 0; i < column.datePrecision; i++)
|
|
||||||
datePrecision += i === 0 ? '.S' : 'S';
|
|
||||||
|
|
||||||
sqlInsertString += moment(val).isValid()
|
|
||||||
? this.escapeAndQuote(moment(val).format(`YYYY-MM-DD HH:mm:ss${datePrecision}`))
|
|
||||||
: this.escapeAndQuote(val);
|
|
||||||
}
|
|
||||||
else if (BIT.includes(column.type))
|
|
||||||
sqlInsertString += `b'${hexToBinary(Buffer.from(val).toString('hex') as undefined as HexChar[])}'`;
|
|
||||||
else if (BLOB.includes(column.type))
|
|
||||||
sqlInsertString += `X'${val.toString('hex').toUpperCase()}'`;
|
|
||||||
else if (NUMBER.includes(column.type))
|
|
||||||
sqlInsertString += val;
|
|
||||||
else if (FLOAT.includes(column.type))
|
|
||||||
sqlInsertString += parseFloat(val);
|
|
||||||
else if (SPATIAL.includes(column.type)) {
|
|
||||||
let geoJson;
|
|
||||||
if (IS_MULTI_SPATIAL.includes(column.type)) {
|
|
||||||
const features = [];
|
|
||||||
for (const element of val)
|
|
||||||
features.push(this._getGeoJSON(element));
|
|
||||||
|
|
||||||
geoJson = {
|
|
||||||
type: 'FeatureCollection',
|
|
||||||
features
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
geoJson = this._getGeoJSON(val);
|
|
||||||
|
|
||||||
sqlInsertString += `ST_GeomFromGeoJSON('${JSON.stringify(geoJson)}')`;
|
|
||||||
}
|
|
||||||
else if (val === '') sqlInsertString += '\'\'';
|
|
||||||
else {
|
|
||||||
sqlInsertString += typeof val === 'string'
|
|
||||||
? this.escapeAndQuote(val)
|
|
||||||
: typeof val === 'object'
|
|
||||||
? this.escapeAndQuote(JSON.stringify(val))
|
|
||||||
: val;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parseInt(i) !== notGeneratedColumns.length - 1)
|
if (parseInt(i) !== notGeneratedColumns.length - 1)
|
||||||
sqlInsertString += ', ';
|
sqlInsertString += ', ';
|
||||||
@@ -435,17 +384,4 @@ CREATE TABLE \`${view.Name}\`(
|
|||||||
|
|
||||||
return `'${escapedVal}'`;
|
return `'${escapedVal}'`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
_getGeoJSON (val: any) {
|
|
||||||
if (Array.isArray(val)) {
|
|
||||||
if (getArrayDepth(val) === 1)
|
|
||||||
return lineString(val.reduce((acc, curr) => [...acc, [curr.x, curr.y]], []));
|
|
||||||
else
|
|
||||||
return polygon(val.map(arr => arr.reduce((acc: any, curr: any) => [...acc, [curr.x, curr.y]], [])));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return point([val.x, val.y]);
|
|
||||||
}
|
|
||||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,11 @@
|
|||||||
import * as antares from 'common/interfaces/antares';
|
import * as antares from 'common/interfaces/antares';
|
||||||
import * as exporter from 'common/interfaces/exporter';
|
import * as exporter from 'common/interfaces/exporter';
|
||||||
import { SqlExporter } from './SqlExporter';
|
import { SqlExporter } from './SqlExporter';
|
||||||
import { BLOB, BIT, DATE, DATETIME, FLOAT, NUMBER, TEXT_SEARCH } from 'common/fieldTypes';
|
|
||||||
import hexToBinary, { HexChar } from 'common/libs/hexToBinary';
|
|
||||||
import * as moment from 'moment';
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import * as QueryStream from 'pg-query-stream';
|
import * as QueryStream from 'pg-query-stream';
|
||||||
import { PostgreSQLClient } from '../../clients/PostgreSQLClient';
|
import { PostgreSQLClient } from '../../clients/PostgreSQLClient';
|
||||||
|
import { valueToSqlString } from 'common/libs/sqlUtils';
|
||||||
|
|
||||||
export default class PostgreSQLExporter extends SqlExporter {
|
export default class PostgreSQLExporter extends SqlExporter {
|
||||||
constructor (client: PostgreSQLClient, tables: exporter.TableParams[], options: exporter.ExportOptions) {
|
constructor (client: PostgreSQLClient, tables: exporter.TableParams[], options: exporter.ExportOptions) {
|
||||||
@@ -223,47 +221,7 @@ SET row_security = off;\n\n\n`;
|
|||||||
const column = columns[i];
|
const column = columns[i];
|
||||||
const val = row[column.name];
|
const val = row[column.name];
|
||||||
|
|
||||||
if (val === null) sqlInsertString += 'NULL';
|
sqlInsertString += valueToSqlString({ val, client: 'pg', field: column });
|
||||||
else if (DATE.includes(column.type)) {
|
|
||||||
sqlInsertString += moment(val).isValid()
|
|
||||||
? this.escapeAndQuote(moment(val).format('YYYY-MM-DD'))
|
|
||||||
: val;
|
|
||||||
}
|
|
||||||
else if (DATETIME.includes(column.type)) {
|
|
||||||
let datePrecision = '';
|
|
||||||
for (let i = 0; i < column.datePrecision; i++)
|
|
||||||
datePrecision += i === 0 ? '.S' : 'S';
|
|
||||||
|
|
||||||
sqlInsertString += moment(val).isValid()
|
|
||||||
? this.escapeAndQuote(moment(val).format(`YYYY-MM-DD HH:mm:ss${datePrecision}`))
|
|
||||||
: this.escapeAndQuote(val);
|
|
||||||
}
|
|
||||||
else if ('isArray' in column) {
|
|
||||||
let parsedVal;
|
|
||||||
if (Array.isArray(val))
|
|
||||||
parsedVal = JSON.stringify(val).replaceAll('[', '{').replaceAll(']', '}');
|
|
||||||
else
|
|
||||||
parsedVal = typeof val === 'string' ? val.replaceAll('[', '{').replaceAll(']', '}') : '';
|
|
||||||
sqlInsertString += `'${parsedVal}'`;
|
|
||||||
}
|
|
||||||
else if (TEXT_SEARCH.includes(column.type))
|
|
||||||
sqlInsertString += `'${val.replaceAll('\'', '\'\'')}'`;
|
|
||||||
else if (BIT.includes(column.type))
|
|
||||||
sqlInsertString += `b'${hexToBinary(Buffer.from(val).toString('hex') as undefined as HexChar[])}'`;
|
|
||||||
else if (BLOB.includes(column.type))
|
|
||||||
sqlInsertString += `decode('${val.toString('hex').toUpperCase()}', 'hex')`;
|
|
||||||
else if (NUMBER.includes(column.type))
|
|
||||||
sqlInsertString += val;
|
|
||||||
else if (FLOAT.includes(column.type))
|
|
||||||
sqlInsertString += parseFloat(val);
|
|
||||||
else if (val === '') sqlInsertString += '\'\'';
|
|
||||||
else {
|
|
||||||
sqlInsertString += typeof val === 'string'
|
|
||||||
? this.escapeAndQuote(val)
|
|
||||||
: typeof val === 'object'
|
|
||||||
? this.escapeAndQuote(JSON.stringify(val))
|
|
||||||
: val;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parseInt(i) !== columns.length - 1)
|
if (parseInt(i) !== columns.length - 1)
|
||||||
sqlInsertString += ', ';
|
sqlInsertString += ', ';
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
import { app, BrowserWindow, /* session, */ nativeImage, Menu, ipcMain } from 'electron';
|
import { app, BrowserWindow, nativeImage, ipcMain } from 'electron';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as Store from 'electron-store';
|
import * as Store from 'electron-store';
|
||||||
import * as windowStateKeeper from 'electron-window-state';
|
import * as windowStateKeeper from 'electron-window-state';
|
||||||
import * as remoteMain from '@electron/remote/main';
|
import * as remoteMain from '@electron/remote/main';
|
||||||
|
|
||||||
import ipcHandlers from './ipc-handlers';
|
import ipcHandlers from './ipc-handlers';
|
||||||
|
import { OsMenu, ShortcutRegister } from './libs/ShortcutRegister';
|
||||||
|
|
||||||
Store.initRenderer();
|
Store.initRenderer();
|
||||||
const persistentStore = new Store({ name: 'settings' });
|
const settingsStore = new Store({ name: 'settings' });
|
||||||
|
const appTheme = settingsStore.get('application_theme');
|
||||||
const appTheme = persistentStore.get('application_theme');
|
|
||||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||||
const isMacOS = process.platform === 'darwin';
|
const isMacOS = process.platform === 'darwin';
|
||||||
const isLinux = process.platform === 'linux';
|
const isLinux = process.platform === 'linux';
|
||||||
@@ -85,7 +85,7 @@ else {
|
|||||||
ipcHandlers();
|
ipcHandlers();
|
||||||
|
|
||||||
ipcMain.on('refresh-theme-settings', () => {
|
ipcMain.on('refresh-theme-settings', () => {
|
||||||
const appTheme = persistentStore.get('application_theme');
|
const appTheme = settingsStore.get('application_theme');
|
||||||
if (isWindows && mainWindow) {
|
if (isWindows && mainWindow) {
|
||||||
mainWindow.setTitleBarOverlay({
|
mainWindow.setTitleBarOverlay({
|
||||||
color: appTheme === 'dark' ? '#3f3f3f' : '#fff',
|
color: appTheme === 'dark' ? '#3f3f3f' : '#fff',
|
||||||
@@ -145,10 +145,8 @@ else {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createAppMenu () {
|
function createAppMenu () {
|
||||||
let menu: Electron.Menu = null;
|
const menuTemplate: OsMenu = {
|
||||||
|
darwin: [
|
||||||
if (isMacOS) {
|
|
||||||
menu = Menu.buildFromTemplate([
|
|
||||||
{
|
{
|
||||||
label: app.name,
|
label: app.name,
|
||||||
submenu: [
|
submenu: [
|
||||||
@@ -179,10 +177,11 @@ function createAppMenu () {
|
|||||||
{
|
{
|
||||||
role: 'windowMenu'
|
role: 'windowMenu'
|
||||||
}
|
}
|
||||||
]);
|
]
|
||||||
}
|
};
|
||||||
|
|
||||||
Menu.setApplicationMenu(menu);
|
const shortCutRegister = ShortcutRegister.getInstance({ mainWindow, menuTemplate, mode: 'local' });
|
||||||
|
shortCutRegister.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveWindowState () {
|
function saveWindowState () {
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
:key="connection.uid"
|
:key="connection.uid"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
/>
|
/>
|
||||||
<div class="connection-panel-wrapper">
|
<div class="connection-panel-wrapper p-relative">
|
||||||
<WorkspaceAddConnectionPanel v-if="selectedWorkspace === 'NEW'" />
|
<WorkspaceAddConnectionPanel v-if="selectedWorkspace === 'NEW'" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineAsyncComponent, onBeforeUnmount, onMounted, Ref, ref } from 'vue';
|
import { defineAsyncComponent, onMounted, Ref, ref } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
@@ -67,24 +67,29 @@ const { changeApplicationTheme } = settingsStore;
|
|||||||
|
|
||||||
const isAllConnectionsModal: Ref<boolean> = ref(false);
|
const isAllConnectionsModal: Ref<boolean> = ref(false);
|
||||||
|
|
||||||
const onKey = (e: KeyboardEvent) => {
|
|
||||||
if (e.ctrlKey || e.metaKey) {
|
|
||||||
if (e.code === 'Space') {
|
|
||||||
isAllConnectionsModal.value = true;
|
|
||||||
e.stopPropagation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
changeApplicationTheme(applicationTheme.value);// Forces persistentStore to save on file and mail process
|
changeApplicationTheme(applicationTheme.value);// Forces persistentStore to save on file and mail process
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('keypress', onKey);
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
ipcRenderer.on('open-all-connections', () => {
|
||||||
|
isAllConnectionsModal.value = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('open-scratchpad', () => {
|
||||||
|
isScratchpad.value = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('open-settings', () => {
|
||||||
|
isSettingModal.value = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('create-connection', () => {
|
||||||
|
workspacesStore.selectWorkspace('NEW');
|
||||||
|
});
|
||||||
|
|
||||||
ipcRenderer.send('check-for-updates');
|
ipcRenderer.send('check-for-updates');
|
||||||
checkVersionUpdate();
|
checkVersionUpdate();
|
||||||
|
|
||||||
@@ -126,10 +131,6 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
window.removeEventListener('keydown', onKey);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -162,7 +163,7 @@ onBeforeUnmount(() => {
|
|||||||
.connection-panel-wrapper {
|
.connection-panel-wrapper {
|
||||||
height: calc(100vh - #{$excluding-size});
|
height: calc(100vh - #{$excluding-size});
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding-top: 15vh;
|
padding-top: 10vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
@@ -31,13 +31,13 @@
|
|||||||
class="btn btn-primary mr-2"
|
class="btn btn-primary mr-2"
|
||||||
@click.stop="confirmModal"
|
@click.stop="confirmModal"
|
||||||
>
|
>
|
||||||
{{ confirmText || $t('word.confirm') }}
|
{{ confirmText || t('word.confirm') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-link"
|
class="btn btn-link"
|
||||||
@click="hideModal"
|
@click="hideModal"
|
||||||
>
|
>
|
||||||
{{ cancelText || $t('word.cancel') }}
|
{{ cancelText || t('word.cancel') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,6 +49,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useFocusTrap } from '@/composables/useFocusTrap';
|
import { useFocusTrap } from '@/composables/useFocusTrap';
|
||||||
import { computed, onBeforeUnmount, PropType, useSlots } from 'vue';
|
import { computed, onBeforeUnmount, PropType, useSlots } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
size: {
|
size: {
|
||||||
@@ -65,6 +68,10 @@ const props = defineProps({
|
|||||||
disableAutofocus: {
|
disableAutofocus: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
closeOnConfirm: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const emit = defineEmits(['confirm', 'hide']);
|
const emit = defineEmits(['confirm', 'hide']);
|
||||||
@@ -87,7 +94,7 @@ const modalSizeClass = computed(() => {
|
|||||||
|
|
||||||
const confirmModal = () => {
|
const confirmModal = () => {
|
||||||
emit('confirm');
|
emit('confirm');
|
||||||
hideModal();
|
if (props.closeOnConfirm) hideModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
const hideModal = () => {
|
const hideModal = () => {
|
||||||
|
@@ -431,6 +431,12 @@ export default defineComponent({
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__item-text {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
&__list-wrapper {
|
&__list-wrapper {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@@ -51,14 +51,17 @@ watch(editorTheme, () => {
|
|||||||
|
|
||||||
watch(editorFontSize, () => {
|
watch(editorFontSize, () => {
|
||||||
const sizes = {
|
const sizes = {
|
||||||
small: 12,
|
xsmall: '10px',
|
||||||
medium: 14,
|
small: '12px',
|
||||||
large: 16
|
medium: '14px',
|
||||||
|
large: '16px',
|
||||||
|
xlarge: '18px',
|
||||||
|
xxlarge: '20px'
|
||||||
};
|
};
|
||||||
|
|
||||||
if (editor) {
|
if (editor) {
|
||||||
editor.setOptions({
|
editor.setOptions({
|
||||||
fontSize: sizes[editorFontSize.value as undefined as 'small' | 'medium' | 'large']
|
fontSize: sizes[editorFontSize.value]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
<i class="mdi mdi-folder-open mr-1" />{{ message }}
|
<i class="mdi mdi-folder-open mr-1" />{{ message }}
|
||||||
</span>
|
</span>
|
||||||
<span class="text-ellipsis file-uploader-value">
|
<span class="text-ellipsis file-uploader-value">
|
||||||
{{ lastPart(modelValue) }}
|
{{ lastPart(modelValue, 19) }}
|
||||||
</span>
|
</span>
|
||||||
<i
|
<i
|
||||||
v-if="modelValue"
|
v-if="modelValue"
|
||||||
@@ -24,6 +24,9 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
|
import { useFilters } from '@/composables/useFilters';
|
||||||
|
|
||||||
|
const { lastPart } = useFilters();
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
message: {
|
message: {
|
||||||
@@ -43,15 +46,6 @@ const id = uidGen();
|
|||||||
const clear = () => {
|
const clear = () => {
|
||||||
emit('clear');
|
emit('clear');
|
||||||
};
|
};
|
||||||
|
|
||||||
const lastPart = (string: string) => {
|
|
||||||
if (!string) return '';
|
|
||||||
|
|
||||||
string = string.split(/[/\\]+/).pop();
|
|
||||||
if (string.length >= 19)
|
|
||||||
string = `...${string.slice(-19)}`;
|
|
||||||
return string;
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
v-model="selectedGroup"
|
v-model="selectedGroup"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
:options="[{name: 'manual'}, ...fakerGroups]"
|
:options="[{name: 'manual'}, ...fakerGroups]"
|
||||||
:option-label="(opt: any) => opt.name === 'manual' ? $t('message.manualValue') : $t(`faker.${opt.name}`)"
|
:option-label="(opt: any) => opt.name === 'manual' ? t('message.manualValue') : t(`faker.${opt.name}`)"
|
||||||
option-track-by="name"
|
option-track-by="name"
|
||||||
:disabled="!isChecked"
|
:disabled="!isChecked"
|
||||||
style="flex-grow: 0;"
|
style="flex-grow: 0;"
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
v-if="selectedGroup !== 'manual'"
|
v-if="selectedGroup !== 'manual'"
|
||||||
v-model="selectedMethod"
|
v-model="selectedMethod"
|
||||||
:options="fakerMethods"
|
:options="fakerMethods"
|
||||||
:option-label="(opt: any) => $t(`faker.${opt.name}`)"
|
:option-label="(opt: any) => t(`faker.${opt.name}`)"
|
||||||
option-track-by="name"
|
option-track-by="name"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
:disabled="!isChecked"
|
:disabled="!isChecked"
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
<BaseUploadInput
|
<BaseUploadInput
|
||||||
v-else-if="inputProps().type === 'file'"
|
v-else-if="inputProps().type === 'file'"
|
||||||
:model-value="selectedValue"
|
:model-value="selectedValue"
|
||||||
:message="$t('word.browse')"
|
:message="t('word.browse')"
|
||||||
@clear="clearValue"
|
@clear="clearValue"
|
||||||
@change="filesChange($event)"
|
@change="filesChange($event)"
|
||||||
/>
|
/>
|
||||||
@@ -92,6 +92,9 @@ import BaseUploadInput from '@/components/BaseUploadInput.vue';
|
|||||||
import ForeignKeySelect from '@/components/ForeignKeySelect.vue';
|
import ForeignKeySelect from '@/components/ForeignKeySelect.vue';
|
||||||
import FakerMethods from 'common/FakerMethods';
|
import FakerMethods from 'common/FakerMethods';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, Ref, ref } from 'vue';
|
import { computed, Ref, ref, watch } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import Tables from '@/ipc-api/Tables';
|
import Tables from '@/ipc-api/Tables';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
@@ -21,6 +21,7 @@ import { useWorkspacesStore } from '@/stores/workspaces';
|
|||||||
import { TEXT, LONG_TEXT } from 'common/fieldTypes';
|
import { TEXT, LONG_TEXT } from 'common/fieldTypes';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
import { TableField } from 'common/interfaces/antares';
|
import { TableField } from 'common/interfaces/antares';
|
||||||
|
import { useFilters } from '@/composables/useFilters';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: [String, Number],
|
modelValue: [String, Number],
|
||||||
@@ -35,17 +36,18 @@ const emit = defineEmits(['update:modelValue', 'blur']);
|
|||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
const { cutText } = useFilters();
|
||||||
|
|
||||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
||||||
|
|
||||||
const editField: Ref<HTMLSelectElement> = ref(null);
|
const editField: Ref<HTMLSelectElement> = ref(null);
|
||||||
const foreignList = ref([]);
|
const foreignList = ref([]);
|
||||||
const currentValue = ref(props.modelValue);
|
const currentValue = ref(null);
|
||||||
|
|
||||||
const isValidDefault = computed(() => {
|
const isValidDefault = computed(() => {
|
||||||
if (!foreignList.value.length) return true;
|
if (!foreignList.value.length) return true;
|
||||||
if (props.modelValue === null) return false;
|
if (props.modelValue === null) return false;
|
||||||
return foreignList.value.some(foreign => foreign.foreign_column.toString() === props.modelValue.toString());
|
return foreignList.value.some(foreign => foreign.foreign_column.toString() === props.modelValue?.toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
const foreigns = computed(() => {
|
const foreigns = computed(() => {
|
||||||
@@ -53,7 +55,7 @@ const foreigns = computed(() => {
|
|||||||
if (!isValidDefault.value)
|
if (!isValidDefault.value)
|
||||||
list.push({ value: props.modelValue, label: props.modelValue === null ? 'NULL' : props.modelValue });
|
list.push({ value: props.modelValue, label: props.modelValue === null ? 'NULL' : props.modelValue });
|
||||||
for (const row of foreignList.value)
|
for (const row of foreignList.value)
|
||||||
list.push({ value: row.foreign_column, label: `${row.foreign_column} ${cutText('foreign_description' in row ? ` - ${row.foreign_description}` : '')}` });
|
list.push({ value: row.foreign_column, label: `${row.foreign_column} ${cutText('foreign_description' in row ? ` - ${row.foreign_description}` : '', 15)}` });
|
||||||
return list;
|
return list;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -61,10 +63,9 @@ const onChange = (opt: HTMLSelectElement) => {
|
|||||||
emit('update:modelValue', opt.value);
|
emit('update:modelValue', opt.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const cutText = (val: string) => {
|
watch(() => props.modelValue, () => {
|
||||||
if (typeof val !== 'string') return val;
|
currentValue.value = props.modelValue;
|
||||||
return val.length > 15 ? `${val.substring(0, 15)}...` : val;
|
});
|
||||||
};
|
|
||||||
|
|
||||||
let foreignDesc: string | false;
|
let foreignDesc: string | false;
|
||||||
const params = {
|
const params = {
|
||||||
|
122
src/renderer/components/KeyPressDetector.vue
Normal file
122
src/renderer/components/KeyPressDetector.vue
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
<template>
|
||||||
|
<div class="form-group has-icon-right m-0">
|
||||||
|
<input
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
:value="pressedKeys"
|
||||||
|
:placeholder="t('message.registerAShortcut')"
|
||||||
|
@focus="isFocus = true"
|
||||||
|
@blur="isFocus = false"
|
||||||
|
@keydown.prevent.stop="onKey"
|
||||||
|
>
|
||||||
|
<i class="form-icon mdi mdi-keyboard-outline mdi-24px" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, PropType, Ref, ref, watch } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import Application from '@/ipc-api/Application';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
|
const isMacOS = process.platform === 'darwin';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: String as PropType<string | Electron.Accelerator>
|
||||||
|
});
|
||||||
|
|
||||||
|
const isFocus = ref(false);
|
||||||
|
const keyboardEvent: Ref<KeyboardEvent> = ref(null);
|
||||||
|
|
||||||
|
const pressedKeys = computed(() => {
|
||||||
|
const keys: string[] = [];
|
||||||
|
const singleKeysToIgnore = ['Dead', 'Backspace', 'ArrotLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
|
||||||
|
const specialKeys = ['Control', 'Alt', 'AltGraph', 'Shift', 'Meta', 'CapsLock', 'ContextMenu', 'Escape'];
|
||||||
|
const keysFromCode = ['Space', 'Minus', 'Equal', 'Slash', 'Quote', 'Semicolon', 'Comma', 'Period', 'Backslash', 'BracketLeft', 'BracketRight'];
|
||||||
|
|
||||||
|
if (props.modelValue && !keyboardEvent.value)
|
||||||
|
return props.modelValue;
|
||||||
|
else if (keyboardEvent.value) {
|
||||||
|
if (keyboardEvent.value.altKey)
|
||||||
|
keys.push('Alt');
|
||||||
|
if (keyboardEvent.value.ctrlKey)
|
||||||
|
keys.push('Control');
|
||||||
|
if (keyboardEvent.value.metaKey && isMacOS)
|
||||||
|
keys.push('Command');
|
||||||
|
if (keyboardEvent.value.shiftKey && keys.length)
|
||||||
|
keys.push('Shift');
|
||||||
|
if (keyboardEvent.value.code) {
|
||||||
|
if (keys.length === 0 && (keyboardEvent.value.key.length === 1 || singleKeysToIgnore.includes(keyboardEvent.value.key)))
|
||||||
|
return t('message.invalidShortcutMessage');
|
||||||
|
else if (!specialKeys.includes(keyboardEvent.value.key)) {
|
||||||
|
if (keyboardEvent.value.key === 'Dead') {
|
||||||
|
keys.push(keyboardEvent.value.code
|
||||||
|
.replace('Digit', '')
|
||||||
|
.replace('Key', '')
|
||||||
|
.replace('Quote', '\'')
|
||||||
|
.replace('Backquote', '`'));
|
||||||
|
}
|
||||||
|
else if (keysFromCode.includes(keyboardEvent.value.code) || keyboardEvent.value.code.includes('Digit')) {
|
||||||
|
keys.push(keyboardEvent.value.code
|
||||||
|
.replace('Quote', '\'')
|
||||||
|
.replace('Semicolon', ';')
|
||||||
|
.replace('Slash', '/')
|
||||||
|
.replace('Backslash', '\\')
|
||||||
|
.replace('BracketLeft', '[')
|
||||||
|
.replace('BracketRight', ']')
|
||||||
|
.replace('Comma', ',')
|
||||||
|
.replace('Period', '.')
|
||||||
|
.replace('Minus', '-')
|
||||||
|
.replace('Equal', '=')
|
||||||
|
.replace('Digit', '')
|
||||||
|
.replace('Key', ''));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
keys.push(keyboardEvent.value.key.length === 1
|
||||||
|
? keyboardEvent.value.key.toUpperCase()
|
||||||
|
: keyboardEvent.value.key
|
||||||
|
.replace('Arrow', '')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return t('message.invalidShortcutMessage');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys.join('+');
|
||||||
|
});
|
||||||
|
|
||||||
|
const onKey = (e: KeyboardEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
keyboardEvent.value = e;
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(pressedKeys, (value) => {
|
||||||
|
if (value !== t('message.invalidShortcutMessage'))
|
||||||
|
emit('update:modelValue', pressedKeys.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(isFocus, (val) => {
|
||||||
|
if (val)
|
||||||
|
Application.unregisterShortcuts();
|
||||||
|
else
|
||||||
|
Application.reloadShortcuts();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.has-icon-right {
|
||||||
|
.form-input {
|
||||||
|
padding-right: 1.8rem;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
caret-color: transparent;
|
||||||
|
}
|
||||||
|
.form-icon {
|
||||||
|
right: 0.4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -7,7 +7,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-apps mr-1" />
|
<i class="mdi mdi-24px mdi-apps mr-1" />
|
||||||
<span class="cut-text">{{ $t('message.allConnections') }}</span>
|
<span class="cut-text">{{ t('message.allConnections') }}</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" />
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
key="trick"
|
key="trick"
|
||||||
readonly
|
readonly
|
||||||
class="p-absolute"
|
class="p-absolute"
|
||||||
style="width: 1px; height: 1px; opacity: 0"
|
style="width: 1px; height: 1px; opacity: 0;"
|
||||||
type="text"
|
type="text"
|
||||||
>
|
>
|
||||||
<!-- workaround for useFocusTrap $lastFocusable -->
|
<!-- workaround for useFocusTrap $lastFocusable -->
|
||||||
@@ -270,89 +270,87 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.vscroll {
|
.vscroll {
|
||||||
height: 1000px;
|
height: 1000px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
overflow-anchor: none;
|
overflow-anchor: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.column-resizable {
|
.column-resizable {
|
||||||
|
&:hover,
|
||||||
&:hover,
|
&:active {
|
||||||
&:active {
|
resize: horizontal;
|
||||||
resize: horizontal;
|
overflow: hidden;
|
||||||
overflow: hidden;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-column-title {
|
.table-column-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sort-icon {
|
.sort-icon {
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
margin-left: 0.2rem;
|
margin-left: 0.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
|
||||||
.modal-container {
|
.modal-container {
|
||||||
max-width: 75vw;
|
max-width: 75vw;
|
||||||
margin-top: 10vh;
|
margin-top: 10vh;
|
||||||
|
|
||||||
.modal-body {
|
.modal-body {
|
||||||
height: 80vh;
|
height: 80vh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.connections-search {
|
.connections-search {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
}
|
}
|
||||||
|
|
||||||
.connection-block {
|
.connection-block {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all .2s;
|
transition: all 0.2s;
|
||||||
border-radius: $border-radius;
|
border-radius: $border-radius;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: 0 0 3px .1rem rgba($primary-color, 80%);
|
box-shadow: 0 0 3px 0.1rem rgba($primary-color, 80%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.all-connections-buttons {
|
|
||||||
|
|
||||||
.all-connections-delete,
|
|
||||||
.all-connections-pinned,
|
|
||||||
.all-connections-pin {
|
|
||||||
opacity: .5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.all-connections-buttons {
|
|
||||||
.all-connections-pinned {
|
|
||||||
opacity: .3;
|
|
||||||
transition: opacity .2s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.all-connections-buttons {
|
||||||
.all-connections-delete,
|
.all-connections-delete,
|
||||||
|
.all-connections-pinned,
|
||||||
.all-connections-pin {
|
.all-connections-pin {
|
||||||
opacity: 0;
|
opacity: 0.5;
|
||||||
transition: opacity .2s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.all-connections-buttons {
|
||||||
|
.all-connections-pinned {
|
||||||
|
opacity: 0.3;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.all-connections-delete,
|
||||||
|
.all-connections-pin {
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-key-variant mr-1" /> {{ $t('word.credentials') }}
|
<i class="mdi mdi-24px mdi-key-variant mr-1" /> {{ t('word.credentials') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<form class="form-horizontal">
|
<form class="form-horizontal">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<label class="form-label">{{ $t('word.user') }}</label>
|
<label class="form-label">{{ t('word.user') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<input
|
<input
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<label class="form-label">{{ $t('word.password') }}</label>
|
<label class="form-label">{{ t('word.password') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<input
|
<input
|
||||||
@@ -44,10 +44,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<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>
|
||||||
<button class="btn btn-link" @click.stop="closeModal">
|
<button class="btn btn-link" @click.stop="closeModal">
|
||||||
{{ $t('word.close') }}
|
{{ t('word.close') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -58,6 +58,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Ref, ref } from 'vue';
|
import { Ref, ref } from 'vue';
|
||||||
import { useFocusTrap } from '@/composables/useFocusTrap';
|
import { useFocusTrap } from '@/composables/useFocusTrap';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const { trapRef } = useFocusTrap();
|
const { trapRef } = useFocusTrap();
|
||||||
|
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
:confirm-text="$t('word.run')"
|
:confirm-text="t('word.run')"
|
||||||
:cancel-text="$t('word.cancel')"
|
:cancel-text="t('word.cancel')"
|
||||||
size="400"
|
size="400"
|
||||||
@confirm="runRoutine"
|
@confirm="runRoutine"
|
||||||
@hide="closeModal"
|
@hide="closeModal"
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-play mr-1" />
|
<i class="mdi mdi-24px mdi-play mr-1" />
|
||||||
<span class="cut-text">{{ $t('word.parameters') }}: {{ localRoutine.name }}</span>
|
<span class="cut-text">{{ t('word.parameters') }}: {{ localRoutine.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
@@ -52,6 +52,12 @@ import { computed, PropType, Ref, ref } from 'vue';
|
|||||||
import { NUMBER, FLOAT } from 'common/fieldTypes';
|
import { NUMBER, FLOAT } from 'common/fieldTypes';
|
||||||
import { FunctionInfos, RoutineInfos } from 'common/interfaces/antares';
|
import { FunctionInfos, RoutineInfos } from 'common/interfaces/antares';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||||
|
import { useFilters } from '@/composables/useFilters';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const { wrapNumber } = useFilters();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
localRoutine: Object as PropType<RoutineInfos | FunctionInfos>,
|
localRoutine: Object as PropType<RoutineInfos | FunctionInfos>,
|
||||||
@@ -106,11 +112,6 @@ const onKey = (e: KeyboardEvent) => {
|
|||||||
closeModal();
|
closeModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapNumber = (num: number) => {
|
|
||||||
if (!num) return '';
|
|
||||||
return `(${num})`;
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('keydown', onKey);
|
window.addEventListener('keydown', onKey);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@@ -1,18 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
:confirm-text="$t('word.discard')"
|
:confirm-text="t('word.discard')"
|
||||||
:cancel-text="$t('word.stay')"
|
:cancel-text="t('word.stay')"
|
||||||
@confirm="emit('confirm')"
|
@confirm="emit('confirm')"
|
||||||
@hide="emit('close')"
|
@hide="emit('close')"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-content-save-alert mr-1" /> {{ $t('message.unsavedChanges') }}
|
<i class="mdi mdi-24px mdi-content-save-alert mr-1" /> {{ t('message.unsavedChanges') }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div>
|
<div>
|
||||||
{{ $t('message.discardUnsavedChanges') }}
|
{{ t('message.discardUnsavedChanges') }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
@@ -21,6 +21,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||||
import { onBeforeUnmount } from 'vue';
|
import { onBeforeUnmount } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const emit = defineEmits(['confirm', 'close']);
|
const emit = defineEmits(['confirm', 'close']);
|
||||||
|
|
||||||
|
@@ -7,7 +7,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-database-edit mr-1" />
|
<i class="mdi mdi-24px mdi-database-edit mr-1" />
|
||||||
<span class="cut-text">{{ $t('message.editSchema') }}</span>
|
<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" />
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<form class="form-horizontal">
|
<form class="form-horizontal">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<label class="form-label">{{ $t('word.name') }}</label>
|
<label class="form-label">{{ t('word.name') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<input
|
<input
|
||||||
@@ -26,14 +26,14 @@
|
|||||||
class="form-input"
|
class="form-input"
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
:placeholder="$t('message.schemaName')"
|
:placeholder="t('message.schemaName')"
|
||||||
readonly
|
readonly
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<label class="form-label">{{ $t('word.collation') }}</label>
|
<label class="form-label">{{ t('word.collation') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
option-label="collation"
|
option-label="collation"
|
||||||
option-track-by="collation"
|
option-track-by="collation"
|
||||||
/>
|
/>
|
||||||
<small>{{ $t('message.serverDefault') }}: {{ defaultCollation }}</small>
|
<small>{{ t('message.serverDefault') }}: {{ defaultCollation }}</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -51,10 +51,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<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>
|
||||||
<button class="btn btn-link" @click.stop="closeModal">
|
<button class="btn btn-link" @click.stop="closeModal">
|
||||||
{{ $t('word.close') }}
|
{{ t('word.close') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,6 +70,9 @@ import { useWorkspacesStore } from '@/stores/workspaces';
|
|||||||
import { useFocusTrap } from '@/composables/useFocusTrap';
|
import { useFocusTrap } from '@/composables/useFocusTrap';
|
||||||
import Schema from '@/ipc-api/Schema';
|
import Schema from '@/ipc-api/Schema';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
selectedSchema: String
|
selectedSchema: String
|
||||||
|
@@ -7,7 +7,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-database-arrow-down mr-1" />
|
<i class="mdi mdi-24px mdi-database-arrow-down mr-1" />
|
||||||
<span class="cut-text">{{ $t('message.exportSchema') }}</span>
|
<span class="cut-text">{{ t('message.exportSchema') }}</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" />
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<label class="form-label">{{ $t('message.directoryPath') }}</label>
|
<label class="form-label">{{ t('message.directoryPath') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<fieldset class="input-group">
|
<fieldset class="input-group">
|
||||||
@@ -26,14 +26,14 @@
|
|||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
readonly
|
readonly
|
||||||
:placeholder="$t('message.schemaName')"
|
:placeholder="t('message.schemaName')"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-primary input-group-btn"
|
class="btn btn-primary input-group-btn"
|
||||||
@click.prevent="openPathDialog"
|
@click.prevent="openPathDialog"
|
||||||
>
|
>
|
||||||
{{ $t('word.change') }}
|
{{ t('word.change') }}
|
||||||
</button>
|
</button>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
@@ -51,14 +51,14 @@
|
|||||||
<div class="column col-auto col-ml-auto ">
|
<div class="column col-auto col-ml-auto ">
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm"
|
class="btn btn-dark btn-sm"
|
||||||
:title="$t('word.refresh')"
|
:title="t('word.refresh')"
|
||||||
@click="refresh"
|
@click="refresh"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-database-refresh" />
|
<i class="mdi mdi-database-refresh" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm mx-1"
|
class="btn btn-dark btn-sm mx-1"
|
||||||
:title="$t('message.uncheckAllTables')"
|
:title="t('message.uncheckAllTables')"
|
||||||
:disabled="isRefreshing"
|
:disabled="isRefreshing"
|
||||||
@click="uncheckAllTables"
|
@click="uncheckAllTables"
|
||||||
>
|
>
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm"
|
class="btn btn-dark btn-sm"
|
||||||
:title="$t('message.checkAllTables')"
|
:title="t('message.checkAllTables')"
|
||||||
:disabled="isRefreshing"
|
:disabled="isRefreshing"
|
||||||
@click="checkAllTables"
|
@click="checkAllTables"
|
||||||
>
|
>
|
||||||
@@ -122,22 +122,22 @@
|
|||||||
<div class="tr">
|
<div class="tr">
|
||||||
<div class="th" style="width: 50%;">
|
<div class="th" style="width: 50%;">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
<span>{{ $t('word.table') }}</span>
|
<span>{{ t('word.table') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="th text-center">
|
<div class="th text-center">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
<span>{{ $t('word.structure') }}</span>
|
<span>{{ t('word.structure') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="th text-center">
|
<div class="th text-center">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
<span>{{ $t('word.content') }}</span>
|
<span>{{ t('word.content') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="th text-center">
|
<div class="th text-center">
|
||||||
<div class="table-column-title">
|
<div class="table-column-title">
|
||||||
<span>{{ $t('word.drop') }}</span>
|
<span>{{ t('word.drop') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -183,19 +183,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="column col-4">
|
<div class="column col-4">
|
||||||
<h5 class="h5">
|
<h5 class="h5">
|
||||||
{{ $t('word.options') }}
|
{{ t('word.options') }}
|
||||||
</h5>
|
</h5>
|
||||||
<span class="h6">{{ $t('word.includes') }}:</span>
|
<span class="h6">{{ t('word.includes') }}:</span>
|
||||||
<label
|
<label
|
||||||
v-for="(_, key) in options.includes"
|
v-for="(_, key) in options.includes"
|
||||||
:key="key"
|
:key="key"
|
||||||
class="form-checkbox"
|
class="form-checkbox"
|
||||||
>
|
>
|
||||||
<input v-model="options.includes[key]" type="checkbox"><i class="form-icon" /> {{ $tc(`word.${key}`, 2) }}
|
<input v-model="options.includes[key]" type="checkbox"><i class="form-icon" /> {{ t(`word.${key}`, 2) }}
|
||||||
</label>
|
</label>
|
||||||
<div v-if="clientCustoms.exportByChunks">
|
<div v-if="clientCustoms.exportByChunks">
|
||||||
<div class="h6 mt-4 mb-2">
|
<div class="h6 mt-4 mb-2">
|
||||||
{{ $t('message.newInserStmtEvery') }}:
|
{{ t('message.newInserStmtEvery') }}:
|
||||||
</div>
|
</div>
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column col-6">
|
<div class="column col-6">
|
||||||
@@ -209,21 +209,21 @@
|
|||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="options.sqlInsertDivider"
|
v-model="options.sqlInsertDivider"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
:options="[{value: 'bytes', label: 'KiB'}, {value: 'rows', label: $tc('word.row', 2)}]"
|
:options="[{value: 'bytes', label: 'KiB'}, {value: 'rows', label: t('word.row', 2)}]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="h6 mb-2 mt-4">
|
<div class="h6 mb-2 mt-4">
|
||||||
{{ $t('message.ourputFormat') }}:
|
{{ t('message.ourputFormat') }}:
|
||||||
</div>
|
</div>
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column h5 mb-4">
|
<div class="column h5 mb-4">
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="options.outputFormat"
|
v-model="options.outputFormat"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
:options="[{value: 'sql', label: $t('message.singleFile', {ext: '.sql'})}, {value: 'sql.zip', label: $t('message.zipCompressedFile', {ext: '.sql'})}]"
|
:options="[{value: 'sql', label: t('message.singleFile', {ext: '.sql'})}, {value: 'sql.zip', label: t('message.zipCompressedFile', {ext: '.sql'})}]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -245,7 +245,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="column col-auto px-0">
|
<div class="column col-auto px-0">
|
||||||
<button class="btn btn-link" @click.stop="closeModal">
|
<button class="btn btn-link" @click.stop="closeModal">
|
||||||
{{ $t('word.close') }}
|
{{ t('word.close') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-primary mr-2"
|
class="btn btn-primary mr-2"
|
||||||
@@ -254,7 +254,7 @@
|
|||||||
autofocus
|
autofocus
|
||||||
@click.prevent="startExport"
|
@click.prevent="startExport"
|
||||||
>
|
>
|
||||||
{{ $t('word.export') }}
|
{{ t('word.export') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -7,7 +7,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-playlist-plus mr-1" />
|
<i class="mdi mdi-24px mdi-playlist-plus mr-1" />
|
||||||
<span class="cut-text">{{ $tc('message.insertRow', 2) }}</span>
|
<span class="cut-text">{{ t('message.insertRow', 2) }}</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" />
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
<span class="input-group-addon field-type" :class="typeClass(field.type)">
|
<span class="input-group-addon field-type" :class="typeClass(field.type)">
|
||||||
{{ field.type }} {{ wrapNumber(fieldLength(field)) }}
|
{{ field.type }} {{ wrapNumber(fieldLength(field)) }}
|
||||||
</span>
|
</span>
|
||||||
<label class="form-checkbox ml-3" :title="$t('word.insert')">
|
<label class="form-checkbox ml-3" :title="t('word.insert')">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
:checked="!fieldsToExclude.includes(field.name)"
|
:checked="!fieldsToExclude.includes(field.name)"
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-footer 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
|
||||||
v-model="nInserts"
|
v-model="nInserts"
|
||||||
type="number"
|
type="number"
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
<div
|
<div
|
||||||
v-if="hasFakes"
|
v-if="hasFakes"
|
||||||
class="tooltip tooltip-right ml-2"
|
class="tooltip tooltip-right ml-2"
|
||||||
:data-tooltip="$t('message.fakeDataLanguage')"
|
:data-tooltip="t('message.fakeDataLanguage')"
|
||||||
>
|
>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="fakerLocale"
|
v-model="fakerLocale"
|
||||||
@@ -85,10 +85,10 @@
|
|||||||
:class="{'loading': isInserting}"
|
:class="{'loading': isInserting}"
|
||||||
@click.stop="insertRows"
|
@click.stop="insertRows"
|
||||||
>
|
>
|
||||||
{{ $t('word.insert') }}
|
{{ t('word.insert') }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-link" @click.stop="closeModal">
|
<button class="btn btn-link" @click.stop="closeModal">
|
||||||
{{ $t('word.close') }}
|
{{ t('word.close') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -109,10 +109,20 @@ import { useFocusTrap } from '@/composables/useFocusTrap';
|
|||||||
import Tables from '@/ipc-api/Tables';
|
import Tables from '@/ipc-api/Tables';
|
||||||
import FakerSelect from '@/components/FakerSelect.vue';
|
import FakerSelect from '@/components/FakerSelect.vue';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { useFilters } from '@/composables/useFilters';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const { wrapNumber } = useFilters();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
tabUid: [String, Number],
|
tabUid: [String, Number],
|
||||||
|
schema: String,
|
||||||
|
table: String,
|
||||||
fields: Array as Prop<TableField[]>,
|
fields: Array as Prop<TableField[]>,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
rowToDuplicate: Object as Prop<any>,
|
||||||
keyUsage: Array as Prop<TableForeign[]>
|
keyUsage: Array as Prop<TableForeign[]>
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -123,8 +133,6 @@ const workspacesStore = useWorkspacesStore();
|
|||||||
|
|
||||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
||||||
|
|
||||||
const { getWorkspace } = workspacesStore;
|
|
||||||
|
|
||||||
const { trapRef } = useFocusTrap({ disableAutofocus: true });
|
const { trapRef } = useFocusTrap({ disableAutofocus: true });
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@@ -134,7 +142,6 @@ const nInserts = ref(1);
|
|||||||
const isInserting = ref(false);
|
const isInserting = ref(false);
|
||||||
const fakerLocale = ref('en');
|
const fakerLocale = ref('en');
|
||||||
|
|
||||||
const workspace = computed(() => getWorkspace(selectedWorkspace.value));
|
|
||||||
const foreignKeys = computed(() => props.keyUsage.map(key => key.field));
|
const foreignKeys = computed(() => props.keyUsage.map(key => key.field));
|
||||||
const hasFakes = computed(() => Object.keys(localRow.value).some(field => 'group' in localRow.value[field] && localRow.value[field].group !== 'manual'));
|
const hasFakes = computed(() => Object.keys(localRow.value).some(field => 'group' in localRow.value[field] && localRow.value[field].group !== 'manual'));
|
||||||
|
|
||||||
@@ -220,8 +227,8 @@ const insertRows = async () => {
|
|||||||
try {
|
try {
|
||||||
const { status, response } = await Tables.insertTableFakeRows({
|
const { status, response } = await Tables.insertTableFakeRows({
|
||||||
uid: selectedWorkspace.value,
|
uid: selectedWorkspace.value,
|
||||||
schema: workspace.value.breadcrumbs.schema,
|
schema: props.schema,
|
||||||
table: workspace.value.breadcrumbs.table,
|
table: props.table,
|
||||||
row: rowToInsert,
|
row: rowToInsert,
|
||||||
repeat: nInserts.value,
|
repeat: nInserts.value,
|
||||||
fields: fieldTypes,
|
fields: fieldTypes,
|
||||||
@@ -266,11 +273,6 @@ const onKey = (e: KeyboardEvent) => {
|
|||||||
closeModal();
|
closeModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
const wrapNumber = (num: number) => {
|
|
||||||
if (!num) return '';
|
|
||||||
return `(${num})`;
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('keydown', onKey);
|
window.addEventListener('keydown', onKey);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -284,44 +286,57 @@ onMounted(() => {
|
|||||||
|
|
||||||
const rowObj: {[key: string]: unknown} = {};
|
const rowObj: {[key: string]: unknown} = {};
|
||||||
|
|
||||||
for (const field of props.fields) {
|
if (!props.rowToDuplicate) {
|
||||||
let fieldDefault;
|
// Set default values
|
||||||
|
for (const field of props.fields) {
|
||||||
|
let fieldDefault;
|
||||||
|
|
||||||
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 || Number.isNaN(+field.default.replaceAll('\'', '')) ? null : +field.default.replaceAll('\'', '');
|
fieldDefault = !field.default || Number.isNaN(+field.default.replaceAll('\'', '')) ? null : +field.default.replaceAll('\'', '');
|
||||||
else if ([...TEXT, ...LONG_TEXT].includes(field.type)) {
|
else if ([...TEXT, ...LONG_TEXT].includes(field.type)) {
|
||||||
fieldDefault = field.default
|
fieldDefault = field.default
|
||||||
? field.default.includes('\'')
|
? field.default.includes('\'')
|
||||||
? field.default.split('\'')[1]
|
? field.default.split('\'')[1]
|
||||||
: field.default
|
: field.default
|
||||||
: '';
|
: '';
|
||||||
}
|
|
||||||
else if ([...TIME, ...DATE].includes(field.type))
|
|
||||||
fieldDefault = field.default;
|
|
||||||
else if (BIT.includes(field.type))
|
|
||||||
fieldDefault = field.default?.replaceAll('\'', '').replaceAll('b', '');
|
|
||||||
else if (DATETIME.includes(field.type)) {
|
|
||||||
if (field.default && ['current_timestamp', 'now()'].some(term => field.default.toLowerCase().includes(term))) {
|
|
||||||
let datePrecision = '';
|
|
||||||
for (let i = 0; i < field.datePrecision; i++)
|
|
||||||
datePrecision += i === 0 ? '.S' : 'S';
|
|
||||||
fieldDefault = moment().format(`YYYY-MM-DD HH:mm:ss${datePrecision}`);
|
|
||||||
}
|
}
|
||||||
|
else if ([...TIME, ...DATE].includes(field.type))
|
||||||
|
fieldDefault = field.default;
|
||||||
|
else if (BIT.includes(field.type))
|
||||||
|
fieldDefault = field.default?.replaceAll('\'', '').replaceAll('b', '');
|
||||||
|
else if (DATETIME.includes(field.type)) {
|
||||||
|
if (field.default && ['current_timestamp', 'now()'].some(term => field.default.toLowerCase().includes(term))) {
|
||||||
|
let datePrecision = '';
|
||||||
|
for (let i = 0; i < field.datePrecision; i++)
|
||||||
|
datePrecision += i === 0 ? '.S' : 'S';
|
||||||
|
fieldDefault = moment().format(`YYYY-MM-DD HH:mm:ss${datePrecision}`);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fieldDefault = field.default;
|
||||||
|
}
|
||||||
|
else if (field.enumValues)
|
||||||
|
fieldDefault = field.enumValues.replaceAll('\'', '').split(',');
|
||||||
else
|
else
|
||||||
fieldDefault = field.default;
|
fieldDefault = field.default;
|
||||||
}
|
}
|
||||||
else if (field.enumValues)
|
|
||||||
fieldDefault = field.enumValues.replaceAll('\'', '').split(',');
|
rowObj[field.name] = { value: fieldDefault };
|
||||||
else
|
|
||||||
fieldDefault = field.default;
|
if (field.autoIncrement || !!field.onUpdate)// Disable by default auto increment or "on update" fields
|
||||||
|
fieldsToExclude.value = [...fieldsToExclude.value, field.name];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Set values to duplicate
|
||||||
|
for (const field of props.fields) {
|
||||||
|
if (typeof props.rowToDuplicate[field.name] !== 'object')
|
||||||
|
rowObj[field.name] = { value: props.rowToDuplicate[field.name] };
|
||||||
|
|
||||||
rowObj[field.name] = { value: fieldDefault };
|
if (field.autoIncrement || !!field.onUpdate)// Disable by default auto increment or "on update" fields
|
||||||
|
fieldsToExclude.value = [...fieldsToExclude.value, field.name];
|
||||||
if (field.autoIncrement || !!field.onUpdate)// Disable by default auto increment or "on update" fields
|
}
|
||||||
fieldsToExclude.value = [...fieldsToExclude.value, field.name];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
localRow.value = { ...rowObj };
|
localRow.value = { ...rowObj };
|
||||||
|
@@ -100,14 +100,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Component, computed, ComputedRef, onBeforeUnmount, onMounted, onUpdated, Prop, Ref, ref, watch } from 'vue';
|
import { Component, computed, ComputedRef, onBeforeUnmount, onMounted, onUpdated, Prop, Ref, ref, watch } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import * as moment from 'moment';
|
|
||||||
import { ConnectionParams } from 'common/interfaces/antares';
|
import { ConnectionParams } from 'common/interfaces/antares';
|
||||||
import { HistoryRecord, useHistoryStore } from '@/stores/history';
|
import { HistoryRecord, useHistoryStore } from '@/stores/history';
|
||||||
import { useConnectionsStore } from '@/stores/connections';
|
import { useConnectionsStore } from '@/stores/connections';
|
||||||
import { useFocusTrap } from '@/composables/useFocusTrap';
|
import { useFocusTrap } from '@/composables/useFocusTrap';
|
||||||
|
import { useFilters } from '@/composables/useFilters';
|
||||||
import BaseVirtualScroll from '@/components/BaseVirtualScroll.vue';
|
import BaseVirtualScroll from '@/components/BaseVirtualScroll.vue';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const { formatDate } = useFilters();
|
||||||
|
|
||||||
const { getHistoryByWorkspace, deleteQueryFromHistory } = useHistoryStore();
|
const { getHistoryByWorkspace, deleteQueryFromHistory } = useHistoryStore();
|
||||||
const { getConnectionName } = useConnectionsStore();
|
const { getConnectionName } = useConnectionsStore();
|
||||||
@@ -164,7 +165,6 @@ const resizeResults = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatDate = (date: Date) => moment(date).isValid() ? moment(date).format('HH:mm:ss - YYYY/MM/DD') : date;
|
|
||||||
const refreshScroller = () => resizeResults();
|
const refreshScroller = () => resizeResults();
|
||||||
const closeModal = () => emit('close');
|
const closeModal = () => emit('close');
|
||||||
|
|
||||||
|
@@ -7,7 +7,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-database-arrow-up mr-1" />
|
<i class="mdi mdi-24px mdi-database-arrow-up mr-1" />
|
||||||
<span class="cut-text">{{ $t('message.importSchema') }}</span>
|
<span class="cut-text">{{ t('message.importSchema') }}</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" />
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<div class="modal-body pb-0">
|
<div class="modal-body pb-0">
|
||||||
{{ sqlFile }}
|
{{ sqlFile }}
|
||||||
<div v-if="queryErrors.length > 0" class="mt-2">
|
<div v-if="queryErrors.length > 0" class="mt-2">
|
||||||
<label>{{ $tc('message.importQueryErrors', queryErrors.length) }}</label>
|
<label>{{ t('message.importQueryErrors', queryErrors.length) }}</label>
|
||||||
<textarea
|
<textarea
|
||||||
v-model="formattedQueryErrors"
|
v-model="formattedQueryErrors"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<div class="column col modal-progress-wrapper text-left">
|
<div class="column col modal-progress-wrapper text-left">
|
||||||
<div class="import-progress">
|
<div class="import-progress">
|
||||||
<span class="progress-status">
|
<span class="progress-status">
|
||||||
{{ progressPercentage }}% - {{ progressStatus }} - {{ $tc('message.executedQueries', queryCount) }}
|
{{ progressPercentage }}% - {{ progressStatus }} - {{ t('message.executedQueries', queryCount) }}
|
||||||
</span>
|
</span>
|
||||||
<progress
|
<progress
|
||||||
class="progress d-block"
|
class="progress d-block"
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="column col-auto px-0">
|
<div class="column col-auto px-0">
|
||||||
<button class="btn btn-link" @click.stop="closeModal">
|
<button class="btn btn-link" @click.stop="closeModal">
|
||||||
{{ completed ? $t('word.close') : $t('word.cancel') }}
|
{{ completed ? t('word.close') : t('word.cancel') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -57,8 +57,8 @@ import { storeToRefs } from 'pinia';
|
|||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import Schema from '@/ipc-api/Schema';
|
import Schema from '@/ipc-api/Schema';
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
import { ImportState } from 'common/interfaces/importer';
|
import { ImportState } from 'common/interfaces/importer';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
@@ -7,7 +7,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-database-plus mr-1" />
|
<i class="mdi mdi-24px mdi-database-plus mr-1" />
|
||||||
<span class="cut-text">{{ $t('message.createNewSchema') }}</span>
|
<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" />
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<form class="form-horizontal" @submit.prevent="createSchema">
|
<form class="form-horizontal" @submit.prevent="createSchema">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<label class="form-label">{{ $t('word.name') }}</label>
|
<label class="form-label">{{ t('word.name') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<input
|
<input
|
||||||
@@ -26,13 +26,13 @@
|
|||||||
class="form-input"
|
class="form-input"
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
:placeholder="$t('message.schemaName')"
|
:placeholder="t('message.schemaName')"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="customizations.collations" class="form-group">
|
<div v-if="customizations.collations" class="form-group">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<label class="form-label">{{ $t('word.collation') }}</label>
|
<label class="form-label">{{ t('word.collation') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
option-label="collation"
|
option-label="collation"
|
||||||
option-track-by="collation"
|
option-track-by="collation"
|
||||||
/>
|
/>
|
||||||
<small>{{ $t('message.serverDefault') }}: {{ defaultCollation }}</small>
|
<small>{{ t('message.serverDefault') }}: {{ defaultCollation }}</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
@@ -54,10 +54,10 @@
|
|||||||
:class="{'loading': isLoading}"
|
:class="{'loading': isLoading}"
|
||||||
@click.stop="createSchema"
|
@click.stop="createSchema"
|
||||||
>
|
>
|
||||||
{{ $t('word.add') }}
|
{{ t('word.add') }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-link" @click.stop="closeModal">
|
<button class="btn btn-link" @click.stop="closeModal">
|
||||||
{{ $t('word.close') }}
|
{{ t('word.close') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -73,6 +73,9 @@ import { useWorkspacesStore } from '@/stores/workspaces';
|
|||||||
import { useFocusTrap } from '@/composables/useFocusTrap';
|
import { useFocusTrap } from '@/composables/useFocusTrap';
|
||||||
import Schema from '@/ipc-api/Schema';
|
import Schema from '@/ipc-api/Schema';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
@@ -17,7 +17,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-memory mr-1" />
|
<i class="mdi mdi-24px mdi-memory mr-1" />
|
||||||
<span class="cut-text">{{ $t('message.processesList') }}: {{ connectionName }}</span>
|
<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" />
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm mr-0 pr-1 d-flex"
|
class="btn btn-dark btn-sm mr-0 pr-1 d-flex"
|
||||||
:class="{'loading':isQuering}"
|
:class="{'loading':isQuering}"
|
||||||
:title="`${$t('word.refresh')} (F5)`"
|
:title="`${t('word.refresh')}`"
|
||||||
@click="getProcessesList"
|
@click="getProcessesList"
|
||||||
>
|
>
|
||||||
<i v-if="!+autorefreshTimer" class="mdi mdi-24px mdi-refresh mr-1" />
|
<i v-if="!+autorefreshTimer" class="mdi mdi-24px mdi-refresh mr-1" />
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
<i class="mdi mdi-24px mdi-menu-down" />
|
<i class="mdi mdi-24px mdi-menu-down" />
|
||||||
</div>
|
</div>
|
||||||
<div class="menu px-3">
|
<div class="menu px-3">
|
||||||
<span>{{ $t('word.autoRefresh') }}: <b>{{ +autorefreshTimer ? `${autorefreshTimer}s` : 'OFF' }}</b></span>
|
<span>{{ t('word.autoRefresh') }}: <b>{{ +autorefreshTimer ? `${autorefreshTimer}s` : 'OFF' }}</b></span>
|
||||||
<input
|
<input
|
||||||
v-model="autorefreshTimer"
|
v-model="autorefreshTimer"
|
||||||
class="slider no-border"
|
class="slider no-border"
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-file-export mr-1" />
|
<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-menu-down" />
|
<i class="mdi mdi-24px mdi-menu-down" />
|
||||||
</button>
|
</button>
|
||||||
<ul class="menu text-left">
|
<ul class="menu text-left">
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-info">
|
<div class="workspace-query-info">
|
||||||
<div v-if="sortedResults.length">
|
<div v-if="sortedResults.length">
|
||||||
{{ $t('word.processes') }}: <b>{{ sortedResults.length.toLocaleString() }}</b>
|
{{ t('word.processes') }}: <b>{{ sortedResults.length.toLocaleString() }}</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -135,8 +135,9 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Component, computed, onBeforeUnmount, onMounted, onUpdated, Prop, Ref, ref } from 'vue';
|
import { Component, computed, onBeforeUnmount, onMounted, onUpdated, Prop, Ref, ref } from 'vue';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
import { ConnectionParams } from 'common/interfaces/antares';
|
import { ConnectionParams } from 'common/interfaces/antares';
|
||||||
import { arrayToFile } from '../libs/arrayToFile';
|
import { exportRows } from '../libs/exportRows';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useFocusTrap } from '@/composables/useFocusTrap';
|
import { useFocusTrap } from '@/composables/useFocusTrap';
|
||||||
import Schema from '@/ipc-api/Schema';
|
import Schema from '@/ipc-api/Schema';
|
||||||
@@ -144,6 +145,9 @@ import { useConnectionsStore } from '@/stores/connections';
|
|||||||
import BaseVirtualScroll from '@/components/BaseVirtualScroll.vue';
|
import BaseVirtualScroll from '@/components/BaseVirtualScroll.vue';
|
||||||
import ModalProcessesListRow from '@/components/ModalProcessesListRow.vue';
|
import ModalProcessesListRow from '@/components/ModalProcessesListRow.vue';
|
||||||
import ModalProcessesListContext from '@/components/ModalProcessesListContext.vue';
|
import ModalProcessesListContext from '@/components/ModalProcessesListContext.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const { getConnectionName } = useConnectionsStore();
|
const { getConnectionName } = useConnectionsStore();
|
||||||
@@ -312,10 +316,10 @@ const closeModal = () => emit('close');
|
|||||||
|
|
||||||
const downloadTable = (format: 'csv' | 'json') => {
|
const downloadTable = (format: 'csv' | 'json') => {
|
||||||
if (!sortedResults.value) return;
|
if (!sortedResults.value) return;
|
||||||
arrayToFile({
|
exportRows({
|
||||||
type: format,
|
type: format,
|
||||||
content: sortedResults.value,
|
content: sortedResults.value,
|
||||||
filename: 'processes'
|
table: 'processes'
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -323,10 +327,10 @@ const onKey = (e:KeyboardEvent) => {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (e.key === 'Escape')
|
if (e.key === 'Escape')
|
||||||
closeModal();
|
closeModal();
|
||||||
if (e.key === 'F5')
|
|
||||||
getProcessesList();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ipcRenderer.on('run-or-reload', getProcessesList);
|
||||||
|
|
||||||
window.addEventListener('keydown', onKey, { capture: true });
|
window.addEventListener('keydown', onKey, { capture: true });
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -345,6 +349,8 @@ onBeforeUnmount(() => {
|
|||||||
window.removeEventListener('keydown', onKey, { capture: true });
|
window.removeEventListener('keydown', onKey, { capture: true });
|
||||||
window.removeEventListener('resize', resizeResults);
|
window.removeEventListener('resize', resizeResults);
|
||||||
clearInterval(refreshInterval.value);
|
clearInterval(refreshInterval.value);
|
||||||
|
|
||||||
|
ipcRenderer.removeListener('run-or-reload', getProcessesList);
|
||||||
});
|
});
|
||||||
|
|
||||||
defineExpose({ refreshScroller });
|
defineExpose({ refreshScroller });
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
@close-context="closeContext"
|
@close-context="closeContext"
|
||||||
>
|
>
|
||||||
<div v-if="props.selectedRow" class="context-element">
|
<div v-if="props.selectedRow" class="context-element">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-content-copy text-light pr-1" /> {{ $t('word.copy') }}</span>
|
<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" />
|
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
|
||||||
<div class="context-submenu">
|
<div class="context-submenu">
|
||||||
<div
|
<div
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
@click="copyCell"
|
@click="copyCell"
|
||||||
>
|
>
|
||||||
<span class="d-flex">
|
<span class="d-flex">
|
||||||
<i class="mdi mdi-18px mdi-numeric-0 mdi-rotate-90 text-light pr-1" /> {{ $tc('word.cell', 1) }}
|
<i class="mdi mdi-18px mdi-numeric-0 mdi-rotate-90 text-light pr-1" /> {{ t('word.cell', 1) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
@click="copyRow"
|
@click="copyRow"
|
||||||
>
|
>
|
||||||
<span class="d-flex">
|
<span class="d-flex">
|
||||||
<i class="mdi mdi-18px mdi-table-row text-light pr-1" /> {{ $tc('word.row', 1) }}
|
<i class="mdi mdi-18px mdi-table-row text-light pr-1" /> {{ t('word.row', 1) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
@click="killProcess"
|
@click="killProcess"
|
||||||
>
|
>
|
||||||
<span class="d-flex">
|
<span class="d-flex">
|
||||||
<i class="mdi mdi-18px mdi-close-circle-outline text-light pr-1" /> {{ $t('message.killProcess') }}
|
<i class="mdi mdi-18px mdi-close-circle-outline text-light pr-1" /> {{ t('message.killProcess') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</BaseContextMenu>
|
</BaseContextMenu>
|
||||||
@@ -41,6 +41,9 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import BaseContextMenu from '@/components/BaseContextMenu.vue';
|
import BaseContextMenu from '@/components/BaseContextMenu.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
contextEvent: MouseEvent,
|
contextEvent: MouseEvent,
|
||||||
|
@@ -12,19 +12,19 @@
|
|||||||
class="cell-content"
|
class="cell-content"
|
||||||
:class="`${isNull(col)} type-${typeof col === 'number' ? 'int' : 'varchar'}`"
|
:class="`${isNull(col)} type-${typeof col === 'number' ? 'int' : 'varchar'}`"
|
||||||
@dblclick="dblClick(cKey)"
|
@dblclick="dblClick(cKey)"
|
||||||
>{{ cutText(col) }}</span>
|
>{{ cutText(col, 250) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
v-if="isInfoModal"
|
v-if="isInfoModal"
|
||||||
:confirm-text="$t('word.update')"
|
:confirm-text="t('word.update')"
|
||||||
:cancel-text="$t('word.close')"
|
:cancel-text="t('word.close')"
|
||||||
size="medium"
|
size="medium"
|
||||||
:hide-footer="true"
|
:hide-footer="true"
|
||||||
@hide="hideInfoModal"
|
@hide="hideInfoModal"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-information-outline mr-1" /> {{ $t('message.processInfo') }}
|
<i class="mdi mdi-24px mdi-information-outline mr-1" /> {{ t('message.processInfo') }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
@@ -48,6 +48,12 @@
|
|||||||
import { Ref, ref } from 'vue';
|
import { Ref, ref } from 'vue';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||||
import TextEditor from '@/components/BaseTextEditor.vue';
|
import TextEditor from '@/components/BaseTextEditor.vue';
|
||||||
|
import { useFilters } from '@/composables/useFilters';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const { cutText } = useFilters();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
row: Object
|
row: Object
|
||||||
@@ -79,11 +85,6 @@ const dblClick = (col: string) => {
|
|||||||
isInfoModal.value = true;
|
isInfoModal.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const cutText = (val: string | number) => {
|
|
||||||
if (typeof val !== 'string') return val;
|
|
||||||
return val.length > 250 ? `${val.substring(0, 250)}[...]` : val;
|
|
||||||
};
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@@ -30,6 +30,13 @@
|
|||||||
>
|
>
|
||||||
<a class="tab-link">{{ t('word.themes') }}</a>
|
<a class="tab-link">{{ t('word.themes') }}</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li
|
||||||
|
class="tab-item c-hand"
|
||||||
|
:class="{'active': selectedTab === 'shortcuts'}"
|
||||||
|
@click="selectTab('shortcuts')"
|
||||||
|
>
|
||||||
|
<a class="tab-link">{{ t('word.shortcuts') }}</a>
|
||||||
|
</li>
|
||||||
<li
|
<li
|
||||||
v-if="updateStatus !== 'disabled'"
|
v-if="updateStatus !== 'disabled'"
|
||||||
class="tab-item c-hand"
|
class="tab-item c-hand"
|
||||||
@@ -232,7 +239,7 @@
|
|||||||
<div class="column col-12 h6 text-uppercase mb-2 mt-4">
|
<div class="column col-12 h6 text-uppercase mb-2 mt-4">
|
||||||
{{ t('message.editorTheme') }}
|
{{ t('message.editorTheme') }}
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-6 h5 mb-4">
|
<div class="column col-5 h5 mb-4">
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localEditorTheme"
|
v-model="localEditorTheme"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
@@ -244,28 +251,49 @@
|
|||||||
@change="changeEditorTheme(localEditorTheme)"
|
@change="changeEditorTheme(localEditorTheme)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-6 mb-4">
|
<div class="column col-7 mb-4">
|
||||||
<div class="btn-group btn-group-block">
|
<div class="btn-group btn-group-block">
|
||||||
|
<button
|
||||||
|
class="btn btn-dark cut-text"
|
||||||
|
:class="{'active': editorFontSize === 'xsmall'}"
|
||||||
|
@click="changeEditorFontSize('xsmall')"
|
||||||
|
>
|
||||||
|
10px
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark cut-text"
|
class="btn btn-dark cut-text"
|
||||||
:class="{'active': editorFontSize === 'small'}"
|
:class="{'active': editorFontSize === 'small'}"
|
||||||
@click="changeEditorFontSize('small')"
|
@click="changeEditorFontSize('small')"
|
||||||
>
|
>
|
||||||
{{ t('word.small') }}
|
12px
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark cut-text"
|
class="btn btn-dark cut-text"
|
||||||
:class="{'active': editorFontSize === 'medium'}"
|
:class="{'active': editorFontSize === 'medium'}"
|
||||||
@click="changeEditorFontSize('medium')"
|
@click="changeEditorFontSize('medium')"
|
||||||
>
|
>
|
||||||
{{ t('word.medium') }}
|
14px
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="btn btn-dark cut-text"
|
class="btn btn-dark cut-text"
|
||||||
:class="{'active': editorFontSize === 'large'}"
|
:class="{'active': editorFontSize === 'large'}"
|
||||||
@click="changeEditorFontSize('large')"
|
@click="changeEditorFontSize('large')"
|
||||||
>
|
>
|
||||||
{{ t('word.large') }}
|
16px
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-dark cut-text"
|
||||||
|
:class="{'active': editorFontSize === 'xlarge'}"
|
||||||
|
@click="changeEditorFontSize('xlarge')"
|
||||||
|
>
|
||||||
|
18px
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-dark cut-text"
|
||||||
|
:class="{'active': editorFontSize === 'xxlarge'}"
|
||||||
|
@click="changeEditorFontSize('xxlarge')"
|
||||||
|
>
|
||||||
|
20px
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -282,6 +310,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-show="selectedTab === 'shortcuts'" class="panel-body py-4">
|
||||||
|
<ModalSettingsShortcuts />
|
||||||
|
</div>
|
||||||
<div v-show="selectedTab === 'update'" class="panel-body py-4">
|
<div v-show="selectedTab === 'update'" class="panel-body py-4">
|
||||||
<ModalSettingsUpdate />
|
<ModalSettingsUpdate />
|
||||||
</div>
|
</div>
|
||||||
@@ -315,7 +346,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onBeforeUnmount, Ref, ref } from 'vue';
|
import { onBeforeUnmount, Ref, ref, computed } from 'vue';
|
||||||
import { shell } from 'electron';
|
import { shell } from 'electron';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
@@ -326,11 +357,12 @@ import { useFocusTrap } from '@/composables/useFocusTrap';
|
|||||||
import { localesNames } from '@/i18n/supported-locales';
|
import { localesNames } from '@/i18n/supported-locales';
|
||||||
import ModalSettingsUpdate from '@/components/ModalSettingsUpdate.vue';
|
import ModalSettingsUpdate from '@/components/ModalSettingsUpdate.vue';
|
||||||
import ModalSettingsChangelog from '@/components/ModalSettingsChangelog.vue';
|
import ModalSettingsChangelog from '@/components/ModalSettingsChangelog.vue';
|
||||||
|
import ModalSettingsShortcuts from '@/components/ModalSettingsShortcuts.vue';
|
||||||
import BaseTextEditor from '@/components/BaseTextEditor.vue';
|
import BaseTextEditor from '@/components/BaseTextEditor.vue';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
import { computed } from '@vue/reactivity';
|
import { AvailableLocale } from '@/i18n';
|
||||||
|
|
||||||
const { t, availableLocales } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const applicationStore = useApplicationStore();
|
const applicationStore = useApplicationStore();
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
@@ -384,7 +416,29 @@ const contributors = process.env.APP_CONTRIBUTORS;
|
|||||||
const appLogo = require('../images/logo.svg');
|
const appLogo = require('../images/logo.svg');
|
||||||
const darkPreview = require('../images/dark.png');
|
const darkPreview = require('../images/dark.png');
|
||||||
const lightPreview = require('../images/light.png');
|
const lightPreview = require('../images/light.png');
|
||||||
const editorThemes= [
|
const exampleQuery = `-- This is an example
|
||||||
|
SELECT
|
||||||
|
employee.id,
|
||||||
|
employee.first_name,
|
||||||
|
employee.last_name,
|
||||||
|
SUM(DATEDIFF("SECOND", call.start, call.end)) AS call_duration
|
||||||
|
FROM call
|
||||||
|
INNER JOIN employee ON call.employee_id = employee.id
|
||||||
|
GROUP BY
|
||||||
|
employee.id,
|
||||||
|
employee.first_name,
|
||||||
|
employee.last_name
|
||||||
|
ORDER BY
|
||||||
|
employee.id ASC;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const localLocale: Ref<AvailableLocale> = ref(null);
|
||||||
|
const localPageSize: Ref<number> = ref(null);
|
||||||
|
const localTimeout: Ref<number> = ref(null);
|
||||||
|
const localEditorTheme: Ref<string> = ref(null);
|
||||||
|
const selectedTab: Ref<string> = ref('general');
|
||||||
|
|
||||||
|
const editorThemes = computed(() => [
|
||||||
{
|
{
|
||||||
group: t('word.light'),
|
group: t('word.light'),
|
||||||
themes: [
|
themes: [
|
||||||
@@ -432,32 +486,11 @@ const editorThemes= [
|
|||||||
{ code: 'vibrant_ink', name: 'Vibrant Ink' }
|
{ code: 'vibrant_ink', name: 'Vibrant Ink' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
]);
|
||||||
const exampleQuery = `-- This is an example
|
|
||||||
SELECT
|
|
||||||
employee.id,
|
|
||||||
employee.first_name,
|
|
||||||
employee.last_name,
|
|
||||||
SUM(DATEDIFF("SECOND", call.start, call.end)) AS call_duration
|
|
||||||
FROM call
|
|
||||||
INNER JOIN employee ON call.employee_id = employee.id
|
|
||||||
GROUP BY
|
|
||||||
employee.id,
|
|
||||||
employee.first_name,
|
|
||||||
employee.last_name
|
|
||||||
ORDER BY
|
|
||||||
employee.id ASC;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const localLocale: Ref<string> = ref(null);
|
|
||||||
const localPageSize: Ref<number> = ref(null);
|
|
||||||
const localTimeout: Ref<number> = ref(null);
|
|
||||||
const localEditorTheme: Ref<string> = ref(null);
|
|
||||||
const selectedTab: Ref<string> = ref('general');
|
|
||||||
|
|
||||||
const locales = computed(() => {
|
const locales = computed(() => {
|
||||||
const locales = [];
|
const locales = [];
|
||||||
for (const locale of availableLocales)
|
for (const locale of Object.keys(localesNames))
|
||||||
locales.push({ code: locale, name: localesNames[locale] });
|
locales.push({ code: locale, name: localesNames[locale] });
|
||||||
|
|
||||||
return locales;
|
return locales;
|
||||||
@@ -517,7 +550,7 @@ const toggleLineWrap = () => {
|
|||||||
changeLineWrap(!selectedLineWrap.value);
|
changeLineWrap(!selectedLineWrap.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
localLocale.value = selectedLocale.value as string;
|
localLocale.value = selectedLocale.value;
|
||||||
localPageSize.value = pageSize.value as number;
|
localPageSize.value = pageSize.value as number;
|
||||||
localTimeout.value = notificationsTimeout.value as number;
|
localTimeout.value = notificationsTimeout.value as number;
|
||||||
localEditorTheme.value = editorTheme.value as string;
|
localEditorTheme.value = editorTheme.value as string;
|
||||||
@@ -538,6 +571,12 @@ onBeforeUnmount(() => {
|
|||||||
.modal-body {
|
.modal-body {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
.tab-link {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
.panel-body {
|
.panel-body {
|
||||||
min-height: calc(25vh - 70px);
|
min-height: calc(25vh - 70px);
|
||||||
max-height: 65vh;
|
max-height: 65vh;
|
||||||
|
313
src/renderer/components/ModalSettingsShortcuts.vue
Normal file
313
src/renderer/components/ModalSettingsShortcuts.vue
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-relative">
|
||||||
|
<div class="shortcuts-tools pb-2 px-2">
|
||||||
|
<button class="btn btn-dark btn-sm d-flex ml-2" @click="showAddModal">
|
||||||
|
<i class="mdi mdi-24px mdi-plus mr-1" /><span>{{ t('message.addShortcut') }}</span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-dark btn-sm d-flex ml-2" @click="isConfirmRestoreModal = true">
|
||||||
|
<i class="mdi mdi-24px mdi-undo mr-1" /><span>{{ t('message.restoreDefaults') }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="container workspace-query-results">
|
||||||
|
<div class="table table-hover">
|
||||||
|
<div class="thead">
|
||||||
|
<div class="tr text-uppercase">
|
||||||
|
<div class="th no-border">
|
||||||
|
<div>
|
||||||
|
{{ t('word.event') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="th no-border" style="width: 100%;">
|
||||||
|
<div>
|
||||||
|
{{ t('word.key', 2) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="th no-border" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tbody">
|
||||||
|
<div
|
||||||
|
v-for="(shortcut, i) in shortcuts"
|
||||||
|
:key="i"
|
||||||
|
class="tr"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div class="td py-1">
|
||||||
|
{{ t(shortcutEvents[shortcut.event].l18n, {param: shortcutEvents[shortcut.event].l18nParam}) }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="td py-1"
|
||||||
|
style="border-right: 0;"
|
||||||
|
v-html="parseKeys(shortcut.keys)"
|
||||||
|
/>
|
||||||
|
<div class="td py-1 pr-2">
|
||||||
|
<button class="shortcut-button btn btn-link btn-sm d-flex p-0 px-1 mr-2" @click="showEditModal({...shortcut, index: i})">
|
||||||
|
<span>{{ t('word.edit') }}</span><i class="mdi mdi-pencil ml-1" />
|
||||||
|
</button>
|
||||||
|
<button class="shortcut-button btn btn-link btn-sm d-flex p-0 px-1" @click="showDeleteModal(shortcut)">
|
||||||
|
<span>{{ t('word.delete') }}</span><i class="mdi mdi-delete-outline ml-1" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Teleport to="#window-content">
|
||||||
|
<ConfirmModal
|
||||||
|
v-if="isConfirmAddModal"
|
||||||
|
:disable-autofocus="true"
|
||||||
|
:confirm-text="t('word.save')"
|
||||||
|
:close-on-confirm="false"
|
||||||
|
@confirm="addShortcut"
|
||||||
|
@hide="closeAddModal"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-plus mr-1" /> {{ t('message.addShortcut') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body>
|
||||||
|
<div class="mb-2">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ t('word.event') }}</label>
|
||||||
|
<BaseSelect
|
||||||
|
v-model="shortcutToAdd.event"
|
||||||
|
class="form-select"
|
||||||
|
:options="eventOptions"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ t('word.key', 2) }}</label>
|
||||||
|
<KeyPressDetector v-model="typedShortcut" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<small v-if="doesShortcutExists" class="text-warning">{{ t('message.shortcutAlreadyExists') }}</small>
|
||||||
|
</template>
|
||||||
|
</ConfirmModal>
|
||||||
|
|
||||||
|
<ConfirmModal
|
||||||
|
v-if="isConfirmEditModal"
|
||||||
|
:disable-autofocus="true"
|
||||||
|
:confirm-text="t('word.save')"
|
||||||
|
:close-on-confirm="false"
|
||||||
|
@confirm="editShortcut"
|
||||||
|
@hide="closeEditModal"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-plus mr-1" /> {{ t('message.editShortcut') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body>
|
||||||
|
<div class="mb-2">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ t('word.event') }}</label>
|
||||||
|
<BaseSelect
|
||||||
|
v-model="shortcutToEdit.event"
|
||||||
|
class="form-select"
|
||||||
|
:options="eventOptions"
|
||||||
|
:disabled="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-2">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ t('word.key', 2) }}</label>
|
||||||
|
<KeyPressDetector v-model="shortcutToEdit.keys[0]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<small v-if="doesShortcutExists" class="text-warning">{{ t('message.shortcutAlreadyExists') }}</small>
|
||||||
|
</template>
|
||||||
|
</ConfirmModal>
|
||||||
|
|
||||||
|
<ConfirmModal
|
||||||
|
v-if="isConfirmDeleteModal"
|
||||||
|
:disable-autofocus="true"
|
||||||
|
@confirm="deleteShortcut"
|
||||||
|
@hide="isConfirmDeleteModal = false"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-delete mr-1" /> {{ t('message.deleteShortcut') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body>
|
||||||
|
<div class="mb-2">
|
||||||
|
{{ t('message.deleteCorfirm') }} <b>{{ t(shortcutEvents[shortcutToDelete.event].l18n, {param: shortcutEvents[shortcutToDelete.event].l18nParam}) }} (<span v-html="parseKeys(shortcutToDelete.keys)" />)</b>?
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ConfirmModal>
|
||||||
|
|
||||||
|
<ConfirmModal
|
||||||
|
v-if="isConfirmRestoreModal"
|
||||||
|
:disable-autofocus="true"
|
||||||
|
@confirm="restoreDefaults"
|
||||||
|
@hide="isConfirmRestoreModal = false"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-undo mr-1" /> {{ t('message.restoreDefaults') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body>
|
||||||
|
<div class="mb-2">
|
||||||
|
{{ t('message.restoreDefaultsQuestion') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ConfirmModal>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Ref, ref, watch } from 'vue';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useSettingsStore } from '@/stores/settings';
|
||||||
|
import KeyPressDetector from './KeyPressDetector.vue';
|
||||||
|
import Application from '@/ipc-api/Application';
|
||||||
|
import { shortcutEvents, ShortcutRecord } from 'common/shortcuts';
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||||
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { computed } from '@vue/reactivity';
|
||||||
|
import { useFilters } from '@/composables/useFilters';
|
||||||
|
|
||||||
|
const { parseKeys } = useFilters();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const isMacOS = process.platform === 'darwin';
|
||||||
|
|
||||||
|
const isConfirmRestoreModal = ref(false);
|
||||||
|
const isConfirmAddModal = ref(false);
|
||||||
|
const isConfirmEditModal = ref(false);
|
||||||
|
const isConfirmDeleteModal = ref(false);
|
||||||
|
const doesShortcutExists = ref(false);
|
||||||
|
const shortcutToAdd: Ref<ShortcutRecord> = ref({ event: undefined, keys: [], os: [process.platform] });
|
||||||
|
const shortcutToEdit: Ref<ShortcutRecord & { index: number }> = ref(null);
|
||||||
|
const shortcutToDelete: Ref<ShortcutRecord> = ref(null);
|
||||||
|
const typedShortcut = ref('');
|
||||||
|
|
||||||
|
const settingsStore = useSettingsStore();
|
||||||
|
const { shortcuts } = storeToRefs(settingsStore);
|
||||||
|
|
||||||
|
const eventOptions = computed(() => {
|
||||||
|
return Object.keys(shortcutEvents)
|
||||||
|
.map(key => {
|
||||||
|
return { value: key, label: t(shortcutEvents[key].l18n, { param: shortcutEvents[key].l18nParam }) };
|
||||||
|
})
|
||||||
|
.sort((a, b) => {
|
||||||
|
if (a.label < b.label) return -1;
|
||||||
|
if (a.label > b.label) return 1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const restoreDefaults = () => {
|
||||||
|
isConfirmRestoreModal.value = false;
|
||||||
|
return Application.restoreDefaultShortcuts();
|
||||||
|
};
|
||||||
|
|
||||||
|
const showAddModal = () => {
|
||||||
|
shortcutToAdd.value.event = eventOptions.value[0].value;
|
||||||
|
isConfirmAddModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeAddModal = () => {
|
||||||
|
typedShortcut.value = '';
|
||||||
|
doesShortcutExists.value = false;
|
||||||
|
shortcutToAdd.value = { event: undefined, keys: [], os: [process.platform] };
|
||||||
|
isConfirmAddModal.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const showEditModal = (shortcut: ShortcutRecord & { index: number }) => {
|
||||||
|
shortcut = {
|
||||||
|
...shortcut,
|
||||||
|
keys: [shortcut.keys[0].replaceAll('CommandOrControl', isMacOS ? 'Command' : 'Control')]
|
||||||
|
};
|
||||||
|
shortcutToEdit.value = shortcut;
|
||||||
|
isConfirmEditModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const editShortcut = () => {
|
||||||
|
const index = shortcutToEdit.value.index;
|
||||||
|
delete shortcutToEdit.value.index;
|
||||||
|
shortcutToEdit.value.index = undefined;
|
||||||
|
|
||||||
|
shortcuts.value[index] = shortcutToEdit.value;
|
||||||
|
|
||||||
|
isConfirmEditModal.value = false;
|
||||||
|
return Application.updateShortcuts(shortcuts.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeEditModal = () => {
|
||||||
|
typedShortcut.value = '';
|
||||||
|
doesShortcutExists.value = false;
|
||||||
|
shortcutToEdit.value = null;
|
||||||
|
isConfirmEditModal.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addShortcut = () => {
|
||||||
|
if (!typedShortcut.value.length || doesShortcutExists.value) return;
|
||||||
|
shortcutToAdd.value.keys = [typedShortcut.value.replaceAll(isMacOS ? 'Command' : 'Control', 'CommandOrControl')];
|
||||||
|
const filteredShortcuts = [shortcutToAdd.value, ...shortcuts.value];
|
||||||
|
|
||||||
|
isConfirmAddModal.value = false;
|
||||||
|
return Application.updateShortcuts(filteredShortcuts);
|
||||||
|
};
|
||||||
|
|
||||||
|
const showDeleteModal = (shortcut: ShortcutRecord) => {
|
||||||
|
isConfirmDeleteModal.value = true;
|
||||||
|
shortcutToDelete.value = shortcut;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteShortcut = () => {
|
||||||
|
const filteredShortcuts = shortcuts.value.filter(s => (
|
||||||
|
shortcutToDelete.value.keys.toString() !== s.keys.toString()
|
||||||
|
));
|
||||||
|
|
||||||
|
isConfirmDeleteModal.value = false;
|
||||||
|
return Application.updateShortcuts(filteredShortcuts);
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(typedShortcut, () => {
|
||||||
|
doesShortcutExists.value = shortcuts.value.some(s => (
|
||||||
|
s.keys.some(k => (
|
||||||
|
k.replaceAll('CommandOrControl', isMacOS ? 'Command' : 'Control') === typedShortcut.value
|
||||||
|
))
|
||||||
|
));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.table {
|
||||||
|
.tr {
|
||||||
|
.td {
|
||||||
|
border-right: 3px solid;
|
||||||
|
border-bottom: 3px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.shortcut-button {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.shortcut-button {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
height: 1rem;
|
||||||
|
line-height: 1rem;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.shortcuts-tools {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -26,27 +26,27 @@
|
|||||||
:class="{'loading': updateStatus === 'checking'}"
|
:class="{'loading': updateStatus === 'checking'}"
|
||||||
@click="checkForUpdates"
|
@click="checkForUpdates"
|
||||||
>
|
>
|
||||||
{{ $t('message.checkForUpdates') }}
|
{{ t('message.checkForUpdates') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-else-if="updateStatus === 'downloaded'"
|
v-else-if="updateStatus === 'downloaded'"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
@click="restartToUpdate"
|
@click="restartToUpdate"
|
||||||
>
|
>
|
||||||
{{ $t('message.restartToInstall') }}
|
{{ t('message.restartToInstall') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-else-if="updateStatus === 'link'"
|
v-else-if="updateStatus === 'link'"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
@click="openOutside('https://antares-sql.app/download.html')"
|
@click="openOutside('https://antares-sql.app/download.html')"
|
||||||
>
|
>
|
||||||
{{ $t('message.goToDownloadPage') }}
|
{{ t('message.goToDownloadPage') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group mt-4">
|
<div class="form-group mt-4">
|
||||||
<label class="form-switch d-inline-block disabled" @click.prevent="toggleAllowPrerelease">
|
<label class="form-switch d-inline-block disabled" @click.prevent="toggleAllowPrerelease">
|
||||||
<input type="checkbox" :checked="allowPrerelease">
|
<input type="checkbox" :checked="allowPrerelease">
|
||||||
<i class="form-icon" /> {{ $t('message.includeBetaUpdates') }}
|
<i class="form-icon" /> {{ t('message.includeBetaUpdates') }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -34,6 +34,15 @@ const {
|
|||||||
lineWrap
|
lineWrap
|
||||||
} = storeToRefs(settingsStore);
|
} = storeToRefs(settingsStore);
|
||||||
|
|
||||||
|
const sizes = {
|
||||||
|
xsmall: '10px',
|
||||||
|
small: '12px',
|
||||||
|
medium: '14px',
|
||||||
|
large: '16px',
|
||||||
|
xlarge: '18px',
|
||||||
|
xxlarge: '20px'
|
||||||
|
};
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: String,
|
modelValue: String,
|
||||||
workspace: Object as Prop<Workspace>,
|
workspace: Object as Prop<Workspace>,
|
||||||
@@ -47,10 +56,11 @@ const props = defineProps({
|
|||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
const cursorPosition = ref(0);
|
const cursorPosition = ref(0);
|
||||||
const fields = ref([]);
|
const lastTableFields = ref([]);
|
||||||
const customCompleter = ref([]);
|
const customCompleter = ref([]);
|
||||||
const id = ref(uidGen());
|
const id = ref(uidGen());
|
||||||
const lastSchema: Ref<string> = ref(null);
|
const lastSchema: Ref<string> = ref(null);
|
||||||
|
const fields: Ref<{name: string; type: string}[]> = ref([]);
|
||||||
|
|
||||||
const tables = computed(() => {
|
const tables = computed(() => {
|
||||||
return props.workspace
|
return props.workspace
|
||||||
@@ -61,13 +71,27 @@ const tables = computed(() => {
|
|||||||
}, []).map(table => {
|
}, []).map(table => {
|
||||||
return {
|
return {
|
||||||
name: table.name as string,
|
name: table.name as string,
|
||||||
type: table.type as string,
|
type: table.type as string
|
||||||
fields: []
|
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const tablesInQuery = computed(() => {
|
||||||
|
if (!props.modelValue) return [];
|
||||||
|
const words = props.modelValue
|
||||||
|
.replaceAll(/[.'"`]/g, ' ')
|
||||||
|
.split(' ')
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
const includedTables = tables.value.reduce((acc, curr) => {
|
||||||
|
acc.push(curr.name);
|
||||||
|
return acc;
|
||||||
|
}, [] as string[]).filter((t) => words.includes(t));
|
||||||
|
|
||||||
|
return includedTables;
|
||||||
|
});
|
||||||
|
|
||||||
const triggers = computed(() => {
|
const triggers = computed(() => {
|
||||||
return props.workspace
|
return props.workspace
|
||||||
? props.workspace.structure.filter(schema => schema.name === props.schema)
|
? props.workspace.structure.filter(schema => schema.name === props.schema)
|
||||||
@@ -150,11 +174,11 @@ const lastWord = computed(() => {
|
|||||||
|
|
||||||
const isLastWordATable = computed(() => /\w+\.\w*/gm.test(lastWord.value));
|
const isLastWordATable = computed(() => /\w+\.\w*/gm.test(lastWord.value));
|
||||||
|
|
||||||
const fieldsCompleter = computed(() => {
|
const tableFieldsCompleter = computed(() => {
|
||||||
return {
|
return {
|
||||||
getCompletions: (editor: never, session: never, pos: never, prefix: never, callback: (err: null, response: ace.Ace.Completion[]) => void) => {
|
getCompletions: (editor: never, session: never, pos: never, prefix: never, callback: (err: null, response: ace.Ace.Completion[]) => void) => {
|
||||||
const completions: ace.Ace.Completion[] = [];
|
const completions: ace.Ace.Completion[] = [];
|
||||||
fields.value.forEach(field => {
|
lastTableFields.value.forEach(field => {
|
||||||
completions.push({
|
completions.push({
|
||||||
value: field,
|
value: field,
|
||||||
meta: 'column',
|
meta: 'column',
|
||||||
@@ -175,7 +199,8 @@ const setCustomCompleter = () => {
|
|||||||
...triggers.value,
|
...triggers.value,
|
||||||
...procedures.value,
|
...procedures.value,
|
||||||
...functions.value,
|
...functions.value,
|
||||||
...schedulers.value
|
...schedulers.value,
|
||||||
|
...fields.value
|
||||||
].forEach(el => {
|
].forEach(el => {
|
||||||
completions.push({
|
completions.push({
|
||||||
value: el.name,
|
value: el.name,
|
||||||
@@ -195,18 +220,35 @@ watch(() => props.modelValue, () => {
|
|||||||
cursorPosition.value = (editor.value.session as any).doc.positionToIndex(editor.value.getCursorPosition());
|
cursorPosition.value = (editor.value.session as any).doc.positionToIndex(editor.value.getCursorPosition());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(() => tablesInQuery.value.length, () => {
|
||||||
|
const localFields: {name: string; type: string}[] = [];
|
||||||
|
tablesInQuery.value.forEach(async table => {
|
||||||
|
const params = {
|
||||||
|
uid: props.workspace.uid,
|
||||||
|
schema: props.schema,
|
||||||
|
table: table
|
||||||
|
};
|
||||||
|
|
||||||
|
const { response } = await Tables.getTableColumns(params);
|
||||||
|
|
||||||
|
response.forEach((field: { name: string }) => {
|
||||||
|
localFields.push({
|
||||||
|
name: field.name,
|
||||||
|
type: 'column'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
fields.value = localFields;
|
||||||
|
setCustomCompleter();
|
||||||
|
});
|
||||||
|
|
||||||
watch(editorTheme, () => {
|
watch(editorTheme, () => {
|
||||||
if (editor.value)
|
if (editor.value)
|
||||||
editor.value.setTheme(`ace/theme/${editorTheme.value}`);
|
editor.value.setTheme(`ace/theme/${editorTheme.value}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(editorFontSize, () => {
|
watch(editorFontSize, () => {
|
||||||
const sizes = {
|
|
||||||
small: '12px',
|
|
||||||
medium: '14px',
|
|
||||||
large: '16px'
|
|
||||||
};
|
|
||||||
|
|
||||||
if (editor.value) {
|
if (editor.value) {
|
||||||
editor.value.setOptions({
|
editor.value.setOptions({
|
||||||
fontSize: sizes[editorFontSize.value]
|
fontSize: sizes[editorFontSize.value]
|
||||||
@@ -266,7 +308,8 @@ onMounted(() => {
|
|||||||
enableBasicAutocompletion: true,
|
enableBasicAutocompletion: true,
|
||||||
wrap: lineWrap.value,
|
wrap: lineWrap.value,
|
||||||
enableSnippets: true,
|
enableSnippets: true,
|
||||||
enableLiveAutocompletion: autoComplete.value
|
enableLiveAutocompletion: autoComplete.value,
|
||||||
|
fontSize: sizes[editorFontSize.value]
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!baseCompleter.value.length)
|
if (!baseCompleter.value.length)
|
||||||
@@ -289,8 +332,8 @@ onMounted(() => {
|
|||||||
|
|
||||||
Tables.getTableColumns(params).then(res => {
|
Tables.getTableColumns(params).then(res => {
|
||||||
if (res.response.length)
|
if (res.response.length)
|
||||||
fields.value = res.response.map((field: { name: string }) => field.name);
|
lastTableFields.value = res.response.map((field: { name: string }) => field.name);
|
||||||
editor.value.completers = [fieldsCompleter.value];
|
editor.value.completers = [tableFieldsCompleter.value];
|
||||||
editor.value.execCommand('startAutocomplete');
|
editor.value.execCommand('startAutocomplete');
|
||||||
}).catch(console.log);
|
}).catch(console.log);
|
||||||
}
|
}
|
||||||
|
@@ -8,27 +8,27 @@
|
|||||||
class="context-element"
|
class="context-element"
|
||||||
@click="unpin"
|
@click="unpin"
|
||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-pin-off text-light pr-1" /> {{ $t('word.unpin') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-pin-off text-light pr-1" /> {{ t('word.unpin') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-else
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@click="pin"
|
@click="pin"
|
||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-pin mdi-rotate-45 text-light pr-1" /> {{ $t('word.pin') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-pin mdi-rotate-45 text-light pr-1" /> {{ t('word.pin') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="isConnected"
|
v-if="isConnected"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@click="disconnect"
|
@click="disconnect"
|
||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-power text-light pr-1" /> {{ $t('word.disconnect') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-power text-light pr-1" /> {{ t('word.disconnect') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element" @click="duplicateConnection">
|
<div class="context-element" @click="duplicateConnection">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-content-duplicate text-light pr-1" /> {{ $t('word.duplicate') }}</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>
|
||||||
|
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
@@ -38,12 +38,12 @@
|
|||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-server-remove mr-1" /> {{ $t('message.deleteConnection') }}
|
<i class="mdi mdi-24px mdi-server-remove mr-1" /> {{ t('message.deleteConnection') }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
{{ $t('message.deleteCorfirm') }} <b>{{ connectionName }}</b>?
|
{{ t('message.deleteCorfirm') }} <b>{{ connectionName }}</b>?
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
@@ -54,12 +54,15 @@
|
|||||||
import { computed, Prop, ref } from 'vue';
|
import { computed, Prop, ref } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useConnectionsStore } from '@/stores/connections';
|
import { useConnectionsStore } from '@/stores/connections';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import BaseContextMenu from '@/components/BaseContextMenu.vue';
|
import BaseContextMenu from '@/components/BaseContextMenu.vue';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||||
import { ConnectionParams } from 'common/interfaces/antares';
|
import { ConnectionParams } from 'common/interfaces/antares';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const connectionsStore = useConnectionsStore();
|
const connectionsStore = useConnectionsStore();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@@ -11,14 +11,30 @@
|
|||||||
|
|
||||||
<div class="footer-right-elements">
|
<div class="footer-right-elements">
|
||||||
<ul class="footer-elements">
|
<ul class="footer-elements">
|
||||||
|
<li
|
||||||
|
v-if="workspace?.connectionStatus === 'connected' "
|
||||||
|
class="footer-element footer-link"
|
||||||
|
@click="toggleConsole()"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-18px mdi-console-line mr-1" />
|
||||||
|
<small>{{ t('word.console') }}</small>
|
||||||
|
</li>
|
||||||
<li class="footer-element footer-link" @click="openOutside('https://www.paypal.com/paypalme/fabiodistasio')">
|
<li class="footer-element footer-link" @click="openOutside('https://www.paypal.com/paypalme/fabiodistasio')">
|
||||||
<i class="mdi mdi-18px mdi-coffee mr-1" />
|
<i class="mdi mdi-18px mdi-coffee mr-1" />
|
||||||
<small>{{ $t('word.donate') }}</small>
|
<small>{{ t('word.donate') }}</small>
|
||||||
</li>
|
</li>
|
||||||
<li class="footer-element footer-link" @click="openOutside('https://github.com/antares-sql/antares/issues')">
|
<li
|
||||||
|
class="footer-element footer-link"
|
||||||
|
:title="t('message.reportABug')"
|
||||||
|
@click="openOutside('https://github.com/antares-sql/antares/issues')"
|
||||||
|
>
|
||||||
<i class="mdi mdi-18px mdi-bug" />
|
<i class="mdi mdi-18px mdi-bug" />
|
||||||
</li>
|
</li>
|
||||||
<li class="footer-element footer-link" @click="showSettingModal('about')">
|
<li
|
||||||
|
class="footer-element footer-link"
|
||||||
|
:title="t('word.about')"
|
||||||
|
@click="showSettingModal('about')"
|
||||||
|
>
|
||||||
<i class="mdi mdi-18px mdi-information-outline" />
|
<i class="mdi mdi-18px mdi-information-outline" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -29,9 +45,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { shell } from 'electron';
|
import { shell } from 'electron';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useApplicationStore } from '@/stores/application';
|
import { useApplicationStore } from '@/stores/application';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import { computed, ComputedRef } from 'vue';
|
import { computed, ComputedRef } from 'vue';
|
||||||
|
import { useConsoleStore } from '@/stores/console';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
interface DatabaseInfos {// TODO: temp
|
interface DatabaseInfos {// TODO: temp
|
||||||
name: string;
|
name: string;
|
||||||
@@ -43,13 +63,15 @@ interface DatabaseInfos {// TODO: temp
|
|||||||
const applicationStore = useApplicationStore();
|
const applicationStore = useApplicationStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
|
||||||
const { getSelected: workspace } = storeToRefs(workspacesStore);
|
const { getSelected: workspaceUid } = storeToRefs(workspacesStore);
|
||||||
|
const { toggleConsole } = useConsoleStore();
|
||||||
|
|
||||||
const { showSettingModal } = applicationStore;
|
const { showSettingModal } = applicationStore;
|
||||||
const { getWorkspace } = workspacesStore;
|
const { getWorkspace } = workspacesStore;
|
||||||
|
|
||||||
|
const workspace = computed(() => getWorkspace(workspaceUid.value));
|
||||||
const version: ComputedRef<DatabaseInfos> = computed(() => {
|
const version: ComputedRef<DatabaseInfos> = computed(() => {
|
||||||
return getWorkspace(workspace.value) ? getWorkspace(workspace.value).version : null;
|
return getWorkspace(workspaceUid.value) ? workspace.value.version : null;
|
||||||
});
|
});
|
||||||
|
|
||||||
const versionString = computed(() => {
|
const versionString = computed(() => {
|
||||||
|
@@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
:confirm-text="$t('word.update')"
|
:confirm-text="t('word.update')"
|
||||||
:cancel-text="$t('word.close')"
|
:cancel-text="t('word.close')"
|
||||||
size="large"
|
size="large"
|
||||||
:hide-footer="true"
|
:hide-footer="true"
|
||||||
@hide="hideScratchpad"
|
@hide="hideScratchpad"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-notebook-edit-outline mr-1" /> {{ $t('word.scratchpad') }}
|
<i class="mdi mdi-24px mdi-notebook-edit-outline mr-1" /> {{ t('word.scratchpad') }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
:show-line-numbers="false"
|
:show-line-numbers="false"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<small class="text-gray">{{ $t('message.markdownSupported') }}</small>
|
<small class="text-gray">{{ t('message.markdownSupported') }}</small>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
@@ -35,6 +35,9 @@ import { useApplicationStore } from '@/stores/application';
|
|||||||
import { useScratchpadStore } from '@/stores/scratchpad';
|
import { useScratchpadStore } from '@/stores/scratchpad';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||||
import TextEditor from '@/components/BaseTextEditor.vue';
|
import TextEditor from '@/components/BaseTextEditor.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const applicationStore = useApplicationStore();
|
const applicationStore = useApplicationStore();
|
||||||
const scratchpadStore = useScratchpadStore();
|
const scratchpadStore = useScratchpadStore();
|
||||||
|
@@ -14,17 +14,20 @@
|
|||||||
@start="isDragging = true"
|
@start="isDragging = true"
|
||||||
@end="dragStop"
|
@end="dragStop"
|
||||||
>
|
>
|
||||||
<template #item="{element}">
|
<template #item="{ element }">
|
||||||
<li
|
<li
|
||||||
:draggable="true"
|
:draggable="true"
|
||||||
class="settingbar-element btn btn-link ex-tooltip"
|
class="settingbar-element btn btn-link"
|
||||||
:class="{'selected': element.uid === selectedWorkspace}"
|
:class="{ 'selected': element.uid === selectedWorkspace }"
|
||||||
|
:title="getConnectionName(element.uid)"
|
||||||
@click.stop="selectWorkspace(element.uid)"
|
@click.stop="selectWorkspace(element.uid)"
|
||||||
@contextmenu.prevent="contextMenu($event, element)"
|
@contextmenu.prevent="contextMenu($event, element)"
|
||||||
@mouseover.self="tooltipPosition"
|
|
||||||
>
|
>
|
||||||
<i class="settingbar-element-icon dbi" :class="[`dbi-${element.client}`, getStatusBadge(element.uid), (pinnedConnections.has(element.uid) ? 'settingbar-element-pin' : false)]" />
|
<i
|
||||||
<span v-if="!isDragging && !isScrolling" class="ex-tooltip-content">{{ getConnectionName(element.uid) }}</span>
|
class="settingbar-element-icon dbi"
|
||||||
|
:class="[`dbi-${element.client}`, getStatusBadge(element.uid), (pinnedConnections.has(element.uid) ? 'settingbar-element-pin' : false)]"
|
||||||
|
/>
|
||||||
|
<small class="settingbar-element-name">{{ getConnectionName(element.uid) }}</small>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
</Draggable>
|
</Draggable>
|
||||||
@@ -34,14 +37,17 @@
|
|||||||
<li
|
<li
|
||||||
v-for="connection in unpinnedConnectionsArr"
|
v-for="connection in unpinnedConnectionsArr"
|
||||||
:key="connection.uid"
|
:key="connection.uid"
|
||||||
class="settingbar-element btn btn-link ex-tooltip"
|
class="settingbar-element btn btn-link"
|
||||||
:class="{'selected': connection.uid === selectedWorkspace}"
|
:class="{ 'selected': connection.uid === selectedWorkspace }"
|
||||||
|
:title="getConnectionName(connection.uid)"
|
||||||
@click.stop="selectWorkspace(connection.uid)"
|
@click.stop="selectWorkspace(connection.uid)"
|
||||||
@contextmenu.prevent="contextMenu($event, connection)"
|
@contextmenu.prevent="contextMenu($event, connection)"
|
||||||
@mouseover.self="tooltipPosition"
|
|
||||||
>
|
>
|
||||||
<i class="settingbar-element-icon dbi" :class="[`dbi-${connection.client}`, getStatusBadge(connection.uid)]" />
|
<i
|
||||||
<span v-if="!isDragging && !isScrolling" class="ex-tooltip-content">{{ getConnectionName(connection.uid) }}</span>
|
class="settingbar-element-icon dbi"
|
||||||
|
:class="[`dbi-${connection.client}`, getStatusBadge(connection.uid)]"
|
||||||
|
/>
|
||||||
|
<small class="settingbar-element-name">{{ getConnectionName(connection.uid) }}</small>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -50,21 +56,19 @@
|
|||||||
<ul class="settingbar-elements">
|
<ul class="settingbar-elements">
|
||||||
<li
|
<li
|
||||||
v-if="isScrollable"
|
v-if="isScrollable"
|
||||||
class="settingbar-element btn btn-link ex-tooltip"
|
class="settingbar-element btn btn-link"
|
||||||
|
:title="t('message.allConnections')"
|
||||||
@click="emit('show-connections-modal')"
|
@click="emit('show-connections-modal')"
|
||||||
@mouseover.self="tooltipPosition"
|
|
||||||
>
|
>
|
||||||
<i class="settingbar-element-icon mdi mdi-24px mdi-dots-horizontal text-light" />
|
<i class="settingbar-element-icon mdi mdi-24px mdi-dots-horizontal text-light" />
|
||||||
<span class="ex-tooltip-content">{{ $t('message.allConnections') }} (CTRL+Space)</span>
|
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="settingbar-element btn btn-link ex-tooltip"
|
class="settingbar-element btn btn-link"
|
||||||
:class="{'selected': 'NEW' === selectedWorkspace}"
|
:class="{ 'selected': 'NEW' === selectedWorkspace }"
|
||||||
|
:title="t('message.addConnection')"
|
||||||
@click="selectWorkspace('NEW')"
|
@click="selectWorkspace('NEW')"
|
||||||
@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" />
|
||||||
<span class="ex-tooltip-content">{{ $t('message.addConnection') }}</span>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -73,15 +77,21 @@
|
|||||||
<ul class="settingbar-elements">
|
<ul class="settingbar-elements">
|
||||||
<li
|
<li
|
||||||
v-if="!disableScratchpad"
|
v-if="!disableScratchpad"
|
||||||
class="settingbar-element btn btn-link ex-tooltip"
|
class="settingbar-element btn btn-link"
|
||||||
|
:title="t('word.scratchpad')"
|
||||||
@click="showScratchpad"
|
@click="showScratchpad"
|
||||||
>
|
>
|
||||||
<i class="settingbar-element-icon mdi mdi-24px mdi-notebook-edit-outline text-light" />
|
<i class="settingbar-element-icon mdi mdi-24px mdi-notebook-edit-outline text-light" />
|
||||||
<span class="ex-tooltip-content">{{ $t('word.scratchpad') }}</span>
|
|
||||||
</li>
|
</li>
|
||||||
<li class="settingbar-element btn btn-link ex-tooltip" @click="showSettingModal('general')">
|
<li
|
||||||
<i class="settingbar-element-icon mdi mdi-24px mdi-cog text-light" :class="{' badge badge-update': hasUpdates}" />
|
class="settingbar-element btn btn-link"
|
||||||
<span class="ex-tooltip-content">{{ $t('word.settings') }}</span>
|
:title="t('word.settings')"
|
||||||
|
@click="showSettingModal('general')"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="settingbar-element-icon mdi mdi-24px mdi-cog text-light"
|
||||||
|
:class="{ ' badge badge-update': hasUpdates }"
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -98,7 +108,10 @@ import { useSettingsStore } from '@/stores/settings';
|
|||||||
import * as Draggable from 'vuedraggable';
|
import * as Draggable from 'vuedraggable';
|
||||||
import SettingBarContext from '@/components/SettingBarContext.vue';
|
import SettingBarContext from '@/components/SettingBarContext.vue';
|
||||||
import { ConnectionParams } from 'common/interfaces/antares';
|
import { ConnectionParams } from 'common/interfaces/antares';
|
||||||
import { useElementBounding, useScroll } from '@vueuse/core';
|
import { useElementBounding } from '@vueuse/core';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const applicationStore = useApplicationStore();
|
const applicationStore = useApplicationStore();
|
||||||
const connectionsStore = useConnectionsStore();
|
const connectionsStore = useConnectionsStore();
|
||||||
@@ -122,7 +135,6 @@ const sidebarConnections: Ref<HTMLDivElement> = ref(null);
|
|||||||
const isContext: Ref<boolean> = ref(false);
|
const isContext: Ref<boolean> = ref(false);
|
||||||
const isDragging: Ref<boolean> = ref(false);
|
const isDragging: Ref<boolean> = ref(false);
|
||||||
const isScrollable: Ref<boolean> = ref(false);
|
const isScrollable: Ref<boolean> = ref(false);
|
||||||
const isScrolling = ref(useScroll(sidebarConnections)?.isScrolling);
|
|
||||||
const contextEvent: Ref<MouseEvent> = ref(null);
|
const contextEvent: Ref<MouseEvent> = ref(null);
|
||||||
const contextConnection: Ref<ConnectionParams> = ref(null);
|
const contextConnection: Ref<ConnectionParams> = ref(null);
|
||||||
const sidebarConnectionsHeight = ref(useElementBounding(sidebarConnections)?.height);
|
const sidebarConnectionsHeight = ref(useElementBounding(sidebarConnections)?.height);
|
||||||
@@ -237,32 +249,32 @@ watch(selectedWorkspace, (newVal, oldVal) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
#settingbar {
|
#settingbar {
|
||||||
width: $settingbar-width;
|
width: $settingbar-width;
|
||||||
height: calc(100vh - #{$excluding-size});
|
height: calc(100vh - #{$excluding-size});
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
// justify-content: space-between;
|
// justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
z-index: 9;
|
z-index: 9;
|
||||||
|
|
||||||
.settingbar-top-elements {
|
.settingbar-top-elements {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
overflow-y: overlay;
|
overflow-y: overlay;
|
||||||
// max-height: calc((100vh - 3.5rem) - #{$excluding-size});
|
// max-height: calc((100vh - 3.5rem) - #{$excluding-size});
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: 3px;
|
width: 3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.settingbar-bottom-elements {
|
.settingbar-bottom-elements {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settingbar-elements {
|
.settingbar-elements {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: $settingbar-width;
|
width: $settingbar-width;
|
||||||
@@ -270,101 +282,85 @@ watch(selectedWorkspace, (newVal, oldVal) => {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
.settingbar-element {
|
.settingbar-element {
|
||||||
height: $settingbar-width;
|
height: $settingbar-width;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
opacity: 0.5;
|
opacity: 0.6;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
height: $settingbar-width;
|
height: $settingbar-width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: "";
|
content: "";
|
||||||
height: 0;
|
height: 0;
|
||||||
width: 3px;
|
width: 3px;
|
||||||
transition: height 0.2s;
|
transition: height 0.2s;
|
||||||
background-color: $primary-color;
|
background-color: $primary-color;
|
||||||
border-radius: $border-radius;
|
border-radius: $border-radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settingbar-element-icon {
|
.settingbar-element-icon {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
|
||||||
&.badge::after {
|
&.badge::after {
|
||||||
bottom: -10px;
|
top: 5px;
|
||||||
right: 0;
|
right: -4px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.badge-update::after {
|
||||||
|
bottom: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settingbar-element-name {
|
||||||
|
font-size: 65%;
|
||||||
|
bottom: 5px;
|
||||||
|
left: 7px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
font-style: normal;
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
width: calc($settingbar-width - 15px);
|
||||||
|
text-align: left;
|
||||||
|
line-height: 1.1;
|
||||||
|
color: rgba($body-font-color-dark, 0.8);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
&.badge-update::after {
|
.settingbar-element-pin {
|
||||||
bottom: initial;
|
margin: 0 auto;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.settingbar-element-pin{
|
&::before {
|
||||||
margin: 0 auto;
|
font: normal normal normal 14px/1 "Material Design Icons";
|
||||||
|
content: "\F0403";
|
||||||
&::before {
|
color: $body-font-color-dark;
|
||||||
font: normal normal normal 14px/1 "Material Design Icons";
|
transform: rotate(45deg);
|
||||||
content: "\F0403";
|
opacity: 0.25;
|
||||||
color: $body-font-color-dark;
|
top: -8px;
|
||||||
transform: rotate(45deg);
|
left: -10px;
|
||||||
opacity: .25;
|
position: absolute;
|
||||||
bottom: -8px;
|
}
|
||||||
left: -4px;
|
}
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ex-tooltip {// Because both overflow-x: visible and overflow-y:auto are evil!!!
|
|
||||||
.ex-tooltip-content {
|
|
||||||
z-index: 999;
|
|
||||||
visibility: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
display: block;
|
|
||||||
position: absolute;
|
|
||||||
text-align: center;
|
|
||||||
margin: 0 0 0 calc(#{$settingbar-width} - 5px);
|
|
||||||
left: 0;
|
|
||||||
padding: 0.2rem 0.4rem;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
background: rgba(48, 55, 66, 0.95);
|
|
||||||
border-radius: $border-radius;
|
|
||||||
color: #fff;
|
|
||||||
max-width: 320px;
|
|
||||||
pointer-events: none;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: opacity 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.sortable-chosen {
|
|
||||||
.ex-tooltip-content {
|
|
||||||
opacity: 0 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover:not(.selected) .ex-tooltip-content {
|
|
||||||
visibility: visible;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@@ -31,10 +31,10 @@
|
|||||||
>
|
>
|
||||||
<i class="mdi mdi-18px mdi-code-tags mr-1" />
|
<i class="mdi mdi-18px mdi-code-tags mr-1" />
|
||||||
<span>
|
<span>
|
||||||
<span>{{ cutText(element.content || 'Query') }} #{{ element.index }}</span>
|
<span>{{ cutText(element.content || 'Query', 20, true) }} #{{ element.index }}</span>
|
||||||
<span
|
<span
|
||||||
class="btn btn-clear"
|
class="btn btn-clear"
|
||||||
:title="$t('word.close')"
|
:title="t('word.close')"
|
||||||
@mousedown.left.stop
|
@mousedown.left.stop
|
||||||
@click.stop="closeTab(element)"
|
@click.stop="closeTab(element)"
|
||||||
/>
|
/>
|
||||||
@@ -47,11 +47,11 @@
|
|||||||
@dblclick="openAsPermanentTab(element)"
|
@dblclick="openAsPermanentTab(element)"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-18px mr-1" :class="element.elementType === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
|
<i class="mdi mdi-18px mr-1" :class="element.elementType === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
|
||||||
<span :title="`${$t('word.data').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
|
<span :title="`${t('word.data').toUpperCase()}: ${t(`word.${element.elementType}`)}`">
|
||||||
<span class=" text-italic">{{ cutText(element.elementName) }}</span>
|
<span class=" text-italic">{{ cutText(element.elementName, 20, true) }}</span>
|
||||||
<span
|
<span
|
||||||
class="btn btn-clear"
|
class="btn btn-clear"
|
||||||
:title="$t('word.close')"
|
:title="t('word.close')"
|
||||||
@mousedown.left.stop
|
@mousedown.left.stop
|
||||||
@click.stop="closeTab(element)"
|
@click.stop="closeTab(element)"
|
||||||
/>
|
/>
|
||||||
@@ -60,11 +60,11 @@
|
|||||||
|
|
||||||
<a v-else-if="element.type === 'data'" class="tab-link">
|
<a v-else-if="element.type === 'data'" class="tab-link">
|
||||||
<i class="mdi mdi-18px mr-1" :class="element.elementType === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
|
<i class="mdi mdi-18px mr-1" :class="element.elementType === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
|
||||||
<span :title="`${$t('word.data').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
|
<span :title="`${t('word.data').toUpperCase()}: ${t(`word.${element.elementType}`)}`">
|
||||||
{{ cutText(element.elementName) }}
|
{{ cutText(element.elementName, 20, true) }}
|
||||||
<span
|
<span
|
||||||
class="btn btn-clear"
|
class="btn btn-clear"
|
||||||
:title="$t('word.close')"
|
:title="t('word.close')"
|
||||||
@mousedown.left.stop
|
@mousedown.left.stop
|
||||||
@click.stop="closeTab(element)"
|
@click.stop="closeTab(element)"
|
||||||
/>
|
/>
|
||||||
@@ -77,11 +77,11 @@
|
|||||||
:class="{'badge': element.isChanged}"
|
:class="{'badge': element.isChanged}"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
||||||
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
|
<span :title="`${t('word.new').toUpperCase()}: ${t(`word.${element.elementType}`)}`">
|
||||||
{{ $t('message.newTable') }}
|
{{ t('message.newTable') }}
|
||||||
<span
|
<span
|
||||||
class="btn btn-clear"
|
class="btn btn-clear"
|
||||||
:title="$t('word.close')"
|
:title="t('word.close')"
|
||||||
@mousedown.left.stop
|
@mousedown.left.stop
|
||||||
@click.stop="closeTab(element)"
|
@click.stop="closeTab(element)"
|
||||||
/>
|
/>
|
||||||
@@ -94,11 +94,11 @@
|
|||||||
:class="{'badge': element.isChanged}"
|
:class="{'badge': element.isChanged}"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-tune-vertical-variant mdi-18px mr-1" />
|
<i class="mdi mdi-tune-vertical-variant mdi-18px mr-1" />
|
||||||
<span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
|
<span :title="`${t('word.settings').toUpperCase()}: ${t(`word.${element.elementType}`)}`">
|
||||||
{{ cutText(element.elementName) }}
|
{{ cutText(element.elementName, 20, true) }}
|
||||||
<span
|
<span
|
||||||
class="btn btn-clear"
|
class="btn btn-clear"
|
||||||
:title="$t('word.close')"
|
:title="t('word.close')"
|
||||||
@mousedown.left.stop
|
@mousedown.left.stop
|
||||||
@click.stop="closeTab(element)"
|
@click.stop="closeTab(element)"
|
||||||
/>
|
/>
|
||||||
@@ -111,11 +111,11 @@
|
|||||||
:class="{'badge': element.isChanged}"
|
:class="{'badge': element.isChanged}"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-tune-vertical-variant mdi-18px mr-1" />
|
<i class="mdi mdi-tune-vertical-variant mdi-18px mr-1" />
|
||||||
<span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.view`)}`">
|
<span :title="`${t('word.settings').toUpperCase()}: ${t(`word.view`)}`">
|
||||||
{{ cutText(element.elementName) }}
|
{{ cutText(element.elementName, 20, true) }}
|
||||||
<span
|
<span
|
||||||
class="btn btn-clear"
|
class="btn btn-clear"
|
||||||
:title="$t('word.close')"
|
:title="t('word.close')"
|
||||||
@mousedown.left.stop
|
@mousedown.left.stop
|
||||||
@click.stop="closeTab(element)"
|
@click.stop="closeTab(element)"
|
||||||
/>
|
/>
|
||||||
@@ -128,11 +128,11 @@
|
|||||||
:class="{'badge': element.isChanged}"
|
:class="{'badge': element.isChanged}"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
||||||
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
|
<span :title="`${t('word.new').toUpperCase()}: ${t(`word.${element.elementType}`)}`">
|
||||||
{{ $t('message.newView') }}
|
{{ t('message.newView') }}
|
||||||
<span
|
<span
|
||||||
class="btn btn-clear"
|
class="btn btn-clear"
|
||||||
:title="$t('word.close')"
|
:title="t('word.close')"
|
||||||
@mousedown.left.stop
|
@mousedown.left.stop
|
||||||
@click.stop="closeTab(element)"
|
@click.stop="closeTab(element)"
|
||||||
/>
|
/>
|
||||||
@@ -145,11 +145,11 @@
|
|||||||
:class="{'badge': element.isChanged}"
|
:class="{'badge': element.isChanged}"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
||||||
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
|
<span :title="`${t('word.new').toUpperCase()}: ${t(`word.${element.elementType}`)}`">
|
||||||
{{ $t('message.newTrigger') }}
|
{{ t('message.newTrigger') }}
|
||||||
<span
|
<span
|
||||||
class="btn btn-clear"
|
class="btn btn-clear"
|
||||||
:title="$t('word.close')"
|
:title="t('word.close')"
|
||||||
@mousedown.left.stop
|
@mousedown.left.stop
|
||||||
@click.stop="closeTab(element)"
|
@click.stop="closeTab(element)"
|
||||||
/>
|
/>
|
||||||
@@ -162,11 +162,11 @@
|
|||||||
:class="{'badge': element.isChanged}"
|
:class="{'badge': element.isChanged}"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
||||||
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
|
<span :title="`${t('word.new').toUpperCase()}: ${t(`word.${element.elementType}`)}`">
|
||||||
{{ $t('message.newRoutine') }}
|
{{ t('message.newRoutine') }}
|
||||||
<span
|
<span
|
||||||
class="btn btn-clear"
|
class="btn btn-clear"
|
||||||
:title="$t('word.close')"
|
:title="t('word.close')"
|
||||||
@mousedown.left.stop
|
@mousedown.left.stop
|
||||||
@click.stop="closeTab(element)"
|
@click.stop="closeTab(element)"
|
||||||
/>
|
/>
|
||||||
@@ -179,11 +179,11 @@
|
|||||||
:class="{'badge': element.isChanged}"
|
:class="{'badge': element.isChanged}"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
||||||
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
|
<span :title="`${t('word.new').toUpperCase()}: ${t(`word.${element.elementType}`)}`">
|
||||||
{{ $t('message.newFunction') }}
|
{{ t('message.newFunction') }}
|
||||||
<span
|
<span
|
||||||
class="btn btn-clear"
|
class="btn btn-clear"
|
||||||
:title="$t('word.close')"
|
:title="t('word.close')"
|
||||||
@mousedown.left.stop
|
@mousedown.left.stop
|
||||||
@click.stop="closeTab(element)"
|
@click.stop="closeTab(element)"
|
||||||
/>
|
/>
|
||||||
@@ -196,11 +196,11 @@
|
|||||||
:class="{'badge': element.isChanged}"
|
:class="{'badge': element.isChanged}"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
||||||
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
|
<span :title="`${t('word.new').toUpperCase()}: ${t(`word.${element.elementType}`)}`">
|
||||||
{{ $t('message.newTriggerFunction') }}
|
{{ t('message.newTriggerFunction') }}
|
||||||
<span
|
<span
|
||||||
class="btn btn-clear"
|
class="btn btn-clear"
|
||||||
:title="$t('word.close')"
|
:title="t('word.close')"
|
||||||
@mousedown.left.stop
|
@mousedown.left.stop
|
||||||
@click.stop="closeTab(element)"
|
@click.stop="closeTab(element)"
|
||||||
/>
|
/>
|
||||||
@@ -213,11 +213,11 @@
|
|||||||
:class="{'badge': element.isChanged}"
|
:class="{'badge': element.isChanged}"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
||||||
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
|
<span :title="`${t('word.new').toUpperCase()}: ${t(`word.${element.elementType}`)}`">
|
||||||
{{ $t('message.newScheduler') }}
|
{{ t('message.newScheduler') }}
|
||||||
<span
|
<span
|
||||||
class="btn btn-clear"
|
class="btn btn-clear"
|
||||||
:title="$t('word.close')"
|
:title="t('word.close')"
|
||||||
@mousedown.left.stop
|
@mousedown.left.stop
|
||||||
@click.stop="closeTab(element)"
|
@click.stop="closeTab(element)"
|
||||||
/>
|
/>
|
||||||
@@ -231,11 +231,11 @@
|
|||||||
@dblclick="openAsPermanentTab(element)"
|
@dblclick="openAsPermanentTab(element)"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
|
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
|
||||||
<span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
|
<span :title="`${t('word.settings').toUpperCase()}: ${t(`word.${element.elementType}`)}`">
|
||||||
<span class=" text-italic">{{ cutText(element.elementName) }}</span>
|
<span class=" text-italic">{{ cutText(element.elementName, 20, true) }}</span>
|
||||||
<span
|
<span
|
||||||
class="btn btn-clear"
|
class="btn btn-clear"
|
||||||
:title="$t('word.close')"
|
:title="t('word.close')"
|
||||||
@mousedown.left.stop
|
@mousedown.left.stop
|
||||||
@click.stop="closeTab(element)"
|
@click.stop="closeTab(element)"
|
||||||
/>
|
/>
|
||||||
@@ -248,11 +248,11 @@
|
|||||||
:class="{'badge': element.isChanged}"
|
:class="{'badge': element.isChanged}"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
|
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
|
||||||
<span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
|
<span :title="`${t('word.settings').toUpperCase()}: ${t(`word.${element.elementType}`)}`">
|
||||||
{{ cutText(element.elementName) }}
|
{{ cutText(element.elementName, 20, true) }}
|
||||||
<span
|
<span
|
||||||
class="btn btn-clear"
|
class="btn btn-clear"
|
||||||
:title="$t('word.close')"
|
:title="t('word.close')"
|
||||||
@mousedown.left.stop
|
@mousedown.left.stop
|
||||||
@click.stop="closeTab(element)"
|
@click.stop="closeTab(element)"
|
||||||
/>
|
/>
|
||||||
@@ -268,7 +268,7 @@
|
|||||||
<a
|
<a
|
||||||
class="tab-link workspace-tools-link dropdown-toggle"
|
class="tab-link workspace-tools-link dropdown-toggle"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
:title="$t('word.tools')"
|
:title="t('word.tools')"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-tools" />
|
<i class="mdi mdi-24px mdi-tools" />
|
||||||
</a>
|
</a>
|
||||||
@@ -276,7 +276,13 @@
|
|||||||
<li class="menu-item">
|
<li class="menu-item">
|
||||||
<a class="c-hand p-vcentered" @click="showProcessesModal">
|
<a class="c-hand p-vcentered" @click="showProcessesModal">
|
||||||
<i class="mdi mdi-memory mr-1 tool-icon" />
|
<i class="mdi mdi-memory mr-1 tool-icon" />
|
||||||
<span>{{ $t('message.processesList') }}</span>
|
<span>{{ t('message.processesList') }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="menu-item">
|
||||||
|
<a class="c-hand p-vcentered" @click="toggleConsole">
|
||||||
|
<i class="mdi mdi-console-line mr-1 tool-icon" />
|
||||||
|
<span>{{ t('word.console') }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
@@ -286,7 +292,7 @@
|
|||||||
>
|
>
|
||||||
<a class="c-hand p-vcentered disabled">
|
<a class="c-hand p-vcentered disabled">
|
||||||
<i class="mdi mdi-shape mr-1 tool-icon" />
|
<i class="mdi mdi-shape mr-1 tool-icon" />
|
||||||
<span>{{ $t('word.variables') }}</span>
|
<span>{{ t('word.variables') }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
@@ -296,7 +302,7 @@
|
|||||||
>
|
>
|
||||||
<a class="c-hand p-vcentered disabled">
|
<a class="c-hand p-vcentered disabled">
|
||||||
<i class="mdi mdi-account-group mr-1 tool-icon" />
|
<i class="mdi mdi-account-group mr-1 tool-icon" />
|
||||||
<span>{{ $t('message.manageUsers') }}</span>
|
<span>{{ t('message.manageUsers') }}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -306,7 +312,7 @@
|
|||||||
<li class="tab-item">
|
<li class="tab-item">
|
||||||
<a
|
<a
|
||||||
class="tab-add"
|
class="tab-add"
|
||||||
:title="$t('message.openNewTab')"
|
:title="t('message.openNewTab')"
|
||||||
@click="addQueryTab"
|
@click="addQueryTab"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-plus" />
|
<i class="mdi mdi-24px mdi-plus" />
|
||||||
@@ -320,7 +326,7 @@
|
|||||||
v-if="tab.type ==='query'"
|
v-if="tab.type ==='query'"
|
||||||
:tab-uid="tab.uid"
|
:tab-uid="tab.uid"
|
||||||
:tab="tab"
|
:tab="tab"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid && isSelected"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
/>
|
/>
|
||||||
<WorkspaceTabTable
|
<WorkspaceTabTable
|
||||||
@@ -328,7 +334,7 @@
|
|||||||
v-once
|
v-once
|
||||||
:tab-uid="tab.uid"
|
:tab-uid="tab.uid"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid && isSelected"
|
||||||
:table="tab.elementName"
|
:table="tab.elementName"
|
||||||
:schema="tab.schema"
|
:schema="tab.schema"
|
||||||
:element-type="tab.elementType"
|
:element-type="tab.elementType"
|
||||||
@@ -338,14 +344,14 @@
|
|||||||
:tab-uid="tab.uid"
|
:tab-uid="tab.uid"
|
||||||
:tab="tab"
|
:tab="tab"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid && isSelected"
|
||||||
:schema="tab.schema"
|
:schema="tab.schema"
|
||||||
/>
|
/>
|
||||||
<WorkspaceTabPropsTable
|
<WorkspaceTabPropsTable
|
||||||
v-else-if="tab.type === 'table-props'"
|
v-else-if="tab.type === 'table-props'"
|
||||||
:tab-uid="tab.uid"
|
:tab-uid="tab.uid"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid && isSelected"
|
||||||
:table="tab.elementName"
|
:table="tab.elementName"
|
||||||
:schema="tab.schema"
|
:schema="tab.schema"
|
||||||
/>
|
/>
|
||||||
@@ -354,13 +360,13 @@
|
|||||||
:tab-uid="tab.uid"
|
:tab-uid="tab.uid"
|
||||||
:tab="tab"
|
:tab="tab"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid && isSelected"
|
||||||
:schema="tab.schema"
|
:schema="tab.schema"
|
||||||
/>
|
/>
|
||||||
<WorkspaceTabPropsView
|
<WorkspaceTabPropsView
|
||||||
v-else-if="tab.type === 'view-props'"
|
v-else-if="tab.type === 'view-props'"
|
||||||
:tab-uid="tab.uid"
|
:tab-uid="tab.uid"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid && isSelected"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:view="tab.elementName"
|
:view="tab.elementName"
|
||||||
:schema="tab.schema"
|
:schema="tab.schema"
|
||||||
@@ -370,7 +376,7 @@
|
|||||||
:tab-uid="tab.uid"
|
:tab-uid="tab.uid"
|
||||||
:tab="tab"
|
:tab="tab"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid && isSelected"
|
||||||
:trigger="tab.elementName"
|
:trigger="tab.elementName"
|
||||||
:schema="tab.schema"
|
:schema="tab.schema"
|
||||||
/>
|
/>
|
||||||
@@ -378,7 +384,7 @@
|
|||||||
v-else-if="['temp-trigger-props', 'trigger-props'].includes(tab.type)"
|
v-else-if="['temp-trigger-props', 'trigger-props'].includes(tab.type)"
|
||||||
:tab-uid="tab.uid"
|
:tab-uid="tab.uid"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid && isSelected"
|
||||||
:trigger="tab.elementName"
|
:trigger="tab.elementName"
|
||||||
:schema="tab.schema"
|
:schema="tab.schema"
|
||||||
/>
|
/>
|
||||||
@@ -387,7 +393,7 @@
|
|||||||
:tab-uid="tab.uid"
|
:tab-uid="tab.uid"
|
||||||
:tab="tab"
|
:tab="tab"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid && isSelected"
|
||||||
:trigger="tab.elementName"
|
:trigger="tab.elementName"
|
||||||
:schema="tab.schema"
|
:schema="tab.schema"
|
||||||
/>
|
/>
|
||||||
@@ -395,7 +401,7 @@
|
|||||||
v-else-if="['temp-trigger-function-props', 'trigger-function-props'].includes(tab.type)"
|
v-else-if="['temp-trigger-function-props', 'trigger-function-props'].includes(tab.type)"
|
||||||
:tab-uid="tab.uid"
|
:tab-uid="tab.uid"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid && isSelected"
|
||||||
:function="tab.elementName"
|
:function="tab.elementName"
|
||||||
:schema="tab.schema"
|
:schema="tab.schema"
|
||||||
/>
|
/>
|
||||||
@@ -404,7 +410,7 @@
|
|||||||
:tab-uid="tab.uid"
|
:tab-uid="tab.uid"
|
||||||
:tab="tab"
|
:tab="tab"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid && isSelected"
|
||||||
:trigger="tab.elementName"
|
:trigger="tab.elementName"
|
||||||
:schema="tab.schema"
|
:schema="tab.schema"
|
||||||
/>
|
/>
|
||||||
@@ -412,7 +418,7 @@
|
|||||||
v-else-if="['temp-routine-props', 'routine-props'].includes(tab.type)"
|
v-else-if="['temp-routine-props', 'routine-props'].includes(tab.type)"
|
||||||
:tab-uid="tab.uid"
|
:tab-uid="tab.uid"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid && isSelected"
|
||||||
:routine="tab.elementName"
|
:routine="tab.elementName"
|
||||||
:schema="tab.schema"
|
:schema="tab.schema"
|
||||||
/>
|
/>
|
||||||
@@ -421,7 +427,7 @@
|
|||||||
:tab-uid="tab.uid"
|
:tab-uid="tab.uid"
|
||||||
:tab="tab"
|
:tab="tab"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid && isSelected"
|
||||||
:trigger="tab.elementName"
|
:trigger="tab.elementName"
|
||||||
:schema="tab.schema"
|
:schema="tab.schema"
|
||||||
/>
|
/>
|
||||||
@@ -429,7 +435,7 @@
|
|||||||
v-else-if="['temp-function-props', 'function-props'].includes(tab.type)"
|
v-else-if="['temp-function-props', 'function-props'].includes(tab.type)"
|
||||||
:tab-uid="tab.uid"
|
:tab-uid="tab.uid"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid && isSelected"
|
||||||
:function="tab.elementName"
|
:function="tab.elementName"
|
||||||
:schema="tab.schema"
|
:schema="tab.schema"
|
||||||
/>
|
/>
|
||||||
@@ -438,7 +444,7 @@
|
|||||||
:tab-uid="tab.uid"
|
:tab-uid="tab.uid"
|
||||||
:tab="tab"
|
:tab="tab"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid && isSelected"
|
||||||
:trigger="tab.elementName"
|
:trigger="tab.elementName"
|
||||||
:schema="tab.schema"
|
:schema="tab.schema"
|
||||||
/>
|
/>
|
||||||
@@ -446,13 +452,14 @@
|
|||||||
v-else-if="['temp-scheduler-props', 'scheduler-props'].includes(tab.type)"
|
v-else-if="['temp-scheduler-props', 'scheduler-props'].includes(tab.type)"
|
||||||
:tab-uid="tab.uid"
|
:tab-uid="tab.uid"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:is-selected="selectedTab === tab.uid && isSelected"
|
||||||
:scheduler="tab.elementName"
|
:scheduler="tab.elementName"
|
||||||
:schema="tab.schema"
|
:schema="tab.schema"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
<WorkspaceQueryConsole v-if="isConsoleOpen(workspace.uid)" :uid="workspace.uid" />
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="connection-panel-wrapper">
|
<div v-else class="connection-panel-wrapper p-relative">
|
||||||
<WorkspaceEditConnectionPanel :connection="connection" />
|
<WorkspaceEditConnectionPanel :connection="connection" />
|
||||||
</div>
|
</div>
|
||||||
<ModalProcessesList
|
<ModalProcessesList
|
||||||
@@ -470,18 +477,22 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onBeforeUnmount, Prop, ref, watch } from 'vue';
|
import { ipcRenderer } from 'electron';
|
||||||
|
import { computed, onMounted, Prop, ref, watch } from 'vue';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import * as Draggable from 'vuedraggable';
|
import * as Draggable from 'vuedraggable';
|
||||||
import Connection from '@/ipc-api/Connection';
|
import Connection from '@/ipc-api/Connection';
|
||||||
import { useWorkspacesStore, WorkspaceTab } from '@/stores/workspaces';
|
import { useWorkspacesStore, WorkspaceTab } from '@/stores/workspaces';
|
||||||
|
import { useConsoleStore } from '@/stores/console';
|
||||||
import { ConnectionParams } from 'common/interfaces/antares';
|
import { ConnectionParams } from 'common/interfaces/antares';
|
||||||
|
import { useFilters } from '@/composables/useFilters';
|
||||||
|
|
||||||
import WorkspaceEmptyState from '@/components/WorkspaceEmptyState.vue';
|
import WorkspaceEmptyState from '@/components/WorkspaceEmptyState.vue';
|
||||||
import WorkspaceExploreBar from '@/components/WorkspaceExploreBar.vue';
|
import WorkspaceExploreBar from '@/components/WorkspaceExploreBar.vue';
|
||||||
import WorkspaceEditConnectionPanel from '@/components/WorkspaceEditConnectionPanel.vue';
|
import WorkspaceEditConnectionPanel from '@/components/WorkspaceEditConnectionPanel.vue';
|
||||||
import WorkspaceTabQuery from '@/components/WorkspaceTabQuery.vue';
|
import WorkspaceTabQuery from '@/components/WorkspaceTabQuery.vue';
|
||||||
import WorkspaceTabTable from '@/components/WorkspaceTabTable.vue';
|
import WorkspaceTabTable from '@/components/WorkspaceTabTable.vue';
|
||||||
|
import WorkspaceQueryConsole from '@/components/WorkspaceQueryConsole.vue';
|
||||||
|
|
||||||
import WorkspaceTabNewTable from '@/components/WorkspaceTabNewTable.vue';
|
import WorkspaceTabNewTable from '@/components/WorkspaceTabNewTable.vue';
|
||||||
import WorkspaceTabNewView from '@/components/WorkspaceTabNewView.vue';
|
import WorkspaceTabNewView from '@/components/WorkspaceTabNewView.vue';
|
||||||
@@ -500,7 +511,11 @@ import WorkspaceTabPropsFunction from '@/components/WorkspaceTabPropsFunction.vu
|
|||||||
import WorkspaceTabPropsScheduler from '@/components/WorkspaceTabPropsScheduler.vue';
|
import WorkspaceTabPropsScheduler from '@/components/WorkspaceTabPropsScheduler.vue';
|
||||||
import ModalProcessesList from '@/components/ModalProcessesList.vue';
|
import ModalProcessesList from '@/components/ModalProcessesList.vue';
|
||||||
import ModalDiscardChanges from '@/components/ModalDiscardChanges.vue';
|
import ModalDiscardChanges from '@/components/ModalDiscardChanges.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const { cutText } = useFilters();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
|
||||||
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
|
||||||
@@ -517,6 +532,11 @@ const {
|
|||||||
selectPrevTab
|
selectPrevTab
|
||||||
} = workspacesStore;
|
} = workspacesStore;
|
||||||
|
|
||||||
|
const consoleStore = useConsoleStore();
|
||||||
|
|
||||||
|
const { isConsoleOpen } = storeToRefs(consoleStore);
|
||||||
|
const { toggleConsole } = consoleStore;
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
connection: Object as Prop<ConnectionParams>
|
connection: Object as Prop<ConnectionParams>
|
||||||
});
|
});
|
||||||
@@ -568,46 +588,13 @@ watch(queryTabs, (newVal, oldVal) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const addQueryTab = () => {
|
const addQueryTab = () => {
|
||||||
newTab({ uid: props.connection.uid, type: 'query' });
|
newTab({ uid: props.connection.uid, type: 'query', schema: workspace.value.breadcrumbs.schema });
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSelectedTab = () => {
|
const getSelectedTab = () => {
|
||||||
return workspace.value.tabs.find(tab => tab.uid === selectedTab.value);
|
return workspace.value.tabs.find(tab => tab.uid === selectedTab.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKey = (e: KeyboardEvent) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
|
|
||||||
if (!isSelected.value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ((e.ctrlKey || e.metaKey) && e.key === 't' && !e.altKey) { // CTRL|Command + t
|
|
||||||
addQueryTab();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((e.ctrlKey || e.metaKey) && e.key === 'w' && !e.altKey) { // CTRL|Command + w
|
|
||||||
const currentTab = getSelectedTab();
|
|
||||||
if (currentTab)
|
|
||||||
closeTab(currentTab);
|
|
||||||
}
|
|
||||||
|
|
||||||
// select next tab
|
|
||||||
if (e.altKey && (e.ctrlKey || e.metaKey) && e.key === 'ArrowRight')
|
|
||||||
selectNextTab({ uid: props.connection.uid });
|
|
||||||
|
|
||||||
// select prev tab
|
|
||||||
if (e.altKey && (e.ctrlKey || e.metaKey) && e.key === 'ArrowLeft')
|
|
||||||
selectPrevTab({ uid: props.connection.uid });
|
|
||||||
|
|
||||||
// select tab by index (range 1-9). CTRL|CMD number
|
|
||||||
if ((e.ctrlKey || e.metaKey) && !e.altKey && e.keyCode >= 49 && e.keyCode <= 57) {
|
|
||||||
const newIndex = parseInt(e.key) - 1;
|
|
||||||
|
|
||||||
if (workspace.value.tabs[newIndex])
|
|
||||||
selectTab({ uid: props.connection.uid, tab: workspace.value.tabs[newIndex].uid });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const openAsPermanentTab = (tab: WorkspaceTab) => {
|
const openAsPermanentTab = (tab: WorkspaceTab) => {
|
||||||
const permanentTabs = {
|
const permanentTabs = {
|
||||||
table: 'data',
|
table: 'data',
|
||||||
@@ -658,24 +645,43 @@ const addWheelEvent = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const cutText = (string: string) => {
|
|
||||||
const limit = 20;
|
|
||||||
const escapedString = string.replace(/\s{2,}/g, ' ');
|
|
||||||
if (escapedString.length > limit)
|
|
||||||
return `${escapedString.substr(0, limit)}...`;
|
|
||||||
return escapedString;
|
|
||||||
};
|
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
window.addEventListener('keydown', onKey);
|
|
||||||
await addWorkspace(props.connection.uid);
|
await addWorkspace(props.connection.uid);
|
||||||
const isInitiated = await Connection.checkConnection(props.connection.uid);
|
const isInitiated = await Connection.checkConnection(props.connection.uid);
|
||||||
if (isInitiated)
|
if (isInitiated)
|
||||||
connectWorkspace(props.connection);
|
connectWorkspace(props.connection);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onMounted(() => {
|
||||||
window.removeEventListener('keydown', onKey);
|
ipcRenderer.on('open-new-tab', () => {
|
||||||
|
if (!isSelected.value) return;
|
||||||
|
addQueryTab();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('close-tab', () => {
|
||||||
|
if (!isSelected.value) return;
|
||||||
|
const currentTab = getSelectedTab();
|
||||||
|
if (currentTab)
|
||||||
|
closeTab(currentTab);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('next-tab', () => {
|
||||||
|
if (!isSelected.value) return;
|
||||||
|
selectNextTab({ uid: props.connection.uid });
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('prev-tab', () => {
|
||||||
|
if (!isSelected.value) return;
|
||||||
|
selectPrevTab({ uid: props.connection.uid });
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = 1; i <= 9; i++) {
|
||||||
|
ipcRenderer.on(`select-tab-${i}`, () => {
|
||||||
|
if (!isSelected.value) return;
|
||||||
|
if (workspace.value.tabs[i-1])
|
||||||
|
selectTab({ uid: props.connection.uid, tab: workspace.value.tabs[i-1].uid });
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -685,8 +691,9 @@ onBeforeUnmount(() => {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
.workspace-tabs {
|
.workspace-tabs {
|
||||||
overflow: hidden;
|
overflow-y: hidden;
|
||||||
height: calc(100vh - #{$excluding-size});
|
height: calc(100vh - #{$excluding-size});
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.tab-block {
|
.tab-block {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
@@ -553,14 +553,15 @@ setDefaults();
|
|||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (firstInput.value) firstInput.value.focus();
|
if (firstInput.value) firstInput.value.focus();
|
||||||
}, 20);
|
}, 200);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.connection-panel {
|
.connection-panel {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 0.5rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
|
||||||
.panel {
|
.panel {
|
||||||
min-width: 450px;
|
min-width: 450px;
|
||||||
|
@@ -551,7 +551,8 @@ localConnection.value = JSON.parse(JSON.stringify(props.connection));
|
|||||||
.connection-panel {
|
.connection-panel {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 0.5rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
|
||||||
.panel {
|
.panel {
|
||||||
min-width: 450px;
|
min-width: 450px;
|
||||||
|
@@ -15,18 +15,18 @@
|
|||||||
<i
|
<i
|
||||||
v-if="customizations.schemas"
|
v-if="customizations.schemas"
|
||||||
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')"
|
||||||
@click="showNewDBModal"
|
@click="showNewDBModal"
|
||||||
/>
|
/>
|
||||||
<i
|
<i
|
||||||
class="mdi mdi-18px mdi-refresh c-hand mr-2"
|
class="mdi mdi-18px mdi-refresh c-hand mr-2"
|
||||||
:class="{'rotate':isRefreshing}"
|
:class="{'rotate':isRefreshing}"
|
||||||
:title="$t('word.refresh')"
|
:title="t('word.refresh')"
|
||||||
@click="refresh"
|
@click="refresh"
|
||||||
/>
|
/>
|
||||||
<i
|
<i
|
||||||
class="mdi mdi-18px mdi-power 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>
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
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 v-if="!searchTerm" class="form-icon mdi mdi-magnify mdi-18px" />
|
<i v-if="!searchTerm" class="form-icon mdi mdi-magnify mdi-18px" />
|
||||||
<i
|
<i
|
||||||
@@ -133,6 +133,9 @@ import TableContext from '@/components/WorkspaceExploreBarTableContext.vue';
|
|||||||
import MiscContext from '@/components/WorkspaceExploreBarMiscContext.vue';
|
import MiscContext from '@/components/WorkspaceExploreBarMiscContext.vue';
|
||||||
import MiscFolderContext from '@/components/WorkspaceExploreBarMiscFolderContext.vue';
|
import MiscFolderContext from '@/components/WorkspaceExploreBarMiscFolderContext.vue';
|
||||||
import ModalNewSchema from '@/components/ModalNewSchema.vue';
|
import ModalNewSchema from '@/components/ModalNewSchema.vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
connection: Object,
|
connection: Object,
|
||||||
@@ -235,17 +238,7 @@ const refresh = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const explorebarSearch = (e: KeyboardEvent) => {
|
const explorebarSearch = () => {
|
||||||
if (e.code === 'Backspace') {
|
|
||||||
e.preventDefault();
|
|
||||||
if (searchTerm.value.length)
|
|
||||||
searchTerm.value = searchTerm.value.slice(0, -1);
|
|
||||||
else
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (e.key.length > 1)// Prevent non-alphanumerics
|
|
||||||
return;
|
|
||||||
|
|
||||||
searchInput.value.focus();
|
searchInput.value.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
class="context-element"
|
class="context-element"
|
||||||
@click="runElementCheck"
|
@click="runElementCheck"
|
||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ $t('word.run') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ t('word.run') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="selectedMisc.type === 'trigger' && customizations.triggerEnableDisable"
|
v-if="selectedMisc.type === 'trigger' && customizations.triggerEnableDisable"
|
||||||
@@ -16,10 +16,10 @@
|
|||||||
@click="toggleTrigger"
|
@click="toggleTrigger"
|
||||||
>
|
>
|
||||||
<span v-if="!selectedMisc.enabled" class="d-flex">
|
<span v-if="!selectedMisc.enabled" class="d-flex">
|
||||||
<i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ $t('word.enable') }}
|
<i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ t('word.enable') }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else class="d-flex">
|
<span v-else class="d-flex">
|
||||||
<i class="mdi mdi-18px mdi-pause text-light pr-1" /> {{ $t('word.disable') }}
|
<i class="mdi mdi-18px mdi-pause text-light pr-1" /> {{ t('word.disable') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -28,14 +28,14 @@
|
|||||||
@click="toggleScheduler"
|
@click="toggleScheduler"
|
||||||
>
|
>
|
||||||
<span v-if="!selectedMisc.enabled" class="d-flex">
|
<span v-if="!selectedMisc.enabled" class="d-flex">
|
||||||
<i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ $t('word.enable') }}
|
<i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ t('word.enable') }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else class="d-flex">
|
<span v-else class="d-flex">
|
||||||
<i class="mdi mdi-18px mdi-pause text-light pr-1" /> {{ $t('word.disable') }}
|
<i class="mdi mdi-18px mdi-pause text-light pr-1" /> {{ t('word.disable') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element" @click="showDeleteModal">
|
<div class="context-element" @click="showDeleteModal">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table-remove text-light pr-1" /> {{ $t('word.delete') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table-remove text-light pr-1" /> {{ t('word.delete') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
v-if="isDeleteModal"
|
v-if="isDeleteModal"
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
{{ $t('message.deleteCorfirm') }} "<b>{{ selectedMisc.name }}</b>"?
|
{{ t('message.deleteCorfirm') }} "<b>{{ selectedMisc.name }}</b>"?
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
@@ -188,8 +188,6 @@ const deleteMisc = async () => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(res);
|
|
||||||
|
|
||||||
const { status, response } = res;
|
const { status, response } = res;
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
@@ -219,7 +217,7 @@ const runElementCheck = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const runElement = (params: string[]) => {
|
const runElement = (params: string[]) => {
|
||||||
if (props.selectedMisc.type === 'procedure')
|
if (['procedure', 'routine'].includes(props.selectedMisc.type))
|
||||||
runRoutine(params);
|
runRoutine(params);
|
||||||
else if (props.selectedMisc.type === 'function')
|
else if (props.selectedMisc.type === 'function')
|
||||||
runFunction(params);
|
runFunction(params);
|
||||||
|
@@ -580,7 +580,7 @@ defineExpose({ selectSchema, schemaAccordion });
|
|||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 0.8;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
@close-context="closeContext"
|
@close-context="closeContext"
|
||||||
>
|
>
|
||||||
<div class="context-element">
|
<div class="context-element">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-plus text-light pr-1" /> {{ $t('word.add') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-plus text-light pr-1" /> {{ t('word.add') }}</span>
|
||||||
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
|
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
|
||||||
<div class="context-submenu">
|
<div class="context-submenu">
|
||||||
<div
|
<div
|
||||||
@@ -12,49 +12,49 @@
|
|||||||
class="context-element"
|
class="context-element"
|
||||||
@click="openCreateTableTab"
|
@click="openCreateTableTab"
|
||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table text-light pr-1" /> {{ $t('word.table') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table text-light pr-1" /> {{ t('word.table') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="workspace.customizations.viewAdd"
|
v-if="workspace.customizations.viewAdd"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@click="openCreateViewTab"
|
@click="openCreateViewTab"
|
||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table-eye text-light pr-1" /> {{ $t('word.view') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table-eye text-light pr-1" /> {{ t('word.view') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="workspace.customizations.triggerAdd"
|
v-if="workspace.customizations.triggerAdd"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@click="openCreateTriggerTab"
|
@click="openCreateTriggerTab"
|
||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ $tc('word.trigger', 1) }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ t('word.trigger', 1) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="workspace.customizations.routineAdd"
|
v-if="workspace.customizations.routineAdd"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@click="openCreateRoutineTab"
|
@click="openCreateRoutineTab"
|
||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-sync-circle pr-1" /> {{ $tc('word.storedRoutine', 1) }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-sync-circle pr-1" /> {{ t('word.storedRoutine', 1) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="workspace.customizations.functionAdd"
|
v-if="workspace.customizations.functionAdd"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@click="openCreateFunctionTab"
|
@click="openCreateFunctionTab"
|
||||||
>
|
>
|
||||||
<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" /> {{ t('word.function', 1) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="workspace.customizations.triggerFunctionAdd"
|
v-if="workspace.customizations.triggerFunctionAdd"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@click="openCreateTriggerFunctionTab"
|
@click="openCreateTriggerFunctionTab"
|
||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-cog-clockwise pr-1" /> {{ $tc('word.triggerFunction', 1) }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-cog-clockwise pr-1" /> {{ t('word.triggerFunction', 1) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="workspace.customizations.schedulerAdd"
|
v-if="workspace.customizations.schedulerAdd"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@click="openCreateSchedulerTab"
|
@click="openCreateSchedulerTab"
|
||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ $tc('word.scheduler', 1) }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ t('word.scheduler', 1) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -63,28 +63,28 @@
|
|||||||
class="context-element"
|
class="context-element"
|
||||||
@click="showExportSchemaModal"
|
@click="showExportSchemaModal"
|
||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-database-arrow-down text-light pr-1" /> {{ $t('word.export') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-database-arrow-down text-light pr-1" /> {{ t('word.export') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="workspace.customizations.schemaImport"
|
v-if="workspace.customizations.schemaImport"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@click="initImport"
|
@click="initImport"
|
||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-database-arrow-up text-light pr-1" /> {{ $t('word.import') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-database-arrow-up text-light pr-1" /> {{ t('word.import') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="workspace.customizations.schemaEdit"
|
v-if="workspace.customizations.schemaEdit"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@click="showEditModal"
|
@click="showEditModal"
|
||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-database-edit text-light pr-1" /> {{ $t('word.edit') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-database-edit text-light pr-1" /> {{ t('word.edit') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="workspace.customizations.schemaDrop"
|
v-if="workspace.customizations.schemaDrop"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@click="showDeleteModal"
|
@click="showDeleteModal"
|
||||||
>
|
>
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-database-remove text-light pr-1" /> {{ $t('word.delete') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-database-remove text-light pr-1" /> {{ t('word.delete') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
@@ -95,12 +95,12 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-database-remove mr-1" />
|
<i class="mdi mdi-24px mdi-database-remove mr-1" />
|
||||||
<span class="cut-text">{{ $t('message.deleteSchema') }}</span>
|
<span class="cut-text">{{ t('message.deleteSchema') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #body>
|
<template #body>
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
{{ $t('message.deleteCorfirm') }} "<b>{{ selectedSchema }}</b>"?
|
{{ t('message.deleteCorfirm') }} "<b>{{ selectedSchema }}</b>"?
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ConfirmModal>
|
</ConfirmModal>
|
||||||
@@ -135,6 +135,9 @@ import ModalExportSchema from '@/components/ModalExportSchema.vue';
|
|||||||
import ModalImportSchema from '@/components/ModalImportSchema.vue';
|
import ModalImportSchema from '@/components/ModalImportSchema.vue';
|
||||||
import Schema from '@/ipc-api/Schema';
|
import Schema from '@/ipc-api/Schema';
|
||||||
import Application from '@/ipc-api/Application';
|
import Application from '@/ipc-api/Application';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
contextEvent: MouseEvent,
|
contextEvent: MouseEvent,
|
||||||
|
180
src/renderer/components/WorkspaceQueryConsole.vue
Normal file
180
src/renderer/components/WorkspaceQueryConsole.vue
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
ref="wrapper"
|
||||||
|
class="query-console-wrapper"
|
||||||
|
@mouseenter="isHover = true"
|
||||||
|
@mouseleave="isHover = false"
|
||||||
|
>
|
||||||
|
<div ref="resizer" class="query-console-resizer" />
|
||||||
|
<div
|
||||||
|
id="query-console"
|
||||||
|
ref="queryConsole"
|
||||||
|
class="query-console column col-12"
|
||||||
|
:style="{height: localHeight ? localHeight+'px' : ''}"
|
||||||
|
>
|
||||||
|
<div class="query-console-header">
|
||||||
|
<div>{{ t('word.console') }}</div>
|
||||||
|
<button class="btn btn-clear mr-1" @click="resizeConsole(0)" />
|
||||||
|
</div>
|
||||||
|
<div ref="queryConsoleBody" class="query-console-body">
|
||||||
|
<div
|
||||||
|
v-for="(wLog, i) in workspaceLogs"
|
||||||
|
:key="i"
|
||||||
|
class="query-console-log"
|
||||||
|
tabindex="0"
|
||||||
|
@contextmenu.prevent="contextMenu($event, wLog)"
|
||||||
|
>
|
||||||
|
<span class="type-datetime">{{ moment(wLog.date).format('HH:mm:ss') }}</span>: <code class="query-console-log-sql">{{ wLog.sql }}</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<BaseContextMenu
|
||||||
|
v-if="isContext"
|
||||||
|
:context-event="contextEvent"
|
||||||
|
@close-context="isContext = false"
|
||||||
|
>
|
||||||
|
<div class="context-element" @click="copyQuery">
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-content-copy text-light pr-1" /> {{ t('word.copy') }}</span>
|
||||||
|
</div>
|
||||||
|
</BaseContextMenu>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, nextTick, onMounted, ref, Ref, watch } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import * as moment from 'moment';
|
||||||
|
import { useConsoleStore } from '@/stores/console';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import BaseContextMenu from '@/components/BaseContextMenu.vue';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const consoleStore = useConsoleStore();
|
||||||
|
|
||||||
|
const { resizeConsole, getLogsByWorkspace } = consoleStore;
|
||||||
|
const { consoleHeight } = storeToRefs(consoleStore);
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
uid: String
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapper: Ref<HTMLInputElement> = ref(null);
|
||||||
|
const queryConsole: Ref<HTMLInputElement> = ref(null);
|
||||||
|
const queryConsoleBody: Ref<HTMLInputElement> = ref(null);
|
||||||
|
const resizer: Ref<HTMLInputElement> = ref(null);
|
||||||
|
const localHeight = ref(250);
|
||||||
|
const isHover = ref(false);
|
||||||
|
const isContext = ref(false);
|
||||||
|
const contextQuery: Ref<string> = ref(null);
|
||||||
|
const contextEvent: Ref<MouseEvent> = ref(null);
|
||||||
|
|
||||||
|
const resize = (e: MouseEvent) => {
|
||||||
|
const el = queryConsole.value;
|
||||||
|
let consoleHeight = el.getBoundingClientRect().bottom - e.pageY;
|
||||||
|
if (consoleHeight > 400) consoleHeight = 400;
|
||||||
|
localHeight.value = consoleHeight;
|
||||||
|
};
|
||||||
|
|
||||||
|
const workspaceLogs = computed(() => {
|
||||||
|
return getLogsByWorkspace(props.uid);
|
||||||
|
});
|
||||||
|
|
||||||
|
const stopResize = () => {
|
||||||
|
if (localHeight.value < 0) localHeight.value = 0;
|
||||||
|
resizeConsole(localHeight.value);
|
||||||
|
window.removeEventListener('mousemove', resize);
|
||||||
|
window.removeEventListener('mouseup', stopResize);
|
||||||
|
};
|
||||||
|
|
||||||
|
const contextMenu = (event: MouseEvent, wLog: {date: Date; sql: string}) => {
|
||||||
|
contextEvent.value = event;
|
||||||
|
contextQuery.value = wLog.sql;
|
||||||
|
isContext.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const copyQuery = () => {
|
||||||
|
navigator.clipboard.writeText(contextQuery.value);
|
||||||
|
isContext.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(workspaceLogs, async () => {
|
||||||
|
if (!isHover.value) {
|
||||||
|
await nextTick();
|
||||||
|
queryConsoleBody.value.scrollTop = queryConsoleBody.value.scrollHeight;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
localHeight.value = consoleHeight.value;
|
||||||
|
queryConsoleBody.value.scrollTop = queryConsoleBody.value.scrollHeight;
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
resizer.value.addEventListener('mousedown', (e: MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
window.addEventListener('mousemove', resize);
|
||||||
|
window.addEventListener('mouseup', stopResize);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.query-console-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
z-index: 9;
|
||||||
|
margin-top: auto;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
.query-console-resizer {
|
||||||
|
height: 4px;
|
||||||
|
top: -1px;
|
||||||
|
width: 100%;
|
||||||
|
cursor: ns-resize;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 99;
|
||||||
|
transition: background 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba($primary-color, 50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-console {
|
||||||
|
padding: 0;
|
||||||
|
padding-bottom: $footer-height;
|
||||||
|
|
||||||
|
.query-console-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 4px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-console-body {
|
||||||
|
overflow: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
max-height: 100%;
|
||||||
|
padding: 0 6px 3px;
|
||||||
|
|
||||||
|
.query-console-log {
|
||||||
|
padding: 1px 3px;
|
||||||
|
margin: 1px 0;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
|
||||||
|
.query-console-log-sql {
|
||||||
|
font-size: 95%;
|
||||||
|
opacity: 0.8;
|
||||||
|
font-weight: 700;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
user-select: text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -7,7 +7,6 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
title="CTRL+S"
|
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
@@ -179,6 +178,8 @@
|
|||||||
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
||||||
import { Ace } from 'ace-builds';
|
import { Ace } from 'ace-builds';
|
||||||
import Functions from '@/ipc-api/Functions';
|
import Functions from '@/ipc-api/Functions';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useConsoleStore } from '@/stores/console';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import BaseLoader from '@/components/BaseLoader.vue';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
@@ -187,6 +188,7 @@ import WorkspaceTabPropsFunctionParamsModal from '@/components/WorkspaceTabProps
|
|||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
import { FunctionInfos, FunctionParam } from 'common/interfaces/antares';
|
import { FunctionInfos, FunctionParam } from 'common/interfaces/antares';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -200,6 +202,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
const { consoleHeight } = storeToRefs(useConsoleStore());
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
@@ -274,8 +277,12 @@ const clearChanges = () => {
|
|||||||
|
|
||||||
const resizeQueryEditor = () => {
|
const resizeQueryEditor = () => {
|
||||||
if (queryEditor.value) {
|
if (queryEditor.value) {
|
||||||
|
let sizeToSubtract = 0;
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
if (footer) sizeToSubtract += footer.offsetHeight;
|
||||||
|
sizeToSubtract += consoleHeight.value;
|
||||||
|
|
||||||
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - sizeToSubtract;
|
||||||
editorHeight.value = size;
|
editorHeight.value = size;
|
||||||
queryEditor.value.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
@@ -293,14 +300,10 @@ const hideParamsModal = () => {
|
|||||||
isParamsModal.value = false;
|
isParamsModal.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const saveContentListener = () => {
|
||||||
if (props.isSelected) {
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
e.stopPropagation();
|
if (props.isSelected && !hasModalOpen && isChanged.value)
|
||||||
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
saveChanges();
|
||||||
if (isChanged.value)
|
|
||||||
saveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => props.isSelected, (val) => {
|
watch(() => props.isSelected, (val) => {
|
||||||
@@ -311,6 +314,10 @@ watch(isChanged, (val) => {
|
|||||||
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(consoleHeight, () => {
|
||||||
|
resizeQueryEditor();
|
||||||
|
});
|
||||||
|
|
||||||
originalFunction.value = {
|
originalFunction.value = {
|
||||||
sql: customizations.value.functionSql,
|
sql: customizations.value.functionSql,
|
||||||
language: customizations.value.languages ? customizations.value.languages[0] : null,
|
language: customizations.value.languages ? customizations.value.languages[0] : null,
|
||||||
@@ -330,19 +337,19 @@ setTimeout(() => {
|
|||||||
resizeQueryEditor();
|
resizeQueryEditor();
|
||||||
}, 50);
|
}, 50);
|
||||||
|
|
||||||
window.addEventListener('keydown', onKey);
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.isSelected)
|
if (props.isSelected)
|
||||||
changeBreadcrumbs({ schema: props.schema });
|
changeBreadcrumbs({ schema: props.schema });
|
||||||
|
|
||||||
|
ipcRenderer.on('save-content', saveContentListener);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
firstInput.value.focus();
|
firstInput.value.focus();
|
||||||
}, 100);
|
}, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('keydown', onKey);
|
ipcRenderer.removeListener('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
@@ -7,31 +7,30 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
title="CTRL+S"
|
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
<span>{{ $t('word.save') }}</span>
|
<span>{{ t('word.save') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
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" />
|
<i class="mdi mdi-24px mdi-delete-sweep mr-1" />
|
||||||
<span>{{ $t('word.clear') }}</span>
|
<span>{{ t('word.clear') }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="divider-vert py-3" />
|
<div class="divider-vert py-3" />
|
||||||
|
|
||||||
<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" />
|
<i class="mdi mdi-24px mdi-dots-horizontal mr-1" />
|
||||||
<span>{{ $t('word.parameters') }}</span>
|
<span>{{ t('word.parameters') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="workspace-query-info">
|
<div class="workspace-query-info">
|
||||||
<div class="d-flex" :title="$t('word.schema')">
|
<div class="d-flex" :title="t('word.schema')">
|
||||||
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
<i class="mdi mdi-18px mdi-database mr-1" /><b>{{ schema }}</b>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -42,7 +41,7 @@
|
|||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.name') }}
|
{{ t('word.name') }}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
ref="firstInput"
|
ref="firstInput"
|
||||||
@@ -55,7 +54,7 @@
|
|||||||
<div v-if="customizations.languages" class="column col-auto">
|
<div v-if="customizations.languages" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.language') }}
|
{{ t('word.language') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localRoutine.language"
|
v-model="localRoutine.language"
|
||||||
@@ -67,11 +66,11 @@
|
|||||||
<div v-if="customizations.definer" 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">
|
<label class="form-label">
|
||||||
{{ $t('word.definer') }}
|
{{ t('word.definer') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localRoutine.definer"
|
v-model="localRoutine.definer"
|
||||||
:options="[{value: '', name:$t('message.currentUser')}, ...workspace.users]"
|
:options="[{value: '', name:t('message.currentUser')}, ...workspace.users]"
|
||||||
:option-label="(user: any) => user.value === '' ? user.name : `${user.name}@${user.host}`"
|
:option-label="(user: any) => user.value === '' ? user.name : `${user.name}@${user.host}`"
|
||||||
:option-track-by="(user: any) => user.value === '' ? '' : `\`${user.name}\`@\`${user.host}\``"
|
:option-track-by="(user: any) => user.value === '' ? '' : `\`${user.name}\`@\`${user.host}\``"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
@@ -81,7 +80,7 @@
|
|||||||
<div v-if="customizations.comment" class="column">
|
<div v-if="customizations.comment" class="column">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('word.comment') }}
|
{{ t('word.comment') }}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
v-model="localRoutine.comment"
|
v-model="localRoutine.comment"
|
||||||
@@ -93,7 +92,7 @@
|
|||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('message.sqlSecurity') }}
|
{{ t('message.sqlSecurity') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localRoutine.security"
|
v-model="localRoutine.security"
|
||||||
@@ -105,7 +104,7 @@
|
|||||||
<div v-if="customizations.procedureDataAccess" class="column col-auto">
|
<div v-if="customizations.procedureDataAccess" class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label">
|
<label class="form-label">
|
||||||
{{ $t('message.dataAccess') }}
|
{{ t('message.dataAccess') }}
|
||||||
</label>
|
</label>
|
||||||
<BaseSelect
|
<BaseSelect
|
||||||
v-model="localRoutine.dataAccess"
|
v-model="localRoutine.dataAccess"
|
||||||
@@ -118,7 +117,7 @@
|
|||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="form-label d-invisible">.</label>
|
<label class="form-label d-invisible">.</label>
|
||||||
<label class="form-checkbox form-inline">
|
<label class="form-checkbox form-inline">
|
||||||
<input v-model="localRoutine.deterministic" type="checkbox"><i class="form-icon" /> {{ $t('word.deterministic') }}
|
<input v-model="localRoutine.deterministic" type="checkbox"><i class="form-icon" /> {{ t('word.deterministic') }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -126,7 +125,7 @@
|
|||||||
</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" />
|
||||||
<label class="form-label ml-2">{{ $t('message.routineBody') }}</label>
|
<label class="form-label ml-2">{{ t('message.routineBody') }}</label>
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
v-show="isSelected"
|
v-show="isSelected"
|
||||||
ref="queryEditor"
|
ref="queryEditor"
|
||||||
@@ -151,6 +150,8 @@
|
|||||||
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
||||||
import { Ace } from 'ace-builds';
|
import { Ace } from 'ace-builds';
|
||||||
import Routines from '@/ipc-api/Routines';
|
import Routines from '@/ipc-api/Routines';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useConsoleStore } from '@/stores/console';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import QueryEditor from '@/components/QueryEditor.vue';
|
import QueryEditor from '@/components/QueryEditor.vue';
|
||||||
@@ -158,6 +159,10 @@ import BaseLoader from '@/components/BaseLoader.vue';
|
|||||||
import WorkspaceTabPropsRoutineParamsModal from '@/components/WorkspaceTabPropsRoutineParamsModal.vue';
|
import WorkspaceTabPropsRoutineParamsModal from '@/components/WorkspaceTabPropsRoutineParamsModal.vue';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
import { FunctionParam } from 'common/interfaces/antares';
|
import { FunctionParam } from 'common/interfaces/antares';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
tabUid: String,
|
tabUid: String,
|
||||||
@@ -169,6 +174,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
const { consoleHeight } = storeToRefs(useConsoleStore());
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
@@ -243,8 +249,12 @@ const clearChanges = () => {
|
|||||||
|
|
||||||
const resizeQueryEditor = () => {
|
const resizeQueryEditor = () => {
|
||||||
if (queryEditor.value) {
|
if (queryEditor.value) {
|
||||||
|
let sizeToSubtract = 0;
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
if (footer) sizeToSubtract += footer.offsetHeight;
|
||||||
|
sizeToSubtract += consoleHeight.value;
|
||||||
|
|
||||||
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - sizeToSubtract;
|
||||||
editorHeight.value = size;
|
editorHeight.value = size;
|
||||||
queryEditor.value.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
@@ -262,14 +272,10 @@ const hideParamsModal = () => {
|
|||||||
isParamsModal.value = false;
|
isParamsModal.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const saveContentListener = () => {
|
||||||
if (props.isSelected) {
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
e.stopPropagation();
|
if (props.isSelected && !hasModalOpen && isChanged.value)
|
||||||
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
saveChanges();
|
||||||
if (isChanged.value)
|
|
||||||
saveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => props.isSelected, (val) => {
|
watch(() => props.isSelected, (val) => {
|
||||||
@@ -280,6 +286,10 @@ watch(isChanged, (val) => {
|
|||||||
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(consoleHeight, () => {
|
||||||
|
resizeQueryEditor();
|
||||||
|
});
|
||||||
|
|
||||||
originalRoutine.value = {
|
originalRoutine.value = {
|
||||||
sql: customizations.value.functionSql,
|
sql: customizations.value.functionSql,
|
||||||
language: customizations.value.languages ? customizations.value.languages[0] : null,
|
language: customizations.value.languages ? customizations.value.languages[0] : null,
|
||||||
@@ -299,19 +309,19 @@ setTimeout(() => {
|
|||||||
resizeQueryEditor();
|
resizeQueryEditor();
|
||||||
}, 50);
|
}, 50);
|
||||||
|
|
||||||
window.addEventListener('keydown', onKey);
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.isSelected)
|
if (props.isSelected)
|
||||||
changeBreadcrumbs({ schema: props.schema });
|
changeBreadcrumbs({ schema: props.schema });
|
||||||
|
|
||||||
|
ipcRenderer.on('save-content', saveContentListener);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
firstInput.value.focus();
|
firstInput.value.focus();
|
||||||
}, 100);
|
}, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('keydown', onKey);
|
ipcRenderer.removeListener('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
title="CTRL+S"
|
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
@@ -129,6 +128,8 @@ import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Prop, Ref
|
|||||||
import { Ace } from 'ace-builds';
|
import { Ace } from 'ace-builds';
|
||||||
import { ConnectionParams, EventInfos } from 'common/interfaces/antares';
|
import { ConnectionParams, EventInfos } from 'common/interfaces/antares';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useConsoleStore } from '@/stores/console';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import BaseLoader from '@/components/BaseLoader.vue';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
@@ -136,6 +137,7 @@ import QueryEditor from '@/components/QueryEditor.vue';
|
|||||||
import WorkspaceTabPropsSchedulerTimingModal from '@/components/WorkspaceTabPropsSchedulerTimingModal.vue';
|
import WorkspaceTabPropsSchedulerTimingModal from '@/components/WorkspaceTabPropsSchedulerTimingModal.vue';
|
||||||
import Schedulers from '@/ipc-api/Schedulers';
|
import Schedulers from '@/ipc-api/Schedulers';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -149,6 +151,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
const { consoleHeight } = storeToRefs(useConsoleStore());
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
@@ -233,8 +236,12 @@ const clearChanges = () => {
|
|||||||
|
|
||||||
const resizeQueryEditor = () => {
|
const resizeQueryEditor = () => {
|
||||||
if (queryEditor.value) {
|
if (queryEditor.value) {
|
||||||
|
let sizeToSubtract = 0;
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
if (footer) sizeToSubtract += footer.offsetHeight;
|
||||||
|
sizeToSubtract += consoleHeight.value;
|
||||||
|
|
||||||
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - sizeToSubtract;
|
||||||
editorHeight.value = size;
|
editorHeight.value = size;
|
||||||
queryEditor.value.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
@@ -252,14 +259,10 @@ const timingUpdate = (options: EventInfos) => {
|
|||||||
localScheduler.value = options;
|
localScheduler.value = options;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const saveContentListener = () => {
|
||||||
if (props.isSelected) {
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
e.stopPropagation();
|
if (props.isSelected && !hasModalOpen && isChanged.value)
|
||||||
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
saveChanges();
|
||||||
if (isChanged.value)
|
|
||||||
saveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => props.isSelected, (val) => {
|
watch(() => props.isSelected, (val) => {
|
||||||
@@ -270,6 +273,10 @@ watch(isChanged, (val) => {
|
|||||||
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(consoleHeight, () => {
|
||||||
|
resizeQueryEditor();
|
||||||
|
});
|
||||||
|
|
||||||
originalScheduler.value = {
|
originalScheduler.value = {
|
||||||
definer: '',
|
definer: '',
|
||||||
sql: 'BEGIN\r\n\r\nEND',
|
sql: 'BEGIN\r\n\r\nEND',
|
||||||
@@ -287,19 +294,19 @@ setTimeout(() => {
|
|||||||
resizeQueryEditor();
|
resizeQueryEditor();
|
||||||
}, 50);
|
}, 50);
|
||||||
|
|
||||||
window.addEventListener('keydown', onKey);
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.isSelected)
|
if (props.isSelected)
|
||||||
changeBreadcrumbs({ schema: props.schema });
|
changeBreadcrumbs({ schema: props.schema });
|
||||||
|
|
||||||
|
ipcRenderer.on('save-content', saveContentListener);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
firstInput.value.focus();
|
firstInput.value.focus();
|
||||||
}, 100);
|
}, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('keydown', onKey);
|
ipcRenderer.removeListener('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged || !isValid"
|
:disabled="!isChanged || !isValid"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
title="CTRL+S"
|
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
@@ -175,6 +174,7 @@ import WorkspaceTabPropsTableForeignModal from '@/components/WorkspaceTabPropsTa
|
|||||||
import WorkspaceTabNewTableEmptyState from '@/components/WorkspaceTabNewTableEmptyState.vue';
|
import WorkspaceTabNewTableEmptyState from '@/components/WorkspaceTabNewTableEmptyState.vue';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
import { ConnectionParams, TableField, TableForeign, TableIndex, TableOptions } from 'common/interfaces/antares';
|
import { ConnectionParams, TableField, TableForeign, TableIndex, TableOptions } from 'common/interfaces/antares';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -420,14 +420,10 @@ const foreignsUpdate = (foreigns: TableForeign[]) => {
|
|||||||
localKeyUsage.value = foreigns;
|
localKeyUsage.value = foreigns;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const saveContentListener = () => {
|
||||||
if (props.isSelected) {
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
e.stopPropagation();
|
if (props.isSelected && !hasModalOpen && isChanged.value)
|
||||||
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
saveChanges();
|
||||||
if (isChanged.value)
|
|
||||||
saveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => props.isSelected, (val) => {
|
watch(() => props.isSelected, (val) => {
|
||||||
@@ -447,18 +443,19 @@ tableOptions.value = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
localOptions.value = JSON.parse(JSON.stringify(tableOptions.value));
|
localOptions.value = JSON.parse(JSON.stringify(tableOptions.value));
|
||||||
window.addEventListener('keydown', onKey);
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.isSelected)
|
if (props.isSelected)
|
||||||
changeBreadcrumbs({ schema: props.schema });
|
changeBreadcrumbs({ schema: props.schema });
|
||||||
|
|
||||||
|
ipcRenderer.on('save-content', saveContentListener);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
firstInput.value.focus();
|
firstInput.value.focus();
|
||||||
}, 100);
|
}, 100);
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('keydown', onKey);
|
ipcRenderer.removeListener('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
title="CTRL+S"
|
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
@@ -30,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="px-2">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -118,12 +117,15 @@
|
|||||||
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
||||||
import { Ace } from 'ace-builds';
|
import { Ace } from 'ace-builds';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
|
import { useConsoleStore } from '@/stores/console';
|
||||||
import QueryEditor from '@/components/QueryEditor.vue';
|
import QueryEditor from '@/components/QueryEditor.vue';
|
||||||
import BaseLoader from '@/components/BaseLoader.vue';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
import Triggers from '@/ipc-api/Triggers';
|
import Triggers from '@/ipc-api/Triggers';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -137,6 +139,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
const { consoleHeight } = storeToRefs(useConsoleStore());
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
@@ -254,21 +257,21 @@ const clearChanges = () => {
|
|||||||
|
|
||||||
const resizeQueryEditor = () => {
|
const resizeQueryEditor = () => {
|
||||||
if (queryEditor.value) {
|
if (queryEditor.value) {
|
||||||
|
let sizeToSubtract = 0;
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
if (footer) sizeToSubtract += footer.offsetHeight;
|
||||||
|
sizeToSubtract += consoleHeight.value;
|
||||||
|
|
||||||
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - sizeToSubtract;
|
||||||
editorHeight.value = size;
|
editorHeight.value = size;
|
||||||
queryEditor.value.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const saveContentListener = () => {
|
||||||
if (props.isSelected) {
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
e.stopPropagation();
|
if (props.isSelected && !hasModalOpen && isChanged.value)
|
||||||
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
saveChanges();
|
||||||
if (isChanged.value)
|
|
||||||
saveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => props.isSelected, (val) => {
|
watch(() => props.isSelected, (val) => {
|
||||||
@@ -288,18 +291,22 @@ originalTrigger.value = {
|
|||||||
name: ''
|
name: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(consoleHeight, () => {
|
||||||
|
resizeQueryEditor();
|
||||||
|
});
|
||||||
|
|
||||||
localTrigger.value = JSON.parse(JSON.stringify(originalTrigger.value));
|
localTrigger.value = JSON.parse(JSON.stringify(originalTrigger.value));
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resizeQueryEditor();
|
resizeQueryEditor();
|
||||||
}, 50);
|
}, 50);
|
||||||
|
|
||||||
window.addEventListener('keydown', onKey);
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.isSelected)
|
if (props.isSelected)
|
||||||
changeBreadcrumbs({ schema: props.schema });
|
changeBreadcrumbs({ schema: props.schema });
|
||||||
|
|
||||||
|
ipcRenderer.on('save-content', saveContentListener);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
firstInput.value.focus();
|
firstInput.value.focus();
|
||||||
}, 100);
|
}, 100);
|
||||||
@@ -312,6 +319,6 @@ onUnmounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('keydown', onKey);
|
ipcRenderer.removeListener('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
title="CTRL+S"
|
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
@@ -99,10 +98,13 @@ import { Ace } from 'ace-builds';
|
|||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useConsoleStore } from '@/stores/console';
|
||||||
import BaseLoader from '@/components/BaseLoader.vue';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
import QueryEditor from '@/components/QueryEditor.vue';
|
import QueryEditor from '@/components/QueryEditor.vue';
|
||||||
import Functions from '@/ipc-api/Functions';
|
import Functions from '@/ipc-api/Functions';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -116,6 +118,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
const { consoleHeight } = storeToRefs(useConsoleStore());
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
@@ -189,21 +192,21 @@ const clearChanges = () => {
|
|||||||
|
|
||||||
const resizeQueryEditor = () => {
|
const resizeQueryEditor = () => {
|
||||||
if (queryEditor.value) {
|
if (queryEditor.value) {
|
||||||
|
let sizeToSubtract = 0;
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
if (footer) sizeToSubtract += footer.offsetHeight;
|
||||||
|
sizeToSubtract += consoleHeight.value;
|
||||||
|
|
||||||
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - sizeToSubtract;
|
||||||
editorHeight.value = size;
|
editorHeight.value = size;
|
||||||
queryEditor.value.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const saveContentListener = () => {
|
||||||
if (props.isSelected) {
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
e.stopPropagation();
|
if (props.isSelected && !hasModalOpen && isChanged.value)
|
||||||
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
saveChanges();
|
||||||
if (isChanged.value)
|
|
||||||
saveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
originalFunction.value = {
|
originalFunction.value = {
|
||||||
@@ -212,18 +215,30 @@ originalFunction.value = {
|
|||||||
name: ''
|
name: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(() => props.isSelected, (val) => {
|
||||||
|
if (val) changeBreadcrumbs({ schema: props.schema });
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(isChanged, (val) => {
|
||||||
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(consoleHeight, () => {
|
||||||
|
resizeQueryEditor();
|
||||||
|
});
|
||||||
|
|
||||||
localFunction.value = JSON.parse(JSON.stringify(originalFunction.value));
|
localFunction.value = JSON.parse(JSON.stringify(originalFunction.value));
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resizeQueryEditor();
|
resizeQueryEditor();
|
||||||
}, 50);
|
}, 50);
|
||||||
|
|
||||||
window.addEventListener('keydown', onKey);
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.isSelected)
|
if (props.isSelected)
|
||||||
changeBreadcrumbs({ schema: props.schema });
|
changeBreadcrumbs({ schema: props.schema });
|
||||||
|
|
||||||
|
ipcRenderer.on('save-content', saveContentListener);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
firstInput.value.focus();
|
firstInput.value.focus();
|
||||||
}, 100);
|
}, 100);
|
||||||
@@ -236,14 +251,6 @@ onUnmounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('keydown', onKey);
|
ipcRenderer.removeListener('save-content', saveContentListener);
|
||||||
});
|
|
||||||
|
|
||||||
watch(() => props.isSelected, (val) => {
|
|
||||||
if (val) changeBreadcrumbs({ schema: props.schema });
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(isChanged, (val) => {
|
|
||||||
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
title="CTRL+S"
|
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
@@ -107,12 +106,15 @@
|
|||||||
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
||||||
import { Ace } from 'ace-builds';
|
import { Ace } from 'ace-builds';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useConsoleStore } from '@/stores/console';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import BaseLoader from '@/components/BaseLoader.vue';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
import QueryEditor from '@/components/QueryEditor.vue';
|
import QueryEditor from '@/components/QueryEditor.vue';
|
||||||
import Views from '@/ipc-api/Views';
|
import Views from '@/ipc-api/Views';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -126,6 +128,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
const { consoleHeight } = storeToRefs(useConsoleStore());
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
@@ -202,21 +205,21 @@ const clearChanges = () => {
|
|||||||
|
|
||||||
const resizeQueryEditor = () => {
|
const resizeQueryEditor = () => {
|
||||||
if (queryEditor.value) {
|
if (queryEditor.value) {
|
||||||
|
let sizeToSubtract = 0;
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
if (footer) sizeToSubtract += footer.offsetHeight;
|
||||||
|
sizeToSubtract += consoleHeight.value;
|
||||||
|
|
||||||
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - sizeToSubtract;
|
||||||
editorHeight.value = size;
|
editorHeight.value = size;
|
||||||
queryEditor.value.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const saveContentListener = () => {
|
||||||
if (props.isSelected) {
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
e.stopPropagation();
|
if (props.isSelected && !hasModalOpen && isChanged.value)
|
||||||
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
saveChanges();
|
||||||
if (isChanged.value)
|
|
||||||
saveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => props.isSelected, (val) => {
|
watch(() => props.isSelected, (val) => {
|
||||||
@@ -233,6 +236,10 @@ watch(isChanged, (val) => {
|
|||||||
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(consoleHeight, () => {
|
||||||
|
resizeQueryEditor();
|
||||||
|
});
|
||||||
|
|
||||||
originalView.value = {
|
originalView.value = {
|
||||||
algorithm: 'UNDEFINED',
|
algorithm: 'UNDEFINED',
|
||||||
definer: '',
|
definer: '',
|
||||||
@@ -248,12 +255,12 @@ setTimeout(() => {
|
|||||||
resizeQueryEditor();
|
resizeQueryEditor();
|
||||||
}, 50);
|
}, 50);
|
||||||
|
|
||||||
window.addEventListener('keydown', onKey);
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (props.isSelected)
|
if (props.isSelected)
|
||||||
changeBreadcrumbs({ schema: props.schema });
|
changeBreadcrumbs({ schema: props.schema });
|
||||||
|
|
||||||
|
ipcRenderer.on('save-content', saveContentListener);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
firstInput.value.focus();
|
firstInput.value.focus();
|
||||||
}, 100);
|
}, 100);
|
||||||
@@ -266,7 +273,7 @@ onUnmounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('keydown', onKey);
|
ipcRenderer.removeListener('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
title="CTRL+S"
|
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
@@ -194,6 +193,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
||||||
import { Ace } from 'ace-builds';
|
import { Ace } from 'ace-builds';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useConsoleStore } from '@/stores/console';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
@@ -205,6 +206,7 @@ import Functions from '@/ipc-api/Functions';
|
|||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { AlterFunctionParams, FunctionInfos, FunctionParam } from 'common/interfaces/antares';
|
import { AlterFunctionParams, FunctionInfos, FunctionParam } from 'common/interfaces/antares';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -218,6 +220,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
const { consoleHeight } = storeToRefs(useConsoleStore());
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
@@ -344,8 +347,12 @@ const clearChanges = () => {
|
|||||||
|
|
||||||
const resizeQueryEditor = () => {
|
const resizeQueryEditor = () => {
|
||||||
if (queryEditor.value) {
|
if (queryEditor.value) {
|
||||||
|
let sizeToSubtract = 0;
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
if (footer) sizeToSubtract += footer.offsetHeight;
|
||||||
|
sizeToSubtract += consoleHeight.value;
|
||||||
|
|
||||||
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - sizeToSubtract;
|
||||||
editorHeight.value = size;
|
editorHeight.value = size;
|
||||||
queryEditor.value.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
@@ -400,14 +407,10 @@ const hideAskParamsModal = () => {
|
|||||||
isAskingParameters.value = false;
|
isAskingParameters.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const saveContentListener = () => {
|
||||||
if (props.isSelected) {
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
e.stopPropagation();
|
if (props.isSelected && !hasModalOpen && isChanged.value)
|
||||||
if (e.ctrlKey && e.keyCode === 83) { // CTRL + S
|
saveChanges();
|
||||||
if (isChanged.value)
|
|
||||||
saveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => props.schema, async () => {
|
watch(() => props.schema, async () => {
|
||||||
@@ -443,14 +446,19 @@ watch(isChanged, (val) => {
|
|||||||
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(consoleHeight, () => {
|
||||||
|
resizeQueryEditor();
|
||||||
|
});
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
await getFunctionData();
|
await getFunctionData();
|
||||||
queryEditor.value.editor.session.setValue(localFunction.value.sql);
|
queryEditor.value.editor.session.setValue(localFunction.value.sql);
|
||||||
window.addEventListener('keydown', onKey);
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('resize', resizeQueryEditor);
|
window.addEventListener('resize', resizeQueryEditor);
|
||||||
|
|
||||||
|
ipcRenderer.on('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@@ -458,6 +466,6 @@ onUnmounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('keydown', onKey);
|
ipcRenderer.removeListener('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
title="CTRL+S"
|
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
@@ -168,6 +167,8 @@ import { Component, computed, onUnmounted, onBeforeUnmount, onMounted, Ref, ref,
|
|||||||
import { AlterRoutineParams, FunctionParam, RoutineInfos } from 'common/interfaces/antares';
|
import { AlterRoutineParams, FunctionParam, RoutineInfos } from 'common/interfaces/antares';
|
||||||
import { Ace } from 'ace-builds';
|
import { Ace } from 'ace-builds';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useConsoleStore } from '@/stores/console';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
@@ -177,6 +178,7 @@ import BaseLoader from '@/components/BaseLoader.vue';
|
|||||||
import WorkspaceTabPropsRoutineParamsModal from '@/components/WorkspaceTabPropsRoutineParamsModal.vue';
|
import WorkspaceTabPropsRoutineParamsModal from '@/components/WorkspaceTabPropsRoutineParamsModal.vue';
|
||||||
import ModalAskParameters from '@/components/ModalAskParameters.vue';
|
import ModalAskParameters from '@/components/ModalAskParameters.vue';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -190,6 +192,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
const { consoleHeight } = storeToRefs(useConsoleStore());
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
@@ -316,8 +319,12 @@ const clearChanges = () => {
|
|||||||
|
|
||||||
const resizeQueryEditor = () => {
|
const resizeQueryEditor = () => {
|
||||||
if (queryEditor.value) {
|
if (queryEditor.value) {
|
||||||
|
let sizeToSubtract = 0;
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
if (footer) sizeToSubtract += footer.offsetHeight;
|
||||||
|
sizeToSubtract += consoleHeight.value;
|
||||||
|
|
||||||
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - sizeToSubtract;
|
||||||
editorHeight.value = size;
|
editorHeight.value = size;
|
||||||
queryEditor.value.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
@@ -370,14 +377,10 @@ const hideAskParamsModal = () => {
|
|||||||
isAskingParameters.value = false;
|
isAskingParameters.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const saveContentListener = () => {
|
||||||
if (props.isSelected) {
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
e.stopPropagation();
|
if (props.isSelected && !hasModalOpen && isChanged.value)
|
||||||
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
saveChanges();
|
||||||
if (isChanged.value)
|
|
||||||
saveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => props.schema, async () => {
|
watch(() => props.schema, async () => {
|
||||||
@@ -413,14 +416,19 @@ watch(isChanged, (val) => {
|
|||||||
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(consoleHeight, () => {
|
||||||
|
resizeQueryEditor();
|
||||||
|
});
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
await getRoutineData();
|
await getRoutineData();
|
||||||
queryEditor.value.editor.session.setValue(localRoutine.value.sql);
|
queryEditor.value.editor.session.setValue(localRoutine.value.sql);
|
||||||
window.addEventListener('keydown', onKey);
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('resize', resizeQueryEditor);
|
window.addEventListener('resize', resizeQueryEditor);
|
||||||
|
|
||||||
|
ipcRenderer.on('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@@ -428,7 +436,7 @@ onUnmounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('keydown', onKey);
|
ipcRenderer.removeListener('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
title="CTRL+S"
|
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
@@ -127,6 +126,8 @@ import { AlterEventParams, EventInfos } from 'common/interfaces/antares';
|
|||||||
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
||||||
import { Ace } from 'ace-builds';
|
import { Ace } from 'ace-builds';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useConsoleStore } from '@/stores/console';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import BaseLoader from '@/components/BaseLoader.vue';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
@@ -134,6 +135,7 @@ import QueryEditor from '@/components/QueryEditor.vue';
|
|||||||
import WorkspaceTabPropsSchedulerTimingModal from '@/components/WorkspaceTabPropsSchedulerTimingModal.vue';
|
import WorkspaceTabPropsSchedulerTimingModal from '@/components/WorkspaceTabPropsSchedulerTimingModal.vue';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
import Schedulers from '@/ipc-api/Schedulers';
|
import Schedulers from '@/ipc-api/Schedulers';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -147,6 +149,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
const { consoleHeight } = storeToRefs(useConsoleStore());
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
@@ -269,8 +272,12 @@ const clearChanges = () => {
|
|||||||
|
|
||||||
const resizeQueryEditor = () => {
|
const resizeQueryEditor = () => {
|
||||||
if (queryEditor.value) {
|
if (queryEditor.value) {
|
||||||
|
let sizeToSubtract = 0;
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
if (footer) sizeToSubtract += footer.offsetHeight;
|
||||||
|
sizeToSubtract += consoleHeight.value;
|
||||||
|
|
||||||
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - sizeToSubtract;
|
||||||
editorHeight.value = size;
|
editorHeight.value = size;
|
||||||
queryEditor.value.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
@@ -288,14 +295,10 @@ const timingUpdate = (options: EventInfos) => {
|
|||||||
localScheduler.value = options;
|
localScheduler.value = options;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const saveContentListener = () => {
|
||||||
if (props.isSelected) {
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
e.stopPropagation();
|
if (props.isSelected && !hasModalOpen && isChanged.value)
|
||||||
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
saveChanges();
|
||||||
if (isChanged.value)
|
|
||||||
saveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => props.schema, async () => {
|
watch(() => props.schema, async () => {
|
||||||
@@ -331,14 +334,19 @@ watch(isChanged, (val) => {
|
|||||||
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(consoleHeight, () => {
|
||||||
|
resizeQueryEditor();
|
||||||
|
});
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
await getSchedulerData();
|
await getSchedulerData();
|
||||||
queryEditor.value.editor.session.setValue(localScheduler.value.sql);
|
queryEditor.value.editor.session.setValue(localScheduler.value.sql);
|
||||||
window.addEventListener('keydown', onKey);
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('resize', resizeQueryEditor);
|
window.addEventListener('resize', resizeQueryEditor);
|
||||||
|
|
||||||
|
ipcRenderer.on('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@@ -346,7 +354,7 @@ onUnmounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('keydown', onKey);
|
ipcRenderer.removeListener('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
:confirm-text="t('word.confirm')"
|
:confirm-text="t('word.confirm')"
|
||||||
size="400"
|
size="400"
|
||||||
|
:disable-autofocus="true"
|
||||||
@confirm="confirmOptionsChange"
|
@confirm="confirmOptionsChange"
|
||||||
@hide="$emit('hide')"
|
@hide="$emit('hide')"
|
||||||
>
|
>
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
title="CTRL+S"
|
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
@@ -173,7 +172,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Component, computed, onBeforeUnmount, Ref, ref, watch } from 'vue';
|
import { Component, computed, onBeforeUnmount, onMounted, Ref, ref, watch } from 'vue';
|
||||||
import { AlterTableParams, TableField, TableForeign, TableIndex, TableInfos, TableOptions } from 'common/interfaces/antares';
|
import { AlterTableParams, TableField, TableForeign, TableIndex, TableInfos, TableOptions } from 'common/interfaces/antares';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
@@ -186,6 +185,7 @@ import BaseSelect from '@/components/BaseSelect.vue';
|
|||||||
import WorkspaceTabPropsTableFields from '@/components/WorkspaceTabPropsTableFields.vue';
|
import WorkspaceTabPropsTableFields from '@/components/WorkspaceTabPropsTableFields.vue';
|
||||||
import WorkspaceTabPropsTableIndexesModal from '@/components/WorkspaceTabPropsTableIndexesModal.vue';
|
import WorkspaceTabPropsTableIndexesModal from '@/components/WorkspaceTabPropsTableIndexesModal.vue';
|
||||||
import WorkspaceTabPropsTableForeignModal from '@/components/WorkspaceTabPropsTableForeignModal.vue';
|
import WorkspaceTabPropsTableForeignModal from '@/components/WorkspaceTabPropsTableForeignModal.vue';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -646,14 +646,10 @@ const foreignsUpdate = (foreigns: TableForeign[]) => {
|
|||||||
localKeyUsage.value = foreigns;
|
localKeyUsage.value = foreigns;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const saveContentListener = () => {
|
||||||
if (props.isSelected) {
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
e.stopPropagation();
|
if (props.isSelected && !hasModalOpen && isChanged.value)
|
||||||
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
saveChanges();
|
||||||
if (isChanged.value)
|
|
||||||
saveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => props.schema, () => {
|
watch(() => props.schema, () => {
|
||||||
@@ -684,9 +680,12 @@ watch(isChanged, (val) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
getFieldsData();
|
getFieldsData();
|
||||||
window.addEventListener('keydown', onKey);
|
|
||||||
|
onMounted(() => {
|
||||||
|
ipcRenderer.on('save-content', saveContentListener);
|
||||||
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('keydown', onKey);
|
ipcRenderer.removeListener('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@@ -126,8 +126,10 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Component, computed, onMounted, onUnmounted, onUpdated, Prop, ref, Ref, watch } from 'vue';
|
import { Component, computed, onMounted, onUnmounted, onUpdated, Prop, ref, Ref, watch } from 'vue';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
|
import { useConsoleStore } from '@/stores/console';
|
||||||
import * as Draggable from 'vuedraggable';
|
import * as Draggable from 'vuedraggable';
|
||||||
import TableRow from '@/components/WorkspaceTabPropsTableRow.vue';
|
import TableRow from '@/components/WorkspaceTabPropsTableRow.vue';
|
||||||
import TableContext from '@/components/WorkspaceTabPropsTableContext.vue';
|
import TableContext from '@/components/WorkspaceTabPropsTableContext.vue';
|
||||||
@@ -150,9 +152,12 @@ const props = defineProps({
|
|||||||
const emit = defineEmits(['add-new-index', 'add-to-index', 'rename-field', 'duplicate-field', 'remove-field']);
|
const emit = defineEmits(['add-new-index', 'add-to-index', 'rename-field', 'duplicate-field', 'remove-field']);
|
||||||
|
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
const consoleStore = useConsoleStore();
|
||||||
|
|
||||||
const { getWorkspace } = workspacesStore;
|
const { getWorkspace } = workspacesStore;
|
||||||
|
|
||||||
|
const { consoleHeight } = storeToRefs(consoleStore);
|
||||||
|
|
||||||
const tableWrapper: Ref<HTMLDivElement> = ref(null);
|
const tableWrapper: Ref<HTMLDivElement> = ref(null);
|
||||||
const propTable: Ref<HTMLDivElement> = ref(null);
|
const propTable: Ref<HTMLDivElement> = ref(null);
|
||||||
const resultTable: Ref<Component> = ref(null);
|
const resultTable: Ref<Component> = ref(null);
|
||||||
@@ -172,8 +177,13 @@ const resizeResults = () => {
|
|||||||
const el = tableWrapper.value;
|
const el = tableWrapper.value;
|
||||||
|
|
||||||
if (el) {
|
if (el) {
|
||||||
|
let sizeToSubtract = 0;
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - el.getBoundingClientRect().top - footer.offsetHeight;
|
if (footer) sizeToSubtract += footer.offsetHeight;
|
||||||
|
|
||||||
|
sizeToSubtract += consoleHeight.value;
|
||||||
|
|
||||||
|
const size = window.innerHeight - el.getBoundingClientRect().top - sizeToSubtract;
|
||||||
resultsSize.value = size;
|
resultsSize.value = size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,6 +226,10 @@ watch(fieldsLength, () => {
|
|||||||
refreshScroller();
|
refreshScroller();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(consoleHeight, () => {
|
||||||
|
resizeResults();
|
||||||
|
});
|
||||||
|
|
||||||
onUpdated(() => {
|
onUpdated(() => {
|
||||||
if (propTable.value)
|
if (propTable.value)
|
||||||
refreshScroller();
|
refreshScroller();
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
title="CTRL+S"
|
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
@@ -30,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="px-2">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column col-auto">
|
<div class="column col-auto">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -118,12 +117,15 @@
|
|||||||
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
||||||
import { Ace } from 'ace-builds';
|
import { Ace } from 'ace-builds';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { useConsoleStore } from '@/stores/console';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import QueryEditor from '@/components/QueryEditor.vue';
|
import QueryEditor from '@/components/QueryEditor.vue';
|
||||||
import BaseLoader from '@/components/BaseLoader.vue';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
import Triggers from '@/ipc-api/Triggers';
|
import Triggers from '@/ipc-api/Triggers';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
type TriggerEventName = 'INSERT' | 'UPDATE' | 'DELETE'
|
type TriggerEventName = 'INSERT' | 'UPDATE' | 'DELETE'
|
||||||
|
|
||||||
@@ -139,6 +141,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
const { consoleHeight } = storeToRefs(useConsoleStore());
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
@@ -304,21 +307,21 @@ const clearChanges = () => {
|
|||||||
|
|
||||||
const resizeQueryEditor = () => {
|
const resizeQueryEditor = () => {
|
||||||
if (queryEditor.value) {
|
if (queryEditor.value) {
|
||||||
|
let sizeToSubtract = 0;
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
if (footer) sizeToSubtract += footer.offsetHeight;
|
||||||
|
sizeToSubtract += consoleHeight.value;
|
||||||
|
|
||||||
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - sizeToSubtract;
|
||||||
editorHeight.value = size;
|
editorHeight.value = size;
|
||||||
queryEditor.value.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const saveContentListener = () => {
|
||||||
if (props.isSelected) {
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
e.stopPropagation();
|
if (props.isSelected && !hasModalOpen && isChanged.value)
|
||||||
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
saveChanges();
|
||||||
if (isChanged.value)
|
|
||||||
saveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => props.schema, async () => {
|
watch(() => props.schema, async () => {
|
||||||
@@ -345,14 +348,19 @@ watch(isChanged, (val) => {
|
|||||||
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
setUnsavedChanges({ uid: props.connection.uid, tUid: props.tabUid, isChanged: val });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(consoleHeight, () => {
|
||||||
|
resizeQueryEditor();
|
||||||
|
});
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
await getTriggerData();
|
await getTriggerData();
|
||||||
queryEditor.value.editor.session.setValue(localTrigger.value.sql);
|
queryEditor.value.editor.session.setValue(localTrigger.value.sql);
|
||||||
window.addEventListener('keydown', onKey);
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('resize', resizeQueryEditor);
|
window.addEventListener('resize', resizeQueryEditor);
|
||||||
|
|
||||||
|
ipcRenderer.on('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@@ -360,6 +368,6 @@ onUnmounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('keydown', onKey);
|
ipcRenderer.removeListener('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
title="CTRL+S"
|
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
@@ -84,13 +83,16 @@
|
|||||||
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
import { Component, computed, onBeforeUnmount, onMounted, onUnmounted, Ref, ref, watch } from 'vue';
|
||||||
import { Ace } from 'ace-builds';
|
import { Ace } from 'ace-builds';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
|
import { useConsoleStore } from '@/stores/console';
|
||||||
import BaseLoader from '@/components/BaseLoader.vue';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
import QueryEditor from '@/components/QueryEditor.vue';
|
import QueryEditor from '@/components/QueryEditor.vue';
|
||||||
import Functions from '@/ipc-api/Functions';
|
import Functions from '@/ipc-api/Functions';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
import { AlterFunctionParams, TriggerFunctionInfos } from 'common/interfaces/antares';
|
import { AlterFunctionParams, TriggerFunctionInfos } from 'common/interfaces/antares';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -104,6 +106,7 @@ const props = defineProps({
|
|||||||
|
|
||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
const { consoleHeight } = storeToRefs(useConsoleStore());
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
@@ -209,21 +212,21 @@ const clearChanges = () => {
|
|||||||
|
|
||||||
const resizeQueryEditor = () => {
|
const resizeQueryEditor = () => {
|
||||||
if (queryEditor.value) {
|
if (queryEditor.value) {
|
||||||
|
let sizeToSubtract = 0;
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - footer.offsetHeight;
|
if (footer) sizeToSubtract += footer.offsetHeight;
|
||||||
|
sizeToSubtract += consoleHeight.value;
|
||||||
|
|
||||||
|
const size = window.innerHeight - queryEditor.value.$el.getBoundingClientRect().top - sizeToSubtract;
|
||||||
editorHeight.value = size;
|
editorHeight.value = size;
|
||||||
queryEditor.value.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const saveContentListener = () => {
|
||||||
if (props.isSelected) {
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
e.stopPropagation();
|
if (props.isSelected && !hasModalOpen && isChanged.value)
|
||||||
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
saveChanges();
|
||||||
if (isChanged.value)
|
|
||||||
saveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => props.schema, async () => {
|
watch(() => props.schema, async () => {
|
||||||
@@ -242,6 +245,10 @@ watch(() => props.function, async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(consoleHeight, () => {
|
||||||
|
resizeQueryEditor();
|
||||||
|
});
|
||||||
|
|
||||||
watch(() => props.isSelected, (val) => {
|
watch(() => props.isSelected, (val) => {
|
||||||
if (val) changeBreadcrumbs({ schema: props.schema });
|
if (val) changeBreadcrumbs({ schema: props.schema });
|
||||||
});
|
});
|
||||||
@@ -253,11 +260,12 @@ watch(isChanged, (val) => {
|
|||||||
(async () => {
|
(async () => {
|
||||||
await getFunctionData();
|
await getFunctionData();
|
||||||
queryEditor.value.editor.session.setValue(localFunction.value.sql);
|
queryEditor.value.editor.session.setValue(localFunction.value.sql);
|
||||||
window.addEventListener('keydown', onKey);
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('resize', resizeQueryEditor);
|
window.addEventListener('resize', resizeQueryEditor);
|
||||||
|
|
||||||
|
ipcRenderer.on('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@@ -265,6 +273,6 @@ onUnmounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('keydown', onKey);
|
ipcRenderer.removeListener('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@@ -7,7 +7,6 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:disabled="!isChanged"
|
:disabled="!isChanged"
|
||||||
:class="{'loading':isSaving}"
|
:class="{'loading':isSaving}"
|
||||||
title="CTRL+S"
|
|
||||||
@click="saveChanges"
|
@click="saveChanges"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
@@ -112,6 +111,7 @@ import BaseLoader from '@/components/BaseLoader.vue';
|
|||||||
import QueryEditor from '@/components/QueryEditor.vue';
|
import QueryEditor from '@/components/QueryEditor.vue';
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
import Views from '@/ipc-api/Views';
|
import Views from '@/ipc-api/Views';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -245,14 +245,10 @@ const resizeQueryEditor = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKey = (e: KeyboardEvent) => {
|
const saveContentListener = () => {
|
||||||
if (props.isSelected) {
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
e.stopPropagation();
|
if (props.isSelected && !hasModalOpen && isChanged.value)
|
||||||
if (e.ctrlKey && e.key === 's') { // CTRL + S
|
saveChanges();
|
||||||
if (isChanged.value)
|
|
||||||
saveChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => props.schema, async () => {
|
watch(() => props.schema, async () => {
|
||||||
@@ -288,11 +284,12 @@ watch(isChanged, (val) => {
|
|||||||
(async () => {
|
(async () => {
|
||||||
await getViewData();
|
await getViewData();
|
||||||
queryEditor.value.editor.session.setValue(localView.value.sql);
|
queryEditor.value.editor.session.setValue(localView.value.sql);
|
||||||
window.addEventListener('keydown', onKey);
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('resize', resizeQueryEditor);
|
window.addEventListener('resize', resizeQueryEditor);
|
||||||
|
|
||||||
|
ipcRenderer.on('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@@ -300,6 +297,6 @@ onUnmounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('keydown', onKey);
|
ipcRenderer.removeListener('save-content', saveContentListener);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@@ -3,15 +3,11 @@
|
|||||||
v-show="isSelected"
|
v-show="isSelected"
|
||||||
class="workspace-query-tab column col-12 columns col-gapless no-outline p-0"
|
class="workspace-query-tab column col-12 columns col-gapless no-outline p-0"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@keydown.f5="runQuery(query)"
|
|
||||||
@keydown.k="killTabQuery"
|
|
||||||
@keydown.ctrl.alt.w="clear"
|
|
||||||
@keydown.ctrl.b="beautify"
|
|
||||||
@keydown.ctrl.g="openHistoryModal"
|
|
||||||
>
|
>
|
||||||
<div class="workspace-query-runner column col-12">
|
<div class="workspace-query-runner column col-12">
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
v-show="isSelected"
|
v-show="isSelected"
|
||||||
|
id="query-editor"
|
||||||
ref="queryEditor"
|
ref="queryEditor"
|
||||||
v-model="query"
|
v-model="query"
|
||||||
:auto-focus="true"
|
:auto-focus="true"
|
||||||
@@ -39,7 +35,6 @@
|
|||||||
class="btn btn-primary btn-sm"
|
class="btn btn-primary btn-sm"
|
||||||
:class="{'loading':isQuering}"
|
:class="{'loading':isQuering}"
|
||||||
:disabled="!query"
|
:disabled="!query"
|
||||||
title="F5"
|
|
||||||
@click="runQuery(query)"
|
@click="runQuery(query)"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-play pr-1" />
|
<i class="mdi mdi-24px mdi-play pr-1" />
|
||||||
@@ -67,7 +62,6 @@
|
|||||||
<button
|
<button
|
||||||
class="btn btn-link btn-sm mr-0"
|
class="btn btn-link btn-sm mr-0"
|
||||||
:disabled="!query || isQuering"
|
:disabled="!query || isQuering"
|
||||||
title="CTRL+W"
|
|
||||||
@click="clear()"
|
@click="clear()"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-delete-sweep pr-1" />
|
<i class="mdi mdi-24px mdi-delete-sweep pr-1" />
|
||||||
@@ -79,7 +73,6 @@
|
|||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm"
|
class="btn btn-dark btn-sm"
|
||||||
:disabled="!query || isQuering"
|
:disabled="!query || isQuering"
|
||||||
title="CTRL+B"
|
|
||||||
@click="beautify()"
|
@click="beautify()"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-brush pr-1" />
|
<i class="mdi mdi-24px mdi-brush pr-1" />
|
||||||
@@ -88,7 +81,6 @@
|
|||||||
<button
|
<button
|
||||||
class="btn btn-dark btn-sm"
|
class="btn btn-dark btn-sm"
|
||||||
:disabled="isQuering"
|
:disabled="isQuering"
|
||||||
title="CTRL+G"
|
|
||||||
@click="openHistoryModal()"
|
@click="openHistoryModal()"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-history pr-1" />
|
<i class="mdi mdi-24px mdi-history pr-1" />
|
||||||
@@ -111,6 +103,9 @@
|
|||||||
<li class="menu-item">
|
<li class="menu-item">
|
||||||
<a class="c-hand" @click="downloadTable('csv')">CSV</a>
|
<a class="c-hand" @click="downloadTable('csv')">CSV</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="menu-item">
|
||||||
|
<a class="c-hand" @click="downloadTable('sql')">SQL INSERT</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group pr-2" :title="t('message.commitMode')">
|
<div class="input-group pr-2" :title="t('message.commitMode')">
|
||||||
@@ -187,19 +182,22 @@
|
|||||||
import { Component, computed, onBeforeUnmount, onMounted, Prop, ref, Ref, watch } from 'vue';
|
import { Component, computed, onBeforeUnmount, onMounted, Prop, ref, Ref, watch } from 'vue';
|
||||||
import { Ace } from 'ace-builds';
|
import { Ace } from 'ace-builds';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
import { format } from 'sql-formatter';
|
import { format } from 'sql-formatter';
|
||||||
import { ConnectionParams } from 'common/interfaces/antares';
|
import { ConnectionParams } from 'common/interfaces/antares';
|
||||||
import { useHistoryStore } from '@/stores/history';
|
import { useHistoryStore } from '@/stores/history';
|
||||||
import { useNotificationsStore } from '@/stores/notifications';
|
import { useNotificationsStore } from '@/stores/notifications';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
|
import { useResultTables } from '@/composables/useResultTables';
|
||||||
|
import { useConsoleStore } from '@/stores/console';
|
||||||
import Schema from '@/ipc-api/Schema';
|
import Schema from '@/ipc-api/Schema';
|
||||||
import QueryEditor from '@/components/QueryEditor.vue';
|
import QueryEditor from '@/components/QueryEditor.vue';
|
||||||
import BaseLoader from '@/components/BaseLoader.vue';
|
import BaseLoader from '@/components/BaseLoader.vue';
|
||||||
import WorkspaceTabQueryTable from '@/components/WorkspaceTabQueryTable.vue';
|
import WorkspaceTabQueryTable from '@/components/WorkspaceTabQueryTable.vue';
|
||||||
import WorkspaceTabQueryEmptyState from '@/components/WorkspaceTabQueryEmptyState.vue';
|
import WorkspaceTabQueryEmptyState from '@/components/WorkspaceTabQueryEmptyState.vue';
|
||||||
import ModalHistory from '@/components/ModalHistory.vue';
|
import ModalHistory from '@/components/ModalHistory.vue';
|
||||||
import { useResultTables } from '@/composables/useResultTables';
|
|
||||||
import BaseSelect from '@/components/BaseSelect.vue';
|
import BaseSelect from '@/components/BaseSelect.vue';
|
||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
@@ -223,6 +221,8 @@ const { saveHistory } = useHistoryStore();
|
|||||||
const { addNotification } = useNotificationsStore();
|
const { addNotification } = useNotificationsStore();
|
||||||
const workspacesStore = useWorkspacesStore();
|
const workspacesStore = useWorkspacesStore();
|
||||||
|
|
||||||
|
const { consoleHeight } = storeToRefs(useConsoleStore());
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
changeBreadcrumbs,
|
changeBreadcrumbs,
|
||||||
@@ -289,6 +289,10 @@ watch(selectedSchema, () => {
|
|||||||
const runQuery = async (query: string) => {
|
const runQuery = async (query: string) => {
|
||||||
if (!query || isQuering.value) return;
|
if (!query || isQuering.value) return;
|
||||||
isQuering.value = true;
|
isQuering.value = true;
|
||||||
|
|
||||||
|
const selectedQuery = queryEditor.value.editor.getSelectedText();
|
||||||
|
if (selectedQuery) query = selectedQuery;
|
||||||
|
|
||||||
clearTabData();
|
clearTabData();
|
||||||
queryTable.value.resetSort();
|
queryTable.value.resetSort();
|
||||||
|
|
||||||
@@ -365,14 +369,17 @@ const resize = (e: MouseEvent) => {
|
|||||||
const el = queryEditor.value.$el;
|
const el = queryEditor.value.$el;
|
||||||
const queryFooterHeight = queryAreaFooter.value.clientHeight;
|
const queryFooterHeight = queryAreaFooter.value.clientHeight;
|
||||||
const bottom = e.pageY || resizer.value.getBoundingClientRect().bottom;
|
const bottom = e.pageY || resizer.value.getBoundingClientRect().bottom;
|
||||||
const maxHeight = window.innerHeight - 100 - queryFooterHeight;
|
const maxHeight = window.innerHeight - 100 - queryFooterHeight - consoleHeight.value;
|
||||||
let localEditorHeight = bottom - el.getBoundingClientRect().top;
|
let localEditorHeight = bottom - el.getBoundingClientRect().top;
|
||||||
if (localEditorHeight > maxHeight) localEditorHeight = maxHeight;
|
if (localEditorHeight > maxHeight) localEditorHeight = maxHeight;
|
||||||
if (localEditorHeight < 50) localEditorHeight = 50;
|
if (localEditorHeight < 50) localEditorHeight = 50;
|
||||||
editorHeight.value = localEditorHeight;
|
editorHeight.value = localEditorHeight;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const resizeResults = () => queryTable.value.resizeResults();
|
||||||
|
|
||||||
const onWindowResize = (e: MouseEvent) => {
|
const onWindowResize = (e: MouseEvent) => {
|
||||||
|
if (!queryEditor.value) return;
|
||||||
const el = queryEditor.value.$el;
|
const el = queryEditor.value.$el;
|
||||||
const queryFooterHeight = queryAreaFooter.value.clientHeight;
|
const queryFooterHeight = queryAreaFooter.value.clientHeight;
|
||||||
const bottom = e.pageY || resizer.value.getBoundingClientRect().bottom;
|
const bottom = e.pageY || resizer.value.getBoundingClientRect().bottom;
|
||||||
@@ -386,7 +393,7 @@ const onWindowResize = (e: MouseEvent) => {
|
|||||||
const stopResize = () => {
|
const stopResize = () => {
|
||||||
window.removeEventListener('mousemove', resize);
|
window.removeEventListener('mousemove', resize);
|
||||||
if (queryTable.value && results.value.length)
|
if (queryTable.value && results.value.length)
|
||||||
queryTable.value.resizeResults();
|
resizeResults();
|
||||||
|
|
||||||
if (queryEditor.value)
|
if (queryEditor.value)
|
||||||
queryEditor.value.editor.resize();
|
queryEditor.value.editor.resize();
|
||||||
@@ -412,7 +419,8 @@ const beautify = () => {
|
|||||||
const formattedQuery = format(query.value, {
|
const formattedQuery = format(query.value, {
|
||||||
language,
|
language,
|
||||||
uppercase: true
|
uppercase: true
|
||||||
});
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
} as any);
|
||||||
queryEditor.value.editor.session.setValue(formattedQuery);
|
queryEditor.value.editor.session.setValue(formattedQuery);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -434,7 +442,7 @@ const clear = () => {
|
|||||||
clearTabData();
|
clearTabData();
|
||||||
};
|
};
|
||||||
|
|
||||||
const downloadTable = (format: 'csv' | 'json') => {
|
const downloadTable = (format: 'csv' | 'json' | 'sql') => {
|
||||||
queryTable.value.downloadTable(format, `${props.tab.type}-${props.tab.index}`);
|
queryTable.value.downloadTable(format, `${props.tab.type}-${props.tab.index}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -476,18 +484,55 @@ const rollbackTab = async () => {
|
|||||||
isQuering.value = false;
|
isQuering.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
defineExpose({ resizeResults });
|
||||||
|
|
||||||
query.value = props.tab.content as string;
|
query.value = props.tab.content as string;
|
||||||
selectedSchema.value = props.tab.schema || breadcrumbsSchema.value;
|
selectedSchema.value = props.tab.schema || breadcrumbsSchema.value;
|
||||||
|
|
||||||
if (!databaseSchemas.value.includes(selectedSchema.value))
|
if (!databaseSchemas.value.includes(selectedSchema.value))
|
||||||
selectedSchema.value = null;
|
selectedSchema.value = null;
|
||||||
|
|
||||||
// window.addEventListener('keydown', onKey);
|
|
||||||
window.addEventListener('resize', onWindowResize);
|
window.addEventListener('resize', onWindowResize);
|
||||||
|
|
||||||
|
const reloadListener = () => {
|
||||||
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
|
if (props.isSelected && !hasModalOpen)
|
||||||
|
runQuery(query.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatListener = () => {
|
||||||
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
|
if (props.isSelected && !hasModalOpen)
|
||||||
|
beautify();
|
||||||
|
};
|
||||||
|
|
||||||
|
const killQueryListener = () => {
|
||||||
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
|
if (props.isSelected && !hasModalOpen)
|
||||||
|
killTabQuery();
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearQueryListener = () => {
|
||||||
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
|
if (props.isSelected && !hasModalOpen)
|
||||||
|
clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
const historyListener = () => {
|
||||||
|
const hasModalOpen = !!document.querySelectorAll('.modal.active').length;
|
||||||
|
if (props.isSelected && !hasModalOpen)
|
||||||
|
openHistoryModal();
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const localResizer = resizer.value;
|
const localResizer = resizer.value;
|
||||||
|
|
||||||
|
ipcRenderer.on('run-or-reload', reloadListener);
|
||||||
|
ipcRenderer.on('format-query', formatListener);
|
||||||
|
ipcRenderer.on('kill-query', killQueryListener);
|
||||||
|
ipcRenderer.on('clear-query', clearQueryListener);
|
||||||
|
ipcRenderer.on('query-history', historyListener);
|
||||||
|
|
||||||
localResizer.addEventListener('mousedown', (e: MouseEvent) => {
|
localResizer.addEventListener('mousedown', (e: MouseEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@@ -501,12 +546,17 @@ onMounted(() => {
|
|||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener('resize', onWindowResize);
|
window.removeEventListener('resize', onWindowResize);
|
||||||
// window.removeEventListener('keydown', onKey);
|
|
||||||
const params = {
|
const params = {
|
||||||
uid: props.connection.uid,
|
uid: props.connection.uid,
|
||||||
tabUid: props.tab.uid
|
tabUid: props.tab.uid
|
||||||
};
|
};
|
||||||
Schema.destroyConnectionToCommit(params);
|
Schema.destroyConnectionToCommit(params);
|
||||||
|
|
||||||
|
ipcRenderer.removeListener('run-or-reload', reloadListener);
|
||||||
|
ipcRenderer.removeListener('format-query', formatListener);
|
||||||
|
ipcRenderer.removeListener('kill-query', killQueryListener);
|
||||||
|
ipcRenderer.removeListener('clear-query', clearQueryListener);
|
||||||
|
ipcRenderer.removeListener('query-history', historyListener);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@@ -2,49 +2,21 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column col-16 text-right">
|
<div class="column col-16 text-right">
|
||||||
<div class="mb-4">
|
<div
|
||||||
{{ t('message.runQuery') }}
|
v-for="(shortcut, i) in tabShortcuts"
|
||||||
</div>
|
:key="i"
|
||||||
<div v-if="customizations.cancelQueries" class="mb-4">
|
class="mb-4"
|
||||||
{{ t('message.killQuery') }}
|
>
|
||||||
</div>
|
{{ t(shortcutEvents[shortcut.event].l18n, {param: shortcutEvents[shortcut.event].l18nParam}) }}
|
||||||
<div class="mb-4">
|
|
||||||
{{ t('word.format') }}
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
{{ t('word.clear') }}
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
{{ t('word.history') }}
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
{{ t('message.openNewTab') }}
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
{{ t('message.closeTab') }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="column col-16">
|
<div class="column col-16">
|
||||||
<div class="mb-4">
|
<div
|
||||||
<code>F5</code>
|
v-for="(shortcut, i) in tabShortcuts"
|
||||||
</div>
|
:key="i"
|
||||||
<div v-if="customizations.cancelQueries" class="mb-4">
|
class="mb-4"
|
||||||
<code>CTRL</code> + <code>K</code>
|
>
|
||||||
</div>
|
<span v-html="parseKeys(shortcut.keys)" />
|
||||||
<div class="mb-4">
|
|
||||||
<code>CTRL</code> + <code>B</code>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
<code>CTRL</code> + <code>ALT</code> + <code>W</code>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
<code>CTRL</code> + <code>G</code>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
<code>CTRL</code> + <code>T</code>
|
|
||||||
</div>
|
|
||||||
<div class="mb-4">
|
|
||||||
<code>CTRL</code> + <code>W</code>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -52,12 +24,22 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useSettingsStore } from '@/stores/settings';
|
||||||
|
import { shortcutEvents } from 'common/shortcuts';
|
||||||
|
import { useFilters } from '@/composables/useFilters';
|
||||||
|
|
||||||
|
const { parseKeys } = useFilters();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
defineProps({
|
const settingsStore = useSettingsStore();
|
||||||
customizations: Object
|
const { shortcuts } = storeToRefs(settingsStore);
|
||||||
|
|
||||||
|
const tabShortcuts = computed(() => {
|
||||||
|
return shortcuts.value.filter(s => shortcutEvents[s.event].context === 'tab');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@@ -14,10 +14,12 @@
|
|||||||
:context-event="contextEvent"
|
:context-event="contextEvent"
|
||||||
:selected-rows="selectedRows"
|
:selected-rows="selectedRows"
|
||||||
:selected-cell="selectedCell"
|
:selected-cell="selectedCell"
|
||||||
|
:mode="mode"
|
||||||
@show-delete-modal="showDeleteConfirmModal"
|
@show-delete-modal="showDeleteConfirmModal"
|
||||||
@set-null="setNull"
|
@set-null="setNull"
|
||||||
@copy-cell="copyCell"
|
@copy-cell="copyCell"
|
||||||
@copy-row="copyRow"
|
@copy-row="copyRow"
|
||||||
|
@duplicate-row="duplicateRow"
|
||||||
@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">
|
||||||
@@ -118,33 +120,44 @@ import { storeToRefs } from 'pinia';
|
|||||||
import { uidGen } from 'common/libs/uidGen';
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import { useSettingsStore } from '@/stores/settings';
|
import { useSettingsStore } from '@/stores/settings';
|
||||||
import { useWorkspacesStore } from '@/stores/workspaces';
|
import { useWorkspacesStore } from '@/stores/workspaces';
|
||||||
import { arrayToFile } from '../libs/arrayToFile';
|
import { useConsoleStore } from '@/stores/console';
|
||||||
|
import { exportRows } from '../libs/exportRows';
|
||||||
import { TEXT, LONG_TEXT, BLOB } from 'common/fieldTypes';
|
import { TEXT, LONG_TEXT, BLOB } from 'common/fieldTypes';
|
||||||
import BaseVirtualScroll from '@/components/BaseVirtualScroll.vue';
|
import BaseVirtualScroll from '@/components/BaseVirtualScroll.vue';
|
||||||
import WorkspaceTabQueryTableRow from '@/components/WorkspaceTabQueryTableRow.vue';
|
import WorkspaceTabQueryTableRow from '@/components/WorkspaceTabQueryTableRow.vue';
|
||||||
import TableContext from '@/components/WorkspaceTabQueryTableContext.vue';
|
import TableContext from '@/components/WorkspaceTabQueryTableContext.vue';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
import ConfirmModal from '@/components/BaseConfirmModal.vue';
|
||||||
import moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { TableField, QueryResult } from 'common/interfaces/antares';
|
import { TableField, QueryResult } from 'common/interfaces/antares';
|
||||||
import { TableUpdateParams } from 'common/interfaces/tableApis';
|
import { TableUpdateParams } from 'common/interfaces/tableApis';
|
||||||
|
import { jsonToSqlInsert } from 'common/libs/sqlUtils';
|
||||||
|
import { unproxify } from '@/libs/unproxify';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
|
const consoleStore = useConsoleStore();
|
||||||
const { getWorkspace } = useWorkspacesStore();
|
const { getWorkspace } = useWorkspacesStore();
|
||||||
|
|
||||||
const { dataTabLimit: pageSize } = storeToRefs(settingsStore);
|
const { dataTabLimit: pageSize } = storeToRefs(settingsStore);
|
||||||
|
|
||||||
|
const { consoleHeight } = storeToRefs(consoleStore);
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
results: Array as Prop<QueryResult[]>,
|
results: Array as Prop<QueryResult[]>,
|
||||||
connUid: String,
|
connUid: String,
|
||||||
mode: String,
|
mode: String as Prop<'table' | 'query'>,
|
||||||
isSelected: Boolean,
|
isSelected: Boolean,
|
||||||
elementType: { type: String, default: 'table' }
|
elementType: { type: String, default: 'table' }
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update-field', 'delete-selected', 'hard-sort']);
|
const emit = defineEmits([
|
||||||
|
'update-field',
|
||||||
|
'delete-selected',
|
||||||
|
'hard-sort',
|
||||||
|
'duplicate-row'
|
||||||
|
]);
|
||||||
|
|
||||||
const resultTable: Ref<Component & {updateWindow: () => void}> = ref(null);
|
const resultTable: Ref<Component & {updateWindow: () => void}> = ref(null);
|
||||||
const tableWrapper: Ref<HTMLDivElement> = ref(null);
|
const tableWrapper: Ref<HTMLDivElement> = ref(null);
|
||||||
@@ -166,6 +179,7 @@ const selectedField = ref(null);
|
|||||||
const isEditingRow = ref(false);
|
const isEditingRow = ref(false);
|
||||||
|
|
||||||
const workspaceSchema = computed(() => getWorkspace(props.connUid).breadcrumbs.schema);
|
const workspaceSchema = computed(() => getWorkspace(props.connUid).breadcrumbs.schema);
|
||||||
|
const workspaceClient = computed(() => getWorkspace(props.connUid).client);
|
||||||
|
|
||||||
const primaryField = computed(() => {
|
const primaryField = computed(() => {
|
||||||
const primaryFields = fields.value.filter(field => field.key === 'pri');
|
const primaryFields = fields.value.filter(field => field.key === 'pri');
|
||||||
@@ -272,6 +286,8 @@ const getSchema = (index: number) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getPrimaryValue = (row: any) => {
|
const getPrimaryValue = (row: any) => {
|
||||||
|
if (!primaryField.value) return null;
|
||||||
|
|
||||||
const primaryFieldName = Object.keys(row).find(prop => [
|
const primaryFieldName = Object.keys(row).find(prop => [
|
||||||
primaryField.value.alias,
|
primaryField.value.alias,
|
||||||
primaryField.value.name,
|
primaryField.value.name,
|
||||||
@@ -296,8 +312,13 @@ const resizeResults = () => {
|
|||||||
const el = tableWrapper.value;
|
const el = tableWrapper.value;
|
||||||
|
|
||||||
if (el) {
|
if (el) {
|
||||||
|
let sizeToSubtract = 0;
|
||||||
const footer = document.getElementById('footer');
|
const footer = document.getElementById('footer');
|
||||||
const size = window.innerHeight - el.getBoundingClientRect().top - footer.offsetHeight;
|
if (footer) sizeToSubtract += footer.offsetHeight;
|
||||||
|
|
||||||
|
sizeToSubtract += consoleHeight.value;
|
||||||
|
|
||||||
|
const size = window.innerHeight - el.getBoundingClientRect().top - sizeToSubtract;
|
||||||
resultsSize.value = size;
|
resultsSize.value = size;
|
||||||
}
|
}
|
||||||
resultTable.value.updateWindow();
|
resultTable.value.updateWindow();
|
||||||
@@ -321,7 +342,7 @@ const updateField = (payload: { field: string; type: string; content: any }, row
|
|||||||
});
|
});
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
primary: primaryField.value.name,
|
primary: primaryField.value?.name,
|
||||||
schema: getSchema(resultsetIndex.value),
|
schema: getSchema(resultsetIndex.value),
|
||||||
table: getTable(resultsetIndex.value),
|
table: getTable(resultsetIndex.value),
|
||||||
id: getPrimaryValue(orgRow),
|
id: getPrimaryValue(orgRow),
|
||||||
@@ -356,7 +377,7 @@ const deleteSelected = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
primary: primaryField.value.name,
|
primary: primaryField.value?.name,
|
||||||
schema: getSchema(resultsetIndex.value),
|
schema: getSchema(resultsetIndex.value),
|
||||||
table: getTable(resultsetIndex.value),
|
table: getTable(resultsetIndex.value),
|
||||||
rows
|
rows
|
||||||
@@ -368,7 +389,7 @@ const setNull = () => {
|
|||||||
const row = localResults.value.find((row: any) => selectedRows.value.includes(row._antares_id));
|
const row = localResults.value.find((row: any) => selectedRows.value.includes(row._antares_id));
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
primary: primaryField.value.name,
|
primary: primaryField.value?.name,
|
||||||
schema: getSchema(resultsetIndex.value),
|
schema: getSchema(resultsetIndex.value),
|
||||||
table: getTable(resultsetIndex.value),
|
table: getTable(resultsetIndex.value),
|
||||||
id: getPrimaryValue(row),
|
id: getPrimaryValue(row),
|
||||||
@@ -394,11 +415,59 @@ const copyCell = () => {
|
|||||||
navigator.clipboard.writeText(valueToCopy);
|
navigator.clipboard.writeText(valueToCopy);
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyRow = () => {
|
const copyRow = (format: string) => {
|
||||||
|
let contentToCopy;
|
||||||
|
|
||||||
|
if (selectedRows.value.length === 1) {
|
||||||
|
const row = localResults.value.find((row: any) => selectedRows.value.includes(row._antares_id));
|
||||||
|
const rowToCopy = unproxify(row);
|
||||||
|
delete rowToCopy._antares_id;
|
||||||
|
contentToCopy = rowToCopy;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
contentToCopy = unproxify(localResults.value).filter((row: any) => selectedRows.value.includes(row._antares_id)).map((row: any) => {
|
||||||
|
delete row._antares_id;
|
||||||
|
return row;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format === 'json')
|
||||||
|
navigator.clipboard.writeText(JSON.stringify(contentToCopy));
|
||||||
|
else if (format === 'sql') {
|
||||||
|
const sqlInserts = [];
|
||||||
|
if (!Array.isArray(contentToCopy)) contentToCopy = [contentToCopy];
|
||||||
|
|
||||||
|
for (const row of contentToCopy) {
|
||||||
|
sqlInserts.push(jsonToSqlInsert({
|
||||||
|
json: row,
|
||||||
|
client: workspaceClient.value,
|
||||||
|
fields: fieldsObj.value as {
|
||||||
|
[key: string]: {type: string; datePrecision: number};
|
||||||
|
},
|
||||||
|
table: getTable(resultsetIndex.value)
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
navigator.clipboard.writeText(sqlInserts.join('\n'));
|
||||||
|
}
|
||||||
|
else if (format === 'csv') {
|
||||||
|
const csv = [];
|
||||||
|
if (!Array.isArray(contentToCopy)) contentToCopy = [contentToCopy];
|
||||||
|
|
||||||
|
if (contentToCopy.length)
|
||||||
|
csv.push(Object.keys(contentToCopy[0]).join(';'));
|
||||||
|
|
||||||
|
for (const row of contentToCopy)
|
||||||
|
csv.push(Object.values(row).map(col => typeof col === 'string' ? `"${col}"` : col).join(';'));
|
||||||
|
|
||||||
|
navigator.clipboard.writeText(csv.join('\n'));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const duplicateRow = () => {
|
||||||
const row = localResults.value.find((row: any) => selectedRows.value.includes(row._antares_id));
|
const row = localResults.value.find((row: any) => selectedRows.value.includes(row._antares_id));
|
||||||
const rowToCopy = JSON.parse(JSON.stringify(row));
|
const rowToDuplicate = JSON.parse(JSON.stringify(row));
|
||||||
delete rowToCopy._antares_id;
|
delete rowToDuplicate._antares_id;
|
||||||
navigator.clipboard.writeText(JSON.stringify(rowToCopy));
|
emit('duplicate-row', rowToDuplicate);
|
||||||
};
|
};
|
||||||
|
|
||||||
const applyUpdate = (params: TableUpdateParams) => {
|
const applyUpdate = (params: TableUpdateParams) => {
|
||||||
@@ -509,7 +578,7 @@ const selectResultset = (index: number) => {
|
|||||||
resultsetIndex.value = index;
|
resultsetIndex.value = index;
|
||||||
};
|
};
|
||||||
|
|
||||||
const downloadTable = (format: 'csv' | 'json', filename: string) => {
|
const downloadTable = (format: 'csv' | 'json' | 'sql', table: string) => {
|
||||||
if (!sortedResults.value) return;
|
if (!sortedResults.value) return;
|
||||||
|
|
||||||
const rows = JSON.parse(JSON.stringify(sortedResults.value)).map((row: any) => {
|
const rows = JSON.parse(JSON.stringify(sortedResults.value)).map((row: any) => {
|
||||||
@@ -517,10 +586,14 @@ const downloadTable = (format: 'csv' | 'json', filename: string) => {
|
|||||||
return row;
|
return row;
|
||||||
});
|
});
|
||||||
|
|
||||||
arrayToFile({
|
exportRows({
|
||||||
type: format,
|
type: format,
|
||||||
content: rows,
|
content: rows,
|
||||||
filename
|
fields: fieldsObj.value as {
|
||||||
|
[key: string]: {type: string; datePrecision: number};
|
||||||
|
},
|
||||||
|
client: workspaceClient.value,
|
||||||
|
table
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -538,7 +611,7 @@ const onKey = async (e: KeyboardEvent) => {
|
|||||||
selectAllRows(e);
|
selectAllRows(e);
|
||||||
|
|
||||||
// row navigation stuff
|
// row navigation stuff
|
||||||
if ((e.code.includes('Arrow') || e.code === 'Tab') && sortedResults.value.length > 0 && !e.altKey) {
|
if (!(e.ctrlKey || e.metaKey) && (e.code.includes('Arrow') || e.code === 'Tab') && sortedResults.value.length > 0 && !e.altKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const aviableFields= Object.keys(sortedResults.value[0]).slice(0, -1); // removes _antares_id
|
const aviableFields= Object.keys(sortedResults.value[0]).slice(0, -1); // removes _antares_id
|
||||||
@@ -662,6 +735,10 @@ watch(() => props.isSelected, async (val) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(consoleHeight, () => {
|
||||||
|
resizeResults();
|
||||||
|
});
|
||||||
|
|
||||||
onUpdated(() => {
|
onUpdated(() => {
|
||||||
if (table.value)
|
if (table.value)
|
||||||
refreshScroller();
|
refreshScroller();
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
:context-event="contextEvent"
|
:context-event="contextEvent"
|
||||||
@close-context="closeContext"
|
@close-context="closeContext"
|
||||||
>
|
>
|
||||||
<div v-if="selectedRows.length === 1" class="context-element">
|
<div class="context-element">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-content-copy text-light pr-1" /> {{ t('word.copy') }}</span>
|
<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" />
|
<i class="mdi mdi-18px mdi-chevron-right text-light pl-1" />
|
||||||
<div class="context-submenu">
|
<div class="context-submenu">
|
||||||
@@ -16,17 +16,32 @@
|
|||||||
<i class="mdi mdi-18px mdi-numeric-0 mdi-rotate-90 text-light pr-1" /> {{ t('word.cell', 1) }}
|
<i class="mdi mdi-18px mdi-numeric-0 mdi-rotate-90 text-light pr-1" /> {{ t('word.cell', 1) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="context-element" @click="copyRow('json')">
|
||||||
v-if="selectedRows.length === 1"
|
|
||||||
class="context-element"
|
|
||||||
@click="copyRow"
|
|
||||||
>
|
|
||||||
<span class="d-flex">
|
<span class="d-flex">
|
||||||
<i class="mdi mdi-18px mdi-table-row text-light pr-1" /> {{ t('word.row', 1) }}
|
<i class="mdi mdi-18px mdi-table-row text-light pr-1" /> {{ t('word.row', selectedRows.length) }} (JSON)
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="context-element" @click="copyRow('csv')">
|
||||||
|
<span class="d-flex">
|
||||||
|
<i class="mdi mdi-18px mdi-table-row text-light pr-1" /> {{ t('word.row', selectedRows.length) }} (CSV)
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="context-element" @click="copyRow('sql')">
|
||||||
|
<span class="d-flex">
|
||||||
|
<i class="mdi mdi-18px mdi-table-row text-light pr-1" /> {{ t('word.row', selectedRows.length) }} (SQL INSERT)
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="selectedRows.length === 1 && selectedCell.isEditable && mode === 'table'"
|
||||||
|
class="context-element"
|
||||||
|
@click="duplicateRow"
|
||||||
|
>
|
||||||
|
<span class="d-flex">
|
||||||
|
<i class="mdi mdi-18px mdi-content-duplicate text-light pr-1" /> {{ t('word.duplicate') }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="selectedRows.length === 1 && selectedCell.isEditable"
|
v-if="selectedRows.length === 1 && selectedCell.isEditable"
|
||||||
class="context-element"
|
class="context-element"
|
||||||
@@ -49,6 +64,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { Prop } from 'vue';
|
||||||
import BaseContextMenu from '@/components/BaseContextMenu.vue';
|
import BaseContextMenu from '@/components/BaseContextMenu.vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
@@ -57,10 +73,18 @@ const { t } = useI18n();
|
|||||||
defineProps({
|
defineProps({
|
||||||
contextEvent: MouseEvent,
|
contextEvent: MouseEvent,
|
||||||
selectedRows: Array,
|
selectedRows: Array,
|
||||||
selectedCell: Object
|
selectedCell: Object,
|
||||||
|
mode: String as Prop<'table' | 'query'>
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['show-delete-modal', 'close-context', 'set-null', 'copy-cell', 'copy-row']);
|
const emit = defineEmits([
|
||||||
|
'show-delete-modal',
|
||||||
|
'close-context',
|
||||||
|
'set-null',
|
||||||
|
'copy-cell',
|
||||||
|
'copy-row',
|
||||||
|
'duplicate-row'
|
||||||
|
]);
|
||||||
|
|
||||||
const showConfirmModal = () => {
|
const showConfirmModal = () => {
|
||||||
emit('show-delete-modal');
|
emit('show-delete-modal');
|
||||||
@@ -80,8 +104,13 @@ const copyCell = () => {
|
|||||||
closeContext();
|
closeContext();
|
||||||
};
|
};
|
||||||
|
|
||||||
const copyRow = () => {
|
const copyRow = (format: string) => {
|
||||||
emit('copy-row');
|
emit('copy-row', format);
|
||||||
|
closeContext();
|
||||||
|
};
|
||||||
|
|
||||||
|
const duplicateRow = () => {
|
||||||
|
emit('duplicate-row');
|
||||||
closeContext();
|
closeContext();
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user