This commit is contained in:
Sense T 2024-04-10 11:00:47 +08:00
parent 65bf461d44
commit 9465bb885d
8 changed files with 267 additions and 45 deletions

View File

@ -25,28 +25,28 @@
<NFormItem :label="t('records.refresh')" path="refresh_interval">
<NInputNumber v-model:value="domain.refresh_interval" :show-button="false">
<template #suffix>
{{ t('domains.form.unitForSecond') }}
{{ t('common.unitForSecond') }}
</template>
</NInputNumber>
</NFormItem>
<NFormItem :label="t('records.retry')" path="retry_interval">
<NInputNumber v-model:value="domain.retry_interval" :show-button="false">
<template #suffix>
{{ t('domains.form.unitForSecond') }}
{{ t('common.unitForSecond') }}
</template>
</NInputNumber>
</NFormItem>
<NFormItem :label="t('records.expire')" path="expiry_period">
<NInputNumber v-model:value="domain.expiry_period" :show-button="false">
<template #suffix>
{{ t('domains.form.unitForSecond') }}
{{ t('common.unitForSecond') }}
</template>
</NInputNumber>
</NFormItem>
<NFormItem :label="t('records.ttl')" path="negative_ttl">
<NInputNumber v-model:value="domain.negative_ttl" :show-button="false">
<template #suffix>
{{ t('domains.form.unitForSecond') }}
{{ t('common.unitForSecond') }}
</template>
</NInputNumber>
</NFormItem>
@ -62,16 +62,16 @@
</template>
{{ t('common.cancel') }}
</NButton>
<NSpin :show="loading">
<NButton size="small" type="primary" :disabled="loading || invalidData !== allFlags" @click="confirm" attr-type="submit">
<template #icon>
<NIcon>
<Check />
</NIcon>
</template>
{{ t('common.confirm') }}
</NButton>
</NSpin>
<NButton size="small" type="primary" :disabled="invalidData !== allFlags" :loading="loading"
@click="confirm" attr-type="submit">
<template #icon>
<NIcon>
<Check />
</NIcon>
</template>
{{ t('common.confirm') }}
</NButton>
</NFlex>
</template>
</NCard>
@ -92,12 +92,11 @@ import {
NButton,
NInput,
NInputNumber,
NSpin,
useNotification,
type FormRules,
type FormItemRule
} from 'naive-ui'
import { ref, watch } from 'vue';
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
const enum validFlags {
@ -105,7 +104,7 @@ const enum validFlags {
mainNsValid = domainNameValid << 1,
adminEmailValid = mainNsValid << 1
}
const allFlags = validFlags.adminEmailValid|validFlags.mainNsValid|validFlags.domainNameValid
const allFlags = validFlags.adminEmailValid | validFlags.mainNsValid | validFlags.domainNameValid
const { t } = useI18n()
@ -133,7 +132,6 @@ const rules = {
main_dns: [{
required: true,
trigger: 'blur',
validator: (_rule: FormItemRule, value: string) => {
return validate(
value,
@ -173,6 +171,7 @@ const rules = {
negative_ttl: [{
required: true,
trigger: 'blur',
type: 'number'
}]
} as FormRules
@ -197,7 +196,7 @@ async function confirm() {
}
function validate(value: string, reg: RegExp, msg: string, flag: validFlags): Promise<void> {
return new Promise<void>((resolve, reject) => {
return new Promise<void>((resolve, reject) => {
if (!value) {
invalidData.value &= ~flag
reject(Error(t('common.mandatory')))

View File

@ -24,17 +24,16 @@
</template>
{{ t('common.cancel') }}
</NButton>
<NSpin :show="loading">
<NButton size="small" type="error" :disabled="(domain_name !== domain?.domain_name) || loading"
@click="confirm">
<template #icon>
<NIcon>
<TrashAlt />
</NIcon>
</template>
{{ t('common.confirm') }}
</NButton>
</NSpin>
<NButton size="small" type="error" :disabled="domain_name !== domain?.domain_name"
:loading="loading" @click="confirm">
<template #icon>
<NIcon>
<TrashAlt />
</NIcon>
</template>
{{ t('common.confirm') }}
</NButton>
</NFlex>
</template>
</NCard>
@ -43,7 +42,7 @@
<script setup lang="ts">
import { useDomainStore, type Domain } from '@/stores/domains';
import { NModal, NCard, NFlex, NButton, NIcon, NInput, NSpin, useNotification } from 'naive-ui'
import { NModal, NCard, NFlex, NButton, NIcon, NInput, useNotification } from 'naive-ui'
import { Times, TrashAlt, QuestionCircle } from '@vicons/fa';
import { useI18n } from 'vue-i18n';

View File

@ -0,0 +1,170 @@
<template>
<NModal :mask-closable="false" :show="show">
<NCard style="width: 640px" role="dialog" aria-modal="true">
<template #header>
<span v-if="!record || !record.id || record.id < 1">{{ t('common.new') }}</span><span v-else>{{
t('common.edit') }}</span><span>{{ t('records._') }}</span>
</template>
<NForm :model="record" inline :rules="rules">
<NFormItem :label="t('records.recordType')">
<NSelect v-model:value="record.record_type" :options="recordTypeOptions" />
</NFormItem>
<NFormItem :label="t('records.name')" path="name">
<NInput v-model:value="record.name" />
</NFormItem>
<NFormItem label="TTL" path="ttl">
<NInputNumber v-model:value="record.ttl" :show-button="false">
<template #suffix>
{{ t('common.unitForSecond') }}
</template>
</NInputNumber>
</NFormItem>
</NForm>
<NForm :model="record" inline>
<NFormItem :label="t('records.content')"
v-if="[RecordTypes.RecordTypeA, RecordTypes.RecordTypeAAAA].indexOf(record.record_type) > -1">
<NInput v-model:value="(record.content as ARecord | AAAARecord).ip" placeholder="IP" />
</NFormItem>
<NFormItem :label="t('records.content')"
v-if="[RecordTypes.RecordTypeCNAME, RecordTypes.RecordTypeNS].indexOf(record.record_type) > -1">
<NInput v-model:value="(record.content as CNAMERecord | NSRecord).host"
:placeholder="t('records.form.host')" />
</NFormItem>
<NFormItem :label="t('records.content')" v-if="RecordTypes.RecordTypeTXT === record.record_type">
<NInput v-model:value="(record.content as TXTRecord).text" :placeholder="t('records.form.text')" />
</NFormItem>
<NFormItem :label="t('records.content')" v-if="RecordTypes.RecordTypeMX === record.record_type">
<NInputGroup>
<NInput :placeholder="t('records.form.host')" v-model:value="(record.content as MXRecord).host"
style="width: 75%;" />
<NInputNumber :placeholder="t('records.form.preference')"
v-model:value="(record.content as MXRecord).preference" style="width: 25%;" />
</NInputGroup>
</NFormItem>
<NFormItem :label="t('records.content')" v-if="RecordTypes.RecordTypeSRV === record.record_type">
<NInputGroup>
<NInputNumber :placeholder="t('records.form.priority')"
v-model:value="(record.content as SRVRecord).priority" style="width: 25%;" />
<NInputNumber :placeholder="t('records.form.weight')"
v-model:value="(record.content as SRVRecord).weight" style="width: 25%;" />
<NInputNumber :placeholder="t('records.form.port')"
v-model:value="(record.content as SRVRecord).port" style="width: 25%;" :min="0"
:max="65535" />
<NInput :placeholder="t('records.form.target')"
v-model:value="(record.content as SRVRecord).target" style="width: 25%;" />
</NInputGroup>
</NFormItem>
<NFormItem :label="t('records.content')" v-if="RecordTypes.RecordTypeCAA === record.record_type">
<NInputGroup>
<NInputNumber :placeholder="t('records.form.flag')"
v-model:value="(record.content as CAARecord).flag" style="width: 20%;" />
<NInput :placeholder="t('records.form.tag')" v-model:value="(record.content as CAARecord).tag"
style="width: 40%;" />
<NInput :placeholder="t('records.form.value')"
v-model:value="(record.content as CAARecord).value" style="width: 40%;" />
</NInputGroup>
</NFormItem>
</NForm>
<template #action>
<NFlex justify="end">
<NButton size="small" @click="show = false">
<template #icon>
<NIcon>
<Times />
</NIcon>
</template>
{{ t('common.cancel') }}
</NButton>
<NButton size="small" type="primary" :loading="loading" @click="confirm" attr-type="submit">
<template #icon>
<NIcon>
<Check />
</NIcon>
</template>
{{ t('common.confirm') }}
</NButton>
</NFlex>
</template>
</NCard>
</NModal>
</template>
<script setup lang="ts">
import {
NModal,
NCard,
NForm,
NFormItem,
NFlex,
NButton,
NInput,
NInputNumber,
NInputGroup,
NSelect,
useNotification,
type FormRules,
type SelectOption
} from 'naive-ui'
import { getErrorInfo } from '@/apis/api';
import {
useRecordStore,
RecordTypes,
type Record,
type ARecord,
type AAAARecord,
type CAARecord,
type CNAMERecord,
type NSRecord,
type SRVRecord,
type TXTRecord,
type MXRecord,
} from '@/stores/records';
import { Check, Times } from '@vicons/fa';
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
const { t } = useI18n()
const props = defineProps<{
record: Record,
domain: string,
}>()
const show = defineModel<boolean>('show', { default: false })
const loading = ref(false)
const notification = useNotification()
const recordStore = useRecordStore()
const recordTypeOptions = Object.entries(RecordTypes).filter(
e => e[1] !== RecordTypes.RecordTypeSOA
).map(e => {
return {
label: e[1],
value: e[1]
} as SelectOption
})
const rules = {
name: {
required: true,
trigger: 'blur',
message: t('common.mandatory')
}
} as FormRules
async function confirm() {
loading.value = true;
try {
if (!props.record.id || props.record.id < 1) {
await recordStore.addRecord(props.domain, props.record)
} else {
await recordStore.updateRecord(props.domain, props.record)
}
show.value = false
} catch (e) {
const msg = getErrorInfo(e)
notification.error(msg)
console.error(e)
}
loading.value = false;
}
</script>

View File

@ -11,7 +11,7 @@
</template>
{{ $t("common.edit") }}
</NTooltip>
<NPopconfirm>
<NPopconfirm @positive-click="confirm">
<template #trigger>
<NButton type="error" size="tiny">
<template #icon>
@ -26,12 +26,22 @@
</template>
<script setup lang="ts">
import { NButton, NButtonGroup, NTooltip, NIcon, NPopconfirm, NFlex } from 'naive-ui'
import { NButton, NButtonGroup, NTooltip, NIcon, NPopconfirm, NFlex, useNotification } from 'naive-ui'
import { TrashAlt, EditRegular } from '@vicons/fa'
import { useI18n } from 'vue-i18n';
import type { Record } from '@/stores/records';
import { useRecordStore, type Record } from '@/stores/records';
import { getErrorInfo } from '@/apis/api';
const { t } = useI18n()
const recordStore = useRecordStore()
const notification = useNotification()
const props = defineProps<{
record: Record
domain: string
}>();
const emit = defineEmits(['record-delete'])
function confirm() {
emit('record-delete', props.domain, props.record)
}
</script>

View File

@ -9,7 +9,8 @@ export default {
new: 'New',
cancel: 'Cancel',
confirm: 'OK',
mandatory: 'This field is mandatory'
mandatory: 'This field is mandatory',
unitForSecond: 'Second(s)'
},
api: {
error400: {
@ -48,7 +49,6 @@ export default {
form: {
adminMail: 'Admin Email',
mainDNS: 'Main DNS',
unitForSecond: 'Second(s)'
},
errors: {
@ -67,5 +67,18 @@ export default {
retry: 'Retry Interval',
expire: 'Expiry Period',
ttl: 'Negative TTL',
form: {
text: 'Text',
host: 'Host',
preference: 'Preference',
priority: 'Priority',
weight: 'Weight',
port: 'Port',
target: 'Target',
flag: 'Flag',
tag: 'Tag',
value: 'Value'
}
}
}

View File

@ -9,7 +9,8 @@ export default {
new: '新增',
cancel: '取消',
confirm: '确定',
mandatory: '此项必填'
mandatory: '此项必填',
unitForSecond: '秒'
},
api: {
error400: {
@ -48,7 +49,6 @@ export default {
form: {
adminMail: '管理员邮箱',
mainDNS: '主 DNS 服务器',
unitForSecond: '秒'
},
errors: {
@ -67,5 +67,18 @@ export default {
retry: '重试间隔',
expire: '超期时间',
ttl: '缓存时间',
form: {
text: '文本',
host: '主机',
preference: '优先级',
priority: '优先级',
weight: '权重',
port: '端口',
target: '目标',
flag: '标志',
tag: '标签',
value: '值'
}
}
}

View File

@ -232,5 +232,5 @@ export const useRecordStore = defineStore('records', () => {
records.value = records.value?.filter(e => e.id !== record.id)
}
return { records, recordsGetter, loadRecords }
return { records, recordsGetter, loadRecords, addRecord, updateRecord, removeRecord }
})

View File

@ -49,7 +49,7 @@ const columns = [
{
key: '',
render(row: Record) {
return <RecordOps record={row} />
return <RecordOps record={row} domain={props.domain} onRecord-delete={deleteRecord} />
}
}
] as DataTableColumns<Record>
@ -70,11 +70,18 @@ onMounted(() => {
}
})
function refreshRecords() {
recordStore.loadRecords(props.domain)
records.value = recordStore.records?.filter(e => e.record_type !== RecordTypes.RecordTypeSOA)
soa.value = recordStore.records?.find(e => e.record_type === RecordTypes.RecordTypeSOA)?.content as SOARecord
loading.value = false;
async function refreshRecords() {
try {
await recordStore.loadRecords(props.domain)
records.value = recordStore.records?.filter(e => e.record_type !== RecordTypes.RecordTypeSOA)
soa.value = recordStore.records?.find(e => e.record_type === RecordTypes.RecordTypeSOA)?.content as SOARecord
} catch (err) {
const msg = getErrorInfo(err)
notification.error(msg)
console.error(err)
} finally {
loading.value = false;
}
}
function goBack() {
@ -91,6 +98,17 @@ function searchRecord(value: string) {
filter(e => e.record_type !== RecordTypes.RecordTypeSOA)
}
}
async function deleteRecord(domain: string, record: Record) {
try {
await recordStore.removeRecord(domain, record)
records.value = recordStore.records?.filter(e => e.record_type !== RecordTypes.RecordTypeSOA)
} catch (err) {
const msg = getErrorInfo(err)
notification.error(msg)
console.error(err)
}
}
</script>
<template>