modal needed for edit
This commit is contained in:
parent
8c0b79066f
commit
69613f9b6e
49
web/package-lock.json
generated
49
web/package-lock.json
generated
@ -11,6 +11,7 @@
|
||||
"axios": "^1.6.8",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.4.21",
|
||||
"vue-i18n": "^9.11.0",
|
||||
"vue-router": "^4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -878,6 +879,38 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/core-base": {
|
||||
"version": "9.11.0",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.11.0.tgz",
|
||||
"integrity": "sha512-cveOqAstjLZIiyatcP/HrzrQ87cZI8ScPQna3yvoM8zjcjcIRK1MRvmxUNlPdg0rTNJMZw7rixPVM58O5aHVPA==",
|
||||
"dependencies": {
|
||||
"@intlify/message-compiler": "9.11.0",
|
||||
"@intlify/shared": "9.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/message-compiler": {
|
||||
"version": "9.11.0",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.11.0.tgz",
|
||||
"integrity": "sha512-x31Gl7cscnoI4UUY1yaIy8e7vVMVW1VVlTXZz4SIHKqoSEUkfmgqK8NAx1e7RcoHEbICR7uyCbud0ZL1s4OGXQ==",
|
||||
"dependencies": {
|
||||
"@intlify/shared": "9.11.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/@intlify/shared": {
|
||||
"version": "9.11.0",
|
||||
"resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.11.0.tgz",
|
||||
"integrity": "sha512-KHSNgi7sRjmSm7aD8QH8WFt9VfKaekJuJ473opbJlkGY3EDnDUU8ikIhG8PbasQbgNvbY3m3tWNGqk2omIdwMA==",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
|
||||
@ -2500,6 +2533,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue-i18n": {
|
||||
"version": "9.11.0",
|
||||
"resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.11.0.tgz",
|
||||
"integrity": "sha512-vU4gY6lu8Pdfs9BgKGiDAJmFDf88cceR47KcSB0VW4xJzUrXR/7qwqM7A8dQ2nedhoIDxoOm5Ro4pFd2KvJqbA==",
|
||||
"dependencies": {
|
||||
"@intlify/core-base": "9.11.0",
|
||||
"@intlify/shared": "9.11.0",
|
||||
"@vue/devtools-api": "^6.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.3.0.tgz",
|
||||
|
@ -14,6 +14,7 @@
|
||||
"axios": "^1.6.8",
|
||||
"pinia": "^2.1.7",
|
||||
"vue": "^3.4.21",
|
||||
"vue-i18n": "^9.11.0",
|
||||
"vue-router": "^4.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -8,20 +8,27 @@ import {
|
||||
lightTheme,
|
||||
type GlobalTheme
|
||||
} from "naive-ui";
|
||||
import { zhCN, dateZhCN, enUS, dateEnUS, type NLocale, type NDateLocale } from 'naive-ui'
|
||||
import { RouterView } from "vue-router";
|
||||
import { onMounted } from "vue";
|
||||
|
||||
const osThemeRef = useOsTheme()
|
||||
const theme = defineModel<GlobalTheme>()
|
||||
const theme = defineModel<GlobalTheme>('theme')
|
||||
theme.value = osThemeRef.value === 'dark' ? darkTheme : lightTheme
|
||||
|
||||
const locale = defineModel<NLocale>('locale')
|
||||
locale.value = navigator.language === "zh-CN" ? zhCN : enUS
|
||||
|
||||
const dateLocale = defineModel<NDateLocale>('dateLocale')
|
||||
dateLocale.value = navigator.language === "zh-CN" ? dateZhCN : dateEnUS
|
||||
|
||||
onMounted(() => {
|
||||
document.title = 'reCoreD-UI'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NConfigProvider :theme="theme">
|
||||
<NConfigProvider :theme="theme" :locale="locale" :date-locale="dateLocale">
|
||||
<NGlobalStyle />
|
||||
<NNotificationProvider :max="3">
|
||||
<RouterView />
|
||||
|
@ -2,6 +2,8 @@ import axios, { type AxiosInstance, type AxiosRequestConfig, type AxiosResponse,
|
||||
import { type Record } from '@/stores/records';
|
||||
import { type Domain } from "@/stores/domains";
|
||||
|
||||
import i18n from "@/locale/i18n";
|
||||
|
||||
type Result<T> = {
|
||||
success: boolean
|
||||
message: string
|
||||
@ -13,31 +15,30 @@ const notificationDuration = 5000
|
||||
const messages = new Map<number, {
|
||||
title: string, content: string, duration: number
|
||||
}>(
|
||||
// TODO: i18n
|
||||
[
|
||||
[400, {
|
||||
title: "请求错误 (400)",
|
||||
content: "参数提交错误",
|
||||
title: i18n.global.t("api.error400.title"),
|
||||
content: i18n.global.t("api.error400.content"),
|
||||
duration: notificationDuration
|
||||
}],
|
||||
[401, {
|
||||
title: "未授权 (401)",
|
||||
content: "请刷新页面重新登录",
|
||||
title: i18n.global.t("api.error401.title"),
|
||||
content: i18n.global.t("api.error401.content"),
|
||||
duration: notificationDuration
|
||||
}],
|
||||
[403, {
|
||||
title: "拒绝访问 (403)",
|
||||
content: "你没有权限!",
|
||||
title: i18n.global.t("api.error403.title"),
|
||||
content: i18n.global.t("api.error403.content"),
|
||||
duration: notificationDuration
|
||||
}],
|
||||
[404, {
|
||||
title: "查无此项 (404)",
|
||||
content: "没有该项内容",
|
||||
title: i18n.global.t("api.error404.title"),
|
||||
content: i18n.global.t("api.error404.content"),
|
||||
duration: notificationDuration
|
||||
}],
|
||||
[500, {
|
||||
title: "服务器错误 (500)",
|
||||
content: "请检查系统日志",
|
||||
title: i18n.global.t("api.error500.title"),
|
||||
content: i18n.global.t("api.error500.content"),
|
||||
duration: notificationDuration
|
||||
}]
|
||||
]
|
||||
@ -46,8 +47,8 @@ const messages = new Map<number, {
|
||||
export function getErrorInfo(err: any) {
|
||||
const msg = messages.get(err.response.status)
|
||||
return msg? msg: {
|
||||
title: "未知错误",
|
||||
content: "请打开控制台了解详情",
|
||||
title: i18n.global.t("api.errorUnknown.title"),
|
||||
content: i18n.global.t("api.errorUnknown.content"),
|
||||
duration: notificationDuration
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
font-weight: normal;
|
||||
display: block;
|
||||
}
|
||||
|
||||
a,
|
||||
@ -29,7 +30,6 @@ a,
|
||||
|
||||
#app {
|
||||
display: flex;
|
||||
grid-template-columns: 1fr;
|
||||
padding: 2rem 2rem;
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
msg: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="greetings">
|
||||
<h1 class="green">{{ msg }}</h1>
|
||||
<h3>
|
||||
You’ve successfully created a project with
|
||||
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
|
||||
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. What's next?
|
||||
</h3>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h1 {
|
||||
font-weight: 500;
|
||||
font-size: 2.6rem;
|
||||
position: relative;
|
||||
top: -10px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.greetings h1,
|
||||
.greetings h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.greetings h1,
|
||||
.greetings h3 {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,88 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import WelcomeItem from './WelcomeItem.vue'
|
||||
import DocumentationIcon from './icons/IconDocumentation.vue'
|
||||
import ToolingIcon from './icons/IconTooling.vue'
|
||||
import EcosystemIcon from './icons/IconEcosystem.vue'
|
||||
import CommunityIcon from './icons/IconCommunity.vue'
|
||||
import SupportIcon from './icons/IconSupport.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<DocumentationIcon />
|
||||
</template>
|
||||
<template #heading>Documentation</template>
|
||||
|
||||
Vue’s
|
||||
<a href="https://vuejs.org/" target="_blank" rel="noopener">official documentation</a>
|
||||
provides you with all information you need to get started.
|
||||
</WelcomeItem>
|
||||
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<ToolingIcon />
|
||||
</template>
|
||||
<template #heading>Tooling</template>
|
||||
|
||||
This project is served and bundled with
|
||||
<a href="https://vitejs.dev/guide/features.html" target="_blank" rel="noopener">Vite</a>. The
|
||||
recommended IDE setup is
|
||||
<a href="https://code.visualstudio.com/" target="_blank" rel="noopener">VSCode</a> +
|
||||
<a href="https://github.com/johnsoncodehk/volar" target="_blank" rel="noopener">Volar</a>. If
|
||||
you need to test your components and web pages, check out
|
||||
<a href="https://www.cypress.io/" target="_blank" rel="noopener">Cypress</a> and
|
||||
<a href="https://on.cypress.io/component" target="_blank" rel="noopener"
|
||||
>Cypress Component Testing</a
|
||||
>.
|
||||
|
||||
<br />
|
||||
|
||||
More instructions are available in <code>README.md</code>.
|
||||
</WelcomeItem>
|
||||
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<EcosystemIcon />
|
||||
</template>
|
||||
<template #heading>Ecosystem</template>
|
||||
|
||||
Get official tools and libraries for your project:
|
||||
<a href="https://pinia.vuejs.org/" target="_blank" rel="noopener">Pinia</a>,
|
||||
<a href="https://router.vuejs.org/" target="_blank" rel="noopener">Vue Router</a>,
|
||||
<a href="https://test-utils.vuejs.org/" target="_blank" rel="noopener">Vue Test Utils</a>, and
|
||||
<a href="https://github.com/vuejs/devtools" target="_blank" rel="noopener">Vue Dev Tools</a>. If
|
||||
you need more resources, we suggest paying
|
||||
<a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">Awesome Vue</a>
|
||||
a visit.
|
||||
</WelcomeItem>
|
||||
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<CommunityIcon />
|
||||
</template>
|
||||
<template #heading>Community</template>
|
||||
|
||||
Got stuck? Ask your question on
|
||||
<a href="https://chat.vuejs.org" target="_blank" rel="noopener">Vue Land</a>, our official
|
||||
Discord server, or
|
||||
<a href="https://stackoverflow.com/questions/tagged/vue.js" target="_blank" rel="noopener"
|
||||
>StackOverflow</a
|
||||
>. You should also subscribe to
|
||||
<a href="https://news.vuejs.org" target="_blank" rel="noopener">our mailing list</a> and follow
|
||||
the official
|
||||
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener">@vuejs</a>
|
||||
twitter account for latest news in the Vue world.
|
||||
</WelcomeItem>
|
||||
|
||||
<WelcomeItem>
|
||||
<template #icon>
|
||||
<SupportIcon />
|
||||
</template>
|
||||
<template #heading>Support Vue</template>
|
||||
|
||||
As an independent project, Vue relies on community backing for its sustainability. You can help
|
||||
us by
|
||||
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>.
|
||||
</WelcomeItem>
|
||||
</template>
|
@ -1,87 +0,0 @@
|
||||
<template>
|
||||
<div class="item">
|
||||
<i>
|
||||
<slot name="icon"></slot>
|
||||
</i>
|
||||
<div class="details">
|
||||
<h3>
|
||||
<slot name="heading"></slot>
|
||||
</h3>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.item {
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.details {
|
||||
flex: 1;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
i {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
place-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.4rem;
|
||||
color: var(--color-heading);
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.item {
|
||||
margin-top: 0;
|
||||
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
i {
|
||||
top: calc(50% - 25px);
|
||||
left: -26px;
|
||||
position: absolute;
|
||||
border: 1px solid var(--color-border);
|
||||
background: var(--color-background);
|
||||
border-radius: 8px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.item:before {
|
||||
content: ' ';
|
||||
border-left: 1px solid var(--color-border);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: calc(50% + 25px);
|
||||
height: calc(50% - 25px);
|
||||
}
|
||||
|
||||
.item:after {
|
||||
content: ' ';
|
||||
border-left: 1px solid var(--color-border);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: calc(50% + 25px);
|
||||
height: calc(50% - 25px);
|
||||
}
|
||||
|
||||
.item:first-of-type:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.item:last-of-type:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
37
web/src/components/records/RecordOps.vue
Normal file
37
web/src/components/records/RecordOps.vue
Normal file
@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<NFlex justify="end">
|
||||
<NButtonGroup>
|
||||
<NTooltip>
|
||||
<template #trigger>
|
||||
<NButton size="tiny">
|
||||
<template #icon>
|
||||
<NIcon :component="EditRegular" />
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
{{ $t("common.edit") }}
|
||||
</NTooltip>
|
||||
<NPopconfirm>
|
||||
<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 } from 'naive-ui'
|
||||
import { TrashAlt, EditRegular } from '@vicons/fa'
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import type { Record } from '@/stores/records';
|
||||
const { t } = useI18n()
|
||||
const props = defineProps<{
|
||||
record: Record
|
||||
}>();
|
||||
</script>
|
51
web/src/locale/en-US.ts
Normal file
51
web/src/locale/en-US.ts
Normal file
@ -0,0 +1,51 @@
|
||||
export default {
|
||||
common: {
|
||||
delete: 'Remove',
|
||||
remove: 'Remove',
|
||||
deleteConfirm: 'Are you sure?',
|
||||
removeConfirm: 'Are you sure?',
|
||||
edit: 'Edit',
|
||||
add: 'New',
|
||||
new: 'New',
|
||||
},
|
||||
api: {
|
||||
error400: {
|
||||
title: 'Bad Request (400)',
|
||||
content: 'Bad Parameters'
|
||||
},
|
||||
error401: {
|
||||
title: 'Unauthorized (401)',
|
||||
content: 'Refresh page and relogin'
|
||||
},
|
||||
error403: {
|
||||
title: 'Forbbiden (403)',
|
||||
content: 'Permission denied'
|
||||
},
|
||||
error404: {
|
||||
title: 'Not Found (404)',
|
||||
content: 'No such content'
|
||||
},
|
||||
error500: {
|
||||
title: "Internal Server Error (500)",
|
||||
content: "Check server log, please"
|
||||
},
|
||||
errorUnknown: {
|
||||
title: "Unknown Error",
|
||||
content: "Open console for details",
|
||||
}
|
||||
},
|
||||
domains: {
|
||||
dnsRecord: 'DNS Record'
|
||||
},
|
||||
records: {
|
||||
name: 'Record Name',
|
||||
recordType: 'Type',
|
||||
content: 'Record',
|
||||
search: 'Search...',
|
||||
|
||||
refresh: 'Refresh Interval',
|
||||
retry: 'Retry Interval',
|
||||
expire: 'Expiry Period',
|
||||
ttl: 'Negative TTL',
|
||||
}
|
||||
}
|
23
web/src/locale/i18n.ts
Normal file
23
web/src/locale/i18n.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { createI18n } from "vue-i18n";
|
||||
import zhCN from "./zh-CN";
|
||||
import enUS from "./en-US";
|
||||
|
||||
export default createI18n({
|
||||
locale: navigator.language,
|
||||
legacy: false,
|
||||
messages: {
|
||||
zh: {
|
||||
...zhCN
|
||||
},
|
||||
'zh-CN': {
|
||||
...zhCN
|
||||
},
|
||||
|
||||
en: {
|
||||
...enUS
|
||||
},
|
||||
'en-US': {
|
||||
...enUS
|
||||
}
|
||||
}
|
||||
})
|
51
web/src/locale/zh-CN.ts
Normal file
51
web/src/locale/zh-CN.ts
Normal file
@ -0,0 +1,51 @@
|
||||
export default {
|
||||
common: {
|
||||
delete: '删除',
|
||||
remove: '删除',
|
||||
deleteConfirm: '确定要删除吗?',
|
||||
removeConfirm: '确定要删除吗?',
|
||||
edit: '修改',
|
||||
add: '新增',
|
||||
new: '新增',
|
||||
},
|
||||
api: {
|
||||
error400: {
|
||||
title: '请求错误 (400)',
|
||||
content: '参数提交错误'
|
||||
},
|
||||
error401: {
|
||||
title: '未授权 (401)',
|
||||
content: '请刷新页面重新登录'
|
||||
},
|
||||
error403: {
|
||||
title: '拒绝访问 (403)',
|
||||
content: '你没有权限!'
|
||||
},
|
||||
error404: {
|
||||
title: '查无此项 (404)',
|
||||
content: '没有该项内容'
|
||||
},
|
||||
error500: {
|
||||
title: "服务器错误 (500)",
|
||||
content: "请检查系统日志"
|
||||
},
|
||||
errorUnknown: {
|
||||
title: "未知错误",
|
||||
content: "请打开控制台了解详情",
|
||||
}
|
||||
},
|
||||
domains: {
|
||||
dnsRecord: 'DNS 记录'
|
||||
},
|
||||
records: {
|
||||
name: '记录名',
|
||||
recordType: '类型',
|
||||
content: '记录值',
|
||||
search: '搜索...',
|
||||
|
||||
refresh: '刷新时间',
|
||||
retry: '重试间隔',
|
||||
expire: '超期时间',
|
||||
ttl: '缓存时间',
|
||||
}
|
||||
}
|
@ -5,10 +5,12 @@ import { createPinia } from 'pinia'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import i18n from './locale/i18n'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
app.use(i18n)
|
||||
|
||||
app.mount('#app')
|
||||
|
@ -1,18 +1,58 @@
|
||||
<script setup lang="ts">
|
||||
<script setup lang="tsx">
|
||||
import { NSpin, NPageHeader, useNotification, NFlex, NButton, NIcon, NGrid, NGi, NStatistic, NDataTable, NInput } from 'naive-ui'
|
||||
import { onMounted } from 'vue'
|
||||
import type { DataTableColumns, DataTableFilterState } from 'naive-ui'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useRecordStore, type Record, type SOARecord, RecordTypes } from '@/stores/records'
|
||||
import { getErrorInfo } from '@/apis/api'
|
||||
import { PlusSquare, RedoAlt, CheckCircle, Clock, Cogs, Search } from '@vicons/fa'
|
||||
import router from '@/router';
|
||||
import RecordOps from '@/components/records/RecordOps.vue'
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps<{
|
||||
domain: string
|
||||
}>()
|
||||
const loading = defineModel<boolean>('loading', { default: true });
|
||||
const records = defineModel<Record[]>('records');
|
||||
const search = defineModel<string>('search', { default: '' })
|
||||
const soa = defineModel<SOARecord | undefined>('soa')
|
||||
const columns = defineModel<DataTableColumns<Record>>('columns')
|
||||
const table = ref<any>(null)
|
||||
|
||||
columns.value = [
|
||||
{
|
||||
key: 'no',
|
||||
title: '#',
|
||||
render(_, index) {
|
||||
return index + 1
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
title: t("records.name"),
|
||||
},
|
||||
{
|
||||
key: 'record_type',
|
||||
title: t('records.recordType')
|
||||
},
|
||||
{
|
||||
key: 'content',
|
||||
title: t('records.content'),
|
||||
render(row: Record) {
|
||||
return Object.entries(row.content).map(i => i[1]).join(" ")
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'ttl',
|
||||
title: 'TTL (s)'
|
||||
},
|
||||
{
|
||||
key: '',
|
||||
render(row: Record) {
|
||||
return <RecordOps record={ row } />
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const recordStore = useRecordStore()
|
||||
const notification = useNotification()
|
||||
@ -27,95 +67,107 @@ onMounted(() => {
|
||||
|
||||
function refreshRecords() {
|
||||
recordStore.loadRecords(props.domain)
|
||||
records.value = recordStore.records
|
||||
soa.value = records.value?.find(e => e.record_type === RecordTypes.RecordTypeSOA)?.content as SOARecord
|
||||
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;
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
router.push('/domains')
|
||||
}
|
||||
|
||||
function searchRecord(value: string) {
|
||||
if (value.length > 0) {
|
||||
records.value = recordStore.records?.
|
||||
filter(e => e.record_type !== RecordTypes.RecordTypeSOA).
|
||||
filter(e => !!~e.name.indexOf(value))
|
||||
} else {
|
||||
records.value = recordStore.records?.
|
||||
filter(e => e.record_type !== RecordTypes.RecordTypeSOA)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="records">
|
||||
<NSpin size="large" v-if="loading" />
|
||||
<NPageHeader v-else title="DNS 记录" :subtitle="domain" @back="goBack">
|
||||
<template #extra>
|
||||
<NFlex :wrap="false" justify="end" inline>
|
||||
<NButton type="primary">
|
||||
<template #icon>
|
||||
<NIcon>
|
||||
<PlusSquare />
|
||||
</NIcon>
|
||||
</template>
|
||||
新增
|
||||
</NButton>
|
||||
<NInput v-model:value="search" placeholder="搜索...">
|
||||
<template #prefix>
|
||||
<NIcon :component="Search" />
|
||||
</template>
|
||||
</NInput>
|
||||
</NFlex>
|
||||
</template>
|
||||
<NGrid :cols="4">
|
||||
<NGi>
|
||||
<NStatistic :value="soa?.refresh">
|
||||
<template #suffix>
|
||||
s
|
||||
</template>
|
||||
<template #label>
|
||||
<NIcon class="icon">
|
||||
<RedoAlt />
|
||||
</NIcon>
|
||||
刷新时间
|
||||
</template>
|
||||
</NStatistic>
|
||||
</NGi>
|
||||
<NGi>
|
||||
<NStatistic :value="soa?.retry">
|
||||
<template #suffix>
|
||||
s
|
||||
</template>
|
||||
<template #label>
|
||||
<NIcon class="icon">
|
||||
<CheckCircle />
|
||||
</NIcon>
|
||||
重试间隔
|
||||
</template>
|
||||
</NStatistic>
|
||||
</NGi>
|
||||
<NGi>
|
||||
<NStatistic :value="soa?.expire">
|
||||
<template #suffix>
|
||||
s
|
||||
</template>
|
||||
<template #label>
|
||||
<NIcon class="icon">
|
||||
<Clock />
|
||||
</NIcon>
|
||||
超期时间
|
||||
</template>
|
||||
</NStatistic>
|
||||
</NGi>
|
||||
<NGi>
|
||||
<NStatistic :value="soa?.minttl">
|
||||
<template #suffix>
|
||||
s
|
||||
</template>
|
||||
<template #label>
|
||||
<NIcon class="icon">
|
||||
<Cogs />
|
||||
</NIcon>
|
||||
缓存时间
|
||||
</template>
|
||||
</NStatistic>
|
||||
</NGi>
|
||||
</NGrid>
|
||||
</NPageHeader>
|
||||
<NDataTable :data="records">
|
||||
|
||||
</NDataTable>
|
||||
<div v-else class="content">
|
||||
<NPageHeader :title="t('domains.dnsRecord')" :subtitle="domain" @back="goBack">
|
||||
<template #extra>
|
||||
<NFlex :wrap="false" justify="end" inline>
|
||||
<NButton type="primary">
|
||||
<template #icon>
|
||||
<NIcon>
|
||||
<PlusSquare />
|
||||
</NIcon>
|
||||
</template>
|
||||
{{ t('common.add') }}
|
||||
</NButton>
|
||||
<NInput :placeholder="t('records.search')" @update:value="searchRecord" clearable>
|
||||
<template #prefix>
|
||||
<NIcon :component="Search" />
|
||||
</template>
|
||||
</NInput>
|
||||
</NFlex>
|
||||
</template>
|
||||
<NGrid :cols="4">
|
||||
<NGi>
|
||||
<NStatistic :value="soa?.refresh">
|
||||
<template #suffix>
|
||||
s
|
||||
</template>
|
||||
<template #label>
|
||||
<NIcon class="icon">
|
||||
<RedoAlt />
|
||||
</NIcon>
|
||||
{{ t('records.refresh') }}
|
||||
</template>
|
||||
</NStatistic>
|
||||
</NGi>
|
||||
<NGi>
|
||||
<NStatistic :value="soa?.retry">
|
||||
<template #suffix>
|
||||
s
|
||||
</template>
|
||||
<template #label>
|
||||
<NIcon class="icon">
|
||||
<CheckCircle />
|
||||
</NIcon>
|
||||
{{ t('records.retry') }}
|
||||
</template>
|
||||
</NStatistic>
|
||||
</NGi>
|
||||
<NGi>
|
||||
<NStatistic :value="soa?.expire">
|
||||
<template #suffix>
|
||||
s
|
||||
</template>
|
||||
<template #label>
|
||||
<NIcon class="icon">
|
||||
<Clock />
|
||||
</NIcon>
|
||||
{{ t('records.expire') }}
|
||||
</template>
|
||||
</NStatistic>
|
||||
</NGi>
|
||||
<NGi>
|
||||
<NStatistic :value="soa?.minttl">
|
||||
<template #suffix>
|
||||
s
|
||||
</template>
|
||||
<template #label>
|
||||
<NIcon class="icon">
|
||||
<Cogs />
|
||||
</NIcon>
|
||||
{{ t('records.ttl') }}
|
||||
</template>
|
||||
</NStatistic>
|
||||
</NGi>
|
||||
</NGrid>
|
||||
</NPageHeader>
|
||||
<br />
|
||||
<NDataTable :data="records" :columns="columns" ref="table" :pagination="{ pageSize: 20 }" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -123,4 +175,12 @@ function goBack() {
|
||||
.icon {
|
||||
transform: translateY(2px);
|
||||
}
|
||||
|
||||
div#records {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue
Block a user