Merge pull request #238 from antares-sql/vue3-migration

Vue3 migration
This commit is contained in:
Fabio Di Stasio 2022-04-30 17:31:02 +02:00 committed by GitHub
commit 75aa299f8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
130 changed files with 4597 additions and 3924 deletions

View File

@ -7,7 +7,7 @@
"extends": [ "extends": [
"standard", "standard",
"plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended",
"plugin:vue/recommended" "plugin:vue/vue3-recommended"
], ],
"parser": "vue-eslint-parser", "parser": "vue-eslint-parser",
"parserOptions": { "parserOptions": {
@ -17,6 +17,7 @@
"requireConfigFile": false "requireConfigFile": false
}, },
"plugins": [ "plugins": [
"vue",
"@typescript-eslint" "@typescript-eslint"
], ],
"rules": { "rules": {

2
.gitignore vendored
View File

@ -1,6 +1,8 @@
.DS_Store .DS_Store
dist dist
build build
misc/*
!misc/.gitkeep
node_modules node_modules
thumbs.db thumbs.db
NOTES.md NOTES.md

2
.vscode/launch.json vendored
View File

@ -28,7 +28,7 @@
"sourceMaps": true, "sourceMaps": true,
"type": "node", "type": "node",
"timeout": 1000000 "timeout": 1000000
}, }
], ],
"compounds": [ "compounds": [
{ {

View File

@ -2,7 +2,7 @@
Antares SQL is an application based on [Electron.js](https://www.electronjs.org/) that uses [Vue.js](https://vuejs.org/) and [Spectre.css](https://picturepan2.github.io/spectre/) as frontend frameworks. Antares SQL is an application based on [Electron.js](https://www.electronjs.org/) that uses [Vue.js](https://vuejs.org/) and [Spectre.css](https://picturepan2.github.io/spectre/) as frontend frameworks.
For the build process it takes advantage of [electron-builder](https://www.electron.build/). For the build process it takes advantage of [electron-builder](https://www.electron.build/).
This application uses [Vuex](https://vuex.vuejs.org/) as application state manager and [electron-store](https://github.com/sindresorhus/electron-store) to save the various settings on disc. This application uses [Pinia🍍](https://pinia.vuejs.org/) as application state manager and [electron-store](https://github.com/sindresorhus/electron-store) to save the various settings on disc.
This guide aims to provide useful information and guidelines to everyone wants to contribute with this open-source project. This guide aims to provide useful information and guidelines to everyone wants to contribute with this open-source project.
For every other question related to this project please [contact me](https://github.com/Fabio286). For every other question related to this project please [contact me](https://github.com/Fabio286).
@ -14,7 +14,7 @@ The main files of the application are located inside `src` folder and are groupp
This folder contains small libraries, classes and objects. The purpose of `common` folder is to group together utilities used by **renderer** and **main** processes. This folder contains small libraries, classes and objects. The purpose of `common` folder is to group together utilities used by **renderer** and **main** processes.
Noteworthy is the `customizations` folder that contains clients related customizations. Those settings are merged with `default.js` that lists every option. Noteworthy is the `customizations` folder that contains clients related customizations. Those settings are merged with `default.js` that lists every option.
Client related customizations are stored on Vuex and can be accessed by `customizations` property of current workspace object, or importing `common/customizations`. Client related customizations are stored on Pinia and can be accessed by `customizations` property of current workspace object, or importing `common/customizations`.
An use case of customizations object can be the following: An use case of customizations object can be the following:
@ -62,12 +62,6 @@ The command to build Antares SQL locally is `npm run build:local`.
- [Order of words in component names](https://vuejs.org/v2/style-guide/#Order-of-words-in-component-names-strongly-recommended). - [Order of words in component names](https://vuejs.org/v2/style-guide/#Order-of-words-in-component-names-strongly-recommended).
- **kebab-case** in templates for property and event names. - **kebab-case** in templates for property and event names.
### Vuex
- **snake_case** for state names.
- **camelCase** for getter and action names.
- **SNAKE_CASE (all caps)** for mutation names.
### Code Style ### Code Style
The project includes [ESlint](https://eslint.org/) and [StyleLint](https://stylelint.io/) config files with style rules. I recommend to set the lint on-save option in your code editor. The project includes [ESlint](https://eslint.org/) and [StyleLint](https://stylelint.io/) config files with style rules. I recommend to set the lint on-save option in your code editor.

0
misc/.gitkeep Normal file
View File

View File

@ -18,8 +18,10 @@
"rebuild:electron": "rimraf ./dist && npm run postinstall", "rebuild:electron": "rimraf ./dist && npm run postinstall",
"release": "standard-version", "release": "standard-version",
"release:pre": "npm run release -- --prerelease alpha", "release:pre": "npm run release -- --prerelease alpha",
"postinstall": "electron-builder install-app-deps", "devtools:install": "node scripts/devtoolsInstaller",
"test": "npm run compile && node tests/app.spec.js", "postinstall": "electron-builder install-app-deps && npm run devtools:install",
"test": "npm run compile && npm run test:dry",
"test:dry": "xvfb-maybe -- playwright test",
"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",
"contributors:add": "all-contributors add", "contributors:add": "all-contributors add",
@ -27,6 +29,9 @@
}, },
"author": "Fabio Di Stasio <fabio286@gmail.com>", "author": "Fabio Di Stasio <fabio286@gmail.com>",
"main": "./dist/main.js", "main": "./dist/main.js",
"antares": {
"devtoolsId": "nhdogjmejiglipccpnnnanhbledajbpd"
},
"build": { "build": {
"appId": "com.fabio286.antares", "appId": "com.fabio286.antares",
"artifactName": "${productName}-${version}-${os}_${arch}.${ext}", "artifactName": "${productName}-${version}-${os}_${arch}.${ext}",
@ -115,7 +120,7 @@
"electron-store": "^8.0.1", "electron-store": "^8.0.1",
"electron-updater": "^4.6.1", "electron-updater": "^4.6.1",
"electron-window-state": "^5.0.3", "electron-window-state": "^5.0.3",
"faker": "^5.5.3", "encoding": "^0.1.13",
"leaflet": "^1.7.1", "leaflet": "^1.7.1",
"marked": "^4.0.0", "marked": "^4.0.0",
"moment": "^2.29.1", "moment": "^2.29.1",
@ -123,24 +128,27 @@
"pg": "^8.7.1", "pg": "^8.7.1",
"pg-query-stream": "^4.2.3", "pg-query-stream": "^4.2.3",
"pgsql-ast-parser": "^7.2.1", "pgsql-ast-parser": "^7.2.1",
"pinia": "^2.0.13",
"source-map-support": "^0.5.20", "source-map-support": "^0.5.20",
"spectre.css": "^0.5.9", "spectre.css": "^0.5.9",
"sql-formatter": "^4.0.2", "sql-formatter": "^4.0.2",
"ssh2-promise": "^1.0.2", "ssh2-promise": "^1.0.2",
"v-mask": "^2.3.0", "v-mask": "^2.3.0",
"vue-i18n": "^8.26.5", "vue": "^3.2.33",
"vuedraggable": "^2.24.3", "vue-i18n": "^9.1.9",
"vuex": "^3.6.2" "vuedraggable": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "^7.15.7", "@babel/eslint-parser": "^7.15.7",
"@babel/preset-env": "^7.15.8", "@babel/preset-env": "^7.15.8",
"@babel/preset-typescript": "^7.16.7", "@babel/preset-typescript": "^7.16.7",
"@playwright/test": "^1.21.1",
"@types/better-sqlite3": "^7.5.0", "@types/better-sqlite3": "^7.5.0",
"@types/node": "^17.0.23", "@types/node": "^17.0.23",
"@types/pg": "^8.6.5", "@types/pg": "^8.6.5",
"@typescript-eslint/eslint-plugin": "^5.18.0", "@typescript-eslint/eslint-plugin": "^5.18.0",
"@typescript-eslint/parser": "^5.18.0", "@typescript-eslint/parser": "^5.18.0",
"@vue/compiler-sfc": "^3.2.33",
"all-contributors-cli": "^6.20.0", "all-contributors-cli": "^6.20.0",
"babel-loader": "^8.2.3", "babel-loader": "^8.2.3",
"chalk": "^4.1.2", "chalk": "^4.1.2",
@ -148,7 +156,6 @@
"css-loader": "^6.5.0", "css-loader": "^6.5.0",
"electron": "^17.0.1", "electron": "^17.0.1",
"electron-builder": "^22.14.11", "electron-builder": "^22.14.11",
"electron-devtools-installer": "^3.2.0",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-config-standard": "^16.0.3", "eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.24.2", "eslint-plugin-import": "^2.24.2",
@ -159,7 +166,8 @@
"html-webpack-plugin": "^5.5.0", "html-webpack-plugin": "^5.5.0",
"mini-css-extract-plugin": "~2.4.5", "mini-css-extract-plugin": "~2.4.5",
"node-loader": "^2.0.0", "node-loader": "^2.0.0",
"playwright": "^1.18.1", "playwright": "^1.21.1",
"playwright-core": "^1.21.1",
"progress-webpack-plugin": "^1.0.12", "progress-webpack-plugin": "^1.0.12",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"sass": "^1.42.1", "sass": "^1.42.1",
@ -172,12 +180,12 @@
"tree-kill": "^1.2.2", "tree-kill": "^1.2.2",
"ts-loader": "^9.2.8", "ts-loader": "^9.2.8",
"typescript": "^4.6.3", "typescript": "^4.6.3",
"vue": "^2.6.14", "unzip-crx-3": "^0.2.0",
"vue-eslint-parser": "^8.3.0", "vue-eslint-parser": "^8.3.0",
"vue-loader": "^15.9.8", "vue-loader": "^16.8.3",
"vue-template-compiler": "^2.6.14",
"webpack": "^5.60.0", "webpack": "^5.60.0",
"webpack-cli": "^4.9.1", "webpack-cli": "^4.9.1",
"webpack-dev-server": "^4.4.0" "webpack-dev-server": "^4.4.0",
"xvfb-maybe": "^0.2.1"
} }
} }

View File

@ -1,5 +1,6 @@
process.env.NODE_ENV = 'development'; process.env.NODE_ENV = 'development';
// process.env.ELECTRON_ENABLE_LOGGING = true // process.env.ELECTRON_ENABLE_LOGGING = true
process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = false;
const chalk = require('chalk'); const chalk = require('chalk');
const electron = require('electron'); const electron = require('electron');
@ -19,7 +20,7 @@ let manualRestart = null;
const remoteDebugging = process.argv.includes('--remote-debug'); const remoteDebugging = process.argv.includes('--remote-debug');
if (remoteDebugging) { if (remoteDebugging) {
// disable dvtools open in electron // disable devtools open in electron
process.env.RENDERER_REMOTE_DEBUGGING = true; process.env.RENDERER_REMOTE_DEBUGGING = true;
} }

View File

@ -0,0 +1,48 @@
// @ts-check
const fs = require('fs');
const path = require('path');
const https = require('https');
const unzip = require('unzip-crx-3');
const { antares } = require('../package.json');
const extensionID = antares.devtoolsId;
const destFolder = path.resolve(__dirname, `../misc/${extensionID}`);
const filePath = path.resolve(__dirname, `${destFolder}${extensionID}.crx`);
const fileUrl = `https://clients2.google.com/service/update2/crx?response=redirect&acceptformat=crx2,crx3&x=id%3D${extensionID}%26uc&prodversion=32`;
const fileStream = fs.createWriteStream(filePath);
const downloadFile = url => {
return new Promise((resolve, reject) => {
const request = https.get(url);
request.on('response', response => {
if (response.statusCode && response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
return downloadFile(response.headers.location)
.then(resolve)
.catch(reject);
}
response.pipe(fileStream);
response.on('close', () => {
console.log('Devtools download completed!');
resolve();
});
response.on('error', reject);
});
request.on('error', reject);
request.end();
});
};
(async () => {
try {
await downloadFile(fileUrl);
await unzip(filePath, destFolder);
fs.unlinkSync(filePath);
fs.unlinkSync(`${destFolder}/package.json`);// <- Avoid to display annoyng npm script in vscode
}
catch (error) {
console.log(error);
}
})();

View File

@ -43,4 +43,4 @@ export function mimeFromHex (hex) {
} }
} }
} }
}; }

View File

@ -5,4 +5,4 @@
*/ */
export function uidGen (prefix) { export function uidGen (prefix) {
return (prefix ? `${prefix}:` : '') + Math.random().toString(36).substr(2, 9).toUpperCase(); return (prefix ? `${prefix}:` : '') + Math.random().toString(36).substr(2, 9).toUpperCase();
}; }

View File

@ -10,7 +10,7 @@ export default () => {
event.returnValue = key; event.returnValue = key;
}); });
ipcMain.handle('showOpenDialog', (event, options) => { ipcMain.handle('show-open-dialog', (event, options) => {
return dialog.showOpenDialog(options); return dialog.showOpenDialog(options);
}); });

View File

@ -135,9 +135,6 @@ export default (connections: {[key: string]: antares.Client}) => {
await connection.connect(); await connection.connect();
// TODO: temporary
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const structure = await connection.getStructure(new Set()); const structure = await connection.getStructure(new Set());
connections[conn.uid] = connection; connections[conn.uid] = connection;

View File

@ -47,22 +47,8 @@ async function createMainWindow () {
remoteMain.enable(window.webContents); remoteMain.enable(window.webContents);
try { try {
if (isDevelopment) { if (isDevelopment)
const { default: installExtension, VUEJS3_DEVTOOLS } = require('electron-devtools-installer');
const options = {
loadExtensionOptions: { allowFileAccess: true }
};
try {
const name = await installExtension(VUEJS3_DEVTOOLS, options);
console.log(`Added Extension: ${name}`);
}
catch (err) {
console.log('An error occurred: ', err);
}
await window.loadURL('http://localhost:9080'); await window.loadURL('http://localhost:9080');
}
else { else {
const indexPath = path.resolve(__dirname, 'index.html'); const indexPath = path.resolve(__dirname, 'index.html');
await window.loadFile(indexPath); await window.loadFile(indexPath);
@ -109,8 +95,8 @@ else {
mainWindow = await createMainWindow(); mainWindow = await createMainWindow();
createAppMenu(); createAppMenu();
// if (isDevelopment) if (isDevelopment)
// mainWindow.webContents.openDevTools(); mainWindow.webContents.openDevTools();
process.on('uncaughtException', error => { process.on('uncaughtException', error => {
mainWindow.webContents.send('unhandled-exception', error); mainWindow.webContents.send('unhandled-exception', error);
@ -120,6 +106,14 @@ else {
mainWindow.webContents.send('unhandled-exception', error); mainWindow.webContents.send('unhandled-exception', error);
}); });
}); });
app.on('browser-window-created', (event, window) => {
if (isDevelopment) {
const { antares } = require('../../package.json');
const extensionPath = path.resolve(__dirname, `../../misc/${antares.devtoolsId}`);
window.webContents.session.loadExtension(extensionPath, { allowFileAccess: true }).catch(console.error);
}
});
} }
function createAppMenu () { function createAppMenu () {

View File

@ -25,37 +25,56 @@
</template> </template>
<script> <script>
import { mapActions, mapGetters } from 'vuex'; import { defineAsyncComponent } from 'vue';
import { storeToRefs } from 'pinia';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import { Menu, getCurrentWindow } from '@electron/remote'; import { Menu, getCurrentWindow } from '@electron/remote';
import { useApplicationStore } from '@/stores/application';
import { useConnectionsStore } from '@/stores/connections';
import { useSettingsStore } from '@/stores/settings';
import { useWorkspacesStore } from '@/stores/workspaces';
import TheSettingBar from '@/components/TheSettingBar';
export default { export default {
name: 'App', name: 'App',
components: { components: {
TheTitleBar: () => import(/* webpackChunkName: "TheTitleBar" */'@/components/TheTitleBar'), TheTitleBar: defineAsyncComponent(() => import(/* webpackChunkName: "TheTitleBar" */'@/components/TheTitleBar')),
TheSettingBar: () => import(/* webpackChunkName: "TheSettingBar" */'@/components/TheSettingBar'), TheSettingBar,
TheFooter: () => import(/* webpackChunkName: "TheFooter" */'@/components/TheFooter'), TheFooter: defineAsyncComponent(() => import(/* webpackChunkName: "TheFooter" */'@/components/TheFooter')),
TheNotificationsBoard: () => import(/* webpackChunkName: "TheNotificationsBoard" */'@/components/TheNotificationsBoard'), TheNotificationsBoard: defineAsyncComponent(() => import(/* webpackChunkName: "TheNotificationsBoard" */'@/components/TheNotificationsBoard')),
Workspace: () => import(/* webpackChunkName: "Workspace" */'@/components/Workspace'), Workspace: defineAsyncComponent(() => import(/* webpackChunkName: "Workspace" */'@/components/Workspace')),
WorkspaceAddConnectionPanel: () => import(/* webpackChunkName: "WorkspaceAddConnectionPanel" */'@/components/WorkspaceAddConnectionPanel'), WorkspaceAddConnectionPanel: defineAsyncComponent(() => import(/* webpackChunkName: "WorkspaceAddConnectionPanel" */'@/components/WorkspaceAddConnectionPanel')),
ModalSettings: () => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings'), ModalSettings: defineAsyncComponent(() => import(/* webpackChunkName: "ModalSettings" */'@/components/ModalSettings')),
TheScratchpad: () => import(/* webpackChunkName: "TheScratchpad" */'@/components/TheScratchpad'), TheScratchpad: defineAsyncComponent(() => import(/* webpackChunkName: "TheScratchpad" */'@/components/TheScratchpad')),
BaseTextEditor: () => import(/* webpackChunkName: "BaseTextEditor" */'@/components/BaseTextEditor') BaseTextEditor: defineAsyncComponent(() => import(/* webpackChunkName: "BaseTextEditor" */'@/components/BaseTextEditor'))
}, },
data () { setup () {
return {}; const applicationStore = useApplicationStore();
}, const connectionsStore = useConnectionsStore();
computed: { const settingsStore = useSettingsStore();
...mapGetters({ const workspacesStore = useWorkspacesStore();
selectedWorkspace: 'workspaces/getSelected',
isLoading: 'application/isLoading', const {
isSettingModal: 'application/isSettingModal', isLoading,
isScratchpad: 'application/isScratchpad', isSettingModal,
connections: 'connections/getConnections', isScratchpad
applicationTheme: 'settings/getApplicationTheme', } = storeToRefs(applicationStore);
disableBlur: 'settings/getDisableBlur', const { connections } = storeToRefs(connectionsStore);
isUnsavedDiscardModal: 'workspaces/isUnsavedDiscardModal' const { applicationTheme, disableBlur } = storeToRefs(settingsStore);
}) const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { checkVersionUpdate } = applicationStore;
return {
isLoading,
isSettingModal,
isScratchpad,
checkVersionUpdate,
connections,
applicationTheme,
disableBlur,
selectedWorkspace
};
}, },
mounted () { mounted () {
ipcRenderer.send('check-for-updates'); ipcRenderer.send('check-for-updates');
@ -97,12 +116,6 @@ export default {
node = node.parentNode; node = node.parentNode;
} }
}); });
},
methods: {
...mapActions({
showNewConnModal: 'application/showNewConnModal',
checkVersionUpdate: 'application/checkVersionUpdate'
})
} }
}; };
</script> </script>

View File

@ -1,44 +1,48 @@
<template> <template>
<div class="modal active" :class="modalSizeClass"> <div class="dummy-wrapper">
<a class="modal-overlay" @click="hideModal" /> <Teleport to="#window-content">
<div class="modal-container"> <div class="modal active" :class="modalSizeClass">
<div v-if="hasHeader" class="modal-header pl-2"> <a class="modal-overlay" @click="hideModal" />
<div class="modal-title h6"> <div class="modal-container">
<slot name="header" /> <div v-if="hasHeader" class="modal-header pl-2">
</div> <div class="modal-title h6">
<a class="btn btn-clear float-right" @click="hideModal" /> <slot name="header" />
</div> </div>
<div v-if="hasDefault" class="modal-header"> <a class="btn btn-clear float-right" @click="hideModal" />
<div class="modal-title h6"> </div>
<slot /> <div v-if="hasDefault" class="modal-header">
</div> <div class="modal-title h6">
<a class="btn btn-clear float-right" @click="hideModal" /> <slot />
</div> </div>
<div v-if="hasBody" class="modal-body"> <a class="btn btn-clear float-right" @click="hideModal" />
<a </div>
v-if="!hasHeader && !hasDefault" <div v-if="hasBody" class="modal-body pb-0">
class="btn btn-clear float-right" <a
@click="hideModal" v-if="!hasHeader && !hasDefault"
/> class="btn btn-clear float-right"
<div class="content"> @click="hideModal"
<slot name="body" /> />
<div class="content">
<slot name="body" />
</div>
</div>
<div v-if="!hideFooter" class="modal-footer">
<button
class="btn btn-primary mr-2"
@click.stop="confirmModal"
>
{{ confirmText || $t('word.confirm') }}
</button>
<button
class="btn btn-link"
@click="hideModal"
>
{{ cancelText || $t('word.cancel') }}
</button>
</div>
</div> </div>
</div> </div>
<div v-if="!hideFooter" class="modal-footer"> </Teleport>
<button
class="btn btn-primary mr-2"
@click.stop="confirmModal"
>
{{ confirmText || $t('word.confirm') }}
</button>
<button
class="btn btn-link"
@click="hideModal"
>
{{ cancelText || $t('word.cancel') }}
</button>
</div>
</div>
</div> </div>
</template> </template>
@ -58,6 +62,7 @@ export default {
confirmText: String, confirmText: String,
cancelText: String cancelText: String
}, },
emits: ['confirm', 'hide'],
computed: { computed: {
hasHeader () { hasHeader () {
return !!this.$slots.header; return !!this.$slots.header;
@ -81,7 +86,7 @@ export default {
created () { created () {
window.addEventListener('keydown', this.onKey); window.addEventListener('keydown', this.onKey);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {

View File

@ -21,6 +21,7 @@ export default {
props: { props: {
contextEvent: MouseEvent contextEvent: MouseEvent
}, },
emits: ['close-context'],
data () { data () {
return { return {
contextSize: null, contextSize: null,
@ -61,7 +62,7 @@ export default {
if (this.$refs.contextContent) if (this.$refs.contextContent)
this.contextSize = this.$refs.contextContent.getBoundingClientRect(); this.contextSize = this.$refs.contextContent.getBoundingClientRect();
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {

View File

@ -27,6 +27,7 @@ export default {
default: '' default: ''
} }
}, },
emits: ['close'],
data () { data () {
return { return {
isExpanded: false isExpanded: false

View File

@ -11,13 +11,15 @@
<script> <script>
import * as ace from 'ace-builds'; import * as ace from 'ace-builds';
import { storeToRefs } from 'pinia';
import 'ace-builds/webpack-resolver'; import 'ace-builds/webpack-resolver';
import { mapGetters } from 'vuex'; import { useSettingsStore } from '@/stores/settings';
import { uidGen } from 'common/libs/uidGen';
export default { export default {
name: 'BaseTextEditor', name: 'BaseTextEditor',
props: { props: {
value: String, modelValue: String,
mode: { type: String, default: 'text' }, mode: { type: String, default: 'text' },
editorClass: { type: String, default: '' }, editorClass: { type: String, default: '' },
autoFocus: { type: Boolean, default: false }, autoFocus: { type: Boolean, default: false },
@ -25,20 +27,30 @@ export default {
showLineNumbers: { type: Boolean, default: true }, showLineNumbers: { type: Boolean, default: true },
height: { type: Number, default: 200 } height: { type: Number, default: 200 }
}, },
emits: ['update:modelValue'],
setup () {
const settingsStore = useSettingsStore();
const {
editorTheme,
editorFontSize,
autoComplete,
lineWrap
} = storeToRefs(settingsStore);
return {
editorTheme,
editorFontSize,
autoComplete,
lineWrap
};
},
data () { data () {
return { return {
editor: null, editor: null,
id: null id: uidGen()
}; };
}, },
computed: {
...mapGetters({
editorTheme: 'settings/getEditorTheme',
editorFontSize: 'settings/getEditorFontSize',
autoComplete: 'settings/getAutoComplete',
lineWrap: 'settings/getLineWrap'
})
},
watch: { watch: {
mode () { mode () {
if (this.editor) if (this.editor)
@ -76,14 +88,11 @@ export default {
} }
} }
}, },
created () {
this.id = this._uid;
},
mounted () { mounted () {
this.editor = ace.edit(`editor-${this.id}`, { this.editor = ace.edit(`editor-${this.id}`, {
mode: `ace/mode/${this.mode}`, mode: `ace/mode/${this.mode}`,
theme: `ace/theme/${this.editorTheme}`, theme: `ace/theme/${this.editorTheme}`,
value: this.value || '', value: this.modelValue || '',
fontSize: '14px', fontSize: '14px',
printMargin: false, printMargin: false,
readOnly: this.readOnly, readOnly: this.readOnly,
@ -100,7 +109,7 @@ export default {
this.editor.session.on('change', () => { this.editor.session.on('change', () => {
const content = this.editor.getValue(); const content = this.editor.getValue();
this.$emit('update:value', content); this.$emit('update:modelValue', content);
}); });
if (this.autoFocus) { if (this.autoFocus) {

View File

@ -22,6 +22,7 @@ export default {
default: '' default: ''
} }
}, },
emits: ['close'],
data () { data () {
return { return {
isVisible: false isVisible: false

View File

@ -4,10 +4,10 @@
<i class="mdi mdi-folder-open mr-1" />{{ message }} <i class="mdi mdi-folder-open mr-1" />{{ message }}
</span> </span>
<span class="text-ellipsis file-uploader-value"> <span class="text-ellipsis file-uploader-value">
{{ value | lastPart }} {{ lastPart(modelValue) }}
</span> </span>
<i <i
v-if="value.length" v-if="modelValue.length"
class="file-uploader-reset mdi mdi-close" class="file-uploader-reset mdi mdi-close"
@click.prevent="clear" @click.prevent="clear"
/> />
@ -25,26 +25,17 @@
<script> <script>
export default { export default {
name: 'BaseUploadInput', name: 'BaseUploadInput',
filters: {
lastPart (string) {
if (!string) return '';
string = string.split(/[/\\]+/).pop();
if (string.length >= 19)
string = `...${string.slice(-19)}`;
return string;
}
},
props: { props: {
message: { message: {
default: 'Browse', default: 'Browse',
type: String type: String
}, },
value: { modelValue: {
default: '', default: '',
type: String type: String
} }
}, },
emits: ['change', 'clear'],
data () { data () {
return { return {
id: null id: null
@ -56,6 +47,14 @@ export default {
methods: { methods: {
clear () { clear () {
this.$emit('clear'); this.$emit('clear');
},
lastPart (string) {
if (!string) return '';
string = string.split(/[/\\]+/).pop();
if (string.length >= 19)
string = `...${string.slice(-19)}`;
return string;
} }
} }
}; };

View File

@ -49,7 +49,7 @@ export default {
mounted () { mounted () {
this.setScrollElement(); this.setScrollElement();
}, },
beforeDestroy () { beforeUnmount () {
this.localScrollElement.removeEventListener('scroll', this.checkScrollPosition); this.localScrollElement.removeEventListener('scroll', this.checkScrollPosition);
}, },
methods: { methods: {

View File

@ -36,8 +36,8 @@
<ForeignKeySelect <ForeignKeySelect
v-else-if="foreignKeys.includes(field.name)" v-else-if="foreignKeys.includes(field.name)"
ref="formInput" ref="formInput"
v-model="selectedValue"
class="form-select" class="form-select"
:value.sync="selectedValue"
:key-usage="getKeyUsage(field.name)" :key-usage="getKeyUsage(field.name)"
:disabled="!isChecked" :disabled="!isChecked"
/> />
@ -105,7 +105,6 @@
</template> </template>
<script> <script>
import { VueMaskDirective } from 'v-mask';
import { TEXT, LONG_TEXT, NUMBER, FLOAT, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes'; import { TEXT, LONG_TEXT, NUMBER, FLOAT, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
import BaseUploadInput from '@/components/BaseUploadInput'; import BaseUploadInput from '@/components/BaseUploadInput';
import ForeignKeySelect from '@/components/ForeignKeySelect'; import ForeignKeySelect from '@/components/ForeignKeySelect';
@ -117,9 +116,6 @@ export default {
ForeignKeySelect, ForeignKeySelect,
BaseUploadInput BaseUploadInput
}, },
directives: {
mask: VueMaskDirective
},
props: { props: {
type: String, type: String,
field: Object, field: Object,
@ -129,6 +125,7 @@ export default {
fieldLength: Number, fieldLength: Number,
fieldObj: Object fieldObj: Object
}, },
emits: ['update:modelValue'],
data () { data () {
return { return {
localType: null, localType: null,
@ -244,7 +241,7 @@ export default {
this.selectedValue = ''; this.selectedValue = '';
}, },
onChange () { onChange () {
this.$emit('update:value', { this.$emit('update:modelValue', {
group: this.selectedGroup, group: this.selectedGroup,
method: this.selectedMethod, method: this.selectedMethod,
params: this.methodParams, params: this.methodParams,

View File

@ -6,53 +6,55 @@
@change="onChange" @change="onChange"
@blur="$emit('blur')" @blur="$emit('blur')"
> >
<option v-if="!isValidDefault" :value="value"> <option v-if="!isValidDefault" :value="modelValue">
{{ value === null ? 'NULL' : value }} {{ modelValue === null ? 'NULL' : modelValue }}
</option> </option>
<option <option
v-for="row in foreignList" v-for="row in foreignList"
:key="row.foreign_column" :key="row.foreign_column"
:value="row.foreign_column" :value="row.foreign_column"
:selected="row.foreign_column === value" :selected="row.foreign_column === modelValue"
> >
{{ row.foreign_column }} {{ 'foreign_description' in row ? ` - ${row.foreign_description}` : '' | cutText }} {{ row.foreign_column }} {{ cutText('foreign_description' in row ? ` - ${row.foreign_description}` : '') }}
</option> </option>
</select> </select>
</template> </template>
<script> <script>
import { storeToRefs } from 'pinia';
import Tables from '@/ipc-api/Tables'; import Tables from '@/ipc-api/Tables';
import { mapGetters, mapActions } from 'vuex'; import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import { TEXT, LONG_TEXT } from 'common/fieldTypes'; import { TEXT, LONG_TEXT } from 'common/fieldTypes';
export default { export default {
name: 'ForeignKeySelect', name: 'ForeignKeySelect',
filters: {
cutText (val) {
if (typeof val !== 'string') return val;
return val.length > 15 ? `${val.substring(0, 15)}...` : val;
}
},
props: { props: {
value: [String, Number], modelValue: [String, Number],
keyUsage: Object, keyUsage: Object,
size: { size: {
type: String, type: String,
default: '' default: ''
} }
}, },
emits: ['update:modelValue', 'blur'],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
return { addNotification, selectedWorkspace };
},
data () { data () {
return { return {
foreignList: [] foreignList: []
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected'
}),
isValidDefault () { isValidDefault () {
if (!this.foreignList.length) return true; if (!this.foreignList.length) return true;
if (this.value === null) return false; if (this.modelValue === null) return false;
return this.foreignList.some(foreign => foreign.foreign_column.toString() === this.value.toString()); return this.foreignList.some(foreign => foreign.foreign_column.toString() === this.modelValue.toString());
} }
}, },
async created () { async created () {
@ -93,11 +95,12 @@ export default {
} }
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification'
}),
onChange () { onChange () {
this.$emit('update:value', this.$refs.editField.value); this.$emit('update:modelValue', this.$refs.editField.value);
},
cutText (val) {
if (typeof val !== 'string') return val;
return val.length > 15 ? `${val.substring(0, 15)}...` : val;
} }
} }
}; };

View File

@ -1,61 +1,64 @@
<template> <template>
<div class="modal active modal-sm"> <Teleport to="#window-content">
<a class="modal-overlay" /> <div class="modal active modal-sm">
<div class="modal-container p-0"> <a class="modal-overlay" />
<div class="modal-header pl-2"> <div class="modal-container p-0">
<div class="modal-title h6"> <div class="modal-header pl-2">
<div class="d-flex"> <div class="modal-title h6">
<i class="mdi mdi-24px mdi-key-variant mr-1" /> {{ $t('word.credentials') }} <div class="d-flex">
<i class="mdi mdi-24px mdi-key-variant mr-1" /> {{ $t('word.credentials') }}
</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">
<div class="form-group">
<div class="col-3">
<label class="form-label">{{ $t('word.user') }}</label>
</div>
<div class="col-9">
<input
ref="firstInput"
v-model="credentials.user"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-3">
<label class="form-label">{{ $t('word.password') }}</label>
</div>
<div class="col-9">
<input
v-model="credentials.password"
class="form-input"
type="password"
>
</div>
</div>
</form>
</div> </div>
</div> </div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" /> <div class="modal-footer">
</div> <button class="btn btn-primary mr-2" @click.stop="sendCredentials">
<div class="modal-body pb-0"> {{ $t('word.send') }}
<div class="content"> </button>
<form class="form-horizontal"> <button class="btn btn-link" @click.stop="closeModal">
<div class="form-group"> {{ $t('word.close') }}
<div class="col-3"> </button>
<label class="form-label">{{ $t('word.user') }}</label>
</div>
<div class="col-9">
<input
ref="firstInput"
v-model="credentials.user"
class="form-input"
type="text"
>
</div>
</div>
<div class="form-group">
<div class="col-3">
<label class="form-label">{{ $t('word.password') }}</label>
</div>
<div class="col-9">
<input
v-model="credentials.password"
class="form-input"
type="password"
>
</div>
</div>
</form>
</div> </div>
</div> </div>
<div class="modal-footer">
<button class="btn btn-primary mr-2" @click.stop="sendCredentials">
{{ $t('word.send') }}
</button>
<button class="btn btn-link" @click.stop="closeModal">
{{ $t('word.close') }}
</button>
</div>
</div> </div>
</div> </Teleport>
</template> </template>
<script> <script>
export default { export default {
name: 'ModalAskCredentials', name: 'ModalAskCredentials',
emits: ['close-asking', 'credentials'],
data () { data () {
return { return {
credentials: { credentials: {

View File

@ -36,7 +36,7 @@
class="input-group-addon field-type cut-text" class="input-group-addon field-type cut-text"
:class="typeClass(parameter.type)" :class="typeClass(parameter.type)"
> >
{{ parameter.type }} {{ parameter.length | wrapNumber }} {{ parameter.type }} {{ wrapNumber(parameter.length) }}
</span> </span>
</div> </div>
</div> </div>
@ -56,16 +56,11 @@ export default {
components: { components: {
ConfirmModal ConfirmModal
}, },
filters: {
wrapNumber (num) {
if (!num) return '';
return `(${num})`;
}
},
props: { props: {
localRoutine: Object, localRoutine: Object,
client: String client: String
}, },
emits: ['confirm', 'close'],
data () { data () {
return { return {
values: {} values: {}
@ -83,7 +78,7 @@ export default {
this.$refs.firstInput[0].focus(); this.$refs.firstInput[0].focus();
}, 20); }, 20);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
@ -122,6 +117,10 @@ export default {
e.stopPropagation(); e.stopPropagation();
if (e.key === 'Escape') if (e.key === 'Escape')
this.closeModal(); this.closeModal();
},
wrapNumber (num) {
if (!num) return '';
return `(${num})`;
} }
} }
}; };

View File

@ -26,10 +26,11 @@ export default {
components: { components: {
ConfirmModal ConfirmModal
}, },
emits: ['confirm', 'close'],
created () { created () {
window.addEventListener('keydown', this.onKey); window.addEventListener('keydown', this.onKey);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {

View File

@ -1,72 +1,76 @@
<template> <template>
<div class="modal active"> <Teleport to="#window-content">
<a class="modal-overlay" @click.stop="closeModal" /> <div class="modal active">
<div class="modal-container p-0"> <a class="modal-overlay" @click.stop="closeModal" />
<div class="modal-header pl-2"> <div class="modal-container p-0">
<div class="modal-title h6"> <div class="modal-header pl-2">
<div class="d-flex"> <div class="modal-title h6">
<i class="mdi mdi-24px mdi-database-edit mr-1" /> <div class="d-flex">
<span class="cut-text">{{ $t('message.editSchema') }}</span> <i class="mdi mdi-24px mdi-database-edit mr-1" />
<span class="cut-text">{{ $t('message.editSchema') }}</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">
<div class="form-group">
<div class="col-3">
<label class="form-label">{{ $t('word.name') }}</label>
</div>
<div class="col-9">
<input
v-model="database.name"
class="form-input"
type="text"
required
:placeholder="$t('message.schemaName')"
readonly
>
</div>
</div>
<div class="form-group">
<div class="col-3">
<label class="form-label">{{ $t('word.collation') }}</label>
</div>
<div class="col-9">
<select
ref="firstInput"
v-model="database.collation"
class="form-select"
>
<option
v-for="collation in collations"
:key="collation.id"
:value="collation.collation"
>
{{ collation.collation }}
</option>
</select>
<small>{{ $t('message.serverDefault') }}: {{ defaultCollation }}</small>
</div>
</div>
</form>
</div> </div>
</div> </div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" /> <div class="modal-footer">
</div> <button class="btn btn-primary mr-2" @click.stop="updateSchema">
<div class="modal-body pb-0"> {{ $t('word.update') }}
<div class="content"> </button>
<form class="form-horizontal"> <button class="btn btn-link" @click.stop="closeModal">
<div class="form-group"> {{ $t('word.close') }}
<div class="col-3"> </button>
<label class="form-label">{{ $t('word.name') }}</label>
</div>
<div class="col-9">
<input
v-model="database.name"
class="form-input"
type="text"
required
:placeholder="$t('message.schemaName')"
readonly
>
</div>
</div>
<div class="form-group">
<div class="col-3">
<label class="form-label">{{ $t('word.collation') }}</label>
</div>
<div class="col-9">
<select
ref="firstInput"
v-model="database.collation"
class="form-select"
>
<option
v-for="collation in collations"
:key="collation.id"
:value="collation.collation"
>
{{ collation.collation }}
</option>
</select>
<small>{{ $t('message.serverDefault') }}: {{ defaultCollation }}</small>
</div>
</div>
</form>
</div> </div>
</div> </div>
<div class="modal-footer">
<button class="btn btn-primary mr-2" @click.stop="updateSchema">
{{ $t('word.update') }}
</button>
<button class="btn btn-link" @click.stop="closeModal">
{{ $t('word.close') }}
</button>
</div>
</div> </div>
</div> </Teleport>
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import Schema from '@/ipc-api/Schema'; import Schema from '@/ipc-api/Schema';
export default { export default {
@ -74,6 +78,22 @@ export default {
props: { props: {
selectedSchema: String selectedSchema: String
}, },
emits: ['close'],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { getWorkspace, getDatabaseVariable } = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
getDatabaseVariable
};
},
data () { data () {
return { return {
database: { database: {
@ -84,11 +104,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace',
getDatabaseVariable: 'workspaces/getDatabaseVariable'
}),
collations () { collations () {
return this.getWorkspace(this.selectedWorkspace).collations; return this.getWorkspace(this.selectedWorkspace).collations;
}, },
@ -124,13 +139,10 @@ export default {
this.$refs.firstInput.focus(); this.$refs.firstInput.focus();
}, 20); }, 20);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification'
}),
async updateSchema () { async updateSchema () {
if (this.database.collation !== this.database.prevCollation) { if (this.database.collation !== this.database.prevCollation) {
try { try {

View File

@ -1,287 +1,310 @@
<template> <template>
<div class="modal active"> <Teleport to="#window-content">
<a class="modal-overlay" @click.stop="closeModal" /> <div class="modal active">
<div class="modal-container p-0"> <a class="modal-overlay" @click.stop="closeModal" />
<div class="modal-header pl-2"> <div class="modal-container p-0">
<div class="modal-title h6"> <div class="modal-header pl-2">
<div class="d-flex"> <div class="modal-title h6">
<i class="mdi mdi-24px mdi-database-arrow-down mr-1" /> <div class="d-flex">
<span class="cut-text">{{ $t('message.exportSchema') }}</span> <i class="mdi mdi-24px mdi-database-arrow-down mr-1" />
</div> <span class="cut-text">{{ $t('message.exportSchema') }}</span>
</div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
</div>
<div class="modal-body pb-0">
<div class="container">
<div class="columns">
<div class="col-3">
<label class="form-label">{{ $t('message.directoryPath') }}</label>
</div>
<div class="col-9">
<fieldset class="input-group">
<input
v-model="basePath"
class="form-input"
type="text"
required
readonly
:placeholder="$t('message.schemaName')"
>
<button
type="button"
class="btn btn-primary input-group-btn"
@click.prevent="openPathDialog"
>
{{ $t('word.change') }}
</button>
</fieldset>
</div> </div>
</div> </div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
</div> </div>
<div class="modal-body pb-0">
<div class="columns export-options"> <div class="container">
<div class="column col-8 left"> <div class="columns">
<div class="columns mb-2"> <div class="col-3">
<div class="column col-auto d-flex text-italic "> <label class="form-label">{{ $t('message.directoryPath') }}</label>
<i class="mdi mdi-file-document-outline mr-2" />
{{ filename }}
</div> </div>
<div class="col-9">
<div class="column col-auto col-ml-auto "> <fieldset class="input-group">
<button <input
class="btn btn-dark btn-sm" v-model="basePath"
:title="$t('word.refresh')" class="form-input"
@click="refresh" type="text"
> required
<i class="mdi mdi-database-refresh" /> readonly
</button> :placeholder="$t('message.schemaName')"
<button
class="btn btn-dark btn-sm"
:title="$t('message.uncheckAllTables')"
:disabled="isRefreshing"
@click="uncheckAllTables"
>
<i class="mdi mdi-file-tree-outline" />
</button>
<button
class="btn btn-dark btn-sm"
:title="$t('message.checkAllTables')"
:disabled="isRefreshing"
@click="checkAllTables"
>
<i class="mdi mdi-file-tree" />
</button>
</div>
</div>
<div class="workspace-query-results">
<div ref="table" class="table table-hover">
<div class="thead">
<div class="tr text-center">
<div class="th no-border" style="width: 50%;" />
<div class="th no-border">
<label
class="form-checkbox m-0 px-2 form-inline"
@click.prevent="toggleAllTablesOption('includeStructure')"
>
<input
type="checkbox"
:indeterminate.prop="includeStructureStatus === 2"
:checked.prop="!!includeStructureStatus"
>
<i class="form-icon" />
</label>
</div>
<div class="th no-border">
<label
class="form-checkbox m-0 px-2 form-inline"
@click.prevent="toggleAllTablesOption('includeContent')"
>
<input
type="checkbox"
:indeterminate.prop="includeContentStatus === 2"
:checked.prop="!!includeContentStatus"
>
<i class="form-icon" />
</label>
</div>
<div class="th no-border">
<label
class="form-checkbox m-0 px-2 form-inline"
@click.prevent="toggleAllTablesOption('includeDropStatement')"
>
<input
type="checkbox"
:indeterminate.prop="includeDropStatementStatus === 2"
:checked.prop="!!includeDropStatementStatus"
>
<i class="form-icon" />
</label>
</div>
</div>
<div class="tr">
<div class="th" style="width: 50%;">
<div class="table-column-title">
<span>{{ $t('word.table') }}</span>
</div>
</div>
<div class="th text-center">
<div class="table-column-title">
<span>{{ $t('word.structure') }}</span>
</div>
</div>
<div class="th text-center">
<div class="table-column-title">
<span>{{ $t('word.content') }}</span>
</div>
</div>
<div class="th text-center">
<div class="table-column-title">
<span>{{ $t('word.drop') }}</span>
</div>
</div>
</div>
</div>
<div class="tbody">
<div
v-for="item in tables"
:key="item.name"
class="tr"
> >
<div class="td"> <button
{{ item.table }} type="button"
class="btn btn-primary input-group-btn"
@click.prevent="openPathDialog"
>
{{ $t('word.change') }}
</button>
</fieldset>
</div>
</div>
</div>
<div class="columns export-options">
<div class="column col-8 left">
<div class="columns mb-2">
<div class="column col-auto d-flex text-italic ">
<i class="mdi mdi-file-document-outline mr-2" />
{{ filename }}
</div>
<div class="column col-auto col-ml-auto ">
<button
class="btn btn-dark btn-sm"
:title="$t('word.refresh')"
@click="refresh"
>
<i class="mdi mdi-database-refresh" />
</button>
<button
class="btn btn-dark btn-sm mx-1"
:title="$t('message.uncheckAllTables')"
:disabled="isRefreshing"
@click="uncheckAllTables"
>
<i class="mdi mdi-file-tree-outline" />
</button>
<button
class="btn btn-dark btn-sm"
:title="$t('message.checkAllTables')"
:disabled="isRefreshing"
@click="checkAllTables"
>
<i class="mdi mdi-file-tree" />
</button>
</div>
</div>
<div class="workspace-query-results">
<div ref="table" class="table table-hover">
<div class="thead">
<div class="tr text-center">
<div class="th no-border" style="width: 50%;" />
<div class="th no-border">
<label
class="form-checkbox m-0 px-2 form-inline"
@click.prevent="toggleAllTablesOption('includeStructure')"
>
<input
type="checkbox"
:indeterminate="includeStructureStatus === 2"
:checked="!!includeStructureStatus"
>
<i class="form-icon" />
</label>
</div>
<div class="th no-border">
<label
class="form-checkbox m-0 px-2 form-inline"
@click.prevent="toggleAllTablesOption('includeContent')"
>
<input
type="checkbox"
:indeterminate="includeContentStatus === 2"
:checked="!!includeContentStatus"
>
<i class="form-icon" />
</label>
</div>
<div class="th no-border">
<label
class="form-checkbox m-0 px-2 form-inline"
@click.prevent="toggleAllTablesOption('includeDropStatement')"
>
<input
type="checkbox"
:indeterminate="includeDropStatementStatus === 2"
:checked="!!includeDropStatementStatus"
>
<i class="form-icon" />
</label>
</div>
</div> </div>
<div class="td text-center"> <div class="tr">
<label class="form-checkbox m-0 px-2 form-inline"> <div class="th" style="width: 50%;">
<input <div class="table-column-title">
v-model="item.includeStructure" <span>{{ $t('word.table') }}</span>
type="checkbox" </div>
><i class="form-icon" /> </div>
</label> <div class="th text-center">
<div class="table-column-title">
<span>{{ $t('word.structure') }}</span>
</div>
</div>
<div class="th text-center">
<div class="table-column-title">
<span>{{ $t('word.content') }}</span>
</div>
</div>
<div class="th text-center">
<div class="table-column-title">
<span>{{ $t('word.drop') }}</span>
</div>
</div>
</div> </div>
<div class="td text-center"> </div>
<label class="form-checkbox m-0 px-2 form-inline">
<input <div class="tbody">
v-model="item.includeContent" <div
type="checkbox" v-for="item in tables"
><i class="form-icon" /> :key="item.name"
</label> class="tr"
</div> >
<div class="td text-center"> <div class="td">
<label class="form-checkbox m-0 px-2 form-inline"> {{ item.table }}
<input </div>
v-model="item.includeDropStatement" <div class="td text-center">
type="checkbox" <label class="form-checkbox m-0 px-2 form-inline">
><i class="form-icon" /> <input
</label> v-model="item.includeStructure"
type="checkbox"
><i class="form-icon" />
</label>
</div>
<div class="td text-center">
<label class="form-checkbox m-0 px-2 form-inline">
<input
v-model="item.includeContent"
type="checkbox"
><i class="form-icon" />
</label>
</div>
<div class="td text-center">
<label class="form-checkbox m-0 px-2 form-inline">
<input
v-model="item.includeDropStatement"
type="checkbox"
><i class="form-icon" />
</label>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> <div class="column col-4">
<div class="column col-4"> <h5 class="h5">
<h5 class="h5"> {{ $t('word.options') }}
{{ $t('word.options') }} </h5>
</h5> <span class="h6">{{ $t('word.includes') }}:</span>
<span class="h6">{{ $t('word.includes') }}:</span> <label
<label v-for="(_, key) in options.includes"
v-for="(_, key) in options.includes" :key="key"
:key="key" class="form-checkbox"
class="form-checkbox" >
> <input v-model="options.includes[key]" type="checkbox"><i class="form-icon" /> {{ $tc(`word.${key}`, 2) }}
<input v-model="options.includes[key]" type="checkbox"><i class="form-icon" /> {{ $tc(`word.${key}`, 2) }} </label>
</label> <div v-if="customizations.exportByChunks">
<div v-if="customizations.exportByChunks"> <div class="h6 mt-4 mb-2">
<div class="h6 mt-4 mb-2"> {{ $t('message.newInserStmtEvery') }}:
{{ $t('message.newInserStmtEvery') }}: </div>
<div class="columns">
<div class="column col-6">
<input
v-model.number="options.sqlInsertAfter"
type="number"
class="form-input"
>
</div>
<div class="column col-6">
<select v-model="options.sqlInsertDivider" class="form-select">
<option value="bytes">
KiB
</option>
<option value="rows">
{{ $tc('word.row', 2) }}
</option>
</select>
</div>
</div>
</div>
<div class="h6 mb-2 mt-4">
{{ $t('message.ourputFormat') }}:
</div> </div>
<div class="columns"> <div class="columns">
<div class="column col-6"> <div class="column h5 mb-4">
<input <select v-model="options.outputFormat" class="form-select">
v-model.number="options.sqlInsertAfter" <option value="sql">
type="number" {{ $t('message.singleFile', {ext: '.sql'}) }}
class="form-input"
value="250"
>
</div>
<div class="column col-6">
<select v-model="options.sqlInsertDivider" class="form-select">
<option value="bytes">
KiB
</option> </option>
<option value="rows"> <option value="sql.zip">
{{ $tc('word.row', 2) }} {{ $t('message.zipCompressedFile', {ext: '.sql'}) }}
</option> </option>
</select> </select>
</div> </div>
</div> </div>
</div> </div>
<div class="h6 mb-2 mt-4">
{{ $t('message.ourputFormat') }}:
</div>
<div class="columns">
<div class="column h5 mb-4">
<select v-model="options.outputFormat" class="form-select">
<option value="sql">
{{ $t('message.singleFile', {ext: '.sql'}) }}
</option>
<option value="sql.zip">
{{ $t('message.zipCompressedFile', {ext: '.sql'}) }}
</option>
</select>
</div>
</div>
</div> </div>
</div> </div>
</div> <div class="modal-footer columns">
<div class="modal-footer columns"> <div class="column col modal-progress-wrapper text-left">
<div class="column col modal-progress-wrapper text-left"> <div v-if="progressPercentage > 0" class="export-progress">
<div v-if="progressPercentage > 0" class="export-progress"> <span class="progress-status">
<span class="progress-status"> {{ progressPercentage }}% - {{ progressStatus }}
{{ progressPercentage }}% - {{ progressStatus }} </span>
</span> <progress
<progress class="progress d-block"
class="progress d-block" :value="progressPercentage"
:value="progressPercentage" max="100"
max="100" />
/> </div>
</div>
<div class="column col-auto px-0">
<button class="btn btn-link" @click.stop="closeModal">
{{ $t('word.close') }}
</button>
<button
class="btn btn-primary mr-2"
:class="{'loading': isExporting}"
:disabled="isExporting || isRefreshing"
autofocus
@click.prevent="startExport"
>
{{ $t('word.export') }}
</button>
</div> </div>
</div>
<div class="column col-auto px-0">
<button class="btn btn-link" @click.stop="closeModal">
{{ $t('word.close') }}
</button>
<button
class="btn btn-primary mr-2"
:class="{'loading': isExporting}"
:disabled="isExporting || isRefreshing"
autofocus
@click.prevent="startExport"
>
{{ $t('word.export') }}
</button>
</div> </div>
</div> </div>
</div> </div>
</div> </Teleport>
</template> </template>
<script> <script>
import { ipcRenderer } from 'electron';
import { mapActions, mapGetters } from 'vuex';
import moment from 'moment'; import moment from 'moment';
import { ipcRenderer } from 'electron';
import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import customizations from 'common/customizations'; import customizations from 'common/customizations';
import Application from '@/ipc-api/Application'; import Application from '@/ipc-api/Application';
import Schema from '@/ipc-api/Schema'; import Schema from '@/ipc-api/Schema';
export default { export default {
name: 'ModalExportSchema', name: 'ModalExportSchema',
props: { props: {
selectedSchema: String selectedSchema: String
}, },
emits: ['close'],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
getDatabaseVariable,
refreshSchema
} = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
getDatabaseVariable,
refreshSchema
};
},
data () { data () {
return { return {
isExporting: false, isExporting: false,
@ -299,11 +322,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace',
getDatabaseVariable: 'workspaces/getDatabaseVariable'
}),
currentWorkspace () { currentWorkspace () {
return this.getWorkspace(this.selectedWorkspace); return this.getWorkspace(this.selectedWorkspace);
}, },
@ -358,20 +376,16 @@ export default {
structure.forEach(feat => { structure.forEach(feat => {
const val = customizations[this.currentWorkspace.client][feat]; const val = customizations[this.currentWorkspace.client][feat];
if (val) if (val)
this.$set(this.options.includes, feat, true); this.options.includes[feat] = true;
}); });
ipcRenderer.on('export-progress', this.updateProgress); ipcRenderer.on('export-progress', this.updateProgress);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
ipcRenderer.off('export-progress', this.updateProgress); ipcRenderer.off('export-progress', this.updateProgress);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
refreshSchema: 'workspaces/refreshSchema'
}),
async startExport () { async startExport () {
this.isExporting = true; this.isExporting = true;
const { uid, client } = this.currentWorkspace; const { uid, client } = this.currentWorkspace;

View File

@ -1,192 +1,196 @@
<template> <template>
<div class="modal active"> <Teleport to="#window-content">
<a class="modal-overlay" @click.stop="closeModal" /> <div class="modal active">
<div class="modal-container p-0"> <a class="modal-overlay" @click.stop="closeModal" />
<div class="modal-header pl-2"> <div class="modal-container p-0">
<div class="modal-title h6"> <div class="modal-header pl-2">
<div class="d-flex"> <div class="modal-title h6">
<i class="mdi mdi-24px mdi-playlist-plus mr-1" /> <div class="d-flex">
<span class="cut-text">{{ $tc('message.insertRow', 2) }}</span> <i class="mdi mdi-24px mdi-playlist-plus mr-1" />
<span class="cut-text">{{ $tc('message.insertRow', 2) }}</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
v-model="localRow[field.name]"
: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]"
>
<span class="input-group-addon field-type" :class="typeClass(field.type)">
{{ field.type }} {{ wrapNumber(fieldLength(field)) }}
</span>
<label class="form-checkbox ml-3" :title="$t('word.insert')">
<input
type="checkbox"
:checked="!fieldsToExclude.includes(field.name)"
@change.prevent="toggleFields($event, field)"
><i class="form-icon" />
</label>
</FakerSelect>
</div>
</div>
</fieldset>
</form>
</div> </div>
</div> </div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" /> <div class="modal-footer columns">
</div> <div class="column d-flex" :class="hasFakes ? 'col-4' : 'col-2'">
<div class="modal-body pb-0"> <div class="input-group tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')">
<div class="content"> <input
<form class="form-horizontal"> v-model="nInserts"
<fieldset :disabled="isInserting"> type="number"
<div class="form-input"
v-for="field in fields" min="1"
:key="field.name" :disabled="isInserting"
class="form-group"
> >
<div class="col-3 col-sm-12"> <span class="input-group-addon">
<label class="form-label" :title="field.name">{{ field.name }}</label> <i class="mdi mdi-24px mdi-repeat" />
</div> </span>
<div class="column columns col-sm-12"> </div>
<FakerSelect <div
:type="field.type" v-if="hasFakes"
class="column columns pr-0" class="tooltip tooltip-right ml-2"
:is-checked="!fieldsToExclude.includes(field.name)" :data-tooltip="$t('message.fakeDataLanguage')"
: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="!fieldsToExclude.includes(field.name)"
@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"> <select v-model="fakerLocale" class="form-select">
<i class="mdi mdi-24px mdi-repeat" /> <option value="ar">
</span> 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>
<div <div class="column col-auto">
v-if="hasFakes" <button
class="tooltip tooltip-right ml-2" class="btn btn-primary mr-2"
:data-tooltip="$t('message.fakeDataLanguage')" :class="{'loading': isInserting}"
> @click.stop="insertRows"
<select v-model="fakerLocale" class="form-select"> >
<option value="ar"> {{ $t('word.insert') }}
Arabic </button>
</option><option value="az"> <button class="btn btn-link" @click.stop="closeModal">
Azerbaijani {{ $t('word.close') }}
</option><option value="zh_CN"> </button>
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> </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> </div>
</div> </Teleport>
</template> </template>
<script> <script>
import moment from 'moment'; import moment from 'moment';
import { storeToRefs } from 'pinia';
import { TEXT, LONG_TEXT, NUMBER, FLOAT, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes'; import { TEXT, LONG_TEXT, NUMBER, FLOAT, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
import { mapGetters, mapActions } from 'vuex'; import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import Tables from '@/ipc-api/Tables'; import Tables from '@/ipc-api/Tables';
import FakerSelect from '@/components/FakerSelect'; import FakerSelect from '@/components/FakerSelect';
@ -195,17 +199,27 @@ export default {
components: { components: {
FakerSelect FakerSelect
}, },
filters: {
wrapNumber (num) {
if (!num) return '';
return `(${num})`;
}
},
props: { props: {
tabUid: [String, Number], tabUid: [String, Number],
fields: Array, fields: Array,
keyUsage: Array keyUsage: Array
}, },
emits: ['reload', 'hide'],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { getWorkspace, getWorkspaceTab } = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
getWorkspaceTab
};
},
data () { data () {
return { return {
localRow: {}, localRow: {},
@ -216,11 +230,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace',
getWorkspaceTab: 'workspaces/getWorkspaceTab'
}),
workspace () { workspace () {
return this.getWorkspace(this.selectedWorkspace); return this.getWorkspace(this.selectedWorkspace);
}, },
@ -287,13 +296,10 @@ export default {
this.localRow = { ...rowObj }; this.localRow = { ...rowObj };
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification'
}),
typeClass (type) { typeClass (type) {
if (type) if (type)
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`; return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
@ -345,8 +351,8 @@ export default {
}, },
fieldLength (field) { fieldLength (field) {
if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null; if ([...BLOB, ...LONG_TEXT].includes(field.type)) return null;
else if (TEXT.includes(field.type)) return field.charLength; else if (TEXT.includes(field.type)) return Number(field.charLength);
return field.length; return Number(field.length);
}, },
toggleFields (event, field) { toggleFields (event, field) {
if (event.target.checked) if (event.target.checked)
@ -367,6 +373,10 @@ export default {
e.stopPropagation(); e.stopPropagation();
if (e.key === 'Escape') if (e.key === 'Escape')
this.closeModal(); this.closeModal();
},
wrapNumber (num) {
if (!num) return '';
return `(${num})`;
} }
} }
}; };

View File

@ -1,103 +1,107 @@
<template> <template>
<div class="modal active"> <Teleport to="#window-content">
<a class="modal-overlay" @click.stop="closeModal" /> <div class="modal active">
<div class="modal-container p-0 pb-4"> <a class="modal-overlay" @click.stop="closeModal" />
<div class="modal-header pl-2"> <div class="modal-container p-0 pb-4">
<div class="modal-title h6"> <div class="modal-header pl-2">
<div class="d-flex"> <div class="modal-title h6">
<i class="mdi mdi-24px mdi-history mr-1" /> <div class="d-flex">
<span class="cut-text">{{ $t('word.history') }}: {{ connectionName }}</span> <i class="mdi mdi-24px mdi-history mr-1" />
<span class="cut-text">{{ $t('word.history') }}: {{ connectionName }}</span>
</div>
</div> </div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
</div> </div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" /> <div class="modal-body p-0 workspace-query-results">
</div> <div
<div class="modal-body p-0 workspace-query-results"> v-if="history.length"
<div ref="searchForm"
v-if="history.length" class="form-group has-icon-right p-2 m-0"
ref="searchForm"
class="form-group has-icon-right p-2 m-0"
>
<input
v-model="searchTerm"
class="form-input"
type="text"
:placeholder="$t('message.searchForQueries')"
> >
<i v-if="!searchTerm" class="form-icon mdi mdi-magnify mdi-18px pr-4" /> <input
<i v-model="searchTerm"
v-else class="form-input"
class="form-icon c-hand mdi mdi-backspace mdi-18px pr-4" type="text"
@click="searchTerm = ''" :placeholder="$t('message.searchForQueries')"
/>
</div>
<div
v-if="history.length"
ref="tableWrapper"
class="vscroll px-1 "
:style="{'height': resultsSize+'px'}"
>
<div ref="table">
<BaseVirtualScroll
ref="resultTable"
:items="filteredHistory"
:item-height="66"
:visible-height="resultsSize"
:scroll-element="scrollElement"
> >
<template slot-scope="{ items }"> <i v-if="!searchTerm" class="form-icon mdi mdi-magnify mdi-18px pr-4" />
<div <i
v-for="query in items" v-else
:key="query.uid" class="form-icon c-hand mdi mdi-backspace mdi-18px pr-4"
class="tile my-2" @click="searchTerm = ''"
tabindex="0" />
> </div>
<div class="tile-icon"> <div
<i class="mdi mdi-code-tags pr-1" /> v-if="history.length"
</div> ref="tableWrapper"
<div class="tile-content"> class="vscroll px-1 "
<div class="tile-title"> :style="{'height': resultsSize+'px'}"
<code >
class="cut-text" <div ref="table">
:title="query.sql" <BaseVirtualScroll
v-html="highlightWord(query.sql)" ref="resultTable"
/> :items="filteredHistory"
:item-height="66"
:visible-height="resultsSize"
:scroll-element="scrollElement"
>
<template #default="{ items }">
<div
v-for="query in items"
:key="query.uid"
class="tile my-2"
tabindex="0"
>
<div class="tile-icon">
<i class="mdi mdi-code-tags pr-1" />
</div> </div>
<div class="tile-bottom-content"> <div class="tile-content">
<small class="tile-subtitle">{{ query.schema }} · {{ formatDate(query.date) }}</small> <div class="tile-title">
<div class="tile-history-buttons"> <code
<button class="btn btn-link pl-1" @click.stop="$emit('select-query', query.sql)"> class="cut-text"
<i class="mdi mdi-open-in-app pr-1" /> {{ $t('word.select') }} :title="query.sql"
</button> v-html="highlightWord(query.sql)"
<button class="btn btn-link pl-1" @click="copyQuery(query.sql)"> />
<i class="mdi mdi-content-copy pr-1" /> {{ $t('word.copy') }} </div>
</button> <div class="tile-bottom-content">
<button class="btn btn-link pl-1" @click="deleteQuery(query)"> <small class="tile-subtitle">{{ query.schema }} · {{ formatDate(query.date) }}</small>
<i class="mdi mdi-delete-forever pr-1" /> {{ $t('word.delete') }} <div class="tile-history-buttons">
</button> <button class="btn btn-link pl-1" @click.stop="$emit('select-query', query.sql)">
<i class="mdi mdi-open-in-app pr-1" /> {{ $t('word.select') }}
</button>
<button class="btn btn-link pl-1" @click="copyQuery(query.sql)">
<i class="mdi mdi-content-copy pr-1" /> {{ $t('word.copy') }}
</button>
<button class="btn btn-link pl-1" @click="deleteQuery(query)">
<i class="mdi mdi-delete-forever pr-1" /> {{ $t('word.delete') }}
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </template>
</template> </BaseVirtualScroll>
</BaseVirtualScroll> </div>
</div> </div>
</div> <div v-else class="empty">
<div v-else class="empty"> <div class="empty-icon">
<div class="empty-icon"> <i class="mdi mdi-history mdi-48px" />
<i class="mdi mdi-history mdi-48px" /> </div>
<p class="empty-title h5">
{{ $t('message.thereIsNoQueriesYet') }}
</p>
</div> </div>
<p class="empty-title h5">
{{ $t('message.thereIsNoQueriesYet') }}
</p>
</div> </div>
</div> </div>
</div> </div>
</div> </Teleport>
</template> </template>
<script> <script>
import moment from 'moment'; import moment from 'moment';
import { mapGetters, mapActions } from 'vuex'; import { useHistoryStore } from '@/stores/history';
import { useConnectionsStore } from '@/stores/connections';
import { useNotificationsStore } from '@/stores/notifications';
import BaseVirtualScroll from '@/components/BaseVirtualScroll'; import BaseVirtualScroll from '@/components/BaseVirtualScroll';
export default { export default {
@ -108,6 +112,19 @@ export default {
props: { props: {
connection: Object connection: Object
}, },
emits: ['select-query', 'close'],
setup () {
const { getHistoryByWorkspace, deleteQueryFromHistory } = useHistoryStore();
const { getConnectionName } = useConnectionsStore();
const { addNotification } = useNotificationsStore();
return {
getHistoryByWorkspace,
deleteQueryFromHistory,
getConnectionName,
addNotification
};
},
data () { data () {
return { return {
resultsSize: 1000, resultsSize: 1000,
@ -119,10 +136,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
getConnectionName: 'connections/getConnectionName',
getHistoryByWorkspace: 'history/getHistoryByWorkspace'
}),
connectionName () { connectionName () {
return this.getConnectionName(this.connection.uid); return this.getConnectionName(this.connection.uid);
}, },
@ -156,16 +169,12 @@ export default {
this.resizeResults(); this.resizeResults();
window.addEventListener('resize', this.resizeResults); window.addEventListener('resize', this.resizeResults);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey, { capture: true }); window.removeEventListener('keydown', this.onKey, { capture: true });
window.removeEventListener('resize', this.resizeResults); window.removeEventListener('resize', this.resizeResults);
clearInterval(this.refreshInterval); clearInterval(this.refreshInterval);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
deleteQueryFromHistory: 'history/deleteQueryFromHistory'
}),
copyQuery (sql) { copyQuery (sql) {
navigator.clipboard.writeText(sql); navigator.clipboard.writeText(sql);
}, },

View File

@ -1,56 +1,61 @@
<template> <template>
<div class="modal active"> <Teleport to="#window-content">
<a class="modal-overlay" @click.stop="closeModal" /> <div class="modal active">
<div class="modal-container p-0"> <a class="modal-overlay" @click.stop="closeModal" />
<div class="modal-header pl-2"> <div class="modal-container p-0">
<div class="modal-title h6"> <div class="modal-header pl-2">
<div class="d-flex"> <div class="modal-title h6">
<i class="mdi mdi-24px mdi-database-arrow-up mr-1" /> <div class="d-flex">
<span class="cut-text">{{ $t('message.importSchema') }}</span> <i class="mdi mdi-24px mdi-database-arrow-up mr-1" />
<span class="cut-text">{{ $t('message.importSchema') }}</span>
</div>
</div> </div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
</div> </div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" /> <div class="modal-body pb-0">
</div> {{ sqlFile }}
<div class="modal-body pb-0"> <div v-if="queryErrors.length > 0" class="mt-2">
{{ sqlFile }} <label>{{ $tc('message.importQueryErrors', queryErrors.length) }}</label>
<div v-if="queryErrors.length > 0" class="mt-2"> <textarea
<label>{{ $tc('message.importQueryErrors', queryErrors.length) }}</label> v-model="formattedQueryErrors"
<textarea class="form-input"
v-model="formattedQueryErrors" rows="5"
class="form-input" readonly
rows="5"
readonly
/>
</div>
</div>
<div class="modal-footer columns">
<div class="column col modal-progress-wrapper text-left">
<div class="import-progress">
<span class="progress-status">
{{ progressPercentage }}% - {{ progressStatus }} - {{ $tc('message.executedQueries', queryCount) }}
</span>
<progress
class="progress d-block"
:value="progressPercentage"
max="100"
/> />
</div> </div>
</div> </div>
<div class="column col-auto px-0"> <div class="modal-footer columns">
<button class="btn btn-link" @click.stop="closeModal"> <div class="column col modal-progress-wrapper text-left">
{{ completed ? $t('word.close') : $t('word.cancel') }} <div class="import-progress">
</button> <span class="progress-status">
{{ progressPercentage }}% - {{ progressStatus }} - {{ $tc('message.executedQueries', queryCount) }}
</span>
<progress
class="progress d-block"
:value="progressPercentage"
max="100"
/>
</div>
</div>
<div class="column col-auto px-0">
<button class="btn btn-link" @click.stop="closeModal">
{{ completed ? $t('word.close') : $t('word.cancel') }}
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> <Teleport to="#window-content" />
</teleport>
</template> </template>
<script> <script>
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import { mapActions, mapGetters } from 'vuex'; import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import moment from 'moment'; import moment from 'moment';
import Schema from '@/ipc-api/Schema'; import Schema from '@/ipc-api/Schema';
import { storeToRefs } from 'pinia';
export default { export default {
name: 'ModalImportSchema', name: 'ModalImportSchema',
@ -58,6 +63,22 @@ export default {
props: { props: {
selectedSchema: String selectedSchema: String
}, },
emits: ['close'],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { getWorkspace, refreshSchema } = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
refreshSchema
};
},
data () { data () {
return { return {
sqlFile: '', sqlFile: '',
@ -70,10 +91,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
currentWorkspace () { currentWorkspace () {
return this.getWorkspace(this.selectedWorkspace); return this.getWorkspace(this.selectedWorkspace);
}, },
@ -89,16 +106,12 @@ export default {
ipcRenderer.on('import-progress', this.updateProgress); ipcRenderer.on('import-progress', this.updateProgress);
ipcRenderer.on('query-error', this.handleQueryError); ipcRenderer.on('query-error', this.handleQueryError);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
ipcRenderer.off('import-progress', this.updateProgress); ipcRenderer.off('import-progress', this.updateProgress);
ipcRenderer.off('query-error', this.handleQueryError); ipcRenderer.off('query-error', this.handleQueryError);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
refreshSchema: 'workspaces/refreshSchema'
}),
async startImport (sqlFile) { async startImport (sqlFile) {
this.isImporting = true; this.isImporting = true;
this.sqlFile = sqlFile; this.sqlFile = sqlFile;

View File

@ -1,76 +1,96 @@
<template> <template>
<div class="modal active"> <Teleport to="#window-content">
<a class="modal-overlay" @click.stop="closeModal" /> <div class="modal active">
<div class="modal-container p-0"> <a class="modal-overlay" @click.stop="closeModal" />
<div class="modal-header pl-2"> <div class="modal-container p-0">
<div class="modal-title h6"> <div class="modal-header pl-2">
<div class="d-flex"> <div class="modal-title h6">
<i class="mdi mdi-24px mdi-database-plus mr-1" /> <div class="d-flex">
<span class="cut-text">{{ $t('message.createNewSchema') }}</span> <i class="mdi mdi-24px mdi-database-plus mr-1" />
<span class="cut-text">{{ $t('message.createNewSchema') }}</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" @submit.prevent="createSchema">
<div class="form-group">
<div class="col-3">
<label class="form-label">{{ $t('word.name') }}</label>
</div>
<div class="col-9">
<input
ref="firstInput"
v-model="database.name"
class="form-input"
type="text"
required
:placeholder="$t('message.schemaName')"
>
</div>
</div>
<div v-if="customizations.collations" class="form-group">
<div class="col-3">
<label class="form-label">{{ $t('word.collation') }}</label>
</div>
<div class="col-9">
<select v-model="database.collation" class="form-select">
<option
v-for="collation in collations"
:key="collation.id"
:value="collation.collation"
>
{{ collation.collation }}
</option>
</select>
<small>{{ $t('message.serverDefault') }}: {{ defaultCollation }}</small>
</div>
</div>
</form>
</div> </div>
</div> </div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" /> <div class="modal-footer">
</div> <button
<div class="modal-body pb-0"> class="btn btn-primary mr-2"
<div class="content"> :class="{'loading': isLoading}"
<form class="form-horizontal" @submit.prevent="createSchema"> @click.stop="createSchema"
<div class="form-group"> >
<div class="col-3"> {{ $t('word.add') }}
<label class="form-label">{{ $t('word.name') }}</label> </button>
</div> <button class="btn btn-link" @click.stop="closeModal">
<div class="col-9"> {{ $t('word.close') }}
<input </button>
ref="firstInput"
v-model="database.name"
class="form-input"
type="text"
required
:placeholder="$t('message.schemaName')"
>
</div>
</div>
<div v-if="customizations.collations" class="form-group">
<div class="col-3">
<label class="form-label">{{ $t('word.collation') }}</label>
</div>
<div class="col-9">
<select v-model="database.collation" class="form-select">
<option
v-for="collation in collations"
:key="collation.id"
:value="collation.collation"
>
{{ collation.collation }}
</option>
</select>
<small>{{ $t('message.serverDefault') }}: {{ defaultCollation }}</small>
</div>
</div>
</form>
</div> </div>
</div> </div>
<div class="modal-footer">
<button
class="btn btn-primary mr-2"
:class="{'loading': isLoading}"
@click.stop="createSchema"
>
{{ $t('word.add') }}
</button>
<button class="btn btn-link" @click.stop="closeModal">
{{ $t('word.close') }}
</button>
</div>
</div> </div>
</div> </Teleport>
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import Schema from '@/ipc-api/Schema'; import Schema from '@/ipc-api/Schema';
import { storeToRefs } from 'pinia';
export default { export default {
name: 'ModalNewSchema', name: 'ModalNewSchema',
emits: ['reload', 'close'],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { getWorkspace, getDatabaseVariable } = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
getDatabaseVariable
};
},
data () { data () {
return { return {
isLoading: false, isLoading: false,
@ -81,11 +101,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace',
getDatabaseVariable: 'workspaces/getDatabaseVariable'
}),
collations () { collations () {
return this.getWorkspace(this.selectedWorkspace).collations; return this.getWorkspace(this.selectedWorkspace).collations;
}, },
@ -103,13 +118,10 @@ export default {
this.$refs.firstInput.focus(); this.$refs.firstInput.focus();
}, 20); }, 20);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification'
}),
async createSchema () { async createSchema () {
this.isLoading = true; this.isLoading = true;
try { try {

View File

@ -1,148 +1,158 @@
<template> <template>
<div class="modal active"> <Teleport to="#window-content">
<a class="modal-overlay" @click.stop="closeModal" /> <div class="modal active">
<div class="modal-container p-0"> <a class="modal-overlay" @click.stop="closeModal" />
<div class="modal-header pl-2"> <div class="modal-container p-0">
<div class="modal-title h6"> <div class="modal-header pl-2">
<div class="d-flex"> <div class="modal-title h6">
<i class="mdi mdi-24px mdi-playlist-plus mr-1" /> <div class="d-flex">
<span class="cut-text">{{ $t('message.addNewRow') }}</span> <i class="mdi mdi-24px mdi-playlist-plus mr-1" />
<span class="cut-text">{{ $t('message.addNewRow') }}</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, key) in fields"
:key="field.name"
class="form-group"
>
<div class="col-4 col-sm-12">
<label class="form-label" :title="field.name">{{ field.name }}</label>
</div>
<div class="input-group col-8 col-sm-12">
<ForeignKeySelect
v-if="foreignKeys.includes(field.name)"
ref="formInput"
v-model="localRow[field.name]"
class="form-select"
:key-usage="getKeyUsage(field.name)"
:disabled="fieldsToExclude.includes(field.name)"
/>
<input
v-else-if="inputProps(field).mask"
ref="formInput"
v-model="localRow[field.name]"
v-mask="inputProps(field).mask"
class="form-input"
:type="inputProps(field).type"
:disabled="fieldsToExclude.includes(field.name)"
:tabindex="key+1"
>
<input
v-else-if="inputProps(field).type === 'file'"
ref="formInput"
class="form-input"
type="file"
:disabled="fieldsToExclude.includes(field.name)"
:tabindex="key+1"
@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
v-else
ref="formInput"
v-model="localRow[field.name]"
class="form-input"
:type="inputProps(field).type"
:disabled="fieldsToExclude.includes(field.name)"
:tabindex="key+1"
>
<span class="input-group-addon" :class="typeCLass(field.type)">
{{ field.type }} {{ wrapNumber(fieldLength(field)) }}
</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>
</div>
</div>
</fieldset>
</form>
</div> </div>
</div> </div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" /> <div class="modal-footer">
</div> <div class="input-group col-3 tooltip tooltip-right" :data-tooltip="$t('message.numberOfInserts')">
<div class="modal-body pb-0"> <input
<div class="content"> v-model="nInserts"
<form class="form-horizontal"> type="number"
<fieldset :disabled="isInserting"> class="form-input"
<div min="1"
v-for="(field, key) in fields" :disabled="isInserting"
:key="field.name" >
class="form-group" <span class="input-group-addon">
> <i class="mdi mdi-24px mdi-repeat" />
<div class="col-4 col-sm-12"> </span>
<label class="form-label" :title="field.name">{{ field.name }}</label> </div>
</div> <div>
<div class="input-group col-8 col-sm-12"> <button
<ForeignKeySelect class="btn btn-primary mr-2"
v-if="foreignKeys.includes(field.name)" :class="{'loading': isInserting}"
ref="formInput" @click.stop="insertRows"
class="form-select" >
:value.sync="localRow[field.name]" {{ $t('word.insert') }}
:key-usage="getKeyUsage(field.name)" </button>
:disabled="fieldsToExclude.includes(field.name)" <button class="btn btn-link" @click.stop="closeModal">
/> {{ $t('word.close') }}
<input </button>
v-else-if="inputProps(field).mask" </div>
ref="formInput"
v-model="localRow[field.name]"
v-mask="inputProps(field).mask"
class="form-input"
:type="inputProps(field).type"
:disabled="fieldsToExclude.includes(field.name)"
:tabindex="key+1"
>
<input
v-else-if="inputProps(field).type === 'file'"
ref="formInput"
class="form-input"
type="file"
:disabled="fieldsToExclude.includes(field.name)"
:tabindex="key+1"
@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
v-else
ref="formInput"
v-model="localRow[field.name]"
class="form-input"
:type="inputProps(field).type"
:disabled="fieldsToExclude.includes(field.name)"
:tabindex="key+1"
>
<span class="input-group-addon" :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>
</div>
</div>
</fieldset>
</form>
</div>
</div>
<div class="modal-footer">
<div class="input-group col-3 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>
<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>
</div> </div>
</div> </Teleport>
</template> </template>
<script> <script>
import moment from 'moment'; import moment from 'moment';
import { TEXT, LONG_TEXT, NUMBER, FLOAT, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes'; import { TEXT, LONG_TEXT, NUMBER, FLOAT, DATE, TIME, DATETIME, BLOB, BIT } from 'common/fieldTypes';
import { VueMaskDirective } from 'v-mask'; import { useNotificationsStore } from '@/stores/notifications';
import { mapGetters, mapActions } from 'vuex'; import { useWorkspacesStore } from '@/stores/workspaces';
import Tables from '@/ipc-api/Tables'; import Tables from '@/ipc-api/Tables';
import ForeignKeySelect from '@/components/ForeignKeySelect'; import ForeignKeySelect from '@/components/ForeignKeySelect';
import { storeToRefs } from 'pinia';
export default { export default {
name: 'ModalNewTableRow', name: 'ModalNewTableRow',
components: { components: {
ForeignKeySelect ForeignKeySelect
}, },
directives: {
mask: VueMaskDirective
},
filters: {
wrapNumber (num) {
if (!num) return '';
return `(${num})`;
}
},
props: { props: {
tabUid: [String, Number], tabUid: [String, Number],
fields: Array, fields: Array,
keyUsage: Array keyUsage: Array
}, },
emits: ['reload', 'hide'],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { getWorkspace, getWorkspaceTab } = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
getWorkspaceTab
};
},
data () { data () {
return { return {
localRow: {}, localRow: {},
@ -152,11 +162,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace',
getWorkspaceTab: 'workspaces/getWorkspaceTab'
}),
workspace () { workspace () {
return this.getWorkspace(this.selectedWorkspace); return this.getWorkspace(this.selectedWorkspace);
}, },
@ -216,13 +221,10 @@ export default {
firstSelectableInput.focus(); firstSelectableInput.focus();
}, 20); }, 20);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification'
}),
typeClass (type) { typeClass (type) {
if (type) if (type)
return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`; return `type-${type.toLowerCase().replaceAll(' ', '_').replaceAll('"', '')}`;
@ -331,6 +333,10 @@ export default {
e.stopPropagation(); e.stopPropagation();
if (e.key === 'Escape') if (e.key === 'Escape')
this.closeModal(); this.closeModal();
},
wrapNumber (num) {
if (!num) return '';
return `(${num})`;
} }
} }
}; };

View File

@ -1,140 +1,143 @@
<template> <template>
<div class="modal active"> <Teleport to="#window-content">
<ModalProcessesListContext <div class="modal active">
v-if="isContext" <ModalProcessesListContext
:context-event="contextEvent" v-if="isContext"
:selected-row="selectedRow" :context-event="contextEvent"
:selected-cell="selectedCell" :selected-row="selectedRow"
@copy-cell="copyCell" :selected-cell="selectedCell"
@copy-row="copyRow" @copy-cell="copyCell"
@kill-process="killProcess" @copy-row="copyRow"
@close-context="closeContext" @kill-process="killProcess"
/> @close-context="closeContext"
<a class="modal-overlay" @click.stop="closeModal" /> />
<div class="modal-container p-0 pb-4"> <a class="modal-overlay" @click.stop="closeModal" />
<div class="modal-header pl-2"> <div class="modal-container p-0 pb-4">
<div class="modal-title h6"> <div class="modal-header pl-2">
<div class="d-flex"> <div class="modal-title h6">
<i class="mdi mdi-24px mdi-memory mr-1" /> <div class="d-flex">
<span class="cut-text">{{ $t('message.processesList') }}: {{ connectionName }}</span> <i class="mdi mdi-24px mdi-memory mr-1" />
</div> <span class="cut-text">{{ $t('message.processesList') }}: {{ connectionName }}</span>
</div>
<a class="btn btn-clear c-hand" @click.stop="closeModal" />
</div>
<div class="processes-toolbar py-2 px-4">
<div class="workspace-query-buttons">
<div class="dropdown pr-1">
<div class="btn-group">
<button
class="btn btn-dark btn-sm mr-0 pr-1 d-flex"
:class="{'loading':isQuering}"
:title="`${$t('word.refresh')} (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" />
</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> </div>
<div class="dropdown table-dropdown"> <a class="btn btn-clear c-hand" @click.stop="closeModal" />
<button
:disabled="isQuering"
class="btn btn-dark btn-sm dropdown-toggle d-flex mr-0 pr-0"
tabindex="0"
>
<i class="mdi mdi-24px mdi-file-export mr-1" />
<span>{{ $t('word.export') }}</span>
<i class="mdi mdi-24px mdi-menu-down" />
</button>
<ul class="menu text-left">
<li class="menu-item">
<a class="c-hand" @click="downloadTable('json')">JSON</a>
</li>
<li class="menu-item">
<a class="c-hand" @click="downloadTable('csv')">CSV</a>
</li>
</ul>
</div>
</div> </div>
<div class="workspace-query-info"> <div class="processes-toolbar py-2 px-4">
<div v-if="sortedResults.length"> <div class="workspace-query-buttons">
{{ $t('word.processes') }}: <b>{{ sortedResults.length.toLocaleString() }}</b> <div class="dropdown pr-1">
</div> <div class="btn-group">
</div> <button
</div> class="btn btn-dark btn-sm mr-0 pr-1 d-flex"
<div class="modal-body py-0 workspace-query-results"> :class="{'loading':isQuering}"
<div :title="`${$t('word.refresh')} (F5)`"
ref="tableWrapper" @click="getProcessesList"
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"> <i v-if="!+autorefreshTimer" class="mdi mdi-24px mdi-refresh mr-1" />
<div class="table-column-title" @click="sort(field)"> <i v-else class="mdi mdi-24px mdi-history mdi-flip-h mr-1" />
<span>{{ field.toUpperCase() }}</span> </button>
<i <div class="btn btn-dark btn-sm dropdown-toggle pl-0 pr-0" tabindex="0">
v-if="currentSort === field" <i class="mdi mdi-24px mdi-menu-down" />
class="mdi sort-icon" </div>
:class="currentSortDir === 'asc' ? 'mdi-sort-ascending':'mdi-sort-descending'" <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="dropdown table-dropdown">
<button
:disabled="isQuering"
class="btn btn-dark btn-sm dropdown-toggle d-flex mr-0 pr-0"
tabindex="0"
>
<i class="mdi mdi-24px mdi-file-export mr-1" />
<span>{{ $t('word.export') }}</span>
<i class="mdi mdi-24px mdi-menu-down" />
</button>
<ul class="menu text-left">
<li class="menu-item">
<a class="c-hand" @click="downloadTable('json')">JSON</a>
</li>
<li class="menu-item">
<a class="c-hand" @click="downloadTable('csv')">CSV</a>
</li>
</ul>
</div>
</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>
</div> </div>
</div> </div>
<BaseVirtualScroll
ref="resultTable"
:items="sortedResults"
:item-height="22"
class="tbody"
:visible-height="resultsSize"
:scroll-element="scrollElement"
>
<template #default="{ items }">
<ModalProcessesListRow
v-for="row in items"
:key="row.id"
class="process-row"
:row="row"
@select-row="selectRow(row.id)"
@contextmenu="contextMenu"
@stop-refresh="stopRefresh"
/>
</template>
</BaseVirtualScroll>
</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"
@select-row="selectRow(row.id)"
@contextmenu="contextMenu"
@stop-refresh="stopRefresh"
/>
</template>
</BaseVirtualScroll>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </Teleport>
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex';
import arrayToFile from '../libs/arrayToFile'; import arrayToFile from '../libs/arrayToFile';
import { useNotificationsStore } from '@/stores/notifications';
import Schema from '@/ipc-api/Schema'; import Schema from '@/ipc-api/Schema';
import { useConnectionsStore } from '@/stores/connections';
import BaseVirtualScroll from '@/components/BaseVirtualScroll'; import BaseVirtualScroll from '@/components/BaseVirtualScroll';
import ModalProcessesListRow from '@/components/ModalProcessesListRow'; import ModalProcessesListRow from '@/components/ModalProcessesListRow';
import ModalProcessesListContext from '@/components/ModalProcessesListContext'; import ModalProcessesListContext from '@/components/ModalProcessesListContext';
@ -149,6 +152,13 @@ export default {
props: { props: {
connection: Object connection: Object
}, },
emits: ['close'],
setup () {
const { addNotification } = useNotificationsStore();
const { getConnectionName } = useConnectionsStore();
return { addNotification, getConnectionName };
},
data () { data () {
return { return {
resultsSize: 1000, resultsSize: 1000,
@ -167,9 +177,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
getConnectionName: 'connections/getConnectionName'
}),
connectionName () { connectionName () {
return this.getConnectionName(this.connection.uid); return this.getConnectionName(this.connection.uid);
}, },
@ -203,15 +210,12 @@ export default {
this.getProcessesList(); this.getProcessesList();
window.addEventListener('resize', this.resizeResults); window.addEventListener('resize', this.resizeResults);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey, { capture: true }); window.removeEventListener('keydown', this.onKey, { capture: true });
window.removeEventListener('resize', this.resizeResults); window.removeEventListener('resize', this.resizeResults);
clearInterval(this.refreshInterval); clearInterval(this.refreshInterval);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification'
}),
async getProcessesList () { async getProcessesList () {
this.isQuering = true; this.isQuering = true;
@ -284,14 +288,14 @@ export default {
this.clearRefresh(); this.clearRefresh();
}, },
selectRow (row) { selectRow (row) {
this.selectedRow = row; this.selectedRow = Number(row);
}, },
contextMenu (event, cell) { contextMenu (event, cell) {
if (event.target.localName === 'input') return; if (event.target.localName === 'input') return;
this.stopRefresh(); this.stopRefresh();
this.selectedCell = cell; this.selectedCell = cell;
this.selectedRow = cell.id; this.selectedRow = Number(cell.id);
this.contextEvent = event; this.contextEvent = event;
this.isContext = true; this.isContext = true;
}, },

View File

@ -52,6 +52,7 @@ export default {
selectedRow: Number, selectedRow: Number,
selectedCell: Object selectedCell: Object
}, },
emits: ['close-context', 'copy-cell', 'copy-row', 'kill-process'],
computed: { computed: {
}, },
methods: { methods: {

View File

@ -12,7 +12,7 @@
class="cell-content" class="cell-content"
:class="`${isNull(col)} type-${typeof col === 'number' ? 'int' : 'varchar'}`" :class="`${isNull(col)} type-${typeof col === 'number' ? 'int' : 'varchar'}`"
@dblclick="dblClick(cKey)" @dblclick="dblClick(cKey)"
>{{ col | cutText }}</span> >{{ cutText(col) }}</span>
</div> </div>
<ConfirmModal <ConfirmModal
v-if="isInfoModal" v-if="isInfoModal"
@ -53,15 +53,10 @@ export default {
ConfirmModal, ConfirmModal,
TextEditor TextEditor
}, },
filters: {
cutText (val) {
if (typeof val !== 'string') return val;
return val.length > 250 ? `${val.substring(0, 250)}[...]` : val;
}
},
props: { props: {
row: Object row: Object
}, },
emits: ['select-row', 'contextmenu', 'stop-refresh'],
data () { data () {
return { return {
isInlineEditor: {}, isInlineEditor: {},
@ -95,6 +90,10 @@ export default {
this.editingField = null; this.editingField = null;
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
} }
},
cutText (val) {
if (typeof val !== 'string') return val;
return val.length > 250 ? `${val.substring(0, 250)}[...]` : val;
} }
} }
}; };

View File

@ -1,327 +1,332 @@
<template> <template>
<div id="settings" class="modal active"> <Teleport to="#window-content">
<a class="modal-overlay c-hand" @click="closeModal" /> <div id="settings" class="modal active">
<div class="modal-container"> <a class="modal-overlay c-hand" @click="closeModal" />
<div class="modal-header pl-2"> <div class="modal-container">
<div class="modal-title h6"> <div class="modal-header pl-2">
<div class="d-flex"> <div class="modal-title h6">
<i class="mdi mdi-24px mdi-cog mr-1" /> <div class="d-flex">
<span class="cut-text">{{ $t('word.settings') }}</span> <i class="mdi mdi-24px mdi-cog mr-1" />
<span class="cut-text">{{ $t('word.settings') }}</span>
</div>
</div> </div>
<a class="btn btn-clear c-hand" @click="closeModal" />
</div> </div>
<a class="btn btn-clear c-hand" @click="closeModal" /> <div class="modal-body p-0">
</div> <div class="panel">
<div class="modal-body p-0"> <div class="panel-nav">
<div class="panel"> <ul class="tab tab-block">
<div class="panel-nav"> <li
<ul class="tab tab-block"> class="tab-item c-hand"
<li :class="{'active': selectedTab === 'general'}"
class="tab-item c-hand" @click="selectTab('general')"
:class="{'active': selectedTab === 'general'}" >
@click="selectTab('general')" <a class="tab-link">{{ $t('word.general') }}</a>
> </li>
<a class="tab-link">{{ $t('word.general') }}</a> <li
</li> class="tab-item c-hand"
<li :class="{'active': selectedTab === 'themes'}"
class="tab-item c-hand" @click="selectTab('themes')"
:class="{'active': selectedTab === 'themes'}" >
@click="selectTab('themes')" <a class="tab-link">{{ $t('word.themes') }}</a>
> </li>
<a class="tab-link">{{ $t('word.themes') }}</a> <li
</li> v-if="updateStatus !== 'disabled'"
<li class="tab-item c-hand"
v-if="updateStatus !== 'disabled'" :class="{'active': selectedTab === 'update'}"
class="tab-item c-hand" @click="selectTab('update')"
:class="{'active': selectedTab === 'update'}" >
@click="selectTab('update')" <a class="tab-link" :class="{'badge badge-update': hasUpdates}">{{ $t('word.update') }}</a>
> </li>
<a class="tab-link" :class="{'badge badge-update': hasUpdates}">{{ $t('word.update') }}</a> <li
</li> class="tab-item c-hand"
<li :class="{'active': selectedTab === 'changelog'}"
class="tab-item c-hand" @click="selectTab('changelog')"
:class="{'active': selectedTab === 'changelog'}" >
@click="selectTab('changelog')" <a class="tab-link">{{ $t('word.changelog') }}</a>
> </li>
<a class="tab-link">{{ $t('word.changelog') }}</a> <li
</li> class="tab-item c-hand"
<li :class="{'active': selectedTab === 'about'}"
class="tab-item c-hand" @click="selectTab('about')"
:class="{'active': selectedTab === 'about'}" >
@click="selectTab('about')" <a class="tab-link">{{ $t('word.about') }}</a>
> </li>
<a class="tab-link">{{ $t('word.about') }}</a> </ul>
</li> </div>
</ul> <div v-show="selectedTab === 'general'" class="panel-body py-4">
</div> <div class="container">
<div v-show="selectedTab === 'general'" class="panel-body py-4"> <form class="form-horizontal columns">
<div class="container"> <div class="column col-12 h6 text-uppercase mb-1">
<form class="form-horizontal columns"> {{ $t('word.application') }}
<div class="column col-12 h6 text-uppercase mb-1"> </div>
{{ $t('word.application') }} <div class="column col-9 col-sm-12 mb-2">
</div> <div class="form-group">
<div class="column col-9 col-sm-12 mb-2"> <div class="col-7 col-sm-12">
<div class="form-group"> <label class="form-label">
<div class="col-7 col-sm-12"> <i class="mdi mdi-18px mdi-translate mr-1" />
<label class="form-label"> {{ $t('word.language') }}
<i class="mdi mdi-18px mdi-translate mr-1" /> </label>
{{ $t('word.language') }} </div>
</label> <div class="col-5 col-sm-12">
</div> <select
<div class="col-5 col-sm-12"> v-model="localLocale"
<select class="form-select"
v-model="localLocale" @change="changeLocale(localLocale)"
class="form-select"
@change="changeLocale(localLocale)"
>
<option
v-for="(locale, key) in locales"
: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>
</div> <div class="form-group">
<div class="form-group"> <div class="col-7 col-sm-12">
<div class="col-7 col-sm-12"> <label class="form-label">
<label class="form-label"> {{ $t('message.dataTabPageSize') }}
{{ $t('message.dataTabPageSize') }} </label>
</label> </div>
</div> <div class="col-5 col-sm-12">
<div class="col-5 col-sm-12"> <select
<select v-model="localPageSize"
v-model="localPageSize" class="form-select"
class="form-select" @change="changePageSize(+localPageSize)"
@change="changePageSize(+localPageSize)"
>
<option
v-for="size in pageSizes"
:key="size"
> >
{{ size }} <option
</option> v-for="size in pageSizes"
</select> :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 mb-0">
<div class="col-7 col-sm-12">
<label class="form-label">
{{ $t('message.disableBlur') }}
</label>
</div>
<div class="col-5 col-sm-12">
<label class="form-switch d-inline-block" @click.prevent="toggleDisableBlur">
<input type="checkbox" :checked="disableBlur">
<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 mb-0">
<div class="col-7 col-sm-12"> <div class="column col-12 h6 mt-4 text-uppercase mb-1">
<label class="form-label"> {{ $t('word.editor') }}
{{ $t('message.restorePreviourSession') }} </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-5 col-sm-12"> <div class="form-group mb-0">
<label class="form-switch d-inline-block" @click.prevent="toggleRestoreSession"> <div class="col-7 col-sm-12">
<input type="checkbox" :checked="restoreTabs"> <label class="form-label">
<i class="form-icon" /> {{ $t('message.wrapLongLines') }}
</label> </label>
</div>
<div class="col-5 col-sm-12">
<label class="form-switch d-inline-block" @click.prevent="toggleLineWrap">
<input type="checkbox" :checked="selectedLineWrap">
<i class="form-icon" />
</label>
</div>
</div> </div>
</div> </div>
<div class="form-group mb-0"> </form>
<div class="col-7 col-sm-12"> </div>
<label class="form-label"> </div>
{{ $t('message.disableBlur') }}
</label> <div v-show="selectedTab === 'themes'" class="panel-body py-4">
</div> <div class="container">
<div class="col-5 col-sm-12"> <div class="columns">
<label class="form-switch d-inline-block" @click.prevent="toggleDisableBlur"> <div class="column col-12 h6 text-uppercase mb-2">
<input type="checkbox" :checked="disableBlur"> {{ $t('message.applicationTheme') }}
<i class="form-icon" /> </div>
</label> <div
class="column col-6 c-hand theme-block"
:class="{'selected': applicationTheme === 'dark'}"
@click="changeApplicationTheme('dark')"
>
<img src="../images/dark.png" 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> </div>
<div class="form-group"> <div
<div class="col-7 col-sm-12"> class="column col-6 c-hand theme-block"
<label class="form-label"> :class="{'selected': applicationTheme === 'light'}"
{{ $t('message.notificationsTimeout') }} @click="changeApplicationTheme('light')"
</label> >
</div> <img src="../images/light.png" class="img-responsive img-fit-cover s-rounded">
<div class="col-5 col-sm-12"> <div class="theme-name text-dark">
<div class="input-group"> <i class="mdi mdi-white-balance-sunny mdi-48px" />
<input <div class="h6 mt-4">
v-model="localTimeout" {{ $t('word.light') }}
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> </div>
<div class="column col-12 h6 mt-4 text-uppercase mb-1"> <div class="columns mt-4">
{{ $t('word.editor') }} <div class="column col-12 h6 text-uppercase mb-2 mt-4">
</div> {{ $t('message.editorTheme') }}
<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="form-group mb-0"> <div class="column col-6 h5 mb-4">
<div class="col-7 col-sm-12"> <select
<label class="form-label"> v-model="localEditorTheme"
{{ $t('message.wrapLongLines') }} class="form-select"
</label> @change="changeEditorTheme(localEditorTheme)"
</div>
<div class="col-5 col-sm-12">
<label class="form-switch d-inline-block" @click.prevent="toggleLineWrap">
<input type="checkbox" :checked="selectedLineWrap">
<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="../images/dark.png" 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="../images/light.png" 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 class="columns mt-4">
<div class="column col-12 h6 text-uppercase mb-2 mt-4">
{{ $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 <optgroup
v-for="theme in group.themes" v-for="group in editorThemes"
:key="theme.name" :key="group.group"
:value="theme.code" :label="group.group"
:selected="editorTheme === theme.code"
> >
{{ theme.name }} <option
</option> v-for="theme in group.themes"
</optgroup> :key="theme.name"
</select> :value="theme.code"
</div> :selected="editorTheme === theme.code"
<div class="column col-6 mb-4"> >
<div class="btn-group btn-group-block"> {{ theme.name }}
<button </option>
class="btn btn-dark cut-text" </optgroup>
:class="{'active': editorFontSize === 'small'}" </select>
@click="changeEditorFontSize('small')" </div>
> <div class="column col-6 mb-4">
{{ $t('word.small') }} <div class="btn-group btn-group-block">
</button> <button
<button class="btn btn-dark cut-text"
class="btn btn-dark cut-text" :class="{'active': editorFontSize === 'small'}"
:class="{'active': editorFontSize === 'medium'}" @click="changeEditorFontSize('small')"
@click="changeEditorFontSize('medium')" >
> {{ $t('word.small') }}
{{ $t('word.medium') }} </button>
</button> <button
<button class="btn btn-dark cut-text"
class="btn btn-dark cut-text" :class="{'active': editorFontSize === 'medium'}"
:class="{'active': editorFontSize === 'large'}" @click="changeEditorFontSize('medium')"
@click="changeEditorFontSize('large')" >
> {{ $t('word.medium') }}
{{ $t('word.large') }} </button>
</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
:model-value="exampleQuery"
mode="sql"
:workspace="workspace"
:read-only="true"
:height="270"
/>
</div> </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>
<div v-show="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"> <div v-show="selectedTab === 'changelog'" class="panel-body py-4">
<ModalSettingsChangelog /> <ModalSettingsChangelog />
</div> </div>
<div v-show="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="../images/logo.svg" width="128"> <img src="../images/logo.svg" width="128">
<h4>{{ appName }}</h4> <h4>{{ appName }}</h4>
<p class="mb-2"> <p class="mb-2">
{{ $t('word.version') }} {{ appVersion }}<br> {{ $t('word.version') }} {{ appVersion }}<br>
<a class="c-hand" @click="openOutside('https://github.com/antares-sql/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> <a class="c-hand" @click="openOutside('https://github.com/antares-sql/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')">{{ appAuthor }}</a></small><br> <small>{{ $t('word.author') }} <a class="c-hand" @click="openOutside('https://github.com/Fabio286')">{{ appAuthor }}</a></small><br>
</p> </p>
<div class="mb-2"> <div class="mb-2">
<small class="d-block text-uppercase">{{ $t('word.contributors') }}:</small> <small class="d-block text-uppercase">{{ $t('word.contributors') }}:</small>
<div class="d-block py-1"> <div class="d-block py-1">
<small v-for="(contributor, i) in otherContributors" :key="i">{{ i !== 0 ? ', ' : '' }}{{ contributor }}</small> <small v-for="(contributor, i) in otherContributors" :key="i">{{ i !== 0 ? ', ' : '' }}{{ contributor }}</small>
</div>
<small>{{ $t('message.madeWithJS') }}</small>
</div> </div>
<small>{{ $t('message.madeWithJS') }}</small>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </Teleport>
</template> </template>
<script> <script>
import { mapActions, mapGetters } from 'vuex'; import { shell } from 'electron';
import { storeToRefs } from 'pinia';
import { useApplicationStore } from '@/stores/application';
import { useSettingsStore } from '@/stores/settings';
import { useWorkspacesStore } from '@/stores/workspaces';
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 ModalSettingsChangelog from '@/components/ModalSettingsChangelog';
import BaseTextEditor from '@/components/BaseTextEditor'; import BaseTextEditor from '@/components/BaseTextEditor';
const { shell } = require('electron');
export default { export default {
name: 'ModalSettings', name: 'ModalSettings',
@ -330,6 +335,79 @@ export default {
ModalSettingsChangelog, ModalSettingsChangelog,
BaseTextEditor BaseTextEditor
}, },
setup () {
const applicationStore = useApplicationStore();
const settingsStore = useSettingsStore();
const workspacesStore = useWorkspacesStore();
const {
selectedSettingTab,
updateStatus
} = storeToRefs(applicationStore);
const {
locale: selectedLocale,
dataTabLimit: pageSize,
autoComplete: selectedAutoComplete,
lineWrap: selectedLineWrap,
notificationsTimeout,
restoreTabs,
disableBlur,
applicationTheme,
editorTheme,
editorFontSize
} = storeToRefs(settingsStore);
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
changeLocale,
changePageSize,
changeRestoreTabs,
changeDisableBlur,
changeAutoComplete,
changeLineWrap,
changeApplicationTheme,
changeEditorTheme,
changeEditorFontSize,
updateNotificationsTimeout
} = settingsStore;
const {
hideSettingModal,
appName,
appVersion
} = applicationStore;
const { getWorkspace } = workspacesStore;
return {
appName,
appVersion,
selectedSettingTab,
updateStatus,
closeModal: hideSettingModal,
selectedLocale,
pageSize,
selectedAutoComplete,
selectedLineWrap,
notificationsTimeout,
restoreTabs,
disableBlur,
applicationTheme,
editorTheme,
editorFontSize,
changeLocale,
changePageSize,
changeRestoreTabs,
changeDisableBlur,
changeAutoComplete,
changeLineWrap,
changeApplicationTheme,
changeEditorTheme,
changeEditorFontSize,
updateNotificationsTimeout,
selectedWorkspace,
getWorkspace
};
},
data () { data () {
return { return {
appAuthor: 'Fabio Di Stasio', appAuthor: 'Fabio Di Stasio',
@ -392,24 +470,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
appName: 'application/appName',
appVersion: 'application/appVersion',
selectedSettingTab: 'application/selectedSettingTab',
selectedLocale: 'settings/getLocale',
pageSize: 'settings/getDataTabLimit',
selectedAutoComplete: 'settings/getAutoComplete',
selectedLineWrap: 'settings/getLineWrap',
notificationsTimeout: 'settings/getNotificationsTimeout',
restoreTabs: 'settings/getRestoreTabs',
disableBlur: 'settings/getDisableBlur',
applicationTheme: 'settings/getApplicationTheme',
editorTheme: 'settings/getEditorTheme',
editorFontSize: 'settings/getEditorFontSize',
updateStatus: 'application/getUpdateStatus',
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
locales () { locales () {
const locales = []; const locales = [];
for (const locale of this.$i18n.availableLocales) for (const locale of this.$i18n.availableLocales)
@ -455,23 +515,10 @@ ORDER BY
this.selectedTab = this.selectedSettingTab; this.selectedTab = this.selectedSettingTab;
window.addEventListener('keydown', this.onKey); window.addEventListener('keydown', this.onKey);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
closeModal: 'application/hideSettingModal',
changeLocale: 'settings/changeLocale',
changePageSize: 'settings/changePageSize',
changeRestoreTabs: 'settings/changeRestoreTabs',
changeDisableBlur: 'settings/changeDisableBlur',
changeAutoComplete: 'settings/changeAutoComplete',
changeLineWrap: 'settings/changeLineWrap',
changeApplicationTheme: 'settings/changeApplicationTheme',
changeEditorTheme: 'settings/changeEditorTheme',
changeEditorFontSize: 'settings/changeEditorFontSize',
updateNotificationsTimeout: 'settings/updateNotificationsTimeout'
}),
selectTab (tab) { selectTab (tab) {
this.selectedTab = tab; this.selectedTab = tab;
}, },

View File

@ -15,15 +15,19 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex';
import { marked } from 'marked'; import { marked } from 'marked';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
import { useApplicationStore } from '@/stores/application';
export default { export default {
name: 'ModalSettingsChangelog', name: 'ModalSettingsChangelog',
components: { components: {
BaseLoader BaseLoader
}, },
setup () {
const { appVersion } = useApplicationStore();
return { appVersion };
},
data () { data () {
return { return {
changelog: '', changelog: '',
@ -32,9 +36,6 @@ export default {
isError: false isError: false
}; };
}, },
computed: {
...mapGetters({ appVersion: 'application/appVersion' })
},
created () { created () {
this.getChangelog(); this.getChangelog();
}, },

View File

@ -53,17 +53,33 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex';
import { ipcRenderer, shell } from 'electron'; import { ipcRenderer, shell } from 'electron';
import { storeToRefs } from 'pinia';
import { useApplicationStore } from '@/stores/application';
import { useSettingsStore } from '@/stores/settings';
export default { export default {
name: 'ModalSettingsUpdate', name: 'ModalSettingsUpdate',
setup () {
const applicationStore = useApplicationStore();
const settingsStore = useSettingsStore();
const {
updateStatus,
getDownloadProgress
} = storeToRefs(applicationStore);
const { allowPrerelease } = storeToRefs(settingsStore);
const { changeAllowPrerelease } = settingsStore;
return {
updateStatus,
downloadPercentage: getDownloadProgress,
allowPrerelease,
changeAllowPrerelease
};
},
computed: { computed: {
...mapGetters({
updateStatus: 'application/getUpdateStatus',
downloadPercentage: 'application/getDownloadProgress',
allowPrerelease: 'settings/getAllowPrerelease'
}),
updateMessage () { updateMessage () {
switch (this.updateStatus) { switch (this.updateStatus) {
case 'noupdate': case 'noupdate':
@ -86,9 +102,6 @@ export default {
} }
}, },
methods: { methods: {
...mapActions({
changeAllowPrerelease: 'settings/changeAllowPrerelease'
}),
openOutside (link) { openOutside (link) {
shell.openExternal(link); shell.openExternal(link);
}, },

View File

@ -12,13 +12,16 @@
import * as ace from 'ace-builds'; import * as ace from 'ace-builds';
import 'ace-builds/webpack-resolver'; import 'ace-builds/webpack-resolver';
import '../libs/ext-language_tools'; import '../libs/ext-language_tools';
import { mapGetters, mapActions } from 'vuex'; import { storeToRefs } from 'pinia';
import { uidGen } from 'common/libs/uidGen';
import { useApplicationStore } from '@/stores/application';
import { useSettingsStore } from '@/stores/settings';
import Tables from '@/ipc-api/Tables'; import Tables from '@/ipc-api/Tables';
export default { export default {
name: 'QueryEditor', name: 'QueryEditor',
props: { props: {
value: String, modelValue: String,
workspace: Object, workspace: Object,
isSelected: Boolean, isSelected: Boolean,
schema: { type: String, default: '' }, schema: { type: String, default: '' },
@ -26,23 +29,42 @@ export default {
readOnly: { type: Boolean, default: false }, readOnly: { type: Boolean, default: false },
height: { type: Number, default: 200 } height: { type: Number, default: 200 }
}, },
emits: ['update:modelValue'],
setup () {
const editor = null;
const applicationStore = useApplicationStore();
const settingsStore = useSettingsStore();
const { setBaseCompleters } = applicationStore;
const { baseCompleter } = storeToRefs(applicationStore);
const {
editorTheme,
editorFontSize,
autoComplete,
lineWrap
} = storeToRefs(settingsStore);
return {
editor,
baseCompleter,
setBaseCompleters,
editorTheme,
editorFontSize,
autoComplete,
lineWrap
};
},
data () { data () {
return { return {
editor: null, cursorPosition: 0,
fields: [], fields: [],
customCompleter: [], customCompleter: [],
id: null, id: uidGen(),
lastSchema: null lastSchema: null
}; };
}, },
computed: { computed: {
...mapGetters({
editorTheme: 'settings/getEditorTheme',
editorFontSize: 'settings/getEditorFontSize',
autoComplete: 'settings/getAutoComplete',
lineWrap: 'settings/getLineWrap',
baseCompleter: 'application/getBaseCompleter'
}),
tables () { tables () {
return this.workspace return this.workspace
? this.workspace.structure.filter(schema => schema.name === this.schema) ? this.workspace.structure.filter(schema => schema.name === this.schema)
@ -127,11 +149,8 @@ export default {
return 'sql'; return 'sql';
} }
}, },
cursorPosition () {
return this.editor.session.doc.positionToIndex(this.editor.getCursorPosition());
},
lastWord () { lastWord () {
const charsBefore = this.value.slice(0, this.cursorPosition); const charsBefore = this.modelValue.slice(0, this.cursorPosition);
const words = charsBefore.replaceAll('\n', ' ').split(' ').filter(Boolean); const words = charsBefore.replaceAll('\n', ' ').split(' ').filter(Boolean);
return words.pop(); return words.pop();
}, },
@ -155,6 +174,9 @@ export default {
} }
}, },
watch: { watch: {
modelValue () {
this.cursorPosition = this.editor.session.doc.positionToIndex(this.editor.getCursorPosition());
},
editorTheme () { editorTheme () {
if (this.editor) if (this.editor)
this.editor.setTheme(`ace/theme/${this.editorTheme}`); this.editor.setTheme(`ace/theme/${this.editorTheme}`);
@ -205,14 +227,13 @@ export default {
} }
}, },
created () { created () {
this.id = this._uid;
this.lastSchema = this.schema; this.lastSchema = this.schema;
}, },
mounted () { mounted () {
this.editor = ace.edit(`editor-${this.id}`, { this.editor = ace.edit(`editor-${this.id}`, {
mode: `ace/mode/${this.mode}`, mode: `ace/mode/${this.mode}`,
theme: `ace/theme/${this.editorTheme}`, theme: `ace/theme/${this.editorTheme}`,
value: this.value, value: this.modelValue,
fontSize: '14px', fontSize: '14px',
printMargin: false, printMargin: false,
readOnly: this.readOnly readOnly: this.readOnly
@ -263,7 +284,7 @@ export default {
this.editor.session.on('change', () => { this.editor.session.on('change', () => {
const content = this.editor.getValue(); const content = this.editor.getValue();
this.$emit('update:value', content); this.$emit('update:modelValue', content);
}); });
this.editor.on('guttermousedown', e => { this.editor.on('guttermousedown', e => {
@ -294,9 +315,6 @@ export default {
}, 20); }, 20);
}, },
methods: { methods: {
...mapActions({
setBaseCompleters: 'application/setBaseCompleter'
}),
setCustomCompleter () { setCustomCompleter () {
this.editor.completers.push({ this.editor.completers.push({
getCompletions: (editor, session, pos, prefix, callback) => { getCompletions: (editor, session, pos, prefix, callback) => {

View File

@ -30,10 +30,12 @@
</template> </template>
<script> <script>
import { mapActions, mapGetters } from 'vuex';
import { uidGen } from 'common/libs/uidGen'; import { uidGen } from 'common/libs/uidGen';
import { useConnectionsStore } from '@/stores/connections';
import { useWorkspacesStore } from '@/stores/workspaces';
import BaseContextMenu from '@/components/BaseContextMenu'; import BaseContextMenu from '@/components/BaseContextMenu';
import ConfirmModal from '@/components/BaseConfirmModal'; import ConfirmModal from '@/components/BaseConfirmModal';
import { storeToRefs } from 'pinia';
export default { export default {
name: 'SettingBarContext', name: 'SettingBarContext',
@ -45,6 +47,26 @@ export default {
contextEvent: MouseEvent, contextEvent: MouseEvent,
contextConnection: Object contextConnection: Object
}, },
emits: ['close-context'],
setup () {
const {
getConnectionName,
addConnection,
deleteConnection
} = useConnectionsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { selectWorkspace } = workspacesStore;
return {
getConnectionName,
addConnection,
deleteConnection,
selectedWorkspace,
selectWorkspace
};
},
data () { data () {
return { return {
isConfirmModal: false, isConfirmModal: false,
@ -52,20 +74,11 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
getConnectionName: 'connections/getConnectionName',
selectedWorkspace: 'workspaces/getSelected'
}),
connectionName () { connectionName () {
return this.getConnectionName(this.contextConnection.uid); return this.getConnectionName(this.contextConnection.uid);
} }
}, },
methods: { methods: {
...mapActions({
addConnection: 'connections/addConnection',
deleteConnection: 'connections/deleteConnection',
selectWorkspace: 'workspaces/selectWorkspace'
}),
confirmDeleteConnection () { confirmDeleteConnection () {
if (this.selectedWorkspace === this.contextConnection.uid) if (this.selectedWorkspace === this.contextConnection.uid)
this.selectWorkspace(); this.selectWorkspace();
@ -77,7 +90,7 @@ export default {
connectionCopy = { connectionCopy = {
...connectionCopy, ...connectionCopy,
uid: uidGen('C'), uid: uidGen('C'),
name: connectionCopy.name ? `${connectionCopy.name}_copy` : '' name: connectionCopy.name ? `${connectionCopy?.name}_copy` : ''
}; };
this.addConnection(connectionCopy); this.addConnection(connectionCopy);

View File

@ -27,17 +27,30 @@
</template> </template>
<script> <script>
import { mapActions, mapGetters } from 'vuex'; import { useApplicationStore } from '@/stores/application';
import { useWorkspacesStore } from '@/stores/workspaces';
import { storeToRefs } from 'pinia';
const { shell } = require('electron'); const { shell } = require('electron');
export default { export default {
name: 'TheFooter', name: 'TheFooter',
setup () {
const applicationStore = useApplicationStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: workspace } = storeToRefs(workspacesStore);
const { appVersion, showSettingModal } = applicationStore;
const { getWorkspace } = workspacesStore;
return {
appVersion,
showSettingModal,
workspace,
getWorkspace
};
},
computed: { computed: {
...mapGetters({
workspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace',
appVersion: 'application/appVersion'
}),
version () { version () {
return this.getWorkspace(this.workspace) ? this.getWorkspace(this.workspace).version : null; return this.getWorkspace(this.workspace) ? this.getWorkspace(this.workspace).version : null;
}, },
@ -48,9 +61,6 @@ export default {
} }
}, },
methods: { methods: {
...mapActions({
showSettingModal: 'application/showSettingModal'
}),
openOutside (link) { openOutside (link) {
shell.openExternal(link); shell.openExternal(link);
} }

View File

@ -4,7 +4,7 @@
@mouseenter="clearTimeouts" @mouseenter="clearTimeouts"
@mouseleave="rearmTimeouts" @mouseleave="rearmTimeouts"
> >
<transition-group name="slide-fade"> <TransitionGroup tag="div" name="slide-fade">
<BaseNotification <BaseNotification
v-for="notification in latestNotifications" v-for="notification in latestNotifications"
:key="notification.uid" :key="notification.uid"
@ -12,29 +12,42 @@
:status="notification.status" :status="notification.status"
@close="removeNotification(notification.uid)" @close="removeNotification(notification.uid)"
/> />
</transition-group> </TransitionGroup>
</div> </div>
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { useNotificationsStore } from '@/stores/notifications';
import { useSettingsStore } from '@/stores/settings';
import BaseNotification from '@/components/BaseNotification'; import BaseNotification from '@/components/BaseNotification';
import { storeToRefs } from 'pinia';
export default { export default {
name: 'TheNotificationsBoard', name: 'TheNotificationsBoard',
components: { components: {
BaseNotification BaseNotification
}, },
setup () {
const notificationsStore = useNotificationsStore();
const settingsStore = useSettingsStore();
const { removeNotification } = notificationsStore;
const { notifications } = storeToRefs(notificationsStore);
const { notificationsTimeout } = storeToRefs(settingsStore);
return {
removeNotification,
notifications,
notificationsTimeout
};
},
data () { data () {
return { return {
timeouts: {} timeouts: {}
}; };
}, },
computed: { computed: {
...mapGetters({
notifications: 'notifications/getNotifications',
notificationsTimeout: 'settings/getNotificationsTimeout'
}),
latestNotifications () { latestNotifications () {
return this.notifications.slice(0, 10); return this.notifications.slice(0, 10);
} }
@ -51,9 +64,6 @@ export default {
} }
}, },
methods: { methods: {
...mapActions({
removeNotification: 'notifications/removeNotification'
}),
clearTimeouts () { clearTimeouts () {
for (const uid in this.timeouts) { for (const uid in this.timeouts) {
clearTimeout(this.timeouts[uid]); clearTimeout(this.timeouts[uid]);

View File

@ -15,7 +15,7 @@
<div> <div>
<div> <div>
<TextEditor <TextEditor
:value.sync="localNotes" v-model="localNotes"
editor-class="textarea-editor" editor-class="textarea-editor"
mode="markdown" mode="markdown"
:auto-focus="true" :auto-focus="true"
@ -29,9 +29,11 @@
</template> </template>
<script> <script>
import { mapActions, mapGetters } from 'vuex'; import { storeToRefs } from 'pinia';
import { useApplicationStore } from '@/stores/application';
import ConfirmModal from '@/components/BaseConfirmModal'; import ConfirmModal from '@/components/BaseConfirmModal';
import TextEditor from '@/components/BaseTextEditor'; import TextEditor from '@/components/BaseTextEditor';
import { useScratchpadStore } from '@/stores/scratchpad';
export default { export default {
name: 'TheScratchpad', name: 'TheScratchpad',
@ -39,17 +41,26 @@ export default {
ConfirmModal, ConfirmModal,
TextEditor TextEditor
}, },
emits: ['hide'],
setup () {
const applicationStore = useApplicationStore();
const scratchpadStore = useScratchpadStore();
const { notes } = storeToRefs(scratchpadStore);
const { changeNotes } = scratchpadStore;
return {
notes,
hideScratchpad: applicationStore.hideScratchpad,
changeNotes
};
},
data () { data () {
return { return {
localNotes: '', localNotes: '',
debounceTimeout: null debounceTimeout: null
}; };
}, },
computed: {
...mapGetters({
notes: 'scratchpad/getNotes'
})
},
watch: { watch: {
localNotes () { localNotes () {
clearTimeout(this.debounceTimeout); clearTimeout(this.debounceTimeout);
@ -63,10 +74,6 @@ export default {
this.localNotes = this.notes; this.localNotes = this.notes;
}, },
methods: { methods: {
...mapActions({
hideScratchpad: 'application/hideScratchpad',
changeNotes: 'scratchpad/changeNotes'
}),
hideModal () { hideModal () {
this.$emit('hide'); this.$emit('hide');
} }

View File

@ -10,22 +10,23 @@
<ul class="settingbar-elements"> <ul class="settingbar-elements">
<Draggable <Draggable
v-model="connections" v-model="connections"
:item-key="'uid'"
@start="isDragging = true" @start="isDragging = true"
@end="dragStop" @end="dragStop"
> >
<li <template #item="{element}">
v-for="connection in connections" <li
: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': element.uid === selectedWorkspace}"
:class="{'selected': connection.uid === selectedWorkspace}" @click.stop="selectWorkspace(element.uid)"
@click.stop="selectWorkspace(connection.uid)" @contextmenu.prevent="contextMenu($event, element)"
@contextmenu.prevent="contextMenu($event, connection)" @mouseover.self="tooltipPosition"
@mouseover.self="tooltipPosition" >
> <i class="settingbar-element-icon dbi" :class="`dbi-${element.client} ${getStatusBadge(element.uid)}`" />
<i class="settingbar-element-icon dbi" :class="`dbi-${connection.client} ${getStatusBadge(connection.uid)}`" /> <span v-if="!isDragging" class="ex-tooltip-content">{{ getConnectionName(element.uid) }}</span>
<span v-if="!isDragging" class="ex-tooltip-content">{{ getConnectionName(connection.uid) }}</span> </li>
</li> </template>
</Draggable> </Draggable>
<li <li
class="settingbar-element btn btn-link ex-tooltip" class="settingbar-element btn btn-link ex-tooltip"
@ -55,7 +56,10 @@
</template> </template>
<script> <script>
import { mapActions, mapGetters } from 'vuex'; import { storeToRefs } from 'pinia';
import { useApplicationStore } from '@/stores/application';
import { useConnectionsStore } from '@/stores/connections';
import { useWorkspacesStore } from '@/stores/workspaces';
import Draggable from 'vuedraggable'; import Draggable from 'vuedraggable';
import SettingBarContext from '@/components/SettingBarContext'; import SettingBarContext from '@/components/SettingBarContext';
@ -65,6 +69,32 @@ export default {
Draggable, Draggable,
SettingBarContext SettingBarContext
}, },
setup () {
const applicationStore = useApplicationStore();
const connectionsStore = useConnectionsStore();
const workspacesStore = useWorkspacesStore();
const { updateStatus } = storeToRefs(applicationStore);
const { connections: getConnections } = storeToRefs(connectionsStore);
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { showSettingModal, showScratchpad } = applicationStore;
const { getConnectionName, updateConnections } = connectionsStore;
const { getWorkspace, selectWorkspace } = workspacesStore;
return {
applicationStore,
updateStatus,
showSettingModal,
showScratchpad,
getConnections,
getConnectionName,
updateConnections,
selectedWorkspace,
getWorkspace,
selectWorkspace
};
},
data () { data () {
return { return {
dragElement: null, dragElement: null,
@ -76,13 +106,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
getConnections: 'connections/getConnections',
getConnectionName: 'connections/getConnectionName',
getWorkspace: 'workspaces/getWorkspace',
selectedWorkspace: 'workspaces/getSelected',
updateStatus: 'application/getUpdateStatus'
}),
connections: { connections: {
get () { get () {
return this.getConnections; return this.getConnections;
@ -96,12 +119,6 @@ export default {
} }
}, },
methods: { methods: {
...mapActions({
updateConnections: 'connections/updateConnections',
showSettingModal: 'application/showSettingModal',
showScratchpad: 'application/showScratchpad',
selectWorkspace: 'workspaces/selectWorkspace'
}),
contextMenu (event, connection) { contextMenu (event, connection) {
this.contextEvent = event; this.contextEvent = event;
this.contextConnection = connection; this.contextConnection = connection;

View File

@ -55,10 +55,26 @@
<script> <script>
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import { getCurrentWindow } from '@electron/remote'; import { getCurrentWindow } from '@electron/remote';
import { mapGetters } from 'vuex'; import { useConnectionsStore } from '@/stores/connections';
import { useWorkspacesStore } from '@/stores/workspaces';
import { storeToRefs } from 'pinia';
export default { export default {
name: 'TheTitleBar', name: 'TheTitleBar',
setup () {
const { getConnectionName } = useConnectionsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { getWorkspace } = workspacesStore;
return {
getConnectionName,
selectedWorkspace,
getWorkspace
};
},
data () { data () {
return { return {
w: getCurrentWindow(), w: getCurrentWindow(),
@ -68,11 +84,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
getConnectionName: 'connections/getConnectionName',
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
windowTitle () { windowTitle () {
if (!this.selectedWorkspace) return ''; if (!this.selectedWorkspace) return '';
if (this.selectedWorkspace === 'NEW') return this.$t('message.createNewConnection'); if (this.selectedWorkspace === 'NEW') return this.$t('message.createNewConnection');
@ -87,7 +98,7 @@ export default {
created () { created () {
window.addEventListener('resize', this.onResize); window.addEventListener('resize', this.onResize);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.onResize); window.removeEventListener('resize', this.onResize);
}, },
methods: { methods: {

View File

@ -1,264 +1,265 @@
<template> <template>
<div v-show="isSelected" class="workspace column columns col-gapless"> <div v-show="isSelected" class="workspace column columns col-gapless">
<WorkspaceExploreBar <WorkspaceExploreBar
v-if="workspace.connectionStatus === 'connected'" v-if="workspace?.connectionStatus === 'connected'"
:connection="connection" :connection="connection"
:is-selected="isSelected" :is-selected="isSelected"
/> />
<div v-if="workspace.connectionStatus === 'connected'" class="workspace-tabs column columns col-gapless"> <div v-if="workspace?.connectionStatus === 'connected'" class="workspace-tabs column columns col-gapless">
<Draggable <Draggable
ref="tabWrap" ref="tabWrap"
v-model="draggableTabs" v-model="draggableTabs"
tag="ul" tag="ul"
item-key="uid"
group="tabs" group="tabs"
class="tab tab-block column col-12" class="tab tab-block column col-12"
draggable=".tab-draggable" draggable=".tab-draggable"
@mouseover.native="addWheelEvent" @mouseover="addWheelEvent"
> >
<li <template #item="{element}">
v-for="(tab, i) of draggableTabs" <li
:key="i" class="tab-item tab-draggable"
class="tab-item tab-draggable" :draggable="true"
draggable="true" :class="{'active': selectedTab === element.uid}"
:class="{'active': selectedTab === tab.uid}" @mousedown.left="selectTab({uid: workspace.uid, tab: element.uid})"
@mousedown.left="selectTab({uid: workspace.uid, tab: tab.uid})" @mouseup.middle="closeTab(element)"
@mouseup.middle="closeTab(tab)"
>
<a
v-if="tab.type === 'query'"
class="tab-link"
:class="{'badge': tab.isChanged}"
> >
<i class="mdi mdi-18px mdi-code-tags mr-1" /> <a
<span> v-if="element.type === 'query'"
<span>{{ tab.content || 'Query' | cutText }} #{{ tab.index }}</span> class="tab-link"
<span :class="{'badge': element.isChanged}"
class="btn btn-clear" >
:title="$t('word.close')" <i class="mdi mdi-18px mdi-code-tags mr-1" />
@mousedown.left.stop <span>
@click.stop="closeTab(tab)" <span>{{ cutText(element.content || 'Query') }} #{{ element.index }}</span>
/> <span
</span> class="btn btn-clear"
</a> :title="$t('word.close')"
@mousedown.left.stop
@click.stop="closeTab(element)"
/>
</span>
</a>
<a <a
v-else-if="tab.type === 'temp-data'" v-else-if="element.type === 'temp-data'"
class="tab-link" class="tab-link"
@dblclick="openAsPermanentTab(tab)" @dblclick="openAsPermanentTab(element)"
> >
<i class="mdi mdi-18px mr-1" :class="tab.elementType === 'view' ? 'mdi-table-eye' : 'mdi-table'" /> <i class="mdi mdi-18px mr-1" :class="element.elementType === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
<span :title="`${$t('word.data').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`"> <span :title="`${$t('word.data').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
<span class=" text-italic">{{ tab.elementName | cutText }}</span> <span class=" text-italic">{{ cutText(element.elementName) }}</span>
<span <span
class="btn btn-clear" class="btn btn-clear"
:title="$t('word.close')" :title="$t('word.close')"
@mousedown.left.stop @mousedown.left.stop
@click.stop="closeTab(tab)" @click.stop="closeTab(element)"
/> />
</span> </span>
</a> </a>
<a v-else-if="tab.type === 'data'" class="tab-link"> <a v-else-if="element.type === 'data'" class="tab-link">
<i class="mdi mdi-18px mr-1" :class="tab.elementType === 'view' ? 'mdi-table-eye' : 'mdi-table'" /> <i class="mdi mdi-18px mr-1" :class="element.elementType === 'view' ? 'mdi-table-eye' : 'mdi-table'" />
<span :title="`${$t('word.data').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`"> <span :title="`${$t('word.data').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
{{ tab.elementName | cutText }} {{ cutText(element.elementName) }}
<span <span
class="btn btn-clear" class="btn btn-clear"
:title="$t('word.close')" :title="$t('word.close')"
@mousedown.left.stop @mousedown.left.stop
@click.stop="closeTab(tab)" @click.stop="closeTab(element)"
/> />
</span> </span>
</a> </a>
<a <a
v-else-if="tab.type === 'new-table'" v-else-if="element.type === 'new-table'"
class="tab-link" class="tab-link"
:class="{'badge': tab.isChanged}" :class="{'badge': element.isChanged}"
> >
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" /> <i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`"> <span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
{{ $t('message.newTable') }} {{ $t('message.newTable') }}
<span <span
class="btn btn-clear" class="btn btn-clear"
:title="$t('word.close')" :title="$t('word.close')"
@mousedown.left.stop @mousedown.left.stop
@click.stop="closeTab(tab)" @click.stop="closeTab(element)"
/> />
</span> </span>
</a> </a>
<a <a
v-else-if="tab.type === 'table-props'" v-else-if="element.type === 'table-props'"
class="tab-link" class="tab-link"
:class="{'badge': tab.isChanged}" :class="{'badge': element.isChanged}"
> >
<i class="mdi mdi-tune-vertical-variant mdi-18px mr-1" /> <i class="mdi mdi-tune-vertical-variant mdi-18px mr-1" />
<span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`"> <span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
{{ tab.elementName | cutText }} {{ cutText(element.elementName) }}
<span <span
class="btn btn-clear" class="btn btn-clear"
:title="$t('word.close')" :title="$t('word.close')"
@mousedown.left.stop @mousedown.left.stop
@click.stop="closeTab(tab)" @click.stop="closeTab(element)"
/> />
</span> </span>
</a> </a>
<a <a
v-else-if="tab.type === 'view-props'" v-else-if="element.type === 'view-props'"
class="tab-link" class="tab-link"
:class="{'badge': tab.isChanged}" :class="{'badge': element.isChanged}"
> >
<i class="mdi mdi-tune-vertical-variant mdi-18px mr-1" /> <i class="mdi mdi-tune-vertical-variant mdi-18px mr-1" />
<span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.view`)}`"> <span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.view`)}`">
{{ tab.elementName | cutText }} {{ cutText(element.elementName) }}
<span <span
class="btn btn-clear" class="btn btn-clear"
:title="$t('word.close')" :title="$t('word.close')"
@mousedown.left.stop @mousedown.left.stop
@click.stop="closeTab(tab)" @click.stop="closeTab(element)"
/> />
</span> </span>
</a> </a>
<a <a
v-else-if="tab.type === 'new-view'" v-else-if="element.type === 'new-view'"
class="tab-link" class="tab-link"
:class="{'badge': tab.isChanged}" :class="{'badge': element.isChanged}"
> >
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" /> <i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`"> <span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
{{ $t('message.newView') }} {{ $t('message.newView') }}
<span <span
class="btn btn-clear" class="btn btn-clear"
:title="$t('word.close')" :title="$t('word.close')"
@mousedown.left.stop @mousedown.left.stop
@click.stop="closeTab(tab)" @click.stop="closeTab(element)"
/> />
</span> </span>
</a> </a>
<a <a
v-else-if="tab.type === 'new-trigger'" v-else-if="element.type === 'new-trigger'"
class="tab-link" class="tab-link"
:class="{'badge': tab.isChanged}" :class="{'badge': element.isChanged}"
> >
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" /> <i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`"> <span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
{{ $t('message.newTrigger') }} {{ $t('message.newTrigger') }}
<span <span
class="btn btn-clear" class="btn btn-clear"
:title="$t('word.close')" :title="$t('word.close')"
@mousedown.left.stop @mousedown.left.stop
@click.stop="closeTab(tab)" @click.stop="closeTab(element)"
/> />
</span> </span>
</a> </a>
<a <a
v-else-if="tab.type === 'new-routine'" v-else-if="element.type === 'new-routine'"
class="tab-link" class="tab-link"
:class="{'badge': tab.isChanged}" :class="{'badge': element.isChanged}"
> >
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" /> <i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`"> <span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
{{ $t('message.newRoutine') }} {{ $t('message.newRoutine') }}
<span <span
class="btn btn-clear" class="btn btn-clear"
:title="$t('word.close')" :title="$t('word.close')"
@mousedown.left.stop @mousedown.left.stop
@click.stop="closeTab(tab)" @click.stop="closeTab(element)"
/> />
</span> </span>
</a> </a>
<a <a
v-else-if="tab.type === 'new-function'" v-else-if="element.type === 'new-function'"
class="tab-link" class="tab-link"
:class="{'badge': tab.isChanged}" :class="{'badge': element.isChanged}"
> >
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" /> <i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`"> <span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
{{ $t('message.newFunction') }} {{ $t('message.newFunction') }}
<span <span
class="btn btn-clear" class="btn btn-clear"
:title="$t('word.close')" :title="$t('word.close')"
@mousedown.left.stop @mousedown.left.stop
@click.stop="closeTab(tab)" @click.stop="closeTab(element)"
/> />
</span> </span>
</a> </a>
<a <a
v-else-if="tab.type === 'new-trigger-function'" v-else-if="element.type === 'new-trigger-function'"
class="tab-link" class="tab-link"
:class="{'badge': tab.isChanged}" :class="{'badge': element.isChanged}"
> >
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" /> <i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`"> <span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
{{ $t('message.newTriggerFunction') }} {{ $t('message.newTriggerFunction') }}
<span <span
class="btn btn-clear" class="btn btn-clear"
:title="$t('word.close')" :title="$t('word.close')"
@mousedown.left.stop @mousedown.left.stop
@click.stop="closeTab(tab)" @click.stop="closeTab(element)"
/> />
</span> </span>
</a> </a>
<a <a
v-else-if="tab.type === 'new-scheduler'" v-else-if="element.type === 'new-scheduler'"
class="tab-link" class="tab-link"
:class="{'badge': tab.isChanged}" :class="{'badge': element.isChanged}"
> >
<i class="mdi mdi-shape-square-plus mdi-18px mr-1" /> <i class="mdi mdi-shape-square-plus mdi-18px mr-1" />
<span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`"> <span :title="`${$t('word.new').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
{{ $t('message.newScheduler') }} {{ $t('message.newScheduler') }}
<span <span
class="btn btn-clear" class="btn btn-clear"
:title="$t('word.close')" :title="$t('word.close')"
@mousedown.left.stop @mousedown.left.stop
@click.stop="closeTab(tab)" @click.stop="closeTab(element)"
/> />
</span> </span>
</a> </a>
<a <a
v-else-if="tab.type.includes('temp-')" v-else-if="element.type.includes('temp-')"
class="tab-link" class="tab-link"
:class="{'badge': tab.isChanged}" :class="{'badge': element.isChanged}"
@dblclick="openAsPermanentTab(tab)" @dblclick="openAsPermanentTab(element)"
> >
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" /> <i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
<span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`"> <span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
<span class=" text-italic">{{ tab.elementName | cutText }}</span> <span class=" text-italic">{{ cutText(element.elementName) }}</span>
<span <span
class="btn btn-clear" class="btn btn-clear"
:title="$t('word.close')" :title="$t('word.close')"
@mousedown.left.stop @mousedown.left.stop
@click.stop="closeTab(tab)" @click.stop="closeTab(element)"
/> />
</span> </span>
</a> </a>
<a <a
v-else v-else
class="tab-link" class="tab-link"
:class="{'badge': tab.isChanged}" :class="{'badge': element.isChanged}"
> >
<i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" /> <i class="mdi mdi-18px mdi-tune-vertical-variant mr-1" />
<span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.${tab.elementType}`)}`"> <span :title="`${$t('word.settings').toUpperCase()}: ${$tc(`word.${element.elementType}`)}`">
{{ tab.elementName | cutText }} {{ cutText(element.elementName) }}
<span <span
class="btn btn-clear" class="btn btn-clear"
:title="$t('word.close')" :title="$t('word.close')"
@mousedown.left.stop @mousedown.left.stop
@click.stop="closeTab(tab)" @click.stop="closeTab(element)"
/> />
</span> </span>
</a> </a>
</li> </li>
</template>
<template #header> <template #header>
<li <li
v-if="workspace.customizations.processesList" v-if="workspace.customizations.processesList"
@ -318,6 +319,7 @@
<WorkspaceTabQuery <WorkspaceTabQuery
v-if="tab.type==='query'" v-if="tab.type==='query'"
:key="tab.uid" :key="tab.uid"
:tab-uid="tab.uid"
:tab="tab" :tab="tab"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
:connection="connection" :connection="connection"
@ -325,6 +327,7 @@
<WorkspaceTabTable <WorkspaceTabTable
v-else-if="['temp-data', 'data'].includes(tab.type)" v-else-if="['temp-data', 'data'].includes(tab.type)"
:key="tab.uid" :key="tab.uid"
:tab-uid="tab.uid"
:connection="connection" :connection="connection"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
:table="tab.elementName" :table="tab.elementName"
@ -334,6 +337,7 @@
<WorkspaceTabNewTable <WorkspaceTabNewTable
v-else-if="tab.type === 'new-table'" v-else-if="tab.type === 'new-table'"
:key="tab.uid" :key="tab.uid"
:tab-uid="tab.uid"
:tab="tab" :tab="tab"
:connection="connection" :connection="connection"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
@ -342,6 +346,7 @@
<WorkspaceTabPropsTable <WorkspaceTabPropsTable
v-else-if="tab.type === 'table-props'" v-else-if="tab.type === 'table-props'"
:key="tab.uid" :key="tab.uid"
:tab-uid="tab.uid"
:connection="connection" :connection="connection"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
:table="tab.elementName" :table="tab.elementName"
@ -350,6 +355,7 @@
<WorkspaceTabNewView <WorkspaceTabNewView
v-else-if="tab.type === 'new-view'" v-else-if="tab.type === 'new-view'"
:key="tab.uid" :key="tab.uid"
:tab-uid="tab.uid"
:tab="tab" :tab="tab"
:connection="connection" :connection="connection"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
@ -358,6 +364,7 @@
<WorkspaceTabPropsView <WorkspaceTabPropsView
v-else-if="tab.type === 'view-props'" v-else-if="tab.type === 'view-props'"
:key="tab.uid" :key="tab.uid"
:tab-uid="tab.uid"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
:connection="connection" :connection="connection"
:view="tab.elementName" :view="tab.elementName"
@ -366,6 +373,7 @@
<WorkspaceTabNewTrigger <WorkspaceTabNewTrigger
v-else-if="tab.type === 'new-trigger'" v-else-if="tab.type === 'new-trigger'"
:key="tab.uid" :key="tab.uid"
:tab-uid="tab.uid"
:tab="tab" :tab="tab"
:connection="connection" :connection="connection"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
@ -375,6 +383,7 @@
<WorkspaceTabPropsTrigger <WorkspaceTabPropsTrigger
v-else-if="['temp-trigger-props', 'trigger-props'].includes(tab.type)" v-else-if="['temp-trigger-props', 'trigger-props'].includes(tab.type)"
:key="tab.uid" :key="tab.uid"
:tab-uid="tab.uid"
:connection="connection" :connection="connection"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
:trigger="tab.elementName" :trigger="tab.elementName"
@ -383,6 +392,7 @@
<WorkspaceTabNewTriggerFunction <WorkspaceTabNewTriggerFunction
v-else-if="tab.type === 'new-trigger-function'" v-else-if="tab.type === 'new-trigger-function'"
:key="tab.uid" :key="tab.uid"
:tab-uid="tab.uid"
:tab="tab" :tab="tab"
:connection="connection" :connection="connection"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
@ -392,6 +402,7 @@
<WorkspaceTabPropsTriggerFunction <WorkspaceTabPropsTriggerFunction
v-else-if="['temp-trigger-function-props', 'trigger-function-props'].includes(tab.type)" v-else-if="['temp-trigger-function-props', 'trigger-function-props'].includes(tab.type)"
:key="tab.uid" :key="tab.uid"
:tab-uid="tab.uid"
:connection="connection" :connection="connection"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
:function="tab.elementName" :function="tab.elementName"
@ -400,6 +411,7 @@
<WorkspaceTabNewRoutine <WorkspaceTabNewRoutine
v-else-if="tab.type === 'new-routine'" v-else-if="tab.type === 'new-routine'"
:key="tab.uid" :key="tab.uid"
:tab-uid="tab.uid"
:tab="tab" :tab="tab"
:connection="connection" :connection="connection"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
@ -409,6 +421,7 @@
<WorkspaceTabPropsRoutine <WorkspaceTabPropsRoutine
v-else-if="['temp-routine-props', 'routine-props'].includes(tab.type)" v-else-if="['temp-routine-props', 'routine-props'].includes(tab.type)"
:key="tab.uid" :key="tab.uid"
:tab-uid="tab.uid"
:connection="connection" :connection="connection"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
:routine="tab.elementName" :routine="tab.elementName"
@ -417,6 +430,7 @@
<WorkspaceTabNewFunction <WorkspaceTabNewFunction
v-else-if="tab.type === 'new-function'" v-else-if="tab.type === 'new-function'"
:key="tab.uid" :key="tab.uid"
:tab-uid="tab.uid"
:tab="tab" :tab="tab"
:connection="connection" :connection="connection"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
@ -426,6 +440,7 @@
<WorkspaceTabPropsFunction <WorkspaceTabPropsFunction
v-else-if="['temp-function-props', 'function-props'].includes(tab.type)" v-else-if="['temp-function-props', 'function-props'].includes(tab.type)"
:key="tab.uid" :key="tab.uid"
:tab-uid="tab.uid"
:connection="connection" :connection="connection"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
:function="tab.elementName" :function="tab.elementName"
@ -434,6 +449,7 @@
<WorkspaceTabNewScheduler <WorkspaceTabNewScheduler
v-else-if="tab.type === 'new-scheduler'" v-else-if="tab.type === 'new-scheduler'"
:key="tab.uid" :key="tab.uid"
:tab-uid="tab.uid"
:tab="tab" :tab="tab"
:connection="connection" :connection="connection"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
@ -443,6 +459,7 @@
<WorkspaceTabPropsScheduler <WorkspaceTabPropsScheduler
v-else-if="['temp-scheduler-props', 'scheduler-props'].includes(tab.type)" v-else-if="['temp-scheduler-props', 'scheduler-props'].includes(tab.type)"
:key="tab.uid" :key="tab.uid"
:tab-uid="tab.uid"
:connection="connection" :connection="connection"
:is-selected="selectedTab === tab.uid" :is-selected="selectedTab === tab.uid"
:scheduler="tab.elementName" :scheduler="tab.elementName"
@ -468,9 +485,11 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { storeToRefs } from 'pinia';
import Draggable from 'vuedraggable'; import Draggable from 'vuedraggable';
import Connection from '@/ipc-api/Connection'; import Connection from '@/ipc-api/Connection';
import { useWorkspacesStore } from '@/stores/workspaces';
import WorkspaceEmptyState from '@/components/WorkspaceEmptyState'; import WorkspaceEmptyState from '@/components/WorkspaceEmptyState';
import WorkspaceExploreBar from '@/components/WorkspaceExploreBar'; import WorkspaceExploreBar from '@/components/WorkspaceExploreBar';
import WorkspaceEditConnectionPanel from '@/components/WorkspaceEditConnectionPanel'; import WorkspaceEditConnectionPanel from '@/components/WorkspaceEditConnectionPanel';
@ -521,18 +540,37 @@ export default {
ModalProcessesList, ModalProcessesList,
ModalDiscardChanges 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
}, },
setup () {
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
addWorkspace,
connectWorkspace,
removeConnected,
selectTab,
newTab,
removeTab,
updateTabs
} = workspacesStore;
return {
selectedWorkspace,
getWorkspace,
addWorkspace,
connectWorkspace,
removeConnected,
selectTab,
newTab,
removeTab,
updateTabs
};
},
data () { data () {
return { return {
hasWheelEvent: false, hasWheelEvent: false,
@ -541,10 +579,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
@ -583,19 +617,25 @@ export default {
return false; return false;
}, },
hasTools () { hasTools () {
return this.workspace.customizations.processesList || if (!this.workspace.customizations) return false;
else {
return this.workspace.customizations.processesList ||
this.workspace.customizations.usersManagement || this.workspace.customizations.usersManagement ||
this.workspace.customizations.variables; this.workspace.customizations.variables;
}
} }
}, },
watch: { watch: {
queryTabs: function (newVal, oldVal) { queryTabs: {
if (newVal.length > oldVal.length) { handler (newVal, oldVal) {
setTimeout(() => { if (newVal.length > oldVal.length) {
const scroller = this.$refs.tabWrap; setTimeout(() => {
if (scroller) scroller.$el.scrollLeft = scroller.$el.scrollWidth; const scroller = this.$refs.tabWrap;
}, 0); if (scroller) scroller.$el.scrollLeft = scroller.$el.scrollWidth;
} }, 0);
}
},
deep: true
} }
}, },
async created () { async created () {
@ -605,19 +645,10 @@ export default {
if (isInitiated) if (isInitiated)
this.connectWorkspace(this.connection); this.connectWorkspace(this.connection);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addWorkspace: 'workspaces/addWorkspace',
connectWorkspace: 'workspaces/connectWorkspace',
removeConnected: 'workspaces/removeConnected',
selectTab: 'workspaces/selectTab',
newTab: 'workspaces/newTab',
removeTab: 'workspaces/removeTab',
updateTabs: 'workspaces/updateTabs'
}),
addQueryTab () { addQueryTab () {
this.newTab({ uid: this.connection.uid, type: 'query' }); this.newTab({ uid: this.connection.uid, type: 'query' });
}, },
@ -683,6 +714,13 @@ export default {
}); });
this.hasWheelEvent = true; this.hasWheelEvent = true;
} }
},
cutText (string) {
const limit = 20;
const escapedString = string.replace(/\s{2,}/g, ' ');
if (escapedString.length > limit)
return `${escapedString.substr(0, limit)}...`;
return escapedString;
} }
} }
}; };
@ -809,48 +847,5 @@ export default {
} }
} }
} }
.workspace-query-results {
overflow: auto;
white-space: nowrap;
.table {
width: auto;
border-collapse: separate;
.th {
position: sticky;
top: 0;
border: 2px solid;
border-left: none;
border-bottom-width: 2px;
padding: 0;
font-weight: 700;
font-size: 0.7rem;
z-index: 1;
> div {
padding: 0.1rem 0.2rem;
min-width: -webkit-fill-available;
}
}
.td {
border-right: 2px solid;
border-bottom: 2px solid;
padding: 0 0.2rem;
text-overflow: ellipsis;
max-width: 200px;
white-space: nowrap;
overflow: hidden;
font-size: 0.7rem;
position: relative;
&:focus {
outline: none;
}
}
}
}
} }
</style> </style>

View File

@ -50,7 +50,11 @@
<label class="form-label cut-text">{{ $t('word.client') }}</label> <label class="form-label cut-text">{{ $t('word.client') }}</label>
</div> </div>
<div class="column col-8 col-sm-12"> <div class="column col-8 col-sm-12">
<select v-model="connection.client" class="form-select"> <select
id="connection-client"
v-model="connection.client"
class="form-select"
>
<option <option
v-for="client in clients" v-for="client in clients"
:key="client.slug" :key="client.slug"
@ -363,6 +367,7 @@
</div> </div>
<div class="panel-footer"> <div class="panel-footer">
<button <button
id="connection-test"
class="btn btn-gray mr-2 d-flex" class="btn btn-gray mr-2 d-flex"
:class="{'loading': isTesting}" :class="{'loading': isTesting}"
:disabled="isBusy" :disabled="isBusy"
@ -372,6 +377,7 @@
{{ $t('message.testConnection') }} {{ $t('message.testConnection') }}
</button> </button>
<button <button
id="connection-save"
class="btn btn-primary mr-2 d-flex" class="btn btn-primary mr-2 d-flex"
:disabled="isBusy" :disabled="isBusy"
@click="saveConnection" @click="saveConnection"
@ -390,10 +396,12 @@
</template> </template>
<script> <script>
import { mapActions } from 'vuex';
import customizations from 'common/customizations'; import customizations from 'common/customizations';
import Connection from '@/ipc-api/Connection'; import Connection from '@/ipc-api/Connection';
import { uidGen } from 'common/libs/uidGen'; import { uidGen } from 'common/libs/uidGen';
import { useConnectionsStore } from '@/stores/connections';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import ModalAskCredentials from '@/components/ModalAskCredentials'; import ModalAskCredentials from '@/components/ModalAskCredentials';
import BaseUploadInput from '@/components/BaseUploadInput'; import BaseUploadInput from '@/components/BaseUploadInput';
@ -403,6 +411,20 @@ export default {
ModalAskCredentials, ModalAskCredentials,
BaseUploadInput BaseUploadInput
}, },
setup () {
const { addConnection } = useConnectionsStore();
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { connectWorkspace, selectWorkspace } = workspacesStore;
return {
addConnection,
addNotification,
connectWorkspace,
selectWorkspace
};
},
data () { data () {
return { return {
clients: [ clients: [
@ -466,12 +488,6 @@ export default {
}, 20); }, 20);
}, },
methods: { methods: {
...mapActions({
addConnection: 'connections/addConnection',
connectWorkspace: 'workspaces/connectWorkspace',
addNotification: 'notifications/addNotification',
selectWorkspace: 'workspaces/selectWorkspace'
}),
setDefaults () { setDefaults () {
this.connection.user = this.customizations.defaultUser; this.connection.user = this.customizations.defaultUser;
this.connection.port = this.customizations.defaultPort; this.connection.port = this.customizations.defaultPort;

View File

@ -355,6 +355,7 @@
</div> </div>
<div class="panel-footer"> <div class="panel-footer">
<button <button
id="connection-test"
class="btn btn-gray mr-2 d-flex" class="btn btn-gray mr-2 d-flex"
:class="{'loading': isTesting}" :class="{'loading': isTesting}"
:disabled="isBusy" :disabled="isBusy"
@ -364,6 +365,7 @@
{{ $t('message.testConnection') }} {{ $t('message.testConnection') }}
</button> </button>
<button <button
id="connection-save"
class="btn btn-primary mr-2 d-flex" class="btn btn-primary mr-2 d-flex"
:disabled="isBusy || !hasChanges" :disabled="isBusy || !hasChanges"
@click="saveConnection" @click="saveConnection"
@ -372,6 +374,7 @@
{{ $t('word.save') }} {{ $t('word.save') }}
</button> </button>
<button <button
id="connection-connect"
class="btn btn-success d-flex" class="btn btn-success d-flex"
:class="{'loading': isConnecting}" :class="{'loading': isConnecting}"
:disabled="isBusy" :disabled="isBusy"
@ -391,8 +394,10 @@
</template> </template>
<script> <script>
import { mapActions } from 'vuex';
import customizations from 'common/customizations'; import customizations from 'common/customizations';
import { useConnectionsStore } from '@/stores/connections';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import Connection from '@/ipc-api/Connection'; import Connection from '@/ipc-api/Connection';
import ModalAskCredentials from '@/components/ModalAskCredentials'; import ModalAskCredentials from '@/components/ModalAskCredentials';
import BaseUploadInput from '@/components/BaseUploadInput'; import BaseUploadInput from '@/components/BaseUploadInput';
@ -406,6 +411,17 @@ export default {
props: { props: {
connection: Object connection: Object
}, },
setup () {
const { editConnection } = useConnectionsStore();
const { addNotification } = useNotificationsStore();
const { connectWorkspace } = useWorkspacesStore();
return {
editConnection,
addNotification,
connectWorkspace
};
},
data () { data () {
return { return {
clients: [ clients: [
@ -441,11 +457,6 @@ export default {
this.localConnection = JSON.parse(JSON.stringify(this.connection)); this.localConnection = JSON.parse(JSON.stringify(this.connection));
}, },
methods: { methods: {
...mapActions({
editConnection: 'connections/editConnection',
connectWorkspace: 'workspaces/connectWorkspace',
addNotification: 'notifications/addNotification'
}),
async startConnection () { async startConnection () {
await this.saveConnection(); await this.saveConnection();
this.isConnecting = true; this.isConnecting = true;

View File

@ -25,26 +25,35 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { useSettingsStore } from '@/stores/settings';
import { useWorkspacesStore } from '@/stores/workspaces';
import { storeToRefs } from 'pinia';
export default { export default {
name: 'WorkspaceEmptyState', name: 'WorkspaceEmptyState',
emits: ['new-tab'],
setup () {
const settingsStore = useSettingsStore();
const workspacesStore = useWorkspacesStore();
const { applicationTheme } = storeToRefs(settingsStore);
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { getWorkspace, changeBreadcrumbs } = workspacesStore;
return {
applicationTheme,
selectedWorkspace,
getWorkspace,
changeBreadcrumbs
};
},
computed: { computed: {
...mapGetters({
applicationTheme: 'settings/getApplicationTheme',
getWorkspace: 'workspaces/getWorkspace',
selectedWorkspace: 'workspaces/getSelected'
}),
workspace () { workspace () {
return this.getWorkspace(this.selectedWorkspace); return this.getWorkspace(this.selectedWorkspace);
} }
}, },
created () { created () {
this.changeBreadcrumbs({ schema: this.workspace.breadcrumbs.schema }); this.changeBreadcrumbs({ schema: this.workspace.breadcrumbs.schema });
},
methods: {
...mapActions({
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
})
} }
}; };
</script> </script>

View File

@ -115,7 +115,12 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { storeToRefs } from 'pinia';
import { useConnectionsStore } from '@/stores/connections';
import { useNotificationsStore } from '@/stores/notifications';
import { useSettingsStore } from '@/stores/settings';
import { useWorkspacesStore } from '@/stores/workspaces';
import Tables from '@/ipc-api/Tables'; import Tables from '@/ipc-api/Tables';
import Views from '@/ipc-api/Views'; import Views from '@/ipc-api/Views';
@ -143,6 +148,45 @@ export default {
connection: Object, connection: Object,
isSelected: Boolean isSelected: Boolean
}, },
setup () {
const { getConnectionName } = useConnectionsStore();
const { addNotification } = useNotificationsStore();
const settingsStore = useSettingsStore();
const workspacesStore = useWorkspacesStore();
const { explorebarSize } = storeToRefs(settingsStore);
const { changeExplorebarSize } = settingsStore;
const {
getWorkspace,
removeConnected: disconnectWorkspace,
refreshStructure,
changeBreadcrumbs,
selectTab,
newTab,
removeTabs,
setSearchTerm,
addLoadingElement,
removeLoadingElement
} = workspacesStore;
return {
getConnectionName,
addNotification,
explorebarSize,
changeExplorebarSize,
getWorkspace,
disconnectWorkspace,
refreshStructure,
changeBreadcrumbs,
selectTab,
newTab,
removeTabs,
setSearchTerm,
addLoadingElement,
removeLoadingElement
};
},
data () { data () {
return { return {
isRefreshing: false, isRefreshing: false,
@ -174,11 +218,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
getWorkspace: 'workspaces/getWorkspace',
explorebarSize: 'settings/getExplorebarSize',
getConnectionName: 'connections/getConnectionName'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
@ -222,19 +261,6 @@ export default {
}); });
}, },
methods: { methods: {
...mapActions({
disconnectWorkspace: 'workspaces/removeConnected',
refreshStructure: 'workspaces/refreshStructure',
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) {
this.isRefreshing = true; this.isRefreshing = true;

View File

@ -65,7 +65,8 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import BaseContextMenu from '@/components/BaseContextMenu'; import BaseContextMenu from '@/components/BaseContextMenu';
import ConfirmModal from '@/components/BaseConfirmModal'; import ConfirmModal from '@/components/BaseConfirmModal';
import ModalAskParameters from '@/components/ModalAskParameters'; import ModalAskParameters from '@/components/ModalAskParameters';
@ -73,6 +74,7 @@ import Triggers from '@/ipc-api/Triggers';
import Routines from '@/ipc-api/Routines'; import Routines from '@/ipc-api/Routines';
import Functions from '@/ipc-api/Functions'; import Functions from '@/ipc-api/Functions';
import Schedulers from '@/ipc-api/Schedulers'; import Schedulers from '@/ipc-api/Schedulers';
import { storeToRefs } from 'pinia';
export default { export default {
name: 'WorkspaceExploreBarMiscContext', name: 'WorkspaceExploreBarMiscContext',
@ -86,6 +88,33 @@ export default {
selectedMisc: Object, selectedMisc: Object,
selectedSchema: String selectedSchema: String
}, },
emits: ['close-context', 'reload'],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
changeBreadcrumbs,
addLoadingElement,
removeLoadingElement,
removeTabs,
newTab
} = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
changeBreadcrumbs,
addLoadingElement,
removeLoadingElement,
removeTabs,
newTab
};
},
data () { data () {
return { return {
isDeleteModal: false, isDeleteModal: false,
@ -95,10 +124,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.selectedWorkspace); return this.getWorkspace(this.selectedWorkspace);
}, },
@ -122,14 +147,6 @@ export default {
} }
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
addLoadingElement: 'workspaces/addLoadingElement',
removeLoadingElement: 'workspaces/removeLoadingElement',
removeTabs: 'workspaces/removeTabs',
newTab: 'workspaces/newTab'
}),
showDeleteModal () { showDeleteModal () {
this.isDeleteModal = true; this.isDeleteModal = true;
}, },

View File

@ -42,8 +42,10 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import BaseContextMenu from '@/components/BaseContextMenu'; import BaseContextMenu from '@/components/BaseContextMenu';
import { storeToRefs } from 'pinia';
export default { export default {
name: 'WorkspaceExploreBarMiscContext', name: 'WorkspaceExploreBarMiscContext',
@ -55,25 +57,40 @@ export default {
selectedMisc: String, selectedMisc: String,
selectedSchema: String selectedSchema: String
}, },
emits: [
'open-create-trigger-tab',
'open-create-routine-tab',
'open-create-function-tab',
'open-create-trigger-function-tab',
'open-create-scheduler-tab',
'close-context'
],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { getWorkspace, changeBreadcrumbs } = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
changeBreadcrumbs
};
},
data () { data () {
return { return {
localElement: {} localElement: {}
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.selectedWorkspace); return this.getWorkspace(this.selectedWorkspace);
} }
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
}),
showDeleteModal () { showDeleteModal () {
this.isDeleteModal = true; this.isDeleteModal = true;
}, },

View File

@ -243,8 +243,10 @@
</template> </template>
<script> <script>
import { mapActions, mapGetters } from 'vuex'; import { useSettingsStore } from '@/stores/settings';
import { useWorkspacesStore } from '@/stores/workspaces';
import { formatBytes } from 'common/libs/formatBytes'; import { formatBytes } from 'common/libs/formatBytes';
import { storeToRefs } from 'pinia';
export default { export default {
name: 'WorkspaceExploreBarSchema', name: 'WorkspaceExploreBarSchema',
@ -252,18 +254,45 @@ export default {
database: Object, database: Object,
connection: Object connection: Object
}, },
emits: [
'show-schema-context',
'show-table-context',
'show-misc-context',
'show-misc-folder-context'
],
setup () {
const settingsStore = useSettingsStore();
const workspacesStore = useWorkspacesStore();
const { applicationTheme } = storeToRefs(settingsStore);
const {
getLoadedSchemas,
getWorkspace,
getSearchTerm,
changeBreadcrumbs,
addLoadedSchema,
newTab,
refreshSchema
} = workspacesStore;
return {
applicationTheme,
getLoadedSchemas,
getWorkspace,
getSearchTerm,
changeBreadcrumbs,
addLoadedSchema,
newTab,
refreshSchema
};
},
data () { data () {
return { return {
isLoading: false isLoading: false
}; };
}, },
computed: { computed: {
...mapGetters({
getLoadedSchemas: 'workspaces/getLoadedSchemas',
getWorkspace: 'workspaces/getWorkspace',
getSearchTerm: 'workspaces/getSearchTerm',
applicationTheme: 'settings/getApplicationTheme'
}),
searchTerm () { searchTerm () {
return this.getSearchTerm(this.connection.uid); return this.getSearchTerm(this.connection.uid);
}, },
@ -331,12 +360,6 @@ export default {
} }
}, },
methods: { methods: {
...mapActions({
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
addLoadedSchema: 'workspaces/addLoadedSchema',
newTab: 'workspaces/newTab',
refreshSchema: 'workspaces/refreshSchema'
}),
formatBytes, formatBytes,
async selectSchema (schema) { async selectSchema (schema) {
if (!this.loadedSchemas.has(schema) && !this.isLoading) { if (!this.loadedSchemas.has(schema) && !this.isLoading) {

View File

@ -124,7 +124,8 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import BaseContextMenu from '@/components/BaseContextMenu'; import BaseContextMenu from '@/components/BaseContextMenu';
import ConfirmModal from '@/components/BaseConfirmModal'; import ConfirmModal from '@/components/BaseConfirmModal';
import ModalEditSchema from '@/components/ModalEditSchema'; import ModalEditSchema from '@/components/ModalEditSchema';
@ -132,6 +133,7 @@ import ModalExportSchema from '@/components/ModalExportSchema';
import ModalImportSchema from '@/components/ModalImportSchema'; import ModalImportSchema from '@/components/ModalImportSchema';
import Schema from '@/ipc-api/Schema'; import Schema from '@/ipc-api/Schema';
import Application from '@/ipc-api/Application'; import Application from '@/ipc-api/Application';
import { storeToRefs } from 'pinia';
export default { export default {
name: 'WorkspaceExploreBarSchemaContext', name: 'WorkspaceExploreBarSchemaContext',
@ -146,6 +148,35 @@ export default {
contextEvent: MouseEvent, contextEvent: MouseEvent,
selectedSchema: String selectedSchema: String
}, },
emits: [
'open-create-table-tab',
'open-create-view-tab',
'open-create-trigger-tab',
'open-create-routine-tab',
'open-create-function-tab',
'open-create-trigger-function-tab',
'open-create-scheduler-tab',
'close-context',
'reload'
],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
changeBreadcrumbs
} = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
changeBreadcrumbs
};
},
data () { data () {
return { return {
isDeleteModal: false, isDeleteModal: false,
@ -155,19 +186,11 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.selectedWorkspace); return this.getWorkspace(this.selectedWorkspace);
} }
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
}),
openCreateTableTab () { openCreateTableTab () {
this.$emit('open-create-table-tab'); this.$emit('open-create-table-tab');
}, },

View File

@ -72,10 +72,12 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import BaseContextMenu from '@/components/BaseContextMenu'; import BaseContextMenu from '@/components/BaseContextMenu';
import ConfirmModal from '@/components/BaseConfirmModal'; import ConfirmModal from '@/components/BaseConfirmModal';
import Tables from '@/ipc-api/Tables'; import Tables from '@/ipc-api/Tables';
import { storeToRefs } from 'pinia';
export default { export default {
name: 'WorkspaceExploreBarTableContext', name: 'WorkspaceExploreBarTableContext',
@ -88,6 +90,33 @@ export default {
selectedTable: Object, selectedTable: Object,
selectedSchema: String selectedSchema: String
}, },
emits: ['close-context', 'duplicate-table', 'reload', 'delete-table'],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
newTab,
removeTabs,
addLoadingElement,
removeLoadingElement,
changeBreadcrumbs
} = workspacesStore;
return {
addNotification,
getWorkspace,
newTab,
removeTabs,
addLoadingElement,
removeLoadingElement,
changeBreadcrumbs,
selectedWorkspace
};
},
data () { data () {
return { return {
isDeleteModal: false, isDeleteModal: false,
@ -95,10 +124,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.selectedWorkspace); return this.getWorkspace(this.selectedWorkspace);
}, },
@ -107,14 +132,6 @@ export default {
} }
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
newTab: 'workspaces/newTab',
removeTabs: 'workspaces/removeTabs',
addLoadingElement: 'workspaces/addLoadingElement',
removeLoadingElement: 'workspaces/removeLoadingElement',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
}),
showDeleteModal () { showDeleteModal () {
this.isDeleteModal = true; this.isDeleteModal = true;
}, },

View File

@ -185,7 +185,7 @@
<QueryEditor <QueryEditor
v-show="isSelected" v-show="isSelected"
ref="queryEditor" ref="queryEditor"
:value.sync="localFunction.sql" v-model="localFunction.sql"
:workspace="workspace" :workspace="workspace"
:schema="schema" :schema="schema"
:height="editorHeight" :height="editorHeight"
@ -203,11 +203,13 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
import QueryEditor from '@/components/QueryEditor'; import QueryEditor from '@/components/QueryEditor';
import WorkspaceTabPropsFunctionParamsModal from '@/components/WorkspaceTabPropsFunctionParamsModal'; import WorkspaceTabPropsFunctionParamsModal from '@/components/WorkspaceTabPropsFunctionParamsModal';
import Functions from '@/ipc-api/Functions'; import Functions from '@/ipc-api/Functions';
import { storeToRefs } from 'pinia';
export default { export default {
name: 'WorkspaceTabNewFunction', name: 'WorkspaceTabNewFunction',
@ -217,11 +219,40 @@ export default {
WorkspaceTabPropsFunctionParamsModal WorkspaceTabPropsFunctionParamsModal
}, },
props: { props: {
tabUid: String,
connection: Object, connection: Object,
tab: Object, tab: Object,
isSelected: Boolean, isSelected: Boolean,
schema: String schema: String
}, },
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
refreshStructure,
changeBreadcrumbs,
setUnsavedChanges,
newTab,
removeTab,
renameTabs
} = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
refreshStructure,
changeBreadcrumbs,
setUnsavedChanges,
newTab,
removeTab,
renameTabs
};
},
data () { data () {
return { return {
isLoading: false, isLoading: false,
@ -235,19 +266,12 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
customizations () { customizations () {
return this.workspace.customizations; return this.workspace.customizations;
}, },
tabUid () {
return this.$vnode.key;
},
isChanged () { isChanged () {
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction); return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
}, },
@ -302,22 +326,13 @@ export default {
this.$refs.firstInput.focus(); this.$refs.firstInput.focus();
}, 100); }, 100);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
setUnsavedChanges: 'workspaces/setUnsavedChanges',
newTab: 'workspaces/newTab',
removeTab: 'workspaces/removeTab',
renameTabs: 'workspaces/renameTabs'
}),
async saveChanges () { async saveChanges () {
if (this.isSaving) return; if (this.isSaving) return;
this.isSaving = true; this.isSaving = true;

View File

@ -145,7 +145,7 @@
v-show="isSelected" v-show="isSelected"
:key="`new-${_uid}`" :key="`new-${_uid}`"
ref="queryEditor" ref="queryEditor"
:value.sync="localRoutine.sql" v-model="localRoutine.sql"
:workspace="workspace" :workspace="workspace"
:schema="schema" :schema="schema"
:height="editorHeight" :height="editorHeight"
@ -163,11 +163,13 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import QueryEditor from '@/components/QueryEditor'; import QueryEditor from '@/components/QueryEditor';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
import WorkspaceTabPropsRoutineParamsModal from '@/components/WorkspaceTabPropsRoutineParamsModal'; import WorkspaceTabPropsRoutineParamsModal from '@/components/WorkspaceTabPropsRoutineParamsModal';
import Routines from '@/ipc-api/Routines'; import Routines from '@/ipc-api/Routines';
import { storeToRefs } from 'pinia';
export default { export default {
name: 'WorkspaceTabNewRoutine', name: 'WorkspaceTabNewRoutine',
@ -177,11 +179,40 @@ export default {
WorkspaceTabPropsRoutineParamsModal WorkspaceTabPropsRoutineParamsModal
}, },
props: { props: {
tabUid: String,
connection: Object, connection: Object,
tab: Object, tab: Object,
isSelected: Boolean, isSelected: Boolean,
schema: String schema: String
}, },
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
refreshStructure,
changeBreadcrumbs,
setUnsavedChanges,
newTab,
removeTab,
renameTabs
} = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
refreshStructure,
changeBreadcrumbs,
setUnsavedChanges,
newTab,
removeTab,
renameTabs
};
},
data () { data () {
return { return {
isLoading: false, isLoading: false,
@ -195,19 +226,12 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
customizations () { customizations () {
return this.workspace.customizations; return this.workspace.customizations;
}, },
tabUid () {
return this.$vnode.key;
},
isChanged () { isChanged () {
return JSON.stringify(this.originalRoutine) !== JSON.stringify(this.localRoutine); return JSON.stringify(this.originalRoutine) !== JSON.stringify(this.localRoutine);
}, },
@ -261,22 +285,13 @@ export default {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
setUnsavedChanges: 'workspaces/setUnsavedChanges',
newTab: 'workspaces/newTab',
removeTab: 'workspaces/removeTab',
renameTabs: 'workspaces/renameTabs'
}),
async saveChanges () { async saveChanges () {
if (this.isSaving) return; if (this.isSaving) return;
this.isSaving = true; this.isSaving = true;

View File

@ -125,7 +125,7 @@
<QueryEditor <QueryEditor
v-show="isSelected" v-show="isSelected"
ref="queryEditor" ref="queryEditor"
:value.sync="localScheduler.sql" v-model="localScheduler.sql"
:workspace="workspace" :workspace="workspace"
:schema="schema" :schema="schema"
:height="editorHeight" :height="editorHeight"
@ -142,7 +142,9 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
import QueryEditor from '@/components/QueryEditor'; import QueryEditor from '@/components/QueryEditor';
import WorkspaceTabPropsSchedulerTimingModal from '@/components/WorkspaceTabPropsSchedulerTimingModal'; import WorkspaceTabPropsSchedulerTimingModal from '@/components/WorkspaceTabPropsSchedulerTimingModal';
@ -156,11 +158,40 @@ export default {
WorkspaceTabPropsSchedulerTimingModal WorkspaceTabPropsSchedulerTimingModal
}, },
props: { props: {
tabUid: String,
connection: Object, connection: Object,
tab: Object, tab: Object,
isSelected: Boolean, isSelected: Boolean,
schema: String schema: String
}, },
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
refreshStructure,
changeBreadcrumbs,
setUnsavedChanges,
newTab,
removeTab,
renameTabs
} = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
refreshStructure,
changeBreadcrumbs,
setUnsavedChanges,
newTab,
removeTab,
renameTabs
};
},
data () { data () {
return { return {
isLoading: false, isLoading: false,
@ -174,16 +205,9 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
tabUid () {
return this.$vnode.key;
},
isChanged () { isChanged () {
return JSON.stringify(this.originalScheduler) !== JSON.stringify(this.localScheduler); return JSON.stringify(this.originalScheduler) !== JSON.stringify(this.localScheduler);
}, },
@ -237,22 +261,13 @@ export default {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
setUnsavedChanges: 'workspaces/setUnsavedChanges',
newTab: 'workspaces/newTab',
removeTab: 'workspaces/removeTab',
renameTabs: 'workspaces/renameTabs'
}),
async saveChanges () { async saveChanges () {
if (this.isSaving) return; if (this.isSaving) return;
this.isSaving = true; this.isSaving = true;

View File

@ -165,7 +165,9 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import { uidGen } from 'common/libs/uidGen'; import { uidGen } from 'common/libs/uidGen';
import Tables from '@/ipc-api/Tables'; import Tables from '@/ipc-api/Tables';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
@ -184,11 +186,42 @@ export default {
WorkspaceTabNewTableEmptyState WorkspaceTabNewTableEmptyState
}, },
props: { props: {
tabUid: String,
connection: Object, connection: Object,
tab: Object, tab: Object,
isSelected: Boolean, isSelected: Boolean,
schema: String schema: String
}, },
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
getDatabaseVariable,
refreshStructure,
setUnsavedChanges,
newTab,
renameTabs,
removeTab,
changeBreadcrumbs
} = workspacesStore;
return {
addNotification,
getWorkspace,
getDatabaseVariable,
refreshStructure,
setUnsavedChanges,
newTab,
renameTabs,
removeTab,
changeBreadcrumbs,
selectedWorkspace
};
},
data () { data () {
return { return {
isLoading: false, isLoading: false,
@ -209,17 +242,9 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
getWorkspace: 'workspaces/getWorkspace',
selectedWorkspace: 'workspaces/getSelected',
getDatabaseVariable: 'workspaces/getDatabaseVariable'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
tabUid () {
return this.$vnode.key;
},
defaultCollation () { defaultCollation () {
if (this.workspace.customizations.collations) if (this.workspace.customizations.collations)
return this.getDatabaseVariable(this.selectedWorkspace, 'collation_server').value || ''; return this.getDatabaseVariable(this.selectedWorkspace, 'collation_server').value || '';
@ -276,19 +301,10 @@ export default {
this.$refs.firstInput.focus(); this.$refs.firstInput.focus();
}, 100); }, 100);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges',
newTab: 'workspaces/newTab',
renameTabs: 'workspaces/renameTabs',
removeTab: 'workspaces/removeTab',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
}),
async saveChanges () { async saveChanges () {
if (this.isSaving || !this.isValid) return; if (this.isSaving || !this.isValid) return;
this.isSaving = true; this.isSaving = true;

View File

@ -14,7 +14,8 @@
<script> <script>
export default { export default {
name: 'WorkspaceTabNewTableEmptyState' name: 'WorkspaceTabNewTableEmptyState',
emits: ['new-field']
}; };
</script> </script>

View File

@ -122,7 +122,7 @@
<QueryEditor <QueryEditor
v-show="isSelected" v-show="isSelected"
ref="queryEditor" ref="queryEditor"
:value.sync="localTrigger.sql" v-model="localTrigger.sql"
:workspace="workspace" :workspace="workspace"
:schema="schema" :schema="schema"
:height="editorHeight" :height="editorHeight"
@ -132,8 +132,10 @@
</template> </template>
<script> <script>
import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import QueryEditor from '@/components/QueryEditor'; import QueryEditor from '@/components/QueryEditor';
import { mapGetters, mapActions } from 'vuex';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
import Triggers from '@/ipc-api/Triggers'; import Triggers from '@/ipc-api/Triggers';
@ -144,11 +146,40 @@ export default {
QueryEditor QueryEditor
}, },
props: { props: {
tabUid: String,
connection: Object, connection: Object,
tab: Object, tab: Object,
isSelected: Boolean, isSelected: Boolean,
schema: String schema: String
}, },
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
refreshStructure,
changeBreadcrumbs,
setUnsavedChanges,
newTab,
removeTab,
renameTabs
} = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
refreshStructure,
changeBreadcrumbs,
setUnsavedChanges,
newTab,
removeTab,
renameTabs
};
},
data () { data () {
return { return {
isLoading: false, isLoading: false,
@ -162,16 +193,9 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
tabUid () {
return this.$vnode.key;
},
customizations () { customizations () {
return this.workspace.customizations; return this.workspace.customizations;
}, },
@ -226,22 +250,13 @@ export default {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
setUnsavedChanges: 'workspaces/setUnsavedChanges',
newTab: 'workspaces/newTab',
removeTab: 'workspaces/removeTab',
renameTabs: 'workspaces/renameTabs'
}),
changeEvents (event) { changeEvents (event) {
if (this.customizations.triggerMultipleEvents) { if (this.customizations.triggerMultipleEvents) {
this.localEvents[event] = !this.localEvents[event]; this.localEvents[event] = !this.localEvents[event];

View File

@ -98,7 +98,7 @@
<QueryEditor <QueryEditor
v-show="isSelected" v-show="isSelected"
ref="queryEditor" ref="queryEditor"
:value.sync="localFunction.sql" v-model="localFunction.sql"
:workspace="workspace" :workspace="workspace"
:schema="schema" :schema="schema"
:height="editorHeight" :height="editorHeight"
@ -108,7 +108,9 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
import QueryEditor from '@/components/QueryEditor'; import QueryEditor from '@/components/QueryEditor';
import Functions from '@/ipc-api/Functions'; import Functions from '@/ipc-api/Functions';
@ -120,11 +122,40 @@ export default {
QueryEditor QueryEditor
}, },
props: { props: {
tabUid: String,
connection: Object, connection: Object,
tab: Object, tab: Object,
isSelected: Boolean, isSelected: Boolean,
schema: String schema: String
}, },
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
refreshStructure,
changeBreadcrumbs,
setUnsavedChanges,
newTab,
removeTab,
renameTabs
} = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
refreshStructure,
changeBreadcrumbs,
setUnsavedChanges,
newTab,
removeTab,
renameTabs
};
},
data () { data () {
return { return {
isLoading: false, isLoading: false,
@ -139,19 +170,12 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
customizations () { customizations () {
return this.workspace.customizations; return this.workspace.customizations;
}, },
tabUid () {
return this.$vnode.key;
},
isChanged () { isChanged () {
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction); return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
}, },
@ -202,22 +226,13 @@ export default {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
setUnsavedChanges: 'workspaces/setUnsavedChanges',
newTab: 'workspaces/newTab',
removeTab: 'workspaces/removeTab',
renameTabs: 'workspaces/renameTabs'
}),
async saveChanges () { async saveChanges () {
if (this.isSaving) return; if (this.isSaving) return;
this.isSaving = true; this.isSaving = true;

View File

@ -111,7 +111,7 @@
<QueryEditor <QueryEditor
v-show="isSelected" v-show="isSelected"
ref="queryEditor" ref="queryEditor"
:value.sync="localView.sql" v-model="localView.sql"
:workspace="workspace" :workspace="workspace"
:schema="schema" :schema="schema"
:height="editorHeight" :height="editorHeight"
@ -121,7 +121,9 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
import QueryEditor from '@/components/QueryEditor'; import QueryEditor from '@/components/QueryEditor';
import Views from '@/ipc-api/Views'; import Views from '@/ipc-api/Views';
@ -133,11 +135,40 @@ export default {
QueryEditor QueryEditor
}, },
props: { props: {
tabUid: String,
connection: Object, connection: Object,
tab: Object, tab: Object,
isSelected: Boolean, isSelected: Boolean,
schema: String schema: String
}, },
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
refreshStructure,
setUnsavedChanges,
changeBreadcrumbs,
newTab,
removeTab,
renameTabs
} = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
refreshStructure,
setUnsavedChanges,
changeBreadcrumbs,
newTab,
removeTab,
renameTabs
};
},
data () { data () {
return { return {
isLoading: false, isLoading: false,
@ -150,16 +181,9 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
tabUid () {
return this.$vnode.key;
},
isChanged () { isChanged () {
return JSON.stringify(this.originalView) !== JSON.stringify(this.localView); return JSON.stringify(this.originalView) !== JSON.stringify(this.localView);
}, },
@ -209,22 +233,13 @@ export default {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
newTab: 'workspaces/newTab',
removeTab: 'workspaces/removeTab',
renameTabs: 'workspaces/renameTabs'
}),
async saveChanges () { async saveChanges () {
if (this.isSaving) return; if (this.isSaving) return;
this.isSaving = true; this.isSaving = true;

View File

@ -197,7 +197,7 @@
<QueryEditor <QueryEditor
v-show="isSelected" v-show="isSelected"
ref="queryEditor" ref="queryEditor"
:value.sync="localFunction.sql" v-model="localFunction.sql"
:workspace="workspace" :workspace="workspace"
:schema="schema" :schema="schema"
:height="editorHeight" :height="editorHeight"
@ -222,7 +222,9 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import { uidGen } from 'common/libs/uidGen'; import { uidGen } from 'common/libs/uidGen';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
import QueryEditor from '@/components/QueryEditor'; import QueryEditor from '@/components/QueryEditor';
@ -239,11 +241,38 @@ export default {
ModalAskParameters ModalAskParameters
}, },
props: { props: {
tabUid: String,
connection: Object, connection: Object,
function: String, function: String,
isSelected: Boolean, isSelected: Boolean,
schema: String schema: String
}, },
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
refreshStructure,
renameTabs,
newTab,
changeBreadcrumbs,
setUnsavedChanges
} = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
refreshStructure,
renameTabs,
newTab,
changeBreadcrumbs,
setUnsavedChanges
};
},
data () { data () {
return { return {
isLoading: false, isLoading: false,
@ -258,19 +287,12 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
customizations () { customizations () {
return this.workspace.customizations; return this.workspace.customizations;
}, },
tabUid () {
return this.$vnode.key;
},
isChanged () { isChanged () {
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction); return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
}, },
@ -339,21 +361,13 @@ export default {
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure',
renameTabs: 'workspaces/renameTabs',
newTab: 'workspaces/newTab',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
setUnsavedChanges: 'workspaces/setUnsavedChanges'
}),
async getFunctionData () { async getFunctionData () {
if (!this.function) return; if (!this.function) return;

View File

@ -181,10 +181,14 @@ export default {
ConfirmModal ConfirmModal
}, },
props: { props: {
localParameters: Array, localParameters: {
type: Array,
default: () => []
},
func: String, func: String,
workspace: Object workspace: Object
}, },
emits: ['hide', 'parameters-update'],
data () { data () {
return { return {
parametersProxy: [], parametersProxy: [],
@ -215,7 +219,7 @@ export default {
this.getModalInnerHeight(); this.getModalInnerHeight();
window.addEventListener('resize', this.getModalInnerHeight); window.addEventListener('resize', this.getModalInnerHeight);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.getModalInnerHeight); window.removeEventListener('resize', this.getModalInnerHeight);
}, },
methods: { methods: {

View File

@ -152,9 +152,8 @@
<label class="form-label ml-2">{{ $t('message.routineBody') }}</label> <label class="form-label ml-2">{{ $t('message.routineBody') }}</label>
<QueryEditor <QueryEditor
v-show="isSelected" v-show="isSelected"
:key="`${routine}-${_uid}`"
ref="queryEditor" ref="queryEditor"
:value.sync="localRoutine.sql" v-model="localRoutine.sql"
:workspace="workspace" :workspace="workspace"
:schema="schema" :schema="schema"
:height="editorHeight" :height="editorHeight"
@ -179,7 +178,9 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import { uidGen } from 'common/libs/uidGen'; import { uidGen } from 'common/libs/uidGen';
import QueryEditor from '@/components/QueryEditor'; import QueryEditor from '@/components/QueryEditor';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
@ -196,11 +197,38 @@ export default {
ModalAskParameters ModalAskParameters
}, },
props: { props: {
tabUid: String,
connection: Object, connection: Object,
routine: String, routine: String,
isSelected: Boolean, isSelected: Boolean,
schema: String schema: String
}, },
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
refreshStructure,
renameTabs,
newTab,
changeBreadcrumbs,
setUnsavedChanges
} = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
refreshStructure,
renameTabs,
newTab,
changeBreadcrumbs,
setUnsavedChanges
};
},
data () { data () {
return { return {
isLoading: false, isLoading: false,
@ -215,19 +243,12 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
customizations () { customizations () {
return this.workspace.customizations; return this.workspace.customizations;
}, },
tabUid () {
return this.$vnode.key;
},
isChanged () { isChanged () {
return JSON.stringify(this.originalRoutine) !== JSON.stringify(this.localRoutine); return JSON.stringify(this.originalRoutine) !== JSON.stringify(this.localRoutine);
}, },
@ -284,21 +305,13 @@ export default {
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure',
renameTabs: 'workspaces/renameTabs',
newTab: 'workspaces/newTab',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
setUnsavedChanges: 'workspaces/setUnsavedChanges'
}),
async getRoutineData () { async getRoutineData () {
if (!this.routine) return; if (!this.routine) return;

View File

@ -181,10 +181,14 @@ export default {
ConfirmModal ConfirmModal
}, },
props: { props: {
localParameters: Array, localParameters: {
type: Array,
default: () => []
},
routine: String, routine: String,
workspace: Object workspace: Object
}, },
emits: ['parameters-update', 'hide'],
data () { data () {
return { return {
parametersProxy: [], parametersProxy: [],
@ -215,7 +219,7 @@ export default {
this.getModalInnerHeight(); this.getModalInnerHeight();
window.addEventListener('resize', this.getModalInnerHeight); window.addEventListener('resize', this.getModalInnerHeight);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.getModalInnerHeight); window.removeEventListener('resize', this.getModalInnerHeight);
}, },
methods: { methods: {

View File

@ -124,7 +124,7 @@
<QueryEditor <QueryEditor
v-show="isSelected" v-show="isSelected"
ref="queryEditor" ref="queryEditor"
:value.sync="localScheduler.sql" v-model="localScheduler.sql"
:workspace="workspace" :workspace="workspace"
:schema="schema" :schema="schema"
:height="editorHeight" :height="editorHeight"
@ -141,7 +141,9 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
import QueryEditor from '@/components/QueryEditor'; import QueryEditor from '@/components/QueryEditor';
import WorkspaceTabPropsSchedulerTimingModal from '@/components/WorkspaceTabPropsSchedulerTimingModal'; import WorkspaceTabPropsSchedulerTimingModal from '@/components/WorkspaceTabPropsSchedulerTimingModal';
@ -155,11 +157,38 @@ export default {
WorkspaceTabPropsSchedulerTimingModal WorkspaceTabPropsSchedulerTimingModal
}, },
props: { props: {
tabUid: String,
connection: Object, connection: Object,
scheduler: String, scheduler: String,
isSelected: Boolean, isSelected: Boolean,
schema: String schema: String
}, },
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
refreshStructure,
renameTabs,
newTab,
changeBreadcrumbs,
setUnsavedChanges
} = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
refreshStructure,
renameTabs,
newTab,
changeBreadcrumbs,
setUnsavedChanges
};
},
data () { data () {
return { return {
isLoading: false, isLoading: false,
@ -173,16 +202,9 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
tabUid () {
return this.$vnode.key;
},
isChanged () { isChanged () {
return JSON.stringify(this.originalScheduler) !== JSON.stringify(this.localScheduler); return JSON.stringify(this.originalScheduler) !== JSON.stringify(this.localScheduler);
}, },
@ -236,21 +258,13 @@ export default {
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure',
renameTabs: 'workspaces/renameTabs',
newTab: 'workspaces/newTab',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
setUnsavedChanges: 'workspaces/setUnsavedChanges'
}),
async getSchedulerData () { async getSchedulerData () {
if (!this.scheduler) return; if (!this.scheduler) return;

View File

@ -138,22 +138,19 @@
</template> </template>
<script> <script>
import ConfirmModal from '@/components/BaseConfirmModal';
import { VueMaskDirective } from 'v-mask';
import moment from 'moment'; import moment from 'moment';
import ConfirmModal from '@/components/BaseConfirmModal';
export default { export default {
name: 'WorkspaceTabPropsSchedulerTimingModal', name: 'WorkspaceTabPropsSchedulerTimingModal',
components: { components: {
ConfirmModal ConfirmModal
}, },
directives: {
mask: VueMaskDirective
},
props: { props: {
localOptions: Object, localOptions: Object,
workspace: Object workspace: Object
}, },
emits: ['hide', 'options-update'],
data () { data () {
return { return {
optionsProxy: {}, optionsProxy: {},

View File

@ -177,7 +177,9 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import { uidGen } from 'common/libs/uidGen'; import { uidGen } from 'common/libs/uidGen';
import Tables from '@/ipc-api/Tables'; import Tables from '@/ipc-api/Tables';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
@ -194,11 +196,38 @@ export default {
WorkspaceTabPropsTableForeignModal WorkspaceTabPropsTableForeignModal
}, },
props: { props: {
tabUid: String,
connection: Object, connection: Object,
isSelected: Boolean, isSelected: Boolean,
table: String, table: String,
schema: String schema: String
}, },
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
getDatabaseVariable,
refreshStructure,
renameTabs,
changeBreadcrumbs,
setUnsavedChanges
} = workspacesStore;
return {
addNotification,
getDatabaseVariable,
getWorkspace,
selectedWorkspace,
refreshStructure,
setUnsavedChanges,
renameTabs,
changeBreadcrumbs
};
},
data () { data () {
return { return {
isLoading: false, isLoading: false,
@ -219,17 +248,9 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
getWorkspace: 'workspaces/getWorkspace',
selectedWorkspace: 'workspaces/getSelected',
getDatabaseVariable: 'workspaces/getDatabaseVariable'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
tabUid () {
return this.$vnode.key;
},
defaultEngine () { defaultEngine () {
const engine = this.getDatabaseVariable(this.connection.uid, 'default_storage_engine'); const engine = this.getDatabaseVariable(this.connection.uid, 'default_storage_engine');
return engine ? engine.value : ''; return engine ? engine.value : '';
@ -277,17 +298,10 @@ export default {
this.getFieldsData(); this.getFieldsData();
window.addEventListener('keydown', this.onKey); window.addEventListener('keydown', this.onKey);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges',
renameTabs: 'workspaces/renameTabs',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
}),
async getTableOptions (params) { async getTableOptions (params) {
const db = this.workspace.structure.find(db => db.name === this.schema); const db = this.workspace.structure.find(db => db.name === this.schema);
@ -415,7 +429,7 @@ export default {
const localIDs = this.localFields.reduce((acc, curr) => [...acc, curr._antares_id], []); const localIDs = this.localFields.reduce((acc, curr) => [...acc, curr._antares_id], []);
// Fields Additions // Fields Additions
const additions = this.localFields.filter((field, i) => !originalIDs.includes(field._antares_id)).map(field => { const additions = this.localFields.filter(field => !originalIDs.includes(field._antares_id)).map(field => {
const lI = this.localFields.findIndex(localField => localField._antares_id === field._antares_id); const lI = this.localFields.findIndex(localField => localField._antares_id === field._antares_id);
const after = lI > 0 ? this.localFields[lI - 1].name : false; const after = lI > 0 ? this.localFields[lI - 1].name : false;
return { ...field, after }; return { ...field, after };

View File

@ -56,6 +56,7 @@ export default {
indexTypes: Array, indexTypes: Array,
selectedField: Object selectedField: Object
}, },
emits: ['close-context', 'duplicate-selected', 'delete-selected', 'add-new-index', 'add-to-index'],
computed: { computed: {
hasPrimary () { hasPrimary () {
return this.indexes.some(index => index.type === 'PRIMARY'); return this.indexes.some(index => index.type === 'PRIMARY');

View File

@ -105,26 +105,29 @@
ref="resultTable" ref="resultTable"
:list="fields" :list="fields"
class="tbody" class="tbody"
item-key="_antares_id"
handle=".row-draggable" handle=".row-draggable"
> >
<TableRow <template #item="{element}">
v-for="row in fields" <TableRow
:key="row._antares_id" :row="element"
:row="row" :indexes="getIndexes(element.name)"
:indexes="getIndexes(row.name)" :foreigns="getForeigns(element.name)"
:foreigns="getForeigns(row.name)" :data-types="dataTypes"
:data-types="dataTypes" :customizations="customizations"
:customizations="customizations" @contextmenu="contextMenu"
@contextmenu="contextMenu" @rename-field="$emit('rename-field', $event)"
@rename-field="$emit('rename-field', $event)" />
/> </template>
</Draggable> </Draggable>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { mapActions, mapGetters } from 'vuex'; import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import Draggable from 'vuedraggable'; import Draggable from 'vuedraggable';
import TableRow from '@/components/WorkspaceTabPropsTableRow'; import TableRow from '@/components/WorkspaceTabPropsTableRow';
import TableContext from '@/components/WorkspaceTabPropsTableContext'; import TableContext from '@/components/WorkspaceTabPropsTableContext';
@ -147,6 +150,21 @@ export default {
schema: String, schema: String,
mode: String mode: String
}, },
emits: ['add-new-index', 'add-to-index', 'rename-field', 'duplicate-field', 'remove-field'],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { getWorkspace } = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace
};
},
data () { data () {
return { return {
resultsSize: 1000, resultsSize: 1000,
@ -157,10 +175,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
getWorkspaceTab: 'workspaces/getWorkspaceTab',
getWorkspace: 'workspaces/getWorkspace'
}),
workspaceSchema () { workspaceSchema () {
return this.getWorkspace(this.connUid).breadcrumbs.schema; return this.getWorkspace(this.connUid).breadcrumbs.schema;
}, },
@ -195,13 +209,10 @@ export default {
mounted () { mounted () {
window.addEventListener('resize', this.resizeResults); window.addEventListener('resize', this.resizeResults);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.resizeResults); window.removeEventListener('resize', this.resizeResults);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification'
}),
resizeResults () { resizeResults () {
if (this.$refs.resultTable) { if (this.$refs.resultTable) {
const el = this.$refs.tableWrapper; const el = this.$refs.tableWrapper;

View File

@ -202,7 +202,7 @@
</template> </template>
<script> <script>
import { mapActions } from 'vuex'; import { useNotificationsStore } from '@/stores/notifications';
import { uidGen } from 'common/libs/uidGen'; import { uidGen } from 'common/libs/uidGen';
import Tables from '@/ipc-api/Tables'; import Tables from '@/ipc-api/Tables';
import ConfirmModal from '@/components/BaseConfirmModal'; import ConfirmModal from '@/components/BaseConfirmModal';
@ -221,6 +221,12 @@ export default {
fields: Array, fields: Array,
workspace: Object workspace: Object
}, },
emits: ['foreigns-update', 'hide'],
setup () {
const { addNotification } = useNotificationsStore();
return { addNotification };
},
data () { data () {
return { return {
foreignProxy: [], foreignProxy: [],
@ -259,13 +265,10 @@ export default {
this.getModalInnerHeight(); this.getModalInnerHeight();
window.addEventListener('resize', this.getModalInnerHeight); window.addEventListener('resize', this.getModalInnerHeight);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.getModalInnerHeight); window.removeEventListener('resize', this.getModalInnerHeight);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification'
}),
confirmForeignsChange () { confirmForeignsChange () {
this.foreignProxy = this.foreignProxy.filter(foreign => this.foreignProxy = this.foreignProxy.filter(foreign =>
foreign.field && foreign.field &&

View File

@ -153,6 +153,7 @@ export default {
workspace: Object, workspace: Object,
indexTypes: Array indexTypes: Array
}, },
emits: ['hide', 'indexes-update'],
data () { data () {
return { return {
indexesProxy: [], indexesProxy: [],
@ -181,7 +182,7 @@ export default {
this.getModalInnerHeight(); this.getModalInnerHeight();
window.addEventListener('resize', this.getModalInnerHeight); window.addEventListener('resize', this.getModalInnerHeight);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.getModalInnerHeight); window.removeEventListener('resize', this.getModalInnerHeight);
}, },
methods: { methods: {

View File

@ -343,7 +343,9 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex'; import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import ConfirmModal from '@/components/BaseConfirmModal'; import ConfirmModal from '@/components/BaseConfirmModal';
export default { export default {
@ -358,6 +360,21 @@ export default {
foreigns: Array, foreigns: Array,
customizations: Object customizations: Object
}, },
emits: ['contextmenu', 'rename-field'],
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { getWorkspace } = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace
};
},
data () { data () {
return { return {
localRow: {}, localRow: {},
@ -375,10 +392,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
localLength () { localLength () {
const localLength = this.localRow.numLength || this.localRow.charLength || this.localRow.datePrecision || this.localRow.numPrecision || 0; const localLength = this.localRow.numLength || this.localRow.charLength || this.localRow.datePrecision || this.localRow.numPrecision || 0;
return localLength === true ? null : localLength; return localLength === true ? null : localLength;

View File

@ -121,7 +121,7 @@
<QueryEditor <QueryEditor
v-show="isSelected" v-show="isSelected"
ref="queryEditor" ref="queryEditor"
:value.sync="localTrigger.sql" v-model="localTrigger.sql"
:workspace="workspace" :workspace="workspace"
:schema="schema" :schema="schema"
:height="editorHeight" :height="editorHeight"
@ -131,8 +131,10 @@
</template> </template>
<script> <script>
import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import QueryEditor from '@/components/QueryEditor'; import QueryEditor from '@/components/QueryEditor';
import { mapGetters, mapActions } from 'vuex';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
import Triggers from '@/ipc-api/Triggers'; import Triggers from '@/ipc-api/Triggers';
@ -143,11 +145,38 @@ export default {
QueryEditor QueryEditor
}, },
props: { props: {
tabUid: String,
connection: Object, connection: Object,
trigger: String, trigger: String,
isSelected: Boolean, isSelected: Boolean,
schema: String schema: String
}, },
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
refreshStructure,
renameTabs,
newTab,
changeBreadcrumbs,
setUnsavedChanges
} = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
refreshStructure,
renameTabs,
newTab,
changeBreadcrumbs,
setUnsavedChanges
};
},
data () { data () {
return { return {
isLoading: false, isLoading: false,
@ -161,16 +190,9 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
tabUid () {
return this.$vnode.key;
},
customizations () { customizations () {
return this.workspace.customizations; return this.workspace.customizations;
}, },
@ -227,21 +249,13 @@ export default {
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure',
renameTabs: 'workspaces/renameTabs',
newTab: 'workspaces/newTab',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
setUnsavedChanges: 'workspaces/setUnsavedChanges'
}),
async getTriggerData () { async getTriggerData () {
if (!this.trigger) return; if (!this.trigger) return;

View File

@ -85,7 +85,7 @@
<QueryEditor <QueryEditor
v-show="isSelected" v-show="isSelected"
ref="queryEditor" ref="queryEditor"
:value.sync="localFunction.sql" v-model="localFunction.sql"
:workspace="workspace" :workspace="workspace"
:schema="schema" :schema="schema"
:height="editorHeight" :height="editorHeight"
@ -102,7 +102,9 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import { uidGen } from 'common/libs/uidGen'; import { uidGen } from 'common/libs/uidGen';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
import QueryEditor from '@/components/QueryEditor'; import QueryEditor from '@/components/QueryEditor';
@ -117,11 +119,38 @@ export default {
ModalAskParameters ModalAskParameters
}, },
props: { props: {
tabUid: String,
connection: Object, connection: Object,
function: String, function: String,
isSelected: Boolean, isSelected: Boolean,
schema: String schema: String
}, },
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
refreshStructure,
renameTabs,
newTab,
changeBreadcrumbs,
setUnsavedChanges
} = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
refreshStructure,
renameTabs,
newTab,
changeBreadcrumbs,
setUnsavedChanges
};
},
data () { data () {
return { return {
isLoading: false, isLoading: false,
@ -136,19 +165,12 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
customizations () { customizations () {
return this.workspace.customizations; return this.workspace.customizations;
}, },
tabUid () {
return this.$vnode.key;
},
isChanged () { isChanged () {
return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction); return JSON.stringify(this.originalFunction) !== JSON.stringify(this.localFunction);
}, },
@ -204,21 +226,13 @@ export default {
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure',
renameTabs: 'workspaces/renameTabs',
newTab: 'workspaces/newTab',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
setUnsavedChanges: 'workspaces/setUnsavedChanges'
}),
async getFunctionData () { async getFunctionData () {
if (!this.function) return; if (!this.function) return;

View File

@ -110,7 +110,7 @@
<QueryEditor <QueryEditor
v-show="isSelected" v-show="isSelected"
ref="queryEditor" ref="queryEditor"
:value.sync="localView.sql" v-model="localView.sql"
:workspace="workspace" :workspace="workspace"
:schema="schema" :schema="schema"
:height="editorHeight" :height="editorHeight"
@ -120,7 +120,9 @@
</template> </template>
<script> <script>
import { mapGetters, mapActions } from 'vuex'; import { storeToRefs } from 'pinia';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
import QueryEditor from '@/components/QueryEditor'; import QueryEditor from '@/components/QueryEditor';
import Views from '@/ipc-api/Views'; import Views from '@/ipc-api/Views';
@ -132,11 +134,36 @@ export default {
QueryEditor QueryEditor
}, },
props: { props: {
tabUid: String,
connection: Object, connection: Object,
isSelected: Boolean, isSelected: Boolean,
schema: String, schema: String,
view: String view: String
}, },
setup () {
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
refreshStructure,
renameTabs,
changeBreadcrumbs,
setUnsavedChanges
} = workspacesStore;
return {
addNotification,
selectedWorkspace,
getWorkspace,
refreshStructure,
renameTabs,
changeBreadcrumbs,
setUnsavedChanges
};
},
data () { data () {
return { return {
isLoading: false, isLoading: false,
@ -149,16 +176,9 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
selectedWorkspace: 'workspaces/getSelected',
getWorkspace: 'workspaces/getWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
tabUid () {
return this.$vnode.key;
},
isChanged () { isChanged () {
return JSON.stringify(this.originalView) !== JSON.stringify(this.localView); return JSON.stringify(this.originalView) !== JSON.stringify(this.localView);
}, },
@ -205,20 +225,13 @@ export default {
mounted () { mounted () {
window.addEventListener('resize', this.resizeQueryEditor); window.addEventListener('resize', this.resizeQueryEditor);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.resizeQueryEditor); window.removeEventListener('resize', this.resizeQueryEditor);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
refreshStructure: 'workspaces/refreshStructure',
setUnsavedChanges: 'workspaces/setUnsavedChanges',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
renameTabs: 'workspaces/renameTabs'
}),
async getViewData () { async getViewData () {
if (!this.view) return; if (!this.view) return;
this.isLoading = true; this.isLoading = true;

View File

@ -3,18 +3,18 @@
v-show="isSelected" v-show="isSelected"
class="workspace-query-tab column col-12 columns col-gapless no-outline p-0" class="workspace-query-tab column col-12 columns col-gapless no-outline p-0"
tabindex="0" tabindex="0"
@keydown.116="runQuery(query)" @keydown.f5="runQuery(query)"
@keydown.75="killTabQuery" @keydown.k="killTabQuery"
@keydown.ctrl.alt.87="clear" @keydown.ctrl.alt.w="clear"
@keydown.ctrl.66="beautify" @keydown.ctrl.b="beautify"
@keydown.ctrl.71="openHistoryModal" @keydown.ctrl.g="openHistoryModal"
> >
<div class="workspace-query-runner column col-12"> <div class="workspace-query-runner column col-12">
<QueryEditor <QueryEditor
v-show="isSelected" v-show="isSelected"
ref="queryEditor" ref="queryEditor"
v-model="query"
:auto-focus="true" :auto-focus="true"
:value.sync="query"
:workspace="workspace" :workspace="workspace"
:schema="breadcrumbsSchema" :schema="breadcrumbsSchema"
:is-selected="isSelected" :is-selected="isSelected"
@ -187,8 +187,11 @@
</template> </template>
<script> <script>
import { storeToRefs } from 'pinia';
import { format } from 'sql-formatter'; import { format } from 'sql-formatter';
import { mapGetters, mapActions } from 'vuex'; import { useHistoryStore } from '@/stores/history';
import { useNotificationsStore } from '@/stores/notifications';
import { useWorkspacesStore } from '@/stores/workspaces';
import Schema from '@/ipc-api/Schema'; import Schema from '@/ipc-api/Schema';
import QueryEditor from '@/components/QueryEditor'; import QueryEditor from '@/components/QueryEditor';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
@ -208,10 +211,36 @@ export default {
}, },
mixins: [tableTabs], mixins: [tableTabs],
props: { props: {
tabUid: String,
connection: Object, connection: Object,
tab: Object, tab: Object,
isSelected: Boolean isSelected: Boolean
}, },
setup () {
const { getHistoryByWorkspace, saveHistory } = useHistoryStore();
const { addNotification } = useNotificationsStore();
const workspacesStore = useWorkspacesStore();
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const {
getWorkspace,
changeBreadcrumbs,
updateTabContent,
setUnsavedChanges
} = workspacesStore;
return {
getHistoryByWorkspace,
saveHistory,
addNotification,
selectedWorkspace,
getWorkspace,
changeBreadcrumbs,
updateTabContent,
setUnsavedChanges
};
},
data () { data () {
return { return {
query: '', query: '',
@ -230,17 +259,9 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
getWorkspace: 'workspaces/getWorkspace',
selectedWorkspace: 'workspaces/getSelected',
getHistoryByWorkspace: 'history/getHistoryByWorkspace'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
tabUid () {
return this.$vnode.key;
},
breadcrumbsSchema () { breadcrumbsSchema () {
return this.workspace.breadcrumbs.schema || null; return this.workspace.breadcrumbs.schema || null;
}, },
@ -296,7 +317,7 @@ export default {
if (this.tab.autorun) if (this.tab.autorun)
this.runQuery(this.query); this.runQuery(this.query);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
const params = { const params = {
uid: this.connection.uid, uid: this.connection.uid,
@ -305,13 +326,6 @@ export default {
Schema.destroyConnectionToCommit(params); Schema.destroyConnectionToCommit(params);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs',
updateTabContent: 'workspaces/updateTabContent',
setUnsavedChanges: 'workspaces/setUnsavedChanges',
saveHistory: 'history/saveHistory'
}),
async runQuery (query) { async runQuery (query) {
if (!query || this.isQuering) return; if (!query || this.isQuering) return;
this.isQuering = true; this.isQuering = true;

View File

@ -4,8 +4,8 @@
class="vscroll no-outline" class="vscroll no-outline"
tabindex="0" tabindex="0"
:style="{'height': resultsSize+'px'}" :style="{'height': resultsSize+'px'}"
@keyup.46="showDeleteConfirmModal" @keyup.delete="showDeleteConfirmModal"
@keydown.ctrl.65="selectAllRows($event)" @keydown.ctrl.a="selectAllRows($event)"
@keydown.esc="deselectRows" @keydown.esc="deselectRows"
> >
<TableContext <TableContext
@ -68,7 +68,7 @@
:visible-height="resultsSize" :visible-height="resultsSize"
:scroll-element="scrollElement" :scroll-element="scrollElement"
> >
<template slot-scope="{ items }"> <template #default="{ items }">
<WorkspaceTabQueryTableRow <WorkspaceTabQueryTableRow
v-for="row in items" v-for="row in items"
:key="row._antares_id" :key="row._antares_id"
@ -107,14 +107,17 @@
</template> </template>
<script> <script>
import { storeToRefs } from 'pinia';
import { uidGen } from 'common/libs/uidGen'; import { uidGen } from 'common/libs/uidGen';
import { useNotificationsStore } from '@/stores/notifications';
import { useSettingsStore } from '@/stores/settings';
import { useWorkspacesStore } from '@/stores/workspaces';
import arrayToFile from '../libs/arrayToFile'; import arrayToFile from '../libs/arrayToFile';
import { TEXT, LONG_TEXT, BLOB } from 'common/fieldTypes'; import { TEXT, LONG_TEXT, BLOB } from 'common/fieldTypes';
import BaseVirtualScroll from '@/components/BaseVirtualScroll'; import BaseVirtualScroll from '@/components/BaseVirtualScroll';
import WorkspaceTabQueryTableRow from '@/components/WorkspaceTabQueryTableRow'; import WorkspaceTabQueryTableRow from '@/components/WorkspaceTabQueryTableRow';
import TableContext from '@/components/WorkspaceTabQueryTableContext'; import TableContext from '@/components/WorkspaceTabQueryTableContext';
import ConfirmModal from '@/components/BaseConfirmModal'; import ConfirmModal from '@/components/BaseConfirmModal';
import { mapActions, mapGetters } from 'vuex';
import moment from 'moment'; import moment from 'moment';
export default { export default {
@ -132,6 +135,20 @@ export default {
isSelected: Boolean, isSelected: Boolean,
elementType: { type: String, default: 'table' } elementType: { type: String, default: 'table' }
}, },
emits: ['update-field', 'delete-selected', 'hard-sort'],
setup () {
const { addNotification } = useNotificationsStore();
const settingsStore = useSettingsStore();
const { getWorkspace } = useWorkspacesStore();
const { dataTabLimit: pageSize } = storeToRefs(settingsStore);
return {
addNotification,
pageSize,
getWorkspace
};
},
data () { data () {
return { return {
resultsSize: 0, resultsSize: 0,
@ -149,10 +166,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
getWorkspace: 'workspaces/getWorkspace',
pageSize: 'settings/getDataTabLimit'
}),
workspaceSchema () { workspaceSchema () {
return this.getWorkspace(this.connUid).breadcrumbs.schema; return this.getWorkspace(this.connUid).breadcrumbs.schema;
}, },
@ -256,13 +269,10 @@ export default {
mounted () { mounted () {
window.addEventListener('resize', this.resizeResults); window.addEventListener('resize', this.resizeResults);
}, },
destroyed () { unmounted () {
window.removeEventListener('resize', this.resizeResults); window.removeEventListener('resize', this.resizeResults);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification'
}),
fieldType (cKey) { fieldType (cKey) {
let type = 'unknown'; let type = 'unknown';
const field = this.fields.filter(field => field.name === cKey)[0]; const field = this.fields.filter(field => field.name === cKey)[0];

View File

@ -61,6 +61,7 @@ export default {
selectedRows: Array, selectedRows: Array,
selectedCell: Object selectedCell: Object
}, },
emits: ['show-delete-modal', 'close-context', 'set-null', 'copy-cell', 'copy-row'],
methods: { methods: {
showConfirmModal () { showConfirmModal () {
this.$emit('show-delete-modal'); this.$emit('show-delete-modal');

View File

@ -18,11 +18,11 @@
class="cell-content" class="cell-content"
:class="`${isNull(col)} ${typeClass(fields[cKey].type)}`" :class="`${isNull(col)} ${typeClass(fields[cKey].type)}`"
@dblclick="editON($event, col, cKey)" @dblclick="editON($event, col, cKey)"
>{{ col | typeFormat(fields[cKey].type.toLowerCase(), fields[cKey].length) | cutText }}</span> >{{ cutText(typeFormat(col, fields[cKey].type.toLowerCase(), fields[cKey].length)) }}</span>
<ForeignKeySelect <ForeignKeySelect
v-else-if="isForeignKey(cKey)" v-else-if="isForeignKey(cKey)"
v-model="editingContent"
class="editable-field" class="editable-field"
:value.sync="editingContent"
:key-usage="getKeyUsage(cKey)" :key-usage="getKeyUsage(cKey)"
size="small" size="small"
@blur="editOFF" @blur="editOFF"
@ -85,7 +85,7 @@
<div class="mb-2"> <div class="mb-2">
<div> <div>
<TextEditor <TextEditor
:value.sync="editingContent" v-model="editingContent"
editor-class="textarea-editor" editor-class="textarea-editor"
:mode="editorMode" :mode="editorMode"
/> />
@ -152,7 +152,7 @@
</template> </template>
<template #body> <template #body>
<div class="mb-2"> <div class="mb-2">
<transition name="jump-down"> <Transition name="jump-down">
<div v-if="contentInfo.size"> <div v-if="contentInfo.size">
<img <img
v-if="isImage" v-if="isImage"
@ -173,10 +173,10 @@
</button> </button>
</div> </div>
</div> </div>
</transition> </Transition>
<div class="editor-field-info"> <div class="editor-field-info">
<div> <div>
<b>{{ $t('word.size') }}</b>: {{ editingContent.length | formatBytes }}<br> <b>{{ $t('word.size') }}</b>: {{ formatBytes(editingContent.length) }}<br>
<b>{{ $t('word.mimeType') }}</b>: {{ contentInfo.mime }} <b>{{ $t('word.mimeType') }}</b>: {{ contentInfo.mime }}
</div> </div>
<div><b>{{ $t('word.type') }}</b>: {{ editingType.toUpperCase() }}</div> <div><b>{{ $t('word.type') }}</b>: {{ editingType.toUpperCase() }}</div>
@ -219,7 +219,6 @@ import {
SPATIAL, SPATIAL,
IS_MULTI_SPATIAL IS_MULTI_SPATIAL
} from 'common/fieldTypes'; } from 'common/fieldTypes';
import { VueMaskDirective } from 'v-mask';
import ConfirmModal from '@/components/BaseConfirmModal'; import ConfirmModal from '@/components/BaseConfirmModal';
import TextEditor from '@/components/BaseTextEditor'; import TextEditor from '@/components/BaseTextEditor';
import BaseMap from '@/components/BaseMap'; import BaseMap from '@/components/BaseMap';
@ -233,61 +232,6 @@ export default {
ForeignKeySelect, ForeignKeySelect,
BaseMap BaseMap
}, },
directives: {
mask: VueMaskDirective
},
filters: {
formatBytes,
cutText (val) {
if (typeof val !== 'string') return val;
return val.length > 128 ? `${val.substring(0, 128)}[...]` : val;
},
typeFormat (val, type, precision) {
if (!val) return val;
type = type.toUpperCase();
if (DATE.includes(type))
return moment(val).isValid() ? moment(val).format('YYYY-MM-DD') : val;
if (DATETIME.includes(type)) {
if (typeof val === 'string')
return val;
let datePrecision = '';
for (let i = 0; i < precision; i++)
datePrecision += i === 0 ? '.S' : 'S';
return moment(val).isValid() ? moment(val).format(`YYYY-MM-DD HH:mm:ss${datePrecision}`) : val;
}
if (BLOB.includes(type)) {
const buff = Buffer.from(val);
if (!buff.length) return '';
const hex = buff.toString('hex').substring(0, 8).toUpperCase();
return `${mimeFromHex(hex).mime} (${formatBytes(buff.length)})`;
}
if (BIT.includes(type)) {
if (typeof val === 'number') val = [val];
const hex = Buffer.from(val).toString('hex');
const bitString = hexToBinary(hex);
return parseInt(bitString).toString().padStart(precision, '0');
}
if (ARRAY.includes(type)) {
if (Array.isArray(val))
return JSON.stringify(val).replaceAll('[', '{').replaceAll(']', '}');
return val;
}
if (SPATIAL.includes(type))
return val;
return typeof val === 'object' ? JSON.stringify(val) : val;
}
},
props: { props: {
row: Object, row: Object,
fields: Object, fields: Object,
@ -295,6 +239,7 @@ export default {
itemHeight: Number, itemHeight: Number,
elementType: { type: String, default: 'table' } elementType: { type: String, default: 'table' }
}, },
emits: ['update-field', 'select-row', 'contextmenu'],
data () { data () {
return { return {
isInlineEditor: {}, isInlineEditor: {},
@ -449,15 +394,15 @@ export default {
window.addEventListener('keydown', this.onKey); window.addEventListener('keydown', this.onKey);
const type = this.fields[field].type.toUpperCase(); ; const type = this.fields[field].type.toUpperCase();
this.originalContent = this.$options.filters.typeFormat(content, type, this.fields[field].length); this.originalContent = this.typeFormat(content, type, this.fields[field].length);
this.editingType = type; this.editingType = type;
this.editingField = field; this.editingField = field;
this.editingLength = this.fields[field].length; this.editingLength = this.fields[field].length;
if ([...LONG_TEXT, ...ARRAY, ...TEXT_SEARCH].includes(type)) { if ([...LONG_TEXT, ...ARRAY, ...TEXT_SEARCH].includes(type)) {
this.isTextareaEditor = true; this.isTextareaEditor = true;
this.editingContent = this.$options.filters.typeFormat(content, type); this.editingContent = this.typeFormat(content, type);
return; return;
} }
@ -465,7 +410,7 @@ export default {
if (content) { if (content) {
this.isMultiSpatial = IS_MULTI_SPATIAL.includes(type); this.isMultiSpatial = IS_MULTI_SPATIAL.includes(type);
this.isMapModal = true; this.isMapModal = true;
this.editingContent = this.$options.filters.typeFormat(content, type); this.editingContent = this.typeFormat(content, type);
} }
return; return;
} }
@ -513,7 +458,7 @@ export default {
this.editingContent = this.editingContent.slice(0, -1); this.editingContent = this.editingContent.slice(0, -1);
} }
if (this.editingContent === this.$options.filters.typeFormat(this.originalContent, this.editingType, this.editingLength)) return;// If not changed if (this.editingContent === this.typeFormat(this.originalContent, this.editingType, this.editingLength)) return;// If not changed
content = this.editingContent; content = this.editingContent;
} }
@ -590,6 +535,56 @@ export default {
this.editingField = null; this.editingField = null;
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
} }
},
formatBytes,
cutText (val) {
if (typeof val !== 'string') return val;
return val.length > 128 ? `${val.substring(0, 128)}[...]` : val;
},
typeFormat (val, type, precision) {
if (!val) return val;
type = type.toUpperCase();
if (DATE.includes(type))
return moment(val).isValid() ? moment(val).format('YYYY-MM-DD') : val;
if (DATETIME.includes(type)) {
if (typeof val === 'string')
return val;
let datePrecision = '';
for (let i = 0; i < precision; i++)
datePrecision += i === 0 ? '.S' : 'S';
return moment(val).isValid() ? moment(val).format(`YYYY-MM-DD HH:mm:ss${datePrecision}`) : val;
}
if (BLOB.includes(type)) {
const buff = Buffer.from(val);
if (!buff.length) return '';
const hex = buff.toString('hex').substring(0, 8).toUpperCase();
return `${mimeFromHex(hex).mime} (${formatBytes(buff.length)})`;
}
if (BIT.includes(type)) {
if (typeof val === 'number') val = [val];
const hex = Buffer.from(val).toString('hex');
const bitString = hexToBinary(hex);
return parseInt(bitString).toString().padStart(precision, '0');
}
if (ARRAY.includes(type)) {
if (Array.isArray(val))
return JSON.stringify(val).replaceAll('[', '{').replaceAll(']', '}');
return val;
}
if (SPATIAL.includes(type))
return val;
return typeof val === 'object' ? JSON.stringify(val) : val;
} }
} }
}; };

View File

@ -117,14 +117,14 @@
<i class="mdi mdi-timer-sand mdi-rotate-180 pr-1" /> <b>{{ results[0].duration / 1000 }}s</b> <i class="mdi mdi-timer-sand mdi-rotate-180 pr-1" /> <b>{{ results[0].duration / 1000 }}s</b>
</div> </div>
<div v-if="results.length && results[0].rows"> <div v-if="results.length && results[0].rows">
{{ $t('word.results') }}: <b>{{ results[0].rows.length | localeString }}</b> {{ $t('word.results') }}: <b>{{ localeString(results[0].rows.length) }}</b>
</div> </div>
<div v-if="hasApproximately || (page > 1 && approximateCount)"> <div v-if="hasApproximately || (page > 1 && approximateCount)">
{{ $t('word.total') }}: <b {{ $t('word.total') }}: <b
:title="!customizations.tableRealCount ? $t('word.approximately') : ''" :title="!customizations.tableRealCount ? $t('word.approximately') : ''"
> >
<span v-if="!customizations.tableRealCount"></span> <span v-if="!customizations.tableRealCount"></span>
{{ approximateCount | localeString }} {{ localeString(approximateCount) }}
</b> </b>
</div> </div>
<div class="d-flex" :title="$t('word.schema')"> <div class="d-flex" :title="$t('word.schema')">
@ -176,13 +176,16 @@
</template> </template>
<script> <script>
import { storeToRefs } from 'pinia';
import Tables from '@/ipc-api/Tables'; import Tables from '@/ipc-api/Tables';
import { useNotificationsStore } from '@/stores/notifications';
import { useSettingsStore } from '@/stores/settings';
import { useWorkspacesStore } from '@/stores/workspaces';
import BaseLoader from '@/components/BaseLoader'; import BaseLoader from '@/components/BaseLoader';
import WorkspaceTabQueryTable from '@/components/WorkspaceTabQueryTable'; import WorkspaceTabQueryTable from '@/components/WorkspaceTabQueryTable';
import WorkspaceTabTableFilters from '@/components/WorkspaceTabTableFilters'; import WorkspaceTabTableFilters from '@/components/WorkspaceTabTableFilters';
import ModalNewTableRow from '@/components/ModalNewTableRow'; import ModalNewTableRow from '@/components/ModalNewTableRow';
import ModalFakerRows from '@/components/ModalFakerRows'; import ModalFakerRows from '@/components/ModalFakerRows';
import { mapGetters, mapActions } from 'vuex';
import tableTabs from '@/mixins/tableTabs'; import tableTabs from '@/mixins/tableTabs';
export default { export default {
@ -194,12 +197,6 @@ export default {
ModalNewTableRow, ModalNewTableRow,
ModalFakerRows ModalFakerRows
}, },
filters: {
localeString (val) {
if (val !== null)
return val.toLocaleString();
}
},
mixins: [tableTabs], mixins: [tableTabs],
props: { props: {
connection: Object, connection: Object,
@ -208,6 +205,24 @@ export default {
schema: String, schema: String,
elementType: String elementType: String
}, },
setup () {
const { addNotification } = useNotificationsStore();
const settingsStore = useSettingsStore();
const workspacesStore = useWorkspacesStore();
const { dataTabLimit: limit } = storeToRefs(settingsStore);
const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { changeBreadcrumbs, getWorkspace } = workspacesStore;
return {
addNotification,
limit,
selectedWorkspace,
changeBreadcrumbs,
getWorkspace
};
},
data () { data () {
return { return {
tabUid: 'data', // ??? tabUid: 'data', // ???
@ -228,11 +243,6 @@ export default {
}; };
}, },
computed: { computed: {
...mapGetters({
getWorkspace: 'workspaces/getWorkspace',
selectedWorkspace: 'workspaces/getSelected',
limit: 'settings/getDataTabLimit'
}),
workspace () { workspace () {
return this.getWorkspace(this.connection.uid); return this.getWorkspace(this.connection.uid);
}, },
@ -310,15 +320,11 @@ export default {
this.getTableData(); this.getTableData();
window.addEventListener('keydown', this.onKey); window.addEventListener('keydown', this.onKey);
}, },
beforeDestroy () { beforeUnmount () {
window.removeEventListener('keydown', this.onKey); window.removeEventListener('keydown', this.onKey);
clearInterval(this.refreshInterval); clearInterval(this.refreshInterval);
}, },
methods: { methods: {
...mapActions({
addNotification: 'notifications/addNotification',
changeBreadcrumbs: 'workspaces/changeBreadcrumbs'
}),
async getTableData () { async getTableData () {
if (!this.table || !this.isSelected) return; if (!this.table || !this.isSelected) return;
this.isQuering = true; this.isQuering = true;
@ -335,8 +341,8 @@ export default {
table: this.table, table: this.table,
limit: this.limit, limit: this.limit,
page: this.page, page: this.page,
sortParams: this.sortParams, sortParams: { ...this.sortParams },
where: this.filters || [] where: [...this.filters] || []
}; };
try { // Table data try { // Table data
@ -458,6 +464,10 @@ export default {
updateFilters (clausoles) { updateFilters (clausoles) {
this.filters = clausoles; this.filters = clausoles;
this.getTableData(); this.getTableData();
},
localeString (val) {
if (val !== null)
return val.toLocaleString();
} }
} }
}; };

View File

@ -79,6 +79,7 @@ export default {
fields: Array, fields: Array,
connClient: String connClient: String
}, },
emits: ['filter-change', 'filter'],
data () { data () {
return { return {
rows: [], rows: [],

View File

@ -0,0 +1,64 @@
// TODO: unfinished
import Tables from '@/ipc-api/Tables';
import { ref } from 'vue';
export default function useResultTables (uid, reloadTable, addNotification) {
const tableRef = ref(null);
const isQuering = ref(false);
async function updateField (payload) {
isQuering.value = true;
const params = {
uid: uid,
...payload
};
try {
const { status, response } = await Tables.updateTableCell(params);
if (status === 'success') {
if (response.reload)// Needed for blob fields
reloadTable();
else
tableRef.applyUpdate(payload);
}
else
addNotification({ status: 'error', message: response });
}
catch (err) {
addNotification({ status: 'error', message: err.stack });
}
isQuering.value = false;
}
async function deleteSelected (payload) {
isQuering.value = true;
const params = {
uid: uid,
...payload
};
try {
const { status, response } = await Tables.deleteTableRows(params);
isQuering.value = false;
if (status === 'success')
reloadTable();
else
this.addNotification({ status: 'error', message: response });
}
catch (err) {
this.addNotification({ status: 'error', message: err.stack });
isQuering.value = false;
}
}
return {
tableRef,
isQuering,
updateField,
deleteSelected
};
}

View File

@ -1,9 +1,7 @@
import Vue from 'vue'; import { createI18n } from 'vue-i18n';
import VueI18n from 'vue-i18n';
Vue.use(VueI18n); const i18n = createI18n({
fallbackLocale: 'en-US',
const i18n = new VueI18n({
messages: { messages: {
'en-US': require('./en-US'), 'en-US': require('./en-US'),
'it-IT': require('./it-IT'), 'it-IT': require('./it-IT'),

View File

@ -1,20 +1,28 @@
'use strict'; 'use strict';
import { createApp } from 'vue';
import Vue from 'vue';
import '@mdi/font/css/materialdesignicons.css'; import '@mdi/font/css/materialdesignicons.css';
import 'leaflet/dist/leaflet.css'; import 'leaflet/dist/leaflet.css';
import '@/scss/main.scss'; import '@/scss/main.scss';
import { VueMaskDirective } from 'v-mask';
import { useSettingsStore } from '@/stores/settings';
import App from '@/App.vue'; import App from '@/App.vue';
import store from '@/store'; import { pinia } from '@/stores';
import i18n from '@/i18n'; import i18n from '@/i18n';
i18n.locale = store.state.settings.locale; // https://github.com/probil/v-mask/issues/498#issuecomment-827027834
Vue.config.productionTip = false; const vMaskV2 = VueMaskDirective;
const vMaskV3 = {
beforeMount: vMaskV2.bind,
updated: vMaskV2.componentUpdated,
unmounted: vMaskV2.unbind
};
new Vue({ createApp(App)
render: h => h(App), .directive('mask', vMaskV3)
store, .use(pinia)
i18n .use(i18n)
}).$mount('#app'); .mount('#app');
const { locale } = useSettingsStore();
i18n.global.locale = locale;

View File

@ -1,13 +1,14 @@
'use strict'; 'use strict';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import { unproxify } from '../libs/unproxify';
export default class { export default class {
static getKey (params) { static getKey (params) {
return ipcRenderer.sendSync('get-key', params); return ipcRenderer.sendSync('get-key', unproxify(params));
} }
static showOpenDialog (options) { static showOpenDialog (options) {
return ipcRenderer.invoke('showOpenDialog', options); return ipcRenderer.invoke('show-open-dialog', unproxify(options));
} }
static getDownloadPathDirectory () { static getDownloadPathDirectory () {

View File

@ -1,17 +1,17 @@
'use strict'; 'use strict';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import connStringConstruct from '../libs/connStringDecode'; import connStringConstruct from '../libs/connStringDecode';
import { unproxify } from '../libs/unproxify';
export default class { export default class {
static makeTest (params) { static makeTest (params) {
params = connStringConstruct(params); params = connStringConstruct(params);
return ipcRenderer.invoke('test-connection', params); return ipcRenderer.invoke('test-connection', unproxify(params));
} }
static connect (params) { static connect (params) {
params = connStringConstruct(params); params = connStringConstruct(params);
return ipcRenderer.invoke('connect', params); return ipcRenderer.invoke('connect', unproxify(params));
} }
static checkConnection (uid) { static checkConnection (uid) {

View File

@ -1,28 +1,29 @@
'use strict'; 'use strict';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import { unproxify } from '../libs/unproxify';
export default class { export default class {
static getFunctionInformations (params) { static getFunctionInformations (params) {
return ipcRenderer.invoke('get-function-informations', params); return ipcRenderer.invoke('get-function-informations', unproxify(params));
} }
static dropFunction (params) { static dropFunction (params) {
return ipcRenderer.invoke('drop-function', params); return ipcRenderer.invoke('drop-function', unproxify(params));
} }
static alterFunction (params) { static alterFunction (params) {
return ipcRenderer.invoke('alter-function', params); return ipcRenderer.invoke('alter-function', unproxify(params));
} }
static alterTriggerFunction (params) { static alterTriggerFunction (params) {
return ipcRenderer.invoke('alter-trigger-function', params); return ipcRenderer.invoke('alter-trigger-function', unproxify(params));
} }
static createFunction (params) { static createFunction (params) {
return ipcRenderer.invoke('create-function', params); return ipcRenderer.invoke('create-function', unproxify(params));
} }
static createTriggerFunction (params) { static createTriggerFunction (params) {
return ipcRenderer.invoke('create-trigger-function', params); return ipcRenderer.invoke('create-trigger-function', unproxify(params));
} }
} }

View File

@ -1,20 +1,21 @@
'use strict'; 'use strict';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import { unproxify } from '../libs/unproxify';
export default class { export default class {
static getRoutineInformations (params) { static getRoutineInformations (params) {
return ipcRenderer.invoke('get-routine-informations', params); return ipcRenderer.invoke('get-routine-informations', unproxify(params));
} }
static dropRoutine (params) { static dropRoutine (params) {
return ipcRenderer.invoke('drop-routine', params); return ipcRenderer.invoke('drop-routine', unproxify(params));
} }
static alterRoutine (params) { static alterRoutine (params) {
return ipcRenderer.invoke('alter-routine', params); return ipcRenderer.invoke('alter-routine', unproxify(params));
} }
static createRoutine (params) { static createRoutine (params) {
return ipcRenderer.invoke('create-routine', params); return ipcRenderer.invoke('create-routine', unproxify(params));
} }
} }

View File

@ -1,24 +1,24 @@
'use strict'; 'use strict';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import { unproxify } from '../libs/unproxify';
export default class { export default class {
static getSchedulerInformations (params) { static getSchedulerInformations (params) {
return ipcRenderer.invoke('get-scheduler-informations', params); return ipcRenderer.invoke('get-scheduler-informations', unproxify(params));
} }
static dropScheduler (params) { static dropScheduler (params) {
return ipcRenderer.invoke('drop-scheduler', params); return ipcRenderer.invoke('drop-scheduler', unproxify(params));
} }
static alterScheduler (params) { static alterScheduler (params) {
return ipcRenderer.invoke('alter-scheduler', params); return ipcRenderer.invoke('alter-scheduler', unproxify(params));
} }
static createScheduler (params) { static createScheduler (params) {
return ipcRenderer.invoke('create-scheduler', params); return ipcRenderer.invoke('create-scheduler', unproxify(params));
} }
static toggleScheduler (params) { static toggleScheduler (params) {
return ipcRenderer.invoke('toggle-scheduler', params); return ipcRenderer.invoke('toggle-scheduler', unproxify(params));
} }
} }

View File

@ -1,25 +1,26 @@
'use strict'; 'use strict';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import { unproxify } from '../libs/unproxify';
export default class { export default class {
static createSchema (params) { static createSchema (params) {
return ipcRenderer.invoke('create-schema', params); return ipcRenderer.invoke('create-schema', unproxify(params));
} }
static updateSchema (params) { static updateSchema (params) {
return ipcRenderer.invoke('update-schema', params); return ipcRenderer.invoke('update-schema', unproxify(params));
} }
static getDatabaseCollation (params) { static getDatabaseCollation (params) {
return ipcRenderer.invoke('get-schema-collation', params); return ipcRenderer.invoke('get-schema-collation', unproxify(params));
} }
static deleteSchema (params) { static deleteSchema (params) {
return ipcRenderer.invoke('delete-schema', params); return ipcRenderer.invoke('delete-schema', unproxify(params));
} }
static getStructure (params) { static getStructure (params) {
return ipcRenderer.invoke('get-structure', params); return ipcRenderer.invoke('get-structure', unproxify(params, false));
} }
static getCollations (uid) { static getCollations (uid) {
@ -43,35 +44,35 @@ export default class {
} }
static killProcess (params) { static killProcess (params) {
return ipcRenderer.invoke('kill-process', params); return ipcRenderer.invoke('kill-process', unproxify(params));
} }
static killTabQuery (params) { static killTabQuery (params) {
return ipcRenderer.invoke('kill-tab-query', params); return ipcRenderer.invoke('kill-tab-query', unproxify(params));
} }
static commitTab (params) { static commitTab (params) {
return ipcRenderer.invoke('commit-tab', params); return ipcRenderer.invoke('commit-tab', unproxify(params));
} }
static rollbackTab (params) { static rollbackTab (params) {
return ipcRenderer.invoke('rollback-tab', params); return ipcRenderer.invoke('rollback-tab', unproxify(params));
} }
static destroyConnectionToCommit (params) { static destroyConnectionToCommit (params) {
return ipcRenderer.invoke('destroy-connection-to-commit', params); return ipcRenderer.invoke('destroy-connection-to-commit', unproxify(params));
} }
static useSchema (params) { static useSchema (params) {
return ipcRenderer.invoke('use-schema', params); return ipcRenderer.invoke('use-schema', unproxify(params));
} }
static rawQuery (params) { static rawQuery (params) {
return ipcRenderer.invoke('raw-query', params); return ipcRenderer.invoke('raw-query', unproxify(params));
} }
static export (params) { static export (params) {
return ipcRenderer.invoke('export', params); return ipcRenderer.invoke('export', unproxify(params));
} }
static abortExport () { static abortExport () {
@ -79,7 +80,7 @@ export default class {
} }
static import (params) { static import (params) {
return ipcRenderer.invoke('import-sql', params); return ipcRenderer.invoke('import-sql', unproxify(params));
} }
static abortImport () { static abortImport () {

View File

@ -1,68 +1,69 @@
'use strict'; 'use strict';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import { unproxify } from '../libs/unproxify';
export default class { export default class {
static getTableColumns (params) { static getTableColumns (params) {
return ipcRenderer.invoke('get-table-columns', params); return ipcRenderer.invoke('get-table-columns', unproxify(params));
} }
static getTableData (params) { static getTableData (params) {
return ipcRenderer.invoke('get-table-data', params); return ipcRenderer.invoke('get-table-data', unproxify(params));
} }
static getTableApproximateCount (params) { static getTableApproximateCount (params) {
return ipcRenderer.invoke('get-table-count', params); return ipcRenderer.invoke('get-table-count', unproxify(params));
} }
static getTableOptions (params) { static getTableOptions (params) {
return ipcRenderer.invoke('get-table-options', params); return ipcRenderer.invoke('get-table-options', unproxify(params));
} }
static getTableIndexes (params) { static getTableIndexes (params) {
return ipcRenderer.invoke('get-table-indexes', params); return ipcRenderer.invoke('get-table-indexes', unproxify(params));
} }
static getKeyUsage (params) { static getKeyUsage (params) {
return ipcRenderer.invoke('get-key-usage', params); return ipcRenderer.invoke('get-key-usage', unproxify(params));
} }
static updateTableCell (params) { static updateTableCell (params) {
return ipcRenderer.invoke('update-table-cell', params); return ipcRenderer.invoke('update-table-cell', unproxify(params));
} }
static deleteTableRows (params) { static deleteTableRows (params) {
return ipcRenderer.invoke('delete-table-rows', params); return ipcRenderer.invoke('delete-table-rows', unproxify(params));
} }
static insertTableRows (params) { static insertTableRows (params) {
return ipcRenderer.invoke('insert-table-rows', params); return ipcRenderer.invoke('insert-table-rows', unproxify(params));
} }
static insertTableFakeRows (params) { static insertTableFakeRows (params) {
return ipcRenderer.invoke('insert-table-fake-rows', params); return ipcRenderer.invoke('insert-table-fake-rows', unproxify(params));
} }
static getForeignList (params) { static getForeignList (params) {
return ipcRenderer.invoke('get-foreign-list', params); return ipcRenderer.invoke('get-foreign-list', unproxify(params));
} }
static createTable (params) { static createTable (params) {
return ipcRenderer.invoke('create-table', params); return ipcRenderer.invoke('create-table', unproxify(params));
} }
static alterTable (params) { static alterTable (params) {
return ipcRenderer.invoke('alter-table', params); return ipcRenderer.invoke('alter-table', unproxify(params));
} }
static duplicateTable (params) { static duplicateTable (params) {
return ipcRenderer.invoke('duplicate-table', params); return ipcRenderer.invoke('duplicate-table', unproxify(params));
} }
static truncateTable (params) { static truncateTable (params) {
return ipcRenderer.invoke('truncate-table', params); return ipcRenderer.invoke('truncate-table', unproxify(params));
} }
static dropTable (params) { static dropTable (params) {
return ipcRenderer.invoke('drop-table', params); return ipcRenderer.invoke('drop-table', unproxify(params));
} }
} }

View File

@ -1,24 +1,25 @@
'use strict'; 'use strict';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import { unproxify } from '../libs/unproxify';
export default class { export default class {
static getTriggerInformations (params) { static getTriggerInformations (params) {
return ipcRenderer.invoke('get-trigger-informations', params); return ipcRenderer.invoke('get-trigger-informations', unproxify(params));
} }
static dropTrigger (params) { static dropTrigger (params) {
return ipcRenderer.invoke('drop-trigger', params); return ipcRenderer.invoke('drop-trigger', unproxify(params));
} }
static alterTrigger (params) { static alterTrigger (params) {
return ipcRenderer.invoke('alter-trigger', params); return ipcRenderer.invoke('alter-trigger', unproxify(params));
} }
static createTrigger (params) { static createTrigger (params) {
return ipcRenderer.invoke('create-trigger', params); return ipcRenderer.invoke('create-trigger', unproxify(params));
} }
static toggleTrigger (params) { static toggleTrigger (params) {
return ipcRenderer.invoke('toggle-trigger', params); return ipcRenderer.invoke('toggle-trigger', unproxify(params));
} }
} }

View File

@ -1,8 +1,9 @@
'use strict'; 'use strict';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import { unproxify } from '../libs/unproxify';
export default class { export default class {
static getUsers (params) { static getUsers (params) {
return ipcRenderer.invoke('get-users', params); return ipcRenderer.invoke('get-users', unproxify(params));
} }
} }

View File

@ -1,20 +1,21 @@
'use strict'; 'use strict';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import { unproxify } from '../libs/unproxify';
export default class { export default class {
static getViewInformations (params) { static getViewInformations (params) {
return ipcRenderer.invoke('get-view-informations', params); return ipcRenderer.invoke('get-view-informations', unproxify(params));
} }
static dropView (params) { static dropView (params) {
return ipcRenderer.invoke('drop-view', params); return ipcRenderer.invoke('drop-view', unproxify(params));
} }
static alterView (params) { static alterView (params) {
return ipcRenderer.invoke('alter-view', params); return ipcRenderer.invoke('alter-view', unproxify(params));
} }
static createView (params) { static createView (params) {
return ipcRenderer.invoke('create-view', params); return ipcRenderer.invoke('create-view', unproxify(params));
} }
} }

View File

@ -1,3 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-empty-function */
/* eslint-disable @typescript-eslint/no-this-alias */
/* eslint-disable no-labels */ /* eslint-disable no-labels */
/* eslint-disable no-return-assign */ /* eslint-disable no-return-assign */
/* eslint-disable no-cond-assign */ /* eslint-disable no-cond-assign */

View File

@ -0,0 +1,21 @@
import { toRaw } from 'vue';
/**
* @param {*} val
* @param {Boolean} json converts the value in JSON object (default true)
*/
export function unproxify (val, json = true) {
if (json)// JSON conversion
return JSON.parse(JSON.stringify(val));
else if (Array.isArray(val))// If array
return toRaw(val);
else if (typeof val === 'object') { // If object
const result = {};
for (const key in val)
result[key] = toRaw(val[key]);
return result;
}
else
return toRaw(val);
}

Some files were not shown because too many files have changed in this diff Show More