Compare commits
481 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ed5189fdc1 | |||
| 265ed66d25 | |||
| 09c07acd5c | |||
| 3c5a69adc9 | |||
| 0203f69e95 | |||
| 9a2498862c | |||
|
|
3a26d4f509 | ||
| ce08cbd8b5 | |||
|
|
d91ffcccca | ||
| 6115eb9409 | |||
| 3fd26a0523 | |||
| e217d5181b | |||
| dcf368b350 | |||
| 21e3e79ddf | |||
| 2918c3cb92 | |||
| 3ad190b18c | |||
| d5b2bde2ea | |||
| a42348ef5c | |||
| 8b93c49778 | |||
| 0842e00098 | |||
| 6cef02bebb | |||
|
|
334c7a31d2 | ||
| bc82289d54 | |||
| c9fa941578 | |||
| 4048df3c7b | |||
| 198368605b | |||
| 8f0ac26b69 | |||
| b35fc5b78b | |||
| 622b519cbb | |||
| 71e2c911ae | |||
| 756d49b259 | |||
| 14c64c537c | |||
| 97b3563e25 | |||
| e834fe31ac | |||
| 065de3a0a2 | |||
| 1573de5b1f | |||
| 04fc1bbee0 | |||
| dea378014d | |||
| 3abff36136 | |||
| 70354aa828 | |||
| 372049ae64 | |||
| 5d271be062 | |||
| 07ee1ae828 | |||
| cbe0e2980a | |||
| 0fba50c6ec | |||
| 420255cdd4 | |||
| a8a47ed5f7 | |||
| 425f0663f9 | |||
| e106d100b5 | |||
| d7fdf53932 | |||
| 62f7e57d0c | |||
| 14577d14bb | |||
| f5f2a697e8 | |||
| a0105cf1c3 | |||
| 77c5d28032 | |||
| d1d8592f79 | |||
| 1f828f69a0 | |||
| adc5477673 | |||
| 1e543aa6b0 | |||
| c41e059b0b | |||
| e6ef5ffa56 | |||
| d67d122270 | |||
| b837e2fc68 | |||
| a73a2f483e | |||
| 0a9983d30d | |||
| 58b91ebfe0 | |||
| e78ca2417e | |||
| e1855a262d | |||
| 6b725b1d40 | |||
| f6faad98f8 | |||
| 320aa8ba04 | |||
| 9f0280b991 | |||
| 04fa320820 | |||
| f7c3aa883d | |||
| f7a74df009 | |||
| 003c02b1fb | |||
| ef21ea7448 | |||
| 525c964c62 | |||
| 7845e3e501 | |||
| 0c29e0d566 | |||
| d38097d056 | |||
| c87b8dc738 | |||
| ed6e7fa72d | |||
| f0fa7c81b7 | |||
| 5bb4e496f2 | |||
| 01057332b0 | |||
| ab382dfbcd | |||
| 88c4cdc8e2 | |||
| 15ff211a41 | |||
| 5c855a520a | |||
| 7488bc7a17 | |||
|
|
fd85cf43a2 | ||
| a87079cd17 | |||
| 14d5842056 | |||
| f19f9e23a2 | |||
| 0252a064d9 | |||
| 439356a019 | |||
| c6897af22d | |||
| 7e40dbfba3 | |||
| 27a153ef43 | |||
| 56fcc2650b | |||
| 9622dbec6b | |||
| a0ab63bdb5 | |||
| 8cd76e711c | |||
| 9af71a6e34 | |||
| e7b3c28826 | |||
| 7570b0add8 | |||
|
|
1801bef019 | ||
| 0db5ebd7bf | |||
| 5c21b67b3d | |||
| 08d5b1b329 | |||
| 61a42d51f5 | |||
| d96907ca2d | |||
| 75bbd5f66e | |||
| faa07a077c | |||
| 7ca69e51ae | |||
| d868c772b9 | |||
| a69bdeb20d | |||
| 51db2795bb | |||
| 2d278aa14e | |||
| f74bca7bb4 | |||
| 978a7c5f5b | |||
| 0110756204 | |||
| 7df0cf8389 | |||
| 3aef7e953e | |||
| a975df38dd | |||
| a0a025e450 | |||
| 7cc5e7b67f | |||
| e2ebb04a90 | |||
| e579f37438 | |||
| 3829b94bf7 | |||
| 5c8ee66f43 | |||
| b77e77f3bb | |||
| 1d5614278c | |||
| 8a20befd09 | |||
| 4133fc452f | |||
| 690a4541f9 | |||
| 71910ca5c8 | |||
| cce5adbac7 | |||
| e6d67fcb0c | |||
| e83b80afc9 | |||
| 8742fa10f0 | |||
| ab54eb086f | |||
| 308f69f12e | |||
| 2f30bafa33 | |||
|
|
1716077182 | ||
| 5562e73e75 | |||
| 8a7cc2a14f | |||
| 9ca059d979 | |||
| c9ccf786cd | |||
| 5e9c88a7fd | |||
| e0e6483d1f | |||
| 66227569f4 | |||
| faa799c8ea | |||
| acc1eeb094 | |||
| 865dad80df | |||
| f502620779 | |||
| f61d7eeaf4 | |||
| e71c7568c0 | |||
| 79f033e524 | |||
| 310cfaa3c2 | |||
| 6f93e1f9ab | |||
| 04bdd085a5 | |||
| c568acae14 | |||
| 7a46a119d8 | |||
| f1636f1528 | |||
| f839d5cd3c | |||
| 8f4f1d393c | |||
| ab17c6d4bf | |||
| b4b2f3d0cf | |||
| 7a766f04e6 | |||
| f6b77d1243 | |||
| 840241c3cc | |||
| 0f10c9e824 | |||
| 1e37f2a96f | |||
| 0d6137195d | |||
| d26f168250 | |||
| 5efcad4d3f | |||
| 0a872e7023 | |||
| 0e425a9c6d | |||
| 434711a360 | |||
| 854472c7a3 | |||
| 9a7cd90d5c | |||
| 026400d242 | |||
| 4bd8cbbf6c | |||
| 1382bc9300 | |||
| ae103e5477 | |||
| 6b0b8b19d7 | |||
| 475397ca34 | |||
| 7a62131cc7 | |||
| c7663be338 | |||
| 496490b14a | |||
|
|
672573d8c2 | ||
| d923453919 | |||
| 0435279604 | |||
| 79cef61fea | |||
| dec334737f | |||
| 84c3692e95 | |||
| 5dc0b0bea4 | |||
| 19d16e46bb | |||
| 3baf6fa173 | |||
| 29e2d92b5b | |||
| bebba64d06 | |||
| 9dfe7cca22 | |||
| 35ef070725 | |||
| 370ad6a536 | |||
| 5822b3df43 | |||
| 5208ec171b | |||
| 6e332da425 | |||
| bf3367b41d | |||
| ecfb732c26 | |||
| fd4f032a6f | |||
| 1b09909126 | |||
| 04cd806954 | |||
| 773cb36ca1 | |||
| ccacd3e2c3 | |||
| 15948b30c9 | |||
| 3dcb5d4f14 | |||
|
|
64d93d7c40 | ||
|
|
2d85295093 | ||
|
|
cd58e2d8ca | ||
| d87495822e | |||
|
|
8720bcdad6 | ||
| 1df57dc705 | |||
| 86240fb53c | |||
| 1d363f755e | |||
| 0d77aee3eb | |||
| a41cf1ab56 | |||
| 5ceddb8e00 | |||
| 16e17b39b6 | |||
| 20cba6ee9b | |||
| 9ffd443a66 | |||
| f82dbd24dc | |||
| 6eb2977568 | |||
| cafb65560a | |||
| 532d963019 | |||
| 1b0a63ff31 | |||
| c22187c305 | |||
| dcccb544f9 | |||
| 7d2ace9456 | |||
| 2584c9b9c2 | |||
| a6b75ad0dc | |||
| 90fd9db917 | |||
| c0f54b9514 | |||
| cd31413256 | |||
| b33199ea59 | |||
| dea5ec7513 | |||
| be816e8588 | |||
| 1e938adc5d | |||
| 8735a0c5f9 | |||
| 3dde1c109e | |||
| d0b3e1b1b8 | |||
| c20bff7bcb | |||
| 9f5ec0276c | |||
| 55932fe115 | |||
| d374372e20 | |||
| bb5f44681f | |||
| 49a4e1cb7b | |||
| c2f76e490a | |||
| e349dd5eab | |||
| 280697698e | |||
| 0783f8b57e | |||
| c981244d7a | |||
| dcb135dd01 | |||
| 99f7511c4d | |||
| fe4c8e12b3 | |||
| 21728a663d | |||
| 9ca03f4625 | |||
| affb7288b0 | |||
| 614e0d3275 | |||
| feef5e30ee | |||
| 82c25711b6 | |||
| 2ca2988832 | |||
| e3f259c6e8 | |||
| e7401cc96e | |||
| 13b9840f3d | |||
| d20414b692 | |||
| 22a8c25717 | |||
| db47b4040a | |||
| e89911b185 | |||
| fccfe92453 | |||
| d465e18dba | |||
| ffb1712a59 | |||
| 9f6a183d9b | |||
| 1f80a64fe1 | |||
| fc651149b9 | |||
| 964570247f | |||
| 8a6c59f7ce | |||
| 4d844fe2c9 | |||
| d892fa6fb3 | |||
| 8c9e4f6e96 | |||
| 966ca60c89 | |||
| 9bbe218f90 | |||
| a1c6be372b | |||
| 7d0c929fb8 | |||
| 25d72e3952 | |||
| b232a3bb5f | |||
| e9a26c1bc0 | |||
| 76c5c0c680 | |||
| ddfb713124 | |||
| 0081a4167c | |||
| 239cb4488f | |||
| fb5adbe676 | |||
| 9cd51c8d8b | |||
| 8dfaa3b7be | |||
| 5d7efa75b7 | |||
| 4862d51fba | |||
| 07f60c3917 | |||
| 049143d143 | |||
| db4430609e | |||
| 71b4310117 | |||
| 201fad9265 | |||
| 45351faeae | |||
| b4ead6992c | |||
| b1ea32b680 | |||
| 39ca1974bc | |||
| 777b73fa6f | |||
| 4494e637f7 | |||
| 219da0aba4 | |||
| 7e8167154f | |||
| 3aa2159a1a | |||
| 76d92cd106 | |||
| c8545a250b | |||
| dbab06fcb8 | |||
| b54fefbf25 | |||
| 9a1bf32128 | |||
| 110b0b414c | |||
| 2f58007af4 | |||
| 9b60bfff8d | |||
| 3b37b7432e | |||
| 7c4ca999ce | |||
| 94c4952319 | |||
| 014257147e | |||
| 970de4962b | |||
| b5a828309f | |||
| 5b21d17f3a | |||
| 2c6e35288f | |||
| bcadac6e95 | |||
|
|
18a93ef1aa | ||
| 6c62052b47 | |||
| 9d5ebefdce | |||
| 34ebc6b72d | |||
| 288ff4c1a1 | |||
| a176174b8d | |||
| 0f69d1dbb7 | |||
| 0386bbac50 | |||
| b0576acdf6 | |||
| 9a190854fe | |||
| 2aace28e80 | |||
| 02c03e3d26 | |||
| a0d85520fb | |||
| ede6fe81ce | |||
| 4e72bb1587 | |||
| 15417e8a77 | |||
| 88ab7c5a62 | |||
| 5940b0b842 | |||
| 574d493908 | |||
| af96647603 | |||
| ab70ff239e | |||
| bacf458936 | |||
| 379d6c169a | |||
|
|
36352f8028 | ||
| 04fcaf2f6e | |||
| 8b914446b0 | |||
| a11bac504c | |||
| b9ed8dd610 | |||
| 1cf6485896 | |||
| 4bc9bbfb34 | |||
| 4923128236 | |||
| 8ff6e70145 | |||
| afcf1c86ed | |||
| 0200ae4a0f | |||
| dbe7b9dd23 | |||
| ceab4ef243 | |||
| 1e7d4ca347 | |||
| c0a32c040e | |||
| f150508547 | |||
| 49d71722e2 | |||
| 59a50bc014 | |||
| 41d75b127c | |||
| 0cbea9d100 | |||
| e351c903a8 | |||
| 6e55f27b23 | |||
| 27fd9ec203 | |||
| 0ec2710872 | |||
| 3bcd02fc4e | |||
| aa33850286 | |||
| 82fdc0bcd7 | |||
| d695c9f8d2 | |||
| b32132ad84 | |||
| 3126625461 | |||
| ab307f82b1 | |||
| 0df2b836b1 | |||
| 6611aad840 | |||
| 8c4aaec167 | |||
| b7053bdf80 | |||
| b6b7be098a | |||
| 56f2a27f00 | |||
| dcf469ebed | |||
| d94b49febf | |||
| 3b4f1475df | |||
| 155154b43d | |||
| a95b8d188c | |||
| cb1fce6f99 | |||
| 0014f48079 | |||
| fc35f271d7 | |||
| 65ad0e954d | |||
| 8cafade8b1 | |||
| d13b708377 | |||
| 206597e5b8 | |||
| c5458159d1 | |||
| 1476e899d1 | |||
| 797ab70e7c | |||
| f81312aeb0 | |||
| 3ed5ea023e | |||
| 9291a7a7b4 | |||
| 5cfdc9b92d | |||
| 15b08d7ea8 | |||
| acebe435ff | |||
| 5712b80022 | |||
| d38583262e | |||
| e0e2131981 | |||
| 7470bddd70 | |||
| 33d1fa2290 | |||
| a4122b4eaa | |||
| e6602d1bfa | |||
| f8cf90a89e | |||
| 41505bde65 | |||
| 8ebc3bce92 | |||
| 45e9cdc591 | |||
| 3cbfc0e148 | |||
| a47e9e1b1f | |||
| e95d29c7c3 | |||
| e954f04828 | |||
|
|
85c800f85b | ||
|
|
e0482244d7 | ||
| 27769f204f | |||
| dfb24c65f3 | |||
| 0fe71572a5 | |||
| db577bfef0 | |||
| 0805b96a75 | |||
| e49823f5c4 | |||
| 76351005b4 | |||
| 3e5770f7de | |||
| 242ddec744 | |||
| 07654039b6 | |||
| 249926b8e0 | |||
| ae47a978c1 | |||
|
|
bc53b0b332 | ||
| d4175bcbda | |||
| c9ba2e5962 | |||
| 2e49d86677 | |||
| c393f86947 | |||
| a0b96aa06c | |||
| 2dc16e8ea8 | |||
| ee183886f6 | |||
| cef6f681c8 | |||
| ea9b489f5f | |||
| 1658432fd3 | |||
| a8cd17748f | |||
| 580105e9f3 | |||
| 0626f6f775 | |||
| 12f5e479f3 | |||
| 04804b07c7 | |||
| 053418ee90 | |||
| 426628f268 | |||
| d4ecaf65e5 | |||
| 27d114beef | |||
| 936de04cd3 | |||
| b7c779eef6 | |||
| d560c384f5 | |||
| 9ecd88870d | |||
| 07d1e82325 | |||
| ce25cd0a31 | |||
| 319d9beef1 | |||
|
|
4272efe73b | ||
| ed5cf0a8e4 | |||
| c70e5b422c | |||
| 0bf2c8dc9d | |||
| d563cec70d | |||
| 6ee4ef4b8b |
40
.eslintrc
@@ -9,15 +9,18 @@
|
|||||||
"plugin:vue/recommended"
|
"plugin:vue/recommended"
|
||||||
],
|
],
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"parser": "babel-eslint",
|
"parser": "@babel/eslint-parser",
|
||||||
"ecmaVersion": 9,
|
"ecmaVersion": 9,
|
||||||
"sourceType": "module"
|
"sourceType": "module",
|
||||||
|
"requireConfigFile": false
|
||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"indent": [
|
"indent": [
|
||||||
"error",
|
"error",
|
||||||
3,
|
3,
|
||||||
{ "SwitchCase": 1 }
|
{
|
||||||
|
"SwitchCase": 1
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"linebreak-style": [
|
"linebreak-style": [
|
||||||
"error",
|
"error",
|
||||||
@@ -44,18 +47,25 @@
|
|||||||
"vue/no-side-effects-in-computed-properties": "off",
|
"vue/no-side-effects-in-computed-properties": "off",
|
||||||
"vue/require-default-prop": "off",
|
"vue/require-default-prop": "off",
|
||||||
"vue/no-v-html": "off",
|
"vue/no-v-html": "off",
|
||||||
"vue/html-indent": ["error", 3, {
|
"vue/html-indent": [
|
||||||
"attribute": 1,
|
"error",
|
||||||
"baseIndent": 1,
|
3,
|
||||||
"closeBracket": 0,
|
{
|
||||||
"ignores": []
|
"attribute": 1,
|
||||||
}],
|
"baseIndent": 1,
|
||||||
"vue/max-attributes-per-line": ["error", {
|
"closeBracket": 0,
|
||||||
"singleline": 2,
|
"ignores": []
|
||||||
"multiline": {
|
|
||||||
"max": 1,
|
|
||||||
"allowFirstLine": false
|
|
||||||
}
|
}
|
||||||
}]
|
],
|
||||||
|
"vue/max-attributes-per-line": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"singleline": 2,
|
||||||
|
"multiline": {
|
||||||
|
"max": 1,
|
||||||
|
"allowFirstLine": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
6
.gitattributes
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
* text eol=lf
|
||||||
|
*.jpg binary
|
||||||
|
*.png binary
|
||||||
|
*.gif binary
|
||||||
|
*.ico binary
|
||||||
|
*.icns binary
|
||||||
2
.github/FUNDING.yml
vendored
@@ -1,7 +1,7 @@
|
|||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
|
|
||||||
github: [fabio286]
|
github: [fabio286]
|
||||||
patreon: fabio286
|
patreon: #fabio286
|
||||||
open_collective: # Replace with a single Open Collective username
|
open_collective: # Replace with a single Open Collective username
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
|
|||||||
20
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -3,7 +3,7 @@ name: Bug report
|
|||||||
about: Create a report to help us improve
|
about: Create a report to help us improve
|
||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
labels: ''
|
||||||
assignees: ''
|
assignees: Fabio286
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ A clear and concise description of what the bug is.
|
|||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
|
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
2. Click on '....'
|
2. Click on '....'
|
||||||
3. Scroll down to '....'
|
3. Scroll down to '....'
|
||||||
@@ -23,16 +24,15 @@ A clear and concise description of what you expected to happen.
|
|||||||
**Screenshots**
|
**Screenshots**
|
||||||
If applicable, add screenshots to help explain your problem.
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
**Desktop (please complete the following information):**
|
**Application (please complete the following information):**
|
||||||
- OS: [e.g. iOS]
|
|
||||||
- Browser [e.g. chrome, safari]
|
|
||||||
- Version [e.g. 22]
|
|
||||||
|
|
||||||
**Smartphone (please complete the following information):**
|
- Version [e.g. 0.14.0]
|
||||||
- Device: [e.g. iPhone6]
|
- Distribution: [e.g. exe, Linux Store, AppImage, dmg]
|
||||||
- OS: [e.g. iOS8.1]
|
|
||||||
- Browser [e.g. stock browser, safari]
|
**Desktop (please complete the following information):**
|
||||||
- Version [e.g. 22]
|
|
||||||
|
- OS: [e.g. iOS]
|
||||||
|
- Version [e.g. 22]
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context about the problem here.
|
Add any other context about the problem here.
|
||||||
|
|||||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -3,7 +3,7 @@ name: Feature request
|
|||||||
about: Suggest an idea for this project
|
about: Suggest an idea for this project
|
||||||
title: ''
|
title: ''
|
||||||
labels: ''
|
labels: ''
|
||||||
assignees: ''
|
assignees: Fabio286
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
26
.github/workflows/build-linux.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
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@v1
|
||||||
|
|
||||||
|
- name: Install Node.js, NPM and Yarn
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 14
|
||||||
|
|
||||||
|
- name: Build/release Electron app
|
||||||
|
uses: samuelmeuli/action-electron-builder@v1
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.github_token }}
|
||||||
|
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
26
.github/workflows/build-mac.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
name: Build/release [mac]
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out Git repository
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Install Node.js, NPM and Yarn
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 14
|
||||||
|
|
||||||
|
- name: Build/release Electron app
|
||||||
|
uses: samuelmeuli/action-electron-builder@v1
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.github_token }}
|
||||||
|
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
26
.github/workflows/build-win.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
name: Build/release [windows]
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [windows-latest]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out Git repository
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Install Node.js, NPM and Yarn
|
||||||
|
uses: actions/setup-node@v1
|
||||||
|
with:
|
||||||
|
node-version: 14
|
||||||
|
|
||||||
|
- name: Build/release Electron app
|
||||||
|
uses: samuelmeuli/action-electron-builder@v1
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.github_token }}
|
||||||
|
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
5
.github/workflows/codeql-analysis.yml
vendored
@@ -31,11 +31,6 @@ jobs:
|
|||||||
# a pull request then we can checkout the head.
|
# a pull request then we can checkout the head.
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
# If this run was triggered by a pull request event, then checkout
|
|
||||||
# the head of the pull request instead of the merge commit.
|
|
||||||
- run: git checkout HEAD^2
|
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v1
|
uses: github/codeql-action/init@v1
|
||||||
|
|||||||
2
.gitignore
vendored
@@ -4,6 +4,6 @@ node_modules/
|
|||||||
thumbs.db
|
thumbs.db
|
||||||
.idea/
|
.idea/
|
||||||
.vscode
|
.vscode
|
||||||
TODO.md
|
NOTES.md
|
||||||
*.txt
|
*.txt
|
||||||
package-lock.json
|
package-lock.json
|
||||||
@@ -8,7 +8,9 @@
|
|||||||
"stylelint-scss"
|
"stylelint-scss"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"at-rule-no-unknown": null
|
"at-rule-no-unknown": null,
|
||||||
|
"no-descending-specificity": null,
|
||||||
|
"declaration-colon-newline-after": "always-multi-line"
|
||||||
},
|
},
|
||||||
"syntax": "scss"
|
"syntax": "scss"
|
||||||
}
|
}
|
||||||
38
.travis.yml
@@ -1,38 +0,0 @@
|
|||||||
language: node_js
|
|
||||||
node_js: 12
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- npm install
|
|
||||||
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- node_modules
|
|
||||||
- app/node_modules
|
|
||||||
- $HOME/.cache/electron
|
|
||||||
- $HOME/.cache/electron-builder
|
|
||||||
- $HOME/.npm/_prebuilds
|
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- ELECTRON_CACHE=$HOME/.cache/electron
|
|
||||||
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
include:
|
|
||||||
- stage: Test
|
|
||||||
script:
|
|
||||||
- npm test
|
|
||||||
- stage: Deploy Linux & Windows
|
|
||||||
if: tag IS present
|
|
||||||
os: linux
|
|
||||||
services: docker
|
|
||||||
script:
|
|
||||||
- docker run --rm --env-file <(env | grep -iE 'DEBUG|NODE_|ELECTRON_|NPM_|CI|CIRCLE|TRAVIS|APPVEYOR_|CSC_|_TOKEN|_KEY|AWS_|STRIP|BUILD_') -v ${PWD}:/project -v ~/.cache/electron:/root/.cache/electron -v ~/.cache/electron-builder:/root/.cache/electron-builder electronuserland/builder:wine /bin/bash -c "npm run build -- --linux --win -p always"
|
|
||||||
before_cache:
|
|
||||||
- rm -rf $HOME/.cache/electron-builder/wine
|
|
||||||
- stage: Deploy Mac
|
|
||||||
if: tag IS present
|
|
||||||
os: osx
|
|
||||||
osx_image: xcode10.2
|
|
||||||
script:
|
|
||||||
- npm run build -- -p always
|
|
||||||
7
.versionrc.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"types": [
|
||||||
|
{"type":"feat","section":"Features"},
|
||||||
|
{"type":"perf","section":"Improvements"},
|
||||||
|
{"type":"fix","section":"Bug Fixes"}
|
||||||
|
]
|
||||||
|
}
|
||||||
720
CHANGELOG.md
@@ -2,36 +2,712 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
### [0.3.4](https://github.com/Fabio286/antares/compare/v0.3.3...v0.3.4) (2021-09-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* new create function tabs ([0203f69](https://github.com/Fabio286/antares/commit/0203f69e95093d25a7ef3e66df1c70f76edcedf2))
|
||||||
|
* new create routine tabs ([3fd26a0](https://github.com/Fabio286/antares/commit/3fd26a05238368ae375197c79d42162a5910bb07))
|
||||||
|
* new create scheduler tabs ([3c5a69a](https://github.com/Fabio286/antares/commit/3c5a69adc9cecdc4b8f46097676005f2a60f06cf))
|
||||||
|
* new create trigger function tabs ([09c07ac](https://github.com/Fabio286/antares/commit/09c07acd5c2a6ed2b75640dbb83e782ed432bc30))
|
||||||
|
* new create trigger tabs ([e217d51](https://github.com/Fabio286/antares/commit/e217d5181b37ec6304151120b4a2aba9455c6a84))
|
||||||
|
* start search when typing with focus on the left bar ([265ed66](https://github.com/Fabio286/antares/commit/265ed66d25d35be99ed0a6b1668dab9f246ed71e))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **UI:** context menu of tables cut if close to bottom edge ([21e3e79](https://github.com/Fabio286/antares/commit/21e3e79ddf9e292bcfc5881b8fa76a1dc58b207c))
|
||||||
|
|
||||||
|
### [0.3.3](https://github.com/Fabio286/antares/compare/v0.3.2...v0.3.3) (2021-08-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* new create table tabs ([c9fa941](https://github.com/Fabio286/antares/commit/c9fa9415787c3953043db5876a99b3664c69c071))
|
||||||
|
* new create view tabs ([8b93c49](https://github.com/Fabio286/antares/commit/8b93c497784ea431f9747c5afb53f6ef075ea9d6))
|
||||||
|
* new table empty state ([0842e00](https://github.com/Fabio286/antares/commit/0842e00098ba420412937aa52276ee33bda53693))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** editing a view causes error for missing database in some conditions ([4048df3](https://github.com/Fabio286/antares/commit/4048df3c7bc2d42a60f7a57c9a4b8b5b445fcd43))
|
||||||
|
* table options not loaded on restored setting tabs at startup ([622b519](https://github.com/Fabio286/antares/commit/622b519cbb5fbe4e38a4baffb8eab169b21eed21))
|
||||||
|
* **UI:** multiple temp tabs opened switching to tables from other elements ([b35fc5b](https://github.com/Fabio286/antares/commit/b35fc5b78bdbeff1422ef088441b17c8b0df663c))
|
||||||
|
* **UI:** no round borders on left of file upload inputs ([d5b2bde](https://github.com/Fabio286/antares/commit/d5b2bde2eaf8ff3e14f49cc26acdcb201b4cb15c))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **UI:** element options in setting tabs accessible directly ([71e2c91](https://github.com/Fabio286/antares/commit/71e2c911ae23e86543b2af1fa885981ff271777d))
|
||||||
|
* **UI:** improved view setting tab ([1983686](https://github.com/Fabio286/antares/commit/198368605b084bd58fd6f7ca0b19895ba23a45e6))
|
||||||
|
* **UI:** primary app color on selected text backgrouns ([756d49b](https://github.com/Fabio286/antares/commit/756d49b2596dea58376c6afa8e0bad0cd62b146c))
|
||||||
|
* **UI:** visual improvements of tables ([bc82289](https://github.com/Fabio286/antares/commit/bc82289d54550a93300fe66d7a660aa70db2fd23))
|
||||||
|
|
||||||
|
### [0.3.2](https://github.com/Fabio286/antares/compare/v0.3.1...v0.3.2) (2021-08-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* contextual menu option to duplicate table fields ([3abff36](https://github.com/Fabio286/antares/commit/3abff3613618ddc86e1d6c898f83bcb360e8e5d9))
|
||||||
|
* **UI:** automatic scroll on selected tab ([e834fe3](https://github.com/Fabio286/antares/commit/e834fe31ac8b73f249e3ad8654e97da834b28cfe))
|
||||||
|
* **UI:** automatic scroll to selected tab element in left bar ([04fc1bb](https://github.com/Fabio286/antares/commit/04fc1bbee0cdc3b02288ff5d3b2c402633cae1f1))
|
||||||
|
* **UI:** button to clear sidebar search input ([1573de5](https://github.com/Fabio286/antares/commit/1573de5b1f545328523aaaaac541f5a8046617be))
|
||||||
|
* **UI:** query tab name based on content ([065de3a](https://github.com/Fabio286/antares/commit/065de3a0a28a9e6667df4e41ac1c5fa5561d2171))
|
||||||
|
* **UI:** shortcuts info on empty query tab ([70354aa](https://github.com/Fabio286/antares/commit/70354aa828121004f70e91d65cb9f0a4811dce57))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* tab selected when clicking closing cross ([07ee1ae](https://github.com/Fabio286/antares/commit/07ee1ae828bb5f9971a263f2e39e73c760fed50a))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* approximate table total updated on table refresh ([dea3780](https://github.com/Fabio286/antares/commit/dea378014dd45bf90f51a599b4a801e0bc22059f))
|
||||||
|
* **UI:** loading animation on tables and table context menu improvements ([372049a](https://github.com/Fabio286/antares/commit/372049ae64232e8e61a974edc8be5b319c5c0811))
|
||||||
|
* update italian translation ([cbe0e29](https://github.com/Fabio286/antares/commit/cbe0e2980a9d4562941aec37a57cc936a46f41d8))
|
||||||
|
|
||||||
|
### [0.3.1](https://github.com/Fabio286/antares/compare/v0.3.0...v0.3.1) (2021-07-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **UI:** tabs or explorebar elements selected with mouse wheel or right button ([a8a47ed](https://github.com/Fabio286/antares/commit/a8a47ed5f7d5d8cbbdd6c33ef8307d9dce5b193b))
|
||||||
|
|
||||||
|
## [0.3.0](https://github.com/Fabio286/antares/compare/v0.2.1...v0.3.0) (2021-07-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* close tabs if element deleted ([320aa8b](https://github.com/Fabio286/antares/commit/320aa8ba04d464f628b938b812165cd1ec786492))
|
||||||
|
* new data tabs ([ab382df](https://github.com/Fabio286/antares/commit/ab382dfbcd89f8ab38a8cbdb4d87e1705618fd0d))
|
||||||
|
* new function, procedure and scheduler tabs ([0a9983d](https://github.com/Fabio286/antares/commit/0a9983d30d6aeb9aee1c68b31a31f8c605deea45))
|
||||||
|
* new trigger function tabs ([58b91eb](https://github.com/Fabio286/antares/commit/58b91ebfe0ab200d76d4cfd442b9f2970ae97384))
|
||||||
|
* new trigger setting tabs ([f6faad9](https://github.com/Fabio286/antares/commit/f6faad98f88222500dfb7265b74c1be914b894cd))
|
||||||
|
* new unsaved change reminder ([f7a74df](https://github.com/Fabio286/antares/commit/f7a74df0097867a82a2a2d8b3c278a81897d7898))
|
||||||
|
* new view setting tabs ([003c02b](https://github.com/Fabio286/antares/commit/003c02b1fbe5aba7f53f3faa5610b0c2f7706793))
|
||||||
|
* option to restore session on startup ([adc5477](https://github.com/Fabio286/antares/commit/adc5477673603cd63fabf77a53db5397e3774e0e))
|
||||||
|
* option to select schema in query tabs ([a73a2f4](https://github.com/Fabio286/antares/commit/a73a2f483ef6927def4ba635e306277c34ae4b53))
|
||||||
|
* **UI:** empty workspace view ([e1855a2](https://github.com/Fabio286/antares/commit/e1855a262dc24363fc143a38a70154938308bd71))
|
||||||
|
* rename tabs if element is renamed ([ef21ea7](https://github.com/Fabio286/antares/commit/ef21ea74481839ba67ca79d40d5f47e1c12aebc0))
|
||||||
|
* **MySQL:** improved schema detection for queries ([5bb4e49](https://github.com/Fabio286/antares/commit/5bb4e496f289f3ae8a46f24d1a2fbb896d9da86e))
|
||||||
|
* **UI:** close temp data tabs ([88c4cdc](https://github.com/Fabio286/antares/commit/88c4cdc8e2a60ffbe26b0f831184c3a6dd9a1637))
|
||||||
|
* **UI:** display schema in data tabs ([0105733](https://github.com/Fabio286/antares/commit/01057332b090997c107bf395bb1fc3b9195e8218))
|
||||||
|
* **UI:** new table settings tabs ([7845e3e](https://github.com/Fabio286/antares/commit/7845e3e501bb7f890d07b42d4f3eb5f9f4bc8586))
|
||||||
|
* **UI:** sortable tabs ([d38097d](https://github.com/Fabio286/antares/commit/d38097d0567020b5265b5a0b347f5e1f38e0b1d4))
|
||||||
|
* **UI:** temporary table data tabs ([a87079c](https://github.com/Fabio286/antares/commit/a87079cd179033cebb6fd228ad7f1b991f3b6c46))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* clear empty indexes and foreign keys on confirm respective modals ([04fa320](https://github.com/Fabio286/antares/commit/04fa320820df1f70a4ef05e4a6e4cbcd4081d047))
|
||||||
|
* enabled copy context on non editable rows ([525c964](https://github.com/Fabio286/antares/commit/525c964c623e7574f6abe732a2b315b8805d5eee))
|
||||||
|
* issues with trigger temp tabs ([6b725b1](https://github.com/Fabio286/antares/commit/6b725b1d40753ad89bfad6f1df57c6fb737e5262))
|
||||||
|
* manual page input not disabled when only one page is available ([62f7e57](https://github.com/Fabio286/antares/commit/62f7e57d0ccf6041b5469bec6a1864aa5b045609))
|
||||||
|
* new field default value unknown instead 'noval' ([77c5d28](https://github.com/Fabio286/antares/commit/77c5d280325792e20befc845f3a6834837131e39))
|
||||||
|
* reload twice after element rename ([1e543aa](https://github.com/Fabio286/antares/commit/1e543aa6b0b63e9134bf544682a26bd98573b794))
|
||||||
|
* solved a vulnerability in table names ([5c855a5](https://github.com/Fabio286/antares/commit/5c855a520a6bc66cc00b0b8afc6d2c03c75c0fab))
|
||||||
|
* sort order of tables is lost switching pages ([14577d1](https://github.com/Fabio286/antares/commit/14577d14bb337898a1fd0fdee1c3760812b9d21f))
|
||||||
|
* wrong editor height with some conditions ([d7fdf53](https://github.com/Fabio286/antares/commit/d7fdf53932a4d0609a88641c40d451c71b12c242))
|
||||||
|
* **UI:** multiple trigger tabs open on single click on explore bar ([e78ca24](https://github.com/Fabio286/antares/commit/e78ca2417e8f996fa0507c5a5586a75393dfe8ee))
|
||||||
|
* **UI:** not disabled buttons during save table setting tabs ([a0105cf](https://github.com/Fabio286/antares/commit/a0105cf1c37144ea27b212029d39275918f4f95e))
|
||||||
|
* tab won't open after table or view creation ([f7c3aa8](https://github.com/Fabio286/antares/commit/f7c3aa883dfcad0a28222c53cf22ab49d381559c))
|
||||||
|
* wrong loaded schema change ([c41e059](https://github.com/Fabio286/antares/commit/c41e059b0ba6d8c6d545c3b86a21a9a2abb6f537))
|
||||||
|
* **UI:** table icon in view data tabs ([f0fa7c8](https://github.com/Fabio286/antares/commit/f0fa7c81b7aa2a05833ce0b243afed39db98d66b))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **UI:** improvements in setting bar connections sort ([0c29e0d](https://github.com/Fabio286/antares/commit/0c29e0d566c792ffd1b2b7045124e170b9c51985))
|
||||||
|
|
||||||
|
### [0.2.1](https://github.com/Fabio286/antares/compare/v0.2.0...v0.2.1) (2021-07-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **UI:** contextual menu shortcuts to create new elements on folders ([0252a06](https://github.com/Fabio286/antares/commit/0252a064d99df09b01c020c7d10c76dd43d4ede8))
|
||||||
|
* context menu option to duplicate connections ([439356a](https://github.com/Fabio286/antares/commit/439356a01993a6a248f5a7d7df305ac5e0e63775))
|
||||||
|
* **MySQL:** possibility to set a default schema in connection parameters ([c6897af](https://github.com/Fabio286/antares/commit/c6897af22d04ed930289a55124b3e8d080fbae3a))
|
||||||
|
* **UI:** new connection add panel ([8cd76e7](https://github.com/Fabio286/antares/commit/8cd76e711c9328254d498b4f9afa221311f5a487))
|
||||||
|
* **UI:** new connection edit panel ([9af71a6](https://github.com/Fabio286/antares/commit/9af71a6e343deda1bf79d8410511cf861af3c304))
|
||||||
|
* SSH Tunnel functionality ([#81](https://github.com/Fabio286/antares/issues/81)) ([1801bef](https://github.com/Fabio286/antares/commit/1801bef019cee77a99df7e3822145ad952465abb))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* clear corrupted configurations to avoid exceptions ([56fcc26](https://github.com/Fabio286/antares/commit/56fcc2650b93ece398118f39f027dc9520dd8a6a))
|
||||||
|
* **UI:** connection tab indicator when scrolling ([a0ab63b](https://github.com/Fabio286/antares/commit/a0ab63bdb533aac9b2bdc2ee07a3b1f2b70ea227))
|
||||||
|
* avoid to trigger schema loading multiple times ([7570b0a](https://github.com/Fabio286/antares/commit/7570b0add8cb9130f15cf8cb807a96dbfd2837d0))
|
||||||
|
|
||||||
|
## [0.2.0](https://github.com/Fabio286/antares/compare/v0.1.13...v0.2.0) (2021-07-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* contextual option to duplicate tables ([08d5b1b](https://github.com/Fabio286/antares/commit/08d5b1b3299adc5c53419f50892aee3b58e6fe72))
|
||||||
|
* **PostgreSQL:** trigger functions support ([75bbd5f](https://github.com/Fabio286/antares/commit/75bbd5f66e38a971482acff9dde636cf89858c8e))
|
||||||
|
* **PostgreSQL:** triggers creation ([7df0cf8](https://github.com/Fabio286/antares/commit/7df0cf83895ba784e30efa7814433cf329175364))
|
||||||
|
* **UI:** active tab animation ([978a7c5](https://github.com/Fabio286/antares/commit/978a7c5f5b314f2ba4067c86471755fdcc341b3f))
|
||||||
|
* **UI:** context option to copy cell or row value ([d868c77](https://github.com/Fabio286/antares/commit/d868c772b9e7c70e173d1f80ad8de51aa40991ef))
|
||||||
|
* **UI:** resizer border mouse hover animation ([a69bdeb](https://github.com/Fabio286/antares/commit/a69bdeb20db7d67613dace554ae37a0c5e112ec2))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **PostgreSQL:** error opening setting tab for some stored routines ([d96907c](https://github.com/Fabio286/antares/commit/d96907ca2de344114e3edae52843f6258468934b))
|
||||||
|
* **UI:** contextual sub-menu alignment when close to the lower edge of the window ([61a42d5](https://github.com/Fabio286/antares/commit/61a42d51f55ec3010c4ee67bad2f699ff14c9f7c))
|
||||||
|
* fields default not correctly set in table filler ([faa07a0](https://github.com/Fabio286/antares/commit/faa07a077c899af5c71124e0c20869876e57fd9a))
|
||||||
|
* rows loses internal id after export ([2d278aa](https://github.com/Fabio286/antares/commit/2d278aa14e0e5080725196538b7265ec00a7aebb))
|
||||||
|
* **UI:** editor blinking on first connection with a dark theme ([0110756](https://github.com/Fabio286/antares/commit/0110756204b79388535a74017b303e1ba41e8fd8))
|
||||||
|
* unhandled exception in connection test ([a975df3](https://github.com/Fabio286/antares/commit/a975df38dd471ab0ac8645a1021470a4600e6598))
|
||||||
|
* vulnerability in server error toast messages ([3aef7e9](https://github.com/Fabio286/antares/commit/3aef7e953ea82a9105d470cc62c68aacfc97f9d9))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* improved contextual menu appearance ([f74bca7](https://github.com/Fabio286/antares/commit/f74bca7bb4fc6802143446a44663fbf12319bf29))
|
||||||
|
* **UI:** increased application border-radius ([a0a025e](https://github.com/Fabio286/antares/commit/a0a025e4502141e95394ca9717b1ec65df2728c1))
|
||||||
|
|
||||||
|
### [0.1.13](https://github.com/Fabio286/antares/compare/v0.1.12...v0.1.13) (2021-06-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **PostgreSQL:** alter trigger support ([5c8ee66](https://github.com/Fabio286/antares/commit/5c8ee66f432585db7bd72103fa814558bf406bcc))
|
||||||
|
* **UI:** ability to manually insert page number in DATA tabs, closes [#65](https://github.com/Fabio286/antares/issues/65) ([e2ebb04](https://github.com/Fabio286/antares/commit/e2ebb04a9047d25187889644aa625fe675de808b))
|
||||||
|
* **UI:** option to change query editors font size, closes [#77](https://github.com/Fabio286/antares/issues/77) ([e579f37](https://github.com/Fabio286/antares/commit/e579f374381b329422b646b8f7ab5acf298db981))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** pool connections not released after MySQL errors, causing endless load animation ([4133fc4](https://github.com/Fabio286/antares/commit/4133fc452fc2e961eb587590e010f4968675db7e))
|
||||||
|
* **UI:** various fixes in displaying content with small window size ([8a20bef](https://github.com/Fabio286/antares/commit/8a20befd0941cb2e6d5e29552606454bd871b092))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* remove comments from queries before execution ([690a454](https://github.com/Fabio286/antares/commit/690a4541f96eaf831dea07b777a421729173b654))
|
||||||
|
|
||||||
|
### [0.1.12](https://github.com/Fabio286/antares/compare/v0.1.11...v0.1.12) (2021-06-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **PostgreSQL:** trigger rename and delete ([cce5adb](https://github.com/Fabio286/antares/commit/cce5adbac75170186956211b54f4db5be25aba07))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* internal exceptions ([8742fa1](https://github.com/Fabio286/antares/commit/8742fa10f08c92a98325df50be9a7df8e36b0f2c))
|
||||||
|
* page offset not reset when selected table changes ([5562e73](https://github.com/Fabio286/antares/commit/5562e73e75c659051a49d61041d1eee9d66ff6e2))
|
||||||
|
* **MySQL:** view's data tab doesn't work with some views, closes [#71](https://github.com/Fabio286/antares/issues/71) ([9ca059d](https://github.com/Fabio286/antares/commit/9ca059d979161c0132f64db372b4ed7ecc844b2d))
|
||||||
|
* **UI:** unable to browse view's result pages ([8a7cc2a](https://github.com/Fabio286/antares/commit/8a7cc2a14fa289c2f3d9e9eb1a5435ffbbaab006))
|
||||||
|
|
||||||
|
### [0.1.11](https://github.com/Fabio286/antares/compare/v0.1.10...v0.1.11) (2021-06-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **UI:** prevent canc key to trigger delete modal when editing a row ([5e9c88a](https://github.com/Fabio286/antares/commit/5e9c88a7fd656dd69b92ba4384ca81f4e8cdd422))
|
||||||
|
* table row loses internal id after cell update ([6622756](https://github.com/Fabio286/antares/commit/66227569f4c032298e20d44c253d30109ffde0b2))
|
||||||
|
* **MySQL:** missing schema altering tables in some conditions ([faa799c](https://github.com/Fabio286/antares/commit/faa799c8ea2329e7ca1b54c9414a060bc8a2a840))
|
||||||
|
* empty offset in cell update queries ([acc1eeb](https://github.com/Fabio286/antares/commit/acc1eeb0948db35f2c9a4d85d70fb654b41b15e5))
|
||||||
|
|
||||||
|
### [0.1.10](https://github.com/Fabio286/antares/compare/v0.1.9...v0.1.10) (2021-05-29)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* key shortcuts to change DATA tab page ([f61d7ee](https://github.com/Fabio286/antares/commit/f61d7eeaf42406eb88b3285ac4606794f56e88d3))
|
||||||
|
* option to set DATA tab page size ([e71c756](https://github.com/Fabio286/antares/commit/e71c7568c09bf8ad184753d3d0f14046f1e386d0))
|
||||||
|
* prev/next buttons to browse the results pages of data tab ([79f033e](https://github.com/Fabio286/antares/commit/79f033e5245e0e744c35540d25a30b1eadc4d603))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** wrong schema in view data tab select, closes [#71](https://github.com/Fabio286/antares/issues/71) ([310cfaa](https://github.com/Fabio286/antares/commit/310cfaa3c2324b1159a46be566b6af2555dc9732))
|
||||||
|
* wrong detection of field default expressions in some cases ([04bdd08](https://github.com/Fabio286/antares/commit/04bdd085a56240273133586d23196e52341d5f27))
|
||||||
|
|
||||||
|
### [0.1.9](https://github.com/Fabio286/antares/compare/v0.1.8...v0.1.9) (2021-05-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** can't access tables having UNIQUE KEY, closes [#69](https://github.com/Fabio286/antares/issues/69) ([f1636f1](https://github.com/Fabio286/antares/commit/f1636f1528b5765423535f84027fdc853c58ce80))
|
||||||
|
|
||||||
|
### [0.1.8](https://github.com/Fabio286/antares/compare/v0.1.7...v0.1.8) (2021-05-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* internal id not removed before row update ([0f10c9e](https://github.com/Fabio286/antares/commit/0f10c9e824b679cfebd8a31085236da3d38a7953))
|
||||||
|
* unable to add new ENUM fields ([1e37f2a](https://github.com/Fabio286/antares/commit/1e37f2a96f660a36f2739fea57ca298059e9c31a))
|
||||||
|
* unable to delete table rows ([0d61371](https://github.com/Fabio286/antares/commit/0d6137195da9e0e9afe68f6d3ebcd50e913a888e))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* improved the way how field default value are handled ([7a766f0](https://github.com/Fabio286/antares/commit/7a766f04e668868e8844aac1d7d445bc2da21558))
|
||||||
|
|
||||||
|
### [0.1.7](https://github.com/Fabio286/antares/compare/v0.1.6...v0.1.7) (2021-05-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* row loses internal id after cell update ([854472c](https://github.com/Fabio286/antares/commit/854472c7a3d8442b1eede12f978cad5aa684094e))
|
||||||
|
* **MySQL:** connection loses schema in some conditions ([6b0b8b1](https://github.com/Fabio286/antares/commit/6b0b8b19d7176ef8647d6f401a33315f8732fdf3))
|
||||||
|
* issue with ENUM and SET fields on table filler modal ([475397c](https://github.com/Fabio286/antares/commit/475397ca34c5b7d8925e2d97d32216d5b80d8211))
|
||||||
|
* issue with ENUM and SET length when creating a new field ([7a62131](https://github.com/Fabio286/antares/commit/7a62131cc707aa1c98bb12513de76229472b7c38))
|
||||||
|
* multiple row select on sorted tables not work properly ([c7663be](https://github.com/Fabio286/antares/commit/c7663be338ccb5ff31e241e824593531ad95bd32))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **core:** increased connection pool size to improve performance ([ae103e5](https://github.com/Fabio286/antares/commit/ae103e5477ad190c541d0f29e277c41de6947063))
|
||||||
|
* **MySQL:** improved connections pool handling ([434711a](https://github.com/Fabio286/antares/commit/434711a360bd7539d2ec3adec92e7ccc96fa828e))
|
||||||
|
|
||||||
|
### [0.1.6](https://github.com/Fabio286/antares/compare/v0.1.5...v0.1.6) (2021-05-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **MySQL:** ENUM and SET fields support, closes [#61](https://github.com/Fabio286/antares/issues/61) ([bebba64](https://github.com/Fabio286/antares/commit/bebba64d06532990405763284e27cb768dc050f7))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* better detection and handling of field default type ([3baf6fa](https://github.com/Fabio286/antares/commit/3baf6fa1736a405c95fb02d17c11514861ca9e04))
|
||||||
|
* no quotes around strings in field default custom value ([29e2d92](https://github.com/Fabio286/antares/commit/29e2d92b5bf66ff9e80bb1fee1274967c4418601))
|
||||||
|
* **UI:** data type figure twice on type select ([9dfe7cc](https://github.com/Fabio286/antares/commit/9dfe7cca2234ce6de512bb4c21205c2217e7f765))
|
||||||
|
* support to mDNS/zeroconf in snap build, closes [#58](https://github.com/Fabio286/antares/issues/58) ([35ef070](https://github.com/Fabio286/antares/commit/35ef070725f5779923bf3ad428b44accddf22dbe))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* italian translation updated ([5dc0b0b](https://github.com/Fabio286/antares/commit/5dc0b0bea40f9d5a5944af2f1c5dc6ff3e60c396))
|
||||||
|
|
||||||
|
### [0.1.5](https://github.com/Fabio286/antares/compare/v0.1.4...v0.1.5) (2021-04-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** multiple queries non properly split in some cases ([5208ec1](https://github.com/Fabio286/antares/commit/5208ec171b44da0e6bfa93f15bfedd03ef2aa868))
|
||||||
|
* % character not properly escaped, closes [#60](https://github.com/Fabio286/antares/issues/60) ([ecfb732](https://github.com/Fabio286/antares/commit/ecfb732c265a5485e131e75f3d20ff07d9409753))
|
||||||
|
* semicolon inside strings breaks queries, closes [#59](https://github.com/Fabio286/antares/issues/59) ([1b09909](https://github.com/Fabio286/antares/commit/1b0990912627a3a4a4e8d62b4593f8a7aa3a7fe5))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **UI:** new application icon ([5822b3d](https://github.com/Fabio286/antares/commit/5822b3df432e0a2b305d0ff37a20dc466c3a3992))
|
||||||
|
|
||||||
|
### [0.1.4](https://github.com/Fabio286/antares/compare/v0.1.3...v0.1.4) (2021-04-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* query results export ([1d363f7](https://github.com/Fabio286/antares/commit/1d363f755e025d0fc6fec61cbd47ff87a8f25728))
|
||||||
|
* **UI:** canc press to delete selected rows ([20cba6e](https://github.com/Fabio286/antares/commit/20cba6ee9bc1daa902b04d6e2ddcb31d04fbf805))
|
||||||
|
* **UI:** ctrl+s shortcut to save changes ([16e17b3](https://github.com/Fabio286/antares/commit/16e17b39b6c8b561cc018d02afee2276190ce304))
|
||||||
|
* **UI:** format and clear queries ([9ffd443](https://github.com/Fabio286/antares/commit/9ffd443a66303f88fc4529896f6d1d7917454f7a))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* launch from shortcut of procedures or functions with parameters without name dont works ([f82dbd2](https://github.com/Fabio286/antares/commit/f82dbd24dcef7b4d8d127a604e256b3f79a6c617))
|
||||||
|
* wrong changelog in some cases ([a41cf1a](https://github.com/Fabio286/antares/commit/a41cf1ab5662f5f5fdedff4a9e1c626c23071377))
|
||||||
|
* **UI:** data type not listed in selection if not present in global types ([6eb2977](https://github.com/Fabio286/antares/commit/6eb2977568987b9440b62ae7dbd7183338bfcc9b))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **UI:** improved connection status indicator ([5ceddb8](https://github.com/Fabio286/antares/commit/5ceddb8e00f3bc1984b8e47de270dc39b367903f))
|
||||||
|
|
||||||
|
### [0.1.3](https://github.com/Fabio286/antares/compare/v0.1.2...v0.1.3) (2021-04-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **PostgreSQL:** functions management ([cd31413](https://github.com/Fabio286/antares/commit/cd3141325681ea572c06b8998dd7bd334ceb3236))
|
||||||
|
* **PostgreSQL:** procedure language select ([b33199e](https://github.com/Fabio286/antares/commit/b33199ea59c60b467601f333857494aa40adf4e8))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** invalid JavaScript datetime values not shown ([dcccb54](https://github.com/Fabio286/antares/commit/dcccb544f9ec24ad693c9e81fb4bcfbdbb7cc4e1))
|
||||||
|
* approximate row count shown for results less than 1000 ([a6b75ad](https://github.com/Fabio286/antares/commit/a6b75ad0dc0d884332464c277e8542b2698630b9))
|
||||||
|
* field apparently loses index or foreign key on rename in table editor ([7d2ace9](https://github.com/Fabio286/antares/commit/7d2ace94562f8da307b15b83c89d919727d800c8))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **MySQL:** improved the way to get routine and functions parameters ([90fd9db](https://github.com/Fabio286/antares/commit/90fd9db917c40262f2bc2501ab86f5feba3d8db4))
|
||||||
|
* **UI:** improved table fields suggestion in query editor ([c22187c](https://github.com/Fabio286/antares/commit/c22187c3053aef368a351cc35e2f1d407ecde209))
|
||||||
|
|
||||||
|
### [0.1.2](https://github.com/Fabio286/antares/compare/v0.1.1...v0.1.2) (2021-04-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* in-app last release changelog ([1e938ad](https://github.com/Fabio286/antares/commit/1e938adc5d8eb5ad16ab16342375eecd88f68d20))
|
||||||
|
* **PostgreSQL:** edit timezone in cell editor ([8735a0c](https://github.com/Fabio286/antares/commit/8735a0c5f9e5b6b3bcaadf37ce158aa7beae2c48))
|
||||||
|
* **PostgreSQL:** procedures management ([3dde1c1](https://github.com/Fabio286/antares/commit/3dde1c109e23342d94362626ef7350dc123ea859))
|
||||||
|
* **PostgreSQL:** support of arrays in table settings ([d0b3e1b](https://github.com/Fabio286/antares/commit/d0b3e1b1b8be9d2c038d70e16d4478671315de8f))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* cell edit doesn't properly use primary or unique index to update if both present, closes [#51](https://github.com/Fabio286/antares/issues/51) ([55932fe](https://github.com/Fabio286/antares/commit/55932fe11583bd5ff48f82b8408965adba4f5071))
|
||||||
|
* deletion of rows from query results ([c20bff7](https://github.com/Fabio286/antares/commit/c20bff7bcbe340ac99ebcacaba3359edd61c068a))
|
||||||
|
* no foreign key select when cell value is NULL, closes [#50](https://github.com/Fabio286/antares/issues/50) ([9f5ec02](https://github.com/Fabio286/antares/commit/9f5ec0276c92904975fdaea34b4c845c92bfe8d4))
|
||||||
|
* wrong datetime conversion when updating a row without an unique key ([d374372](https://github.com/Fabio286/antares/commit/d374372e208318d7e50b258a8041145bdf7992c5))
|
||||||
|
* **PostgreSQL:** issue with selected schema different than public ([49a4e1c](https://github.com/Fabio286/antares/commit/49a4e1cb7b24642641265d5830d3fee370cceeb4))
|
||||||
|
* **UI:** white readonly inputs with dark theme ([bb5f446](https://github.com/Fabio286/antares/commit/bb5f44681f87aacf2cd2f60a6d958c5732289790))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **UI:** improved setting modal rendering ([be816e8](https://github.com/Fabio286/antares/commit/be816e85888b4f3d26cbb9caac0adbc4dde0ea94))
|
||||||
|
|
||||||
|
### [0.1.1](https://github.com/Fabio286/antares/compare/v0.1.0...v0.1.1) (2021-04-03)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* scratchpad to save persistent notes ([e349dd5](https://github.com/Fabio286/antares/commit/e349dd5eaba608591257f2799b830805e4936c27))
|
||||||
|
* **PostgreSQL:** foreign keys management ([fe4c8e1](https://github.com/Fabio286/antares/commit/fe4c8e12b39dd3cdfc233f07e3fe2ff0676252b0))
|
||||||
|
* **PostgreSQL:** indexes management ([9ca03f4](https://github.com/Fabio286/antares/commit/9ca03f462560b634970a19d3d97b878d60509acc))
|
||||||
|
* **PostgreSQL:** table fields edit ([e3f259c](https://github.com/Fabio286/antares/commit/e3f259c6e8327d71bd7dd0a9c33d957dc6ca1fb8))
|
||||||
|
* **PostgreSQL:** tables addition ([feef5e3](https://github.com/Fabio286/antares/commit/feef5e30eec915cbb219223cc428bd4e98d2e9c5))
|
||||||
|
* **PostgreSQL:** unique keys management ([614e0d3](https://github.com/Fabio286/antares/commit/614e0d32758c13b59139d349d4682a5bafc3ca88))
|
||||||
|
* **PostgreSQL:** views management ([99f7511](https://github.com/Fabio286/antares/commit/99f7511c4d5fab4030b30d5134cd03248167faea))
|
||||||
|
* **UI:** light theme ([2806976](https://github.com/Fabio286/antares/commit/280697698ea5fae6d54326970c823878888c196c))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **UI:** editor theme preview not properly loaded in some cases ([c981244](https://github.com/Fabio286/antares/commit/c981244d7aa93ca18ca2de44bf8df06f253b9d20))
|
||||||
|
* fields of ref. table not automatically loaded in foreign keys modal ([e7401cc](https://github.com/Fabio286/antares/commit/e7401cc96e76e00100a88eea9f40541fd8027adb))
|
||||||
|
* hide update tab for Windows Store distributions ([dcb135d](https://github.com/Fabio286/antares/commit/dcb135dd015b8f8c6cfb44021211bb8cf3089192))
|
||||||
|
|
||||||
|
## [0.1.0](https://github.com/Fabio286/antares/compare/v0.0.20...v0.1.0) (2021-03-21)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **PostgreSQL:** database in connection parameters ([9645702](https://github.com/Fabio286/antares/commit/964570247ff5b7b8317419730eec5bed4f0f0580))
|
||||||
|
* **PostgreSQL:** edit array and text search fields ([fc65114](https://github.com/Fabio286/antares/commit/fc651149b95399c52d2d63e946731e9c1b0303a9))
|
||||||
|
* **PostgreSQL:** insert and edit blob fields ([1f80a64](https://github.com/Fabio286/antares/commit/1f80a64fe1400baacca26f1a762c5aeb4ef6350d))
|
||||||
|
* **PostgreSQL:** partial postgre implementation ([d892fa6](https://github.com/Fabio286/antares/commit/d892fa6fb3e86fbb96887d8eb67319ae855260a1))
|
||||||
|
* **PostgreSQL:** support to microseconds ([d465e18](https://github.com/Fabio286/antares/commit/d465e18dba8ea3aa00726e33f9b1f70ca4c0683c))
|
||||||
|
* **UI:** support to boolean fields ([ffb1712](https://github.com/Fabio286/antares/commit/ffb1712a593d1421793011e48a17369b884ea3c0))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* update or delete rows with more than one primary key ([22a8c25](https://github.com/Fabio286/antares/commit/22a8c25717a4d4b285855426098a3a2846ce7448))
|
||||||
|
* **MySQL:** handle NEWDECIMAL data type ([a1c6be3](https://github.com/Fabio286/antares/commit/a1c6be372b570cf13e89ef7ecf9b7a7c033a9293))
|
||||||
|
* **PostgreSQL:** issue getting foreign keys informations ([db47b40](https://github.com/Fabio286/antares/commit/db47b4040a5282a6ac0711b1926c4c2ac867999e))
|
||||||
|
* remove last char from datetime and time if is a dot ([e89911b](https://github.com/Fabio286/antares/commit/e89911b1851c19813d4acf2c79adfbc2ac7c1112))
|
||||||
|
* **PostgreSQL:** single quote escape ([9f6a183](https://github.com/Fabio286/antares/commit/9f6a183d9b293dfe9ad9f3759f2375f05f37db8e))
|
||||||
|
* **PostgreSQL:** various issues in query results ([fccfe92](https://github.com/Fabio286/antares/commit/fccfe92453325cd54c0331cc5670af0a56822c5b))
|
||||||
|
* schema content not loaded if selected with right click ([8a6c59f](https://github.com/Fabio286/antares/commit/8a6c59f7ce7d051315b04cea38a96e4739b5b9d3))
|
||||||
|
|
||||||
|
### [0.0.20](https://github.com/Fabio286/antares/compare/v0.0.19...v0.0.20) (2021-03-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **UI:** loader layers on query and data tabs ([b232a3b](https://github.com/Fabio286/antares/commit/b232a3bb5ff7e38c83aa33e8b96ec7202bc4881e))
|
||||||
|
* **UI:** row markers in sql editors ([ddfb713](https://github.com/Fabio286/antares/commit/ddfb7131248a47fa2055ccfb72e223986a17f986))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **UI:** avoid unnecessary updates when cell content not change ([e9a26c1](https://github.com/Fabio286/antares/commit/e9a26c1bc0641b7087d8143cc948405850d7552f))
|
||||||
|
* **UI:** row mark not applied on first click ([25d72e3](https://github.com/Fabio286/antares/commit/25d72e39529884c09cf1286ff64ba00c8a5c7b24))
|
||||||
|
* **UI:** table rows lose internal id after an update ([76c5c0c](https://github.com/Fabio286/antares/commit/76c5c0c680521b4a20de1f12bab25314ac084d5c))
|
||||||
|
|
||||||
|
### [0.0.19](https://github.com/Fabio286/antares/compare/v0.0.18...v0.0.19) (2021-03-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **UI:** modal that shows process query ([07f60c3](https://github.com/Fabio286/antares/commit/07f60c39173e9db452909d74573c3aecf4b6466b))
|
||||||
|
* processes list tool ([049143d](https://github.com/Fabio286/antares/commit/049143d143d8187bfcb6377f2bf374c471c28046))
|
||||||
|
* **MySQL:** support to new mysql8 authentication, closes [#45](https://github.com/Fabio286/antares/issues/45) ([db44306](https://github.com/Fabio286/antares/commit/db4430609e22816f4a3a8ecdbf61e9b51cde2579))
|
||||||
|
* context menu shortcut to set NULL a table cell ([71b4310](https://github.com/Fabio286/antares/commit/71b43101172c27d810d533c936534bcf089dbca2))
|
||||||
|
* **UI:** esc key to cancel cell edit ([45351fa](https://github.com/Fabio286/antares/commit/45351faeaea6ad4af8280da6c0ec1b4ded0f86fe))
|
||||||
|
* setting to enable beta updates (future use) ([b1ea32b](https://github.com/Fabio286/antares/commit/b1ea32b68024593481120e2ef67642e127554888))
|
||||||
|
* **UI:** query duration calc ([777b73f](https://github.com/Fabio286/antares/commit/777b73fa6f9d6f7806e0d3d07589dfaee0c40786))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** wrong TIMESTAMP fields length ([201fad9](https://github.com/Fabio286/antares/commit/201fad9265e01e19ae4c8dc16bff84ee9dc9c894))
|
||||||
|
* **UI:** modal processes list does not regain size on window resize ([5d7efa7](https://github.com/Fabio286/antares/commit/5d7efa75b76f59b85654603b4df8035a36af0576))
|
||||||
|
* **UI:** wrong height in scrolling tables in some cases ([4862d51](https://github.com/Fabio286/antares/commit/4862d51fba863e055ca6735586b0abe4894037eb))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **UI:** big performance improvement in tables rendering ([39ca197](https://github.com/Fabio286/antares/commit/39ca1974bcea84c9047779893ed3f458300474e3))
|
||||||
|
* **UI:** improvements of date time inputs ([b4ead69](https://github.com/Fabio286/antares/commit/b4ead6992c8ddc172380a97e7aa125e0dd078f1e))
|
||||||
|
|
||||||
|
### [0.0.18](https://github.com/Fabio286/antares/compare/v0.0.17...v0.0.18) (2021-02-25)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **UI:** context menu for input and textarea tags ([b54fefb](https://github.com/Fabio286/antares/commit/b54fefbf255c91f3cdd0c52b917b239ea40dae16))
|
||||||
|
* **UI:** html, xml, json, svg and yaml editor modes in long text fields edit ([9a1bf32](https://github.com/Fabio286/antares/commit/9a1bf3212850d3783ac6382f8a980eb25c4f4226))
|
||||||
|
* **UI:** run procedures/functions from sidebar context menu ([219da0a](https://github.com/Fabio286/antares/commit/219da0aba46839295807a01056199d42584e0af9))
|
||||||
|
* **UI:** run routines/functions from settings tab ([7e81671](https://github.com/Fabio286/antares/commit/7e8167154ffd64e46d78a50cd13cf90cee61370c))
|
||||||
|
* **UI:** search filter in explore bar ([2f58007](https://github.com/Fabio286/antares/commit/2f58007af4f32207fe4be9fc15bb43916e8e0abc))
|
||||||
|
* **UI:** sticky schema names in explore bar ([110b0b4](https://github.com/Fabio286/antares/commit/110b0b414cef25395ae28493032c2048307e4783))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **MySQL:** issue obtaining routine/function parameters ([3aa2159](https://github.com/Fabio286/antares/commit/3aa2159a1ae3b6785646fc7ac1e866cb0277d087))
|
||||||
|
* issue managing function/routine parameters ([76d92cd](https://github.com/Fabio286/antares/commit/76d92cd106b0409e8752c43d7d587d09dd3e1e32))
|
||||||
|
* **UI:** data tab opened when non-table element is selected ([dbab06f](https://github.com/Fabio286/antares/commit/dbab06fcb80ccdae85215ceae9ba24af3e7ff263))
|
||||||
|
* **UI:** elements from previous selected schemas in query suggestions ([c8545a2](https://github.com/Fabio286/antares/commit/c8545a250bc696c339e418a69a2942d5bac10cbf))
|
||||||
|
* disabled sort for fields without a name property ([3b37b74](https://github.com/Fabio286/antares/commit/3b37b7432e969e6315ebd6ed74905ec3980a049c))
|
||||||
|
* prevents F5 shortcut to run in non-selected workspaces ([7c4ca99](https://github.com/Fabio286/antares/commit/7c4ca999ce64af8077d29e419f430d5beaaead93))
|
||||||
|
* support of bit fields in table filler ([94c4952](https://github.com/Fabio286/antares/commit/94c49523197284510361ebbb6984816a3bb1b243))
|
||||||
|
|
||||||
|
### [0.0.17](https://github.com/Fabio286/antares/compare/v0.0.16...v0.0.17) (2021-02-17)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Added french language ([18a93ef](https://github.com/Fabio286/antares/commit/18a93ef1aae530e69c9062bbeb08a3beec206eda))
|
||||||
|
* fake table data generator ([a176174](https://github.com/Fabio286/antares/commit/a176174b8d7dc232920f4cd7c5e3f8e4c58d51a0))
|
||||||
|
* min and max option for random floats and numbers ([6c62052](https://github.com/Fabio286/antares/commit/6c62052b4764731c774fef90784342447b36deb7))
|
||||||
|
* support to fake data locales ([970de49](https://github.com/Fabio286/antares/commit/970de4962b3bffec271bfa6d4747e6fb9d408ba6))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **UI:** file uploader in table filler ([b5a8283](https://github.com/Fabio286/antares/commit/b5a828309f067636eae2120032f07e01233706e2))
|
||||||
|
* **UI:** no foreign key select editing query results ([5b21d17](https://github.com/Fabio286/antares/commit/5b21d17f3a8f2482c3aafe85f26c34cd3c0a1fcf))
|
||||||
|
* cut faker text based on field length ([288ff4c](https://github.com/Fabio286/antares/commit/288ff4c1a1c77f4b8b86b24649d805836366fdd3))
|
||||||
|
* wrong date or time detection in field default ([9d5ebef](https://github.com/Fabio286/antares/commit/9d5ebefdced999af595f3d8dc2fac2b18fa8258b))
|
||||||
|
* **UI:** better text on ssl file selectors ([9a19085](https://github.com/Fabio286/antares/commit/9a190854fe3c73ed4e7f89545ff259e15ee9f947))
|
||||||
|
* **UI:** wrong length for char fields on table header ([0f69d1d](https://github.com/Fabio286/antares/commit/0f69d1dbb7958e45059b6b738c845abea1ad3225))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* **core:** bulk inserts support ([b0576ac](https://github.com/Fabio286/antares/commit/b0576acdf65d41c1c8e0b0bca6cf6522dcb372be))
|
||||||
|
|
||||||
|
### [0.0.16](https://github.com/Fabio286/antares/compare/v0.0.15...v0.0.16) (2021-02-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* MySQL and MariaDB auto detection ([02c03e3](https://github.com/Fabio286/antares/commit/02c03e3d266052872ac8edeb159ec8182f41c6a5))
|
||||||
|
* **UI:** enanched file upload input ([a0d8552](https://github.com/Fabio286/antares/commit/a0d85520fb0669d5eec4475f5e255adb1e2a0159))
|
||||||
|
* support to ssl connections ([4e72bb1](https://github.com/Fabio286/antares/commit/4e72bb15874324214aa0f5b89057cdd565744468))
|
||||||
|
* **UI:** database version in app footer ([15417e8](https://github.com/Fabio286/antares/commit/15417e8a776c6aa9f90762d198d70b26163bb2df))
|
||||||
|
* **UI:** resize query editor area ([88ab7c5](https://github.com/Fabio286/antares/commit/88ab7c5a62654c6a6026b63464224226d33b1950))
|
||||||
|
* delete rows from tables without a primary key ([574d493](https://github.com/Fabio286/antares/commit/574d4939083577ffcb8e7c65f572c364eb8415fb))
|
||||||
|
* edit rows from tables without a primary key ([5940b0b](https://github.com/Fabio286/antares/commit/5940b0b84207093da141b5c617c41e0a18fcb1d7))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* compatibility with electron-store 7 ([bacf458](https://github.com/Fabio286/antares/commit/bacf45893676cb0744907703f6534ffd472bd1dd))
|
||||||
|
* edit bit fields ([ede6fe8](https://github.com/Fabio286/antares/commit/ede6fe81cefc91cdce2bbb0cd7cd6f85bbca99b8))
|
||||||
|
|
||||||
|
### [0.0.15](https://github.com/Fabio286/antares/compare/v0.0.14...v0.0.15) (2021-01-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* functions and schedulers in query suggestions ([8ff6e70](https://github.com/Fabio286/antares/commit/8ff6e70145ed2a207ae8b23a2c688258382a5d74))
|
||||||
|
* loading animation in properties tabs ([1cf6485](https://github.com/Fabio286/antares/commit/1cf64858964f4894913db42f7c268013bb06e40b))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* error retriving dato of some schedulers ([b9ed8dd](https://github.com/Fabio286/antares/commit/b9ed8dd610e3be1489e01cf53f7d632cb1bd6ac5))
|
||||||
|
* unable to call stored routines from query tabs ([4923128](https://github.com/Fabio286/antares/commit/4923128236131482ca948ae8052c294bd9269ed0))
|
||||||
|
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
|
||||||
|
* better fields type detection ([4bc9bbf](https://github.com/Fabio286/antares/commit/4bc9bbfb34ebdc51061f718cdf9cbca8507fa0f4))
|
||||||
|
* big performance improvement in database structure loading ([a11bac5](https://github.com/Fabio286/antares/commit/a11bac504cd4ee865ea6c614a15ee809dc38202e))
|
||||||
|
|
||||||
|
### [0.0.14](https://github.com/Fabio286/antares/compare/v0.0.13...v0.0.14) (2021-01-16)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* export data tables to json or csv file ([0cbea9d](https://github.com/Fabio286/antares/commit/0cbea9d1007304a5b9cf893d165b4b4104266651))
|
||||||
|
* functions creation ([49d7172](https://github.com/Fabio286/antares/commit/49d71722e26172232f7b54c6568e1e588ce0d049))
|
||||||
|
* functions delete ([59a50bc](https://github.com/Fabio286/antares/commit/59a50bc014facc9643f9153cff61dc9d5a8605a9))
|
||||||
|
* functions edit ([41d75b1](https://github.com/Fabio286/antares/commit/41d75b127cbcf1481fd259a14e6e7688638e18a4))
|
||||||
|
* scheduler edit ([ceab4ef](https://github.com/Fabio286/antares/commit/ceab4ef243881ba64517fb95320844a21fce4849))
|
||||||
|
* schedulers creation ([dbe7b9d](https://github.com/Fabio286/antares/commit/dbe7b9dd239248e806377ae6236b477456f175a3))
|
||||||
|
* schedulers delete ([1e7d4ca](https://github.com/Fabio286/antares/commit/1e7d4ca347f4b9337ff266ec78bb4bbc6dd20d4d))
|
||||||
|
* triggers and stored routines in sql suggestions ([e351c90](https://github.com/Fabio286/antares/commit/e351c903a8a8d7e908d6a7d54c0491438ac6f024))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* error with empty functions/procedures ([f150508](https://github.com/Fabio286/antares/commit/f1505085477a760a768a7d245c9517a858c1379c))
|
||||||
|
* removed internal row _id from exported files ([c0a32c0](https://github.com/Fabio286/antares/commit/c0a32c040e653729ef80d580d6dd1796d1b2adcd))
|
||||||
|
|
||||||
|
### [0.0.13](https://github.com/Fabio286/antares/compare/v0.0.12...v0.0.13) (2021-01-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* option to toggle line wrap mode ([d94b49f](https://github.com/Fabio286/antares/commit/d94b49febf54b0200127859f2a8ed7ef591e56ab))
|
||||||
|
* select definer in view creation/edit ([ab307f8](https://github.com/Fabio286/antares/commit/ab307f82b1d78c5f9571233090b6678a964bd674))
|
||||||
|
* stored routines creation ([3bcd02f](https://github.com/Fabio286/antares/commit/3bcd02fc4ea9a4b780305212f906d6d78c7a8dae))
|
||||||
|
* stored routines delete ([aa33850](https://github.com/Fabio286/antares/commit/aa3385028685417860b3ce985cc7a74f9da377ad))
|
||||||
|
* stored routines edit ([82fdc0b](https://github.com/Fabio286/antares/commit/82fdc0bcd7514b321c1c9852a773adacf81baf87))
|
||||||
|
* triggers creation ([d695c9f](https://github.com/Fabio286/antares/commit/d695c9f8d2418a6a4523a7a242fa1a8cba80e035))
|
||||||
|
* triggers delete ([b32132a](https://github.com/Fabio286/antares/commit/b32132ad84d5798555b80eec3c624b681c37c339))
|
||||||
|
* triggers edit ([3126625](https://github.com/Fabio286/antares/commit/3126625461f4b6d68d641b6b0eda8fcd390bb636))
|
||||||
|
* views creation ([8c4aaec](https://github.com/Fabio286/antares/commit/8c4aaec167f58333a343b52927205b68137ad408))
|
||||||
|
* views deletion ([dcf469e](https://github.com/Fabio286/antares/commit/dcf469ebed6252b4a496800206e0c34cd83b1f5e))
|
||||||
|
* views edit ([56f2a27](https://github.com/Fabio286/antares/commit/56f2a27f0059cc10316204210db078a97408973c))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* breadcrumb not change after table rename ([b6b7be0](https://github.com/Fabio286/antares/commit/b6b7be098ad5ab4d55bfe05a7f862f045c1f54da))
|
||||||
|
* unable to rename views ([b7053bd](https://github.com/Fabio286/antares/commit/b7053bdf8036d027e1685d6b5080d6b927a80e08))
|
||||||
|
* wrong new stored routine modal icon ([0ec2710](https://github.com/Fabio286/antares/commit/0ec2710872c692b3feac076a3250d3b760af4009))
|
||||||
|
* wrong or duplicate fields in some queries ([0df2b83](https://github.com/Fabio286/antares/commit/0df2b836b15436a2397d6a2202bd049b5cd53de4))
|
||||||
|
|
||||||
|
### [0.0.12](https://github.com/Fabio286/antares/compare/v0.0.11...v0.0.12) (2020-12-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* better security connections credentials storage ([fc35f27](https://github.com/Fabio286/antares/commit/fc35f271d7fe384cd786ce33547c0ef17135ddd8))
|
||||||
|
* option to change editor theme ([a95b8d1](https://github.com/Fabio286/antares/commit/a95b8d188cfcc8f563ad73b4f0b676d068775d36))
|
||||||
|
* option to toggle editor auto completion ([155154b](https://github.com/Fabio286/antares/commit/155154b43d0cd02ae875ded3ce865a37a999da5c))
|
||||||
|
* query editor auto-completer for tables and columns ([cb1fce6](https://github.com/Fabio286/antares/commit/cb1fce6f998ea7332886820910e245ab19416a9d))
|
||||||
|
|
||||||
|
### [0.0.11](https://github.com/Fabio286/antares/compare/v0.0.10...v0.0.11) (2020-12-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* auto focus on first input in modals ([1476e89](https://github.com/Fabio286/antares/commit/1476e899d164562f12342ced8c76903b9bdcfa55))
|
||||||
|
* foreign keys management ([206597e](https://github.com/Fabio286/antares/commit/206597e5b891e13e6f7635075bd11599355ab778))
|
||||||
|
* improved data table sorts ([5712b80](https://github.com/Fabio286/antares/commit/5712b8002203b32027f0e820f98a61e2ec965e79))
|
||||||
|
* query tabs auto focus ([f81312a](https://github.com/Fabio286/antares/commit/f81312aeb02ef55affd2ae9e81a9b4cb4c2e6da2))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* data tab sort not maintained at refresh ([15b08d7](https://github.com/Fabio286/antares/commit/15b08d7ea858cb28111c7b548af9a13b1bf0da91))
|
||||||
|
* deletion of rows with non-numeric ID ([d385832](https://github.com/Fabio286/antares/commit/d38583262e672a2b47c5ad0aca0f13c129830a7b))
|
||||||
|
* file field editor not show ([9291a7a](https://github.com/Fabio286/antares/commit/9291a7a7b41e7aeb9b65c7f32e496f523e482272))
|
||||||
|
* improved changes dedection in props tab ([acebe43](https://github.com/Fabio286/antares/commit/acebe435ff6fa1581692fbf7ee1bc23b334e3947))
|
||||||
|
* some properties do not reset after fields changes ([3ed5ea0](https://github.com/Fabio286/antares/commit/3ed5ea023e1852d724b2b59ab156f8722876f85b))
|
||||||
|
* unable to switch tabs when no table selected ([c545815](https://github.com/Fabio286/antares/commit/c5458159d1e30cecf6615086c074d98b4b599637))
|
||||||
|
* wrong field type detection ([5cfdc9b](https://github.com/Fabio286/antares/commit/5cfdc9b92d4b778a7863b02fd64e6445236c89bc))
|
||||||
|
|
||||||
|
### [0.0.10](https://github.com/Fabio286/antares/compare/v0.0.9...v0.0.10) (2020-12-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* approximate totals in table tata tab ([e95d29c](https://github.com/Fabio286/antares/commit/e95d29c7c37e24e7cc14b466f9b539fa667042c2))
|
||||||
|
* create new tables ([e6602d1](https://github.com/Fabio286/antares/commit/e6602d1bfa9ca10c6bb078ee80ddc94fb338763d))
|
||||||
|
* display all keys in properties tab ([27769f2](https://github.com/Fabio286/antares/commit/27769f204f731d20c7ba2f838c02b7c2f28fa0c3))
|
||||||
|
* drop and truncate tables ([a4122b4](https://github.com/Fabio286/antares/commit/a4122b4eaaa5b30d97ba5a93df8c9d21c30bc40b))
|
||||||
|
* index management ([41505bd](https://github.com/Fabio286/antares/commit/41505bde6547c0af3c3413248ad8a0d182838bb1))
|
||||||
|
* tables options edit ([0805b96](https://github.com/Fabio286/antares/commit/0805b96a75e439a7d65e8341ecc86fa938679a9f))
|
||||||
|
* unsaved changes reminder ([33d1fa2](https://github.com/Fabio286/antares/commit/33d1fa22905f477924292135b0dcfefe168ee641))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* index deletion issue ([f8cf90a](https://github.com/Fabio286/antares/commit/f8cf90a89e7367c95e164b7dc669506df392b700))
|
||||||
|
* some problems with properties and data tabs when changing database from sidebar ([0fe7157](https://github.com/Fabio286/antares/commit/0fe71572a5e74c17a5c66237351bb0b02c33e824))
|
||||||
|
* sqlEscaper function wrong quotes conversion ([dfb24c6](https://github.com/Fabio286/antares/commit/dfb24c65f3c395d78d27a2f29e9aa8eeb427cff7))
|
||||||
|
|
||||||
|
### [0.0.9](https://github.com/EStarium/antares/compare/v0.0.8...v0.0.9) (2020-11-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* ability to edit table fields ([249926b](https://github.com/EStarium/antares/commit/249926b8e040d62d50244362b7f999f26337b93c))
|
||||||
|
* support to aliased tables ([1658432](https://github.com/EStarium/antares/commit/1658432fd30073ba3bffb39b5c4ca69194ae1330))
|
||||||
|
* table fields addition ([0765403](https://github.com/EStarium/antares/commit/07654039b6a99f3115c378b53d659593e5c81f35))
|
||||||
|
* table fields deletion ([242ddec](https://github.com/EStarium/antares/commit/242ddec744814d15657db1ca88b2d865045ea219))
|
||||||
|
* **ui:** display table properties tab ([2dc16e8](https://github.com/EStarium/antares/commit/2dc16e8ea8d6d9b79288335888e155ff180eebf5))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* duplicate header fields on join result tables ([ea9b489](https://github.com/EStarium/antares/commit/ea9b489f5f45cffa4a7ac87873fff070205e88c4))
|
||||||
|
* F9 key shortcut refresh all query tabs instead of just selected one ([c9ba2e5](https://github.com/EStarium/antares/commit/c9ba2e5962eae1afc46daf35e55fb0ea5c3af5a4))
|
||||||
|
* issue with tabs horizontal scroll with wheel ([c393f86](https://github.com/EStarium/antares/commit/c393f86947d1f65f896cadaa39d53f13e0a1f4eb))
|
||||||
|
* zero fill field option was not saved ([3e5770f](https://github.com/EStarium/antares/commit/3e5770f7de51bdf2bc0f1f38c7ceb9ef0f4dcd00))
|
||||||
|
* **mysql:** error getting foreign key list ([ee18388](https://github.com/EStarium/antares/commit/ee183886f64947305cc4f0d38dbdf7919953ec01))
|
||||||
|
* wrong result fields type and order with some queries ([a8cd177](https://github.com/EStarium/antares/commit/a8cd17748f4ac7d75092f65ae7ca5f96a8a9e8c5))
|
||||||
|
|
||||||
|
### [0.0.8](https://github.com/EStarium/antares/compare/v0.0.7...v0.0.8) (2020-10-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **render:** field type and length on table header mouse hover ([04804b0](https://github.com/EStarium/antares/commit/04804b07c71cec271c31ace13bd41b2c7415e892))
|
||||||
|
* close modals pressing ESC ([d563cec](https://github.com/EStarium/antares/commit/d563cec70d996f66c4f724bba7de618fc8678e66))
|
||||||
|
* data table autorefresh, closes [#36](https://github.com/EStarium/antares/issues/36) ([9ecd888](https://github.com/EStarium/antares/commit/9ecd88870d1fcf32bb2c970a1506206c477810a0))
|
||||||
|
* pie chart with table size in database explore bar ([426628f](https://github.com/EStarium/antares/commit/426628f268c77496a13b3498f03fd7b11fee299a))
|
||||||
|
* query and data tabs keyboard shortcuts (F5, F9) ([0bf2c8d](https://github.com/EStarium/antares/commit/0bf2c8dc9dd9bdf7a8f48bed61eed7f1f1aacf71))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* context menu outside window when near bottom or right edge ([d4ecaf6](https://github.com/EStarium/antares/commit/d4ecaf65e56044170139bac61c3ee69efc35a8f0))
|
||||||
|
* disable cell editor for not editable results ([b7c779e](https://github.com/EStarium/antares/commit/b7c779eef63c257c166e7128ea643bdd6142aa88))
|
||||||
|
* missing connection name when "ask for crendential" selected ([c70e5b4](https://github.com/EStarium/antares/commit/c70e5b422c3534e92a64a0b534eb58663621489c))
|
||||||
|
* missing header for some query results ([d560c38](https://github.com/EStarium/antares/commit/d560c384f5aed58ea975935975843c3b9061dd85))
|
||||||
|
* no connection passed to connection's edit modal ([ce25cd0](https://github.com/EStarium/antares/commit/ce25cd0a3130db486ea4da24dd393d45c2ef9e0d))
|
||||||
|
|
||||||
### [0.0.7](https://github.com/EStarium/antares/compare/v0.0.6...v0.0.7) (2020-10-03)
|
### [0.0.7](https://github.com/EStarium/antares/compare/v0.0.6...v0.0.7) (2020-10-03)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* database creation ([3d0a83f](https://github.com/EStarium/antares/commit/3d0a83f2cf68c4dd412fd7679c39d63f081b7c19))
|
* Database creation ([3d0a83f](https://github.com/EStarium/antares/commit/3d0a83f2cf68c4dd412fd7679c39d63f081b7c19))
|
||||||
* databases deletion ([4288a1f](https://github.com/EStarium/antares/commit/4288a1fd331f4a28de2e756f898d208a6a6599c4))
|
* Database deletion ([4288a1f](https://github.com/EStarium/antares/commit/4288a1fd331f4a28de2e756f898d208a6a6599c4))
|
||||||
* edit database collation ([54717e1](https://github.com/EStarium/antares/commit/54717e1f6a36ec0b3dd096d0e1e747512f6dda09))
|
* Edit database collation ([54717e1](https://github.com/EStarium/antares/commit/54717e1f6a36ec0b3dd096d0e1e747512f6dda09))
|
||||||
* field comment on mouse over a table field name ([2554444](https://github.com/EStarium/antares/commit/2554444322b59a6b1ab3ff05ccf8604bf6f8c8b8))
|
* Field comment on mouse over a table field name ([2554444](https://github.com/EStarium/antares/commit/2554444322b59a6b1ab3ff05ccf8604bf6f8c8b8))
|
||||||
* support to multiple queries in the same tab ([48f77ba](https://github.com/EStarium/antares/commit/48f77bae01efbff40bd0f5ce8c66e2619f44bf3a))
|
* Support to multiple queries in the same tab ([48f77ba](https://github.com/EStarium/antares/commit/48f77bae01efbff40bd0f5ce8c66e2619f44bf3a))
|
||||||
* update italian translation ([89c3dc9](https://github.com/EStarium/antares/commit/89c3dc9fede63c77eb22b48df1a375ea44830306))
|
* Update italian translation ([89c3dc9](https://github.com/EStarium/antares/commit/89c3dc9fede63c77eb22b48df1a375ea44830306))
|
||||||
* Update italian translation ([fe3d741](https://github.com/EStarium/antares/commit/fe3d7416013c44a4974471ab59b7c9a98afb7255))
|
* **Spanish translation** thanks to
|
||||||
|
[hongkfui](https://github.com/hongkfui) ([#32](https://github.com/EStarium/antares/pull/32))
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* cell update soft reload doesn't apply changes ([1b04b21](https://github.com/EStarium/antares/commit/1b04b216b21b697e47062a9366bc1b6a040a1a72))
|
* Cell update soft reload doesn't apply changes ([1b04b21](https://github.com/EStarium/antares/commit/1b04b216b21b697e47062a9366bc1b6a040a1a72))
|
||||||
* empty databases not shown in explore bar ([3e737cb](https://github.com/EStarium/antares/commit/3e737cba62f795f225e944939c6bff04b27fa3d4))
|
* Empty databases not shown in explore bar ([3e737cb](https://github.com/EStarium/antares/commit/3e737cba62f795f225e944939c6bff04b27fa3d4))
|
||||||
* glitch on table data tab ([10b426b](https://github.com/EStarium/antares/commit/10b426b90b6b9461cfffce3026c982463f6e0599))
|
* Glitch on table data tab ([10b426b](https://github.com/EStarium/antares/commit/10b426b90b6b9461cfffce3026c982463f6e0599))
|
||||||
* lack of loading progressbar when an update is available ([86aec4f](https://github.com/EStarium/antares/commit/86aec4f5e41c059e88066a01f0d85155de99a5ee))
|
* Lack of loading progressbar when an update is available ([86aec4f](https://github.com/EStarium/antares/commit/86aec4f5e41c059e88066a01f0d85155de99a5ee))
|
||||||
* missing schema when queryng INFORMATION_SCHEMA ([530d1bd](https://github.com/EStarium/antares/commit/530d1bd43fa95de05f594b9b5cae2f4b397f96e0))
|
* Missing schema when queryng INFORMATION_SCHEMA ([530d1bd](https://github.com/EStarium/antares/commit/530d1bd43fa95de05f594b9b5cae2f4b397f96e0))
|
||||||
* prevent multiple app instances ([12fbe8c](https://github.com/EStarium/antares/commit/12fbe8c1a03259648554f2a5c69b5abbedc18a48))
|
* Prevent multiple app instances ([12fbe8c](https://github.com/EStarium/antares/commit/12fbe8c1a03259648554f2a5c69b5abbedc18a48))
|
||||||
* several fix on data and query tabs ([530907d](https://github.com/EStarium/antares/commit/530907d097ac4d995e1bfcb02e6c890fd6007e21))
|
* Several fix on data and query tabs ([530907d](https://github.com/EStarium/antares/commit/530907d097ac4d995e1bfcb02e6c890fd6007e21))
|
||||||
* unable to obtain fields informations for some queries ([43c7072](https://github.com/EStarium/antares/commit/43c7072c1c83a2455ae48a37be69b444b3eb6560))
|
* Unable to obtain fields informations for some queries ([43c7072](https://github.com/EStarium/antares/commit/43c7072c1c83a2455ae48a37be69b444b3eb6560))
|
||||||
* unable to obtain keyUsage informations when adding new row ([023c6a6](https://github.com/EStarium/antares/commit/023c6a633a7f268b1a97b748ad08d2416cc30ffe))
|
* Unable to obtain keyUsage informations when adding new row ([023c6a6](https://github.com/EStarium/antares/commit/023c6a633a7f268b1a97b748ad08d2416cc30ffe))
|
||||||
* value overridden when join tables with fields with same name ([78965d2](https://github.com/EStarium/antares/commit/78965d23e3efb7d8d6d110d79142966e57200757))
|
* Value overridden when join tables with fields with same name ([78965d2](https://github.com/EStarium/antares/commit/78965d23e3efb7d8d6d110d79142966e57200757))
|
||||||
* wrong field names when join tables ([ad0bad8](https://github.com/EStarium/antares/commit/ad0bad8486c3d67ec14ec1aed3d8aff6cce9df87))
|
* Wrong field names when join tables ([ad0bad8](https://github.com/EStarium/antares/commit/ad0bad8486c3d67ec14ec1aed3d8aff6cce9df87))
|
||||||
* wrong italian translation ([b29e07c](https://github.com/EStarium/antares/commit/b29e07c3b722aec7e78f3cef2e357a53cbcac474))
|
* Wrong italian translation ([b29e07c](https://github.com/EStarium/antares/commit/b29e07c3b722aec7e78f3cef2e357a53cbcac474))
|
||||||
* wrong schema fetching table fields and key usage ([8e71f42](https://github.com/EStarium/antares/commit/8e71f42a28060fdfeeb81502b0759d0d11f5bcfd))
|
* Wrong schema fetching table fields and key usage ([8e71f42](https://github.com/EStarium/antares/commit/8e71f42a28060fdfeeb81502b0759d0d11f5bcfd))
|
||||||
* wrong table and schema when more than one query in a tab ([4684b41](https://github.com/EStarium/antares/commit/4684b4114b9c9c253120292d7d164d7676011f86))
|
* Wrong table and schema when more than one query in a tab ([4684b41](https://github.com/EStarium/antares/commit/4684b4114b9c9c253120292d7d164d7676011f86))
|
||||||
|
|
||||||
### [0.0.6](https://github.com/EStarium/antares/compare/v0.0.5...v0.0.6) (2020-09-03)
|
### [0.0.6](https://github.com/EStarium/antares/compare/v0.0.5...v0.0.6) (2020-09-03)
|
||||||
|
|
||||||
|
|||||||
102
README.md
@@ -1,76 +1,114 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<img width="800" src="https://raw.githubusercontent.com/Fabio286/antares/master/docs/screen-alpha.png">
|
<img width="800" src="https://raw.githubusercontent.com/Fabio286/antares/master/docs/gh-logo.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
# Antares SQL Client
|
# Antares SQL Client
|
||||||
|
|
||||||
 [](https://travis-ci.com/EStarium/antares)  
|
  [](https://actions-badge.atrox.dev/fabio286/antares/goto) [](https://snapcraft.io/antares) [](https://snapcraft.io/antares) [](https://twitter.com/AntaresSQL) [](https://www.treedom.net/en/user/fabio-di-stasio/event/antares-for-the-planet)
|
||||||
|
|
||||||
Antares is an SQL client based on [Electron.js](https://github.com/electron/electron) and [Vue.js](https://github.com/vuejs/vue) that aims to become a useful tool, especially for developers.
|
Antares is an SQL client based on [Electron.js](https://github.com/electron/electron) and [Vue.js](https://github.com/vuejs/vue) that aims to become a useful tool, especially for developers.
|
||||||
My target is to support as many databases as possible, and all major operating systems, including the ARM versions.
|
Our target is to support as many databases as possible, and all major operating systems, including the ARM versions.
|
||||||
|
|
||||||
**At the moment this application is an alpha, it lacks many features, and isn't ready as a main SQL client**. However i'm actively working on it, hoping to provide all essential features as soon as possible.
|
**At the moment this application is in development state, many features will come in future updates**, and supports only MySQL/MariaDB and PostgreSQL.
|
||||||
|
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.
|
||||||
|
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 this early state of Antares you can download and install the [latest release](https://github.com/EStarium/antares/releases).
|
🔗 If you are curious to try Antares you can download and install the [latest release](https://github.com/Fabio286/antares/releases/latest).
|
||||||
👁 To stay tuned for new releases watch this repo on **Release only** channel.
|
👁 To stay tuned for new releases [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.
|
||||||
|
|
||||||
|
## Current key features
|
||||||
|
|
||||||
|
- Multiple database connections at same time.
|
||||||
|
- Database management (add/edit/delete).
|
||||||
|
- Full tables management, including indexes and foreign keys.
|
||||||
|
- Views, triggers, stored routines, functions and schedulers management (add/edit/delete).
|
||||||
|
- A modern and friendly tab system; keep open every kind of tab you need in your workspace.
|
||||||
|
- Fake table data filler to generate tons of data for test purpose.
|
||||||
|
- Query suggestions and auto complete.
|
||||||
|
- SSH tunnel support.
|
||||||
|
- Dark and light theme.
|
||||||
|
- Editor themes.
|
||||||
|
- Scratchpad.
|
||||||
|
- Secure password storage.
|
||||||
|
|
||||||
## Philosophy
|
## Philosophy
|
||||||
|
|
||||||
Why am I 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 totally free, cross platform and open source alternative, empowered by JavaScript's ecosystem.
|
The main goal is to develop a totally free, full featured, cross platform and open source alternative, empowered by JavaScript's ecosystem.
|
||||||
An application created with minimalism and semplicity in mind, with features in the right places, not hundreds of tiny buttons or submenu.
|
A modern application created with minimalism and semplicity in mind, with features in the right places, not hundreds of tiny buttons, nested tabs or submenu; productivity comes first.
|
||||||
|
|
||||||
## How to contribute
|
## Installation
|
||||||
|
|
||||||
- [Translate Antares](https://github.com/EStarium/antares/wiki/Translate-Antares)
|
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.
|
||||||
|
|
||||||
## Roadmap
|
### Linux
|
||||||
|
|
||||||
|
On Linux you can simply download and run `.AppImage` distributions, install from Snap Store or from AUR.
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
On Windows you can choose between Microsoft Store and download `.exe` distribution. The latter lacks of a certificate, so to install you need to click on "More info" and then "Run anyway" on SmartScreen prompt.
|
||||||
|
|
||||||
|
### MacOS
|
||||||
|
|
||||||
|
On macOS you can run `.dmg` distribution following [this guide](https://support.apple.com/guide/mac-help/mh40616/mac) to install apps from unknown developers.
|
||||||
|
|
||||||
|
## Download
|
||||||
|
|
||||||
|
[](https://snapcraft.io/antares) [](https://aur.archlinux.org/packages/antares-sql/) [](https://www.microsoft.com/p/antares-sql-client/9nhtb9sq51r1?cid=storebadge&ocid=badge&rtc=1&activetab=pivot:overviewtab)
|
||||||
|
🚀 **[Other Downloads](https://github.com/Fabio286/antares/releases/latest)**
|
||||||
|
|
||||||
|
## Coming soon
|
||||||
|
|
||||||
This is a roadmap with major features will come in near future.
|
This is a roadmap with major features will come in near future.
|
||||||
|
|
||||||
- Improvements of query editor area.
|
- Support for other databases.
|
||||||
- Database management (add/edit/delete).
|
|
||||||
- Tables management (add/edit/delete).
|
|
||||||
- Users management (add/edit/delete).
|
|
||||||
- Stored procedures, views, schedulers and trigger support.
|
|
||||||
- Database tools.
|
- Database tools.
|
||||||
- Context menu shortcuts.
|
- Users management (add/edit/delete).
|
||||||
- Keyboard shortcuts.
|
- Query history and bookmarks.
|
||||||
- More secure password storage.
|
- More context menu shortcuts.
|
||||||
- Query logs console.
|
- More keyboard shortcuts.
|
||||||
- Fake data filler.
|
|
||||||
- Import/export and migration.
|
- Import/export and migration.
|
||||||
- SSH tunnel.
|
- Apple Silicon distribution
|
||||||
- Themes.
|
|
||||||
|
|
||||||
## Currently supported
|
## Currently supported
|
||||||
|
|
||||||
### Databases
|
### Databases
|
||||||
|
|
||||||
- [x] MySQL/MariaDB
|
- [x] MySQL/MariaDB
|
||||||
- [ ] PostrgreSQL
|
- [x] PostgreSQL
|
||||||
- [ ] MSSQL
|
|
||||||
- [ ] SQLite
|
- [ ] SQLite
|
||||||
|
- [ ] MSSQL
|
||||||
- [ ] OracleDB
|
- [ ] OracleDB
|
||||||
- [ ] More...
|
- [ ] More...
|
||||||
|
|
||||||
### Operating Systems
|
### Operating Systems
|
||||||
|
|
||||||
#### • x86
|
#### • x64
|
||||||
|
|
||||||
- [x] Windows
|
- [x] Windows
|
||||||
- [x] Linux
|
- [x] Linux
|
||||||
- [x] MacOS (needs tests)
|
- [x] MacOS
|
||||||
|
|
||||||
#### • ARM
|
#### • ARM
|
||||||
|
|
||||||
- [ ] Windows
|
- [ ] Windows
|
||||||
- [ ] Linux
|
- [x] Linux
|
||||||
- [ ] MacOS
|
- [ ] MacOS
|
||||||
|
|
||||||
|
## How to contribute
|
||||||
|
|
||||||
|
- 🌍 [Translate Antares](https://github.com/Fabio286/antares/wiki/Translate-Antares)
|
||||||
|
- 📖 [Contributors Guide](https://github.com/Fabio286/antares/wiki/Contributors-Guide)
|
||||||
|
- 🚧 [Project Board](https://github.com/users/Fabio286/projects/1)
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
[Giuseppe Gigliotti](https://github.com/ReverbOD) / [Italian Translation](https://github.com/EStarium/antares/pull/20)
|
**Italian Translation** / [Giuseppe Gigliotti](https://github.com/ReverbOD) [[#20](https://github.com/Fabio286/antares/pull/20)]
|
||||||
[Mohd-PH](https://github.com/Mohd-PH) / [Arabic Translation](https://github.com/EStarium/antares/pull/29)
|
**Arabic Translation** (needs updates) / [Mohd-PH](https://github.com/Mohd-PH) [[#29](https://github.com/Fabio286/antares/pull/29)]
|
||||||
[hongkfui](https://github.com/hongkfui) / [Spanish Translation](https://github.com/EStarium/antares/pull/32)
|
**Spanish Translation** (needs updates) / [hongkfui](https://github.com/hongkfui) [[#32](https://github.com/Fabio286/antares/pull/32)]
|
||||||
|
**French Translation** (needs updates) / [MrAnyx](https://github.com/MrAnyx) [[#44](https://github.com/Fabio286/antares/pull/44)]
|
||||||
|
**Portugues (Brasil)** / [Daniel Eduardo](https://github.com/daeleduardo) [[#54](https://github.com/Fabio286/antares/pull/54)]
|
||||||
|
**Deutsch (Deutschland)** / [Christian Ratz](https://github.com/digitalgopnik) [[#74](https://github.com/Fabio286/antares/pull/74)]
|
||||||
|
|||||||
BIN
build/appx/LargeTile.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
build/appx/SmallTile.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
build/appx/Square150x150Logo.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
build/appx/Square44x44Logo.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
build/appx/StoreLogo.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
build/appx/Wide310x150Logo.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
build/icon.icns
BIN
build/icon.ico
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 50 KiB |
77
docs/aur-badge.svg
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Gravit.io --><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
style="isolation:isolate" viewBox="0 0 182 56" width="182px" height="56px">
|
||||||
|
<defs>
|
||||||
|
<clipPath id="_clipPath_tR1uglJ1Zei76xP861DY1TsjAiQWS9qF">
|
||||||
|
<rect width="182" height="56" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<g clip-path="url(#_clipPath_tR1uglJ1Zei76xP861DY1TsjAiQWS9qF)">
|
||||||
|
<path
|
||||||
|
d="M 2.5 0.5 L 180.5 0.5 C 181.604 0.5 182.5 1.396 182.5 2.5 L 182.5 54.5 C 182.5 55.604 181.604 56.5 180.5 56.5 L 2.5 56.5 C 1.396 56.5 0.5 55.604 0.5 54.5 L 0.5 2.5 C 0.5 1.396 1.396 0.5 2.5 0.5 Z"
|
||||||
|
style="stroke:none;fill:#252525;stroke-miterlimit:10;" />
|
||||||
|
<path
|
||||||
|
d="M 2.5 0.5 L 180.5 0.5 C 181.604 0.5 182.5 1.396 182.5 2.5 L 182.5 54.5 C 182.5 55.604 181.604 56.5 180.5 56.5 L 2.5 56.5 C 1.396 56.5 0.5 55.604 0.5 54.5 L 0.5 2.5 C 0.5 1.396 1.396 0.5 2.5 0.5 Z"
|
||||||
|
style="fill:none;stroke:#CDCDCD;stroke-width:1;stroke-miterlimit:2;" />
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
d=" M 60.898 13.777 C 58.555 13.774 56.61 14.254 55.858 14.516 L 55.083 18.697 C 55.081 18.712 58.937 17.669 60.635 17.73 C 63.447 17.831 63.706 18.805 63.656 20.119 C 63.704 20.196 62.931 18.931 60.498 18.889 C 57.43 18.836 53.098 19.976 53.104 24.608 C 53.022 29.818 56.997 31.351 59.704 31.379 C 62.138 31.335 63.279 30.458 63.904 29.988 C 64.726 29.129 65.665 28.265 66.561 27.229 C 65.714 28.77 64.978 29.835 64.213 30.651 L 64.213 31.339 L 67.913 30.716 L 67.938 20.66 C 67.901 19.237 68.754 13.791 60.898 13.777 Z M 60.367 22.533 C 61.9 22.554 63.659 23.31 63.662 25.129 C 63.669 26.784 61.589 27.674 60.235 27.66 C 58.881 27.646 57.085 26.596 57.077 24.982 C 57.103 23.54 58.771 22.496 60.367 22.533 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(255,255,255)" />
|
||||||
|
<path
|
||||||
|
d=" M 70.378 14.707 L 70.352 31.36 L 74.662 30.529 L 74.67 21.087 C 74.671 19.681 76.679 18.039 79.198 18.065 C 79.733 17.097 80.738 14.625 80.983 14.062 C 75.354 14.049 75.282 15.68 74.303 16.483 C 74.293 14.952 74.3 14.033 74.3 14.033 L 70.378 14.707 L 70.378 14.707 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(255,255,255)" />
|
||||||
|
<path
|
||||||
|
d=" M 94.632 16.893 C 94.591 16.873 92.385 14.312 87.949 14.292 C 83.795 14.223 79.135 15.834 79.061 22.8 C 79.097 28.925 83.537 31.318 87.973 31.365 C 92.72 31.414 94.609 28.396 94.722 28.322 C 94.156 27.83 92.034 25.728 92.034 25.728 C 92.034 25.728 90.709 27.615 88.138 27.639 C 85.566 27.664 83.331 25.651 83.299 22.844 C 83.266 20.036 85.354 18.515 88.157 18.392 C 90.584 18.392 91.984 19.959 91.984 19.959 L 94.632 16.893 L 94.632 16.893 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(255,255,255)" />
|
||||||
|
<path
|
||||||
|
d=" M 100.065 8.879 L 95.996 9.835 L 96.026 31.526 L 100.034 30.802 L 100.08 20.595 C 100.089 19.524 101.628 17.88 104.2 17.933 C 106.658 17.958 107.207 19.571 107.201 19.775 L 107.272 31.592 L 111.224 30.894 L 111.239 18.363 C 111.265 17.157 108.598 14.61 104.311 14.592 C 102.273 14.596 101.145 15.057 100.571 15.397 C 99.588 16.156 98.466 16.883 97.362 17.811 C 98.382 16.501 99.239 15.595 100.075 14.921 L 100.065 8.879 L 100.065 8.879 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(255,255,255)" />
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
d=" M 114.673 9.441 L 116.508 8.982 L 116.595 30.85 L 114.73 31.168 L 114.673 9.441 L 114.673 9.441 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||||
|
<path
|
||||||
|
d=" M 119.663 15.968 L 121.271 15.252 L 121.285 30.932 L 119.731 31.253 L 119.663 15.968 L 119.663 15.968 Z M 119.28 10.314 L 120.577 9.255 L 121.655 10.454 L 120.357 11.54 L 119.28 10.314 L 119.28 10.314 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||||
|
<path
|
||||||
|
d=" M 124.296 15.682 L 126.131 15.308 L 126.14 18.586 C 126.14 18.727 127.148 14.924 132.008 15.009 C 136.727 15.035 137.499 18.688 137.473 19.507 L 137.531 31.034 L 135.922 31.384 L 135.913 19.998 C 135.932 19.665 135.178 16.853 131.843 16.843 C 128.509 16.833 126.199 19.265 126.203 20.818 L 126.229 30.848 L 124.365 31.335 L 124.296 15.682 L 124.296 15.682 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||||
|
<path
|
||||||
|
d=" M 153.547 31.117 L 151.711 31.492 L 151.703 28.214 C 151.703 28.073 150.694 31.876 145.835 31.791 C 141.116 31.765 140.344 28.112 140.37 27.293 L 140.311 15.765 L 142.261 15.372 L 142.292 26.758 C 142.292 27.069 142.665 29.947 145.999 29.957 C 149.334 29.967 151.669 27.949 151.686 24.911 L 151.662 15.928 L 153.477 15.464 L 153.547 31.117 L 153.547 31.117 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||||
|
<path
|
||||||
|
d=" M 157.144 15.553 L 155.857 16.56 L 160.792 23.018 L 155.529 30.478 L 156.894 31.492 L 161.841 24.563 L 166.948 31.656 L 168.211 30.649 L 162.738 23.065 L 167.104 16.933 L 165.762 15.797 L 161.785 21.472 L 157.144 15.553 L 157.144 15.553 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
d=" M 33.112 3.879 C 30.965 9.143 29.67 12.587 27.279 17.695 C 28.745 19.248 30.544 21.058 33.466 23.101 C 30.325 21.809 28.182 20.511 26.581 19.164 C 23.521 25.549 18.728 34.643 9 52.121 C 16.645 47.707 22.572 44.986 28.095 43.948 C 27.858 42.928 27.723 41.824 27.733 40.673 L 27.742 40.428 C 27.863 35.53 30.411 31.763 33.43 32.019 C 36.448 32.274 38.794 36.455 38.673 41.353 C 38.65 42.275 38.546 43.162 38.364 43.984 C 43.828 45.053 49.691 47.767 57.233 52.121 C 55.746 49.383 54.419 46.915 53.151 44.565 C 51.154 43.017 49.072 41.003 44.823 38.822 C 47.743 39.581 49.834 40.456 51.464 41.435 C 38.575 17.439 37.532 14.251 33.112 3.879 Z "
|
||||||
|
fill-rule="evenodd" fill="rgb(23,147,209)" />
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
d=" M 170.614 30.156 L 170.614 28.802 L 170.109 28.802 L 170.109 28.621 L 171.325 28.621 L 171.325 28.802 L 170.817 28.802 L 170.817 30.156 L 170.614 30.156 Z "
|
||||||
|
fill="rgb(23,147,209)" />
|
||||||
|
<path
|
||||||
|
d=" M 171.536 30.156 L 171.536 28.621 L 171.842 28.621 L 172.205 29.708 C 172.238 29.809 172.263 29.884 172.278 29.935 C 172.295 29.879 172.323 29.797 172.36 29.689 L 172.727 28.621 L 173 28.621 L 173 30.156 L 172.804 30.156 L 172.804 28.871 L 172.358 30.156 L 172.175 30.156 L 171.732 28.849 L 171.732 30.156 L 171.536 30.156 Z "
|
||||||
|
fill="rgb(23,147,209)" />
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<path
|
||||||
|
d=" M 57.471 47.815 L 57.471 46.493 L 56.977 46.493 L 56.977 46.316 L 58.166 46.316 L 58.166 46.493 L 57.67 46.493 L 57.67 47.815 L 57.471 47.815 Z "
|
||||||
|
fill="rgb(23,147,209)" />
|
||||||
|
<path
|
||||||
|
d=" M 58.372 47.815 L 58.372 46.316 L 58.671 46.316 L 59.026 47.377 C 59.059 47.476 59.083 47.55 59.098 47.599 C 59.115 47.545 59.141 47.465 59.177 47.359 L 59.536 46.316 L 59.803 46.316 L 59.803 47.815 L 59.612 47.815 L 59.612 46.56 L 59.176 47.815 L 58.997 47.815 L 58.564 46.539 L 58.564 47.815 L 58.372 47.815"
|
||||||
|
fill="rgb(23,147,209)" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g clip-path="url(#_clipPath_NvFIpNfWUS6M4fZAtfyVzggsKR3URDoi)"><text transform="matrix(1,0,0,1,87.023,43.899)"
|
||||||
|
style="font-family:'Open Sans';font-weight:700;font-size:11px;font-style:normal;fill:#ffffff;stroke:none;">user
|
||||||
|
repository</text></g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="_clipPath_NvFIpNfWUS6M4fZAtfyVzggsKR3URDoi">
|
||||||
|
<rect x="0" y="0" width="86" height="14.98" transform="matrix(1,0,0,1,87,32.142)" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.5 KiB |
BIN
docs/gh-logo-2.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
docs/gh-logo.png
Normal file
|
After Width: | Height: | Size: 304 KiB |
BIN
docs/logo.png
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 172 KiB |
5
jsconfig.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"include": [
|
||||||
|
"./src/renderer/**/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
135
package.json
@@ -1,24 +1,71 @@
|
|||||||
{
|
{
|
||||||
"name": "antares",
|
"name": "antares",
|
||||||
"productName": "Antares",
|
"productName": "Antares",
|
||||||
"version": "0.0.7",
|
"version": "0.3.4",
|
||||||
"description": "A cross-platform easy to use SQL client.",
|
"description": "A cross-platform easy to use SQL client.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": "https://github.com/EStarium/antares.git",
|
"repository": "https://github.com/Fabio286/antares.git",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cross-env NODE_ENV=development electron-webpack dev",
|
"dev": "cross-env NODE_ENV=development electron-webpack dev",
|
||||||
"compile": "electron-webpack",
|
"compile": "electron-webpack",
|
||||||
"build": "cross-env NODE_ENV=production npm run compile && electron-builder",
|
"build": "cross-env NODE_ENV=production npm run compile",
|
||||||
|
"build:local": "npm run build && electron-builder",
|
||||||
|
"build:appx": "npm run build:local -- --win appx",
|
||||||
"release": "standard-version",
|
"release": "standard-version",
|
||||||
"release:pre": "npm run release -- --prerelease alpha",
|
"release:pre": "npm run release -- --prerelease alpha",
|
||||||
"test": "npm run lint",
|
"test": "npm run lint",
|
||||||
"lint": "eslint . --ext .js,.vue && stylelint \"./src/**/*.{css,scss,sass,vue}\"",
|
"lint": "eslint . --ext .js,.vue && stylelint \"./src/**/*.{css,scss,sass,vue}\"",
|
||||||
"lint:fix": "eslint . --ext .js,.vue --fix && stylelint \"./src/**/*.{css,scss,sass,vue}\" --fix"
|
"lint:fix": "eslint . --ext .js,.vue --fix && stylelint \"./src/**/*.{css,scss,sass,vue}\" --fix",
|
||||||
|
"postinstall": "electron-builder install-app-deps"
|
||||||
},
|
},
|
||||||
"author": "Fabio Di Stasio <fabio286@gmail.com>",
|
"author": "Fabio Di Stasio <fabio286@gmail.com>",
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "com.estarium.antares",
|
"appId": "com.fabio286.antares",
|
||||||
"artifactName": "${productName}-${version}-${os}_${arch}.${ext}",
|
"artifactName": "${productName}-${version}-${os}_${arch}.${ext}",
|
||||||
|
"win": {
|
||||||
|
"target": [
|
||||||
|
"nsis",
|
||||||
|
"portable"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mac": {
|
||||||
|
"target": {
|
||||||
|
"target": "default",
|
||||||
|
"arch": [
|
||||||
|
"x64"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"linux": {
|
||||||
|
"target": [
|
||||||
|
{
|
||||||
|
"target": "deb",
|
||||||
|
"arch": "x64"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"target": "AppImage",
|
||||||
|
"arch": [
|
||||||
|
"x64",
|
||||||
|
"armv7l",
|
||||||
|
"arm64"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"category": "Development"
|
||||||
|
},
|
||||||
|
"appImage": {
|
||||||
|
"license": "./LICENSE",
|
||||||
|
"category": "Development"
|
||||||
|
},
|
||||||
|
"portable": {
|
||||||
|
"artifactName": "${productName}-${version}-portable.exe"
|
||||||
|
},
|
||||||
|
"appx": {
|
||||||
|
"displayName": "Antares SQL Client",
|
||||||
|
"identityName": "62514FabioDiStasio.AntaresSQLClient",
|
||||||
|
"publisher": "CN=1A2729ED-865C-41D2-9038-39AE2A63AA52",
|
||||||
|
"applicationId": "FabioDiStasio.AntaresSQLClient"
|
||||||
|
},
|
||||||
"dmg": {
|
"dmg": {
|
||||||
"contents": [
|
"contents": [
|
||||||
{
|
{
|
||||||
@@ -32,13 +79,6 @@
|
|||||||
"path": "/Applications"
|
"path": "/Applications"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"linux": {
|
|
||||||
"target": [
|
|
||||||
"deb",
|
|
||||||
"AppImage"
|
|
||||||
],
|
|
||||||
"category": "Development"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electronWebpack": {
|
"electronWebpack": {
|
||||||
@@ -47,48 +87,49 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/font": "^5.6.55",
|
"@electron/remote": "^1.2.1",
|
||||||
"electron-log": "^4.2.4",
|
"@mdi/font": "^6.1.95",
|
||||||
"electron-updater": "^4.3.5",
|
"ace-builds": "^1.4.12",
|
||||||
"lodash": "^4.17.20",
|
"electron-log": "^4.4.1",
|
||||||
"moment": "^2.29.0",
|
"electron-store": "^8.0.0",
|
||||||
"monaco-editor": "^0.20.0",
|
"electron-updater": "^4.3.9",
|
||||||
"mssql": "^6.2.2",
|
"faker": "^5.5.3",
|
||||||
"mysql": "^2.18.1",
|
"marked": "^3.0.0",
|
||||||
"pg": "^8.3.3",
|
"moment": "^2.29.1",
|
||||||
|
"mysql2": "^2.3.0",
|
||||||
|
"pg": "^8.7.1",
|
||||||
|
"pgsql-ast-parser": "^7.2.1",
|
||||||
"source-map-support": "^0.5.16",
|
"source-map-support": "^0.5.16",
|
||||||
"spectre.css": "^0.5.9",
|
"spectre.css": "^0.5.9",
|
||||||
"vue-click-outside": "^1.1.0",
|
"sql-formatter": "^4.0.2",
|
||||||
"vue-i18n": "^8.21.0",
|
"ssh2-promise": "^0.2.0",
|
||||||
"vue-the-mask": "^0.11.1",
|
"v-mask": "^2.2.4",
|
||||||
"vuedraggable": "^2.24.1",
|
"vue-i18n": "^8.24.4",
|
||||||
"vuex": "^3.5.1",
|
"vuedraggable": "^2.24.3",
|
||||||
"vuex-persist": "^3.1.0"
|
"vuex": "^3.6.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-eslint": "^10.1.0",
|
"@babel/eslint-parser": "^7.15.4",
|
||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"electron": "^10.1.0",
|
"electron": "^14.0.0",
|
||||||
"electron-builder": "^22.8.1",
|
"electron-builder": "^22.11.7",
|
||||||
"electron-devtools-installer": "^3.1.1",
|
"electron-devtools-installer": "^3.2.0",
|
||||||
"electron-webpack": "^2.8.2",
|
"electron-webpack": "^2.8.2",
|
||||||
"electron-webpack-vue": "^2.4.0",
|
"electron-webpack-vue": "^2.4.0",
|
||||||
"eslint": "^7.8.1",
|
"eslint": "^7.32.0",
|
||||||
"eslint-config-standard": "^14.1.1",
|
"eslint-config-standard": "^16.0.3",
|
||||||
"eslint-plugin-import": "^2.22.0",
|
"eslint-plugin-import": "^2.24.2",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^5.1.0",
|
||||||
"eslint-plugin-standard": "^4.0.1",
|
"eslint-plugin-vue": "^7.17.0",
|
||||||
"eslint-plugin-vue": "^6.2.2",
|
"sass": "^1.39.0",
|
||||||
"monaco-editor-webpack-plugin": "^1.9.1",
|
"sass-loader": "^10.2.0",
|
||||||
"node-sass": "^4.14.1",
|
"standard-version": "^9.3.1",
|
||||||
"sass-loader": "^10.0.2",
|
"stylelint": "^13.13.1",
|
||||||
"standard-version": "^9.0.0",
|
"stylelint-config-standard": "^22.0.0",
|
||||||
"stylelint": "^13.7.0",
|
"stylelint-scss": "^3.20.1",
|
||||||
"stylelint-config-standard": "^20.0.0",
|
"vue": "^2.6.14",
|
||||||
"stylelint-scss": "^3.18.0",
|
"vue-template-compiler": "^2.6.14",
|
||||||
"vue": "^2.6.12",
|
"webpack": "^4.46.0"
|
||||||
"vue-template-compiler": "^2.6.12",
|
|
||||||
"webpack": "^4.44.2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
161
snap/snapcraft.yaml
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
name: antares
|
||||||
|
adopt-info: antares
|
||||||
|
summary: Open source SQL client made to be simple and complete.
|
||||||
|
description: |
|
||||||
|
Antares is an SQL client that aims to become an useful and complete tool, especially for developers.
|
||||||
|
The target is to support as many databases as possible, and all major operating systems, including the ARM versions.
|
||||||
|
base: core20
|
||||||
|
|
||||||
|
grade: stable
|
||||||
|
confinement: strict
|
||||||
|
|
||||||
|
architectures:
|
||||||
|
- build-on: amd64
|
||||||
|
compression: lzo
|
||||||
|
layout:
|
||||||
|
/etc/nsswitch.conf:
|
||||||
|
bind-file: $SNAP/etc/nsswitch.conf
|
||||||
|
|
||||||
|
parts:
|
||||||
|
antares:
|
||||||
|
plugin: dump
|
||||||
|
source: .
|
||||||
|
override-build: |
|
||||||
|
snapcraftctl build
|
||||||
|
ARCHITECTURE=$(dpkg --print-architecture)
|
||||||
|
if [ "${ARCHITECTURE}" = "amd64" ]; then
|
||||||
|
FILTER="amd64.deb"
|
||||||
|
else
|
||||||
|
echo "ERROR! Antares only produces debs for amd64. Failing the build here."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Get the latest releases json
|
||||||
|
echo "Get GitHub releases..."
|
||||||
|
wget --quiet https://api.github.com/repos/fabio286/antares/releases/latest -O releases.json
|
||||||
|
# Get the version from the tag_name and the download URL.
|
||||||
|
VERSION=$(jq . releases.json | grep tag_name | cut -d'"' -f4 | sed s'/release-//')
|
||||||
|
DEB_URL=$(cat releases.json | jq -r ".assets[] | select(.name | test(\"${FILTER}\")) | .browser_download_url")
|
||||||
|
DEB=$(basename "${DEB_URL}")
|
||||||
|
echo "Downloading ${DEB_URL}..."
|
||||||
|
wget --quiet "${DEB_URL}" -O "${SNAPCRAFT_PART_INSTALL}/${DEB}"
|
||||||
|
echo "Unpacking ${DEB}..."
|
||||||
|
dpkg -x "${SNAPCRAFT_PART_INSTALL}/${DEB}" ${SNAPCRAFT_PART_INSTALL}
|
||||||
|
rm -f releases.json 2>/dev/null
|
||||||
|
rm -f "${SNAPCRAFT_PART_INSTALL}/${DEB}" 2>/dev/null
|
||||||
|
echo $VERSION > $SNAPCRAFT_STAGE/version
|
||||||
|
# Correct path to icon.
|
||||||
|
sed -i 's|Icon=antares|Icon=/usr/share/icons/hicolor/256x256/apps/antares\.png|g' ${SNAPCRAFT_PART_INSTALL}/usr/share/applications/antares.desktop
|
||||||
|
# Delete usr/bin/antares, it's a broken symlink pointing outside the snap.
|
||||||
|
rm -f ${SNAPCRAFT_PART_INSTALL}/usr/bin/antares
|
||||||
|
chmod -s ${SNAPCRAFT_PART_INSTALL}/opt/Antares/chrome-sandbox
|
||||||
|
snapcraftctl set-version "$(echo $VERSION)"
|
||||||
|
build-packages:
|
||||||
|
- dpkg
|
||||||
|
- jq
|
||||||
|
- sed
|
||||||
|
- wget
|
||||||
|
stage-packages:
|
||||||
|
- fcitx-frontend-gtk3
|
||||||
|
- libappindicator3-1
|
||||||
|
- libasound2
|
||||||
|
- libcurl4
|
||||||
|
- libgconf-2-4
|
||||||
|
- libgtk-3-0
|
||||||
|
- libnotify4
|
||||||
|
- libnspr4
|
||||||
|
- libnss3
|
||||||
|
- libpcre3
|
||||||
|
- libpulse0
|
||||||
|
- libxss1
|
||||||
|
- libsecret-1-0
|
||||||
|
- libxtst6
|
||||||
|
- libxkbfile1
|
||||||
|
- gcc-10-base
|
||||||
|
- libapparmor1
|
||||||
|
- libblkid1
|
||||||
|
- libbsd0
|
||||||
|
- libcom-err2
|
||||||
|
- libcrypt1
|
||||||
|
- libdb5.3
|
||||||
|
- libdbus-1-3
|
||||||
|
- libexpat1
|
||||||
|
- libffi7
|
||||||
|
- libgcc-s1
|
||||||
|
- libgcrypt20
|
||||||
|
- libglib2.0-0
|
||||||
|
- libgmp10
|
||||||
|
- libgnutls30
|
||||||
|
- libgpg-error0
|
||||||
|
- libgssapi-krb5-2
|
||||||
|
- libhogweed5
|
||||||
|
- libidn2-0
|
||||||
|
- libjson-c4
|
||||||
|
- libk5crypto3
|
||||||
|
- libkeyutils1
|
||||||
|
- libkrb5-3
|
||||||
|
- libkrb5support0
|
||||||
|
- liblz4-1
|
||||||
|
- liblzma5
|
||||||
|
- libmount1
|
||||||
|
- libnettle7
|
||||||
|
- libp11-kit0
|
||||||
|
- libpcre2-8-0
|
||||||
|
- libselinux1
|
||||||
|
- libsqlite3-0
|
||||||
|
- libssl1.1
|
||||||
|
- libstdc++6
|
||||||
|
- libsystemd0
|
||||||
|
- libtasn1-6
|
||||||
|
- libudev1
|
||||||
|
- libunistring2
|
||||||
|
- libuuid1
|
||||||
|
- libwrap0
|
||||||
|
- libzstd1
|
||||||
|
- zlib1g
|
||||||
|
- libx11-xcb1
|
||||||
|
- libdrm2
|
||||||
|
- libgbm1
|
||||||
|
- libxcb-dri3-0
|
||||||
|
cleanup:
|
||||||
|
after: [antares]
|
||||||
|
plugin: nil
|
||||||
|
build-snaps: [gnome-3-38-2004]
|
||||||
|
override-prime: |
|
||||||
|
set -eux
|
||||||
|
cd /snap/gnome-3-38-2004/current
|
||||||
|
find . -type f,l -exec rm -f $SNAPCRAFT_PRIME/{} \;
|
||||||
|
|
||||||
|
mdns-lookup:
|
||||||
|
# Make resolution of ".local" host names (Zero-Conf/mDNS/DNS-SD)
|
||||||
|
# working: Take the original nsswitch.conf file from the base
|
||||||
|
# Snap and add "mdns4_minimal [NOTFOUND=return]" to its "hosts:" line
|
||||||
|
# Also install corresponding mdns4_minimal plug-in
|
||||||
|
# See: https://forum.snapcraft.io/t/no-mdns-support-in-snaps-should-core-have-a-modified-nsswitch-conf/
|
||||||
|
plugin: nil
|
||||||
|
stage-packages:
|
||||||
|
- libnss-mdns
|
||||||
|
override-prime: |
|
||||||
|
set -eux
|
||||||
|
sed -Ee 's/^\s*hosts:(\s+)files/hosts:\1files mdns4_minimal \[NOTFOUND=return\]/' /snap/core20/current/etc/nsswitch.conf > $SNAPCRAFT_STAGE/etc/nsswitch.conf
|
||||||
|
snapcraftctl prime
|
||||||
|
prime:
|
||||||
|
- lib/$SNAPCRAFT_ARCH_TRIPLET/libnss_mdns4_minimal*
|
||||||
|
- etc/nsswitch.conf
|
||||||
|
|
||||||
|
apps:
|
||||||
|
antares:
|
||||||
|
command: opt/Antares/antares --no-sandbox
|
||||||
|
desktop: usr/share/applications/antares.desktop
|
||||||
|
extensions: [gnome-3-38]
|
||||||
|
environment:
|
||||||
|
# Fallback to XWayland if running in a Wayland session.
|
||||||
|
DISABLE_WAYLAND: 1
|
||||||
|
plugs:
|
||||||
|
- browser-support
|
||||||
|
- cups-control
|
||||||
|
- home
|
||||||
|
- network
|
||||||
|
- opengl
|
||||||
|
- pulseaudio
|
||||||
|
- removable-media
|
||||||
|
- unity7
|
||||||
217
src/common/FakerMethods.js
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
export default class {
|
||||||
|
static get _methods () {
|
||||||
|
return [
|
||||||
|
{ name: 'zipCode', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'zipCodeByState', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'city', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'cityPrefix', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'citySuffix', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'streetName', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'streetAddress', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'streetSuffix', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'streetPrefix', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'secondaryAddress', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'county', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'country', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'countryCode', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'state', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'stateAbbr', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'latitude', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'longitude', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'direction', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'cardinalDirection', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'ordinalDirection', group: 'address', types: ['string'] },
|
||||||
|
// { name: 'nearbyGPSCoordinate', group: 'address', types: ['string'] },
|
||||||
|
{ name: 'timeZone', group: 'address', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'color', group: 'commerce', types: ['string'] },
|
||||||
|
{ name: 'department', group: 'commerce', types: ['string'] },
|
||||||
|
{ name: 'productName', group: 'commerce', types: ['string'] },
|
||||||
|
{ name: 'price', group: 'commerce', types: ['string', 'float'] },
|
||||||
|
{ name: 'productAdjective', group: 'commerce', types: ['string'] },
|
||||||
|
{ name: 'productMaterial', group: 'commerce', types: ['string'] },
|
||||||
|
{ name: 'product', group: 'commerce', types: ['string'] },
|
||||||
|
{ name: 'productDescription', group: 'commerce', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'suffixes', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'companyName', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'companySuffix', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'catchPhrase', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'bs', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'catchPhraseAdjective', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'catchPhraseDescriptor', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'catchPhraseNoun', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'bsAdjective', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'bsBuzz', group: 'company', types: ['string'] },
|
||||||
|
{ name: 'bsNoun', group: 'company', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'column', group: 'database', types: ['string'] },
|
||||||
|
{ name: 'type', group: 'database', types: ['string'] },
|
||||||
|
{ name: 'collation', group: 'database', types: ['string'] },
|
||||||
|
{ name: 'engine', group: 'database', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'past', group: 'date', types: ['string', 'datetime'] },
|
||||||
|
{ name: 'future', group: 'date', types: ['string', 'datetime'] },
|
||||||
|
// { name: 'between', group: 'date', types: ['string'] },
|
||||||
|
{ name: 'recent', group: 'date', types: ['string', 'datetime'] },
|
||||||
|
{ name: 'soon', group: 'date', types: ['string', 'datetime'] },
|
||||||
|
{ name: 'month', group: 'date', types: ['string'] },
|
||||||
|
{ name: 'weekday', group: 'date', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'account', group: 'finance', types: ['string', 'number'] },
|
||||||
|
{ name: 'accountName', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'routingNumber', group: 'finance', types: ['string', 'number'] },
|
||||||
|
{ name: 'mask', group: 'finance', types: ['string', 'number'] },
|
||||||
|
{ name: 'amount', group: 'finance', types: ['string', 'float'] },
|
||||||
|
{ name: 'transactionType', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'currencyCode', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'currencyName', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'currencySymbol', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'bitcoinAddress', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'litecoinAddress', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'creditCardNumber', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'creditCardCVV', group: 'finance', types: ['string', 'number'] },
|
||||||
|
{ name: 'ethereumAddress', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'iban', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'bic', group: 'finance', types: ['string'] },
|
||||||
|
{ name: 'transactionDescription', group: 'finance', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'branch', group: 'git', types: ['string'] },
|
||||||
|
{ name: 'commitEntry', group: 'git', types: ['string'] },
|
||||||
|
{ name: 'commitMessage', group: 'git', types: ['string'] },
|
||||||
|
{ name: 'commitSha', group: 'git', types: ['string'] },
|
||||||
|
{ name: 'shortSha', group: 'git', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'abbreviation', group: 'hacker', types: ['string'] },
|
||||||
|
{ name: 'adjective', group: 'hacker', types: ['string'] },
|
||||||
|
{ name: 'noun', group: 'hacker', types: ['string'] },
|
||||||
|
{ name: 'verb', group: 'hacker', types: ['string'] },
|
||||||
|
{ name: 'ingverb', group: 'hacker', types: ['string'] },
|
||||||
|
{ name: 'phrase', group: 'hacker', types: ['string'] },
|
||||||
|
|
||||||
|
// { name: 'avatar', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'email', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'exampleEmail', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'userName', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'protocol', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'url', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'domainName', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'domainSuffix', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'domainWord', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'ip', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'ipv6', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'userAgent', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'color', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'mac', group: 'internet', types: ['string'] },
|
||||||
|
{ name: 'password', group: 'internet', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'word', group: 'lorem', types: ['string'] },
|
||||||
|
{ name: 'words', group: 'lorem', types: ['string'] },
|
||||||
|
{ name: 'sentence', group: 'lorem', types: ['string'] },
|
||||||
|
{ name: 'slug', group: 'lorem', types: ['string'] },
|
||||||
|
{ name: 'sentences', group: 'lorem', types: ['string'] },
|
||||||
|
{ name: 'paragraph', group: 'lorem', types: ['string'] },
|
||||||
|
{ name: 'paragraphs', group: 'lorem', types: ['string'] },
|
||||||
|
{ name: 'text', group: 'lorem', types: ['string'] },
|
||||||
|
{ name: 'lines', group: 'lorem', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'genre', group: 'music', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'firstName', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'lastName', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'middleName', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'findName', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'jobTitle', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'gender', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'prefix', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'suffix', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'title', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'jobDescriptor', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'jobArea', group: 'name', types: ['string'] },
|
||||||
|
{ name: 'jobType', group: 'name', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'phoneNumber', group: 'phone', types: ['string'] },
|
||||||
|
{ name: 'phoneNumberFormat', group: 'phone', types: ['string'] },
|
||||||
|
{ name: 'phoneFormats', group: 'phone', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'number', group: 'random', types: ['string', 'number'], params: ['min', 'max'] },
|
||||||
|
{ name: 'float', group: 'random', types: ['string', 'float'], params: ['min', 'max'] },
|
||||||
|
{ name: 'arrayElement', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'arrayElements', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'objectElement', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'uuid', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'boolean', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'word', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'words', group: 'random', types: ['string'] },
|
||||||
|
// { name: 'image', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'locale', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'alpha', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'alphaNumeric', group: 'random', types: ['string'] },
|
||||||
|
{ name: 'hexaDecimal', group: 'random', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'fileName', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'commonFileName', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'mimeType', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'commonFileType', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'commonFileExt', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'fileType', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'fileExt', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'directoryPath', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'filePath', group: 'system', types: ['string'] },
|
||||||
|
{ name: 'semver', group: 'system', types: ['string'] },
|
||||||
|
|
||||||
|
{ name: 'recent', group: 'time', types: ['string', 'time'] },
|
||||||
|
|
||||||
|
{ name: 'vehicle', group: 'vehicle', types: ['string'] },
|
||||||
|
{ name: 'manufacturer', group: 'vehicle', types: ['string'] },
|
||||||
|
{ name: 'model', group: 'vehicle', types: ['string'] },
|
||||||
|
{ name: 'type', group: 'vehicle', types: ['string'] },
|
||||||
|
{ name: 'fuel', group: 'vehicle', types: ['string'] },
|
||||||
|
{ name: 'vin', group: 'vehicle', types: ['string'] },
|
||||||
|
{ name: 'color', group: 'vehicle', types: ['string'] }
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
static getGroups () {
|
||||||
|
const groupsObj = this._methods.reduce((acc, curr) => {
|
||||||
|
if (curr.group in acc)
|
||||||
|
curr.types.forEach(type => acc[curr.group].add(type));
|
||||||
|
else
|
||||||
|
acc[curr.group] = new Set(curr.types);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const groupsArr = [];
|
||||||
|
|
||||||
|
for (const key in groupsObj)
|
||||||
|
groupsArr.push({ name: key, types: [...groupsObj[key]] });
|
||||||
|
|
||||||
|
return groupsArr.sort((a, b) => {
|
||||||
|
if (a.name < b.name)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (b.name > a.name)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}); ;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getGroupsByType (type) {
|
||||||
|
if (!type) return [];
|
||||||
|
return this.getGroups().filter(group => group.types.includes(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
static getMethods ({ type, group }) {
|
||||||
|
return this._methods.filter(method => method.group === group && method.types.includes(type)).sort((a, b) => {
|
||||||
|
if (a.name < b.name)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (b.name > a.name)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/common/customizations/defaults.js
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
module.exports = {
|
||||||
|
// Defaults
|
||||||
|
defaultPort: null,
|
||||||
|
defaultUser: null,
|
||||||
|
defaultDatabase: null,
|
||||||
|
// Core
|
||||||
|
database: false,
|
||||||
|
collations: false,
|
||||||
|
engines: false,
|
||||||
|
connectionSchema: false,
|
||||||
|
// Tools
|
||||||
|
processesList: false,
|
||||||
|
usersManagement: false,
|
||||||
|
variables: false,
|
||||||
|
// Structure
|
||||||
|
schemas: false,
|
||||||
|
tables: false,
|
||||||
|
views: false,
|
||||||
|
triggers: false,
|
||||||
|
triggerFunctions: false,
|
||||||
|
routines: false,
|
||||||
|
functions: false,
|
||||||
|
schedulers: false,
|
||||||
|
// Settings
|
||||||
|
tableAdd: false,
|
||||||
|
viewAdd: false,
|
||||||
|
triggerAdd: false,
|
||||||
|
triggerFunctionAdd: false,
|
||||||
|
routineAdd: false,
|
||||||
|
functionAdd: false,
|
||||||
|
schedulerAdd: false,
|
||||||
|
databaseEdit: false,
|
||||||
|
schemaEdit: false,
|
||||||
|
tableSettings: false,
|
||||||
|
viewSettings: false,
|
||||||
|
triggerSettings: false,
|
||||||
|
triggerFunctionSettings: false,
|
||||||
|
routineSettings: false,
|
||||||
|
functionSettings: false,
|
||||||
|
schedulerSettings: false,
|
||||||
|
indexes: false,
|
||||||
|
foreigns: false,
|
||||||
|
sortableFields: false,
|
||||||
|
unsigned: false,
|
||||||
|
nullable: false,
|
||||||
|
zerofill: false,
|
||||||
|
tableOptions: false,
|
||||||
|
autoIncrement: false,
|
||||||
|
comment: false,
|
||||||
|
collation: false,
|
||||||
|
definer: false,
|
||||||
|
onUpdate: false,
|
||||||
|
tableArray: false,
|
||||||
|
viewAlgorithm: false,
|
||||||
|
viewSqlSecurity: false,
|
||||||
|
viewUpdateOption: false,
|
||||||
|
procedureDeterministic: false,
|
||||||
|
procedureDataAccess: false,
|
||||||
|
procedureSql: false,
|
||||||
|
procedureContext: false,
|
||||||
|
procedureLanguage: false,
|
||||||
|
functionDeterministic: false,
|
||||||
|
functionDataAccess: false,
|
||||||
|
functionSql: false,
|
||||||
|
functionContext: false,
|
||||||
|
functionLanguage: false,
|
||||||
|
triggerSql: false,
|
||||||
|
triggerStatementInCreation: false,
|
||||||
|
triggerMultipleEvents: false,
|
||||||
|
triggerTableInName: false,
|
||||||
|
triggerUpdateColumns: false,
|
||||||
|
triggerOnlyRename: false,
|
||||||
|
triggerFunctionSql: false,
|
||||||
|
triggerFunctionlanguages: false,
|
||||||
|
parametersLength: false,
|
||||||
|
languages: false
|
||||||
|
};
|
||||||
5
src/common/customizations/index.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
maria: require('./mysql'),
|
||||||
|
mysql: require('./mysql'),
|
||||||
|
pg: require('./postgresql')
|
||||||
|
};
|
||||||
61
src/common/customizations/mysql.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
const defaults = require('./defaults');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
...defaults,
|
||||||
|
// Defaults
|
||||||
|
defaultPort: 3306,
|
||||||
|
defaultUser: 'root',
|
||||||
|
defaultDatabase: null,
|
||||||
|
// Core
|
||||||
|
connectionSchema: true,
|
||||||
|
collations: true,
|
||||||
|
engines: true,
|
||||||
|
// Tools
|
||||||
|
processesList: true,
|
||||||
|
// Structure
|
||||||
|
schemas: true,
|
||||||
|
tables: true,
|
||||||
|
views: true,
|
||||||
|
triggers: true,
|
||||||
|
routines: true,
|
||||||
|
functions: true,
|
||||||
|
schedulers: true,
|
||||||
|
// Settings
|
||||||
|
tableAdd: true,
|
||||||
|
viewAdd: true,
|
||||||
|
triggerAdd: true,
|
||||||
|
routineAdd: true,
|
||||||
|
functionAdd: true,
|
||||||
|
schedulerAdd: true,
|
||||||
|
schemaEdit: true,
|
||||||
|
tableSettings: true,
|
||||||
|
viewSettings: true,
|
||||||
|
triggerSettings: true,
|
||||||
|
routineSettings: true,
|
||||||
|
functionSettings: true,
|
||||||
|
schedulerSettings: true,
|
||||||
|
indexes: true,
|
||||||
|
foreigns: true,
|
||||||
|
sortableFields: true,
|
||||||
|
unsigned: true,
|
||||||
|
nullable: true,
|
||||||
|
zerofill: true,
|
||||||
|
tableOptions: true,
|
||||||
|
autoIncrement: true,
|
||||||
|
comment: true,
|
||||||
|
collation: true,
|
||||||
|
definer: true,
|
||||||
|
onUpdate: true,
|
||||||
|
viewAlgorithm: true,
|
||||||
|
viewSqlSecurity: true,
|
||||||
|
viewUpdateOption: true,
|
||||||
|
procedureDeterministic: true,
|
||||||
|
procedureDataAccess: true,
|
||||||
|
procedureSql: 'BEGIN\r\n\r\nEND',
|
||||||
|
procedureContext: true,
|
||||||
|
triggerSql: 'BEGIN\r\n\r\nEND',
|
||||||
|
functionDeterministic: true,
|
||||||
|
functionDataAccess: true,
|
||||||
|
functionSql: 'BEGIN\r\n\r\nEND',
|
||||||
|
parametersLength: true
|
||||||
|
};
|
||||||
52
src/common/customizations/postgresql.js
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
const defaults = require('./defaults');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
...defaults,
|
||||||
|
// Defaults
|
||||||
|
defaultPort: 5432,
|
||||||
|
defaultUser: 'postgres',
|
||||||
|
defaultDatabase: 'postgres',
|
||||||
|
// Core
|
||||||
|
database: true,
|
||||||
|
// Tools
|
||||||
|
processesList: true,
|
||||||
|
// Structure
|
||||||
|
tables: true,
|
||||||
|
views: true,
|
||||||
|
triggers: true,
|
||||||
|
triggerFunctions: true,
|
||||||
|
routines: true,
|
||||||
|
functions: true,
|
||||||
|
// Settings
|
||||||
|
tableAdd: true,
|
||||||
|
viewAdd: true,
|
||||||
|
triggerAdd: true,
|
||||||
|
triggerFunctionAdd: true,
|
||||||
|
routineAdd: true,
|
||||||
|
functionAdd: true,
|
||||||
|
databaseEdit: false,
|
||||||
|
tableSettings: true,
|
||||||
|
viewSettings: true,
|
||||||
|
triggerSettings: true,
|
||||||
|
triggerFunctionSettings: true,
|
||||||
|
routineSettings: true,
|
||||||
|
functionSettings: true,
|
||||||
|
indexes: true,
|
||||||
|
foreigns: true,
|
||||||
|
nullable: true,
|
||||||
|
tableArray: true,
|
||||||
|
procedureSql: '$procedure$\r\n\r\n$procedure$',
|
||||||
|
procedureContext: true,
|
||||||
|
procedureLanguage: true,
|
||||||
|
functionSql: '$function$\r\n\r\n$function$',
|
||||||
|
triggerFunctionSql: '$function$\r\nBEGIN\r\n\r\nEND\r\n$function$',
|
||||||
|
triggerFunctionlanguages: ['plpgsql'],
|
||||||
|
functionContext: true,
|
||||||
|
functionLanguage: true,
|
||||||
|
triggerSql: 'EXECUTE PROCEDURE ',
|
||||||
|
triggerStatementInCreation: true,
|
||||||
|
triggerMultipleEvents: true,
|
||||||
|
triggerTableInName: true,
|
||||||
|
triggerOnlyRename: false,
|
||||||
|
languages: ['sql', 'plpgsql', 'c', 'internal']
|
||||||
|
};
|
||||||
308
src/common/data-types/mysql.js
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
group: 'integer',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'TINYINT',
|
||||||
|
length: 4,
|
||||||
|
collation: false,
|
||||||
|
unsigned: true,
|
||||||
|
zerofill: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'SMALLINT',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: true,
|
||||||
|
zerofill: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'INT',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: true,
|
||||||
|
zerofill: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MEDIUMINT',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: true,
|
||||||
|
zerofill: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BIGINT',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: true,
|
||||||
|
zerofill: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BIT',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: true,
|
||||||
|
zerofill: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'float',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'FLOAT',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DOUBLE',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DECIMAL',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'string',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'CHAR',
|
||||||
|
length: true,
|
||||||
|
collation: true,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'VARCHAR',
|
||||||
|
length: true,
|
||||||
|
collation: true,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TINYTEXT',
|
||||||
|
length: false,
|
||||||
|
collation: true,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MEDIUMTEXT',
|
||||||
|
length: false,
|
||||||
|
collation: true,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TEXT',
|
||||||
|
length: false,
|
||||||
|
collation: true,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LONGTEXT',
|
||||||
|
length: false,
|
||||||
|
collation: true,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'JSON',
|
||||||
|
length: false,
|
||||||
|
collation: true,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'binary',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'BINARY',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'VARBINARY',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TINYBLOB',
|
||||||
|
length: false,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BLOB',
|
||||||
|
length: false,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MEDIUMBLOB',
|
||||||
|
length: false,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LONGBLOB',
|
||||||
|
length: false,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'time',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'DATE',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TIME',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'YEAR',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DATETIME',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TIMESTAMP',
|
||||||
|
length: false,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'spatial',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'POINT',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LINESTRING',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'POLYGON',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'GEOMETRY',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MULTIPOINT',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MULTILINESTRING',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MULTIPOLYGON',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'GEOMETRYCOLLECTION',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'other',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'ENUM',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'SET',
|
||||||
|
length: true,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'unknown',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'UNKNOWN',
|
||||||
|
length: false,
|
||||||
|
collation: false,
|
||||||
|
unsigned: false,
|
||||||
|
zerofill: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
292
src/common/data-types/postgresql.js
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
group: 'integer',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'SMALLINT',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'INTEGER',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BIGINT',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DECIMAL',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NUMERIC',
|
||||||
|
length: true,
|
||||||
|
unsigned: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'SMALLSERIAL',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'SERIAL',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BIGSERIAL',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'float',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'REAL',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DOUBLE PRECISION',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'monetary',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'money',
|
||||||
|
length: false,
|
||||||
|
unsigned: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'string',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'CHARACTER VARYING',
|
||||||
|
length: true,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'CHARACTER',
|
||||||
|
length: true,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TEXT',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '"CHAR"',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NAME',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'binary',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'BYTEA',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'time',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'TIMESTAMP WITHOUT TIME ZONE',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TIMESTAMP WITH TIME ZONE',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DATE',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TIME WITHOUT TIME ZONE',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TIME WITH TIME ZONE',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'INTERVAL',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'boolean',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'BOOLEAN',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'geometric',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'POINT',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LINE',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'LSEG',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BOX',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'PATH',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'POLYGON',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'CIRCLE',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'network',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'CIDR',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'INET',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MACADDR',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'MACADDR8',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'bit',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'BIT',
|
||||||
|
length: true,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BIT VARYING',
|
||||||
|
length: true,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'text search',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'TSVECTOR',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TSQUERY',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'uuid',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'UUID',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'xml',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'XML',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: 'json',
|
||||||
|
types: [
|
||||||
|
{
|
||||||
|
name: 'JSON',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'JSONB',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'JSONPATH',
|
||||||
|
length: false,
|
||||||
|
unsigned: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
@@ -1,12 +1,82 @@
|
|||||||
export const TEXT = ['char', 'varchar'];
|
export const TEXT = [
|
||||||
export const LONG_TEXT = ['text', 'mediumtext', 'longtext'];
|
'CHAR',
|
||||||
|
'VARCHAR',
|
||||||
|
'CHARACTER',
|
||||||
|
'CHARACTER VARYING'
|
||||||
|
];
|
||||||
|
export const LONG_TEXT = [
|
||||||
|
'TEXT',
|
||||||
|
'MEDIUMTEXT',
|
||||||
|
'LONGTEXT'
|
||||||
|
];
|
||||||
|
|
||||||
export const NUMBER = ['int', 'tinyint', 'smallint', 'mediumint', 'bigint', 'float', 'double', 'decimal'];
|
export const ARRAY = [
|
||||||
|
'ARRAY',
|
||||||
|
'ANYARRAY'
|
||||||
|
];
|
||||||
|
|
||||||
export const DATE = ['date'];
|
export const TEXT_SEARCH = [
|
||||||
export const TIME = ['time'];
|
'TSVECTOR',
|
||||||
export const DATETIME = ['datetime', 'timestamp'];
|
'TSQUERY'
|
||||||
|
];
|
||||||
|
|
||||||
export const BLOB = ['blob', 'mediumblob', 'longblob'];
|
export const NUMBER = [
|
||||||
|
'INT',
|
||||||
|
'TINYINT',
|
||||||
|
'SMALLINT',
|
||||||
|
'MEDIUMINT',
|
||||||
|
'BIGINT',
|
||||||
|
'DECIMAL',
|
||||||
|
'NUMERIC',
|
||||||
|
'INTEGER',
|
||||||
|
'SMALLSERIAL',
|
||||||
|
'SERIAL',
|
||||||
|
'BIGSERIAL',
|
||||||
|
'OID',
|
||||||
|
'XID'
|
||||||
|
];
|
||||||
|
|
||||||
export const BIT = ['bit'];
|
export const FLOAT = [
|
||||||
|
'FLOAT',
|
||||||
|
'DOUBLE',
|
||||||
|
'REAL',
|
||||||
|
'DOUBLE PRECISION',
|
||||||
|
'MONEY'
|
||||||
|
];
|
||||||
|
|
||||||
|
export const BOOLEAN = [
|
||||||
|
'BOOL',
|
||||||
|
'BOOLEAN'
|
||||||
|
];
|
||||||
|
|
||||||
|
export const DATE = ['DATE'];
|
||||||
|
export const TIME = [
|
||||||
|
'TIME',
|
||||||
|
'TIME WITH TIME ZONE'
|
||||||
|
];
|
||||||
|
|
||||||
|
export const DATETIME = [
|
||||||
|
'DATETIME',
|
||||||
|
'TIMESTAMP',
|
||||||
|
'TIMESTAMP WITHOUT TIME ZONE',
|
||||||
|
'TIMESTAMP WITH TIME ZONE'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Used to check datetime fields only
|
||||||
|
export const HAS_TIMEZONE = [
|
||||||
|
'TIMESTAMP WITH TIME ZONE',
|
||||||
|
'TIME WITH TIME ZONE'
|
||||||
|
];
|
||||||
|
|
||||||
|
export const BLOB = [
|
||||||
|
'BLOB',
|
||||||
|
'TINYBLOB',
|
||||||
|
'MEDIUMBLOB',
|
||||||
|
'LONGBLOB',
|
||||||
|
'BYTEA'
|
||||||
|
];
|
||||||
|
|
||||||
|
export const BIT = [
|
||||||
|
'BIT',
|
||||||
|
'BIT VARYING'
|
||||||
|
];
|
||||||
|
|||||||
6
src/common/index-types/mysql.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = [
|
||||||
|
'PRIMARY',
|
||||||
|
'INDEX',
|
||||||
|
'UNIQUE',
|
||||||
|
'FULLTEXT'
|
||||||
|
];
|
||||||
5
src/common/index-types/postgresql.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module.exports = [
|
||||||
|
'PRIMARY',
|
||||||
|
'INDEX',
|
||||||
|
'UNIQUE'
|
||||||
|
];
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable no-useless-escape */
|
/* eslint-disable no-useless-escape */
|
||||||
// eslint-disable-next-line no-control-regex
|
// eslint-disable-next-line no-control-regex
|
||||||
const regex = new RegExp(/[\0\x08\x09\x1a\n\r"'\\\%]/gm);
|
const pattern = /[\0\x08\x09\x1a\n\r"'\\\%]/gm;
|
||||||
|
const regex = new RegExp(pattern);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Escapes a string
|
* Escapes a string
|
||||||
@@ -10,8 +11,8 @@ const regex = new RegExp(/[\0\x08\x09\x1a\n\r"'\\\%]/gm);
|
|||||||
*/
|
*/
|
||||||
function sqlEscaper (string) {
|
function sqlEscaper (string) {
|
||||||
return string.replace(regex, char => {
|
return string.replace(regex, char => {
|
||||||
const m = ['\\0', '\\x08', '\\x09', '\\x1a', '\\n', '\\r', '\'', '"', '\\', '\\\\', '%'];
|
const m = ['\\0', '\\x08', '\\x09', '\\x1a', '\\n', '\\r', '\'', '\"', '\\', '\\\\', '%'];
|
||||||
const r = ['\\\\0', '\\\\b', '\\\\t', '\\\\z', '\\\\n', '\\\\r', '\'\'', '""', '\\\\', '\\\\\\\\', '\\%'];
|
const r = ['\\\\0', '\\\\b', '\\\\t', '\\\\z', '\\\\n', '\\\\r', '\\\'', '\\\"', '\\\\', '\\\\\\\\', '\%'];
|
||||||
return r[m.indexOf(char)] || char;
|
return r[m.indexOf(char)] || char;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { app, BrowserWindow, nativeImage } from 'electron';
|
import { app, BrowserWindow, /* session, */ nativeImage, Menu } from 'electron';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { format as formatUrl } from 'url';
|
import Store from 'electron-store';
|
||||||
|
|
||||||
import ipcHandlers from './ipc-handlers';
|
import ipcHandlers from './ipc-handlers';
|
||||||
|
|
||||||
|
Store.initRenderer();
|
||||||
|
|
||||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||||
const gotTheLock = app.requestSingleInstanceLock();
|
const gotTheLock = app.requestSingleInstanceLock();
|
||||||
|
|
||||||
@@ -25,6 +28,7 @@ async function createMainWindow () {
|
|||||||
icon: nativeImage.createFromDataURL(icon.default),
|
icon: nativeImage.createFromDataURL(icon.default),
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
nodeIntegration: true,
|
nodeIntegration: true,
|
||||||
|
contextIsolation: false,
|
||||||
'web-security': false,
|
'web-security': false,
|
||||||
enableRemoteModule: true,
|
enableRemoteModule: true,
|
||||||
spellcheck: false
|
spellcheck: false
|
||||||
@@ -33,26 +37,22 @@ async function createMainWindow () {
|
|||||||
backgroundColor: '#1d1d1d'
|
backgroundColor: '#1d1d1d'
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isDevelopment) {
|
try {
|
||||||
await window.loadURL(`http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`);
|
if (isDevelopment) { //
|
||||||
|
await window.loadURL(`http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`);
|
||||||
|
|
||||||
const { default: installExtension, VUEJS_DEVTOOLS } = require('electron-devtools-installer');
|
// const { default: installExtension, VUEJS3_DEVTOOLS } = require('electron-devtools-installer');
|
||||||
window.webContents.openDevTools();
|
|
||||||
|
|
||||||
installExtension(VUEJS_DEVTOOLS)
|
// const oldDevToolsID = session.defaultSession.getAllExtensions().find(ext => ext.name === 'Vue.js devtools').id;
|
||||||
.then(name => {
|
// session.defaultSession.removeExtension(oldDevToolsID);
|
||||||
console.log(name, 'installed');
|
// const toolName = await installExtension(VUEJS3_DEVTOOLS);
|
||||||
})
|
// console.log(toolName, 'installed');
|
||||||
.catch(err => {
|
}
|
||||||
console.log(err);
|
else
|
||||||
});
|
await window.loadURL(new URL(`file:///${path.join(__dirname, 'index.html')}`).href);
|
||||||
}
|
}
|
||||||
else {
|
catch (err) {
|
||||||
await window.loadURL(formatUrl({
|
console.log(err);
|
||||||
pathname: path.join(__dirname, 'index.html'),
|
|
||||||
protocol: 'file',
|
|
||||||
slashes: true
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.on('closed', () => {
|
window.on('closed', () => {
|
||||||
@@ -72,6 +72,8 @@ async function createMainWindow () {
|
|||||||
if (!gotTheLock)
|
if (!gotTheLock)
|
||||||
app.quit();
|
app.quit();
|
||||||
else {
|
else {
|
||||||
|
require('@electron/remote/main').initialize();
|
||||||
|
|
||||||
// Initialize ipcHandlers
|
// Initialize ipcHandlers
|
||||||
ipcHandlers();
|
ipcHandlers();
|
||||||
|
|
||||||
@@ -82,14 +84,20 @@ else {
|
|||||||
app.quit();
|
app.quit();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('activate', () => {
|
app.on('activate', async () => {
|
||||||
// on macOS it is common to re-create a window even after all windows have been closed
|
// on macOS it is common to re-create a window even after all windows have been closed
|
||||||
if (mainWindow === null)
|
if (mainWindow === null) {
|
||||||
mainWindow = createMainWindow();
|
mainWindow = await createMainWindow();
|
||||||
|
if (isDevelopment)
|
||||||
|
mainWindow.webContents.openDevTools();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// create main BrowserWindow when electron is ready
|
// create main BrowserWindow when electron is ready
|
||||||
app.on('ready', () => {
|
app.on('ready', async () => {
|
||||||
mainWindow = createMainWindow();
|
mainWindow = await createMainWindow();
|
||||||
|
Menu.setApplicationMenu(null);
|
||||||
|
if (isDevelopment)
|
||||||
|
mainWindow.webContents.openDevTools();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,9 @@ export default () => {
|
|||||||
ipcMain.on('close-app', () => {
|
ipcMain.on('close-app', () => {
|
||||||
app.exit();
|
app.exit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.on('get-key', async event => {
|
||||||
|
const key = false;
|
||||||
|
event.returnValue = key;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,22 +1,46 @@
|
|||||||
|
import fs from 'fs';
|
||||||
import { ipcMain } from 'electron';
|
import { ipcMain } from 'electron';
|
||||||
import { ClientsFactory } from '../libs/ClientsFactory';
|
import { ClientsFactory } from '../libs/ClientsFactory';
|
||||||
|
|
||||||
export default connections => {
|
export default connections => {
|
||||||
ipcMain.handle('test-connection', async (event, conn) => {
|
ipcMain.handle('test-connection', async (event, conn) => {
|
||||||
const connection = ClientsFactory.getConnection({
|
const params = {
|
||||||
client: conn.client,
|
host: conn.host,
|
||||||
params: {
|
port: +conn.port,
|
||||||
host: conn.host,
|
user: conn.user,
|
||||||
port: +conn.port,
|
password: conn.password,
|
||||||
user: conn.user,
|
application_name: 'Antares SQL'
|
||||||
password: conn.password
|
};
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await connection.connect();
|
if (conn.database)
|
||||||
|
params.database = conn.database;
|
||||||
|
|
||||||
|
if (conn.ssl) {
|
||||||
|
params.ssl = {
|
||||||
|
key: conn.key ? fs.readFileSync(conn.key) : null,
|
||||||
|
cert: conn.cert ? fs.readFileSync(conn.cert) : null,
|
||||||
|
ca: conn.ca ? fs.readFileSync(conn.ca) : null,
|
||||||
|
ciphers: conn.ciphers
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn.ssh) {
|
||||||
|
params.ssh = {
|
||||||
|
host: conn.sshHost,
|
||||||
|
username: conn.sshUser,
|
||||||
|
password: conn.sshPass,
|
||||||
|
port: conn.sshPort ? conn.sshPort : 22,
|
||||||
|
identity: conn.sshKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const connection = await ClientsFactory.getConnection({
|
||||||
|
client: conn.client,
|
||||||
|
params
|
||||||
|
});
|
||||||
|
await connection.connect();
|
||||||
|
|
||||||
await connection.select('1+1').run();
|
await connection.select('1+1').run();
|
||||||
connection.destroy();
|
connection.destroy();
|
||||||
|
|
||||||
@@ -32,21 +56,49 @@ export default connections => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('connect', async (event, conn) => {
|
ipcMain.handle('connect', async (event, conn) => {
|
||||||
const connection = ClientsFactory.getConnection({
|
const params = {
|
||||||
client: conn.client,
|
host: conn.host,
|
||||||
params: {
|
port: +conn.port,
|
||||||
host: conn.host,
|
user: conn.user,
|
||||||
port: +conn.port,
|
password: conn.password,
|
||||||
user: conn.user,
|
application_name: 'Antares SQL'
|
||||||
password: conn.password
|
};
|
||||||
},
|
|
||||||
poolSize: 1
|
if (conn.database)
|
||||||
});
|
params.database = conn.database;
|
||||||
|
|
||||||
|
if (conn.schema)
|
||||||
|
params.schema = conn.schema;
|
||||||
|
|
||||||
|
if (conn.ssl) {
|
||||||
|
params.ssl = {
|
||||||
|
key: conn.key ? fs.readFileSync(conn.key) : null,
|
||||||
|
cert: conn.cert ? fs.readFileSync(conn.cert) : null,
|
||||||
|
ca: conn.ca ? fs.readFileSync(conn.ca) : null,
|
||||||
|
ciphers: conn.ciphers
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn.ssh) {
|
||||||
|
params.ssh = {
|
||||||
|
host: conn.sshHost,
|
||||||
|
username: conn.sshUser,
|
||||||
|
password: conn.sshPass,
|
||||||
|
port: conn.sshPort ? conn.sshPort : 22,
|
||||||
|
identity: conn.sshKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const connection = ClientsFactory.getConnection({
|
||||||
|
client: conn.client,
|
||||||
|
params,
|
||||||
|
poolSize: 5
|
||||||
|
});
|
||||||
|
|
||||||
await connection.connect();
|
await connection.connect();
|
||||||
|
|
||||||
const structure = await connection.getStructure();
|
const structure = await connection.getStructure(new Set());
|
||||||
|
|
||||||
connections[conn.uid] = connection;
|
connections[conn.uid] = connection;
|
||||||
|
|
||||||
|
|||||||
63
src/main/ipc-handlers/functions.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { ipcMain } from 'electron';
|
||||||
|
|
||||||
|
export default (connections) => {
|
||||||
|
ipcMain.handle('get-function-informations', async (event, params) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[params.uid].getFunctionInformations(params);
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('drop-function', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].dropFunction(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('alter-function', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].alterFunction(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('alter-trigger-function', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].alterTriggerFunction(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('create-function', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].createFunction(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('create-trigger-function', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].createTriggerFunction(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,15 +1,27 @@
|
|||||||
import connection from './connection';
|
import connection from './connection';
|
||||||
import tables from './tables';
|
import tables from './tables';
|
||||||
|
import views from './views';
|
||||||
|
import triggers from './triggers';
|
||||||
|
import routines from './routines';
|
||||||
|
import functions from './functions';
|
||||||
|
import schedulers from './schedulers';
|
||||||
import updates from './updates';
|
import updates from './updates';
|
||||||
import application from './application';
|
import application from './application';
|
||||||
import database from './database';
|
import schema from './schema';
|
||||||
|
import users from './users';
|
||||||
|
|
||||||
const connections = {};
|
const connections = {};
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
connection(connections);
|
connection(connections);
|
||||||
tables(connections);
|
tables(connections);
|
||||||
database(connections);
|
views(connections);
|
||||||
|
triggers(connections);
|
||||||
|
routines(connections);
|
||||||
|
functions(connections);
|
||||||
|
schedulers(connections);
|
||||||
|
schema(connections);
|
||||||
|
users(connections);
|
||||||
updates();
|
updates();
|
||||||
application();
|
application();
|
||||||
};
|
};
|
||||||
|
|||||||
43
src/main/ipc-handlers/routines.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { ipcMain } from 'electron';
|
||||||
|
|
||||||
|
export default (connections) => {
|
||||||
|
ipcMain.handle('get-routine-informations', async (event, params) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[params.uid].getRoutineInformations(params);
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('drop-routine', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].dropRoutine(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('alter-routine', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].alterRoutine(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('create-routine', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].createRoutine(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
43
src/main/ipc-handlers/schedulers.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { ipcMain } from 'electron';
|
||||||
|
|
||||||
|
export default (connections) => {
|
||||||
|
ipcMain.handle('get-scheduler-informations', async (event, params) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[params.uid].getEventInformations(params);
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('drop-scheduler', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].dropEvent(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('alter-scheduler', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].alterEvent(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('create-scheduler', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].createEvent(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -2,10 +2,9 @@
|
|||||||
import { ipcMain } from 'electron';
|
import { ipcMain } from 'electron';
|
||||||
|
|
||||||
export default connections => {
|
export default connections => {
|
||||||
ipcMain.handle('create-database', async (event, params) => {
|
ipcMain.handle('create-schema', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
const query = `CREATE DATABASE \`${params.name}\` COLLATE ${params.collation}`;
|
await connections[params.uid].createSchema(params);
|
||||||
await connections[params.uid].raw(query);
|
|
||||||
|
|
||||||
return { status: 'success' };
|
return { status: 'success' };
|
||||||
}
|
}
|
||||||
@@ -14,10 +13,9 @@ export default connections => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('update-database', async (event, params) => {
|
ipcMain.handle('update-schema', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
const query = `ALTER DATABASE \`${params.name}\` COLLATE ${params.collation}`;
|
await connections[params.uid].alterSchema(params);
|
||||||
await connections[params.uid].raw(query);
|
|
||||||
|
|
||||||
return { status: 'success' };
|
return { status: 'success' };
|
||||||
}
|
}
|
||||||
@@ -26,10 +24,9 @@ export default connections => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('delete-database', async (event, params) => {
|
ipcMain.handle('delete-schema', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
const query = `DROP DATABASE \`${params.database}\``;
|
await connections[params.uid].dropSchema(params);
|
||||||
await connections[params.uid].raw(query);
|
|
||||||
|
|
||||||
return { status: 'success' };
|
return { status: 'success' };
|
||||||
}
|
}
|
||||||
@@ -38,10 +35,9 @@ export default connections => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('get-database-collation', async (event, params) => {
|
ipcMain.handle('get-schema-collation', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
const query = `SELECT \`DEFAULT_COLLATION_NAME\` FROM \`information_schema\`.\`SCHEMATA\` WHERE \`SCHEMA_NAME\`='${params.database}'`;
|
const collation = await connections[params.uid].getDatabaseCollation(params);
|
||||||
const collation = await connections[params.uid].raw(query);
|
|
||||||
|
|
||||||
return { status: 'success', response: collation.rows.length ? collation.rows[0].DEFAULT_COLLATION_NAME : '' };
|
return { status: 'success', response: collation.rows.length ? collation.rows[0].DEFAULT_COLLATION_NAME : '' };
|
||||||
}
|
}
|
||||||
@@ -50,9 +46,9 @@ export default connections => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('get-structure', async (event, uid) => {
|
ipcMain.handle('get-structure', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
const structure = await connections[uid].getStructure();
|
const structure = await connections[params.uid].getStructure(params.schemas);
|
||||||
|
|
||||||
return { status: 'success', response: structure };
|
return { status: 'success', response: structure };
|
||||||
}
|
}
|
||||||
@@ -83,6 +79,39 @@ export default connections => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('get-engines', async (event, uid) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[uid].getEngines();
|
||||||
|
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('get-version', async (event, uid) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[uid].getVersion();
|
||||||
|
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('get-processes', async (event, uid) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[uid].getProcesses();
|
||||||
|
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.handle('use-schema', async (event, { uid, schema }) => {
|
ipcMain.handle('use-schema', async (event, { uid, schema }) => {
|
||||||
if (!schema) return;
|
if (!schema) return;
|
||||||
|
|
||||||
@@ -99,7 +128,12 @@ export default connections => {
|
|||||||
if (!query) return;
|
if (!query) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await connections[uid].raw(query, true);
|
const result = await connections[uid].raw(query, {
|
||||||
|
nest: true,
|
||||||
|
details: true,
|
||||||
|
schema,
|
||||||
|
comments: false
|
||||||
|
});
|
||||||
|
|
||||||
return { status: 'success', response: result };
|
return { status: 'success', response: result };
|
||||||
}
|
}
|
||||||
@@ -1,39 +1,14 @@
|
|||||||
import { ipcMain } from 'electron';
|
import { ipcMain } from 'electron';
|
||||||
|
import faker from 'faker';
|
||||||
|
import moment from 'moment';
|
||||||
import { sqlEscaper } from 'common/libs/sqlEscaper';
|
import { sqlEscaper } from 'common/libs/sqlEscaper';
|
||||||
import { TEXT, LONG_TEXT, NUMBER, BLOB } from 'common/fieldTypes';
|
import { TEXT, LONG_TEXT, ARRAY, TEXT_SEARCH, NUMBER, FLOAT, BLOB, BIT, DATE, DATETIME } from 'common/fieldTypes';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|
||||||
// TODO: remap objects based on client
|
|
||||||
|
|
||||||
export default (connections) => {
|
export default (connections) => {
|
||||||
ipcMain.handle('get-table-columns', async (event, { uid, schema, table }) => {
|
ipcMain.handle('get-table-columns', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
const { rows } = await connections[uid]
|
const result = await connections[params.uid].getTableColumns(params);
|
||||||
.select('*')
|
|
||||||
.schema('information_schema')
|
|
||||||
.from('COLUMNS')
|
|
||||||
.where({ TABLE_SCHEMA: `= '${schema}'`, TABLE_NAME: `= '${table}'` })
|
|
||||||
.orderBy({ ORDINAL_POSITION: 'ASC' })
|
|
||||||
.run();
|
|
||||||
|
|
||||||
const result = rows.map(field => {
|
|
||||||
return {
|
|
||||||
name: field.COLUMN_NAME,
|
|
||||||
key: field.COLUMN_KEY.toLowerCase(),
|
|
||||||
type: field.DATA_TYPE,
|
|
||||||
schema: field.TABLE_SCHEMA,
|
|
||||||
table: field.TABLE_NAME,
|
|
||||||
numPrecision: field.NUMERIC_PRECISION,
|
|
||||||
datePrecision: field.DATETIME_PRECISION,
|
|
||||||
charLength: field.CHARACTER_MAXIMUM_LENGTH,
|
|
||||||
isNullable: field.IS_NULLABLE,
|
|
||||||
default: field.COLUMN_DEFAULT,
|
|
||||||
charset: field.CHARACTER_SET_NAME,
|
|
||||||
collation: field.COLLATION_NAME,
|
|
||||||
autoIncrement: field.EXTRA.includes('auto_increment'),
|
|
||||||
comment: field.COLUMN_COMMENT
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return { status: 'success', response: result };
|
return { status: 'success', response: result };
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
@@ -41,14 +16,20 @@ export default (connections) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('get-table-data', async (event, { uid, schema, table }) => {
|
ipcMain.handle('get-table-data', async (event, { uid, schema, table, limit, page, sortParams }) => {
|
||||||
try {
|
try {
|
||||||
const result = await connections[uid]
|
const offset = (page - 1) * limit;
|
||||||
|
const query = connections[uid]
|
||||||
.select('*')
|
.select('*')
|
||||||
.schema(schema)
|
.schema(schema)
|
||||||
.from(table)
|
.from(table)
|
||||||
.limit(1000)
|
.limit(limit)
|
||||||
.run();
|
.offset(offset);
|
||||||
|
|
||||||
|
if (sortParams && sortParams.field && sortParams.dir)
|
||||||
|
query.orderBy({ [sortParams.field]: sortParams.dir.toUpperCase() });
|
||||||
|
|
||||||
|
const result = await query.run({ details: true, schema });
|
||||||
|
|
||||||
return { status: 'success', response: result };
|
return { status: 'success', response: result };
|
||||||
}
|
}
|
||||||
@@ -57,28 +38,40 @@ export default (connections) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('get-key-usage', async (event, { uid, schema, table }) => {
|
ipcMain.handle('get-table-count', async (event, params) => {
|
||||||
try {
|
try {
|
||||||
const { rows } = await connections[uid]
|
const result = await connections[params.uid].getTableApproximateCount(params);
|
||||||
.select('*')
|
return { status: 'success', response: result };
|
||||||
.schema('information_schema')
|
}
|
||||||
.from('KEY_COLUMN_USAGE')
|
catch (err) {
|
||||||
.where({ TABLE_SCHEMA: `= '${schema}'`, TABLE_NAME: `= '${table}'`, REFERENCED_TABLE_NAME: 'IS NOT NULL' })
|
return { status: 'error', response: err.toString() };
|
||||||
.run();
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const result = rows.map(field => {
|
ipcMain.handle('get-table-options', async (event, params) => {
|
||||||
return {
|
try {
|
||||||
schema: field.TABLE_SCHEMA,
|
const result = await connections[params.uid].getTableOptions(params);
|
||||||
table: field.TABLE_NAME,
|
return { status: 'success', response: result };
|
||||||
column: field.COLUMN_NAME,
|
}
|
||||||
position: field.ORDINAL_POSITION,
|
catch (err) {
|
||||||
constraintPosition: field.POSITION_IN_UNIQUE_CONSTRAINT,
|
return { status: 'error', response: err.toString() };
|
||||||
constraintName: field.CONSTRAINT_NAME,
|
}
|
||||||
refSchema: field.REFERENCED_TABLE_SCHEMA,
|
});
|
||||||
refTable: field.REFERENCED_TABLE_NAME,
|
|
||||||
refColumn: field.REFERENCED_COLUMN_NAME
|
ipcMain.handle('get-table-indexes', async (event, params) => {
|
||||||
};
|
try {
|
||||||
});
|
const result = await connections[params.uid].getTableIndexes(params);
|
||||||
|
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('get-key-usage', async (event, params) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[params.uid].getKeyUsage(params);
|
||||||
|
|
||||||
return { status: 'success', response: result };
|
return { status: 'success', response: result };
|
||||||
}
|
}
|
||||||
@@ -88,33 +81,96 @@ export default (connections) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('update-table-cell', async (event, params) => {
|
ipcMain.handle('update-table-cell', async (event, params) => {
|
||||||
try {
|
delete params.row._id;
|
||||||
|
|
||||||
|
try { // TODO: move to client classes
|
||||||
let escapedParam;
|
let escapedParam;
|
||||||
let reload = false;
|
let reload = false;
|
||||||
const id = typeof params.id === 'number' ? params.id : `"${params.id}"`;
|
const id = typeof params.id === 'number' ? params.id : `"${params.id}"`;
|
||||||
|
|
||||||
if (NUMBER.includes(params.type))
|
if ([...NUMBER, ...FLOAT].includes(params.type))
|
||||||
escapedParam = params.content;
|
escapedParam = params.content;
|
||||||
else if ([...TEXT, ...LONG_TEXT].includes(params.type))
|
else if ([...TEXT, ...LONG_TEXT].includes(params.type)) {
|
||||||
escapedParam = `"${sqlEscaper(params.content)}"`;
|
switch (connections[params.uid]._client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
escapedParam = `"${sqlEscaper(params.content)}"`;
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
escapedParam = `'${params.content.replaceAll('\'', '\'\'')}'`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ARRAY.includes(params.type))
|
||||||
|
escapedParam = `'${params.content}'`;
|
||||||
|
else if (TEXT_SEARCH.includes(params.type))
|
||||||
|
escapedParam = `'${params.content.replaceAll('\'', '\'\'')}'`;
|
||||||
else if (BLOB.includes(params.type)) {
|
else if (BLOB.includes(params.type)) {
|
||||||
if (params.content) {
|
if (params.content) {
|
||||||
const fileBlob = fs.readFileSync(params.content);
|
let fileBlob;
|
||||||
escapedParam = `0x${fileBlob.toString('hex')}`;
|
|
||||||
|
switch (connections[params.uid]._client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
fileBlob = fs.readFileSync(params.content);
|
||||||
|
escapedParam = `0x${fileBlob.toString('hex')}`;
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
fileBlob = fs.readFileSync(params.content);
|
||||||
|
escapedParam = `decode('${fileBlob.toString('hex')}', 'hex')`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
reload = true;
|
reload = true;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
escapedParam = '""';
|
switch (connections[params.uid]._client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
escapedParam = '\'\'';
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
escapedParam = 'decode(\'\', \'hex\')';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else if ([...BIT].includes(params.type)) {
|
||||||
|
escapedParam = `b'${sqlEscaper(params.content)}'`;
|
||||||
|
reload = true;
|
||||||
|
}
|
||||||
|
else if (params.content === null)
|
||||||
|
escapedParam = 'NULL';
|
||||||
else
|
else
|
||||||
escapedParam = `"${sqlEscaper(params.content)}"`;
|
escapedParam = `'${sqlEscaper(params.content)}'`;
|
||||||
|
|
||||||
await connections[params.uid]
|
if (params.primary) { // TODO: handle multiple primary
|
||||||
.update({ [params.field]: `= ${escapedParam}` })
|
await connections[params.uid]
|
||||||
.schema(params.schema)
|
.update({ [params.field]: `= ${escapedParam}` })
|
||||||
.from(params.table)
|
.schema(params.schema)
|
||||||
.where({ [params.primary]: `= ${id}` })
|
.from(params.table)
|
||||||
.run();
|
.where({ [params.primary]: `= ${id}` })
|
||||||
|
.limit(1)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const { orgRow } = params;
|
||||||
|
reload = true;
|
||||||
|
|
||||||
|
for (const key in orgRow) {
|
||||||
|
if (typeof orgRow[key] === 'string')
|
||||||
|
orgRow[key] = `'${orgRow[key]}'`;
|
||||||
|
|
||||||
|
orgRow[key] = `= ${orgRow[key]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await connections[params.uid]
|
||||||
|
.schema(params.schema)
|
||||||
|
.update({ [params.field]: `= ${escapedParam}` })
|
||||||
|
.from(params.table)
|
||||||
|
.where(orgRow)
|
||||||
|
.limit(1)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
return { status: 'success', response: { reload } };
|
return { status: 'success', response: { reload } };
|
||||||
}
|
}
|
||||||
@@ -124,22 +180,56 @@ export default (connections) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('delete-table-rows', async (event, params) => {
|
ipcMain.handle('delete-table-rows', async (event, params) => {
|
||||||
try {
|
if (params.primary) {
|
||||||
const result = await connections[params.uid]
|
const idString = params.rows.map(row => {
|
||||||
.schema(params.schema)
|
const fieldName = Object.keys(row)[0].includes('.') ? `${params.table}.${params.primary}` : params.primary;
|
||||||
.delete(params.table)
|
|
||||||
.where({ [params.primary]: `IN (${params.rows.join(',')})` })
|
|
||||||
.run();
|
|
||||||
|
|
||||||
return { status: 'success', response: result };
|
return typeof row[fieldName] === 'string'
|
||||||
|
? `"${row[fieldName]}"`
|
||||||
|
: row[fieldName];
|
||||||
|
}).join(',');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await connections[params.uid]
|
||||||
|
.schema(params.schema)
|
||||||
|
.delete(params.table)
|
||||||
|
.where({ [params.primary]: `IN (${idString})` })
|
||||||
|
.run();
|
||||||
|
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (err) {
|
else {
|
||||||
return { status: 'error', response: err.toString() };
|
try {
|
||||||
|
for (const row of params.rows) {
|
||||||
|
for (const key in row) {
|
||||||
|
if (typeof row[key] === 'string')
|
||||||
|
row[key] = `'${row[key]}'`;
|
||||||
|
|
||||||
|
row[key] = `= ${row[key]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
await connections[params.uid]
|
||||||
|
.schema(params.schema)
|
||||||
|
.delete(params.table)
|
||||||
|
.where(row)
|
||||||
|
.limit(1)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
return { status: 'success', response: [] };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('insert-table-rows', async (event, params) => {
|
ipcMain.handle('insert-table-rows', async (event, params) => {
|
||||||
try {
|
try { // TODO: move to client classes
|
||||||
const insertObj = {};
|
const insertObj = {};
|
||||||
for (const key in params.row) {
|
for (const key in params.row) {
|
||||||
const type = params.fields[key];
|
const type = params.fields[key];
|
||||||
@@ -147,31 +237,58 @@ export default (connections) => {
|
|||||||
|
|
||||||
if (params.row[key] === null)
|
if (params.row[key] === null)
|
||||||
escapedParam = 'NULL';
|
escapedParam = 'NULL';
|
||||||
else if (NUMBER.includes(type))
|
else if ([...NUMBER, ...FLOAT].includes(type))
|
||||||
escapedParam = params.row[key];
|
escapedParam = +params.row[key];
|
||||||
else if ([...TEXT, ...LONG_TEXT].includes(type))
|
else if ([...TEXT, ...LONG_TEXT].includes(type)) {
|
||||||
escapedParam = `"${sqlEscaper(params.row[key])}"`;
|
switch (connections[params.uid]._client) {
|
||||||
else if (BLOB.includes(type)) {
|
case 'mysql':
|
||||||
if (params.row[key]) {
|
case 'maria':
|
||||||
const fileBlob = fs.readFileSync(params.row[key]);
|
escapedParam = `"${sqlEscaper(params.row[key].value)}"`;
|
||||||
escapedParam = `0x${fileBlob.toString('hex')}`;
|
break;
|
||||||
|
case 'pg':
|
||||||
|
escapedParam = `'${params.row[key].value.replaceAll('\'', '\'\'')}'`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (BLOB.includes(type)) {
|
||||||
|
if (params.row[key].value) {
|
||||||
|
let fileBlob;
|
||||||
|
|
||||||
|
switch (connections[params.uid]._client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
fileBlob = fs.readFileSync(params.row[key].value);
|
||||||
|
escapedParam = `0x${fileBlob.toString('hex')}`;
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
fileBlob = fs.readFileSync(params.row[key].value);
|
||||||
|
escapedParam = `decode('${fileBlob.toString('hex')}', 'hex')`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch (connections[params.uid]._client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
escapedParam = '""';
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
escapedParam = 'decode(\'\', \'hex\')';
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
escapedParam = '""';
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
escapedParam = `"${sqlEscaper(params.row[key])}"`;
|
|
||||||
|
|
||||||
insertObj[key] = escapedParam;
|
insertObj[key] = escapedParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < params.repeat; i++) {
|
const rows = new Array(+params.repeat).fill(insertObj);
|
||||||
await connections[params.uid]
|
|
||||||
.schema(params.schema)
|
await connections[params.uid]
|
||||||
.into(params.table)
|
.schema(params.schema)
|
||||||
.insert(insertObj)
|
.into(params.table)
|
||||||
.run();
|
.insert(rows)
|
||||||
}
|
.run();
|
||||||
|
|
||||||
return { status: 'success' };
|
return { status: 'success' };
|
||||||
}
|
}
|
||||||
@@ -180,16 +297,123 @@ export default (connections) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.handle('get-foreign-list', async (event, params) => {
|
ipcMain.handle('insert-table-fake-rows', async (event, params) => {
|
||||||
try {
|
try { // TODO: move to client classes
|
||||||
const query = connections[params.uid]
|
const rows = [];
|
||||||
.select(`${params.column} AS foreignColumn`)
|
|
||||||
.schema(params.schema)
|
|
||||||
.from(params.table)
|
|
||||||
.orderBy('foreignColumn ASC');
|
|
||||||
|
|
||||||
if (params.description)
|
for (let i = 0; i < +params.repeat; i++) {
|
||||||
query.select(`LEFT(${params.description}, 20) AS foreignDescription`);
|
const insertObj = {};
|
||||||
|
|
||||||
|
for (const key in params.row) {
|
||||||
|
const type = params.fields[key];
|
||||||
|
let escapedParam;
|
||||||
|
|
||||||
|
if (!('group' in params.row[key]) || params.row[key].group === 'manual') { // Manual value
|
||||||
|
if (params.row[key].value === null || params.row[key].value === undefined)
|
||||||
|
escapedParam = 'NULL';
|
||||||
|
else if ([...NUMBER, ...FLOAT].includes(type))
|
||||||
|
escapedParam = params.row[key].value;
|
||||||
|
else if ([...TEXT, ...LONG_TEXT].includes(type)) {
|
||||||
|
switch (connections[params.uid]._client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
escapedParam = `"${sqlEscaper(params.row[key].value)}"`;
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
escapedParam = `'${params.row[key].value.replaceAll('\'', '\'\'')}'`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (BLOB.includes(type)) {
|
||||||
|
if (params.row[key].value) {
|
||||||
|
let fileBlob;
|
||||||
|
|
||||||
|
switch (connections[params.uid]._client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
fileBlob = fs.readFileSync(params.row[key].value);
|
||||||
|
escapedParam = `0x${fileBlob.toString('hex')}`;
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
fileBlob = fs.readFileSync(params.row[key].value);
|
||||||
|
escapedParam = `decode('${fileBlob.toString('hex')}', 'hex')`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch (connections[params.uid]._client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
escapedParam = '""';
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
escapedParam = 'decode(\'\', \'hex\')';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (BIT.includes(type))
|
||||||
|
escapedParam = `b'${sqlEscaper(params.row[key].value)}'`;
|
||||||
|
else
|
||||||
|
escapedParam = `'${sqlEscaper(params.row[key].value)}'`;
|
||||||
|
|
||||||
|
insertObj[key] = escapedParam;
|
||||||
|
}
|
||||||
|
else { // Faker value
|
||||||
|
const parsedParams = {};
|
||||||
|
let fakeValue;
|
||||||
|
|
||||||
|
if (params.locale)
|
||||||
|
faker.locale = params.locale;
|
||||||
|
|
||||||
|
if (Object.keys(params.row[key].params).length) {
|
||||||
|
Object.keys(params.row[key].params).forEach(param => {
|
||||||
|
if (!isNaN(params.row[key].params[param]))
|
||||||
|
parsedParams[param] = +params.row[key].params[param];
|
||||||
|
});
|
||||||
|
fakeValue = faker[params.row[key].group][params.row[key].method](parsedParams);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fakeValue = faker[params.row[key].group][params.row[key].method]();
|
||||||
|
|
||||||
|
if (typeof fakeValue === 'string') {
|
||||||
|
if (params.row[key].length)
|
||||||
|
fakeValue = fakeValue.substr(0, params.row[key].length);
|
||||||
|
fakeValue = `'${sqlEscaper(fakeValue)}'`;
|
||||||
|
}
|
||||||
|
else if ([...DATE, ...DATETIME].includes(type))
|
||||||
|
fakeValue = `'${moment(fakeValue).format('YYYY-MM-DD HH:mm:ss.SSSSSS')}'`;
|
||||||
|
|
||||||
|
insertObj[key] = fakeValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.push(insertObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
await connections[params.uid]
|
||||||
|
.schema(params.schema)
|
||||||
|
.into(params.table)
|
||||||
|
.insert(rows)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('get-foreign-list', async (event, { uid, schema, table, column, description }) => {
|
||||||
|
try {
|
||||||
|
const query = connections[uid]
|
||||||
|
.select(`${column} AS foreign_column`)
|
||||||
|
.schema(schema)
|
||||||
|
.from(table)
|
||||||
|
.orderBy('foreign_column ASC');
|
||||||
|
|
||||||
|
if (description)
|
||||||
|
query.select(`LEFT(${description}, 20) AS foreign_description`);
|
||||||
|
|
||||||
const results = await query.run();
|
const results = await query.run();
|
||||||
|
|
||||||
@@ -199,4 +423,54 @@ export default (connections) => {
|
|||||||
return { status: 'error', response: err.toString() };
|
return { status: 'error', response: err.toString() };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('create-table', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].createTable(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('alter-table', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].alterTable(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('duplicate-table', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].duplicateTable(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('truncate-table', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].truncateTable(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('drop-table', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].dropTable(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
43
src/main/ipc-handlers/triggers.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { ipcMain } from 'electron';
|
||||||
|
|
||||||
|
export default (connections) => {
|
||||||
|
ipcMain.handle('get-trigger-informations', async (event, params) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[params.uid].getTriggerInformations(params);
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('drop-trigger', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].dropTrigger(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('alter-trigger', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].alterTrigger(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('create-trigger', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].createTrigger(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,16 +1,21 @@
|
|||||||
import { ipcMain } from 'electron';
|
import { ipcMain } from 'electron';
|
||||||
import { autoUpdater } from 'electron-updater';
|
import { autoUpdater } from 'electron-updater';
|
||||||
|
import Store from 'electron-store';
|
||||||
|
const persistentStore = new Store({ name: 'settings' });
|
||||||
|
|
||||||
let mainWindow;
|
let mainWindow;
|
||||||
autoUpdater.allowPrerelease = true;
|
autoUpdater.allowPrerelease = persistentStore.get('allow_prerelease', true);
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
ipcMain.on('check-for-updates', event => {
|
ipcMain.on('check-for-updates', event => {
|
||||||
mainWindow = event;
|
mainWindow = event;
|
||||||
|
if (process.windowsStore || (process.platform === 'linux' && !process.env.APPIMAGE))
|
||||||
autoUpdater.checkForUpdatesAndNotify().catch(() => {
|
mainWindow.reply('no-auto-update');
|
||||||
mainWindow.reply('check-failed');
|
else {
|
||||||
});
|
autoUpdater.checkForUpdatesAndNotify().catch(() => {
|
||||||
|
mainWindow.reply('check-failed');
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('restart-to-update', () => {
|
ipcMain.on('restart-to-update', () => {
|
||||||
|
|||||||
15
src/main/ipc-handlers/users.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { ipcMain } from 'electron';
|
||||||
|
|
||||||
|
export default (connections) => {
|
||||||
|
ipcMain.handle('get-users', async (event, uid) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[uid].getUsers();
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
if (err.code === 'ER_TABLEACCESS_DENIED_ERROR')
|
||||||
|
return { status: 'success', response: [] };
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
43
src/main/ipc-handlers/views.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { ipcMain } from 'electron';
|
||||||
|
|
||||||
|
export default (connections) => {
|
||||||
|
ipcMain.handle('get-view-informations', async (event, params) => {
|
||||||
|
try {
|
||||||
|
const result = await connections[params.uid].getViewInformations(params);
|
||||||
|
return { status: 'success', response: result };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('drop-view', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].dropView(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('alter-view', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].alterView(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('create-view', async (event, params) => {
|
||||||
|
try {
|
||||||
|
await connections[params.uid].createView(params);
|
||||||
|
return { status: 'success' };
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
return { status: 'error', response: err.toString() };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -26,9 +26,10 @@ export class AntaresCore {
|
|||||||
groupBy: [],
|
groupBy: [],
|
||||||
orderBy: [],
|
orderBy: [],
|
||||||
limit: [],
|
limit: [],
|
||||||
|
offset: [],
|
||||||
join: [],
|
join: [],
|
||||||
update: [],
|
update: [],
|
||||||
insert: {},
|
insert: [],
|
||||||
delete: false
|
delete: false
|
||||||
};
|
};
|
||||||
this._query = Object.assign({}, this._queryDefaults);
|
this._query = Object.assign({}, this._queryDefaults);
|
||||||
@@ -109,6 +110,11 @@ export class AntaresCore {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offset (...args) {
|
||||||
|
this._query.offset = args;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String | Array} args field = value
|
* @param {String | Array} args field = value
|
||||||
* @returns
|
* @returns
|
||||||
@@ -120,22 +126,23 @@ export class AntaresCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} obj field: value
|
* @param {Array} arr Array of row objects
|
||||||
* @returns
|
* @returns
|
||||||
* @memberof AntaresCore
|
* @memberof AntaresCore
|
||||||
*/
|
*/
|
||||||
insert (obj) {
|
insert (arr) {
|
||||||
this._query.insert = { ...this._query.insert, ...obj };
|
this._query.insert = [...this._query.insert, ...arr];
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param {Object} args
|
||||||
* @returns {Promise}
|
* @returns {Promise}
|
||||||
* @memberof AntaresCore
|
* @memberof AntaresCore
|
||||||
*/
|
*/
|
||||||
async run () {
|
run (args) {
|
||||||
const rawQuery = this.getSQL();
|
const rawQuery = this.getSQL();
|
||||||
this._resetQuery();
|
this._resetQuery();
|
||||||
return this.raw(rawQuery);
|
return this.raw(rawQuery, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
import { MySQLClient } from './clients/MySQLClient';
|
import { MySQLClient } from './clients/MySQLClient';
|
||||||
|
import { PostgreSQLClient } from './clients/PostgreSQLClient';
|
||||||
|
|
||||||
|
const queryLogger = sql => {
|
||||||
|
// Remove comments, newlines and multiple spaces
|
||||||
|
const escapedSql = sql.replace(/(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm, '').replace(/\s\s+/g, ' ');
|
||||||
|
console.log(escapedSql);
|
||||||
|
};
|
||||||
|
|
||||||
export class ClientsFactory {
|
export class ClientsFactory {
|
||||||
/**
|
/**
|
||||||
@@ -11,17 +18,27 @@ export class ClientsFactory {
|
|||||||
* @param {String} args.params.host
|
* @param {String} args.params.host
|
||||||
* @param {Number} args.params.port
|
* @param {Number} args.params.port
|
||||||
* @param {String} args.params.password
|
* @param {String} args.params.password
|
||||||
|
* @param {String=} args.params.database
|
||||||
|
* @param {String=} args.params.schema
|
||||||
|
* @param {String} args.params.ssh.host
|
||||||
|
* @param {String} args.params.ssh.username
|
||||||
|
* @param {String} args.params.ssh.password
|
||||||
|
* @param {Number} args.params.ssh.port
|
||||||
* @param {Number=} args.poolSize
|
* @param {Number=} args.poolSize
|
||||||
* @returns Database Connection
|
* @returns Database Connection
|
||||||
* @memberof ClientsFactory
|
* @memberof ClientsFactory
|
||||||
*/
|
*/
|
||||||
static getConnection (args) {
|
static getConnection (args) {
|
||||||
|
args.logger = queryLogger;
|
||||||
|
|
||||||
switch (args.client) {
|
switch (args.client) {
|
||||||
case 'mysql':
|
case 'mysql':
|
||||||
case 'maria':
|
case 'maria':
|
||||||
return new MySQLClient(args);
|
return new MySQLClient(args);
|
||||||
|
case 'pg':
|
||||||
|
return new PostgreSQLClient(args);
|
||||||
default:
|
default:
|
||||||
return new Error(`Unknown database client: ${args.client}`);
|
throw new Error(`Unknown database client: ${args.client}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1494
src/main/libs/clients/PostgreSQLClient.js
Normal file
@@ -1,23 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="wrapper">
|
<div id="wrapper" :class="`theme-${applicationTheme}`">
|
||||||
<TheTitleBar />
|
<TheTitleBar />
|
||||||
<div id="window-content">
|
<div id="window-content">
|
||||||
<TheSettingBar />
|
<TheSettingBar />
|
||||||
<div id="main-content" class="container">
|
<div id="main-content" class="container">
|
||||||
<TheAppWelcome v-if="!connections.length" @new-conn="showNewConnModal" />
|
<div class="columns col-gapless">
|
||||||
<div v-else class="columns col-gapless">
|
|
||||||
<Workspace
|
<Workspace
|
||||||
v-for="connection in connections"
|
v-for="connection in connections"
|
||||||
:key="connection.uid"
|
:key="connection.uid"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
/>
|
/>
|
||||||
|
<WorkspaceAddConnectionPanel v-if="selectedWorkspace === 'NEW'" />
|
||||||
</div>
|
</div>
|
||||||
|
<TheFooter />
|
||||||
|
<TheNotificationsBoard />
|
||||||
|
<TheScratchpad v-if="isScratchpad" />
|
||||||
|
<ModalSettings v-if="isSettingModal" />
|
||||||
|
<BaseTextEditor class="d-none" value="" />
|
||||||
</div>
|
</div>
|
||||||
<TheFooter />
|
|
||||||
<TheNotificationsBoard />
|
|
||||||
<ModalNewConnection v-if="isNewConnModal" />
|
|
||||||
<ModalEditConnection v-if="isEditModal" />
|
|
||||||
<ModalSettings v-if="isSettingModal" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapActions, mapGetters } from 'vuex';
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
|
import { Menu, getCurrentWindow } from '@electron/remote';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'App',
|
name: 'App',
|
||||||
@@ -33,30 +34,71 @@ export default {
|
|||||||
TheSettingBar: () => import(/* webpackChunkName: "TheSettingBar" */'@/components/TheSettingBar'),
|
TheSettingBar: () => import(/* webpackChunkName: "TheSettingBar" */'@/components/TheSettingBar'),
|
||||||
TheFooter: () => import(/* webpackChunkName: "TheFooter" */'@/components/TheFooter'),
|
TheFooter: () => import(/* webpackChunkName: "TheFooter" */'@/components/TheFooter'),
|
||||||
TheNotificationsBoard: () => import(/* webpackChunkName: "TheNotificationsBoard" */'@/components/TheNotificationsBoard'),
|
TheNotificationsBoard: () => import(/* webpackChunkName: "TheNotificationsBoard" */'@/components/TheNotificationsBoard'),
|
||||||
TheAppWelcome: () => import(/* webpackChunkName: "TheAppWelcome" */'@/components/TheAppWelcome'),
|
|
||||||
Workspace: () => import(/* webpackChunkName: "Workspace" */'@/components/Workspace'),
|
Workspace: () => import(/* webpackChunkName: "Workspace" */'@/components/Workspace'),
|
||||||
ModalNewConnection: () => import(/* webpackChunkName: "ModalNewConnection" */'@/components/ModalNewConnection'),
|
WorkspaceAddConnectionPanel: () => import(/* webpackChunkName: "WorkspaceAddConnectionPanel" */'@/components/WorkspaceAddConnectionPanel'),
|
||||||
ModalEditConnection: () => import(/* webpackChunkName: "ModalEditConnection" */'@/components/ModalEditConnection'),
|
ModalSettings: () => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings'),
|
||||||
ModalSettings: () => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings')
|
TheScratchpad: () => import(/* webpackChunkName: "TheScratchpad" */'@/components/TheScratchpad'),
|
||||||
|
BaseTextEditor: () => import(/* webpackChunkName: "BaseTextEditor" */'@/components/BaseTextEditor')
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
isLoading: 'application/isLoading',
|
isLoading: 'application/isLoading',
|
||||||
isNewConnModal: 'application/isNewModal',
|
|
||||||
isEditModal: 'application/isEditModal',
|
|
||||||
isSettingModal: 'application/isSettingModal',
|
isSettingModal: 'application/isSettingModal',
|
||||||
connections: 'connections/getConnections'
|
isScratchpad: 'application/isScratchpad',
|
||||||
|
connections: 'connections/getConnections',
|
||||||
|
applicationTheme: 'settings/getApplicationTheme',
|
||||||
|
isUnsavedDiscardModal: 'workspaces/isUnsavedDiscardModal'
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
ipcRenderer.send('check-for-updates');
|
ipcRenderer.send('check-for-updates');
|
||||||
|
this.checkVersionUpdate();
|
||||||
|
|
||||||
|
const InputMenu = Menu.buildFromTemplate([
|
||||||
|
{
|
||||||
|
label: this.$t('word.cut'),
|
||||||
|
role: 'cut'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('word.copy'),
|
||||||
|
role: 'copy'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('word.paste'),
|
||||||
|
role: 'paste'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'separator'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.$t('message.selectAll'),
|
||||||
|
role: 'selectall'
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
document.body.addEventListener('contextmenu', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
let node = e.target;
|
||||||
|
|
||||||
|
while (node) {
|
||||||
|
if (node.nodeName.match(/^(input|textarea)$/i) || node.isContentEditable) {
|
||||||
|
InputMenu.popup(getCurrentWindow());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
node = node.parentNode;
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
showNewConnModal: 'application/showNewConnModal'
|
showNewConnModal: 'application/showNewConnModal',
|
||||||
|
checkVersionUpdate: 'application/checkVersionUpdate'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
<slot name="body" />
|
<slot name="body" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div v-if="!hideFooter" class="modal-footer">
|
||||||
<button
|
<button
|
||||||
class="btn btn-primary mr-2"
|
class="btn btn-primary mr-2"
|
||||||
@click.stop="confirmModal"
|
@click.stop="confirmModal"
|
||||||
@@ -48,9 +48,13 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String,
|
||||||
validator: prop => ['small', 'medium', 'large'].includes(prop),
|
validator: prop => ['small', 'medium', '400', 'large'].includes(prop),
|
||||||
default: 'small'
|
default: 'small'
|
||||||
},
|
},
|
||||||
|
hideFooter: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
confirmText: String,
|
confirmText: String,
|
||||||
cancelText: String
|
cancelText: String
|
||||||
},
|
},
|
||||||
@@ -67,11 +71,19 @@ export default {
|
|||||||
modalSizeClass () {
|
modalSizeClass () {
|
||||||
if (this.size === 'small')
|
if (this.size === 'small')
|
||||||
return 'modal-sm';
|
return 'modal-sm';
|
||||||
|
if (this.size === '400')
|
||||||
|
return 'modal-400';
|
||||||
else if (this.size === 'large')
|
else if (this.size === 'large')
|
||||||
return 'modal-lg';
|
return 'modal-lg';
|
||||||
else return '';
|
else return '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
confirmModal () {
|
confirmModal () {
|
||||||
this.$emit('confirm');
|
this.$emit('confirm');
|
||||||
@@ -80,13 +92,23 @@ export default {
|
|||||||
|
|
||||||
hideModal () {
|
hideModal () {
|
||||||
this.$emit('hide');
|
this.$emit('hide');
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape')
|
||||||
|
this.hideModal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.modal.modal-sm .modal-container {
|
.modal-400 .modal-container {
|
||||||
padding: 0;
|
max-width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal.modal-sm .modal-container {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="context">
|
<div class="context" :class="{'bottom': isBottom}">
|
||||||
|
<a
|
||||||
|
class="context-overlay"
|
||||||
|
@click="close"
|
||||||
|
@contextmenu="close"
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
v-click-outside="close"
|
ref="contextContent"
|
||||||
class="context-container"
|
class="context-container"
|
||||||
:style="position"
|
:style="position"
|
||||||
>
|
>
|
||||||
@@ -11,85 +16,151 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ClickOutside from 'vue-click-outside';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BaseContextMenu',
|
name: 'BaseContextMenu',
|
||||||
directives: {
|
|
||||||
ClickOutside
|
|
||||||
},
|
|
||||||
props: {
|
props: {
|
||||||
contextEvent: MouseEvent
|
contextEvent: MouseEvent
|
||||||
},
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
contextSize: null,
|
||||||
|
isBottom: false
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
position () {
|
position () {
|
||||||
return { // TODO: calc direction if near corners
|
let topCord = 0;
|
||||||
top: this.contextEvent.clientY + 5 + 'px',
|
let leftCord = 0;
|
||||||
left: this.contextEvent.clientX + 5 + 'px'
|
|
||||||
|
if (this.contextEvent) {
|
||||||
|
const { clientY, clientX } = this.contextEvent;
|
||||||
|
topCord = `${clientY + 2}px`;
|
||||||
|
leftCord = `${clientX + 5}px`;
|
||||||
|
|
||||||
|
if (this.contextSize) {
|
||||||
|
if (clientY + (this.contextSize.height < 200 ? 200 : this.contextSize.height) + 5 >= window.innerHeight) {
|
||||||
|
topCord = `${clientY + 3 - this.contextSize.height}px`;
|
||||||
|
this.isBottom = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clientX + this.contextSize.width + 5 >= window.innerWidth)
|
||||||
|
leftCord = `${clientX - this.contextSize.width}px`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
top: topCord,
|
||||||
|
left: leftCord
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
if (this.$refs.contextContent)
|
||||||
|
this.contextSize = this.$refs.contextContent.getBoundingClientRect();
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
close () {
|
close () {
|
||||||
this.$emit('close-context');
|
this.$emit('close-context');
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape')
|
||||||
|
this.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.context {
|
.context {
|
||||||
|
display: flex;
|
||||||
|
font-size: 16px;
|
||||||
|
z-index: 400;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
position: fixed;
|
||||||
|
height: 100vh;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
&:not(.bottom) .context-submenu {
|
||||||
|
top: -0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.bottom .context-submenu {
|
||||||
|
bottom: -0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.context-container {
|
||||||
|
min-width: 100px;
|
||||||
|
z-index: 10;
|
||||||
|
padding: 0;
|
||||||
|
background: #1d1d1d;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
border: 1px solid $bg-color-light-dark;
|
||||||
display: flex;
|
display: flex;
|
||||||
color: $body-font-color;
|
flex-direction: column;
|
||||||
font-size: 16px;
|
position: absolute;
|
||||||
z-index: 400;
|
pointer-events: initial;
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
overflow: hidden;
|
|
||||||
position: fixed;
|
|
||||||
height: 100vh;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
|
|
||||||
.context-container {
|
.context-element {
|
||||||
min-width: 100px;
|
|
||||||
max-width: 150px;
|
|
||||||
z-index: 10;
|
|
||||||
box-shadow: 0 0 1px 0 #000;
|
|
||||||
padding: 0;
|
|
||||||
background: #1d1d1d;
|
|
||||||
border-radius: 0.1rem;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
align-items: center;
|
||||||
position: absolute;
|
margin: 0.2rem;
|
||||||
pointer-events: initial;
|
padding: 0.1rem 0.3rem;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
cursor: pointer;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: relative;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
.context-element {
|
.context-submenu {
|
||||||
display: flex;
|
border-radius: $border-radius;
|
||||||
align-items: center;
|
border: 1px solid $bg-color-light-dark;
|
||||||
padding: 0.1rem 0.3rem;
|
opacity: 0;
|
||||||
cursor: pointer;
|
visibility: hidden;
|
||||||
justify-content: space-between;
|
transition: opacity 0.2s;
|
||||||
|
position: absolute;
|
||||||
|
left: 100%;
|
||||||
|
min-width: 100px;
|
||||||
|
background: #1d1d1d;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: $primary-color;
|
.context-submenu {
|
||||||
|
display: block;
|
||||||
|
visibility: visible;
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-overlay {
|
|
||||||
background: transparent;
|
|
||||||
bottom: 0;
|
|
||||||
cursor: default;
|
|
||||||
display: block;
|
|
||||||
left: 0;
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.context-overlay {
|
||||||
|
background: transparent;
|
||||||
|
bottom: 0;
|
||||||
|
cursor: default;
|
||||||
|
display: block;
|
||||||
|
left: 0;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
filter: grayscale(100%);
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
22
src/renderer/components/BaseLoader.vue
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<template>
|
||||||
|
<div class="empty">
|
||||||
|
<div class="loading loading-lg" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'BaseLoader'
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.empty {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
left: 0;
|
||||||
|
justify-content: center;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
131
src/renderer/components/BaseTextEditor.vue
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
<template>
|
||||||
|
<div class="editor-wrapper">
|
||||||
|
<div
|
||||||
|
:id="`editor-${id}`"
|
||||||
|
class="editor"
|
||||||
|
:class="editorClass"
|
||||||
|
:style="{height: `${height}px`}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as ace from 'ace-builds';
|
||||||
|
import 'ace-builds/webpack-resolver';
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'BaseTextEditor',
|
||||||
|
props: {
|
||||||
|
value: String,
|
||||||
|
mode: { type: String, default: 'text' },
|
||||||
|
editorClass: { type: String, default: '' },
|
||||||
|
autoFocus: { type: Boolean, default: false },
|
||||||
|
readOnly: { type: Boolean, default: false },
|
||||||
|
showLineNumbers: { type: Boolean, default: true },
|
||||||
|
height: { type: Number, default: 200 }
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
editor: null,
|
||||||
|
id: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
editorTheme: 'settings/getEditorTheme',
|
||||||
|
editorFontSize: 'settings/getEditorFontSize',
|
||||||
|
autoComplete: 'settings/getAutoComplete',
|
||||||
|
lineWrap: 'settings/getLineWrap'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
mode () {
|
||||||
|
if (this.editor)
|
||||||
|
this.editor.session.setMode(`ace/mode/${this.mode}`);
|
||||||
|
},
|
||||||
|
editorTheme () {
|
||||||
|
if (this.editor)
|
||||||
|
this.editor.setTheme(`ace/theme/${this.editorTheme}`);
|
||||||
|
},
|
||||||
|
editorFontSize () {
|
||||||
|
const sizes = {
|
||||||
|
small: '12px',
|
||||||
|
medium: '14px',
|
||||||
|
large: '16px'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.setOptions({
|
||||||
|
fontSize: sizes[this.editorFontSize]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
autoComplete () {
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.setOptions({
|
||||||
|
enableLiveAutocompletion: this.autoComplete
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lineWrap () {
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.setOptions({
|
||||||
|
wrap: this.lineWrap
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.id = this._uid;
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.editor = ace.edit(`editor-${this.id}`, {
|
||||||
|
mode: `ace/mode/${this.mode}`,
|
||||||
|
theme: `ace/theme/${this.editorTheme}`,
|
||||||
|
value: this.value || '',
|
||||||
|
fontSize: '14px',
|
||||||
|
printMargin: false,
|
||||||
|
readOnly: this.readOnly,
|
||||||
|
showLineNumbers: this.showLineNumbers,
|
||||||
|
showGutter: this.showLineNumbers
|
||||||
|
});
|
||||||
|
|
||||||
|
this.editor.setOptions({
|
||||||
|
enableBasicAutocompletion: false,
|
||||||
|
wrap: this.lineWrap,
|
||||||
|
enableSnippets: false,
|
||||||
|
enableLiveAutocompletion: false
|
||||||
|
});
|
||||||
|
|
||||||
|
this.editor.session.on('change', () => {
|
||||||
|
const content = this.editor.getValue();
|
||||||
|
this.$emit('update:value', content);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.autoFocus) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.editor.focus();
|
||||||
|
this.editor.resize();
|
||||||
|
}, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.editor.resize();
|
||||||
|
}, 20);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.editor-wrapper {
|
||||||
|
.editor {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ace_.mdi {
|
||||||
|
display: inline-block;
|
||||||
|
width: 17px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
class="toast mt-2"
|
class="toast mt-2"
|
||||||
:class="toastStatus.className"
|
:class="toastStatus.className"
|
||||||
>
|
>
|
||||||
<span class="p-vcentered text-left" v-html="`${toastStatus.iconTag} ${message}`" />
|
<span class="p-vcentered text-left"><i class="mdi mdi-24px mr-1" :class="toastStatus.iconName" /> {{ message }}</span>
|
||||||
<button class="btn btn-clear" @click="hideToast" />
|
<button class="btn btn-clear" @click="hideToast" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -30,27 +30,27 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
toastStatus () {
|
toastStatus () {
|
||||||
let className = '';
|
let className = '';
|
||||||
let iconTag = '';
|
let iconName = '';
|
||||||
switch (this.status) {
|
switch (this.status) {
|
||||||
case 'success':
|
case 'success':
|
||||||
className = 'toast-success';
|
className = 'toast-success';
|
||||||
iconTag = '<i class="mdi mdi-24px mdi-check mr-1"></i>';
|
iconName = 'mdi-check';
|
||||||
break;
|
break;
|
||||||
case 'error':
|
case 'error':
|
||||||
className = 'toast-error';
|
className = 'toast-error';
|
||||||
iconTag = '<i class="mdi mdi-24px mdi-alert-rhombus mr-1"></i>';
|
iconName = 'mdi-alert-rhombus';
|
||||||
break;
|
break;
|
||||||
case 'warning':
|
case 'warning':
|
||||||
className = 'toast-warning';
|
className = 'toast-warning';
|
||||||
iconTag = '<i class="mdi mdi-24px mdi-alert mr-1"></i>';
|
iconName = 'mdi-alert';
|
||||||
break;
|
break;
|
||||||
case 'primary':
|
case 'primary':
|
||||||
className = 'toast-primary';
|
className = 'toast-primary';
|
||||||
iconTag = '<i class="mdi mdi-24px mdi-information-outline mr-1"></i>';
|
iconName = 'mdi-information-outline';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { className, iconTag };
|
return { className, iconName };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|||||||
108
src/renderer/components/BaseUploadInput.vue
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
<template>
|
||||||
|
<label :for="`id_${id}`" class="file-uploader">
|
||||||
|
<span class="file-uploader-message">
|
||||||
|
<i class="mdi mdi-folder-open mr-1" />{{ message }}
|
||||||
|
</span>
|
||||||
|
<span class="text-ellipsis file-uploader-value">
|
||||||
|
{{ value | lastPart }}
|
||||||
|
</span>
|
||||||
|
<i
|
||||||
|
v-if="value.length"
|
||||||
|
class="file-uploader-reset mdi mdi-close"
|
||||||
|
@click.prevent="clear"
|
||||||
|
/>
|
||||||
|
<form :ref="`form_${id}`">
|
||||||
|
<input
|
||||||
|
:id="`id_${id}`"
|
||||||
|
class="file-uploader-input"
|
||||||
|
type="file"
|
||||||
|
@change="$emit('change', $event)"
|
||||||
|
>
|
||||||
|
</form>
|
||||||
|
</label>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'BaseUploadInput',
|
||||||
|
filters: {
|
||||||
|
lastPart (string) {
|
||||||
|
if (!string) return '';
|
||||||
|
|
||||||
|
string = string.split(/[/\\]+/).pop();
|
||||||
|
if (string.length >= 19)
|
||||||
|
string = `...${string.slice(-19)}`;
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
message: {
|
||||||
|
default: 'Browse',
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
default: '',
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
id: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.id = this._uid;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
clear () {
|
||||||
|
this.$emit('clear');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.file-uploader {
|
||||||
|
border-radius: $border-radius;
|
||||||
|
height: 1.8rem;
|
||||||
|
line-height: 1.2rem;
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s, border 0.2s, box-shadow 0.2s, color 0.2s;
|
||||||
|
position: relative;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
padding: 0.25rem 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-uploader-message {
|
||||||
|
display: flex;
|
||||||
|
border-radius: $border-radius 0 0 $border-radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-uploader-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-uploader-value {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-uploader-reset {
|
||||||
|
z-index: 1;
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
top: calc(50% - 8px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:disabled {
|
||||||
|
.file-uploader {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -41,14 +41,16 @@ export default {
|
|||||||
localScrollElement: null
|
localScrollElement: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
scrollElement () {
|
||||||
|
this.setScrollElement();
|
||||||
|
}
|
||||||
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this._checkScrollPosition = this.checkScrollPosition.bind(this);
|
this.setScrollElement();
|
||||||
this.localScrollElement = this.scrollElement ? this.scrollElement : this.$el;
|
|
||||||
this.updateWindow();
|
|
||||||
this.localScrollElement.addEventListener('scroll', this._checkScrollPosition);
|
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
this.localScrollElement.removeEventListener('scroll', this._checkScrollPosition);
|
this.localScrollElement.removeEventListener('scroll', this.checkScrollPosition);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
checkScrollPosition (e) {
|
checkScrollPosition (e) {
|
||||||
@@ -58,7 +60,7 @@ export default {
|
|||||||
this.updateWindow(e);
|
this.updateWindow(e);
|
||||||
}, 200);
|
}, 200);
|
||||||
},
|
},
|
||||||
updateWindow (e) {
|
updateWindow () {
|
||||||
const visibleItemsCount = Math.ceil(this.visibleHeight / this.itemHeight);
|
const visibleItemsCount = Math.ceil(this.visibleHeight / this.itemHeight);
|
||||||
const totalScrollHeight = this.items.length * this.itemHeight;
|
const totalScrollHeight = this.items.length * this.itemHeight;
|
||||||
const offset = 50;
|
const offset = 50;
|
||||||
@@ -74,6 +76,14 @@ export default {
|
|||||||
|
|
||||||
this.topHeight = firstCutIndex * this.itemHeight;
|
this.topHeight = firstCutIndex * this.itemHeight;
|
||||||
this.bottomHeight = totalScrollHeight - this.visibleItems.length * this.itemHeight - this.topHeight;
|
this.bottomHeight = totalScrollHeight - this.visibleItems.length * this.itemHeight - this.topHeight;
|
||||||
|
},
|
||||||
|
setScrollElement () {
|
||||||
|
if (this.localScrollElement)
|
||||||
|
this.localScrollElement.removeEventListener('scroll', this.checkScrollPosition);
|
||||||
|
|
||||||
|
this.localScrollElement = this.scrollElement ? this.scrollElement : this.$el;
|
||||||
|
this.updateWindow();
|
||||||
|
this.localScrollElement.addEventListener('scroll', this.checkScrollPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
257
src/renderer/components/FakerSelect.vue
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
<template>
|
||||||
|
<fieldset class="input-group mb-0">
|
||||||
|
<select
|
||||||
|
v-model="selectedGroup"
|
||||||
|
class="form-select"
|
||||||
|
:disabled="!isChecked"
|
||||||
|
style="flex-grow: 0;"
|
||||||
|
@change="onChange"
|
||||||
|
>
|
||||||
|
<option value="manual">
|
||||||
|
{{ $t('message.manualValue') }}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="group in fakerGroups"
|
||||||
|
:key="group.name"
|
||||||
|
:value="group.name"
|
||||||
|
>
|
||||||
|
{{ $t(`faker.${group.name}`) }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<select
|
||||||
|
v-if="selectedGroup !== 'manual'"
|
||||||
|
v-model="selectedMethod"
|
||||||
|
class="form-select"
|
||||||
|
:disabled="!isChecked"
|
||||||
|
@change="onChange"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="method in fakerMethods"
|
||||||
|
:key="method.name"
|
||||||
|
:value="method.name"
|
||||||
|
>
|
||||||
|
{{ $t(`faker.${method.name}`) }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<ForeignKeySelect
|
||||||
|
v-else-if="foreignKeys.includes(field.name)"
|
||||||
|
ref="formInput"
|
||||||
|
class="form-select"
|
||||||
|
:value.sync="selectedValue"
|
||||||
|
:key-usage="getKeyUsage(field.name)"
|
||||||
|
:disabled="!isChecked"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
v-else-if="inputProps().mask"
|
||||||
|
ref="formInput"
|
||||||
|
v-model="selectedValue"
|
||||||
|
v-mask="inputProps().mask"
|
||||||
|
class="form-input"
|
||||||
|
:type="inputProps().type"
|
||||||
|
:disabled="!isChecked"
|
||||||
|
>
|
||||||
|
<BaseUploadInput
|
||||||
|
v-else-if="inputProps().type === 'file'"
|
||||||
|
:value="selectedValue"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="clearValue"
|
||||||
|
@change="filesChange($event)"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
v-else-if="inputProps().type === 'number'"
|
||||||
|
ref="formInput"
|
||||||
|
v-model="selectedValue"
|
||||||
|
class="form-input"
|
||||||
|
step="any"
|
||||||
|
:type="inputProps().type"
|
||||||
|
:disabled="!isChecked"
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
v-else-if="enumArray"
|
||||||
|
v-model="selectedValue"
|
||||||
|
class="form-select"
|
||||||
|
:disabled="!isChecked"
|
||||||
|
@change="onChange"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="val in enumArray"
|
||||||
|
:key="val"
|
||||||
|
:value="val"
|
||||||
|
>
|
||||||
|
{{ val }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<input
|
||||||
|
v-else
|
||||||
|
ref="formInput"
|
||||||
|
v-model="selectedValue"
|
||||||
|
class="form-input"
|
||||||
|
:type="inputProps().type"
|
||||||
|
:disabled="!isChecked"
|
||||||
|
>
|
||||||
|
<template v-if="methodData && 'params' in methodData" class="columns">
|
||||||
|
<input
|
||||||
|
v-for="(option, key) in methodData.params"
|
||||||
|
:key="key"
|
||||||
|
v-model="methodParams[option]"
|
||||||
|
class="form-input column"
|
||||||
|
:type="inputProps().type"
|
||||||
|
:disabled="!isChecked"
|
||||||
|
:placeholder="option"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<slot />
|
||||||
|
</fieldset>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { VueMaskDirective } from 'v-mask';
|
||||||
|
import { TEXT, LONG_TEXT, NUMBER, FLOAT, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
|
||||||
|
import BaseUploadInput from '@/components/BaseUploadInput';
|
||||||
|
import ForeignKeySelect from '@/components/ForeignKeySelect';
|
||||||
|
import FakerMethods from 'common/FakerMethods';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FakerSelect',
|
||||||
|
components: {
|
||||||
|
ForeignKeySelect,
|
||||||
|
BaseUploadInput
|
||||||
|
},
|
||||||
|
directives: {
|
||||||
|
mask: VueMaskDirective
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
type: String,
|
||||||
|
field: Object,
|
||||||
|
isChecked: Boolean,
|
||||||
|
foreignKeys: Array,
|
||||||
|
keyUsage: Array,
|
||||||
|
fieldLength: Number,
|
||||||
|
fieldObj: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
localType: null,
|
||||||
|
selectedGroup: 'manual',
|
||||||
|
selectedMethod: '',
|
||||||
|
selectedValue: '',
|
||||||
|
debounceTimeout: null,
|
||||||
|
methodParams: {},
|
||||||
|
enumArray: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
fakerGroups () {
|
||||||
|
if ([...TEXT, ...LONG_TEXT].includes(this.type))
|
||||||
|
this.localType = 'string';
|
||||||
|
else if (NUMBER.includes(this.type))
|
||||||
|
this.localType = 'number';
|
||||||
|
else if (FLOAT.includes(this.type))
|
||||||
|
this.localType = 'float';
|
||||||
|
else if ([...DATE, ...DATETIME].includes(this.type))
|
||||||
|
this.localType = 'datetime';
|
||||||
|
else if (TIME.includes(this.type))
|
||||||
|
this.localType = 'time';
|
||||||
|
else
|
||||||
|
this.localType = 'none';
|
||||||
|
|
||||||
|
return FakerMethods.getGroupsByType(this.localType);
|
||||||
|
},
|
||||||
|
fakerMethods () {
|
||||||
|
return FakerMethods.getMethods({ type: this.localType, group: this.selectedGroup });
|
||||||
|
},
|
||||||
|
methodData () {
|
||||||
|
return this.fakerMethods.find(method => method.name === this.selectedMethod);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
fieldObj () {
|
||||||
|
if (this.fieldObj) {
|
||||||
|
if (Array.isArray(this.fieldObj.value)) {
|
||||||
|
this.enumArray = this.fieldObj.value;
|
||||||
|
this.selectedValue = this.fieldObj.value[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.selectedValue = this.fieldObj.value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectedGroup () {
|
||||||
|
if (this.fakerMethods.length)
|
||||||
|
this.selectedMethod = this.fakerMethods[0].name;
|
||||||
|
else
|
||||||
|
this.selectedMethod = '';
|
||||||
|
},
|
||||||
|
selectedMethod () {
|
||||||
|
this.onChange();
|
||||||
|
},
|
||||||
|
selectedValue () {
|
||||||
|
clearTimeout(this.debounceTimeout);
|
||||||
|
this.debounceTimeout = null;
|
||||||
|
this.debounceTimeout = setTimeout(() => {
|
||||||
|
this.onChange();
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
inputProps () {
|
||||||
|
if ([...TEXT, ...LONG_TEXT].includes(this.type))
|
||||||
|
return { type: 'text', mask: false };
|
||||||
|
|
||||||
|
if ([...NUMBER, ...FLOAT].includes(this.type))
|
||||||
|
return { type: 'number', mask: false };
|
||||||
|
|
||||||
|
if (TIME.includes(this.type)) {
|
||||||
|
let timeMask = '##:##:##';
|
||||||
|
const precision = this.fieldLength;
|
||||||
|
|
||||||
|
for (let i = 0; i < precision; i++)
|
||||||
|
timeMask += i === 0 ? '.#' : '#';
|
||||||
|
|
||||||
|
return { type: 'text', mask: timeMask };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DATE.includes(this.type))
|
||||||
|
return { type: 'text', mask: '####-##-##' };
|
||||||
|
|
||||||
|
if (DATETIME.includes(this.type)) {
|
||||||
|
let datetimeMask = '####-##-## ##:##:##';
|
||||||
|
const precision = this.fieldLength;
|
||||||
|
|
||||||
|
for (let i = 0; i < precision; i++)
|
||||||
|
datetimeMask += i === 0 ? '.#' : '#';
|
||||||
|
|
||||||
|
return { type: 'text', mask: datetimeMask };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BLOB.includes(this.type))
|
||||||
|
return { type: 'file', mask: false };
|
||||||
|
|
||||||
|
if (BIT.includes(this.type))
|
||||||
|
return { type: 'text', mask: false };
|
||||||
|
|
||||||
|
return { type: 'text', mask: false };
|
||||||
|
},
|
||||||
|
getKeyUsage (keyName) {
|
||||||
|
return this.keyUsage.find(key => key.field === keyName);
|
||||||
|
},
|
||||||
|
filesChange (event) {
|
||||||
|
const { files } = event.target;
|
||||||
|
if (!files.length) return;
|
||||||
|
|
||||||
|
this.selectedValue = files[0].path;
|
||||||
|
},
|
||||||
|
clearValue () {
|
||||||
|
this.selectedValue = '';
|
||||||
|
},
|
||||||
|
onChange () {
|
||||||
|
this.$emit('update:value', {
|
||||||
|
group: this.selectedGroup,
|
||||||
|
method: this.selectedMethod,
|
||||||
|
params: this.methodParams,
|
||||||
|
value: this.selectedValue,
|
||||||
|
length: this.fieldLength
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,17 +1,21 @@
|
|||||||
<template>
|
<template>
|
||||||
<select
|
<select
|
||||||
ref="editField"
|
ref="editField"
|
||||||
class="px-1"
|
class="form-select pl-1 pr-4"
|
||||||
|
:class="{'small-select': size === 'small'}"
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
@blur="$emit('blur')"
|
@blur="$emit('blur')"
|
||||||
>
|
>
|
||||||
|
<option v-if="!isValidDefault" :value="value">
|
||||||
|
{{ value }} - {{ $t('message.invalidDefault') }}
|
||||||
|
</option>
|
||||||
<option
|
<option
|
||||||
v-for="row in foreignList"
|
v-for="row in foreignList"
|
||||||
:key="row.foreignColumn"
|
:key="row.foreign_column"
|
||||||
:value="row.foreignColumn"
|
:value="row.foreign_column"
|
||||||
:selected="row.foreignColumn === value"
|
:selected="row.foreign_column === value"
|
||||||
>
|
>
|
||||||
{{ row.foreignColumn }} {{ 'foreignDescription' in row ? ` - ${row.foreignDescription}` : '' | cutText }}
|
{{ row.foreign_column }} {{ 'foreign_description' in row ? ` - ${row.foreign_description}` : '' | cutText }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</template>
|
</template>
|
||||||
@@ -30,7 +34,11 @@ export default {
|
|||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
value: [String, Number],
|
value: [String, Number],
|
||||||
keyUsage: Object
|
keyUsage: Object,
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -40,10 +48,15 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
selectedWorkspace: 'workspaces/getSelected'
|
selectedWorkspace: 'workspaces/getSelected'
|
||||||
})
|
}),
|
||||||
|
isValidDefault () {
|
||||||
|
if (!this.foreignList.length) return true;
|
||||||
|
if (this.value === null) return false;
|
||||||
|
return this.foreignList.some(foreign => foreign.foreign_column.toString() === this.value.toString());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async created () {
|
async created () {
|
||||||
let firstTextField;
|
let foreignDesc;
|
||||||
const params = {
|
const params = {
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
schema: this.keyUsage.refSchema,
|
schema: this.keyUsage.refSchema,
|
||||||
@@ -52,8 +65,10 @@ export default {
|
|||||||
|
|
||||||
try { // Field data
|
try { // Field data
|
||||||
const { status, response } = await Tables.getTableColumns(params);
|
const { status, response } = await Tables.getTableColumns(params);
|
||||||
if (status === 'success')
|
if (status === 'success') {
|
||||||
firstTextField = response.find(field => [...TEXT, ...LONG_TEXT].includes(field.type)).name || false;
|
const textField = response.find(field => [...TEXT, ...LONG_TEXT].includes(field.type) && field.name !== this.keyUsage.refField);
|
||||||
|
foreignDesc = textField ? textField.name : false;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
this.addNotification({ status: 'error', message: response });
|
this.addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
@@ -64,8 +79,8 @@ export default {
|
|||||||
try { // Foregn list
|
try { // Foregn list
|
||||||
const { status, response } = await Tables.getForeignList({
|
const { status, response } = await Tables.getForeignList({
|
||||||
...params,
|
...params,
|
||||||
column: this.keyUsage.refColumn,
|
column: this.keyUsage.refField,
|
||||||
description: firstTextField
|
description: foreignDesc
|
||||||
});
|
});
|
||||||
|
|
||||||
if (status === 'success')
|
if (status === 'success')
|
||||||
|
|||||||
@@ -15,10 +15,11 @@
|
|||||||
<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
|
||||||
|
ref="firstInput"
|
||||||
v-model="credentials.user"
|
v-model="credentials.user"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -27,7 +28,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
|
||||||
@@ -40,7 +41,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer text-light">
|
<div class="modal-footer">
|
||||||
<button class="btn btn-primary mr-2" @click.stop="sendCredentials">
|
<button class="btn btn-primary mr-2" @click.stop="sendCredentials">
|
||||||
{{ $t('word.send') }}
|
{{ $t('word.send') }}
|
||||||
</button>
|
</button>
|
||||||
@@ -63,6 +64,11 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
created () {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
closeModal () {
|
closeModal () {
|
||||||
this.$emit('close-asking');
|
this.$emit('close-asking');
|
||||||
|
|||||||
138
src/renderer/components/ModalAskParameters.vue
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.run')"
|
||||||
|
:cancel-text="$t('word.cancel')"
|
||||||
|
size="400"
|
||||||
|
@confirm="runRoutine"
|
||||||
|
@hide="closeModal"
|
||||||
|
>
|
||||||
|
<template slot="header">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-play mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('word.parameters') }}: {{ localRoutine.name }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div slot="body">
|
||||||
|
<div class="content">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div
|
||||||
|
v-for="(parameter, i) in inParameters"
|
||||||
|
:key="parameter._id"
|
||||||
|
class="form-group"
|
||||||
|
>
|
||||||
|
<div class="col-4">
|
||||||
|
<label class="form-label">{{ parameter.name }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-8">
|
||||||
|
<div class="input-group">
|
||||||
|
<input
|
||||||
|
:ref="i === 0 ? 'firstInput' : ''"
|
||||||
|
v-model="values[`${i}-${parameter.name}`]"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
:title="`${parameter.type} ${parameter.length}`"
|
||||||
|
class="input-group-addon field-type cut-text"
|
||||||
|
:class="typeClass(parameter.type)"
|
||||||
|
>
|
||||||
|
{{ parameter.type }} {{ parameter.length | wrapNumber }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { NUMBER, FLOAT } from 'common/fieldTypes';
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalAskParameters',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
wrapNumber (num) {
|
||||||
|
if (!num) return '';
|
||||||
|
return `(${num})`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
localRoutine: Object,
|
||||||
|
client: String
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
values: {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
inParameters () {
|
||||||
|
return this.localRoutine.parameters.filter(param => param.context === 'IN');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput[0].focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
typeClass (type) {
|
||||||
|
if (type)
|
||||||
|
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
runRoutine () {
|
||||||
|
const valArr = Object.keys(this.values).reduce((acc, curr, i) => {
|
||||||
|
let qc;
|
||||||
|
switch (this.client) {
|
||||||
|
case 'maria':
|
||||||
|
case 'mysql':
|
||||||
|
qc = '"';
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
qc = '\'';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qc = '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
const param = this.localRoutine.parameters.find(param => `${i}-${param.name}` === curr);
|
||||||
|
|
||||||
|
const value = [...NUMBER, ...FLOAT].includes(param.type) ? this.values[curr] : `${qc}${this.values[curr]}${qc}`;
|
||||||
|
acc.push(value);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
this.$emit('confirm', valArr);
|
||||||
|
},
|
||||||
|
closeModal () {
|
||||||
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape')
|
||||||
|
this.closeModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.field-type {
|
||||||
|
font-size: 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group-addon {
|
||||||
|
max-width: 100px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
49
src/renderer/components/ModalDiscardChanges.vue
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.discard')"
|
||||||
|
:cancel-text="$t('word.stay')"
|
||||||
|
@confirm="$emit('confirm')"
|
||||||
|
@hide="$emit('close')"
|
||||||
|
>
|
||||||
|
<template slot="header">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-content-save-alert mr-1" /> {{ $t('message.unsavedChanges') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div slot="body">
|
||||||
|
<div>
|
||||||
|
{{ $t('message.discardUnsavedChanges') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalDiscardChanges',
|
||||||
|
components: {
|
||||||
|
ConfirmModal
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape')
|
||||||
|
this.closeModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.modal-container {
|
||||||
|
max-width: 360px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="modal active">
|
|
||||||
<a class="modal-overlay c-hand" @click="closeModal" />
|
|
||||||
<div class="modal-container">
|
|
||||||
<div class="modal-header pl-2">
|
|
||||||
<div class="modal-title h6">
|
|
||||||
<div class="d-flex">
|
|
||||||
<i class="mdi mdi-24px mdi-server mr-1" /> {{ $t('message.editConnection') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<a class="btn btn-clear c-hand" @click="closeModal" />
|
|
||||||
</div>
|
|
||||||
<div class="modal-body pb-0">
|
|
||||||
<div class="content">
|
|
||||||
<form class="form-horizontal">
|
|
||||||
<fieldset class="m-0" :disabled="isTesting">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.connectionName') }}:</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<input
|
|
||||||
v-model="localConnection.name"
|
|
||||||
class="form-input"
|
|
||||||
type="text"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.client') }}:</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<select v-model="localConnection.client" class="form-select">
|
|
||||||
<option value="mysql">
|
|
||||||
MySQL
|
|
||||||
</option>
|
|
||||||
<option value="maria">
|
|
||||||
MariaDB
|
|
||||||
</option>
|
|
||||||
<!-- <option value="mssql">
|
|
||||||
Microsoft SQL
|
|
||||||
</option>
|
|
||||||
<option value="pg">
|
|
||||||
PostgreSQL
|
|
||||||
</option>
|
|
||||||
<option value="oracledb">
|
|
||||||
Oracle DB
|
|
||||||
</option> -->
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.hostName') }}/IP:</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<input
|
|
||||||
v-model="localConnection.host"
|
|
||||||
class="form-input"
|
|
||||||
type="text"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.port') }}:</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<input
|
|
||||||
v-model="localConnection.port"
|
|
||||||
class="form-input"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
max="65535"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.user') }}:</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<input
|
|
||||||
v-model="localConnection.user"
|
|
||||||
class="form-input"
|
|
||||||
type="text"
|
|
||||||
:disabled="localConnection.ask"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.password') }}:</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<input
|
|
||||||
v-model="localConnection.password"
|
|
||||||
class="form-input"
|
|
||||||
type="password"
|
|
||||||
:disabled="localConnection.ask"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12" />
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<label class="form-checkbox form-inline">
|
|
||||||
<input v-model="localConnection.ask" type="checkbox"><i class="form-icon" /> {{ $t('message.askCredentials') }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<BaseToast
|
|
||||||
class="mb-2"
|
|
||||||
:message="toast.message"
|
|
||||||
:status="toast.status"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer text-light">
|
|
||||||
<button
|
|
||||||
class="btn btn-gray mr-2"
|
|
||||||
:class="{'loading': isTesting}"
|
|
||||||
@click="startTest"
|
|
||||||
>
|
|
||||||
{{ $t('message.testConnection') }}
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-primary mr-2" @click="saveEditConnection">
|
|
||||||
{{ $t('word.save') }}
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-link" @click="closeModal">
|
|
||||||
{{ $t('word.close') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ModalAskCredentials
|
|
||||||
v-if="isAsking"
|
|
||||||
@close-asking="closeAsking"
|
|
||||||
@credentials="continueTest"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapActions, mapGetters } from 'vuex';
|
|
||||||
import Connection from '@/ipc-api/Connection';
|
|
||||||
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
|
||||||
import BaseToast from '@/components/BaseToast';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ModalEditConnection',
|
|
||||||
components: {
|
|
||||||
ModalAskCredentials,
|
|
||||||
BaseToast
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
toast: {
|
|
||||||
status: '',
|
|
||||||
message: ''
|
|
||||||
},
|
|
||||||
isTesting: false,
|
|
||||||
isAsking: false,
|
|
||||||
localConnection: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapGetters({
|
|
||||||
connection: 'application/getSelectedConnection'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
created () {
|
|
||||||
this.localConnection = Object.assign({}, this.connection);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions({
|
|
||||||
closeModal: 'application/hideEditConnModal',
|
|
||||||
editConnection: 'connections/editConnection'
|
|
||||||
}),
|
|
||||||
async startTest () {
|
|
||||||
this.isTesting = true;
|
|
||||||
this.toast = {
|
|
||||||
status: '',
|
|
||||||
message: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.localConnection.ask)
|
|
||||||
this.isAsking = true;
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
const res = await Connection.makeTest(this.localConnection);
|
|
||||||
if (res.status === 'error')
|
|
||||||
this.toast = { status: 'error', message: res.response.message };
|
|
||||||
else
|
|
||||||
this.toast = { status: 'success', message: this.$t('message.connectionSuccessfullyMade') };
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
this.toast = { status: 'error', message: err.stack };
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isTesting = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async continueTest (credentials) { // if "Ask for credentials" is true
|
|
||||||
this.isAsking = false;
|
|
||||||
const params = Object.assign({}, this.localConnection, credentials);
|
|
||||||
try {
|
|
||||||
const res = await Connection.makeTest(params);
|
|
||||||
if (res.status === 'error')
|
|
||||||
this.toast = { status: 'error', message: res.response.message };
|
|
||||||
else
|
|
||||||
this.toast = { status: 'success', message: this.$t('message.connectionSuccessfullyMade') };
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
this.toast = { status: 'error', message: err.stack };
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isTesting = false;
|
|
||||||
},
|
|
||||||
saveEditConnection () {
|
|
||||||
this.editConnection(this.localConnection);
|
|
||||||
this.closeModal();
|
|
||||||
},
|
|
||||||
closeAsking () {
|
|
||||||
this.isTesting = false;
|
|
||||||
this.isAsking = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.modal-container {
|
|
||||||
max-width: 450px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -5,7 +5,8 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-database-edit mr-1" /> {{ $t('message.editDatabase') }}
|
<i class="mdi mdi-24px mdi-database-edit mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.editSchema') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
@@ -15,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.name') }}:</label>
|
<label class="form-label">{{ $t('word.name') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<input
|
<input
|
||||||
@@ -23,17 +24,21 @@
|
|||||||
class="form-input"
|
class="form-input"
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
:placeholder="$t('message.databaseName')"
|
: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">
|
||||||
<select v-model="database.collation" class="form-select">
|
<select
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="database.collation"
|
||||||
|
class="form-select"
|
||||||
|
>
|
||||||
<option
|
<option
|
||||||
v-for="collation in collations"
|
v-for="collation in collations"
|
||||||
:key="collation.id"
|
:key="collation.id"
|
||||||
@@ -48,8 +53,8 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer text-light">
|
<div class="modal-footer">
|
||||||
<button class="btn btn-primary mr-2" @click.stop="updateDatabase">
|
<button class="btn btn-primary mr-2" @click.stop="updateSchema">
|
||||||
{{ $t('word.update') }}
|
{{ $t('word.update') }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-link" @click.stop="closeModal">
|
<button class="btn btn-link" @click.stop="closeModal">
|
||||||
@@ -62,12 +67,12 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import Database from '@/ipc-api/Database';
|
import Schema from '@/ipc-api/Schema';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ModalEditDatabase',
|
name: 'ModalEditSchema',
|
||||||
props: {
|
props: {
|
||||||
selectedDatabase: String
|
selectedSchema: String
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -94,7 +99,7 @@ export default {
|
|||||||
async created () {
|
async created () {
|
||||||
let actualCollation;
|
let actualCollation;
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Database.getDatabaseCollation({ uid: this.selectedWorkspace, database: this.selectedDatabase });
|
const { status, response } = await Schema.getDatabaseCollation({ uid: this.selectedWorkspace, database: this.selectedSchema });
|
||||||
|
|
||||||
if (status === 'success')
|
if (status === 'success')
|
||||||
actualCollation = response;
|
actualCollation = response;
|
||||||
@@ -107,20 +112,29 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.database = {
|
this.database = {
|
||||||
name: this.selectedDatabase,
|
name: this.selectedSchema,
|
||||||
prevName: this.selectedDatabase,
|
prevName: this.selectedSchema,
|
||||||
collation: actualCollation || this.defaultCollation,
|
collation: actualCollation || this.defaultCollation,
|
||||||
prevCollation: actualCollation || this.defaultCollation
|
prevCollation: actualCollation || this.defaultCollation
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification'
|
addNotification: 'notifications/addNotification'
|
||||||
}),
|
}),
|
||||||
async updateDatabase () {
|
async updateSchema () {
|
||||||
if (this.database.collation !== this.database.prevCollation) {
|
if (this.database.collation !== this.database.prevCollation) {
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Database.updateDatabase({
|
const { status, response } = await Schema.updateSchema({
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
...this.database
|
...this.database
|
||||||
});
|
});
|
||||||
@@ -139,6 +153,11 @@ export default {
|
|||||||
},
|
},
|
||||||
closeModal () {
|
closeModal () {
|
||||||
this.$emit('close');
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape')
|
||||||
|
this.closeModal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
400
src/renderer/components/ModalFakerRows.vue
Normal file
@@ -0,0 +1,400 @@
|
|||||||
|
<template>
|
||||||
|
<div class="modal active">
|
||||||
|
<a class="modal-overlay" @click.stop="closeModal" />
|
||||||
|
<div class="modal-container p-0">
|
||||||
|
<div class="modal-header pl-2">
|
||||||
|
<div class="modal-title h6">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-playlist-plus mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.tableFiller') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
|
</div>
|
||||||
|
<div class="modal-body pb-0">
|
||||||
|
<div class="content">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<fieldset :disabled="isInserting">
|
||||||
|
<div
|
||||||
|
v-for="field in fields"
|
||||||
|
:key="field.name"
|
||||||
|
class="form-group"
|
||||||
|
>
|
||||||
|
<div class="col-3 col-sm-12">
|
||||||
|
<label class="form-label" :title="field.name">{{ field.name }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column columns col-sm-12">
|
||||||
|
<FakerSelect
|
||||||
|
:type="field.type"
|
||||||
|
class="column columns pr-0"
|
||||||
|
:is-checked="!fieldsToExclude.includes(field.name)"
|
||||||
|
:foreign-keys="foreignKeys"
|
||||||
|
:key-usage="keyUsage"
|
||||||
|
:field="field"
|
||||||
|
:field-length="fieldLength(field)"
|
||||||
|
:field-obj="localRow[field.name]"
|
||||||
|
:value.sync="localRow[field.name]"
|
||||||
|
>
|
||||||
|
<span class="input-group-addon field-type" :class="typeClass(field.type)">
|
||||||
|
{{ field.type }} {{ fieldLength(field) | wrapNumber }}
|
||||||
|
</span>
|
||||||
|
<label class="form-checkbox ml-3" :title="$t('word.insert')">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
:checked="!field.autoIncrement"
|
||||||
|
@change.prevent="toggleFields($event, field)"
|
||||||
|
><i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</FakerSelect>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer columns">
|
||||||
|
<div class="column d-flex" :class="hasFakes ? 'col-4' : 'col-2'">
|
||||||
|
<div class="input-group tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')">
|
||||||
|
<input
|
||||||
|
v-model="nInserts"
|
||||||
|
type="number"
|
||||||
|
class="form-input"
|
||||||
|
min="1"
|
||||||
|
:disabled="isInserting"
|
||||||
|
>
|
||||||
|
<span class="input-group-addon">
|
||||||
|
<i class="mdi mdi-24px mdi-repeat" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="hasFakes"
|
||||||
|
class="tooltip tooltip-right ml-2"
|
||||||
|
:data-tooltip="$t('message.fakeDataLanguage')"
|
||||||
|
>
|
||||||
|
<select v-model="fakerLocale" class="form-select">
|
||||||
|
<option value="ar">
|
||||||
|
Arabic
|
||||||
|
</option><option value="az">
|
||||||
|
Azerbaijani
|
||||||
|
</option><option value="zh_CN">
|
||||||
|
Chinese
|
||||||
|
</option><option value="zh_TW">
|
||||||
|
Chinese (Taiwan)
|
||||||
|
</option><option value="cz">
|
||||||
|
Czech
|
||||||
|
</option><option value="nl">
|
||||||
|
Dutch
|
||||||
|
</option><option value="nl_BE">
|
||||||
|
Dutch (Belgium)
|
||||||
|
</option><option value="en">
|
||||||
|
English
|
||||||
|
</option><option value="en_AU_ocker">
|
||||||
|
English (Australia Ocker)
|
||||||
|
</option><option value="en_AU">
|
||||||
|
English (Australia)
|
||||||
|
</option><option value="en_BORK">
|
||||||
|
English (Bork)
|
||||||
|
</option><option value="en_CA">
|
||||||
|
English (Canada)
|
||||||
|
</option><option value="en_GB">
|
||||||
|
English (Great Britain)
|
||||||
|
</option><option value="en_IND">
|
||||||
|
English (India)
|
||||||
|
</option><option value="en_IE">
|
||||||
|
English (Ireland)
|
||||||
|
</option><option value="en_ZA">
|
||||||
|
English (South Africa)
|
||||||
|
</option><option value="en_US">
|
||||||
|
English (United States)
|
||||||
|
</option><option value="fa">
|
||||||
|
Farsi
|
||||||
|
</option><option value="fi">
|
||||||
|
Finnish
|
||||||
|
</option><option value="fr">
|
||||||
|
French
|
||||||
|
</option><option value="fr_CA">
|
||||||
|
French (Canada)
|
||||||
|
</option><option value="fr_CH">
|
||||||
|
French (Switzerland)
|
||||||
|
</option><option value="ge">
|
||||||
|
Georgian
|
||||||
|
</option><option value="de">
|
||||||
|
German
|
||||||
|
</option><option value="de_AT">
|
||||||
|
German (Austria)
|
||||||
|
</option><option value="de_CH">
|
||||||
|
German (Switzerland)
|
||||||
|
</option><option value="hr">
|
||||||
|
Hrvatski
|
||||||
|
</option><option value="id_ID">
|
||||||
|
Indonesia
|
||||||
|
</option><option value="it">
|
||||||
|
Italian
|
||||||
|
</option><option value="ja">
|
||||||
|
Japanese
|
||||||
|
</option><option value="ko">
|
||||||
|
Korean
|
||||||
|
</option><option value="nep">
|
||||||
|
Nepalese
|
||||||
|
</option><option value="nb_NO">
|
||||||
|
Norwegian
|
||||||
|
</option><option value="pl">
|
||||||
|
Polish
|
||||||
|
</option><option value="pt_BR">
|
||||||
|
Portuguese (Brazil)
|
||||||
|
</option><option value="pt_PT">
|
||||||
|
Portuguese (Portugal)
|
||||||
|
</option><option value="ro">
|
||||||
|
Romanian
|
||||||
|
</option><option value="ru">
|
||||||
|
Russian
|
||||||
|
</option><option value="sk">
|
||||||
|
Slovakian
|
||||||
|
</option><option value="es">
|
||||||
|
Spanish
|
||||||
|
</option><option value="es_MX">
|
||||||
|
Spanish (Mexico)
|
||||||
|
</option><option value="sv">
|
||||||
|
Swedish
|
||||||
|
</option><option value="tr">
|
||||||
|
Turkish
|
||||||
|
</option><option value="uk">
|
||||||
|
Ukrainian
|
||||||
|
</option><option value="vi">
|
||||||
|
Vietnamese
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-auto">
|
||||||
|
<button
|
||||||
|
class="btn btn-primary mr-2"
|
||||||
|
:class="{'loading': isInserting}"
|
||||||
|
@click.stop="insertRows"
|
||||||
|
>
|
||||||
|
{{ $t('word.insert') }}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-link" @click.stop="closeModal">
|
||||||
|
{{ $t('word.close') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import moment from 'moment';
|
||||||
|
import { TEXT, LONG_TEXT, NUMBER, FLOAT, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import Tables from '@/ipc-api/Tables';
|
||||||
|
import FakerSelect from '@/components/FakerSelect';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalFakerRows',
|
||||||
|
components: {
|
||||||
|
FakerSelect
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
wrapNumber (num) {
|
||||||
|
if (!num) return '';
|
||||||
|
return `(${num})`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
tabUid: [String, Number],
|
||||||
|
fields: Array,
|
||||||
|
keyUsage: Array
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
localRow: {},
|
||||||
|
fieldsToExclude: [],
|
||||||
|
nInserts: 1,
|
||||||
|
isInserting: false,
|
||||||
|
fakerLocale: 'en'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
|
getWorkspace: 'workspaces/getWorkspace',
|
||||||
|
getWorkspaceTab: 'workspaces/getWorkspaceTab'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.selectedWorkspace);
|
||||||
|
},
|
||||||
|
foreignKeys () {
|
||||||
|
return this.keyUsage.map(key => key.field);
|
||||||
|
},
|
||||||
|
hasFakes () {
|
||||||
|
return Object.keys(this.localRow).some(field => 'group' in this.localRow[field] && this.localRow[field].group !== 'manual');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
nInserts (val) {
|
||||||
|
if (!val || val < 1)
|
||||||
|
this.nInserts = 1;
|
||||||
|
else if (val > 1000)
|
||||||
|
this.nInserts = 1000;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
const rowObj = {};
|
||||||
|
|
||||||
|
for (const field of this.fields) {
|
||||||
|
let fieldDefault;
|
||||||
|
|
||||||
|
if (field.default === 'NULL') fieldDefault = null;
|
||||||
|
else {
|
||||||
|
if ([...NUMBER, ...FLOAT].includes(field.type))
|
||||||
|
fieldDefault = !field.default || Number.isNaN(+field.default.replaceAll('\'', '')) ? null : +field.default.replaceAll('\'', '');
|
||||||
|
else if ([...TEXT, ...LONG_TEXT].includes(field.type)) {
|
||||||
|
fieldDefault = field.default
|
||||||
|
? field.default.includes('\'')
|
||||||
|
? field.default.split('\'')[1]
|
||||||
|
: 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()'].includes(field.default.toLowerCase())) {
|
||||||
|
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
|
||||||
|
fieldDefault = field.default;
|
||||||
|
}
|
||||||
|
|
||||||
|
rowObj[field.name] = { value: fieldDefault };
|
||||||
|
|
||||||
|
if (field.autoIncrement)// Disable by default auto increment fields
|
||||||
|
this.fieldsToExclude = [...this.fieldsToExclude, field.name];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.localRow = { ...rowObj };
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification'
|
||||||
|
}),
|
||||||
|
typeClass (type) {
|
||||||
|
if (type)
|
||||||
|
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
async insertRows () {
|
||||||
|
this.isInserting = true;
|
||||||
|
const rowToInsert = this.localRow;
|
||||||
|
|
||||||
|
Object.keys(rowToInsert).forEach(key => {
|
||||||
|
if (this.fieldsToExclude.includes(key))
|
||||||
|
delete rowToInsert[key];
|
||||||
|
|
||||||
|
if (typeof rowToInsert[key] === 'undefined')
|
||||||
|
delete rowToInsert[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
const fieldTypes = {};
|
||||||
|
this.fields.forEach(field => {
|
||||||
|
fieldTypes[field.name] = field.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Tables.insertTableFakeRows({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
schema: this.workspace.breadcrumbs.schema,
|
||||||
|
table: this.workspace.breadcrumbs.table,
|
||||||
|
row: rowToInsert,
|
||||||
|
repeat: this.nInserts,
|
||||||
|
fields: fieldTypes,
|
||||||
|
locale: this.fakerLocale
|
||||||
|
});
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
this.closeModal();
|
||||||
|
this.$emit('reload');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isInserting = false;
|
||||||
|
},
|
||||||
|
closeModal () {
|
||||||
|
this.$emit('hide');
|
||||||
|
},
|
||||||
|
fieldLength (field) {
|
||||||
|
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
||||||
|
else if (TEXT.includes(field.type)) return field.charLength;
|
||||||
|
return field.length;
|
||||||
|
},
|
||||||
|
toggleFields (event, field) {
|
||||||
|
if (event.target.checked)
|
||||||
|
this.fieldsToExclude = this.fieldsToExclude.filter(f => f !== field.name);
|
||||||
|
else
|
||||||
|
this.fieldsToExclude = [...this.fieldsToExclude, field.name];
|
||||||
|
},
|
||||||
|
filesChange (event, field) {
|
||||||
|
const { files } = event.target;
|
||||||
|
if (!files.length) return;
|
||||||
|
|
||||||
|
this.localRow[field] = files[0].path;
|
||||||
|
},
|
||||||
|
getKeyUsage (keyName) {
|
||||||
|
return this.keyUsage.find(key => key.field === keyName);
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape')
|
||||||
|
this.closeModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.modal-container {
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: normal;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group-addon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-type {
|
||||||
|
font-size: 0.6rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="modal active">
|
|
||||||
<a class="modal-overlay c-hand" @click="closeModal" />
|
|
||||||
<div class="modal-container">
|
|
||||||
<div class="modal-header pl-2">
|
|
||||||
<div class="modal-title h6">
|
|
||||||
<div class="d-flex">
|
|
||||||
<i class="mdi mdi-24px mdi-server-plus mr-1" /> {{ $t('message.createNewConnection') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<a class="btn btn-clear c-hand" @click="closeModal" />
|
|
||||||
</div>
|
|
||||||
<div class="modal-body pb-0">
|
|
||||||
<div class="content">
|
|
||||||
<form class="form-horizontal">
|
|
||||||
<fieldset class="m-0" :disabled="isTesting">
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.connectionName') }}:</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<input
|
|
||||||
v-model="connection.name"
|
|
||||||
class="form-input"
|
|
||||||
type="text"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.client') }}:</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<select
|
|
||||||
v-model="connection.client"
|
|
||||||
class="form-select"
|
|
||||||
@change="setDefaults"
|
|
||||||
>
|
|
||||||
<option value="mysql">
|
|
||||||
MySQL
|
|
||||||
</option>
|
|
||||||
<option value="maria">
|
|
||||||
MariaDB
|
|
||||||
</option>
|
|
||||||
<!-- <option value="mssql">
|
|
||||||
Microsoft SQL
|
|
||||||
</option>
|
|
||||||
<option value="pg">
|
|
||||||
PostgreSQL
|
|
||||||
</option>
|
|
||||||
<option value="oracledb">
|
|
||||||
Oracle DB
|
|
||||||
</option> -->
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.hostName') }}/IP:</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<input
|
|
||||||
v-model="connection.host"
|
|
||||||
class="form-input"
|
|
||||||
type="text"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.port') }}:</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<input
|
|
||||||
v-model="connection.port"
|
|
||||||
class="form-input"
|
|
||||||
type="number"
|
|
||||||
min="1"
|
|
||||||
max="65535"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.user') }}:</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<input
|
|
||||||
v-model="connection.user"
|
|
||||||
class="form-input"
|
|
||||||
type="text"
|
|
||||||
:disabled="connection.ask"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12">
|
|
||||||
<label class="form-label">{{ $t('word.password') }}:</label>
|
|
||||||
</div>
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<input
|
|
||||||
v-model="connection.password"
|
|
||||||
class="form-input"
|
|
||||||
type="password"
|
|
||||||
:disabled="connection.ask"
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-4 col-sm-12" />
|
|
||||||
<div class="col-8 col-sm-12">
|
|
||||||
<label class="form-checkbox form-inline">
|
|
||||||
<input v-model="connection.ask" type="checkbox"><i class="form-icon" /> {{ $t('message.askCredentials') }}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<BaseToast
|
|
||||||
class="mb-2"
|
|
||||||
:message="toast.message"
|
|
||||||
:status="toast.status"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer text-light">
|
|
||||||
<button
|
|
||||||
class="btn btn-gray mr-2"
|
|
||||||
:class="{'loading': isTesting}"
|
|
||||||
@click="startTest"
|
|
||||||
>
|
|
||||||
{{ $t('message.testConnection') }}
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-primary mr-2" @click="saveNewConnection">
|
|
||||||
{{ $t('word.save') }}
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-link" @click="closeModal">
|
|
||||||
{{ $t('word.close') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ModalAskCredentials
|
|
||||||
v-if="isAsking"
|
|
||||||
@close-asking="closeAsking"
|
|
||||||
@credentials="continueTest"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapActions } from 'vuex';
|
|
||||||
import Connection from '@/ipc-api/Connection';
|
|
||||||
import { uidGen } from 'common/libs/uidGen';
|
|
||||||
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
|
||||||
import BaseToast from '@/components/BaseToast';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ModalNewConnection',
|
|
||||||
components: {
|
|
||||||
ModalAskCredentials,
|
|
||||||
BaseToast
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
connection: {
|
|
||||||
name: '',
|
|
||||||
client: 'mysql',
|
|
||||||
host: '127.0.0.1',
|
|
||||||
port: '3306',
|
|
||||||
user: 'root',
|
|
||||||
password: '',
|
|
||||||
ask: false,
|
|
||||||
uid: uidGen('C')
|
|
||||||
},
|
|
||||||
toast: {
|
|
||||||
status: '',
|
|
||||||
message: ''
|
|
||||||
},
|
|
||||||
isTesting: false,
|
|
||||||
isAsking: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions({
|
|
||||||
closeModal: 'application/hideNewConnModal',
|
|
||||||
addConnection: 'connections/addConnection'
|
|
||||||
}),
|
|
||||||
setDefaults () {
|
|
||||||
switch (this.connection.client) {
|
|
||||||
case 'mysql':
|
|
||||||
this.connection.port = '3306';
|
|
||||||
break;
|
|
||||||
case 'mssql':
|
|
||||||
this.connection.port = '1433';
|
|
||||||
break;
|
|
||||||
case 'pg':
|
|
||||||
this.connection.port = '5432';
|
|
||||||
break;
|
|
||||||
case 'oracledb':
|
|
||||||
this.connection.port = '1521';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async startTest () {
|
|
||||||
this.isTesting = true;
|
|
||||||
this.toast = {
|
|
||||||
status: '',
|
|
||||||
message: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.connection.ask)
|
|
||||||
this.isAsking = true;
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
const res = await Connection.makeTest(this.connection);
|
|
||||||
if (res.status === 'error')
|
|
||||||
this.toast = { status: 'error', message: res.response.message };
|
|
||||||
else
|
|
||||||
this.toast = { status: 'success', message: this.$t('message.connectionSuccessfullyMade') };
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
this.toast = { status: 'error', message: err.stack };
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isTesting = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async continueTest (credentials) { // if "Ask for credentials" is true
|
|
||||||
this.isAsking = false;
|
|
||||||
const params = Object.assign({}, this.connection, credentials);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await Connection.makeTest(params);
|
|
||||||
if (res.status === 'error')
|
|
||||||
this.toast = { status: 'error', message: res.response.message };
|
|
||||||
else
|
|
||||||
this.toast = { status: 'success', message: this.$t('message.connectionSuccessfullyMade') };
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
this.toast = { status: 'error', message: err.stack };
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isTesting = false;
|
|
||||||
},
|
|
||||||
saveNewConnection () {
|
|
||||||
this.addConnection(this.connection);
|
|
||||||
this.closeModal();
|
|
||||||
},
|
|
||||||
closeAsking () {
|
|
||||||
this.isAsking = false;
|
|
||||||
this.isTesting = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.modal-container {
|
|
||||||
max-width: 450px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -5,7 +5,8 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-database-plus mr-1" /> {{ $t('message.createNewDatabase') }}
|
<i class="mdi mdi-24px mdi-database-plus mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.createNewSchema') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
@@ -15,21 +16,22 @@
|
|||||||
<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
|
||||||
|
ref="firstInput"
|
||||||
v-model="database.name"
|
v-model="database.name"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
type="text"
|
type="text"
|
||||||
required
|
required
|
||||||
:placeholder="$t('message.databaseName')"
|
:placeholder="$t('message.schemaName')"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.collations" class="form-group">
|
||||||
<div class="col-3">
|
<div class="col-3">
|
||||||
<label class="form-label">{{ $t('word.collation') }}:</label>
|
<label class="form-label">{{ $t('word.collation') }}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-9">
|
<div class="col-9">
|
||||||
<select v-model="database.collation" class="form-select">
|
<select v-model="database.collation" class="form-select">
|
||||||
@@ -47,8 +49,12 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer text-light">
|
<div class="modal-footer">
|
||||||
<button class="btn btn-primary mr-2" @click.stop="createDatabase">
|
<button
|
||||||
|
class="btn btn-primary mr-2"
|
||||||
|
:class="{'loading': isLoading}"
|
||||||
|
@click.stop="createSchema"
|
||||||
|
>
|
||||||
{{ $t('word.add') }}
|
{{ $t('word.add') }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-link" @click.stop="closeModal">
|
<button class="btn btn-link" @click.stop="closeModal">
|
||||||
@@ -61,12 +67,13 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import Database from '@/ipc-api/Database';
|
import Schema from '@/ipc-api/Schema';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ModalNewDatabase',
|
name: 'ModalNewSchema',
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
isLoading: false,
|
||||||
database: {
|
database: {
|
||||||
name: '',
|
name: '',
|
||||||
collation: ''
|
collation: ''
|
||||||
@@ -82,20 +89,31 @@ export default {
|
|||||||
collations () {
|
collations () {
|
||||||
return this.getWorkspace(this.selectedWorkspace).collations;
|
return this.getWorkspace(this.selectedWorkspace).collations;
|
||||||
},
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.getWorkspace(this.selectedWorkspace).customizations;
|
||||||
|
},
|
||||||
defaultCollation () {
|
defaultCollation () {
|
||||||
return this.getDatabaseVariable(this.selectedWorkspace, 'collation_server').value || '';
|
return this.getDatabaseVariable(this.selectedWorkspace, 'collation_server') ? this.getDatabaseVariable(this.selectedWorkspace, 'collation_server').value : '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.database = { ...this.database, collation: this.defaultCollation };
|
this.database = { ...this.database, collation: this.defaultCollation };
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification'
|
addNotification: 'notifications/addNotification'
|
||||||
}),
|
}),
|
||||||
async createDatabase () {
|
async createSchema () {
|
||||||
|
this.isLoading = true;
|
||||||
try {
|
try {
|
||||||
const { status, response } = await Database.createDatabase({
|
const { status, response } = await Schema.createSchema({
|
||||||
uid: this.selectedWorkspace,
|
uid: this.selectedWorkspace,
|
||||||
...this.database
|
...this.database
|
||||||
});
|
});
|
||||||
@@ -110,9 +128,15 @@ export default {
|
|||||||
catch (err) {
|
catch (err) {
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
}
|
}
|
||||||
|
this.isLoading = false;
|
||||||
},
|
},
|
||||||
closeModal () {
|
closeModal () {
|
||||||
this.$emit('close');
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape')
|
||||||
|
this.closeModal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -5,7 +5,8 @@
|
|||||||
<div class="modal-header pl-2">
|
<div class="modal-header pl-2">
|
||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-playlist-plus mr-1" /> {{ $t('message.addNewRow') }}
|
<i class="mdi mdi-24px mdi-playlist-plus mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.addNewRow') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
<div class="input-group col-8 col-sm-12">
|
<div class="input-group col-8 col-sm-12">
|
||||||
<ForeignKeySelect
|
<ForeignKeySelect
|
||||||
v-if="foreignKeys.includes(field.name)"
|
v-if="foreignKeys.includes(field.name)"
|
||||||
|
ref="formInput"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
:value.sync="localRow[field.name]"
|
:value.sync="localRow[field.name]"
|
||||||
:key-usage="getKeyUsage(field.name)"
|
:key-usage="getKeyUsage(field.name)"
|
||||||
@@ -32,6 +34,7 @@
|
|||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
v-else-if="inputProps(field).mask"
|
v-else-if="inputProps(field).mask"
|
||||||
|
ref="formInput"
|
||||||
v-model="localRow[field.name]"
|
v-model="localRow[field.name]"
|
||||||
v-mask="inputProps(field).mask"
|
v-mask="inputProps(field).mask"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
@@ -41,21 +44,33 @@
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
v-else-if="inputProps(field).type === 'file'"
|
v-else-if="inputProps(field).type === 'file'"
|
||||||
|
ref="formInput"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
type="file"
|
type="file"
|
||||||
:disabled="fieldsToExclude.includes(field.name)"
|
:disabled="fieldsToExclude.includes(field.name)"
|
||||||
:tabindex="key+1"
|
:tabindex="key+1"
|
||||||
@change="filesChange($event,field.name)"
|
@change="filesChange($event,field.name)"
|
||||||
>
|
>
|
||||||
|
<input
|
||||||
|
v-else-if="inputProps(field).type === 'number'"
|
||||||
|
ref="formInput"
|
||||||
|
v-model="localRow[field.name]"
|
||||||
|
class="form-input"
|
||||||
|
step="any"
|
||||||
|
:type="inputProps(field).type"
|
||||||
|
:disabled="fieldsToExclude.includes(field.name)"
|
||||||
|
:tabindex="key+1"
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
v-else
|
v-else
|
||||||
|
ref="formInput"
|
||||||
v-model="localRow[field.name]"
|
v-model="localRow[field.name]"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
:type="inputProps(field).type"
|
:type="inputProps(field).type"
|
||||||
:disabled="fieldsToExclude.includes(field.name)"
|
:disabled="fieldsToExclude.includes(field.name)"
|
||||||
:tabindex="key+1"
|
:tabindex="key+1"
|
||||||
>
|
>
|
||||||
<span class="input-group-addon" :class="`type-${field.type}`">
|
<span class="input-group-addon" :class="typeCLass(field.type)">
|
||||||
{{ field.type }} {{ fieldLength(field) | wrapNumber }}
|
{{ field.type }} {{ fieldLength(field) | wrapNumber }}
|
||||||
</span>
|
</span>
|
||||||
<label class="form-checkbox ml-3" :title="$t('word.insert')">
|
<label class="form-checkbox ml-3" :title="$t('word.insert')">
|
||||||
@@ -71,7 +86,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer text-light">
|
<div class="modal-footer">
|
||||||
<div class="input-group col-3 tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')">
|
<div class="input-group col-3 tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')">
|
||||||
<input
|
<input
|
||||||
v-model="nInserts"
|
v-model="nInserts"
|
||||||
@@ -103,8 +118,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { TEXT, LONG_TEXT, NUMBER, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
|
import { TEXT, LONG_TEXT, NUMBER, FLOAT, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
|
||||||
import { mask } from 'vue-the-mask';
|
import { VueMaskDirective } from 'v-mask';
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import Tables from '@/ipc-api/Tables';
|
import Tables from '@/ipc-api/Tables';
|
||||||
import ForeignKeySelect from '@/components/ForeignKeySelect';
|
import ForeignKeySelect from '@/components/ForeignKeySelect';
|
||||||
@@ -115,7 +130,7 @@ export default {
|
|||||||
ForeignKeySelect
|
ForeignKeySelect
|
||||||
},
|
},
|
||||||
directives: {
|
directives: {
|
||||||
mask
|
mask: VueMaskDirective
|
||||||
},
|
},
|
||||||
filters: {
|
filters: {
|
||||||
wrapNumber (num) {
|
wrapNumber (num) {
|
||||||
@@ -124,8 +139,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object,
|
tabUid: [String, Number],
|
||||||
tabUid: [String, Number]
|
fields: Array,
|
||||||
|
keyUsage: Array
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -145,21 +161,20 @@ export default {
|
|||||||
return this.getWorkspace(this.selectedWorkspace);
|
return this.getWorkspace(this.selectedWorkspace);
|
||||||
},
|
},
|
||||||
foreignKeys () {
|
foreignKeys () {
|
||||||
return this.keyUsage.map(key => key.column);
|
return this.keyUsage.map(key => key.field);
|
||||||
},
|
|
||||||
fields () {
|
|
||||||
return this.getWorkspaceTab(this.tabUid) ? this.getWorkspaceTab(this.tabUid).fields[0] : [];
|
|
||||||
},
|
|
||||||
keyUsage () {
|
|
||||||
return this.getWorkspaceTab(this.tabUid) ? this.getWorkspaceTab(this.tabUid).keyUsage[0] : [];
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
nInserts (val) {
|
nInserts (val) {
|
||||||
if (!val || val < 1)
|
if (!val || val < 1)
|
||||||
this.nInserts = 1;
|
this.nInserts = 1;
|
||||||
|
else if (val > 1000)
|
||||||
|
this.nInserts = 1000;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
const rowObj = {};
|
const rowObj = {};
|
||||||
|
|
||||||
@@ -168,7 +183,7 @@ export default {
|
|||||||
|
|
||||||
if (field.default === 'NULL') fieldDefault = null;
|
if (field.default === 'NULL') fieldDefault = null;
|
||||||
else {
|
else {
|
||||||
if (NUMBER.includes(field.type))
|
if ([...NUMBER, ...FLOAT].includes(field.type))
|
||||||
fieldDefault = +field.default;
|
fieldDefault = +field.default;
|
||||||
|
|
||||||
if ([...TEXT, ...LONG_TEXT].includes(field.type))
|
if ([...TEXT, ...LONG_TEXT].includes(field.type))
|
||||||
@@ -194,11 +209,25 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.localRow = { ...rowObj };
|
this.localRow = { ...rowObj };
|
||||||
|
|
||||||
|
// Auto focus
|
||||||
|
setTimeout(() => {
|
||||||
|
const firstSelectableInput = this.$refs.formInput.find(input => !input.disabled);
|
||||||
|
firstSelectableInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
addNotification: 'notifications/addNotification'
|
addNotification: 'notifications/addNotification'
|
||||||
}),
|
}),
|
||||||
|
typeClass (type) {
|
||||||
|
if (type)
|
||||||
|
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
|
||||||
|
return '';
|
||||||
|
},
|
||||||
async insertRows () {
|
async insertRows () {
|
||||||
this.isInserting = true;
|
this.isInserting = true;
|
||||||
const rowToInsert = this.localRow;
|
const rowToInsert = this.localRow;
|
||||||
@@ -242,13 +271,14 @@ export default {
|
|||||||
},
|
},
|
||||||
fieldLength (field) {
|
fieldLength (field) {
|
||||||
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
|
||||||
return field.numPrecision || field.datePrecision || field.charLength || 0;
|
else if (TEXT.includes(field.type)) return field.charLength;
|
||||||
|
return field.length;
|
||||||
},
|
},
|
||||||
inputProps (field) {
|
inputProps (field) {
|
||||||
if ([...TEXT, ...LONG_TEXT].includes(field.type))
|
if ([...TEXT, ...LONG_TEXT].includes(field.type))
|
||||||
return { type: 'text', mask: false };
|
return { type: 'text', mask: false };
|
||||||
|
|
||||||
if (NUMBER.includes(field.type))
|
if ([...NUMBER, ...FLOAT].includes(field.type))
|
||||||
return { type: 'number', mask: false };
|
return { type: 'number', mask: false };
|
||||||
|
|
||||||
if (TIME.includes(field.type)) {
|
if (TIME.includes(field.type)) {
|
||||||
@@ -294,9 +324,13 @@ export default {
|
|||||||
|
|
||||||
this.localRow[field] = files[0].path;
|
this.localRow[field] = files[0].path;
|
||||||
},
|
},
|
||||||
|
|
||||||
getKeyUsage (keyName) {
|
getKeyUsage (keyName) {
|
||||||
return this.keyUsage.find(key => key.column === keyName);
|
return this.keyUsage.find(key => key.field === keyName);
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape')
|
||||||
|
this.closeModal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
311
src/renderer/components/ModalProcessesList.vue
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
<template>
|
||||||
|
<div class="modal active">
|
||||||
|
<a class="modal-overlay" @click.stop="closeModal" />
|
||||||
|
<div class="modal-container p-0 pb-4">
|
||||||
|
<div class="modal-header pl-2">
|
||||||
|
<div class="modal-title h6">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-memory mr-1" />
|
||||||
|
<span class="cut-text">{{ $t('message.processesList') }}: {{ connectionName }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
|
||||||
|
</div>
|
||||||
|
<div class="processes-toolbar py-2 px-4">
|
||||||
|
<div class="dropdown">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button
|
||||||
|
class="btn btn-dark btn-sm mr-0 pr-1 d-flex"
|
||||||
|
:class="{'loading':isQuering}"
|
||||||
|
title="F5"
|
||||||
|
@click="getProcessesList"
|
||||||
|
>
|
||||||
|
<i v-if="!+autorefreshTimer" class="mdi mdi-24px mdi-refresh mr-1" />
|
||||||
|
<i v-else class="mdi mdi-24px mdi-history mdi-flip-h mr-1" />
|
||||||
|
<span>{{ $t('word.refresh') }}</span>
|
||||||
|
</button>
|
||||||
|
<div class="btn btn-dark btn-sm dropdown-toggle pl-0 pr-0" tabindex="0">
|
||||||
|
<i class="mdi mdi-24px mdi-menu-down" />
|
||||||
|
</div>
|
||||||
|
<div class="menu px-3">
|
||||||
|
<span>{{ $t('word.autoRefresh') }}: <b>{{ +autorefreshTimer ? `${autorefreshTimer}s` : 'OFF' }}</b></span>
|
||||||
|
<input
|
||||||
|
v-model="autorefreshTimer"
|
||||||
|
class="slider no-border"
|
||||||
|
type="range"
|
||||||
|
min="0"
|
||||||
|
max="15"
|
||||||
|
step="0.5"
|
||||||
|
@change="setRefreshInterval"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="workspace-query-info">
|
||||||
|
<div v-if="sortedResults.length">
|
||||||
|
{{ $t('word.processes') }}: <b>{{ sortedResults.length.toLocaleString() }}</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body py-0 workspace-query-results">
|
||||||
|
<div
|
||||||
|
ref="tableWrapper"
|
||||||
|
class="vscroll"
|
||||||
|
:style="{'height': resultsSize+'px'}"
|
||||||
|
>
|
||||||
|
<div ref="table" class="table table-hover">
|
||||||
|
<div class="thead">
|
||||||
|
<div class="tr">
|
||||||
|
<div
|
||||||
|
v-for="(field, index) in fields"
|
||||||
|
:key="index"
|
||||||
|
class="th c-hand"
|
||||||
|
>
|
||||||
|
<div ref="columnResize" class="column-resizable">
|
||||||
|
<div class="table-column-title" @click="sort(field)">
|
||||||
|
<span>{{ field.toUpperCase() }}</span>
|
||||||
|
<i
|
||||||
|
v-if="currentSort === field"
|
||||||
|
class="mdi sort-icon"
|
||||||
|
:class="currentSortDir === 'asc' ? 'mdi-sort-ascending':'mdi-sort-descending'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<BaseVirtualScroll
|
||||||
|
ref="resultTable"
|
||||||
|
:items="sortedResults"
|
||||||
|
:item-height="22"
|
||||||
|
class="tbody"
|
||||||
|
:visible-height="resultsSize"
|
||||||
|
:scroll-element="scrollElement"
|
||||||
|
>
|
||||||
|
<template slot-scope="{ items }">
|
||||||
|
<ModalProcessesListRow
|
||||||
|
v-for="row in items"
|
||||||
|
:key="row._id"
|
||||||
|
class="process-row"
|
||||||
|
:row="row"
|
||||||
|
@contextmenu="contextMenu"
|
||||||
|
@stop-refresh="stopRefresh"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</BaseVirtualScroll>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import Schema from '@/ipc-api/Schema';
|
||||||
|
import BaseVirtualScroll from '@/components/BaseVirtualScroll';
|
||||||
|
import ModalProcessesListRow from '@/components/ModalProcessesListRow';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalProcessesList',
|
||||||
|
components: {
|
||||||
|
BaseVirtualScroll,
|
||||||
|
ModalProcessesListRow
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
connection: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
resultsSize: 1000,
|
||||||
|
isQuering: false,
|
||||||
|
autorefreshTimer: 0,
|
||||||
|
refreshInterval: null,
|
||||||
|
results: [],
|
||||||
|
fields: [],
|
||||||
|
currentSort: '',
|
||||||
|
currentSortDir: 'asc',
|
||||||
|
scrollElement: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
getConnectionName: 'connections/getConnectionName'
|
||||||
|
}),
|
||||||
|
connectionName () {
|
||||||
|
return this.getConnectionName(this.connection.uid);
|
||||||
|
},
|
||||||
|
sortedResults () {
|
||||||
|
if (this.currentSort) {
|
||||||
|
return [...this.results].sort((a, b) => {
|
||||||
|
let modifier = 1;
|
||||||
|
const valA = typeof a[this.currentSort] === 'string' ? a[this.currentSort].toLowerCase() : a[this.currentSort];
|
||||||
|
const valB = typeof b[this.currentSort] === 'string' ? b[this.currentSort].toLowerCase() : b[this.currentSort];
|
||||||
|
if (this.currentSortDir === 'desc') modifier = -1;
|
||||||
|
if (valA < valB) return -1 * modifier;
|
||||||
|
if (valA > valB) return 1 * modifier;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return this.results;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
window.addEventListener('keydown', this.onKey, { capture: true });
|
||||||
|
},
|
||||||
|
updated () {
|
||||||
|
if (this.$refs.table)
|
||||||
|
this.refreshScroller();
|
||||||
|
|
||||||
|
if (this.$refs.tableWrapper)
|
||||||
|
this.scrollElement = this.$refs.tableWrapper;
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.getProcessesList();
|
||||||
|
window.addEventListener('resize', this.resizeResults);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey, { capture: true });
|
||||||
|
window.removeEventListener('resize', this.resizeResults);
|
||||||
|
clearInterval(this.refreshInterval);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification'
|
||||||
|
}),
|
||||||
|
async getProcessesList () {
|
||||||
|
this.isQuering = true;
|
||||||
|
|
||||||
|
// if table changes clear cached values
|
||||||
|
if (this.lastTable !== this.table)
|
||||||
|
this.results = [];
|
||||||
|
|
||||||
|
try { // Table data
|
||||||
|
const { status, response } = await Schema.getProcesses(this.connection.uid);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
this.results = response;
|
||||||
|
this.fields = response.length ? Object.keys(response[0]) : [];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isQuering = false;
|
||||||
|
},
|
||||||
|
setRefreshInterval () {
|
||||||
|
this.clearRefresh();
|
||||||
|
|
||||||
|
if (+this.autorefreshTimer) {
|
||||||
|
this.refreshInterval = setInterval(() => {
|
||||||
|
if (!this.isQuering)
|
||||||
|
this.getProcessesList();
|
||||||
|
}, this.autorefreshTimer * 1000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clearRefresh () {
|
||||||
|
if (this.refreshInterval)
|
||||||
|
clearInterval(this.refreshInterval);
|
||||||
|
},
|
||||||
|
resizeResults () {
|
||||||
|
if (this.$refs.resultTable) {
|
||||||
|
const el = this.$refs.tableWrapper.parentElement;
|
||||||
|
|
||||||
|
if (el) {
|
||||||
|
const size = el.offsetHeight;
|
||||||
|
this.resultsSize = size;
|
||||||
|
}
|
||||||
|
this.$refs.resultTable.updateWindow();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
refreshScroller () {
|
||||||
|
this.resizeResults();
|
||||||
|
},
|
||||||
|
sort (field) {
|
||||||
|
if (field === this.currentSort) {
|
||||||
|
if (this.currentSortDir === 'asc')
|
||||||
|
this.currentSortDir = 'desc';
|
||||||
|
else
|
||||||
|
this.resetSort();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.currentSortDir = 'asc';
|
||||||
|
this.currentSort = field;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetSort () {
|
||||||
|
this.currentSort = '';
|
||||||
|
this.currentSortDir = 'asc';
|
||||||
|
},
|
||||||
|
stopRefresh () {
|
||||||
|
this.autorefreshTimer = 0;
|
||||||
|
this.clearRefresh();
|
||||||
|
},
|
||||||
|
contextMenu () {},
|
||||||
|
closeModal () {
|
||||||
|
this.$emit('close');
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape')
|
||||||
|
this.closeModal();
|
||||||
|
if (e.key === 'F5')
|
||||||
|
this.getProcessesList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.vscroll {
|
||||||
|
height: 1000px;
|
||||||
|
overflow: auto;
|
||||||
|
overflow-anchor: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-resizable {
|
||||||
|
&:hover,
|
||||||
|
&:active {
|
||||||
|
resize: horizontal;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-column-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-icon {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
line-height: 1;
|
||||||
|
margin-left: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-tabs {
|
||||||
|
background: transparent !important;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
.modal-container {
|
||||||
|
max-width: 75vw;
|
||||||
|
margin-top: 10vh;
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
height: 80vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.processes-toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
156
src/renderer/components/ModalProcessesListRow.vue
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
<template>
|
||||||
|
<div class="tr" @click="selectRow($event, row._id)">
|
||||||
|
<div
|
||||||
|
v-for="(col, cKey) in row"
|
||||||
|
v-show="cKey !== '_id'"
|
||||||
|
:key="cKey"
|
||||||
|
class="td p-0"
|
||||||
|
tabindex="0"
|
||||||
|
@contextmenu.prevent="openContext($event, { id: row._id, field: cKey })"
|
||||||
|
>
|
||||||
|
<template v-if="cKey !== '_id'">
|
||||||
|
<span
|
||||||
|
v-if="!isInlineEditor[cKey]"
|
||||||
|
class="cell-content"
|
||||||
|
:class="`${isNull(col)} type-${typeof col === 'number' ? 'int' : 'varchar'}`"
|
||||||
|
@dblclick="dblClick(cKey)"
|
||||||
|
>{{ col | cutText }}</span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<ConfirmModal
|
||||||
|
v-if="isInfoModal"
|
||||||
|
:confirm-text="$t('word.update')"
|
||||||
|
:cancel-text="$t('word.close')"
|
||||||
|
size="medium"
|
||||||
|
:hide-footer="true"
|
||||||
|
@hide="hideInfoModal"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-information-outline mr-1" /> {{ $t('message.processInfo') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<TextEditor
|
||||||
|
:value="row.info || ''"
|
||||||
|
editor-class="textarea-editor"
|
||||||
|
:mode="editorMode"
|
||||||
|
:read-only="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
import TextEditor from '@/components/BaseTextEditor';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalProcessesListRow',
|
||||||
|
components: {
|
||||||
|
ConfirmModal,
|
||||||
|
TextEditor
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
cutText (val) {
|
||||||
|
if (typeof val !== 'string') return val;
|
||||||
|
return val.length > 250 ? `${val.substring(0, 250)}[...]` : val;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
row: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isInlineEditor: {},
|
||||||
|
isInfoModal: false,
|
||||||
|
editorMode: 'sql'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {},
|
||||||
|
watch: {
|
||||||
|
fields () {
|
||||||
|
Object.keys(this.fields).forEach(field => {
|
||||||
|
this.isInlineEditor[field.name] = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isNull (value) {
|
||||||
|
return value === null ? ' is-null' : '';
|
||||||
|
},
|
||||||
|
selectRow (event, row) {
|
||||||
|
this.$emit('select-row', event, row);
|
||||||
|
},
|
||||||
|
openContext (event, payload) {
|
||||||
|
if (this.isEditable) {
|
||||||
|
payload.field = this.fields[payload.field].name;// Ensures field name only
|
||||||
|
this.$emit('contextmenu', event, payload);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hideInfoModal () {
|
||||||
|
this.isInfoModal = false;
|
||||||
|
},
|
||||||
|
dblClick (col) {
|
||||||
|
if (col !== 'info') return;
|
||||||
|
this.$emit('stop-refresh');
|
||||||
|
this.isInfoModal = true;
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
this.isInlineEditor[this.editingField] = false;
|
||||||
|
this.editingField = null;
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.editable-field {
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
line-height: 1;
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-content {
|
||||||
|
display: block;
|
||||||
|
padding: 0 0.2rem;
|
||||||
|
min-height: 0.8rem;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textarea-editor {
|
||||||
|
height: 50vh !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-field-info {
|
||||||
|
margin-top: 0.4rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class="modal-title h6">
|
<div class="modal-title h6">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<i class="mdi mdi-24px mdi-cog mr-1" />
|
<i class="mdi mdi-24px mdi-cog mr-1" />
|
||||||
{{ $t('word.settings') }}
|
<span class="cut-text">{{ $t('word.settings') }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-clear c-hand" @click="closeModal" />
|
<a class="btn btn-clear c-hand" @click="closeModal" />
|
||||||
@@ -16,102 +16,276 @@
|
|||||||
<div class="panel-nav">
|
<div class="panel-nav">
|
||||||
<ul class="tab tab-block">
|
<ul class="tab tab-block">
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'general'}"
|
:class="{'active': selectedTab === 'general'}"
|
||||||
@click="selectTab('general')"
|
@click="selectTab('general')"
|
||||||
>
|
>
|
||||||
<a class="c-hand">{{ $t('word.general') }}</a>
|
<a class="tab-link">{{ $t('word.general') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'themes'}"
|
:class="{'active': selectedTab === 'themes'}"
|
||||||
@click="selectTab('themes')"
|
@click="selectTab('themes')"
|
||||||
>
|
>
|
||||||
<a class="c-hand">{{ $t('word.themes') }}</a>
|
<a class="tab-link">{{ $t('word.themes') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
v-if="updateStatus !== 'disabled'"
|
||||||
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'update'}"
|
:class="{'active': selectedTab === 'update'}"
|
||||||
@click="selectTab('update')"
|
@click="selectTab('update')"
|
||||||
>
|
>
|
||||||
<a class="c-hand" :class="{'badge badge-update': hasUpdates}">{{ $t('word.update') }}</a>
|
<a class="tab-link" :class="{'badge badge-update': hasUpdates}">{{ $t('word.update') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="tab-item"
|
class="tab-item c-hand"
|
||||||
|
:class="{'active': selectedTab === 'changelog'}"
|
||||||
|
@click="selectTab('changelog')"
|
||||||
|
>
|
||||||
|
<a class="tab-link">{{ $t('word.changelog') }}</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="tab-item c-hand"
|
||||||
:class="{'active': selectedTab === 'about'}"
|
:class="{'active': selectedTab === 'about'}"
|
||||||
@click="selectTab('about')"
|
@click="selectTab('about')"
|
||||||
>
|
>
|
||||||
<a class="c-hand">{{ $t('word.about') }}</a>
|
<a class="tab-link">{{ $t('word.about') }}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-show="selectedTab === 'general'" class="panel-body py-4">
|
||||||
<div v-if="selectedTab === 'general'" class="panel-body py-4">
|
<div class="container">
|
||||||
<form class="form-horizontal">
|
<form class="form-horizontal columns">
|
||||||
<div class="col-8 col-sm-12">
|
<div class="column col-12 h6 text-uppercase mb-1">
|
||||||
<div class="form-group mb-4">
|
{{ $t('word.application') }}
|
||||||
<div class="col-6 col-sm-12">
|
</div>
|
||||||
<label class="form-label">
|
<div class="column col-9 col-sm-12 mb-2">
|
||||||
<i class="mdi mdi-18px mdi-translate mr-1" />
|
<div class="form-group">
|
||||||
{{ $t('word.language') }}:
|
<div class="col-7 col-sm-12">
|
||||||
</label>
|
<label class="form-label">
|
||||||
</div>
|
<i class="mdi mdi-18px mdi-translate mr-1" />
|
||||||
<div class="col-6 col-sm-12">
|
{{ $t('word.language') }}
|
||||||
<select
|
</label>
|
||||||
v-model="localLocale"
|
</div>
|
||||||
class="form-select"
|
<div class="col-5 col-sm-12">
|
||||||
@change="changeLocale(localLocale)"
|
<select
|
||||||
>
|
v-model="localLocale"
|
||||||
<option
|
class="form-select"
|
||||||
v-for="(locale, key) in locales"
|
@change="changeLocale(localLocale)"
|
||||||
:key="key"
|
|
||||||
:value="locale.code"
|
|
||||||
>
|
>
|
||||||
{{ locale.name }}
|
<option
|
||||||
</option>
|
v-for="(locale, key) in locales"
|
||||||
</select>
|
:key="key"
|
||||||
|
:value="locale.code"
|
||||||
|
>
|
||||||
|
{{ locale.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-7 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('message.dataTabPageSize') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-5 col-sm-12">
|
||||||
|
<select
|
||||||
|
v-model="localPageSize"
|
||||||
|
class="form-select"
|
||||||
|
@change="changePageSize(+localPageSize)"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="size in pageSizes"
|
||||||
|
:key="size"
|
||||||
|
>
|
||||||
|
{{ size }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<div class="col-7 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('message.restorePreviourSession') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-5 col-sm-12">
|
||||||
|
<label class="form-switch d-inline-block" @click.prevent="toggleRestoreSession">
|
||||||
|
<input type="checkbox" :checked="restoreTabs">
|
||||||
|
<i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-7 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('message.notificationsTimeout') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-5 col-sm-12">
|
||||||
|
<div class="input-group">
|
||||||
|
<input
|
||||||
|
v-model="localTimeout"
|
||||||
|
class="form-input"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
@focusout="checkNotificationsTimeout"
|
||||||
|
>
|
||||||
|
<span class="input-group-addon">{{ $t('word.seconds') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
|
||||||
<div class="col-6 col-sm-12">
|
<div class="column col-12 h6 mt-4 text-uppercase mb-1">
|
||||||
<label class="form-label">
|
{{ $t('word.editor') }}
|
||||||
{{ $t('message.notificationsTimeout') }}:
|
</div>
|
||||||
</label>
|
<div class="column col-9 col-sm-12">
|
||||||
|
<div class="form-group mb-0">
|
||||||
|
<div class="col-7 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('word.autoCompletion') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="col-5 col-sm-12">
|
||||||
|
<label class="form-switch d-inline-block" @click.prevent="toggleAutoComplete">
|
||||||
|
<input type="checkbox" :checked="selectedAutoComplete">
|
||||||
|
<i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 col-sm-12">
|
<div class="form-group mb-0">
|
||||||
<div class="input-group">
|
<div class="col-7 col-sm-12">
|
||||||
<input
|
<label class="form-label">
|
||||||
v-model="localTimeout"
|
{{ $t('message.wrapLongLines') }}
|
||||||
class="form-input"
|
</label>
|
||||||
type="number"
|
</div>
|
||||||
min="1"
|
<div class="col-5 col-sm-12">
|
||||||
@focusout="checkNotificationsTimeout"
|
<label class="form-switch d-inline-block" @click.prevent="toggleLineWrap">
|
||||||
>
|
<input type="checkbox" :checked="selectedLineWrap">
|
||||||
<span class="input-group-addon">{{ $t('word.seconds') }}</span>
|
<i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="selectedTab === 'themes'" class="panel-body py-4">
|
||||||
|
<div class="container">
|
||||||
|
<div class="columns">
|
||||||
|
<div class="column col-12 h6 text-uppercase mb-2">
|
||||||
|
{{ $t('message.applicationTheme') }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="column col-6 c-hand theme-block"
|
||||||
|
:class="{'selected': applicationTheme === 'dark'}"
|
||||||
|
@click="changeApplicationTheme('dark')"
|
||||||
|
>
|
||||||
|
<img :src="require('@/images/dark.png').default" class="img-responsive img-fit-cover s-rounded">
|
||||||
|
<div class="theme-name text-light">
|
||||||
|
<i class="mdi mdi-moon-waning-crescent mdi-48px" />
|
||||||
|
<div class="h6 mt-4">
|
||||||
|
{{ $t('word.dark') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="column col-6 c-hand theme-block"
|
||||||
|
:class="{'selected': applicationTheme === 'light'}"
|
||||||
|
@click="changeApplicationTheme('light')"
|
||||||
|
>
|
||||||
|
<img :src="require('@/images/light.png').default" class="img-responsive img-fit-cover s-rounded">
|
||||||
|
<div class="theme-name text-dark">
|
||||||
|
<i class="mdi mdi-white-balance-sunny mdi-48px" />
|
||||||
|
<div class="h6 mt-4">
|
||||||
|
{{ $t('word.light') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="selectedTab === 'themes'" class="panel-body py-4">
|
<div class="columns mt-4">
|
||||||
<div class="text-center">
|
<div class="column col-12 h6 text-uppercase mb-2 mt-4">
|
||||||
<p>In future releases</p>
|
{{ $t('message.editorTheme') }}
|
||||||
|
</div>
|
||||||
|
<div class="column col-6 h5 mb-4">
|
||||||
|
<select
|
||||||
|
v-model="localEditorTheme"
|
||||||
|
class="form-select"
|
||||||
|
@change="changeEditorTheme(localEditorTheme)"
|
||||||
|
>
|
||||||
|
<optgroup
|
||||||
|
v-for="group in editorThemes"
|
||||||
|
:key="group.group"
|
||||||
|
:label="group.group"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="theme in group.themes"
|
||||||
|
:key="theme.name"
|
||||||
|
:value="theme.code"
|
||||||
|
:selected="editorTheme === theme.code"
|
||||||
|
>
|
||||||
|
{{ theme.name }}
|
||||||
|
</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="column col-6 mb-4">
|
||||||
|
<div class="btn-group btn-group-block">
|
||||||
|
<button
|
||||||
|
class="btn btn-dark cut-text"
|
||||||
|
:class="{'active': editorFontSize === 'small'}"
|
||||||
|
@click="changeEditorFontSize('small')"
|
||||||
|
>
|
||||||
|
{{ $t('word.small') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-dark cut-text"
|
||||||
|
:class="{'active': editorFontSize === 'medium'}"
|
||||||
|
@click="changeEditorFontSize('medium')"
|
||||||
|
>
|
||||||
|
{{ $t('word.medium') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-dark cut-text"
|
||||||
|
:class="{'active': editorFontSize === 'large'}"
|
||||||
|
@click="changeEditorFontSize('large')"
|
||||||
|
>
|
||||||
|
{{ $t('word.large') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="column col-12">
|
||||||
|
<BaseTextEditor
|
||||||
|
:value="exampleQuery"
|
||||||
|
mode="sql"
|
||||||
|
:workspace="workspace"
|
||||||
|
:read-only="true"
|
||||||
|
:height="270"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="selectedTab === 'update'" class="panel-body py-4">
|
<div v-show="selectedTab === 'update'" class="panel-body py-4">
|
||||||
<ModalSettingsUpdate />
|
<ModalSettingsUpdate />
|
||||||
</div>
|
</div>
|
||||||
|
<div v-show="selectedTab === 'changelog'" class="panel-body py-4">
|
||||||
|
<ModalSettingsChangelog />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="selectedTab === 'about'" class="panel-body py-4">
|
<div v-show="selectedTab === 'about'" class="panel-body py-4">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<img :src="require('@/images/logo.svg').default" width="128">
|
<img :src="require('@/images/logo.svg').default" width="128">
|
||||||
<h4>{{ appName }}</h4>
|
<h4>{{ appName }}</h4>
|
||||||
<p>
|
<p>
|
||||||
{{ $t('word.version') }}: {{ appVersion }}<br>
|
{{ $t('word.version') }} {{ appVersion }}<br>
|
||||||
<a class="c-hand" @click="openOutside('https://github.com/EStarium/antares')">GitHub</a><br>
|
<a class="c-hand" @click="openOutside('https://github.com/Fabio286/antares')"><i class="mdi mdi-github d-inline" /> GitHub</a> • <a class="c-hand" @click="openOutside('https://twitter.com/AntaresSQL')"><i class="mdi mdi-twitter d-inline" /> Twitter</a> • <a class="c-hand" @click="openOutside('https://antares-sql.app/')"><i class="mdi mdi-web d-inline" /> Website</a><br>
|
||||||
|
<small>{{ $t('word.author') }} <a class="c-hand" @click="openOutside('https://github.com/Fabio286')">Fabio Di Stasio</a></small><br>
|
||||||
<small>{{ $t('message.madeWithJS') }}</small>
|
<small>{{ $t('message.madeWithJS') }}</small>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -126,18 +300,74 @@
|
|||||||
import { mapActions, mapGetters } from 'vuex';
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
import localesNames from '@/i18n/supported-locales';
|
import localesNames from '@/i18n/supported-locales';
|
||||||
import ModalSettingsUpdate from '@/components/ModalSettingsUpdate';
|
import ModalSettingsUpdate from '@/components/ModalSettingsUpdate';
|
||||||
|
import ModalSettingsChangelog from '@/components/ModalSettingsChangelog';
|
||||||
|
import BaseTextEditor from '@/components/BaseTextEditor';
|
||||||
const { shell } = require('electron');
|
const { shell } = require('electron');
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ModalSettings',
|
name: 'ModalSettings',
|
||||||
components: {
|
components: {
|
||||||
ModalSettingsUpdate
|
ModalSettingsUpdate,
|
||||||
|
ModalSettingsChangelog,
|
||||||
|
BaseTextEditor
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
localLocale: null,
|
localLocale: null,
|
||||||
|
localPageSize: null,
|
||||||
localTimeout: null,
|
localTimeout: null,
|
||||||
selectedTab: 'general'
|
localEditorTheme: null,
|
||||||
|
selectedTab: 'general',
|
||||||
|
pageSizes: [30, 40, 100, 250, 500, 1000],
|
||||||
|
editorThemes: [
|
||||||
|
{
|
||||||
|
group: this.$t('word.light'),
|
||||||
|
themes: [
|
||||||
|
{ code: 'chrome', name: 'Chrome' },
|
||||||
|
{ code: 'clouds', name: 'Clouds' },
|
||||||
|
{ code: 'crimson_editor', name: 'Crimson Editor' },
|
||||||
|
{ code: 'dawn', name: 'Dawn' },
|
||||||
|
{ code: 'dreamweaver', name: 'Dreamweaver' },
|
||||||
|
{ code: 'eclupse', name: 'Eclipse' },
|
||||||
|
{ code: 'github', name: 'GitHub' },
|
||||||
|
{ code: 'iplastic', name: 'IPlastic' },
|
||||||
|
{ code: 'solarized_light', name: 'Solarized Light' },
|
||||||
|
{ code: 'textmate', name: 'TextMate' },
|
||||||
|
{ code: 'tomorrow', name: 'Tomorrow' },
|
||||||
|
{ code: 'xcode', name: 'Xcode' },
|
||||||
|
{ code: 'kuroir', name: 'Kuroir' },
|
||||||
|
{ code: 'katzenmilch', name: 'KatzenMilch' },
|
||||||
|
{ code: 'sqlserver', name: 'SQL Server' }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
group: this.$t('word.dark'),
|
||||||
|
themes: [
|
||||||
|
{ code: 'ambiance', name: 'Ambiance' },
|
||||||
|
{ code: 'chaos', name: 'Chaos' },
|
||||||
|
{ code: 'clouds_midnight', name: 'Clouds Midnight' },
|
||||||
|
{ code: 'dracula', name: 'Dracula' },
|
||||||
|
{ code: 'cobalt', name: 'Cobalt' },
|
||||||
|
{ code: 'gruvbox', name: 'Gruvbox' },
|
||||||
|
{ code: 'gob', name: 'Green on Black' },
|
||||||
|
{ code: 'idle_fingers', name: 'Idle Fingers' },
|
||||||
|
{ code: 'kr_theme', name: 'krTheme' },
|
||||||
|
{ code: 'merbivore', name: 'Merbivore' },
|
||||||
|
{ code: 'mono_industrial', name: 'Mono Industrial' },
|
||||||
|
{ code: 'monokai', name: 'Monokai' },
|
||||||
|
{ code: 'nord_dark', name: 'Nord Dark' },
|
||||||
|
{ code: 'pastel_on_dark', name: 'Pastel on Dark' },
|
||||||
|
{ code: 'solarized_dark', name: 'Solarized Dark' },
|
||||||
|
{ code: 'terminal', name: 'Terminal' },
|
||||||
|
{ code: 'tomorrow_night', name: 'Tomorrow Night' },
|
||||||
|
{ code: 'tomorrow_night_blue', name: 'Tomorrow Night Blue' },
|
||||||
|
{ code: 'tomorrow_night_bright', name: 'Tomorrow Night Bright' },
|
||||||
|
{ code: 'tomorrow_night_eighties', name: 'Tomorrow Night 80s' },
|
||||||
|
{ code: 'twilight', name: 'Twilight' },
|
||||||
|
{ code: 'vibrant_ink', name: 'Vibrant Ink' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -146,8 +376,17 @@ export default {
|
|||||||
appVersion: 'application/appVersion',
|
appVersion: 'application/appVersion',
|
||||||
selectedSettingTab: 'application/selectedSettingTab',
|
selectedSettingTab: 'application/selectedSettingTab',
|
||||||
selectedLocale: 'settings/getLocale',
|
selectedLocale: 'settings/getLocale',
|
||||||
|
pageSize: 'settings/getDataTabLimit',
|
||||||
|
selectedAutoComplete: 'settings/getAutoComplete',
|
||||||
|
selectedLineWrap: 'settings/getLineWrap',
|
||||||
notificationsTimeout: 'settings/getNotificationsTimeout',
|
notificationsTimeout: 'settings/getNotificationsTimeout',
|
||||||
updateStatus: 'application/getUpdateStatus'
|
restoreTabs: 'settings/getRestoreTabs',
|
||||||
|
applicationTheme: 'settings/getApplicationTheme',
|
||||||
|
editorTheme: 'settings/getEditorTheme',
|
||||||
|
editorFontSize: 'settings/getEditorFontSize',
|
||||||
|
updateStatus: 'application/getUpdateStatus',
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
}),
|
}),
|
||||||
locales () {
|
locales () {
|
||||||
const locales = [];
|
const locales = [];
|
||||||
@@ -158,17 +397,50 @@ export default {
|
|||||||
},
|
},
|
||||||
hasUpdates () {
|
hasUpdates () {
|
||||||
return ['available', 'downloading', 'downloaded'].includes(this.updateStatus);
|
return ['available', 'downloading', 'downloaded'].includes(this.updateStatus);
|
||||||
|
},
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.selectedWorkspace);
|
||||||
|
},
|
||||||
|
exampleQuery () {
|
||||||
|
return `-- This is an example
|
||||||
|
SELECT
|
||||||
|
employee.id,
|
||||||
|
employee.first_name,
|
||||||
|
employee.last_name,
|
||||||
|
SUM(DATEDIFF("SECOND", call.start, call.end)) AS call_duration
|
||||||
|
FROM call
|
||||||
|
INNER JOIN employee ON call.employee_id = employee.id
|
||||||
|
GROUP BY
|
||||||
|
employee.id,
|
||||||
|
employee.first_name,
|
||||||
|
employee.last_name
|
||||||
|
ORDER BY
|
||||||
|
employee.id ASC;
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
this.localLocale = this.selectedLocale;
|
this.localLocale = this.selectedLocale;
|
||||||
|
this.localPageSize = this.pageSize;
|
||||||
this.localTimeout = this.notificationsTimeout;
|
this.localTimeout = this.notificationsTimeout;
|
||||||
|
this.localEditorTheme = this.editorTheme;
|
||||||
this.selectedTab = this.selectedSettingTab;
|
this.selectedTab = this.selectedSettingTab;
|
||||||
|
window.addEventListener('keydown', this.onKey);
|
||||||
|
},
|
||||||
|
beforeDestroy () {
|
||||||
|
window.removeEventListener('keydown', this.onKey);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
closeModal: 'application/hideSettingModal',
|
closeModal: 'application/hideSettingModal',
|
||||||
changeLocale: 'settings/changeLocale',
|
changeLocale: 'settings/changeLocale',
|
||||||
|
changePageSize: 'settings/changePageSize',
|
||||||
|
changeRestoreTabs: 'settings/changeRestoreTabs',
|
||||||
|
changeAutoComplete: 'settings/changeAutoComplete',
|
||||||
|
changeLineWrap: 'settings/changeLineWrap',
|
||||||
|
changeApplicationTheme: 'settings/changeApplicationTheme',
|
||||||
|
changeEditorTheme: 'settings/changeEditorTheme',
|
||||||
|
changeEditorFontSize: 'settings/changeEditorFontSize',
|
||||||
updateNotificationsTimeout: 'settings/updateNotificationsTimeout'
|
updateNotificationsTimeout: 'settings/updateNotificationsTimeout'
|
||||||
}),
|
}),
|
||||||
selectTab (tab) {
|
selectTab (tab) {
|
||||||
@@ -182,6 +454,20 @@ export default {
|
|||||||
this.localTimeout = 10;
|
this.localTimeout = 10;
|
||||||
|
|
||||||
this.updateNotificationsTimeout(+this.localTimeout);
|
this.updateNotificationsTimeout(+this.localTimeout);
|
||||||
|
},
|
||||||
|
onKey (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (e.key === 'Escape')
|
||||||
|
this.closeModal();
|
||||||
|
},
|
||||||
|
toggleRestoreSession () {
|
||||||
|
this.changeRestoreTabs(!this.restoreTabs);
|
||||||
|
},
|
||||||
|
toggleAutoComplete () {
|
||||||
|
this.changeAutoComplete(!this.selectedAutoComplete);
|
||||||
|
},
|
||||||
|
toggleLineWrap () {
|
||||||
|
this.changeLineWrap(!this.selectedLineWrap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -189,26 +475,59 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
#settings {
|
#settings {
|
||||||
.modal-body {
|
.modal-container {
|
||||||
overflow: hidden;
|
position: absolute;
|
||||||
|
top: 17.5vh;
|
||||||
|
|
||||||
.panel-body {
|
.modal-body {
|
||||||
height: calc(70vh - 70px);
|
overflow: hidden;
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge::after {
|
.panel-body {
|
||||||
background: #32b643;
|
min-height: calc(25vh - 70px);
|
||||||
}
|
max-height: 65vh;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
.badge-update::after {
|
.theme-block {
|
||||||
bottom: initial;
|
position: relative;
|
||||||
background: $primary-color;
|
text-align: center;
|
||||||
}
|
|
||||||
|
|
||||||
.form-label {
|
&.selected {
|
||||||
display: flex;
|
img {
|
||||||
align-items: center;
|
box-shadow: 0 0 0 3px $primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-name {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge::after {
|
||||||
|
background: #32b643;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-update::after {
|
||||||
|
bottom: initial;
|
||||||
|
background: $primary-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
82
src/renderer/components/ModalSettingsChangelog.vue
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<template>
|
||||||
|
<div class="p-relative">
|
||||||
|
<BaseLoader v-if="isLoading" />
|
||||||
|
<div
|
||||||
|
id="changelog"
|
||||||
|
class="container"
|
||||||
|
v-html="changelog"
|
||||||
|
/>
|
||||||
|
<div v-if="isError" class="empty">
|
||||||
|
<div class="empty-icon">
|
||||||
|
<i class="mdi mdi-48px mdi-alert-outline" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
import marked from 'marked';
|
||||||
|
import BaseLoader from '@/components/BaseLoader';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ModalSettingsChangelog',
|
||||||
|
components: {
|
||||||
|
BaseLoader
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
changelog: '',
|
||||||
|
isLoading: true,
|
||||||
|
error: '',
|
||||||
|
isError: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({ appVersion: 'application/appVersion' })
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.getChangelog();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async getChangelog () {
|
||||||
|
try {
|
||||||
|
const apiRes = await fetch(`https://api.github.com/repos/Fabio286/antares/releases/tags/v${this.appVersion}`, {
|
||||||
|
method: 'GET'
|
||||||
|
});
|
||||||
|
|
||||||
|
const { body } = await apiRes.json();
|
||||||
|
const markdown = body.substr(0, body.indexOf('### Download'));
|
||||||
|
const renderer = {
|
||||||
|
link (href, title, text) {
|
||||||
|
return text;
|
||||||
|
},
|
||||||
|
listitem (text) {
|
||||||
|
return `<li>${text.replace(/ *\([^)]*\) */g, '')}</li>`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
marked.use({ renderer });
|
||||||
|
|
||||||
|
this.changelog = marked(markdown);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.error = err.message;
|
||||||
|
this.isError = true;
|
||||||
|
}
|
||||||
|
this.isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
#changelog {
|
||||||
|
h3 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -36,11 +36,17 @@
|
|||||||
{{ $t('message.restartToInstall') }}
|
{{ $t('message.restartToInstall') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group mt-4">
|
||||||
|
<label class="form-switch d-inline-block disabled" @click.prevent="toggleAllowPrerelease">
|
||||||
|
<input type="checkbox" :checked="allowPrerelease">
|
||||||
|
<i class="form-icon" /> {{ $t('message.includeBetaUpdates') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -48,7 +54,8 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
updateStatus: 'application/getUpdateStatus',
|
updateStatus: 'application/getUpdateStatus',
|
||||||
downloadPercentage: 'application/getDownloadProgress'
|
downloadPercentage: 'application/getDownloadProgress',
|
||||||
|
allowPrerelease: 'settings/getAllowPrerelease'
|
||||||
}),
|
}),
|
||||||
updateMessage () {
|
updateMessage () {
|
||||||
switch (this.updateStatus) {
|
switch (this.updateStatus) {
|
||||||
@@ -70,18 +77,18 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
changeAllowPrerelease: 'settings/changeAllowPrerelease'
|
||||||
|
}),
|
||||||
checkForUpdates () {
|
checkForUpdates () {
|
||||||
ipcRenderer.send('check-for-updates');
|
ipcRenderer.send('check-for-updates');
|
||||||
},
|
},
|
||||||
restartToUpdate () {
|
restartToUpdate () {
|
||||||
ipcRenderer.send('restart-to-update');
|
ipcRenderer.send('restart-to-update');
|
||||||
|
},
|
||||||
|
toggleAllowPrerelease () {
|
||||||
|
this.changeAllowPrerelease(!this.allowPrerelease);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.empty {
|
|
||||||
color: $body-font-color;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,70 +1,356 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="editor-wrapper">
|
<div class="editor-wrapper">
|
||||||
<div ref="editor" class="editor" />
|
<div
|
||||||
|
:id="`editor-${id}`"
|
||||||
|
class="editor"
|
||||||
|
:style="{height: `${height}px`}"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import * as ace from 'ace-builds';
|
||||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
import 'ace-builds/webpack-resolver';
|
||||||
import { completionItemProvider } from '@/suggestions/sql';
|
import '../libs/ext-language_tools';
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
monaco.languages.registerCompletionItemProvider('sql', completionItemProvider(monaco));
|
import Tables from '@/ipc-api/Tables';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'QueryEditor',
|
name: 'QueryEditor',
|
||||||
props: {
|
props: {
|
||||||
value: String
|
value: String,
|
||||||
|
workspace: Object,
|
||||||
|
isSelected: Boolean,
|
||||||
|
schema: { type: String, default: '' },
|
||||||
|
autoFocus: { type: Boolean, default: false },
|
||||||
|
readOnly: { type: Boolean, default: false },
|
||||||
|
height: { type: Number, default: 200 }
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
editor: null
|
editor: null,
|
||||||
|
fields: [],
|
||||||
|
customCompleter: [],
|
||||||
|
id: null,
|
||||||
|
lastSchema: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
editorTheme: 'settings/getEditorTheme',
|
||||||
|
editorFontSize: 'settings/getEditorFontSize',
|
||||||
|
autoComplete: 'settings/getAutoComplete',
|
||||||
|
lineWrap: 'settings/getLineWrap',
|
||||||
|
baseCompleter: 'application/getBaseCompleter'
|
||||||
|
}),
|
||||||
|
tables () {
|
||||||
|
return this.workspace
|
||||||
|
? this.workspace.structure.filter(schema => schema.name === this.schema)
|
||||||
|
.reduce((acc, curr) => {
|
||||||
|
acc.push(...curr.tables);
|
||||||
|
return acc;
|
||||||
|
}, []).map(table => {
|
||||||
|
return {
|
||||||
|
name: table.name,
|
||||||
|
type: table.type,
|
||||||
|
fields: []
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
triggers () {
|
||||||
|
return this.workspace
|
||||||
|
? this.workspace.structure.filter(schema => schema.name === this.schema)
|
||||||
|
.reduce((acc, curr) => {
|
||||||
|
acc.push(...curr.triggers);
|
||||||
|
return acc;
|
||||||
|
}, []).map(trigger => {
|
||||||
|
return {
|
||||||
|
name: trigger.name,
|
||||||
|
type: 'trigger'
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
procedures () {
|
||||||
|
return this.workspace
|
||||||
|
? this.workspace.structure.filter(schema => schema.name === this.schema)
|
||||||
|
.reduce((acc, curr) => {
|
||||||
|
acc.push(...curr.procedures);
|
||||||
|
return acc;
|
||||||
|
}, []).map(procedure => {
|
||||||
|
return {
|
||||||
|
name: `${procedure.name}()`,
|
||||||
|
type: 'routine'
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
functions () {
|
||||||
|
return this.workspace
|
||||||
|
? this.workspace.structure.filter(schema => schema.name === this.schema)
|
||||||
|
.reduce((acc, curr) => {
|
||||||
|
acc.push(...curr.functions);
|
||||||
|
return acc;
|
||||||
|
}, []).map(func => {
|
||||||
|
return {
|
||||||
|
name: `${func.name}()`,
|
||||||
|
type: 'function'
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
schedulers () {
|
||||||
|
return this.workspace
|
||||||
|
? this.workspace.structure.filter(schema => schema.name === this.schema)
|
||||||
|
.reduce((acc, curr) => {
|
||||||
|
acc.push(...curr.schedulers);
|
||||||
|
return acc;
|
||||||
|
}, []).map(scheduler => {
|
||||||
|
return {
|
||||||
|
name: scheduler.name,
|
||||||
|
type: 'scheduler'
|
||||||
|
};
|
||||||
|
})
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
mode () {
|
||||||
|
switch (this.workspace.client) {
|
||||||
|
case 'mysql':
|
||||||
|
case 'maria':
|
||||||
|
return 'mysql';
|
||||||
|
case 'mssql':
|
||||||
|
return 'sqlserver';
|
||||||
|
case 'pg':
|
||||||
|
return 'pgsql';
|
||||||
|
default:
|
||||||
|
return 'sql';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cursorPosition () {
|
||||||
|
return this.editor.session.doc.positionToIndex(this.editor.getCursorPosition());
|
||||||
|
},
|
||||||
|
lastWord () {
|
||||||
|
const charsBefore = this.value.slice(0, this.cursorPosition);
|
||||||
|
const words = charsBefore.replaceAll('\n', ' ').split(' ').filter(Boolean);
|
||||||
|
return words.pop();
|
||||||
|
},
|
||||||
|
isLastWordATable () {
|
||||||
|
return /\w+\.\w*/gm.test(this.lastWord);
|
||||||
|
},
|
||||||
|
fieldsCompleter () {
|
||||||
|
return {
|
||||||
|
getCompletions: (editor, session, pos, prefix, callback) => {
|
||||||
|
const completions = [];
|
||||||
|
this.fields.forEach(field => {
|
||||||
|
completions.push({
|
||||||
|
value: field,
|
||||||
|
meta: 'column',
|
||||||
|
score: 1000
|
||||||
|
});
|
||||||
|
});
|
||||||
|
callback(null, completions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
editorTheme () {
|
||||||
|
if (this.editor)
|
||||||
|
this.editor.setTheme(`ace/theme/${this.editorTheme}`);
|
||||||
|
},
|
||||||
|
editorFontSize () {
|
||||||
|
const sizes = {
|
||||||
|
small: '12px',
|
||||||
|
medium: '14px',
|
||||||
|
large: '16px'
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.setOptions({
|
||||||
|
fontSize: sizes[this.editorFontSize]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
autoComplete () {
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.setOptions({
|
||||||
|
enableLiveAutocompletion: this.autoComplete
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lineWrap () {
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.setOptions({
|
||||||
|
wrap: this.lineWrap
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isSelected () {
|
||||||
|
if (this.isSelected) {
|
||||||
|
this.lastSchema = this.schema;
|
||||||
|
this.editor.resize();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
height () {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.editor.resize();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
lastSchema () {
|
||||||
|
if (this.editor) {
|
||||||
|
this.editor.completers = this.baseCompleter.map(el => Object.assign({}, el));
|
||||||
|
this.setCustomCompleter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.id = this._uid;
|
||||||
|
this.lastSchema = this.schema;
|
||||||
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
this.editor = monaco.editor.create(this.$refs.editor, {
|
this.editor = ace.edit(`editor-${this.id}`, {
|
||||||
|
mode: `ace/mode/${this.mode}`,
|
||||||
|
theme: `ace/theme/${this.editorTheme}`,
|
||||||
value: this.value,
|
value: this.value,
|
||||||
language: 'sql',
|
fontSize: '14px',
|
||||||
theme: 'vs-dark',
|
printMargin: false,
|
||||||
autoIndent: true,
|
readOnly: this.readOnly
|
||||||
minimap: {
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
contextmenu: false,
|
|
||||||
wordBasedSuggestions: true,
|
|
||||||
acceptSuggestionOnEnter: 'smart',
|
|
||||||
quickSuggestions: true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.editor.onDidChangeModelContent(e => {
|
this.editor.setOptions({
|
||||||
|
enableBasicAutocompletion: true,
|
||||||
|
wrap: this.lineWrap,
|
||||||
|
enableSnippets: true,
|
||||||
|
enableLiveAutocompletion: this.autoComplete
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!this.baseCompleter.length)
|
||||||
|
this.setBaseCompleters(this.editor.completers.map(el => Object.assign({}, el)));
|
||||||
|
|
||||||
|
this.setCustomCompleter();
|
||||||
|
|
||||||
|
this.editor.commands.on('afterExec', e => {
|
||||||
|
if (['insertstring', 'backspace', 'del'].includes(e.command.name)) {
|
||||||
|
if (this.isLastWordATable || e.args === '.') {
|
||||||
|
if (e.args !== ' ') {
|
||||||
|
const table = this.tables.find(t => t.name === this.lastWord.split('.').pop().trim());
|
||||||
|
|
||||||
|
if (table) {
|
||||||
|
const params = {
|
||||||
|
uid: this.workspace.uid,
|
||||||
|
schema: this.schema,
|
||||||
|
table: table.name
|
||||||
|
};
|
||||||
|
|
||||||
|
Tables.getTableColumns(params).then(res => {
|
||||||
|
if (res.response.length)
|
||||||
|
this.fields = res.response.map(field => field.name);
|
||||||
|
this.editor.completers = [this.fieldsCompleter];
|
||||||
|
this.editor.execCommand('startAutocomplete');
|
||||||
|
}).catch(console.log);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.editor.completers = this.customCompleter;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.editor.completers = this.customCompleter;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.editor.completers = this.customCompleter;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.editor.session.on('change', () => {
|
||||||
const content = this.editor.getValue();
|
const content = this.editor.getValue();
|
||||||
this.$emit('update:value', content);
|
this.$emit('update:value', content);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.editor.on('guttermousedown', e => {
|
||||||
|
const target = e.domEvent.target;
|
||||||
|
if (target.className.indexOf('ace_gutter-cell') === -1)
|
||||||
|
return;
|
||||||
|
if (e.clientX > 25 + target.getBoundingClientRect().left)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const row = e.getDocumentPosition().row;
|
||||||
|
const breakpoints = e.editor.session.getBreakpoints(row, 0);
|
||||||
|
if (typeof breakpoints[row] === typeof undefined)
|
||||||
|
e.editor.session.setBreakpoint(row);
|
||||||
|
else
|
||||||
|
e.editor.session.clearBreakpoint(row);
|
||||||
|
e.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.autoFocus) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.editor.focus();
|
||||||
|
this.editor.resize();
|
||||||
|
}, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.editor.resize();
|
||||||
|
}, 20);
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
methods: {
|
||||||
this.editor && this.editor.dispose();
|
...mapActions({
|
||||||
|
setBaseCompleters: 'application/setBaseCompleter'
|
||||||
|
}),
|
||||||
|
setCustomCompleter () {
|
||||||
|
this.editor.completers.push({
|
||||||
|
getCompletions: (editor, session, pos, prefix, callback) => {
|
||||||
|
const completions = [];
|
||||||
|
[
|
||||||
|
...this.tables,
|
||||||
|
...this.triggers,
|
||||||
|
...this.procedures,
|
||||||
|
...this.functions,
|
||||||
|
...this.schedulers
|
||||||
|
].forEach(el => {
|
||||||
|
completions.push({
|
||||||
|
value: el.name,
|
||||||
|
meta: el.type
|
||||||
|
});
|
||||||
|
});
|
||||||
|
callback(null, completions);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.customCompleter = this.editor.completers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.editor-wrapper {
|
.editor-wrapper {
|
||||||
border-bottom: 1px solid #444;
|
border-bottom: 1px solid #444;
|
||||||
|
|
||||||
.editor {
|
.editor {
|
||||||
height: 200px;
|
width: 100%;
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.CodeMirror {
|
.ace_.mdi {
|
||||||
.CodeMirror-scroll {
|
display: inline-block;
|
||||||
max-width: 100%;
|
width: 17px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror-line {
|
.ace_gutter-cell.ace_breakpoint {
|
||||||
word-break: break-word !important;
|
&::before {
|
||||||
white-space: pre-wrap !important;
|
content: '\F0403';
|
||||||
}
|
position: absolute;
|
||||||
|
left: 3px;
|
||||||
|
top: 2px;
|
||||||
|
color: $primary-color;
|
||||||
|
display: inline-block;
|
||||||
|
font: normal normal normal 24px/1 "Material Design Icons", sans-serif;
|
||||||
|
font-size: inherit;
|
||||||
|
text-rendering: auto;
|
||||||
|
line-height: inherit;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
:context-event="contextEvent"
|
:context-event="contextEvent"
|
||||||
@close-context="$emit('close-context')"
|
@close-context="$emit('close-context')"
|
||||||
>
|
>
|
||||||
<div class="context-element" @click="showEditModal(contextConnection)">
|
<div class="context-element" @click="duplicateConnection">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-pencil text-light pr-1" /> {{ $t('word.edit') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-content-duplicate text-light pr-1" /> {{ $t('word.duplicate') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="context-element" @click="showConfirmModal">
|
<div class="context-element" @click="showConfirmModal">
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $t('word.delete') }}</span>
|
<span class="d-flex"><i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $t('word.delete') }}</span>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
v-if="isConfirmModal"
|
v-if="isConfirmModal"
|
||||||
@confirm="deleteConnection(contextConnection)"
|
@confirm="confirmDeleteConnection"
|
||||||
@hide="hideConfirmModal"
|
@hide="hideConfirmModal"
|
||||||
>
|
>
|
||||||
<template :slot="'header'">
|
<template :slot="'header'">
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapActions, mapGetters } from 'vuex';
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
import BaseContextMenu from '@/components/BaseContextMenu';
|
import BaseContextMenu from '@/components/BaseContextMenu';
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
|
||||||
@@ -46,12 +47,14 @@ export default {
|
|||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
isConfirmModal: false
|
isConfirmModal: false,
|
||||||
|
isEditModal: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
getConnectionName: 'connections/getConnectionName'
|
getConnectionName: 'connections/getConnectionName',
|
||||||
|
selectedWorkspace: 'workspaces/getSelected'
|
||||||
}),
|
}),
|
||||||
connectionName () {
|
connectionName () {
|
||||||
return this.getConnectionName(this.contextConnection.uid);
|
return this.getConnectionName(this.contextConnection.uid);
|
||||||
@@ -59,14 +62,36 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
|
addConnection: 'connections/addConnection',
|
||||||
deleteConnection: 'connections/deleteConnection',
|
deleteConnection: 'connections/deleteConnection',
|
||||||
showEditModal: 'application/showEditConnModal'
|
selectWorkspace: 'workspaces/selectWorkspace'
|
||||||
}),
|
}),
|
||||||
|
confirmDeleteConnection () {
|
||||||
|
if (this.selectedWorkspace === this.contextConnection.uid)
|
||||||
|
this.selectWorkspace();
|
||||||
|
this.deleteConnection(this.contextConnection);
|
||||||
|
this.closeContext();
|
||||||
|
},
|
||||||
|
duplicateConnection () {
|
||||||
|
let connectionCopy = Object.assign({}, this.contextConnection);
|
||||||
|
connectionCopy = {
|
||||||
|
...connectionCopy,
|
||||||
|
uid: uidGen('C'),
|
||||||
|
name: connectionCopy.name ? `${connectionCopy.name}_copy` : ''
|
||||||
|
};
|
||||||
|
|
||||||
|
this.addConnection(connectionCopy);
|
||||||
|
this.closeContext();
|
||||||
|
},
|
||||||
showConfirmModal () {
|
showConfirmModal () {
|
||||||
this.isConfirmModal = true;
|
this.isConfirmModal = true;
|
||||||
},
|
},
|
||||||
hideConfirmModal () {
|
hideConfirmModal () {
|
||||||
this.isConfirmModal = false;
|
this.isConfirmModal = false;
|
||||||
|
this.closeContext();
|
||||||
|
},
|
||||||
|
closeContext () {
|
||||||
|
this.$emit('close-context');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="columns">
|
|
||||||
<div class="column col-12 empty text-light">
|
|
||||||
<div class="empty-icon">
|
|
||||||
<i class="mdi mdi-48px mdi-emoticon" />
|
|
||||||
</div>
|
|
||||||
<p class="empty-title h5">
|
|
||||||
{{ $t('message.appWelcome') }}
|
|
||||||
</p>
|
|
||||||
<p class="empty-subtitle">
|
|
||||||
{{ $t('message.appFirstStep') }}
|
|
||||||
</p>
|
|
||||||
<div class="empty-action">
|
|
||||||
<button class="btn btn-primary" @click="$emit('new-conn')">
|
|
||||||
{{ $t('message.createConnection') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'TheAppWelcome'
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.empty {
|
|
||||||
height: 100%;
|
|
||||||
border-radius: 0;
|
|
||||||
background: transparent;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -3,19 +3,19 @@
|
|||||||
<div class="footer-left-elements">
|
<div class="footer-left-elements">
|
||||||
<ul class="footer-elements">
|
<ul class="footer-elements">
|
||||||
<li class="footer-element">
|
<li class="footer-element">
|
||||||
<i class="mdi mdi-18px mdi-memory mr-1" />
|
<i class="mdi mdi-18px mdi-database mr-1" />
|
||||||
<small>{{ appVersion }}</small>
|
<small>{{ versionString }}</small>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="footer-right-elements">
|
<div class="footer-right-elements">
|
||||||
<ul class="footer-elements">
|
<ul class="footer-elements">
|
||||||
<li class="footer-element footer-link" @click="openOutside('https://github.com/sponsors/Fabio286')">
|
<li class="footer-element footer-link" @click="openOutside('https://www.treedom.net/en/user/fabio-di-stasio/event/antares-for-the-planet')">
|
||||||
<i class="mdi mdi-18px mdi-coffee mr-1" />
|
<i class="mdi mdi-18px mdi-tree mr-1" />
|
||||||
<small>{{ $t('word.donate') }}</small>
|
<small>{{ $t('message.plantATree') }}</small>
|
||||||
</li>
|
</li>
|
||||||
<li class="footer-element footer-link" @click="openOutside('https://github.com/EStarium/antares/issues')">
|
<li class="footer-element footer-link" @click="openOutside('https://github.com/Fabio286/antares/issues')">
|
||||||
<i class="mdi mdi-18px mdi-bug" />
|
<i class="mdi mdi-18px mdi-bug" />
|
||||||
</li>
|
</li>
|
||||||
<li class="footer-element footer-link" @click="showSettingModal('about')">
|
<li class="footer-element footer-link" @click="showSettingModal('about')">
|
||||||
@@ -34,9 +34,18 @@ export default {
|
|||||||
name: 'TheFooter',
|
name: 'TheFooter',
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
appName: 'application/appName',
|
workspace: 'workspaces/getSelected',
|
||||||
|
getWorkspace: 'workspaces/getWorkspace',
|
||||||
appVersion: 'application/appVersion'
|
appVersion: 'application/appVersion'
|
||||||
})
|
}),
|
||||||
|
version () {
|
||||||
|
return this.getWorkspace(this.workspace) ? this.getWorkspace(this.workspace).version : null;
|
||||||
|
},
|
||||||
|
versionString () {
|
||||||
|
if (this.version)
|
||||||
|
return `${this.version.name} ${this.version.number} (${this.version.arch} ${this.version.os})`;
|
||||||
|
return '';
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
@@ -55,13 +64,11 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: $primary-color;
|
|
||||||
padding: 0 0.2rem;
|
padding: 0 0.2rem;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
box-shadow: 0 0 1px 0 #000;
|
|
||||||
|
|
||||||
.footer-elements {
|
.footer-elements {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
@@ -79,10 +86,6 @@ export default {
|
|||||||
&.footer-link {
|
&.footer-link {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background 0.2s;
|
transition: background 0.2s;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba($color: #fff, $alpha: 0.1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
75
src/renderer/components/TheScratchpad.vue
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<template>
|
||||||
|
<ConfirmModal
|
||||||
|
:confirm-text="$t('word.update')"
|
||||||
|
:cancel-text="$t('word.close')"
|
||||||
|
size="large"
|
||||||
|
:hide-footer="true"
|
||||||
|
@hide="hideScratchpad"
|
||||||
|
>
|
||||||
|
<template :slot="'header'">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-notebook-edit-outline mr-1" /> {{ $t('word.scratchpad') }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div :slot="'body'">
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<TextEditor
|
||||||
|
:value.sync="localNotes"
|
||||||
|
editor-class="textarea-editor"
|
||||||
|
mode="markdown"
|
||||||
|
:auto-focus="true"
|
||||||
|
:show-line-numbers="false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<small class="text-gray">{{ $t('message.markdownSupported') }}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
import TextEditor from '@/components/BaseTextEditor';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TheScratchpad',
|
||||||
|
components: {
|
||||||
|
ConfirmModal,
|
||||||
|
TextEditor
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
localNotes: '',
|
||||||
|
debounceTimeout: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
notes: 'scratchpad/getNotes'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
localNotes () {
|
||||||
|
clearTimeout(this.debounceTimeout);
|
||||||
|
|
||||||
|
this.debounceTimeout = setTimeout(() => {
|
||||||
|
this.changeNotes(this.localNotes);
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.localNotes = this.notes;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
hideScratchpad: 'application/hideScratchpad',
|
||||||
|
changeNotes: 'scratchpad/changeNotes'
|
||||||
|
}),
|
||||||
|
hideModal () {
|
||||||
|
this.$emit('hide');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -8,24 +8,29 @@
|
|||||||
@close-context="isContext = false"
|
@close-context="isContext = false"
|
||||||
/>
|
/>
|
||||||
<ul class="settingbar-elements">
|
<ul class="settingbar-elements">
|
||||||
<draggable v-model="connections">
|
<Draggable
|
||||||
|
v-model="connections"
|
||||||
|
@start="isDragging = true"
|
||||||
|
@end="dragStop"
|
||||||
|
>
|
||||||
<li
|
<li
|
||||||
v-for="connection in connections"
|
v-for="connection in connections"
|
||||||
:key="connection.uid"
|
:key="connection.uid"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
class="settingbar-element btn btn-link ex-tooltip"
|
class="settingbar-element btn btn-link ex-tooltip"
|
||||||
:class="{'selected': connection.uid === selectedWorkspace}"
|
:class="{'selected': connection.uid === selectedWorkspace}"
|
||||||
@click="selectWorkspace(connection.uid)"
|
@click.stop="selectWorkspace(connection.uid)"
|
||||||
@contextmenu.prevent="contextMenu($event, connection)"
|
@contextmenu.prevent="contextMenu($event, connection)"
|
||||||
@mouseover.self="tooltipPosition"
|
@mouseover.self="tooltipPosition"
|
||||||
>
|
>
|
||||||
<i class="settingbar-element-icon dbi" :class="`dbi-${connection.client} ${connected.includes(connection.uid) ? 'badge' : ''}`" />
|
<i class="settingbar-element-icon dbi" :class="`dbi-${connection.client} ${getStatusBadge(connection.uid)}`" />
|
||||||
<span class="ex-tooltip-content">{{ getConnectionName(connection.uid) }}</span>
|
<span v-if="!isDragging" class="ex-tooltip-content">{{ getConnectionName(connection.uid) }}</span>
|
||||||
</li>
|
</li>
|
||||||
</draggable>
|
</Draggable>
|
||||||
<li
|
<li
|
||||||
class="settingbar-element btn btn-link ex-tooltip"
|
class="settingbar-element btn btn-link ex-tooltip"
|
||||||
@click="showNewConnModal"
|
:class="{'selected': 'NEW' === selectedWorkspace}"
|
||||||
|
@click="selectWorkspace('NEW')"
|
||||||
@mouseover.self="tooltipPosition"
|
@mouseover.self="tooltipPosition"
|
||||||
>
|
>
|
||||||
<i class="settingbar-element-icon mdi mdi-24px mdi-plus text-light" />
|
<i class="settingbar-element-icon mdi mdi-24px mdi-plus text-light" />
|
||||||
@@ -36,6 +41,10 @@
|
|||||||
|
|
||||||
<div class="settingbar-bottom-elements">
|
<div class="settingbar-bottom-elements">
|
||||||
<ul class="settingbar-elements">
|
<ul class="settingbar-elements">
|
||||||
|
<li class="settingbar-element btn btn-link ex-tooltip" @click="showScratchpad">
|
||||||
|
<i class="settingbar-element-icon mdi mdi-24px mdi-notebook-edit-outline text-light" />
|
||||||
|
<span class="ex-tooltip-content">{{ $t('word.scratchpad') }}</span>
|
||||||
|
</li>
|
||||||
<li class="settingbar-element btn btn-link ex-tooltip" @click="showSettingModal('general')">
|
<li class="settingbar-element btn btn-link ex-tooltip" @click="showSettingModal('general')">
|
||||||
<i class="settingbar-element-icon mdi mdi-24px mdi-cog text-light" :class="{' badge badge-update': hasUpdates}" />
|
<i class="settingbar-element-icon mdi mdi-24px mdi-cog text-light" :class="{' badge badge-update': hasUpdates}" />
|
||||||
<span class="ex-tooltip-content">{{ $t('word.settings') }}</span>
|
<span class="ex-tooltip-content">{{ $t('word.settings') }}</span>
|
||||||
@@ -47,19 +56,20 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapActions, mapGetters } from 'vuex';
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
import draggable from 'vuedraggable';
|
import Draggable from 'vuedraggable';
|
||||||
import SettingBarContext from '@/components/SettingBarContext';
|
import SettingBarContext from '@/components/SettingBarContext';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TheSettingBar',
|
name: 'TheSettingBar',
|
||||||
components: {
|
components: {
|
||||||
draggable,
|
Draggable,
|
||||||
SettingBarContext
|
SettingBarContext
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
dragElement: null,
|
dragElement: null,
|
||||||
isContext: false,
|
isContext: false,
|
||||||
|
isDragging: false,
|
||||||
contextEvent: null,
|
contextEvent: null,
|
||||||
contextConnection: {},
|
contextConnection: {},
|
||||||
scale: 0
|
scale: 0
|
||||||
@@ -69,7 +79,7 @@ export default {
|
|||||||
...mapGetters({
|
...mapGetters({
|
||||||
getConnections: 'connections/getConnections',
|
getConnections: 'connections/getConnections',
|
||||||
getConnectionName: 'connections/getConnectionName',
|
getConnectionName: 'connections/getConnectionName',
|
||||||
connected: 'workspaces/getConnected',
|
getWorkspace: 'workspaces/getWorkspace',
|
||||||
selectedWorkspace: 'workspaces/getSelected',
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
updateStatus: 'application/getUpdateStatus'
|
updateStatus: 'application/getUpdateStatus'
|
||||||
}),
|
}),
|
||||||
@@ -88,8 +98,8 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
updateConnections: 'connections/updateConnections',
|
updateConnections: 'connections/updateConnections',
|
||||||
showNewConnModal: 'application/showNewConnModal',
|
|
||||||
showSettingModal: 'application/showSettingModal',
|
showSettingModal: 'application/showSettingModal',
|
||||||
|
showScratchpad: 'application/showScratchpad',
|
||||||
selectWorkspace: 'workspaces/selectWorkspace'
|
selectWorkspace: 'workspaces/selectWorkspace'
|
||||||
}),
|
}),
|
||||||
contextMenu (event, connection) {
|
contextMenu (event, connection) {
|
||||||
@@ -101,9 +111,32 @@ export default {
|
|||||||
return connection.ask ? '' : `${connection.user + '@'}${connection.host}:${connection.port}`;
|
return connection.ask ? '' : `${connection.user + '@'}${connection.host}:${connection.port}`;
|
||||||
},
|
},
|
||||||
tooltipPosition (e) {
|
tooltipPosition (e) {
|
||||||
const el = e.target;
|
const el = e.target ? e.target : e;
|
||||||
const fromTop = window.pageYOffset + el.getBoundingClientRect().top - (el.offsetHeight / 4);
|
const fromTop = window.pageYOffset + el.getBoundingClientRect().top - (el.offsetHeight / 4);
|
||||||
el.querySelector('.ex-tooltip-content').style.top = `${fromTop}px`;
|
el.querySelector('.ex-tooltip-content').style.top = `${fromTop}px`;
|
||||||
|
},
|
||||||
|
getStatusBadge (uid) {
|
||||||
|
if (this.getWorkspace(uid)) {
|
||||||
|
const status = this.getWorkspace(uid).connectionStatus;
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 'connected':
|
||||||
|
return 'badge badge-connected';
|
||||||
|
case 'connecting':
|
||||||
|
return 'badge badge-connecting';
|
||||||
|
case 'failed':
|
||||||
|
return 'badge badge-failed';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dragStop (e) {
|
||||||
|
this.isDragging = false;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.tooltipPosition(e.originalEvent.target.parentNode);
|
||||||
|
}, 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -117,9 +150,7 @@ export default {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: $bg-color-light;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-shadow: 0 0 1px 0 #000;
|
|
||||||
z-index: 9;
|
z-index: 9;
|
||||||
|
|
||||||
.settingbar-top-elements {
|
.settingbar-top-elements {
|
||||||
@@ -134,7 +165,6 @@ export default {
|
|||||||
|
|
||||||
.settingbar-bottom-elements {
|
.settingbar-bottom-elements {
|
||||||
padding-top: 0.5rem;
|
padding-top: 0.5rem;
|
||||||
background: $bg-color-light;
|
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,34 +179,46 @@ export default {
|
|||||||
height: $settingbar-width;
|
height: $settingbar-width;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border-left: 3px solid transparent;
|
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-content: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: flex-start;
|
||||||
flex-direction: column;
|
border-radius: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
border-left-color: $body-font-color;
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
height: $settingbar-width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
height: 0;
|
||||||
|
width: 3px;
|
||||||
|
transition: height 0.2s;
|
||||||
|
background-color: $primary-color;
|
||||||
|
border-radius: $border-radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settingbar-element-icon {
|
.settingbar-element-icon {
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
&.badge::after {
|
&.badge::after {
|
||||||
bottom: -10px;
|
bottom: -10px;
|
||||||
right: 0;
|
right: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: $success-color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.badge-update::after {
|
&.badge-update::after {
|
||||||
bottom: initial;
|
bottom: initial;
|
||||||
background: $primary-color;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,7 +238,7 @@ export default {
|
|||||||
padding: 0.2rem 0.4rem;
|
padding: 0.2rem 0.4rem;
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
background: rgba(48, 55, 66, 0.95);
|
background: rgba(48, 55, 66, 0.95);
|
||||||
border-radius: 0.1rem;
|
border-radius: $border-radius;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
max-width: 320px;
|
max-width: 320px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@@ -205,7 +247,13 @@ export default {
|
|||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover .ex-tooltip-content {
|
&.sortable-chosen {
|
||||||
|
.ex-tooltip-content {
|
||||||
|
opacity: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(.selected) .ex-tooltip-content {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,15 +37,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { remote, ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
|
import { getCurrentWindow } from '@electron/remote';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TheTitleBar',
|
name: 'TheTitleBar',
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
w: remote.getCurrentWindow(),
|
w: getCurrentWindow(),
|
||||||
isMaximized: remote.getCurrentWindow().isMaximized(),
|
isMaximized: getCurrentWindow().isMaximized(),
|
||||||
isDevelopment: process.env.NODE_ENV === 'development'
|
isDevelopment: process.env.NODE_ENV === 'development'
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -57,6 +58,7 @@ export default {
|
|||||||
}),
|
}),
|
||||||
windowTitle () {
|
windowTitle () {
|
||||||
if (!this.selectedWorkspace) return '';
|
if (!this.selectedWorkspace) return '';
|
||||||
|
if (this.selectedWorkspace === 'NEW') return this.$t('message.createNewConnection');
|
||||||
|
|
||||||
const connectionName = this.getConnectionName(this.selectedWorkspace);
|
const connectionName = this.getConnectionName(this.selectedWorkspace);
|
||||||
const workspace = this.getWorkspace(this.selectedWorkspace);
|
const workspace = this.getWorkspace(this.selectedWorkspace);
|
||||||
@@ -102,12 +104,10 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
background: $bg-color-light;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: $titlebar-height;
|
height: $titlebar-height;
|
||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
box-shadow: 0 0 1px 0 #000;
|
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
|
|
||||||
.titlebar-resizer {
|
.titlebar-resizer {
|
||||||
@@ -134,7 +134,7 @@ export default {
|
|||||||
|
|
||||||
.titlebar-logo {
|
.titlebar-logo {
|
||||||
height: $titlebar-height;
|
height: $titlebar-height;
|
||||||
padding: 0 0.4rem;
|
padding: 0.3rem 0.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.titlebar-element {
|
.titlebar-element {
|
||||||
@@ -149,11 +149,6 @@ export default {
|
|||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
background: rgba($color: #fff, $alpha: 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.close-button:hover {
|
|
||||||
background: red;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,98 +1,533 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-show="isSelected" class="workspace column columns col-gapless">
|
<div v-show="isSelected" class="workspace column columns col-gapless">
|
||||||
<WorkspaceExploreBar :connection="connection" :is-selected="isSelected" />
|
<WorkspaceExploreBar
|
||||||
<div v-if="workspace.connected" class="workspace-tabs column columns col-gapless">
|
v-if="workspace.connectionStatus === 'connected'"
|
||||||
<ul ref="tabWrap" class="tab tab-block column col-12">
|
:connection="connection"
|
||||||
|
:is-selected="isSelected"
|
||||||
|
/>
|
||||||
|
<div v-if="workspace.connectionStatus === 'connected'" class="workspace-tabs column columns col-gapless">
|
||||||
|
<Draggable
|
||||||
|
ref="tabWrap"
|
||||||
|
v-model="draggableTabs"
|
||||||
|
tag="ul"
|
||||||
|
group="tabs"
|
||||||
|
class="tab tab-block column col-12"
|
||||||
|
draggable=".tab-draggable"
|
||||||
|
@mouseover.native="addWheelEvent"
|
||||||
|
>
|
||||||
<li
|
<li
|
||||||
v-if="workspace.breadcrumbs.table"
|
v-for="(tab, i) of draggableTabs"
|
||||||
class="tab-item"
|
:key="i"
|
||||||
:class="{'active': selectedTab === 'prop'}"
|
:ref="selectedTab === tab.uid ? 'tab-selected' : ''"
|
||||||
@click="selectTab({uid: workspace.uid, tab: 'prop'})"
|
class="tab-item tab-draggable"
|
||||||
>
|
draggable="true"
|
||||||
<a class="tab-link">
|
|
||||||
<i class="mdi mdi-18px mdi-tune mr-1" />
|
|
||||||
<span :title="workspace.breadcrumbs.table">{{ $t('word.properties').toUpperCase() }}: {{ workspace.breadcrumbs.table }}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
v-if="workspace.breadcrumbs.table"
|
|
||||||
class="tab-item"
|
|
||||||
:class="{'active': selectedTab === 'data'}"
|
|
||||||
@click="selectTab({uid: workspace.uid, tab: 'data'})"
|
|
||||||
>
|
|
||||||
<a class="tab-link">
|
|
||||||
<i class="mdi mdi-18px mdi-table mr-1" />
|
|
||||||
<span :title="workspace.breadcrumbs.table">{{ $t('word.data').toUpperCase() }}: {{ workspace.breadcrumbs.table }}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
v-for="tab of queryTabs"
|
|
||||||
:key="tab.uid"
|
|
||||||
class="tab-item"
|
|
||||||
:class="{'active': selectedTab === tab.uid}"
|
:class="{'active': selectedTab === tab.uid}"
|
||||||
@click="selectTab({uid: workspace.uid, tab: tab.uid})"
|
@mousedown.left="selectTab({uid: workspace.uid, tab: tab.uid})"
|
||||||
@mousedown.middle="closeTab(tab.uid)"
|
@mouseup.middle="closeTab(tab)"
|
||||||
>
|
>
|
||||||
<a>
|
<a v-if="tab.type === 'query'" class="tab-link">
|
||||||
|
<i class="mdi mdi-18px mdi-code-tags mr-1" />
|
||||||
<span>
|
<span>
|
||||||
Query #{{ tab.index }}
|
<span>{{ tab.content || 'Query' | cutText }} #{{ tab.index }}</span>
|
||||||
<span
|
<span
|
||||||
v-if="queryTabs.length > 1"
|
|
||||||
class="btn btn-clear"
|
class="btn btn-clear"
|
||||||
:title="$t('word.close')"
|
:title="$t('word.close')"
|
||||||
@click.stop="closeTab(tab.uid)"
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else-if="tab.type === 'temp-data'"
|
||||||
|
class="tab-link"
|
||||||
|
@dblclick="openAsPermanentTab(tab)"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-18px mr-1" :class="tab.elementType === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
|
||||||
|
<span :title="`${$t('word.data').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
<span class=" text-italic">{{ tab.elementName | cutText }}</span>
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a v-else-if="tab.type === 'data'" class="tab-link">
|
||||||
|
<i class="mdi mdi-18px mr-1" :class="tab.elementType === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
|
||||||
|
<span :title="`${$t('word.data').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
{{ tab.elementName | cutText }}
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else-if="tab.type === 'new-table'"
|
||||||
|
class="tab-link"
|
||||||
|
:class="{'badge': tab.isChanged}"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
||||||
|
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
{{ $t('message.newTable') }}
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else-if="tab.type === 'table-props'"
|
||||||
|
class="tab-link"
|
||||||
|
:class="{'badge': tab.isChanged}"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-tune-vertical-variant mdi-18px mr-1" />
|
||||||
|
<span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
{{ tab.elementName | cutText }}
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else-if="tab.type === 'view-props'"
|
||||||
|
class="tab-link"
|
||||||
|
:class="{'badge': tab.isChanged}"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-tune-vertical-variant mdi-18px mr-1" />
|
||||||
|
<span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.view`)}`">
|
||||||
|
{{ tab.elementName | cutText }}
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else-if="tab.type === 'new-view'"
|
||||||
|
class="tab-link"
|
||||||
|
:class="{'badge': tab.isChanged}"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
||||||
|
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
{{ $t('message.newView') }}
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else-if="tab.type === 'new-trigger'"
|
||||||
|
class="tab-link"
|
||||||
|
:class="{'badge': tab.isChanged}"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
||||||
|
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
{{ $t('message.newTrigger') }}
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else-if="tab.type === 'new-routine'"
|
||||||
|
class="tab-link"
|
||||||
|
:class="{'badge': tab.isChanged}"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
||||||
|
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
{{ $t('message.newRoutine') }}
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else-if="tab.type === 'new-function'"
|
||||||
|
class="tab-link"
|
||||||
|
:class="{'badge': tab.isChanged}"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
||||||
|
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
{{ $t('message.newFunction') }}
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else-if="tab.type === 'new-trigger-function'"
|
||||||
|
class="tab-link"
|
||||||
|
:class="{'badge': tab.isChanged}"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
||||||
|
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
{{ $t('message.newTriggerFunction') }}
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else-if="tab.type === 'new-scheduler'"
|
||||||
|
class="tab-link"
|
||||||
|
:class="{'badge': tab.isChanged}"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
|
||||||
|
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
{{ $t('message.newScheduler') }}
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else-if="tab.type.includes('temp-')"
|
||||||
|
class="tab-link"
|
||||||
|
:class="{'badge': tab.isChanged}"
|
||||||
|
@dblclick="openAsPermanentTab(tab)"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
|
||||||
|
<span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
<span class=" text-italic">{{ tab.elementName | cutText }}</span>
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
v-else
|
||||||
|
class="tab-link"
|
||||||
|
:class="{'badge': tab.isChanged}"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
|
||||||
|
<span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`">
|
||||||
|
{{ tab.elementName | cutText }}
|
||||||
|
<span
|
||||||
|
class="btn btn-clear"
|
||||||
|
:title="$t('word.close')"
|
||||||
|
@mousedown.left.stop
|
||||||
|
@click.stop="closeTab(tab)"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="tab-item">
|
<li slot="header" class="tab-item dropdown tools-dropdown">
|
||||||
|
<a
|
||||||
|
class="tab-link workspace-tools-link dropdown-toggle"
|
||||||
|
tabindex="0"
|
||||||
|
:title="$t('word.tools')"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-tools" />
|
||||||
|
</a>
|
||||||
|
<ul class="menu text-left text-uppercase">
|
||||||
|
<li v-if="workspace.customizations.processesList" class="menu-item">
|
||||||
|
<a class="c-hand p-vcentered" @click="showProcessesModal">
|
||||||
|
<i class="mdi mdi-memory mr-1 tool-icon" />
|
||||||
|
<span>{{ $t('message.processesList') }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
v-if="workspace.customizations.variables"
|
||||||
|
class="menu-item"
|
||||||
|
title="Coming..."
|
||||||
|
>
|
||||||
|
<a class="c-hand p-vcentered disabled">
|
||||||
|
<i class="mdi mdi-shape mr-1 tool-icon" />
|
||||||
|
<span>{{ $t('word.variables') }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
v-if="workspace.customizations.usersManagement"
|
||||||
|
class="menu-item"
|
||||||
|
title="Coming..."
|
||||||
|
>
|
||||||
|
<a class="c-hand p-vcentered disabled">
|
||||||
|
<i class="mdi mdi-account-group mr-1 tool-icon" />
|
||||||
|
<span>{{ $t('message.manageUsers') }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li slot="footer" class="tab-item">
|
||||||
<a
|
<a
|
||||||
class="tab-add"
|
class="tab-add"
|
||||||
:title="$t('message.openNewTab')"
|
:title="$t('message.openNewTab')"
|
||||||
@click="addTab"
|
@click="addQueryTab"
|
||||||
>
|
>
|
||||||
<i class="mdi mdi-24px mdi-plus" />
|
<i class="mdi mdi-24px mdi-plus" />
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</Draggable>
|
||||||
<div v-show="selectedTab === 'prop'" class="column col-12">
|
<WorkspaceEmptyState v-if="!workspace.tabs.length" @new-tab="addQueryTab" />
|
||||||
<p class="px-2">
|
<template v-for="tab of workspace.tabs">
|
||||||
In future releases
|
<WorkspaceTabQuery
|
||||||
</p>
|
v-if="tab.type==='query'"
|
||||||
</div>
|
:key="tab.uid"
|
||||||
<WorkspaceTableTab
|
:tab="tab"
|
||||||
v-show="selectedTab === 'data'"
|
:is-selected="selectedTab === tab.uid"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
:table="workspace.breadcrumbs.table"
|
/>
|
||||||
/>
|
<WorkspaceTabTable
|
||||||
<WorkspaceQueryTab
|
v-else-if="['temp-data', 'data'].includes(tab.type)"
|
||||||
v-for="tab of queryTabs"
|
:key="tab.uid"
|
||||||
:key="tab.uid"
|
:connection="connection"
|
||||||
:tab-uid="tab.uid"
|
:is-selected="selectedTab === tab.uid"
|
||||||
:is-selected="selectedTab === tab.uid"
|
:table="tab.elementName"
|
||||||
:connection="connection"
|
:schema="tab.schema"
|
||||||
/>
|
:element-type="tab.elementType"
|
||||||
|
/>
|
||||||
|
<WorkspaceTabNewTable
|
||||||
|
v-else-if="tab.type === 'new-table'"
|
||||||
|
:key="tab.uid"
|
||||||
|
:tab="tab"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspaceTabPropsTable
|
||||||
|
v-else-if="tab.type === 'table-props'"
|
||||||
|
:key="tab.uid"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:table="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspaceTabNewView
|
||||||
|
v-else-if="tab.type === 'new-view'"
|
||||||
|
:key="tab.uid"
|
||||||
|
:tab="tab"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspaceTabPropsView
|
||||||
|
v-else-if="tab.type === 'view-props'"
|
||||||
|
:key="tab.uid"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:connection="connection"
|
||||||
|
:view="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspaceTabNewTrigger
|
||||||
|
v-else-if="tab.type === 'new-trigger'"
|
||||||
|
:key="tab.uid"
|
||||||
|
:tab="tab"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:trigger="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspaceTabPropsTrigger
|
||||||
|
v-else-if="['temp-trigger-props', 'trigger-props'].includes(tab.type)"
|
||||||
|
:key="tab.uid"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:trigger="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspaceTabNewTriggerFunction
|
||||||
|
v-else-if="tab.type === 'new-trigger-function'"
|
||||||
|
:key="tab.uid"
|
||||||
|
:tab="tab"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:trigger="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspaceTabPropsTriggerFunction
|
||||||
|
v-else-if="['temp-trigger-function-props', 'trigger-function-props'].includes(tab.type)"
|
||||||
|
:key="tab.uid"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:function="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspaceTabNewRoutine
|
||||||
|
v-else-if="tab.type === 'new-routine'"
|
||||||
|
:key="tab.uid"
|
||||||
|
:tab="tab"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:trigger="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspaceTabPropsRoutine
|
||||||
|
v-else-if="['temp-routine-props', 'routine-props'].includes(tab.type)"
|
||||||
|
:key="tab.uid"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:routine="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspaceTabNewFunction
|
||||||
|
v-else-if="tab.type === 'new-function'"
|
||||||
|
:key="tab.uid"
|
||||||
|
:tab="tab"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:trigger="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspaceTabPropsFunction
|
||||||
|
v-else-if="['temp-function-props', 'function-props'].includes(tab.type)"
|
||||||
|
:key="tab.uid"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:function="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspaceTabNewScheduler
|
||||||
|
v-else-if="tab.type === 'new-scheduler'"
|
||||||
|
:key="tab.uid"
|
||||||
|
:tab="tab"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:trigger="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
<WorkspaceTabPropsScheduler
|
||||||
|
v-else-if="['temp-scheduler-props', 'scheduler-props'].includes(tab.type)"
|
||||||
|
:key="tab.uid"
|
||||||
|
:connection="connection"
|
||||||
|
:is-selected="selectedTab === tab.uid"
|
||||||
|
:scheduler="tab.elementName"
|
||||||
|
:schema="tab.schema"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
<WorkspaceEditConnectionPanel v-else :connection="connection" />
|
||||||
|
<ModalProcessesList
|
||||||
|
v-if="isProcessesModal"
|
||||||
|
:connection="connection"
|
||||||
|
@close="hideProcessesModal"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ModalDiscardChanges
|
||||||
|
v-if="unsavedTab"
|
||||||
|
@confirm="closeTab(unsavedTab, true)"
|
||||||
|
@close="unsavedTab = null"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import Draggable from 'vuedraggable';
|
||||||
import Connection from '@/ipc-api/Connection';
|
import Connection from '@/ipc-api/Connection';
|
||||||
|
import WorkspaceEmptyState from '@/components/WorkspaceEmptyState';
|
||||||
import WorkspaceExploreBar from '@/components/WorkspaceExploreBar';
|
import WorkspaceExploreBar from '@/components/WorkspaceExploreBar';
|
||||||
import WorkspaceQueryTab from '@/components/WorkspaceQueryTab';
|
import WorkspaceEditConnectionPanel from '@/components/WorkspaceEditConnectionPanel';
|
||||||
import WorkspaceTableTab from '@/components/WorkspaceTableTab';
|
import WorkspaceTabQuery from '@/components/WorkspaceTabQuery';
|
||||||
|
import WorkspaceTabTable from '@/components/WorkspaceTabTable';
|
||||||
|
|
||||||
|
import WorkspaceTabNewTable from '@/components/WorkspaceTabNewTable';
|
||||||
|
import WorkspaceTabNewView from '@/components/WorkspaceTabNewView';
|
||||||
|
import WorkspaceTabNewTrigger from '@/components/WorkspaceTabNewTrigger';
|
||||||
|
import WorkspaceTabNewRoutine from '@/components/WorkspaceTabNewRoutine';
|
||||||
|
import WorkspaceTabNewFunction from '@/components/WorkspaceTabNewFunction';
|
||||||
|
import WorkspaceTabNewScheduler from '@/components/WorkspaceTabNewScheduler';
|
||||||
|
import WorkspaceTabNewTriggerFunction from '@/components/WorkspaceTabNewTriggerFunction';
|
||||||
|
|
||||||
|
import WorkspaceTabPropsTable from '@/components/WorkspaceTabPropsTable';
|
||||||
|
import WorkspaceTabPropsView from '@/components/WorkspaceTabPropsView';
|
||||||
|
import WorkspaceTabPropsTrigger from '@/components/WorkspaceTabPropsTrigger';
|
||||||
|
import WorkspaceTabPropsTriggerFunction from '@/components/WorkspaceTabPropsTriggerFunction';
|
||||||
|
import WorkspaceTabPropsRoutine from '@/components/WorkspaceTabPropsRoutine';
|
||||||
|
import WorkspaceTabPropsFunction from '@/components/WorkspaceTabPropsFunction';
|
||||||
|
import WorkspaceTabPropsScheduler from '@/components/WorkspaceTabPropsScheduler';
|
||||||
|
import ModalProcessesList from '@/components/ModalProcessesList';
|
||||||
|
import ModalDiscardChanges from '@/components/ModalDiscardChanges';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Workspace',
|
name: 'Workspace',
|
||||||
components: {
|
components: {
|
||||||
|
Draggable,
|
||||||
|
WorkspaceEmptyState,
|
||||||
WorkspaceExploreBar,
|
WorkspaceExploreBar,
|
||||||
WorkspaceQueryTab,
|
WorkspaceEditConnectionPanel,
|
||||||
WorkspaceTableTab
|
WorkspaceTabQuery,
|
||||||
|
WorkspaceTabTable,
|
||||||
|
WorkspaceTabNewTable,
|
||||||
|
WorkspaceTabPropsTable,
|
||||||
|
WorkspaceTabNewView,
|
||||||
|
WorkspaceTabPropsView,
|
||||||
|
WorkspaceTabNewTrigger,
|
||||||
|
WorkspaceTabPropsTrigger,
|
||||||
|
WorkspaceTabNewTriggerFunction,
|
||||||
|
WorkspaceTabPropsTriggerFunction,
|
||||||
|
WorkspaceTabNewRoutine,
|
||||||
|
WorkspaceTabNewFunction,
|
||||||
|
WorkspaceTabPropsRoutine,
|
||||||
|
WorkspaceTabPropsFunction,
|
||||||
|
WorkspaceTabNewScheduler,
|
||||||
|
WorkspaceTabPropsScheduler,
|
||||||
|
ModalProcessesList,
|
||||||
|
ModalDiscardChanges
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
cutText (string) {
|
||||||
|
const limit = 20;
|
||||||
|
const escapedString = string.replace(/\s{2,}/g, ' ');
|
||||||
|
if (escapedString.length > limit)
|
||||||
|
return `${escapedString.substr(0, limit)}...`;
|
||||||
|
return escapedString;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object
|
connection: Object
|
||||||
},
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
hasWheelEvent: false,
|
||||||
|
isProcessesModal: false,
|
||||||
|
unsavedTab: null
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
selectedWorkspace: 'workspaces/getSelected',
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
@@ -101,14 +536,53 @@ export default {
|
|||||||
workspace () {
|
workspace () {
|
||||||
return this.getWorkspace(this.connection.uid);
|
return this.getWorkspace(this.connection.uid);
|
||||||
},
|
},
|
||||||
|
draggableTabs: {
|
||||||
|
get () {
|
||||||
|
return this.workspace.tabs;
|
||||||
|
},
|
||||||
|
set (val) {
|
||||||
|
this.updateTabs({ uid: this.connection.uid, tabs: val });
|
||||||
|
}
|
||||||
|
},
|
||||||
isSelected () {
|
isSelected () {
|
||||||
return this.selectedWorkspace === this.connection.uid;
|
return this.selectedWorkspace === this.connection.uid;
|
||||||
},
|
},
|
||||||
|
isSettingSupported () {
|
||||||
|
if (this.workspace.breadcrumbs.table && this.workspace.customizations.tableSettings) return true;
|
||||||
|
if (this.workspace.breadcrumbs.view && this.workspace.customizations.viewSettings) return true;
|
||||||
|
if (this.workspace.breadcrumbs.trigger && this.workspace.customizations.triggerSettings) return true;
|
||||||
|
if (this.workspace.breadcrumbs.procedure && this.workspace.customizations.routineSettings) return true;
|
||||||
|
if (this.workspace.breadcrumbs.function && this.workspace.customizations.functionSettings) return true;
|
||||||
|
if (this.workspace.breadcrumbs.triggerFunction && this.workspace.customizations.functionSettings) return true;
|
||||||
|
if (this.workspace.breadcrumbs.scheduler && this.workspace.customizations.schedulerSettings) return true;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
selectedTab () {
|
selectedTab () {
|
||||||
return this.queryTabs.find(tab => tab.uid === this.workspace.selected_tab) || ['data', 'prop'].includes(this.workspace.selected_tab) ? this.workspace.selected_tab : this.queryTabs[0].uid;
|
return this.workspace ? this.workspace.selectedTab : null;
|
||||||
},
|
},
|
||||||
queryTabs () {
|
queryTabs () {
|
||||||
return this.workspace.tabs.filter(tab => tab.type === 'query');
|
return this.workspace.tabs.filter(tab => tab.type === 'query');
|
||||||
|
},
|
||||||
|
schemaChild () {
|
||||||
|
for (const key in this.workspace.breadcrumbs) {
|
||||||
|
if (key === 'schema') continue;
|
||||||
|
if (this.workspace.breadcrumbs[key]) return this.workspace.breadcrumbs[key];
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
selectedTab (newVal, oldVal) {
|
||||||
|
if (newVal !== oldVal) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const element = this.$refs['tab-selected'] ? this.$refs['tab-selected'][0] : null;
|
||||||
|
if (element) {
|
||||||
|
element.setAttribute('tabindex', '-1');
|
||||||
|
element.focus();
|
||||||
|
element.removeAttribute('tabindex');
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created () {
|
async created () {
|
||||||
@@ -117,14 +591,6 @@ export default {
|
|||||||
if (isInitiated)
|
if (isInitiated)
|
||||||
this.connectWorkspace(this.connection);
|
this.connectWorkspace(this.connection);
|
||||||
},
|
},
|
||||||
mounted () {
|
|
||||||
if (this.$refs.tabWrap) {
|
|
||||||
this.$refs.tabWrap.addEventListener('wheel', e => {
|
|
||||||
if (e.deltaY > 0) this.$refs.tabWrap.scrollLeft += 50;
|
|
||||||
else this.$refs.tabWrap.scrollLeft -= 50;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions({
|
...mapActions({
|
||||||
addWorkspace: 'workspaces/addWorkspace',
|
addWorkspace: 'workspaces/addWorkspace',
|
||||||
@@ -132,14 +598,55 @@ export default {
|
|||||||
removeConnected: 'workspaces/removeConnected',
|
removeConnected: 'workspaces/removeConnected',
|
||||||
selectTab: 'workspaces/selectTab',
|
selectTab: 'workspaces/selectTab',
|
||||||
newTab: 'workspaces/newTab',
|
newTab: 'workspaces/newTab',
|
||||||
removeTab: 'workspaces/removeTab'
|
removeTab: 'workspaces/removeTab',
|
||||||
|
updateTabs: 'workspaces/updateTabs'
|
||||||
}),
|
}),
|
||||||
addTab () {
|
addQueryTab () {
|
||||||
this.newTab(this.connection.uid);
|
this.newTab({ uid: this.connection.uid, type: 'query' });
|
||||||
},
|
},
|
||||||
closeTab (tUid) {
|
openAsPermanentTab (tab) {
|
||||||
if (this.queryTabs.length === 1) return;
|
const permanentTabs = {
|
||||||
this.removeTab({ uid: this.connection.uid, tab: tUid });
|
table: 'data',
|
||||||
|
view: 'data',
|
||||||
|
trigger: 'trigger-props',
|
||||||
|
triggerFunction: 'trigger-function-props',
|
||||||
|
function: 'function-props',
|
||||||
|
routine: 'routine-props',
|
||||||
|
scheduler: 'scheduler-props'
|
||||||
|
};
|
||||||
|
|
||||||
|
this.newTab({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: tab.schema,
|
||||||
|
elementName: tab.elementName,
|
||||||
|
type: permanentTabs[tab.elementType],
|
||||||
|
elementType: tab.elementType
|
||||||
|
});
|
||||||
|
},
|
||||||
|
closeTab (tab, force) {
|
||||||
|
this.unsavedTab = null;
|
||||||
|
// if (tab.type === 'query' && this.queryTabs.length === 1) return;
|
||||||
|
if (!force && tab.isChanged) {
|
||||||
|
this.unsavedTab = tab;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.removeTab({ uid: this.connection.uid, tab: tab.uid });
|
||||||
|
},
|
||||||
|
showProcessesModal () {
|
||||||
|
this.isProcessesModal = true;
|
||||||
|
},
|
||||||
|
hideProcessesModal () {
|
||||||
|
this.isProcessesModal = false;
|
||||||
|
},
|
||||||
|
addWheelEvent () {
|
||||||
|
if (!this.hasWheelEvent) {
|
||||||
|
this.$refs.tabWrap.$el.addEventListener('wheel', e => {
|
||||||
|
if (e.deltaY > 0) this.$refs.tabWrap.$el.scrollLeft += 50;
|
||||||
|
else this.$refs.tabWrap.$el.scrollLeft -= 50;
|
||||||
|
});
|
||||||
|
this.hasWheelEvent = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -155,12 +662,12 @@ export default {
|
|||||||
height: calc(100vh - #{$excluding-size});
|
height: calc(100vh - #{$excluding-size});
|
||||||
|
|
||||||
.tab-block {
|
.tab-block {
|
||||||
background: $bg-color-light;
|
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: 2px;
|
width: 2px;
|
||||||
@@ -168,21 +675,35 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tab-item {
|
.tab-item {
|
||||||
max-width: 12rem;
|
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
flex: initial;
|
flex: initial;
|
||||||
|
|
||||||
> a {
|
> a {
|
||||||
padding: 0.2rem 0.8rem;
|
padding: 0.2rem 0.6rem;
|
||||||
color: $body-font-color;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
transition: opacity 0.2s;
|
transition: opacity 0.2s;
|
||||||
|
|
||||||
|
&.badge::after {
|
||||||
|
position: absolute;
|
||||||
|
right: 35px;
|
||||||
|
top: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-clear {
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
||||||
|
.btn-clear {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.tab-add {
|
&.tab-add {
|
||||||
@@ -201,6 +722,53 @@ export default {
|
|||||||
|
|
||||||
&.active a {
|
&.active a {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
||||||
|
.btn-clear {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tools-dropdown {
|
||||||
|
height: 34px;
|
||||||
|
|
||||||
|
.tab-link:focus {
|
||||||
|
opacity: 1;
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
min-width: 100%;
|
||||||
|
|
||||||
|
.menu-item a {
|
||||||
|
border-radius: $border-radius;
|
||||||
|
color: inherit;
|
||||||
|
display: block;
|
||||||
|
margin: 0 -0.4rem;
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
.tool-icon {
|
||||||
|
line-height: 1;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
z-index: 9;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.tools-dropdown + .tab-item {
|
||||||
|
margin-left: 56px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-tools-link {
|
||||||
|
padding-bottom: 0;
|
||||||
|
padding-top: 0.3rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,27 +785,24 @@ export default {
|
|||||||
.th {
|
.th {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
background: $bg-color;
|
border: 2px solid;
|
||||||
border: 1px solid;
|
|
||||||
border-left: none;
|
border-left: none;
|
||||||
border-bottom-width: 2px;
|
border-bottom-width: 2px;
|
||||||
border-color: $bg-color-light;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
padding: 0.1rem 0.4rem;
|
padding: 0.1rem 0.2rem;
|
||||||
min-width: -webkit-fill-available;
|
min-width: -webkit-fill-available;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.td {
|
.td {
|
||||||
border-right: 1px solid;
|
border-right: 2px solid;
|
||||||
border-bottom: 1px solid;
|
border-bottom: 2px solid;
|
||||||
border-color: $bg-color-light;
|
padding: 0 0.2rem;
|
||||||
padding: 0 0.4rem;
|
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@@ -246,8 +811,6 @@ export default {
|
|||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: inset 0 0 0 1px $body-font-color;
|
|
||||||
background: rgba($color: #000, $alpha: 0.3);
|
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
519
src/renderer/components/WorkspaceAddConnectionPanel.vue
Normal file
@@ -0,0 +1,519 @@
|
|||||||
|
<template>
|
||||||
|
<div class="connection-panel">
|
||||||
|
<div class="panel">
|
||||||
|
<div class="panel-nav">
|
||||||
|
<ul class="tab tab-block">
|
||||||
|
<li
|
||||||
|
class="tab-item c-hand"
|
||||||
|
:class="{'active': selectedTab === 'general'}"
|
||||||
|
@click="selectTab('general')"
|
||||||
|
>
|
||||||
|
<a class="tab-link">{{ $t('word.general') }}</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="tab-item c-hand"
|
||||||
|
:class="{'active': selectedTab === 'ssl'}"
|
||||||
|
@click="selectTab('ssl')"
|
||||||
|
>
|
||||||
|
<a class="tab-link">{{ $t('word.ssl') }}</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="tab-item c-hand"
|
||||||
|
:class="{'active': selectedTab === 'ssh'}"
|
||||||
|
@click="selectTab('ssh')"
|
||||||
|
>
|
||||||
|
<a class="tab-link">{{ $t('word.sshTunnel') }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedTab === 'general'" class="panel-body py-0">
|
||||||
|
<div>
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<fieldset class="m-0" :disabled="isBusy">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.connectionName') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="connection.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.client') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<select v-model="connection.client" class="form-select">
|
||||||
|
<option value="mysql">
|
||||||
|
MySQL
|
||||||
|
</option>
|
||||||
|
<option value="maria">
|
||||||
|
MariaDB
|
||||||
|
</option>
|
||||||
|
<option value="pg">
|
||||||
|
PostgreSQL
|
||||||
|
</option>
|
||||||
|
<!-- <option value="mssql">
|
||||||
|
Microsoft SQL
|
||||||
|
</option>
|
||||||
|
<option value="oracledb">
|
||||||
|
Oracle DB
|
||||||
|
</option> -->
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.host"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.port') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.port"
|
||||||
|
class="form-input"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="65535"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.database" class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.database') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.database"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.user') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.user"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
:disabled="connection.ask"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.password') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.password"
|
||||||
|
class="form-input"
|
||||||
|
type="password"
|
||||||
|
:disabled="connection.ask"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.connectionSchema" class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.schema') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.schema"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
:placeholder="$t('word.all')"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12" />
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<label class="form-checkbox form-inline">
|
||||||
|
<input v-model="connection.ask" type="checkbox"><i class="form-icon" /> {{ $t('message.askCredentials') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedTab === 'ssl'" class="panel-body py-0">
|
||||||
|
<div>
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('message.enableSsl') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<label class="form-switch d-inline-block" @click.prevent="toggleSsl">
|
||||||
|
<input type="checkbox" :checked="connection.ssl">
|
||||||
|
<i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fieldset class="m-0" :disabled="isBusy || !connection.ssl">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.privateKey') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<BaseUploadInput
|
||||||
|
:value="connection.key"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="pathClear('key')"
|
||||||
|
@change="pathSelection($event, 'key')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.certificate') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<BaseUploadInput
|
||||||
|
:value="connection.cert"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="pathClear('cert')"
|
||||||
|
@change="pathSelection($event, 'cert')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.caCertificate') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<BaseUploadInput
|
||||||
|
:value="connection.ca"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="pathClear('ca')"
|
||||||
|
@change="pathSelection($event, 'ca')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.ciphers') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="connection.ciphers"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedTab === 'ssh'" class="panel-body py-0">
|
||||||
|
<div>
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('message.enableSsh') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<label class="form-switch d-inline-block" @click.prevent="toggleSsh">
|
||||||
|
<input type="checkbox" :checked="connection.ssh">
|
||||||
|
<i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fieldset class="m-0" :disabled="isBusy || !connection.ssh">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.sshHost"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.user') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.sshUser"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.password') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.sshPass"
|
||||||
|
class="form-input"
|
||||||
|
type="password"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.port') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="connection.sshPort"
|
||||||
|
class="form-input"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="65535"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.privateKey') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<BaseUploadInput
|
||||||
|
:value="connection.sshKey"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="pathClear('sshKey')"
|
||||||
|
@change="pathSelection($event, 'sshKey')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer">
|
||||||
|
<button
|
||||||
|
class="btn btn-gray mr-2 d-flex"
|
||||||
|
:class="{'loading': isTesting}"
|
||||||
|
:disabled="isBusy"
|
||||||
|
@click="startTest"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-lightning-bolt mr-1" />
|
||||||
|
{{ $t('message.testConnection') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-primary mr-2 d-flex"
|
||||||
|
:disabled="isBusy"
|
||||||
|
@click="saveConnection"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
|
{{ $t('word.save') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ModalAskCredentials
|
||||||
|
v-if="isAsking"
|
||||||
|
@close-asking="closeAsking"
|
||||||
|
@credentials="continueTest"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapActions } from 'vuex';
|
||||||
|
import customizations from 'common/customizations';
|
||||||
|
import Connection from '@/ipc-api/Connection';
|
||||||
|
import { uidGen } from 'common/libs/uidGen';
|
||||||
|
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
||||||
|
import BaseUploadInput from '@/components/BaseUploadInput';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspaceAddConnectionPanel',
|
||||||
|
components: {
|
||||||
|
ModalAskCredentials,
|
||||||
|
BaseUploadInput
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
connection: {
|
||||||
|
name: '',
|
||||||
|
client: 'mysql',
|
||||||
|
host: '127.0.0.1',
|
||||||
|
database: null,
|
||||||
|
port: null,
|
||||||
|
user: null,
|
||||||
|
password: '',
|
||||||
|
ask: false,
|
||||||
|
uid: uidGen('C'),
|
||||||
|
ssl: false,
|
||||||
|
cert: '',
|
||||||
|
key: '',
|
||||||
|
ca: '',
|
||||||
|
ciphers: '',
|
||||||
|
ssh: false,
|
||||||
|
sshHost: '',
|
||||||
|
sshUser: '',
|
||||||
|
sshPass: '',
|
||||||
|
sshKey: '',
|
||||||
|
sshPort: 22
|
||||||
|
},
|
||||||
|
isConnecting: false,
|
||||||
|
isTesting: false,
|
||||||
|
isAsking: false,
|
||||||
|
selectedTab: 'general'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
customizations () {
|
||||||
|
return customizations[this.connection.client];
|
||||||
|
},
|
||||||
|
isBusy () {
|
||||||
|
return this.isConnecting || this.isTesting;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.setDefaults();
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.$refs.firstInput) this.$refs.firstInput.focus();
|
||||||
|
}, 20);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addConnection: 'connections/addConnection',
|
||||||
|
connectWorkspace: 'workspaces/connectWorkspace',
|
||||||
|
addNotification: 'notifications/addNotification',
|
||||||
|
selectWorkspace: 'workspaces/selectWorkspace'
|
||||||
|
}),
|
||||||
|
setDefaults () {
|
||||||
|
this.connection.user = this.customizations.defaultUser;
|
||||||
|
this.connection.port = this.customizations.defaultPort;
|
||||||
|
this.connection.database = this.customizations.defaultDatabase;
|
||||||
|
},
|
||||||
|
async startConnection () {
|
||||||
|
await this.saveConnection();
|
||||||
|
this.isConnecting = true;
|
||||||
|
|
||||||
|
if (this.connection.ask)
|
||||||
|
this.isAsking = true;
|
||||||
|
else {
|
||||||
|
await this.connectWorkspace(this.connection);
|
||||||
|
this.isConnecting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async startTest () {
|
||||||
|
this.isTesting = true;
|
||||||
|
|
||||||
|
if (this.connection.ask)
|
||||||
|
this.isAsking = true;
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
const res = await Connection.makeTest(this.connection);
|
||||||
|
if (res.status === 'error')
|
||||||
|
this.addNotification({ status: 'error', message: res.response.message });
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'success', message: this.$t('message.connectionSuccessfullyMade') });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isTesting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async continueTest (credentials) { // if "Ask for credentials" is true
|
||||||
|
this.isAsking = false;
|
||||||
|
const params = Object.assign({}, this.connection, credentials);
|
||||||
|
try {
|
||||||
|
if (this.isConnecting) {
|
||||||
|
const params = Object.assign({}, this.connection, credentials);
|
||||||
|
await this.connectWorkspace(params);
|
||||||
|
this.isConnecting = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const res = await Connection.makeTest(params);
|
||||||
|
if (res.status === 'error')
|
||||||
|
this.addNotification({ status: 'error', message: res.response.message });
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'success', message: this.$t('message.connectionSuccessfullyMade') });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isTesting = false;
|
||||||
|
},
|
||||||
|
saveConnection () {
|
||||||
|
this.selectWorkspace(this.connection.uid);
|
||||||
|
return this.addConnection(this.connection);
|
||||||
|
},
|
||||||
|
closeAsking () {
|
||||||
|
this.isTesting = false;
|
||||||
|
this.isAsking = false;
|
||||||
|
},
|
||||||
|
selectTab (tab) {
|
||||||
|
this.selectedTab = tab;
|
||||||
|
},
|
||||||
|
toggleSsl () {
|
||||||
|
this.connection.ssl = !this.connection.ssl;
|
||||||
|
},
|
||||||
|
toggleSsh () {
|
||||||
|
this.connection.ssh = !this.connection.ssh;
|
||||||
|
},
|
||||||
|
pathSelection (event, name) {
|
||||||
|
const { files } = event.target;
|
||||||
|
if (!files.length) return;
|
||||||
|
|
||||||
|
this.connection[name] = files[0].path;
|
||||||
|
},
|
||||||
|
pathClear (name) {
|
||||||
|
this.connection[name] = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.connection-panel {
|
||||||
|
margin-top: 15vh;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
width: 450px;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
|
||||||
|
.panel-body {
|
||||||
|
flex: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="columns">
|
|
||||||
<div class="column col-12 empty text-light">
|
|
||||||
<div class="empty-icon">
|
|
||||||
<i class="mdi mdi-48px mdi-power-plug-off" />
|
|
||||||
</div>
|
|
||||||
<p class="empty-title h5">
|
|
||||||
{{ isConnecting ? $t('word.connecting') : $t('word.disconnected') }}
|
|
||||||
</p>
|
|
||||||
<div class="empty-action">
|
|
||||||
<button
|
|
||||||
class="btn btn-success"
|
|
||||||
:class="{'loading': isConnecting}"
|
|
||||||
@click="startConnection"
|
|
||||||
>
|
|
||||||
{{ $t('word.connect') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ModalAskCredentials
|
|
||||||
v-if="isAsking"
|
|
||||||
@close-asking="closeAsking"
|
|
||||||
@credentials="continueTest"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapActions } from 'vuex';
|
|
||||||
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'WorkspaceConnectPanel',
|
|
||||||
components: {
|
|
||||||
ModalAskCredentials
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
connection: Object
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
isConnecting: false,
|
|
||||||
isAsking: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions({
|
|
||||||
connectWorkspace: 'workspaces/connectWorkspace'
|
|
||||||
}),
|
|
||||||
async startConnection () {
|
|
||||||
this.isConnecting = true;
|
|
||||||
|
|
||||||
if (this.connection.ask)
|
|
||||||
this.isAsking = true;
|
|
||||||
else {
|
|
||||||
await this.connectWorkspace(this.connection);
|
|
||||||
this.isConnecting = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async continueTest (credentials) { // if "Ask for credentials" is true
|
|
||||||
this.isAsking = false;
|
|
||||||
const params = Object.assign({}, this.connection, credentials);
|
|
||||||
await this.connectWorkspace(params);
|
|
||||||
this.isConnecting = false;
|
|
||||||
},
|
|
||||||
closeAsking () {
|
|
||||||
this.isAsking = false;
|
|
||||||
this.isConnecting = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.empty {
|
|
||||||
height: 100%;
|
|
||||||
border-radius: 0;
|
|
||||||
background: transparent;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
500
src/renderer/components/WorkspaceEditConnectionPanel.vue
Normal file
@@ -0,0 +1,500 @@
|
|||||||
|
<template>
|
||||||
|
<div class="connection-panel">
|
||||||
|
<div class="panel">
|
||||||
|
<div class="panel-nav">
|
||||||
|
<ul class="tab tab-block">
|
||||||
|
<li
|
||||||
|
class="tab-item c-hand"
|
||||||
|
:class="{'active': selectedTab === 'general'}"
|
||||||
|
@click="selectTab('general')"
|
||||||
|
>
|
||||||
|
<a class="tab-link">{{ $t('word.general') }}</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="tab-item c-hand"
|
||||||
|
:class="{'active': selectedTab === 'ssl'}"
|
||||||
|
@click="selectTab('ssl')"
|
||||||
|
>
|
||||||
|
<a class="tab-link">{{ $t('word.ssl') }}</a>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
class="tab-item c-hand"
|
||||||
|
:class="{'active': selectedTab === 'ssh'}"
|
||||||
|
@click="selectTab('ssh')"
|
||||||
|
>
|
||||||
|
<a class="tab-link">{{ $t('word.sshTunnel') }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedTab === 'general'" class="panel-body py-0">
|
||||||
|
<div>
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<fieldset class="m-0" :disabled="isBusy">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.connectionName') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="localConnection.name"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.client') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<select v-model="localConnection.client" class="form-select">
|
||||||
|
<option value="mysql">
|
||||||
|
MySQL
|
||||||
|
</option>
|
||||||
|
<option value="maria">
|
||||||
|
MariaDB
|
||||||
|
</option>
|
||||||
|
<option value="pg">
|
||||||
|
PostgreSQL
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.host"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.port') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.port"
|
||||||
|
class="form-input"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="65535"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.database" class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.database') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.database"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.user') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.user"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
:disabled="localConnection.ask"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.password') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.password"
|
||||||
|
class="form-input"
|
||||||
|
type="password"
|
||||||
|
:disabled="localConnection.ask"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="customizations.connectionSchema" class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.schema') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.schema"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
:placeholder="$t('word.all')"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12" />
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<label class="form-checkbox form-inline">
|
||||||
|
<input v-model="localConnection.ask" type="checkbox"><i class="form-icon" /> {{ $t('message.askCredentials') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedTab === 'ssl'" class="panel-body py-0">
|
||||||
|
<div>
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('message.enableSsl') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<label class="form-switch d-inline-block" @click.prevent="toggleSsl">
|
||||||
|
<input type="checkbox" :checked="localConnection.ssl">
|
||||||
|
<i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fieldset class="m-0" :disabled="isBusy || !localConnection.ssl">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.privateKey') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<BaseUploadInput
|
||||||
|
:value="localConnection.key"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="pathClear('key')"
|
||||||
|
@change="pathSelection($event, 'key')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.certificate') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<BaseUploadInput
|
||||||
|
:value="localConnection.cert"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="pathClear('cert')"
|
||||||
|
@change="pathSelection($event, 'cert')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.caCertificate') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<BaseUploadInput
|
||||||
|
:value="localConnection.ca"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="pathClear('ca')"
|
||||||
|
@change="pathSelection($event, 'ca')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.ciphers') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
ref="firstInput"
|
||||||
|
v-model="localConnection.ciphers"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="selectedTab === 'ssh'" class="panel-body py-0">
|
||||||
|
<div>
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ $t('message.enableSsh') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<label class="form-switch d-inline-block" @click.prevent="toggleSsh">
|
||||||
|
<input type="checkbox" :checked="localConnection.ssh">
|
||||||
|
<i class="form-icon" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fieldset class="m-0" :disabled="isBusy || !localConnection.ssh">
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.hostName') }}/IP</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.sshHost"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.user') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.sshUser"
|
||||||
|
class="form-input"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.password') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.sshPass"
|
||||||
|
class="form-input"
|
||||||
|
type="password"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.port') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<input
|
||||||
|
v-model="localConnection.sshPort"
|
||||||
|
class="form-input"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
max="65535"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group columns">
|
||||||
|
<div class="column col-4 col-sm-12">
|
||||||
|
<label class="form-label">{{ $t('word.privateKey') }}</label>
|
||||||
|
</div>
|
||||||
|
<div class="column col-8 col-sm-12">
|
||||||
|
<BaseUploadInput
|
||||||
|
:value="localConnection.sshKey"
|
||||||
|
:message="$t('word.browse')"
|
||||||
|
@clear="pathClear('sshKey')"
|
||||||
|
@change="pathSelection($event, 'sshKey')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer">
|
||||||
|
<button
|
||||||
|
class="btn btn-gray mr-2 d-flex"
|
||||||
|
:class="{'loading': isTesting}"
|
||||||
|
:disabled="isBusy"
|
||||||
|
@click="startTest"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-lightning-bolt mr-1" />
|
||||||
|
{{ $t('message.testConnection') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-primary mr-2 d-flex"
|
||||||
|
:disabled="isBusy || !hasChanges"
|
||||||
|
@click="saveConnection"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-content-save mr-1" />
|
||||||
|
{{ $t('word.save') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-success d-flex"
|
||||||
|
:class="{'loading': isConnecting}"
|
||||||
|
:disabled="isBusy"
|
||||||
|
@click="startConnection"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-24px mdi-connection mr-1" />
|
||||||
|
{{ $t('word.connect') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ModalAskCredentials
|
||||||
|
v-if="isAsking"
|
||||||
|
@close-asking="closeAsking"
|
||||||
|
@credentials="continueTest"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapActions } from 'vuex';
|
||||||
|
import customizations from 'common/customizations';
|
||||||
|
import Connection from '@/ipc-api/Connection';
|
||||||
|
import ModalAskCredentials from '@/components/ModalAskCredentials';
|
||||||
|
import BaseUploadInput from '@/components/BaseUploadInput';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspaceEditConnectionPanel',
|
||||||
|
components: {
|
||||||
|
ModalAskCredentials,
|
||||||
|
BaseUploadInput
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
connection: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isConnecting: false,
|
||||||
|
isTesting: false,
|
||||||
|
isAsking: false,
|
||||||
|
localConnection: null,
|
||||||
|
selectedTab: 'general'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
customizations () {
|
||||||
|
return customizations[this.connection.client];
|
||||||
|
},
|
||||||
|
isBusy () {
|
||||||
|
return this.isConnecting || this.isTesting;
|
||||||
|
},
|
||||||
|
hasChanges () {
|
||||||
|
return JSON.stringify(this.connection) !== JSON.stringify(this.localConnection);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
connection () {
|
||||||
|
this.localConnection = JSON.parse(JSON.stringify(this.connection));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.localConnection = JSON.parse(JSON.stringify(this.connection));
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
editConnection: 'connections/editConnection',
|
||||||
|
connectWorkspace: 'workspaces/connectWorkspace',
|
||||||
|
addNotification: 'notifications/addNotification'
|
||||||
|
}),
|
||||||
|
async startConnection () {
|
||||||
|
await this.saveConnection();
|
||||||
|
this.isConnecting = true;
|
||||||
|
|
||||||
|
if (this.localConnection.ask)
|
||||||
|
this.isAsking = true;
|
||||||
|
else {
|
||||||
|
await this.connectWorkspace(this.localConnection);
|
||||||
|
this.isConnecting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async startTest () {
|
||||||
|
this.isTesting = true;
|
||||||
|
|
||||||
|
if (this.localConnection.ask)
|
||||||
|
this.isAsking = true;
|
||||||
|
else {
|
||||||
|
try {
|
||||||
|
const res = await Connection.makeTest(this.localConnection);
|
||||||
|
if (res.status === 'error')
|
||||||
|
this.addNotification({ status: 'error', message: res.response.message });
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'success', message: this.$t('message.connectionSuccessfullyMade') });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isTesting = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async continueTest (credentials) { // if "Ask for credentials" is true
|
||||||
|
this.isAsking = false;
|
||||||
|
const params = Object.assign({}, this.localConnection, credentials);
|
||||||
|
try {
|
||||||
|
if (this.isConnecting) {
|
||||||
|
const params = Object.assign({}, this.connection, credentials);
|
||||||
|
await this.connectWorkspace(params);
|
||||||
|
this.isConnecting = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const res = await Connection.makeTest(params);
|
||||||
|
if (res.status === 'error')
|
||||||
|
this.addNotification({ status: 'error', message: res.response.message });
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'success', message: this.$t('message.connectionSuccessfullyMade') });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isTesting = false;
|
||||||
|
},
|
||||||
|
saveConnection () {
|
||||||
|
return this.editConnection(this.localConnection);
|
||||||
|
},
|
||||||
|
closeAsking () {
|
||||||
|
this.isTesting = false;
|
||||||
|
this.isAsking = false;
|
||||||
|
},
|
||||||
|
selectTab (tab) {
|
||||||
|
this.selectedTab = tab;
|
||||||
|
},
|
||||||
|
toggleSsl () {
|
||||||
|
this.localConnection.ssl = !this.localConnection.ssl;
|
||||||
|
},
|
||||||
|
toggleSsh () {
|
||||||
|
this.localConnection.ssh = !this.localConnection.ssh;
|
||||||
|
},
|
||||||
|
pathSelection (event, name) {
|
||||||
|
const { files } = event.target;
|
||||||
|
if (!files.length) return;
|
||||||
|
|
||||||
|
this.localConnection[name] = files[0].path;
|
||||||
|
},
|
||||||
|
pathClear (name) {
|
||||||
|
this.localConnection[name] = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.connection-panel {
|
||||||
|
margin-top: 15vh;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
width: 450px;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
|
||||||
|
.panel-body {
|
||||||
|
flex: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
55
src/renderer/components/WorkspaceEmptyState.vue
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<template>
|
||||||
|
<div class="column col-12 empty">
|
||||||
|
<div class="empty-icon">
|
||||||
|
<img :src="require(`@/images/logo-${applicationTheme}.svg`).default" width="200">
|
||||||
|
</div>
|
||||||
|
<p class="h6 empty-subtitle">
|
||||||
|
{{ $t('message.noOpenTabs') }}
|
||||||
|
</p>
|
||||||
|
<div class="empty-action">
|
||||||
|
<button class="btn btn-gray d-flex" @click="$emit('new-tab')">
|
||||||
|
<i class="mdi mdi-24px mdi-tab-plus mr-2" />
|
||||||
|
{{ $t('message.openNewTab') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
export default {
|
||||||
|
name: 'WorkspaceEmptyState',
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
applicationTheme: 'settings/getApplicationTheme',
|
||||||
|
getWorkspace: 'workspaces/getWorkspace',
|
||||||
|
selectedWorkspace: 'workspaces/getSelected'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.selectedWorkspace);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.changeBreadcrumbs({ schema: this.workspace.breadcrumbs.schema });
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.empty {
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -5,13 +5,16 @@
|
|||||||
ref="explorebar"
|
ref="explorebar"
|
||||||
class="workspace-explorebar column"
|
class="workspace-explorebar column"
|
||||||
:style="{width: localWidth ? localWidth+'px' : ''}"
|
:style="{width: localWidth ? localWidth+'px' : ''}"
|
||||||
|
tabindex="0"
|
||||||
|
@keypress="explorebarSearch"
|
||||||
|
@keydown="explorebarSearch"
|
||||||
>
|
>
|
||||||
<div class="workspace-explorebar-header">
|
<div class="workspace-explorebar-header">
|
||||||
<span class="workspace-explorebar-title">{{ connectionName }}</span>
|
<span class="workspace-explorebar-title">{{ connectionName }}</span>
|
||||||
<span v-if="workspace.connected" class="workspace-explorebar-tools">
|
<span v-if="workspace.connectionStatus === 'connected'" class="workspace-explorebar-tools">
|
||||||
<i
|
<i
|
||||||
class="mdi mdi-18px mdi-database-plus c-hand mr-2"
|
class="mdi mdi-18px mdi-database-plus c-hand mr-2"
|
||||||
:title="$t('message.createNewDatabase')"
|
:title="$t('message.createNewSchema')"
|
||||||
@click="showNewDBModal"
|
@click="showNewDBModal"
|
||||||
/>
|
/>
|
||||||
<i
|
<i
|
||||||
@@ -21,37 +24,90 @@
|
|||||||
@click="refresh"
|
@click="refresh"
|
||||||
/>
|
/>
|
||||||
<i
|
<i
|
||||||
class="mdi mdi-18px mdi-power-plug-off c-hand"
|
class="mdi mdi-18px mdi-power c-hand"
|
||||||
:title="$t('word.disconnect')"
|
:title="$t('word.disconnect')"
|
||||||
@click="disconnectWorkspace(connection.uid)"
|
@click="disconnectWorkspace(connection.uid)"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<WorkspaceConnectPanel
|
<div class="workspace-explorebar-search">
|
||||||
v-if="!workspace.connected"
|
<div v-if="workspace.connectionStatus === 'connected'" class="has-icon-right">
|
||||||
class="workspace-explorebar-body"
|
<input
|
||||||
:connection="connection"
|
ref="searchInput"
|
||||||
/>
|
v-model="searchTerm"
|
||||||
<div v-else class="workspace-explorebar-body">
|
class="form-input input-sm"
|
||||||
<WorkspaceExploreBarDatabase
|
type="text"
|
||||||
|
:placeholder="$t('message.searchForElements')"
|
||||||
|
>
|
||||||
|
<i v-if="!searchTerm" class="form-icon mdi mdi-magnify mdi-18px" />
|
||||||
|
<i
|
||||||
|
v-else
|
||||||
|
class="form-icon c-hand mdi mdi-backspace mdi-18px pr-1"
|
||||||
|
@click="searchTerm = ''"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="workspace-explorebar-body" @click="$refs.explorebar.focus()">
|
||||||
|
<WorkspaceExploreBarSchema
|
||||||
v-for="db of workspace.structure"
|
v-for="db of workspace.structure"
|
||||||
:key="db.name"
|
:key="db.name"
|
||||||
:database="db"
|
:database="db"
|
||||||
:connection="connection"
|
:connection="connection"
|
||||||
@show-database-context="openDatabaseContext"
|
@show-schema-context="openSchemaContext"
|
||||||
|
@show-table-context="openTableContext"
|
||||||
|
@show-misc-context="openMiscContext"
|
||||||
|
@show-misc-folder-context="openMiscFolderContext"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ModalNewDatabase
|
<ModalNewSchema
|
||||||
v-if="isNewDBModal"
|
v-if="isNewDBModal"
|
||||||
@close="hideNewDBModal"
|
@close="hideNewDBModal"
|
||||||
@reload="refresh"
|
@reload="refresh"
|
||||||
/>
|
/>
|
||||||
<DatabaseContext
|
<DatabaseContext
|
||||||
v-if="isDatabaseContext"
|
v-if="isDatabaseContext"
|
||||||
:selected-database="selectedDatabase"
|
:selected-schema="selectedSchema"
|
||||||
:context-event="databaseContextEvent"
|
:context-event="databaseContextEvent"
|
||||||
@close-context="closeDatabaseContext"
|
@close-context="closeDatabaseContext"
|
||||||
|
@open-create-table-tab="openCreateElementTab('table')"
|
||||||
|
@open-create-view-tab="openCreateElementTab('view')"
|
||||||
|
@open-create-trigger-tab="openCreateElementTab('trigger')"
|
||||||
|
@open-create-routine-tab="openCreateElementTab('routine')"
|
||||||
|
@open-create-function-tab="openCreateElementTab('function')"
|
||||||
|
@open-create-trigger-function-tab="openCreateElementTab('trigger-function')"
|
||||||
|
@open-create-scheduler-tab="openCreateElementTab('scheduler')"
|
||||||
|
@reload="refresh"
|
||||||
|
/>
|
||||||
|
<TableContext
|
||||||
|
v-if="isTableContext"
|
||||||
|
:selected-schema="selectedSchema"
|
||||||
|
:selected-table="selectedTable"
|
||||||
|
:context-event="tableContextEvent"
|
||||||
|
@delete-table="deleteTable"
|
||||||
|
@duplicate-table="duplicateTable"
|
||||||
|
@close-context="closeTableContext"
|
||||||
|
@reload="refresh"
|
||||||
|
/>
|
||||||
|
<MiscContext
|
||||||
|
v-if="isMiscContext"
|
||||||
|
:selected-misc="selectedMisc"
|
||||||
|
:selected-schema="selectedSchema"
|
||||||
|
:context-event="miscContextEvent"
|
||||||
|
@close-context="closeMiscContext"
|
||||||
|
@reload="refresh"
|
||||||
|
/>
|
||||||
|
<MiscFolderContext
|
||||||
|
v-if="isMiscFolderContext"
|
||||||
|
:selected-misc="selectedMisc"
|
||||||
|
:selected-schema="selectedSchema"
|
||||||
|
:context-event="miscContextEvent"
|
||||||
|
@open-create-trigger-tab="openCreateElementTab('trigger')"
|
||||||
|
@open-create-routine-tab="openCreateElementTab('routine')"
|
||||||
|
@open-create-function-tab="openCreateElementTab('function')"
|
||||||
|
@open-create-trigger-function-tab="openCreateElementTab('trigger-function')"
|
||||||
|
@open-create-scheduler-tab="openCreateElementTab('scheduler')"
|
||||||
|
@close-context="closeMiscFolderContext"
|
||||||
@reload="refresh"
|
@reload="refresh"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -59,19 +115,28 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
import _ from 'lodash';
|
|
||||||
import WorkspaceConnectPanel from '@/components/WorkspaceConnectPanel';
|
import Tables from '@/ipc-api/Tables';
|
||||||
import WorkspaceExploreBarDatabase from '@/components/WorkspaceExploreBarDatabase';
|
import Views from '@/ipc-api/Views';
|
||||||
import DatabaseContext from '@/components/WorkspaceExploreBarDatabaseContext';
|
import Functions from '@/ipc-api/Functions';
|
||||||
import ModalNewDatabase from '@/components/ModalNewDatabase';
|
import Schedulers from '@/ipc-api/Schedulers';
|
||||||
|
|
||||||
|
import WorkspaceExploreBarSchema from '@/components/WorkspaceExploreBarSchema';
|
||||||
|
import DatabaseContext from '@/components/WorkspaceExploreBarSchemaContext';
|
||||||
|
import TableContext from '@/components/WorkspaceExploreBarTableContext';
|
||||||
|
import MiscContext from '@/components/WorkspaceExploreBarMiscContext';
|
||||||
|
import MiscFolderContext from '@/components/WorkspaceExploreBarMiscFolderContext';
|
||||||
|
import ModalNewSchema from '@/components/ModalNewSchema';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WorkspaceExploreBar',
|
name: 'WorkspaceExploreBar',
|
||||||
components: {
|
components: {
|
||||||
WorkspaceConnectPanel,
|
WorkspaceExploreBarSchema,
|
||||||
WorkspaceExploreBarDatabase,
|
|
||||||
DatabaseContext,
|
DatabaseContext,
|
||||||
ModalNewDatabase
|
TableContext,
|
||||||
|
MiscContext,
|
||||||
|
MiscFolderContext,
|
||||||
|
ModalNewSchema
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
connection: Object,
|
connection: Object,
|
||||||
@@ -80,14 +145,31 @@ export default {
|
|||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
isRefreshing: false,
|
isRefreshing: false,
|
||||||
|
|
||||||
isNewDBModal: false,
|
isNewDBModal: false,
|
||||||
|
isNewViewModal: false,
|
||||||
|
isNewTriggerModal: false,
|
||||||
|
isNewRoutineModal: false,
|
||||||
|
isNewFunctionModal: false,
|
||||||
|
isNewTriggerFunctionModal: false,
|
||||||
|
isNewSchedulerModal: false,
|
||||||
|
|
||||||
localWidth: null,
|
localWidth: null,
|
||||||
|
explorebarWidthInterval: null,
|
||||||
|
searchTermInterval: null,
|
||||||
isDatabaseContext: false,
|
isDatabaseContext: false,
|
||||||
isTableContext: false,
|
isTableContext: false,
|
||||||
|
isMiscContext: false,
|
||||||
|
isMiscFolderContext: false,
|
||||||
|
|
||||||
databaseContextEvent: null,
|
databaseContextEvent: null,
|
||||||
tableContextEvent: null,
|
tableContextEvent: null,
|
||||||
selectedDatabase: '',
|
miscContextEvent: null,
|
||||||
selectedTable: ''
|
|
||||||
|
selectedSchema: '',
|
||||||
|
selectedTable: null,
|
||||||
|
selectedMisc: null,
|
||||||
|
searchTerm: ''
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -101,14 +183,28 @@ export default {
|
|||||||
},
|
},
|
||||||
connectionName () {
|
connectionName () {
|
||||||
return this.getConnectionName(this.connection.uid);
|
return this.getConnectionName(this.connection.uid);
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace.customizations;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
localWidth: _.debounce(function (val) {
|
localWidth (val) {
|
||||||
this.changeExplorebarSize(val);
|
clearTimeout(this.explorebarWidthInterval);
|
||||||
}, 500),
|
|
||||||
|
this.explorebarWidthInterval = setTimeout(() => {
|
||||||
|
this.changeExplorebarSize(val);
|
||||||
|
}, 500);
|
||||||
|
},
|
||||||
isSelected (val) {
|
isSelected (val) {
|
||||||
if (val) this.localWidth = this.explorebarSize;
|
if (val) this.localWidth = this.explorebarSize;
|
||||||
|
},
|
||||||
|
searchTerm () {
|
||||||
|
clearTimeout(this.searchTermInterval);
|
||||||
|
|
||||||
|
this.searchTermInterval = setTimeout(() => {
|
||||||
|
this.setSearchTerm(this.searchTerm);
|
||||||
|
}, 200);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created () {
|
created () {
|
||||||
@@ -117,7 +213,7 @@ export default {
|
|||||||
mounted () {
|
mounted () {
|
||||||
const resizer = this.$refs.resizer;
|
const resizer = this.$refs.resizer;
|
||||||
|
|
||||||
resizer.addEventListener('mousedown', (e) => {
|
resizer.addEventListener('mousedown', e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
window.addEventListener('mousemove', this.resize);
|
window.addEventListener('mousemove', this.resize);
|
||||||
@@ -128,7 +224,15 @@ export default {
|
|||||||
...mapActions({
|
...mapActions({
|
||||||
disconnectWorkspace: 'workspaces/removeConnected',
|
disconnectWorkspace: 'workspaces/removeConnected',
|
||||||
refreshStructure: 'workspaces/refreshStructure',
|
refreshStructure: 'workspaces/refreshStructure',
|
||||||
changeExplorebarSize: 'settings/changeExplorebarSize'
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
|
selectTab: 'workspaces/selectTab',
|
||||||
|
newTab: 'workspaces/newTab',
|
||||||
|
removeTabs: 'workspaces/removeTabs',
|
||||||
|
setSearchTerm: 'workspaces/setSearchTerm',
|
||||||
|
addNotification: 'notifications/addNotification',
|
||||||
|
changeExplorebarSize: 'settings/changeExplorebarSize',
|
||||||
|
addLoadingElement: 'workspaces/addLoadingElement',
|
||||||
|
removeLoadingElement: 'workspaces/removeLoadingElement'
|
||||||
}),
|
}),
|
||||||
async refresh () {
|
async refresh () {
|
||||||
if (!this.isRefreshing) {
|
if (!this.isRefreshing) {
|
||||||
@@ -137,6 +241,19 @@ export default {
|
|||||||
this.isRefreshing = false;
|
this.isRefreshing = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
explorebarSearch (e) {
|
||||||
|
if (e.code === 'Backspace') {
|
||||||
|
e.preventDefault();
|
||||||
|
if (this.searchTerm.length)
|
||||||
|
this.searchTerm = this.searchTerm.slice(0, -1);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (e.key.length > 1)// Prevent non-alphanumerics
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.$refs.searchInput.focus();
|
||||||
|
},
|
||||||
resize (e) {
|
resize (e) {
|
||||||
const el = this.$refs.explorebar;
|
const el = this.$refs.explorebar;
|
||||||
let explorebarWidth = e.pageX - el.getBoundingClientRect().left;
|
let explorebarWidth = e.pageX - el.getBoundingClientRect().left;
|
||||||
@@ -153,15 +270,247 @@ export default {
|
|||||||
hideNewDBModal () {
|
hideNewDBModal () {
|
||||||
this.isNewDBModal = false;
|
this.isNewDBModal = false;
|
||||||
},
|
},
|
||||||
openDatabaseContext (payload) {
|
openCreateElementTab (element) {
|
||||||
this.isTableContext = false;
|
this.closeDatabaseContext();
|
||||||
this.selectedDatabase = payload.database;
|
this.closeMiscFolderContext();
|
||||||
|
|
||||||
|
this.newTab({
|
||||||
|
uid: this.workspace.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
elementName: '',
|
||||||
|
elementType: element,
|
||||||
|
type: `new-${element}`
|
||||||
|
});
|
||||||
|
},
|
||||||
|
openSchemaContext (payload) {
|
||||||
|
this.selectedSchema = payload.schema;
|
||||||
this.databaseContextEvent = payload.event;
|
this.databaseContextEvent = payload.event;
|
||||||
this.isDatabaseContext = true;
|
this.isDatabaseContext = true;
|
||||||
},
|
},
|
||||||
closeDatabaseContext () {
|
closeDatabaseContext () {
|
||||||
this.isDatabaseContext = false;
|
this.isDatabaseContext = false;
|
||||||
this.selectedDatabase = '';
|
},
|
||||||
|
openTableContext (payload) {
|
||||||
|
this.selectedTable = payload.table;
|
||||||
|
this.selectedSchema = payload.schema;
|
||||||
|
this.tableContextEvent = payload.event;
|
||||||
|
this.isTableContext = true;
|
||||||
|
},
|
||||||
|
closeTableContext () {
|
||||||
|
this.isTableContext = false;
|
||||||
|
},
|
||||||
|
openMiscContext (payload) {
|
||||||
|
this.selectedMisc = payload.misc;
|
||||||
|
this.selectedSchema = payload.schema;
|
||||||
|
this.miscContextEvent = payload.event;
|
||||||
|
this.isMiscContext = true;
|
||||||
|
},
|
||||||
|
openMiscFolderContext (payload) {
|
||||||
|
this.selectedMisc = payload.type;
|
||||||
|
this.selectedSchema = payload.schema;
|
||||||
|
this.miscContextEvent = payload.event;
|
||||||
|
this.isMiscFolderContext = true;
|
||||||
|
},
|
||||||
|
closeMiscContext () {
|
||||||
|
this.isMiscContext = false;
|
||||||
|
},
|
||||||
|
closeMiscFolderContext () {
|
||||||
|
this.isMiscFolderContext = false;
|
||||||
|
},
|
||||||
|
showCreateTriggerModal () {
|
||||||
|
this.closeDatabaseContext();
|
||||||
|
this.closeMiscFolderContext();
|
||||||
|
this.isNewTriggerModal = true;
|
||||||
|
},
|
||||||
|
hideCreateTriggerModal () {
|
||||||
|
this.isNewTriggerModal = false;
|
||||||
|
},
|
||||||
|
showCreateRoutineModal () {
|
||||||
|
this.closeDatabaseContext();
|
||||||
|
this.closeMiscFolderContext();
|
||||||
|
this.isNewRoutineModal = true;
|
||||||
|
},
|
||||||
|
hideCreateRoutineModal () {
|
||||||
|
this.isNewRoutineModal = false;
|
||||||
|
},
|
||||||
|
showCreateFunctionModal () {
|
||||||
|
this.closeDatabaseContext();
|
||||||
|
this.closeMiscFolderContext();
|
||||||
|
this.isNewFunctionModal = true;
|
||||||
|
},
|
||||||
|
hideCreateFunctionModal () {
|
||||||
|
this.isNewFunctionModal = false;
|
||||||
|
},
|
||||||
|
showCreateTriggerFunctionModal () {
|
||||||
|
this.closeDatabaseContext();
|
||||||
|
this.closeMiscFolderContext();
|
||||||
|
this.isNewTriggerFunctionModal = true;
|
||||||
|
},
|
||||||
|
hideCreateTriggerFunctionModal () {
|
||||||
|
this.isNewTriggerFunctionModal = false;
|
||||||
|
},
|
||||||
|
showCreateSchedulerModal () {
|
||||||
|
this.closeDatabaseContext();
|
||||||
|
this.closeMiscFolderContext();
|
||||||
|
this.isNewSchedulerModal = true;
|
||||||
|
},
|
||||||
|
hideCreateSchedulerModal () {
|
||||||
|
this.isNewSchedulerModal = false;
|
||||||
|
},
|
||||||
|
async deleteTable (payload) {
|
||||||
|
this.closeTableContext();
|
||||||
|
|
||||||
|
this.addLoadingElement({
|
||||||
|
name: payload.table.name,
|
||||||
|
schema: payload.schema,
|
||||||
|
type: 'table'
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
let res;
|
||||||
|
|
||||||
|
if (payload.table.type === 'table') {
|
||||||
|
res = await Tables.dropTable({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
table: payload.table.name,
|
||||||
|
schema: payload.schema
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (payload.table.type === 'view') {
|
||||||
|
res = await Views.dropView({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
view: payload.table.name,
|
||||||
|
schema: payload.schema
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { status, response } = res;
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
this.refresh();
|
||||||
|
|
||||||
|
this.removeTabs({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
elementName: payload.table.name,
|
||||||
|
elementType: payload.table.type,
|
||||||
|
schema: payload.schema
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.removeLoadingElement({
|
||||||
|
name: payload.table.name,
|
||||||
|
schema: payload.schema,
|
||||||
|
type: 'table'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async duplicateTable (payload) {
|
||||||
|
this.closeTableContext();
|
||||||
|
|
||||||
|
this.addLoadingElement({
|
||||||
|
name: payload.table.name,
|
||||||
|
schema: payload.schema,
|
||||||
|
type: 'table'
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Tables.duplicateTable({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
table: payload.table.name,
|
||||||
|
schema: payload.schema
|
||||||
|
});
|
||||||
|
|
||||||
|
if (status === 'success')
|
||||||
|
this.refresh();
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.removeLoadingElement({
|
||||||
|
name: payload.table.name,
|
||||||
|
schema: payload.schema,
|
||||||
|
type: 'table'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async openCreateFunctionEditor (payload) {
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
...payload
|
||||||
|
};
|
||||||
|
|
||||||
|
const { status, response } = await Functions.createFunction(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
await this.refresh();
|
||||||
|
this.changeBreadcrumbs({ schema: this.selectedSchema, function: payload.name });
|
||||||
|
|
||||||
|
this.newTab({
|
||||||
|
uid: this.workspace.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
elementName: payload.name,
|
||||||
|
elementType: 'function',
|
||||||
|
type: 'function-props'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
},
|
||||||
|
async openCreateTriggerFunctionEditor (payload) {
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
...payload
|
||||||
|
};
|
||||||
|
|
||||||
|
const { status, response } = await Functions.createTriggerFunction(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
await this.refresh();
|
||||||
|
this.changeBreadcrumbs({ schema: this.selectedSchema, triggerFunction: payload.name });
|
||||||
|
|
||||||
|
this.newTab({
|
||||||
|
uid: this.workspace.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
elementName: payload.name,
|
||||||
|
elementType: 'triggerFunction',
|
||||||
|
type: 'trigger-function-props'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
},
|
||||||
|
async openCreateSchedulerEditor (payload) {
|
||||||
|
const params = {
|
||||||
|
uid: this.connection.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
...payload
|
||||||
|
};
|
||||||
|
|
||||||
|
const { status, response } = await Schedulers.createScheduler(params);
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
await this.refresh();
|
||||||
|
this.changeBreadcrumbs({ schema: this.selectedSchema, scheduler: payload.name });
|
||||||
|
|
||||||
|
this.newTab({
|
||||||
|
uid: this.workspace.uid,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
elementName: payload.name,
|
||||||
|
elementType: 'scheduler',
|
||||||
|
type: 'scheduler-props'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -176,6 +525,11 @@ export default {
|
|||||||
height: calc(100vh - #{$excluding-size});
|
height: calc(100vh - #{$excluding-size});
|
||||||
cursor: ew-resize;
|
cursor: ew-resize;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
|
transition: background 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: rgba($primary-color, 50%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspace-explorebar {
|
.workspace-explorebar {
|
||||||
@@ -185,13 +539,15 @@ export default {
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
background: $bg-color-gray;
|
|
||||||
box-shadow: 0 0 1px 0 #000;
|
|
||||||
z-index: 8;
|
z-index: 8;
|
||||||
flex: initial;
|
flex: initial;
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
.workspace-explorebar-header {
|
.workspace-explorebar-header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.3rem;
|
padding: 0.3rem;
|
||||||
@@ -227,9 +583,40 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.workspace-explorebar-search {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 0.6rem;
|
||||||
|
height: 28px;
|
||||||
|
|
||||||
|
.has-icon-right {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.1rem;
|
||||||
|
|
||||||
|
.form-icon {
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
height: 1.2rem;
|
||||||
|
padding-left: 0.2rem;
|
||||||
|
|
||||||
|
&:focus + .form-icon {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.workspace-explorebar-body {
|
.workspace-explorebar-body {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc((100vh - 30px) - #{$excluding-size});
|
height: calc((100vh - 58px) - #{$excluding-size});
|
||||||
overflow: overlay;
|
overflow: overlay;
|
||||||
padding: 0 0.1rem;
|
padding: 0 0.1rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
<template>
|
|
||||||
<details class="accordion workspace-explorebar-database">
|
|
||||||
<summary
|
|
||||||
class="accordion-header database-name pb-0"
|
|
||||||
:class="{'text-bold': breadcrumbs.schema === database.name}"
|
|
||||||
@click="changeBreadcrumbs({schema: database.name, table:null})"
|
|
||||||
@contextmenu.prevent="showDatabaseContext($event, database.name)"
|
|
||||||
>
|
|
||||||
<i class="icon mdi mdi-18px mdi-chevron-right" />
|
|
||||||
<i class="database-icon mdi mdi-18px mdi-database mr-1" />
|
|
||||||
<span>{{ database.name }}</span>
|
|
||||||
</summary>
|
|
||||||
<div class="accordion-body">
|
|
||||||
<div class="database-tables">
|
|
||||||
<ul class="menu menu-nav pt-0">
|
|
||||||
<li
|
|
||||||
v-for="table of database.tables"
|
|
||||||
:key="table.TABLE_NAME"
|
|
||||||
class="menu-item"
|
|
||||||
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.table === table.TABLE_NAME}"
|
|
||||||
@click="changeBreadcrumbs({schema: database.name, table: table.TABLE_NAME})"
|
|
||||||
@contextmenu.prevent="showTableContext($event, table.TABLE_NAME)"
|
|
||||||
>
|
|
||||||
<a class="table-name">
|
|
||||||
<i class="table-icon mdi mdi-18px mdi-table mr-1" />
|
|
||||||
<span>{{ table.TABLE_NAME }}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</details>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapActions, mapGetters } from 'vuex';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'WorkspaceExploreBarDatabase',
|
|
||||||
props: {
|
|
||||||
database: Object,
|
|
||||||
connection: Object
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapGetters({
|
|
||||||
getWorkspace: 'workspaces/getWorkspace'
|
|
||||||
}),
|
|
||||||
breadcrumbs () {
|
|
||||||
return this.getWorkspace(this.connection.uid).breadcrumbs;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions({
|
|
||||||
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
|
||||||
}),
|
|
||||||
showDatabaseContext (event, database) {
|
|
||||||
this.$emit('show-database-context', { event, database });
|
|
||||||
},
|
|
||||||
showTableContext (table) {
|
|
||||||
this.$emit('show-table-context', table);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.workspace-explorebar-database {
|
|
||||||
.database-name,
|
|
||||||
a.table-name {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0.1rem;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
|
|
||||||
> span {
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
display: block;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: $body-font-color;
|
|
||||||
background: rgba($color: #fff, $alpha: 0.05);
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.database-icon,
|
|
||||||
.table-icon {
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-item {
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.database-tables {
|
|
||||||
margin-left: 1.2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
<template>
|
|
||||||
<BaseContextMenu
|
|
||||||
:context-event="contextEvent"
|
|
||||||
@close-context="closeContext"
|
|
||||||
>
|
|
||||||
<!-- <div class="context-element">
|
|
||||||
<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" />
|
|
||||||
</div> -->
|
|
||||||
<div class="context-element" @click="showEditModal">
|
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-pencil text-light pr-1" /> {{ $t('word.edit') }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="context-element" @click="showDeleteModal">
|
|
||||||
<span class="d-flex"><i class="mdi mdi-18px mdi-delete text-light pr-1" /> {{ $t('word.delete') }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ConfirmModal
|
|
||||||
v-if="isDeleteModal"
|
|
||||||
@confirm="deleteDatabase"
|
|
||||||
@hide="hideDeleteModal"
|
|
||||||
>
|
|
||||||
<template slot="header">
|
|
||||||
<div class="d-flex">
|
|
||||||
<i class="mdi mdi-24px mdi-database-remove mr-1" /> {{ $t('message.deleteDatabase') }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div slot="body">
|
|
||||||
<div class="mb-2">
|
|
||||||
{{ $t('message.deleteCorfirm') }} "<b>{{ selectedDatabase }}</b>"?
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ConfirmModal>
|
|
||||||
<ModalEditDatabase
|
|
||||||
v-if="isEditModal"
|
|
||||||
:selected-database="selectedDatabase"
|
|
||||||
@close="hideEditModal"
|
|
||||||
/>
|
|
||||||
</BaseContextMenu>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { mapGetters, mapActions } from 'vuex';
|
|
||||||
import BaseContextMenu from '@/components/BaseContextMenu';
|
|
||||||
import ConfirmModal from '@/components/BaseConfirmModal';
|
|
||||||
import ModalEditDatabase from '@/components/ModalEditDatabase';
|
|
||||||
import Database from '@/ipc-api/Database';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'WorkspaceExploreBarDatabaseContext',
|
|
||||||
components: {
|
|
||||||
BaseContextMenu,
|
|
||||||
ConfirmModal,
|
|
||||||
ModalEditDatabase
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
contextEvent: MouseEvent,
|
|
||||||
selectedDatabase: String
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
isDeleteModal: false,
|
|
||||||
isEditModal: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapGetters({
|
|
||||||
selectedWorkspace: 'workspaces/getSelected'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions({
|
|
||||||
deleteConnection: 'connections/deleteConnection',
|
|
||||||
showEditModal: 'application/showEditConnModal',
|
|
||||||
addNotification: 'notifications/addNotification'
|
|
||||||
}),
|
|
||||||
showDeleteModal () {
|
|
||||||
this.isDeleteModal = true;
|
|
||||||
},
|
|
||||||
hideDeleteModal () {
|
|
||||||
this.isDeleteModal = false;
|
|
||||||
},
|
|
||||||
showEditModal () {
|
|
||||||
this.isEditModal = true;
|
|
||||||
},
|
|
||||||
hideEditModal () {
|
|
||||||
this.isEditModal = false;
|
|
||||||
this.closeContext();
|
|
||||||
},
|
|
||||||
closeContext () {
|
|
||||||
this.$emit('close-context');
|
|
||||||
},
|
|
||||||
async deleteDatabase () {
|
|
||||||
try {
|
|
||||||
const { status, response } = await Database.deleteDatabase({
|
|
||||||
uid: this.selectedWorkspace,
|
|
||||||
database: this.selectedDatabase
|
|
||||||
});
|
|
||||||
|
|
||||||
if (status === 'success') {
|
|
||||||
this.closeContext();
|
|
||||||
this.$emit('reload');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
this.addNotification({ status: 'error', message: response });
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
this.addNotification({ status: 'error', message: err.stack });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
279
src/renderer/components/WorkspaceExploreBarMiscContext.vue
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
<template>
|
||||||
|
<BaseContextMenu
|
||||||
|
:context-event="contextEvent"
|
||||||
|
@close-context="closeContext"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="['procedure', 'function'].includes(selectedMisc.type)"
|
||||||
|
class="context-element"
|
||||||
|
@click="runElementCheck"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-play text-light pr-1" /> {{ $t('word.run') }}</span>
|
||||||
|
</div>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<ConfirmModal
|
||||||
|
v-if="isDeleteModal"
|
||||||
|
@confirm="deleteMisc"
|
||||||
|
@hide="hideDeleteModal"
|
||||||
|
>
|
||||||
|
<template slot="header">
|
||||||
|
<div class="d-flex">
|
||||||
|
<i class="mdi mdi-24px mdi-delete mr-1" />
|
||||||
|
<span class="cut-text">{{ deleteMessage }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div slot="body">
|
||||||
|
<div class="mb-2">
|
||||||
|
{{ $t('message.deleteCorfirm') }} "<b>{{ selectedMisc.name }}</b>"?
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ConfirmModal>
|
||||||
|
<ModalAskParameters
|
||||||
|
v-if="isAskingParameters"
|
||||||
|
:local-routine="localElement"
|
||||||
|
:client="workspace.client"
|
||||||
|
@confirm="runElement"
|
||||||
|
@close="hideAskParamsModal"
|
||||||
|
/>
|
||||||
|
</BaseContextMenu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import BaseContextMenu from '@/components/BaseContextMenu';
|
||||||
|
import ConfirmModal from '@/components/BaseConfirmModal';
|
||||||
|
import ModalAskParameters from '@/components/ModalAskParameters';
|
||||||
|
import Triggers from '@/ipc-api/Triggers';
|
||||||
|
import Routines from '@/ipc-api/Routines';
|
||||||
|
import Functions from '@/ipc-api/Functions';
|
||||||
|
import Schedulers from '@/ipc-api/Schedulers';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspaceExploreBarMiscContext',
|
||||||
|
components: {
|
||||||
|
BaseContextMenu,
|
||||||
|
ConfirmModal,
|
||||||
|
ModalAskParameters
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
contextEvent: MouseEvent,
|
||||||
|
selectedMisc: Object,
|
||||||
|
selectedSchema: String
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isDeleteModal: false,
|
||||||
|
isRunModal: false,
|
||||||
|
isAskingParameters: false,
|
||||||
|
localElement: {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.selectedWorkspace);
|
||||||
|
},
|
||||||
|
deleteMessage () {
|
||||||
|
switch (this.selectedMisc.type) {
|
||||||
|
case 'trigger':
|
||||||
|
return this.$t('message.deleteTrigger');
|
||||||
|
case 'procedure':
|
||||||
|
return this.$t('message.deleteRoutine');
|
||||||
|
case 'function':
|
||||||
|
case 'triggerFunction':
|
||||||
|
return this.$t('message.deleteFunction');
|
||||||
|
case 'scheduler':
|
||||||
|
return this.$t('message.deleteScheduler');
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
|
removeTabs: 'workspaces/removeTabs',
|
||||||
|
newTab: 'workspaces/newTab'
|
||||||
|
}),
|
||||||
|
showDeleteModal () {
|
||||||
|
this.isDeleteModal = true;
|
||||||
|
},
|
||||||
|
hideDeleteModal () {
|
||||||
|
this.isDeleteModal = false;
|
||||||
|
},
|
||||||
|
showAskParamsModal () {
|
||||||
|
this.isAskingParameters = true;
|
||||||
|
},
|
||||||
|
hideAskParamsModal () {
|
||||||
|
this.isAskingParameters = false;
|
||||||
|
this.closeContext();
|
||||||
|
},
|
||||||
|
closeContext () {
|
||||||
|
this.$emit('close-context');
|
||||||
|
},
|
||||||
|
async deleteMisc () {
|
||||||
|
try {
|
||||||
|
let res;
|
||||||
|
|
||||||
|
switch (this.selectedMisc.type) {
|
||||||
|
case 'trigger':
|
||||||
|
res = await Triggers.dropTrigger({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
trigger: this.selectedMisc.name
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'procedure':
|
||||||
|
res = await Routines.dropRoutine({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
routine: this.selectedMisc.name
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'function':
|
||||||
|
case 'triggerFunction':
|
||||||
|
res = await Functions.dropFunction({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
func: this.selectedMisc.name
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'scheduler':
|
||||||
|
res = await Schedulers.dropScheduler({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
scheduler: this.selectedMisc.name
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { status, response } = res;
|
||||||
|
|
||||||
|
if (status === 'success') {
|
||||||
|
this.removeTabs({
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
elementName: this.selectedMisc.name,
|
||||||
|
elementType: this.selectedMisc.type,
|
||||||
|
schema: this.selectedSchema
|
||||||
|
});
|
||||||
|
|
||||||
|
this.closeContext();
|
||||||
|
this.$emit('reload');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
runElementCheck () {
|
||||||
|
if (this.selectedMisc.type === 'procedure')
|
||||||
|
this.runRoutineCheck();
|
||||||
|
else if (this.selectedMisc.type === 'function')
|
||||||
|
this.runFunctionCheck();
|
||||||
|
},
|
||||||
|
runElement (params) {
|
||||||
|
if (this.selectedMisc.type === 'procedure')
|
||||||
|
this.runRoutine(params);
|
||||||
|
else if (this.selectedMisc.type === 'function')
|
||||||
|
this.runFunction(params);
|
||||||
|
},
|
||||||
|
async runRoutineCheck () {
|
||||||
|
const params = {
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
routine: this.selectedMisc.name
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Routines.getRoutineInformations(params);
|
||||||
|
if (status === 'success')
|
||||||
|
this.localElement = response;
|
||||||
|
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.localElement.parameters.length)
|
||||||
|
this.showAskParamsModal();
|
||||||
|
else
|
||||||
|
this.runRoutine();
|
||||||
|
},
|
||||||
|
runRoutine (params) {
|
||||||
|
if (!params) params = [];
|
||||||
|
|
||||||
|
let sql;
|
||||||
|
switch (this.workspace.client) { // TODO: move in a better place
|
||||||
|
case 'maria':
|
||||||
|
case 'mysql':
|
||||||
|
case 'pg':
|
||||||
|
sql = `CALL ${this.localElement.name}(${params.join(',')})`;
|
||||||
|
break;
|
||||||
|
case 'mssql':
|
||||||
|
sql = `EXEC ${this.localElement.name} ${params.join(',')}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sql = `CALL \`${this.localElement.name}\`(${params.join(',')})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.newTab({ uid: this.workspace.uid, content: sql, type: 'query', autorun: true });
|
||||||
|
this.closeContext();
|
||||||
|
},
|
||||||
|
async runFunctionCheck () {
|
||||||
|
const params = {
|
||||||
|
uid: this.selectedWorkspace,
|
||||||
|
schema: this.selectedSchema,
|
||||||
|
func: this.selectedMisc.name
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { status, response } = await Functions.getFunctionInformations(params);
|
||||||
|
if (status === 'success')
|
||||||
|
this.localElement = response;
|
||||||
|
else
|
||||||
|
this.addNotification({ status: 'error', message: response });
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
this.addNotification({ status: 'error', message: err.stack });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.localElement.parameters.length)
|
||||||
|
this.showAskParamsModal();
|
||||||
|
else
|
||||||
|
this.runFunction();
|
||||||
|
},
|
||||||
|
runFunction (params) {
|
||||||
|
if (!params) params = [];
|
||||||
|
|
||||||
|
let sql;
|
||||||
|
switch (this.workspace.client) { // TODO: move in a better place
|
||||||
|
case 'maria':
|
||||||
|
case 'mysql':
|
||||||
|
sql = `SELECT \`${this.localElement.name}\` (${params.join(',')})`;
|
||||||
|
break;
|
||||||
|
case 'pg':
|
||||||
|
sql = `SELECT ${this.localElement.name}(${params.join(',')})`;
|
||||||
|
break;
|
||||||
|
case 'mssql':
|
||||||
|
sql = `SELECT ${this.localElement.name} ${params.join(',')}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sql = `SELECT \`${this.localElement.name}\` (${params.join(',')})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.newTab({ uid: this.workspace.uid, content: sql, type: 'query', autorun: true });
|
||||||
|
this.closeContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
<template>
|
||||||
|
<BaseContextMenu
|
||||||
|
:context-event="contextEvent"
|
||||||
|
@close-context="closeContext"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="selectedMisc === 'trigger'"
|
||||||
|
class="context-element"
|
||||||
|
@click="$emit('open-create-trigger-tab')"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ $t('message.createNewTrigger') }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="selectedMisc === 'procedure'"
|
||||||
|
class="context-element"
|
||||||
|
@click="$emit('open-create-routine-tab')"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-sync-circle text-light pr-1" /> {{ $t('message.createNewRoutine') }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="selectedMisc === 'function'"
|
||||||
|
class="context-element"
|
||||||
|
@click="$emit('open-create-function-tab')"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box text-light pr-1" /> {{ $t('message.createNewFunction') }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="selectedMisc === 'triggerFunction'"
|
||||||
|
class="context-element"
|
||||||
|
@click="$emit('open-create-trigger-function-tab')"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-cog-clockwise text-light pr-1" /> {{ $t('message.createNewFunction') }}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="selectedMisc === 'scheduler'"
|
||||||
|
class="context-element"
|
||||||
|
@click="$emit('open-create-scheduler-tab')"
|
||||||
|
>
|
||||||
|
<span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ $t('message.createNewScheduler') }}</span>
|
||||||
|
</div>
|
||||||
|
</BaseContextMenu>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapGetters, mapActions } from 'vuex';
|
||||||
|
import BaseContextMenu from '@/components/BaseContextMenu';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspaceExploreBarMiscContext',
|
||||||
|
components: {
|
||||||
|
BaseContextMenu
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
contextEvent: MouseEvent,
|
||||||
|
selectedMisc: String,
|
||||||
|
selectedSchema: String
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
localElement: {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
selectedWorkspace: 'workspaces/getSelected',
|
||||||
|
getWorkspace: 'workspaces/getWorkspace'
|
||||||
|
}),
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.selectedWorkspace);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
addNotification: 'notifications/addNotification',
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
|
||||||
|
}),
|
||||||
|
showDeleteModal () {
|
||||||
|
this.isDeleteModal = true;
|
||||||
|
},
|
||||||
|
hideDeleteModal () {
|
||||||
|
this.isDeleteModal = false;
|
||||||
|
},
|
||||||
|
showAskParamsModal () {
|
||||||
|
this.isAskingParameters = true;
|
||||||
|
},
|
||||||
|
hideAskParamsModal () {
|
||||||
|
this.isAskingParameters = false;
|
||||||
|
this.closeContext();
|
||||||
|
},
|
||||||
|
closeContext () {
|
||||||
|
this.$emit('close-context');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
533
src/renderer/components/WorkspaceExploreBarSchema.vue
Normal file
@@ -0,0 +1,533 @@
|
|||||||
|
<template>
|
||||||
|
<details class="accordion workspace-explorebar-database">
|
||||||
|
<summary
|
||||||
|
class="accordion-header database-name"
|
||||||
|
:class="{'text-bold': breadcrumbs.schema === database.name}"
|
||||||
|
@click="selectSchema(database.name)"
|
||||||
|
@contextmenu.prevent="showSchemaContext($event, database.name)"
|
||||||
|
>
|
||||||
|
<div v-if="isLoading" class="icon loading" />
|
||||||
|
<i v-else class="icon mdi mdi-18px mdi-chevron-right" />
|
||||||
|
<i class="database-icon mdi mdi-18px mdi-database mr-1" />
|
||||||
|
<span>{{ database.name }}</span>
|
||||||
|
</summary>
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div class="database-tables">
|
||||||
|
<ul class="menu menu-nav pt-0">
|
||||||
|
<li
|
||||||
|
v-for="table of filteredTables"
|
||||||
|
:ref="breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name) ? 'explorebar-selected' : ''"
|
||||||
|
:key="table.name"
|
||||||
|
class="menu-item"
|
||||||
|
:class="{'selected': breadcrumbs.schema === database.name && [breadcrumbs.table, breadcrumbs.view].includes(table.name)}"
|
||||||
|
@mousedown.left="selectTable({schema: database.name, table})"
|
||||||
|
@dblclick="openDataTab({schema: database.name, table})"
|
||||||
|
@contextmenu.prevent="showTableContext($event, table)"
|
||||||
|
>
|
||||||
|
<a class="table-name">
|
||||||
|
<div v-if="checkLoadingStatus(table.name, 'table')" class="icon loading mr-1" />
|
||||||
|
<i
|
||||||
|
v-else
|
||||||
|
class="table-icon mdi mdi-18px mr-1"
|
||||||
|
:class="table.type === 'view' ? 'mdi-table-eye' : 'mdi-table'"
|
||||||
|
/>
|
||||||
|
<span v-html="highlightWord(table.name)" />
|
||||||
|
</a>
|
||||||
|
<div
|
||||||
|
v-if="table.type === 'table'"
|
||||||
|
class="table-size tooltip tooltip-left mr-1"
|
||||||
|
:data-tooltip="formatBytes(table.size)"
|
||||||
|
>
|
||||||
|
<div class="pie" :style="piePercentage(table.size)" />
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="filteredTriggers.length && customizations.triggers" class="database-misc">
|
||||||
|
<details class="accordion">
|
||||||
|
<summary
|
||||||
|
class="accordion-header misc-name"
|
||||||
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.trigger}"
|
||||||
|
@contextmenu.prevent="showMiscFolderContext($event, 'trigger')"
|
||||||
|
>
|
||||||
|
<i class="misc-icon mdi mdi-18px mdi-folder-cog mr-1" />
|
||||||
|
{{ $tc('word.trigger', 2) }}
|
||||||
|
</summary>
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div>
|
||||||
|
<ul class="menu menu-nav pt-0">
|
||||||
|
<li
|
||||||
|
v-for="trigger of filteredTriggers"
|
||||||
|
:key="trigger.name"
|
||||||
|
:ref="breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name ? 'explorebar-selected' : ''"
|
||||||
|
class="menu-item"
|
||||||
|
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.trigger === trigger.name}"
|
||||||
|
@mousedown.left="selectMisc({schema: database.name, misc: trigger, type: 'trigger'})"
|
||||||
|
@dblclick="openMiscPermanentTab({schema: database.name, misc: trigger, type: 'trigger'})"
|
||||||
|
@contextmenu.prevent="showMiscContext($event, {...trigger, type: 'trigger'})"
|
||||||
|
>
|
||||||
|
<a class="table-name">
|
||||||
|
<i class="table-icon mdi mdi-table-cog mdi-18px mr-1" />
|
||||||
|
<span v-html="highlightWord(trigger.name)" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="filteredProcedures.length && customizations.routines" class="database-misc">
|
||||||
|
<details class="accordion">
|
||||||
|
<summary
|
||||||
|
class="accordion-header misc-name"
|
||||||
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.routine}"
|
||||||
|
@contextmenu.prevent="showMiscFolderContext($event, 'procedure')"
|
||||||
|
>
|
||||||
|
<i class="misc-icon mdi mdi-18px mdi-folder-sync mr-1" />
|
||||||
|
{{ $tc('word.storedRoutine', 2) }}
|
||||||
|
</summary>
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div>
|
||||||
|
<ul class="menu menu-nav pt-0">
|
||||||
|
<li
|
||||||
|
v-for="(procedure, i) of filteredProcedures"
|
||||||
|
:key="`${procedure.name}-${i}`"
|
||||||
|
:ref="breadcrumbs.schema === database.name && breadcrumbs.routine === procedure.name ? 'explorebar-selected' : ''"
|
||||||
|
class="menu-item"
|
||||||
|
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.routine === procedure.name}"
|
||||||
|
@mousedown.left="selectMisc({schema: database.name, misc: procedure, type: 'routine'})"
|
||||||
|
@dblclick="openMiscPermanentTab({schema: database.name, misc: procedure, type: 'routine'})"
|
||||||
|
@contextmenu.prevent="showMiscContext($event, {...procedure, type: 'procedure'})"
|
||||||
|
>
|
||||||
|
<a class="table-name">
|
||||||
|
<i class="table-icon mdi mdi-sync-circle mdi-18px mr-1" />
|
||||||
|
<span v-html="highlightWord(procedure.name)" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="filteredTriggerFunctions.length && customizations.triggerFunctions" class="database-misc">
|
||||||
|
<details class="accordion">
|
||||||
|
<summary
|
||||||
|
class="accordion-header misc-name"
|
||||||
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.triggerFunction}"
|
||||||
|
@contextmenu.prevent="showMiscFolderContext($event, 'triggerFunction')"
|
||||||
|
>
|
||||||
|
<i class="misc-icon mdi mdi-18px mdi-folder-refresh mr-1" />
|
||||||
|
{{ $tc('word.triggerFunction', 2) }}
|
||||||
|
</summary>
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div>
|
||||||
|
<ul class="menu menu-nav pt-0">
|
||||||
|
<li
|
||||||
|
v-for="(func, i) of filteredTriggerFunctions"
|
||||||
|
:key="`${func.name}-${i}`"
|
||||||
|
:ref="breadcrumbs.schema === database.name && breadcrumbs.triggerFunction === func.name ? 'explorebar-selected' : ''"
|
||||||
|
class="menu-item"
|
||||||
|
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.triggerFunction === func.name}"
|
||||||
|
@mousedown.left="selectMisc({schema: database.name, misc: func, type: 'triggerFunction'})"
|
||||||
|
@dblclick="openMiscPermanentTab({schema: database.name, misc: func, type: 'triggerFunction'})"
|
||||||
|
@contextmenu.prevent="showMiscContext($event, {...func, type: 'triggerFunction'})"
|
||||||
|
>
|
||||||
|
<a class="table-name">
|
||||||
|
<i class="table-icon mdi mdi-cog-clockwise mdi-18px mr-1" />
|
||||||
|
<span v-html="highlightWord(func.name)" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="filteredFunctions.length && customizations.functions" class="database-misc">
|
||||||
|
<details class="accordion">
|
||||||
|
<summary
|
||||||
|
class="accordion-header misc-name"
|
||||||
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function}"
|
||||||
|
@contextmenu.prevent="showMiscFolderContext($event, 'function')"
|
||||||
|
>
|
||||||
|
<i class="misc-icon mdi mdi-18px mdi-folder-move mr-1" />
|
||||||
|
{{ $tc('word.function', 2) }}
|
||||||
|
</summary>
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div>
|
||||||
|
<ul class="menu menu-nav pt-0">
|
||||||
|
<li
|
||||||
|
v-for="(func, i) of filteredFunctions"
|
||||||
|
:key="`${func.name}-${i}`"
|
||||||
|
:ref="breadcrumbs.schema === database.name && breadcrumbs.function === func.name ? 'explorebar-selected' : ''"
|
||||||
|
class="menu-item"
|
||||||
|
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}"
|
||||||
|
@mousedown.left="selectMisc({schema: database.name, misc: func, type: 'function'})"
|
||||||
|
@dblclick="openMiscPermanentTab({schema: database.name, misc: func, type: 'function'})"
|
||||||
|
@contextmenu.prevent="showMiscContext($event, {...func, type: 'function'})"
|
||||||
|
>
|
||||||
|
<a class="table-name">
|
||||||
|
<i class="table-icon mdi mdi-arrow-right-bold-box mdi-18px mr-1" />
|
||||||
|
<span v-html="highlightWord(func.name)" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="filteredSchedulers.length && customizations.schedulers" class="database-misc">
|
||||||
|
<details class="accordion">
|
||||||
|
<summary
|
||||||
|
class="accordion-header misc-name"
|
||||||
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler}"
|
||||||
|
@contextmenu.prevent="showMiscFolderContext($event, 'scheduler')"
|
||||||
|
>
|
||||||
|
<i class="misc-icon mdi mdi-18px mdi-folder-clock mr-1" />
|
||||||
|
{{ $tc('word.scheduler', 2) }}
|
||||||
|
</summary>
|
||||||
|
<div class="accordion-body">
|
||||||
|
<div>
|
||||||
|
<ul class="menu menu-nav pt-0">
|
||||||
|
<li
|
||||||
|
v-for="scheduler of filteredSchedulers"
|
||||||
|
:key="scheduler.name"
|
||||||
|
:ref="breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name ? 'explorebar-selected' : ''"
|
||||||
|
class="menu-item"
|
||||||
|
:class="{'selected': breadcrumbs.schema === database.name && breadcrumbs.scheduler === scheduler.name}"
|
||||||
|
@mousedown.left="selectMisc({schema: database.name, misc: scheduler, type: 'scheduler'})"
|
||||||
|
@dblclick="openMiscPermanentTab({schema: database.name, misc: scheduler, type: 'scheduler'})"
|
||||||
|
@contextmenu.prevent="showMiscContext($event, {...scheduler, type: 'scheduler'})"
|
||||||
|
>
|
||||||
|
<a class="table-name">
|
||||||
|
<i class="table-icon mdi mdi-calendar-clock mdi-18px mr-1" />
|
||||||
|
<span v-html="highlightWord(scheduler.name)" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
|
import { formatBytes } from 'common/libs/formatBytes';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkspaceExploreBarSchema',
|
||||||
|
props: {
|
||||||
|
database: Object,
|
||||||
|
connection: Object
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
isLoading: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters({
|
||||||
|
getLoadedSchemas: 'workspaces/getLoadedSchemas',
|
||||||
|
getWorkspace: 'workspaces/getWorkspace',
|
||||||
|
getSearchTerm: 'workspaces/getSearchTerm',
|
||||||
|
applicationTheme: 'settings/getApplicationTheme'
|
||||||
|
}),
|
||||||
|
searchTerm () {
|
||||||
|
return this.getSearchTerm(this.connection.uid);
|
||||||
|
},
|
||||||
|
filteredTables () {
|
||||||
|
return this.database.tables.filter(table => table.name.search(this.searchTerm) >= 0);
|
||||||
|
},
|
||||||
|
filteredTriggers () {
|
||||||
|
return this.database.triggers.filter(trigger => trigger.name.search(this.searchTerm) >= 0);
|
||||||
|
},
|
||||||
|
filteredProcedures () {
|
||||||
|
return this.database.procedures.filter(procedure => procedure.name.search(this.searchTerm) >= 0);
|
||||||
|
},
|
||||||
|
filteredFunctions () {
|
||||||
|
return this.database.functions.filter(func => func.name.search(this.searchTerm) >= 0);
|
||||||
|
},
|
||||||
|
filteredTriggerFunctions () {
|
||||||
|
return this.database.triggerFunctions
|
||||||
|
? this.database.triggerFunctions.filter(func => func.name.search(this.searchTerm) >= 0)
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
filteredSchedulers () {
|
||||||
|
return this.database.schedulers.filter(scheduler => scheduler.name.search(this.searchTerm) >= 0);
|
||||||
|
},
|
||||||
|
workspace () {
|
||||||
|
return this.getWorkspace(this.connection.uid);
|
||||||
|
},
|
||||||
|
breadcrumbs () {
|
||||||
|
return this.workspace.breadcrumbs;
|
||||||
|
},
|
||||||
|
customizations () {
|
||||||
|
return this.workspace.customizations;
|
||||||
|
},
|
||||||
|
loadedSchemas () {
|
||||||
|
return this.getLoadedSchemas(this.connection.uid);
|
||||||
|
},
|
||||||
|
maxSize () {
|
||||||
|
return this.database.tables.reduce((acc, curr) => {
|
||||||
|
if (curr.size > acc) acc = curr.size;
|
||||||
|
return acc;
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
totalSize () {
|
||||||
|
return this.database.tables.reduce((acc, curr) => acc + curr.size, 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
breadcrumbs (newVal, oldVal) {
|
||||||
|
if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const element = this.$refs['explorebar-selected'] ? this.$refs['explorebar-selected'][0] : null;
|
||||||
|
if (element) {
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
const elemTop = rect.top;
|
||||||
|
const elemBottom = rect.bottom;
|
||||||
|
const isVisible = (elemTop >= 0) && (elemBottom <= window.innerHeight);
|
||||||
|
|
||||||
|
if (!isVisible) {
|
||||||
|
element.setAttribute('tabindex', '-1');
|
||||||
|
element.focus();
|
||||||
|
element.removeAttribute('tabindex');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions({
|
||||||
|
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
|
||||||
|
addLoadedSchema: 'workspaces/addLoadedSchema',
|
||||||
|
newTab: 'workspaces/newTab',
|
||||||
|
refreshSchema: 'workspaces/refreshSchema'
|
||||||
|
}),
|
||||||
|
formatBytes,
|
||||||
|
async selectSchema (schema) {
|
||||||
|
if (!this.loadedSchemas.has(schema) && !this.isLoading) {
|
||||||
|
this.isLoading = true;
|
||||||
|
await this.refreshSchema({ uid: this.connection.uid, schema });
|
||||||
|
this.addLoadedSchema(schema);
|
||||||
|
this.isLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectTable ({ schema, table }) {
|
||||||
|
this.newTab({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
elementName: table.name,
|
||||||
|
schema: this.database.name,
|
||||||
|
type: 'temp-data',
|
||||||
|
elementType: table.type
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setBreadcrumbs({ schema, [table.type]: table.name });
|
||||||
|
},
|
||||||
|
selectMisc ({ schema, misc, type }) {
|
||||||
|
const miscTempTabs = {
|
||||||
|
trigger: 'temp-trigger-props',
|
||||||
|
triggerFunction: 'temp-trigger-function-props',
|
||||||
|
function: 'temp-function-props',
|
||||||
|
routine: 'temp-routine-props',
|
||||||
|
scheduler: 'temp-scheduler-props'
|
||||||
|
};
|
||||||
|
|
||||||
|
this.newTab({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
elementName: misc.name,
|
||||||
|
schema: this.database.name,
|
||||||
|
type: miscTempTabs[type],
|
||||||
|
elementType: type
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setBreadcrumbs({ schema, [type]: misc.name });
|
||||||
|
},
|
||||||
|
openDataTab ({ schema, table }) {
|
||||||
|
this.newTab({ uid: this.connection.uid, elementName: table.name, schema: this.database.name, type: 'data', elementType: table.type });
|
||||||
|
this.setBreadcrumbs({ schema, [table.type]: table.name });
|
||||||
|
},
|
||||||
|
openMiscPermanentTab ({ schema, misc, type }) {
|
||||||
|
const miscTabs = {
|
||||||
|
trigger: 'trigger-props',
|
||||||
|
triggerFunction: 'trigger-function-props',
|
||||||
|
function: 'function-props',
|
||||||
|
routine: 'routine-props',
|
||||||
|
scheduler: 'scheduler-props'
|
||||||
|
};
|
||||||
|
|
||||||
|
this.newTab({
|
||||||
|
uid: this.connection.uid,
|
||||||
|
elementName: misc.name,
|
||||||
|
schema: this.database.name,
|
||||||
|
type: miscTabs[type],
|
||||||
|
elementType: type
|
||||||
|
});
|
||||||
|
this.setBreadcrumbs({ schema, [type]: misc.name });
|
||||||
|
},
|
||||||
|
showSchemaContext (event, schema) {
|
||||||
|
this.$emit('show-schema-context', { event, schema });
|
||||||
|
},
|
||||||
|
showTableContext (event, table) {
|
||||||
|
this.$emit('show-table-context', { event, schema: this.database.name, table });
|
||||||
|
},
|
||||||
|
showMiscContext (event, misc) {
|
||||||
|
this.$emit('show-misc-context', { event, schema: this.database.name, misc });
|
||||||
|
},
|
||||||
|
showMiscFolderContext (event, type) {
|
||||||
|
this.$emit('show-misc-folder-context', { event, schema: this.database.name, type });
|
||||||
|
},
|
||||||
|
piePercentage (val) {
|
||||||
|
const perc = val / this.maxSize * 100;
|
||||||
|
if (this.applicationTheme === 'dark')
|
||||||
|
return { background: `conic-gradient(lime ${perc}%, white 0)` };
|
||||||
|
else
|
||||||
|
return { background: `conic-gradient(teal ${perc}%, silver 0)` };
|
||||||
|
},
|
||||||
|
setBreadcrumbs (payload) {
|
||||||
|
if (this.breadcrumbs.schema === payload.schema && this.breadcrumbs.table === payload.table) return;
|
||||||
|
this.changeBreadcrumbs(payload);
|
||||||
|
},
|
||||||
|
highlightWord (string) {
|
||||||
|
string = string.replaceAll('<', '<').replaceAll('>', '>');
|
||||||
|
|
||||||
|
if (this.searchTerm) {
|
||||||
|
const regexp = new RegExp(`(${this.searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
|
||||||
|
return string.replace(regexp, '<span class="text-primary">$1</span>');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return string;
|
||||||
|
},
|
||||||
|
checkLoadingStatus (name, type) {
|
||||||
|
return this.workspace.loadingElements.some(el =>
|
||||||
|
el.name === name &&
|
||||||
|
el.type === type &&
|
||||||
|
el.schema === this.database.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.workspace-explorebar-database {
|
||||||
|
.selected {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.database-name {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.database-name,
|
||||||
|
.misc-name,
|
||||||
|
a.table-name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.1rem 1rem 0.1rem 0.1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: block;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.database-icon,
|
||||||
|
.table-icon,
|
||||||
|
.misc-icon {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
height: 0.6rem;
|
||||||
|
width: 0.6rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.misc-name {
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0.1rem 1rem 0.1rem 0.1rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.database-name,
|
||||||
|
.misc-name {
|
||||||
|
&:hover {
|
||||||
|
border-radius: $border-radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
line-height: 1.2;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.selected {
|
||||||
|
border-radius: $border-radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.database-tables {
|
||||||
|
margin-left: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.database-misc {
|
||||||
|
margin-left: 1.6rem;
|
||||||
|
|
||||||
|
.accordion[open] .accordion-header > .misc-icon:first-child::before {
|
||||||
|
content: "\F0770";
|
||||||
|
}
|
||||||
|
|
||||||
|
.accordion-body {
|
||||||
|
margin-bottom: 0.2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-size {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
opacity: 0.2;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pie {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||