278 lines
8.8 KiB
Vue
278 lines
8.8 KiB
Vue
<script setup>
|
|
import { computed, h, nextTick, reactive, ref, watchEffect } from 'vue'
|
|
import { types, typesColor } from '@/consts/support_redis_type.js'
|
|
import useDialog from 'stores/dialog'
|
|
import { endsWith, get, isEmpty, keys, map, trim } from 'lodash'
|
|
import NewStringValue from '@/components/new_value/NewStringValue.vue'
|
|
import NewHashValue from '@/components/new_value/NewHashValue.vue'
|
|
import NewListValue from '@/components/new_value/NewListValue.vue'
|
|
import NewZSetValue from '@/components/new_value/NewZSetValue.vue'
|
|
import NewSetValue from '@/components/new_value/NewSetValue.vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { NSpace } from 'naive-ui'
|
|
import useTabStore from 'stores/tab.js'
|
|
import NewStreamValue from '@/components/new_value/NewStreamValue.vue'
|
|
import useBrowserStore from 'stores/browser.js'
|
|
import Import from '@/components/icons/Import.vue'
|
|
import NewJsonValue from '@/components/new_value/NewJsonValue.vue'
|
|
|
|
const i18n = useI18n()
|
|
const newForm = reactive({
|
|
server: '',
|
|
db: 0,
|
|
key: '',
|
|
type: '',
|
|
ttl: -1,
|
|
value: null,
|
|
})
|
|
const formRules = computed(() => {
|
|
const requiredMsg = i18n.t('dialogue.field_required')
|
|
return {
|
|
key: { required: true, message: requiredMsg, trigger: 'input' },
|
|
type: { required: true, message: requiredMsg, trigger: 'input' },
|
|
ttl: { required: true, message: requiredMsg, trigger: 'input' },
|
|
}
|
|
})
|
|
const dbOptions = computed(() =>
|
|
map(keys(browserStore.getDBList(newForm.server)), (key) => ({
|
|
label: key,
|
|
value: parseInt(key),
|
|
})),
|
|
)
|
|
const newFormRef = ref(null)
|
|
const subFormRef = ref(null)
|
|
|
|
const options = computed(() => {
|
|
return Object.keys(types).map((t) => ({
|
|
value: t,
|
|
label: t,
|
|
}))
|
|
})
|
|
const newValueComponent = {
|
|
[types.STRING]: NewStringValue,
|
|
[types.HASH]: NewHashValue,
|
|
[types.LIST]: NewListValue,
|
|
[types.SET]: NewSetValue,
|
|
[types.ZSET]: NewZSetValue,
|
|
[types.STREAM]: NewStreamValue,
|
|
[types.JSON]: NewJsonValue,
|
|
}
|
|
const defaultValue = {
|
|
[types.STRING]: '',
|
|
[types.HASH]: [],
|
|
[types.LIST]: [],
|
|
[types.SET]: [],
|
|
[types.ZSET]: [],
|
|
[types.STREAM]: [],
|
|
[types.JSON]: '{}',
|
|
}
|
|
|
|
const dialogStore = useDialog()
|
|
const scrollRef = ref(null)
|
|
watchEffect(() => {
|
|
if (dialogStore.newKeyDialogVisible) {
|
|
const { prefix, server, db } = dialogStore.newKeyParam
|
|
const separator = browserStore.getSeparator(server)
|
|
newForm.server = server
|
|
if (isEmpty(prefix)) {
|
|
newForm.key = ''
|
|
} else {
|
|
if (!endsWith(prefix, separator)) {
|
|
newForm.key = prefix + separator
|
|
} else {
|
|
newForm.key = prefix
|
|
}
|
|
}
|
|
newForm.db = db
|
|
newForm.type = options.value[0].value
|
|
newForm.ttl = -1
|
|
newForm.value = null
|
|
}
|
|
})
|
|
|
|
const renderTypeLabel = (option) => {
|
|
return h(
|
|
NSpace,
|
|
{
|
|
align: 'center',
|
|
inline: true,
|
|
size: 3,
|
|
itemStyle: {
|
|
lineHeight: 'var(--n-blank-height)',
|
|
},
|
|
},
|
|
{
|
|
default: () => [
|
|
h('div', {
|
|
style: {
|
|
borderRadius: '9999px',
|
|
backgroundColor: typesColor[option.value],
|
|
width: '13px',
|
|
height: '13px',
|
|
border: '2px solid white',
|
|
},
|
|
}),
|
|
option.value,
|
|
],
|
|
},
|
|
)
|
|
}
|
|
|
|
const onAppend = () => {
|
|
nextTick(() => {
|
|
scrollRef.value?.scrollTo({ position: 'bottom' })
|
|
})
|
|
}
|
|
|
|
const onChangeType = () => {
|
|
newForm.value = null
|
|
}
|
|
|
|
const browserStore = useBrowserStore()
|
|
const tabStore = useTabStore()
|
|
const onAdd = async () => {
|
|
await newFormRef.value?.validate((errs) => {
|
|
const err = get(errs, '0.0.message')
|
|
if (err != null) {
|
|
$message.error(err)
|
|
}
|
|
})
|
|
if (subFormRef.value?.validate) {
|
|
await subFormRef.value?.validate((errs) => {
|
|
const err = get(errs, '0.0.message')
|
|
if (err != null) {
|
|
$message.error(err)
|
|
} else {
|
|
$message.error(i18n.t('dialogue.spec_field_required', { key: i18n.t('dialogue.field.element') }))
|
|
}
|
|
})
|
|
}
|
|
try {
|
|
const { server, db, key, type, ttl } = newForm
|
|
let { value } = newForm
|
|
if (value == null) {
|
|
value = defaultValue[type]
|
|
}
|
|
// await browserStore.reloadKey({server, db, key: trim(key)})
|
|
const {
|
|
success,
|
|
msg,
|
|
nodeKey = '',
|
|
} = await browserStore.setKey({
|
|
server,
|
|
db,
|
|
key: trim(key),
|
|
keyType: type,
|
|
value,
|
|
ttl,
|
|
})
|
|
if (success) {
|
|
// select current key
|
|
await nextTick()
|
|
const selectedDB = browserStore.getSelectedDB(server)
|
|
if (selectedDB === db) {
|
|
tabStore.setSelectedKeys(server, nodeKey)
|
|
browserStore.reloadKey({ server, db, key })
|
|
}
|
|
} else if (!isEmpty(msg)) {
|
|
$message.error(msg)
|
|
}
|
|
dialogStore.closeNewKeyDialog()
|
|
} catch (e) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
const onClose = () => {
|
|
dialogStore.closeNewKeyDialog()
|
|
}
|
|
|
|
const onImport = () => {
|
|
dialogStore.closeNewKeyDialog()
|
|
dialogStore.openImportKeyDialog(newForm.server, newForm.db)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<n-modal
|
|
v-model:show="dialogStore.newKeyDialogVisible"
|
|
:closable="false"
|
|
:mask-closable="false"
|
|
:show-icon="false"
|
|
:title="$t('dialogue.key.new')"
|
|
close-on-esc
|
|
preset="dialog"
|
|
style="width: 600px"
|
|
transform-origin="center"
|
|
@esc="onClose">
|
|
<n-scrollbar ref="scrollRef" style="max-height: 500px">
|
|
<n-form
|
|
ref="newFormRef"
|
|
:model="newForm"
|
|
:rules="formRules"
|
|
:show-require-mark="false"
|
|
label-placement="top"
|
|
style="padding-right: 15px">
|
|
<n-form-item :label="$t('common.key')" path="key" required>
|
|
<n-input v-model:value="newForm.key" placeholder="" />
|
|
</n-form-item>
|
|
<n-form-item :label="$t('dialogue.key.db_index')" path="db" required>
|
|
<n-select v-model:value="newForm.db" :options="dbOptions" filterable />
|
|
</n-form-item>
|
|
<n-form-item :label="$t('interface.type')" path="type" required>
|
|
<n-select
|
|
v-model:value="newForm.type"
|
|
:options="options"
|
|
:render-label="renderTypeLabel"
|
|
@update:value="onChangeType" />
|
|
</n-form-item>
|
|
<n-form-item :label="$t('interface.ttl')" required>
|
|
<n-input-group>
|
|
<n-input-number
|
|
v-model:value="newForm.ttl"
|
|
:max="Number.MAX_SAFE_INTEGER"
|
|
:min="-1"
|
|
:show-button="false"
|
|
placeholder="TTL">
|
|
<template #suffix>
|
|
{{ $t('common.second') }}
|
|
</template>
|
|
</n-input-number>
|
|
<n-button :focusable="false" secondary type="primary" @click="() => (newForm.ttl = -1)">
|
|
{{ $t('interface.forever') }}
|
|
</n-button>
|
|
</n-input-group>
|
|
</n-form-item>
|
|
<component
|
|
:is="newValueComponent[newForm.type]"
|
|
ref="subFormRef"
|
|
v-model:value="newForm.value"
|
|
@append="onAppend" />
|
|
<!-- TODO: Add import from txt file option -->
|
|
</n-form>
|
|
</n-scrollbar>
|
|
|
|
<template #action>
|
|
<div class="flex-item-expand">
|
|
<n-button :focusable="false" size="medium" @click="onImport">
|
|
<template #icon>
|
|
<n-icon :component="Import" />
|
|
</template>
|
|
{{ $t('interface.import_key') }}
|
|
</n-button>
|
|
</div>
|
|
<div class="flex-item n-dialog__action">
|
|
<n-button :focusable="false" size="medium" @click="onClose">
|
|
{{ $t('common.cancel') }}
|
|
</n-button>
|
|
<n-button :focusable="false" size="medium" type="primary" @click="onAdd">
|
|
{{ $t('common.confirm') }}
|
|
</n-button>
|
|
</div>
|
|
</template>
|
|
</n-modal>
|
|
</template>
|
|
|
|
<style lang="scss" scoped></style>
|