tsx used - stage 2

This commit is contained in:
Sense T 2024-04-11 22:05:58 +08:00
parent b669a3e68e
commit 731504ae82
8 changed files with 282 additions and 283 deletions

View File

@ -0,0 +1,219 @@
import {
NModal,
NCard,
NForm,
NFormItem,
NFlex,
NIcon,
NButton,
NInput,
NInputNumber,
type FormRules,
type FormItemRule,
createDiscreteApi
} from 'naive-ui'
import { getErrorInfo } from '@/apis/api';
import { useDomainStore, type Domain } from '@/stores/domains';
import { Check, Times } from '@vicons/fa';
import i18n from '@/locale/i18n'
import { ref, type SetupContext } from 'vue';
const { t } = i18n.global
const domainStore = useDomainStore()
const { notification } = createDiscreteApi(['notification'])
const enum validFlags {
domainNameValid = 1,
mainNsValid = domainNameValid << 1,
adminEmailValid = mainNsValid << 1
}
const allFlags = validFlags.adminEmailValid | validFlags.mainNsValid | validFlags.domainNameValid
const rules = {
domain_name: [{
required: true,
trigger: 'blur',
validator: (_rule: FormItemRule, value: string) => {
return validate(
value,
/^([\w-]+\.)+[\w-]+$/,
'domains.errors.domainName',
validFlags.domainNameValid
)
}
}],
main_dns: [{
required: true,
trigger: 'blur',
validator: (_rule: FormItemRule, value: string) => {
return validate(
value,
/^([\w-]+\.)+[\w-]+$/,
'domains.errors.domainName',
validFlags.mainNsValid,
)
}
}],
admin_email: [{
required: true,
trigger: 'blur',
validator: (_rule: FormItemRule, value: string) => {
return validate(
value,
/^[\w-.]+@([\w-]+\.)+[\w-]+$/,
'domains.errors.mail',
validFlags.adminEmailValid
)
}
}],
refresh_interval: [{
required: true,
trigger: 'blur',
type: 'number',
}],
retry_interval: [{
required: true,
trigger: 'blur',
type: 'number',
}],
expiry_period: [{
required: true,
trigger: 'blur',
type: 'number',
}],
negative_ttl: [{
required: true,
trigger: 'blur',
type: 'number'
}]
} as FormRules
type Props = {
domain: Domain
show: boolean
}
type Events = {
'update:show': (v: boolean) => void
'update:value': (v: string | number | null) => void
}
const loading = ref(false)
const invalidData = ref(0)
function validate(value: string, reg: RegExp, msg: string, flag: validFlags): Promise<void> {
return new Promise<void>((resolve, reject) => {
if (!value) {
invalidData.value &= ~flag
reject(Error(t('common.mandatory')))
} else if (!reg.test(value)) {
invalidData.value &= ~flag
reject(Error(t(msg)))
} else {
invalidData.value |= flag
resolve()
}
})
}
async function confirm(domain: Domain) {
loading.value = true;
try {
if (!domain.id || domain.id < 1) {
await domainStore.addDomain(domain)
} else {
await domainStore.updateDomain(domain)
}
} catch (e) {
const msg = getErrorInfo(e)
notification.error(msg)
console.error(e)
} finally {
loading.value = false
}
}
function easyInput(domain_name: string, domain: Domain) {
domain.admin_email = `admin@${domain_name}`
domain.main_dns = `ns1.${domain_name}`
}
function modalHeader({ domain }: Props) {
return (
<>
{(!domain || !domain.id || domain.id < 1) ? <span>{t('common.new')}</span> : <span>{t('common.edit')}</span>}
<span>{t('domains._')}</span>
</>
)
}
function modalInputNumbers({ value, label, path }: { value: number, label: string, path: string }, { emit }: SetupContext<Events>) {
return (
<NFormItem label={t(label)} path={path}>
<NInputNumber value={value} onUpdate:value={v => emit('update:value', v)} showButton={false}>
{{
suffix: () => t('common.unitForSecond')
}}
</NInputNumber>
</NFormItem>
)
}
function modalBody({ domain }: Props) {
return (
<>
<NForm model={domain} rules={rules}>
<NFormItem label={t('domains._')} path='domain_name'>
<NInput placeholder='example.com' value={domain.domain_name} onUpdate:value={v => domain.domain_name = v} onInput={v => easyInput(v, domain)} />
</NFormItem>
<NFormItem label={t('domains.form.mainDNS')} path='main_dns'>
<NInput placeholder="ns1.example.com" value={domain.main_dns} onUpdate:value={v => domain.main_dns = v} />
</NFormItem>
<NFormItem label={t('domains.form.adminMail')} path='admin_email'>
<NInput placeholder="admin@example.com" value={domain.admin_email} onUpdate:value={v => domain.admin_email = v} inputProps={{ type: 'email' }} />
</NFormItem>
</NForm>
<NForm model={domain} rules={rules} inline>
<modalInputNumbers value={domain.refresh_interval} onUpdate:value={(v: number) => domain.refresh_interval = v} path='refresh_interval' label='records.refresh' />
<modalInputNumbers value={domain.retry_interval} onUpdate:value={(v: number) => domain.retry_interval = v} path='retry_interval' label='records.retry' />
<modalInputNumbers value={domain.expiry_period} onUpdate:value={(v: number) => domain.expiry_period = v} path='expiry_period' label='records.expire' />
<modalInputNumbers value={domain.negative_ttl} onUpdate:value={(v: number) => domain.negative_ttl = v} path='negative_ttl' label='records.ttl' />
</NForm>
</>
)
}
function modalActions({ domain }: Props, { emit }: SetupContext<Events>) {
return (
<NFlex justify='end'>
<NButton size='small' onClick={() => emit("update:show", false)} >
{{
default: () => t('common.cancel'),
icon: () => <NIcon><Times /></NIcon>
}}
</NButton>
<NButton size='small' type='primary' disabled={invalidData.value !== allFlags} loading={loading.value} onClick={() => confirm(domain).then(() => emit('update:show', false))} attrType='submit'>
{{
default: () => t('common.confirm'),
icon: () => <NIcon><Check /></NIcon>
}}
</NButton>
</NFlex>
)
}
function DomainEditModal({ domain, show }: Props, { emit }: SetupContext<Events>) {
return (
<NModal maskClosable={false} show={show}>
<NCard style={{ width: '640px' }} role='dialog'>
{{
headler: () => <modalHeader domain={domain} />,
default: () => <modalBody domain={domain} />,
action: () => <modalActions domain={domain} onUpdate:show={(v: boolean) => { emit("update:show", v) }} />
}}
</NCard>
</NModal>
)
}
export default DomainEditModal

View File

@ -1,217 +0,0 @@
<template>
<NModal :mask-closable="false" :show="show">
<NCard style="width: 640px" role="dialog" aria-modal="true">
<template #header>
<span v-if="!domain || !domain.id || domain.id < 1">{{ t('common.new') }}</span><span v-else>{{
t('common.edit')
}}</span><span>{{
t('domains._') }}</span>
</template>
<template #header-extra></template>
<NForm :model="domain" :rules="rules">
<NFormItem :label="t('domains._')" path="domain_name">
<NInput placeholder="example.com" v-model:value="domain.domain_name" @input="easyInput"
@keydown.enter.prevent />
</NFormItem>
<NFormItem :label="t('domains.form.mainDNS')" path="main_dns">
<NInput placeholder="ns1.example.com" v-model:value="domain.main_dns" @keydown.enter.prevent />
</NFormItem>
<NFormItem :label="t('domains.form.adminMail')" path="admin_email">
<NInput placeholder="admin@example.com" v-model:value="domain.admin_email" @keydown.enter.prevent />
</NFormItem>
</NForm>
<NForm :model="domain" :rules="rules" inline>
<NFormItem :label="t('records.refresh')" path="refresh_interval">
<NInputNumber v-model:value="domain.refresh_interval" :show-button="false">
<template #suffix>
{{ 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('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('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('common.unitForSecond') }}
</template>
</NInputNumber>
</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" :disabled="invalidData !== allFlags" :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 { getErrorInfo } from '@/apis/api';
import { useDomainStore, type Domain } from '@/stores/domains';
import { Check, Times } from '@vicons/fa';
import {
NModal,
NCard,
NForm,
NFormItem,
NFlex,
NIcon,
NButton,
NInput,
NInputNumber,
useNotification,
type FormRules,
type FormItemRule
} from 'naive-ui'
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
const enum validFlags {
domainNameValid = 1,
mainNsValid = domainNameValid << 1,
adminEmailValid = mainNsValid << 1
}
const allFlags = validFlags.adminEmailValid | validFlags.mainNsValid | validFlags.domainNameValid
const { t } = useI18n()
const props = defineProps<{
domain: Domain
}>()
const show = defineModel<boolean>('show', { default: false })
const loading = ref(false)
const invalidData = ref(0)
const rules = {
domain_name: [{
required: true,
trigger: 'blur',
validator: (_rule: FormItemRule, value: string) => {
return validate(
value,
/^([\w-]+\.)+[\w-]+$/,
'domains.errors.domainName',
validFlags.domainNameValid
)
}
}],
main_dns: [{
required: true,
trigger: 'blur',
validator: (_rule: FormItemRule, value: string) => {
return validate(
value,
/^([\w-]+\.)+[\w-]+$/,
'domains.errors.domainName',
validFlags.mainNsValid,
)
}
}],
admin_email: [{
required: true,
trigger: 'blur',
validator: (_rule: FormItemRule, value: string) => {
return validate(
value,
/^[\w-.]+@([\w-]+\.)+[\w-]+$/,
'domains.errors.mail',
validFlags.adminEmailValid
)
}
}],
refresh_interval: [{
required: true,
trigger: 'blur',
type: 'number',
}],
retry_interval: [{
required: true,
trigger: 'blur',
type: 'number',
}],
expiry_period: [{
required: true,
trigger: 'blur',
type: 'number',
}],
negative_ttl: [{
required: true,
trigger: 'blur',
type: 'number'
}]
} as FormRules
const domainStore = useDomainStore()
const notification = useNotification()
async function confirm() {
loading.value = true;
try {
if (!props.domain.id || props.domain.id < 1) {
await domainStore.addDomain(props.domain)
} else {
await domainStore.updateDomain(props.domain)
}
show.value = false
} catch (e) {
const msg = getErrorInfo(e)
notification.error(msg)
console.error(e)
}
loading.value = false
}
function validate(value: string, reg: RegExp, msg: string, flag: validFlags): Promise<void> {
return new Promise<void>((resolve, reject) => {
if (!value) {
invalidData.value &= ~flag
reject(Error(t('common.mandatory')))
} else if (!reg.test(value)) {
invalidData.value &= ~flag
reject(Error(t(msg)))
} else {
invalidData.value |= flag
resolve()
}
})
}
function easyInput(domain: string) {
props.domain.admin_email = `admin@${domain}`
props.domain.main_dns = `ns1.${domain}`
}
</script>

View File

@ -4,7 +4,7 @@ import { type Domain } from "../../stores/domains"
import router from '@/router'; import router from '@/router';
import i18n from '@/locale/i18n' import i18n from '@/locale/i18n'
import type { SetupContext } from 'vue'; import type { SetupContext } from 'vue';
const t = i18n.global.t const { t } = i18n.global
type Props = { type Props = {
domain: Domain domain: Domain

View File

@ -1,15 +1,15 @@
import './DomainRemoveModal.css' import './DomainRemoveModal.css'
import { useDomainStore, type Domain } from '@/stores/domains'; import { useDomainStore, type Domain } from '@/stores/domains';
import { NModal, NCard, NFlex, NButton, NIcon, NInput, useNotification } from 'naive-ui' import { NModal, NCard, NFlex, NButton, NIcon, NInput, createDiscreteApi } from 'naive-ui'
import { Times, TrashAlt, QuestionCircle } from '@vicons/fa'; import { Times, TrashAlt, QuestionCircle } from '@vicons/fa';
import { getErrorInfo } from '@/apis/api'; import { getErrorInfo } from '@/apis/api';
import i18n from '@/locale/i18n'; import i18n from '@/locale/i18n';
import type { EmitsOptions, ObjectEmitsOptions, SetupContext } from 'vue'; import { ref, type EmitsOptions, type ObjectEmitsOptions, type SetupContext } from 'vue';
const t = i18n.global.t const t = i18n.global.t
const domainStore = useDomainStore() const domainStore = useDomainStore()
const notification = useNotification() const { notification } = createDiscreteApi(['notification'])
type Props = { type Props = {
domain: Domain domain: Domain
@ -20,12 +20,12 @@ type Events = {
'update:show': (value: boolean) => void 'update:show': (value: boolean) => void
} }
let domain_name = '' const domain_name = ref('')
let loading = false const loading = ref(false)
async function confirm(domain: Domain) { async function confirm(domain: Domain) {
domain_name = '' domain_name.value = ''
loading = true loading.value = true
try { try {
if (domain) if (domain)
@ -35,7 +35,7 @@ async function confirm(domain: Domain) {
notification.error(msg) notification.error(msg)
console.error(e) console.error(e)
} finally { } finally {
loading = false loading.value = false
} }
} }
@ -47,7 +47,7 @@ function modalBody({ domain }: Props) {
<p>{t('domains.confirm1')} <b id="boldit">{domain.domain_name}</b> {t('domains.confirm2')}</p> <p>{t('domains.confirm1')} <b id="boldit">{domain.domain_name}</b> {t('domains.confirm2')}</p>
<br /> <br />
<p> <p>
<NInput onUpdate:value={(v) => domain_name = v} placeholder={domain.domain_name} /> <NInput onUpdate:value={(v) => domain_name.value = v} placeholder={domain.domain_name} />
</p> </p>
</> </>
) )
@ -63,7 +63,7 @@ function modalActions({ domain }: Props, { emit }: SetupContext<Events>) {
}} }}
</NButton> </NButton>
<NButton size='small' type='error' disabled={domain_name !== domain.domain_name} attrType='submit' loading={loading} onClick={() => confirm(domain)}> <NButton size='small' type='error' disabled={domain_name.value !== domain.domain_name} attrType='submit' loading={loading.value} onClick={() => confirm(domain).then(() => emit('update:show', false))}>
{{ {{
icon: () => <NIcon><TrashAlt /></NIcon>, icon: () => <NIcon><TrashAlt /></NIcon>,
default: () => t('common.confirm') default: () => t('common.confirm')

View File

@ -0,0 +1,47 @@
import { NButton, NButtonGroup, NTooltip, NIcon, NPopconfirm, NFlex } from 'naive-ui'
import { TrashAlt, EditRegular } from '@vicons/fa'
import type { Record } from '@/stores/records'
import i18n from '@/locale/i18n'
import type { SetupContext } from 'vue'
const { t } = i18n.global
type Props = {
record: Record
domain: string
}
type Events = {
recordDelete(domain: string, record: Record): void
editRecord(domain: string, record: Record): void
}
function RecordOps({ record, domain }: Props, { emit }: SetupContext<Events>) {
return (
<NFlex justify='end'>
<NButtonGroup>
<NTooltip>
{{
trigger: () => <NButton size='tiny' onClick={() => emit('editRecord', domain, record)}>
{{
icon: () => <NIcon component={EditRegular} />
}}
</NButton>,
default: () => t("common.edit")
}}
</NTooltip>
<NPopconfirm onPositiveClick={() => emit('recordDelete', domain, record)}>
{{
trigger: () => <NButton type='error' size='tiny'>
{{
icon: () => <NIcon component={TrashAlt} />
}}
</NButton>,
default: () => t("common.deleteConfirm")
}}
</NPopconfirm>
</NButtonGroup>
</NFlex>
)
}
export default RecordOps

View File

@ -1,51 +0,0 @@
<template>
<NFlex justify="end">
<NButtonGroup>
<NTooltip>
<template #trigger>
<NButton size="tiny" @click="edit">
<template #icon>
<NIcon :component="EditRegular" />
</template>
</NButton>
</template>
{{ $t("common.edit") }}
</NTooltip>
<NPopconfirm @positive-click="confirm">
<template #trigger>
<NButton type="error" size="tiny">
<template #icon>
<NIcon :component="TrashAlt" />
</template>
</NButton>
</template>
{{ $t("common.deleteConfirm") }}
</NPopconfirm>
</NButtonGroup>
</NFlex>
</template>
<script setup lang="ts">
import { NButton, NButtonGroup, NTooltip, NIcon, NPopconfirm, NFlex, useNotification } from 'naive-ui'
import { TrashAlt, EditRegular } from '@vicons/fa'
import { useI18n } from 'vue-i18n';
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', 'edit-record'])
function confirm() {
emit('record-delete', props.domain, props.record)
}
function edit() {
emit('edit-record', props.domain, props.record)
}
</script>

View File

@ -7,7 +7,7 @@ import { getErrorInfo } from '@/apis/api'
import DomainInfo from '@/components/domains/DomainInfo' import DomainInfo from '@/components/domains/DomainInfo'
import DomainOps from '@/components/domains/DomainOps' import DomainOps from '@/components/domains/DomainOps'
import DomainRemoveModal from '@/components/domains/DomainRemoveModal' import DomainRemoveModal from '@/components/domains/DomainRemoveModal'
import DomainEditModal from '@/components/domains/DomainEditModal.vue' import DomainEditModal from '@/components/domains/DomainEditModal'
const domainStore = useDomainStore() const domainStore = useDomainStore()
const notification = useNotification() const notification = useNotification()

View File

@ -11,7 +11,7 @@ import { useRecordStore, type Record, type SOARecord, RecordTypes } from '@/stor
import { getErrorInfo } from '@/apis/api' import { getErrorInfo } from '@/apis/api'
import { PlusSquare, RedoAlt, CheckCircle, Clock, Cogs, Search } from '@vicons/fa' import { PlusSquare, RedoAlt, CheckCircle, Clock, Cogs, Search } from '@vicons/fa'
import router from '@/router'; import router from '@/router';
import RecordOps from '@/components/records/RecordOps.vue' import RecordOps from '@/components/records/RecordOps'
import RecordEditModal from '@/components/records/RecordEditModal.vue' import RecordEditModal from '@/components/records/RecordEditModal.vue'
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
const { t } = useI18n() const { t } = useI18n()
@ -131,7 +131,8 @@ function newRecord() {
<template> <template>
<div id="records"> <div id="records">
<RecordEditModal v-model:show="editModalShow" :domain="domain" :record="editingRecord" @reload-records="reloadRecords"/> <RecordEditModal v-model:show="editModalShow" :domain="domain" :record="editingRecord"
@reload-records="reloadRecords" />
<NSpin size="large" v-if="loading" /> <NSpin size="large" v-if="loading" />
<div v-else class="content"> <div v-else class="content">
<NModalProvider> <NModalProvider>