tiny-rdm/frontend/src/components/dialogs/ConnectionDialog.vue

278 lines
9.9 KiB
Vue

<script setup>
import { get, isEmpty, map } from 'lodash'
import { computed, nextTick, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { TestConnection } from '../../../wailsjs/go/services/connectionService.js'
import useDialog from '../../stores/dialog'
import { useMessage } from 'naive-ui'
import Close from '../icons/Close.vue'
import useConnectionStore from '../../stores/connections.js'
/**
* Dialog for create or edit connection
*/
const dialogStore = useDialog()
const connectionStore = useConnectionStore()
const message = useMessage()
const i18n = useI18n()
const editName = ref('')
const generalForm = ref(null)
const generalFormRules = () => {
const requiredMsg = i18n.t('field_required')
return {
name: { required: true, message: requiredMsg, trigger: 'input' },
addr: { required: true, message: requiredMsg, trigger: 'input' },
defaultFilter: { required: true, message: requiredMsg, trigger: 'input' },
keySeparator: { required: true, message: requiredMsg, trigger: 'input' },
}
}
const isEditMode = computed(() => !isEmpty(editName.value))
const groupOptions = computed(() => {
const options = map(connectionStore.groups, (group) => ({
label: group,
value: group,
}))
options.splice(0, 0, {
label: i18n.t('no_group'),
value: '',
})
return options
})
const tab = ref('general')
const testing = ref(false)
const showTestResult = ref(false)
const testResult = ref('')
const showTestConnSuccResult = computed(() => {
return isEmpty(testResult.value) && showTestResult.value === true
})
const showTestConnFailResult = computed(() => {
return !isEmpty(testResult.value) && showTestResult.value === true
})
const formLabelWidth = computed(() => {
// Compatible with long english word
if (tab.value === 'advanced' && i18n.locale.value === 'en') {
return '140px'
}
return '80px'
})
const predefineColors = ref(['', '#FE5959', '#FEC230', '#FEF27F', '#6CEFAF', '#46C3FC', '#B388FC', '#B0BEC5'])
const generalFormRef = ref(null)
const advanceFormRef = ref(null)
const onSaveConnection = async () => {
// Validate general form
await generalFormRef.value?.validate((err) => {
if (err) {
nextTick(() => (tab.value = 'general'))
}
})
// Validate advance form
await advanceFormRef.value?.validate((err) => {
if (err) {
nextTick(() => (tab.value = 'advanced'))
}
})
// Store new connection
const { success, msg } = await connectionStore.saveConnection(editName.value, generalForm.value)
if (!success) {
message.error(msg)
return
}
message.success(i18n.t('handle_succ'))
onClose()
}
const resetForm = () => {
generalForm.value = connectionStore.newDefaultConnection()
generalFormRef.value?.restoreValidation()
showTestResult.value = false
testResult.value = ''
tab.value = 'general'
}
watch(
() => dialogStore.connDialogVisible,
(visible) => {
if (visible) {
editName.value = get(dialogStore.connParam, 'name', '')
generalForm.value = dialogStore.connParam || connectionStore.newDefaultConnection()
}
}
)
const onTestConnection = async () => {
testResult.value = ''
testing.value = true
let result = ''
try {
const { addr, port, username, password } = generalForm.value
const { success = false, msg } = await TestConnection(addr, port, username, password)
if (!success) {
result = msg
}
} catch (e) {
result = e.message
} finally {
testing.value = false
showTestResult.value = true
}
if (!isEmpty(result)) {
testResult.value = result
} else {
testResult.value = ''
}
}
const onClose = () => {
if (isEditMode.value) {
dialogStore.closeEditDialog()
} else {
dialogStore.closeNewDialog()
}
}
</script>
<template>
<n-modal
v-model:show="dialogStore.connDialogVisible"
:closable="false"
:close-on-esc="false"
:mask-closable="false"
:on-after-leave="resetForm"
:show-icon="false"
:title="isEditMode ? $t('edit_conn_title') : $t('new_conn_title')"
preset="dialog"
transform-origin="center"
>
<n-tabs v-model:value="tab">
<n-tab-pane :tab="$t('general')" display-directive="show" name="general">
<n-form
ref="generalFormRef"
:label-width="formLabelWidth"
:model="generalForm"
:rules="generalFormRules()"
:show-require-mark="false"
label-align="right"
label-placement="left"
>
<n-form-item :label="$t('conn_name')" path="name" required>
<n-input v-model:value="generalForm.name" :placeholder="$t('conn_name_tip')" />
</n-form-item>
<n-form-item v-if="!isEditMode" :label="$t('conn_group')" required>
<n-select v-model:value="generalForm.group" :options="groupOptions" />
</n-form-item>
<n-form-item :label="$t('conn_addr')" path="addr" required>
<n-input v-model:value="generalForm.addr" :placeholder="$t('conn_addr_tip')" />
<n-text style="width: 40px; text-align: center">:</n-text>
<n-input-number v-model:value="generalForm.port" :max="65535" :min="1" style="width: 200px" />
</n-form-item>
<n-form-item :label="$t('conn_pwd')" path="password">
<n-input
v-model:value="generalForm.password"
:placeholder="$t('conn_pwd_tip')"
show-password-on="click"
type="password"
/>
</n-form-item>
<n-form-item :label="$t('conn_usr')" path="username">
<n-input v-model="generalForm.username" :placeholder="$t('conn_usr_tip')" />
</n-form-item>
</n-form>
</n-tab-pane>
<n-tab-pane :tab="$t('advanced')" display-directive="show" name="advanced">
<n-form
ref="advanceFormRef"
:label-width="formLabelWidth"
:model="generalForm"
:rules="generalFormRules()"
:show-require-mark="false"
label-align="right"
label-placement="left"
>
<n-form-item :label="$t('conn_advn_filter')" path="defaultFilter">
<n-input v-model:value="generalForm.defaultFilter" :placeholder="$t('conn_advn_filter_tip')" />
</n-form-item>
<n-form-item :label="$t('conn_advn_separator')" path="keySeparator">
<n-input
v-model:value="generalForm.keySeparator"
:placeholder="$t('conn_advn_separator_tip')"
/>
</n-form-item>
<n-form-item :label="$t('conn_advn_conn_timeout')" path="connTimeout">
<n-input-number v-model:value="generalForm.connTimeout" :max="999999" :min="1">
<template #suffix>
{{ $t('second') }}
</template>
</n-input-number>
</n-form-item>
<n-form-item :label="$t('conn_advn_exec_timeout')" path="execTimeout">
<n-input-number v-model:value="generalForm.execTimeout" :max="999999" :min="1">
<template #suffix>
{{ $t('second') }}
</template>
</n-input-number>
</n-form-item>
<n-form-item :label="$t('conn_advn_mark_color')" path="markColor">
<div
v-for="color in predefineColors"
:key="color"
:class="{
'color-preset-item_selected': generalForm.markColor === color,
}"
:style="{ backgroundColor: color }"
class="color-preset-item"
@click="generalForm.markColor = color"
>
<n-icon v-if="color === ''" :component="Close" size="24" />
</div>
</n-form-item>
</n-form>
</n-tab-pane>
</n-tabs>
<!-- test result alert-->
<n-alert v-if="showTestConnSuccResult" title="" type="success">
{{ $t('conn_test_succ') }}
</n-alert>
<n-alert v-if="showTestConnFailResult" title="" type="error">
{{ $t('conn_test_fail') }}: {{ testResult }}
</n-alert>
<template #action>
<div class="flex-item-expand">
<n-button :loading="testing" @click="onTestConnection">{{ $t('conn_test') }}</n-button>
</div>
<div class="flex-item n-dialog__action">
<n-button @click="onClose">{{ $t('cancel') }}</n-button>
<n-button type="primary" @click="onSaveConnection">
{{ isEditMode ? $t('update') : $t('confirm') }}
</n-button>
</div>
</template>
</n-modal>
</template>
<style lang="scss" scoped>
.color-preset-item {
width: 24px;
height: 24px;
margin-right: 2px;
border: white 3px solid;
cursor: pointer;
&_selected,
&:hover {
border-color: #cdd0d6;
}
}
</style>