🎨 增加 Go 后端
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: "admin",
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
123
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div class="login-container flex items-center justify-center p-[20px]">
|
||||
<div class="login-box w-[960px] h-[560px] md:w-[100%] md:px-[50px] px-[80px] py-[30px]">
|
||||
<div class="flex items-center justify-center md:hidden">
|
||||
<img :src="LoginPic" alt="login-pic"/>
|
||||
</div>
|
||||
<div class="flex justify-center w-[360px] md:w-[100%] flex-col items-center space-y-8">
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-center items-center space-x-[10px]">
|
||||
<n-gradient-text :gradient="gradient" class="text-[24px] p-5">Nuxt Whois 后台管理</n-gradient-text>
|
||||
</div>
|
||||
</div>
|
||||
<n-form
|
||||
ref="formRef"
|
||||
:model="formModel"
|
||||
:rules="formRules"
|
||||
label-placement="left"
|
||||
size="large"
|
||||
class="w-[100%]"
|
||||
:show-require-mark="false"
|
||||
:show-label="false"
|
||||
>
|
||||
<n-form-item path="username">
|
||||
<n-input v-model:value="formModel.username" round placeholder="请输入用户名">
|
||||
<template #prefix>
|
||||
<Icon name="mdi:user-outline"/>
|
||||
</template>
|
||||
</n-input>
|
||||
</n-form-item>
|
||||
<n-form-item path="password">
|
||||
<n-input v-model:value="formModel.password" round placeholder="请输入密码">
|
||||
<template #prefix>
|
||||
<Icon name="material-symbols:lock-outline"/>
|
||||
</template>
|
||||
</n-input>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<div class="flex justify-between w-[100%] px-[2px]">
|
||||
<div class="flex-initial">
|
||||
<n-checkbox v-model:checked="autoLogin">自动登录</n-checkbox>
|
||||
</div>
|
||||
<div class="flex-initial order-last">
|
||||
<n-button text type="primary">忘记密码</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button type="primary" size="large" round @click="handleSubmitForm" :loading="submitLoading" block>登录
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
layout: "empty",
|
||||
})
|
||||
import LoginPic from "assets/images/login-pic.png";
|
||||
|
||||
const router = useRouter();
|
||||
const message = useMessage();
|
||||
|
||||
const autoLogin = ref(false);
|
||||
const submitLoading = ref(false);
|
||||
const formRef = ref(null);
|
||||
const formModel = reactive({
|
||||
username: "",
|
||||
password: "",
|
||||
});
|
||||
|
||||
const formRules = {
|
||||
username: {required: true, message: "请输入用户名", trigger: "blur"},
|
||||
password: {required: true, message: "请输入密码", trigger: "blur"},
|
||||
};
|
||||
|
||||
const gradient = {
|
||||
deg: 92.06,
|
||||
from: "#33c2ff 0%",
|
||||
// to: `${appStore.appTheme} 100%`,
|
||||
};
|
||||
|
||||
const handleSubmitForm = (e) => {
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.login-container {
|
||||
background: linear-gradient(-135deg, #d765cf, #495fd1);
|
||||
min-height: 100%;
|
||||
|
||||
.login-box {
|
||||
@apply shadow-md rounded-xl bg-white flex justify-between;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: "full",
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: "full",
|
||||
})
|
||||
const siteName = ref('');
|
||||
const siteNameAlt = ref('');
|
||||
|
||||
function saveSettings() {
|
||||
// 这里应该是保存设置的逻辑,例如调用API或者本地状态管理
|
||||
console.log('保存的网站名称:', siteName.value);
|
||||
}
|
||||
|
||||
const active = ref(false);
|
||||
const GnOptions = ref([
|
||||
{
|
||||
label: 'Whois',
|
||||
value: 'whois'
|
||||
}, {
|
||||
label: 'Dns',
|
||||
value: 'dns'
|
||||
}, {
|
||||
label: 'Domain',
|
||||
value: 'domain'
|
||||
}
|
||||
])
|
||||
const GnValue = ref('whois')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-gray-100 p-10">
|
||||
<div class=" bg-white rounded-lg shadow-md p-5">
|
||||
<h2 class="text-xl font-semibold mb-5">网站设置</h2>
|
||||
<div class="flex mb-6 -mx-2">
|
||||
<div class="flex-1 px-2">
|
||||
<label for="site-name" class="block mb-2 text-sm font-medium text-gray-900">网站Logo名称</label>
|
||||
<n-input id="site-name" v-model:value="siteName" placeholder="请输入网站名称"/>
|
||||
</div>
|
||||
<div class="flex-1 px-2">
|
||||
<label for="site-name-alt" class="block mb-2 text-sm font-medium text-gray-900">Logo右上角名称</label>
|
||||
<n-input id="site-name-alt" v-model:value="siteNameAlt" placeholder="请输入备用网站名称"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex mb-6 -mx-2">
|
||||
<div class="flex-1 px-2">
|
||||
<label for="site-name" class="block mb-2 text-sm font-medium text-gray-900">开启极简模式</label>
|
||||
<n-switch v-model:value="active"/>
|
||||
</div>
|
||||
<div class="flex-1 px-2">
|
||||
<label for="site-name-alt" class="block mb-2 text-sm font-medium text-gray-900">功能开启选择</label>
|
||||
<n-select v-model:value="GnValue" multiple :options="GnOptions"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<n-button type="primary" @click="saveSettings">保存设置</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,90 @@
|
||||
<script setup lang="ts">
|
||||
definePageMeta({
|
||||
layout: 'empty',
|
||||
})
|
||||
const localePath = useLocalePath()
|
||||
const {t} = useI18n()
|
||||
useHead({
|
||||
title: t('api.title'),
|
||||
meta: [
|
||||
{
|
||||
name: 'description',
|
||||
content: t('api.description')
|
||||
},{
|
||||
name: 'keywords',
|
||||
content: t('api.keywords')
|
||||
}
|
||||
]
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="max-w-4xl mx-auto py-8 px-4">
|
||||
<div class="overflow-hidden shadow-md rounded-lg">
|
||||
<div class="px-6 py-4 bg-red-500 text-white font-bold uppercase">
|
||||
<div class="flex items-center justify-between">
|
||||
<NuxtLink :to="localePath('/')" class="hover:text-white">
|
||||
<Icon name="ic:outline-home" class="h-6 w-6" /> <!-- 调整图标大小 -->
|
||||
</NuxtLink>
|
||||
<span>{{ t('api.h1') }}</span>
|
||||
<span></span> <!-- 占位符以保持标题居中 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-6">
|
||||
<div class="grid grid-cols-3 gap-4 mb-4">
|
||||
<div class="font-semibold">{{ t('api.url') }}</div>
|
||||
<div class="col-span-2">/api/whois</div>
|
||||
</div>
|
||||
<!-- <div class="grid grid-cols-3 gap-4 mb-4">-->
|
||||
<!-- <div class="font-semibold">累计调用</div>-->
|
||||
<!-- <div class="col-span-2"><span class="count">156</span> 次</div>-->
|
||||
<!-- </div>-->
|
||||
<div class="grid grid-cols-3 gap-4 mb-4">
|
||||
<div class="font-semibold">{{ t('api.method') }}</div>
|
||||
<div class="col-span-2">POST</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-4 mb-4">
|
||||
<div class="font-semibold">{{ t('api.params') }}</div>
|
||||
<div class="col-span-2">String</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-6 py-4 border-t border-gray-200">
|
||||
<div class="font-bold mb-2">{{ t('api.input') }}</div>
|
||||
<div class="grid grid-cols-3 gap-4 mb-4">
|
||||
<div class="font-semibold">{{ t('api.name') }}</div>
|
||||
<div class="font-semibold">{{ t('api.type') }}</div>
|
||||
<div class="font-semibold">{{ t('api.desc') }}</div>
|
||||
</div>
|
||||
<!-- 参数列表 -->
|
||||
<div class="grid grid-cols-3 gap-4 mb-4">
|
||||
<div>domain</div>
|
||||
<div>string</div>
|
||||
<div>({{ t('api.required') }}) {{ t('api.domain') }}</div>
|
||||
</div>
|
||||
<!-- <div class="grid grid-cols-3 gap-4 mb-4">-->
|
||||
<!-- <div>whois</div>-->
|
||||
<!-- <div>string</div>-->
|
||||
<!-- <div>(必选) 域名的whois信息</div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="grid grid-cols-3 gap-4">-->
|
||||
<!-- <div>lang</div>-->
|
||||
<!-- <div>string</div>-->
|
||||
<!-- <div>(可选) 语言代码 ISO 639-1,例: "zh"、"en"</div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="grid grid-cols-3 gap-4 mb-4">-->
|
||||
<!-- <div>time_zone</div>-->
|
||||
<!-- <div>string</div>-->
|
||||
<!-- <div>(可选) 时区,例: "8"、"-3"</div>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
<div class="px-6 py-4 border-t border-gray-200">
|
||||
<div class="font-bold">{{ t('api.output') }}</div>
|
||||
<div class="mt-2">{{ t('api.whois') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,109 @@
|
||||
<script setup lang="ts">
|
||||
import {AdjustTimeToUTCOffset} from "~/utils/utc";
|
||||
|
||||
definePageMeta({
|
||||
stylePage: true,
|
||||
})
|
||||
|
||||
const {t} = useI18n()
|
||||
const localePath = useLocalePath()
|
||||
|
||||
const route = useRoute();
|
||||
const {domain}: any = route.params;
|
||||
const domainData = typeof domain === "string" ? domain?.replace(/_/g, '.') : "";
|
||||
|
||||
|
||||
const styleStore = useStyleStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const configStore = useConfigStore()
|
||||
const {currentServer} = storeToRefs(configStore)
|
||||
|
||||
const {data, pending, error, refresh} = await useAsyncData(
|
||||
'dns',
|
||||
() => $fetch('/api/dns', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
domain: domainData,
|
||||
serverName: currentServer.value.dns,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
if (!error.value && settingsStore.getIsHistory) {
|
||||
styleStore.addOrUpdateHistory(
|
||||
{
|
||||
id: domainData,
|
||||
type: 'dns',
|
||||
domain: domainData,
|
||||
path: localePath(`/dns/${domain}.html`),
|
||||
date: AdjustTimeToUTCOffset(new Date().toString(), settingsStore.timeZones)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
useHead({
|
||||
title: `${domainData} - ${t('dns.title')}`,
|
||||
meta: [
|
||||
{
|
||||
name: 'description',
|
||||
content: t('dns.description', {domain: domainData})
|
||||
}, {
|
||||
name: 'keywords',
|
||||
content: t('dns.keywords', {domain: domainData})
|
||||
}
|
||||
]
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mt-5">
|
||||
<div class=" shadow-lg rounded-lg overflow-hidden">
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-2xl font-bold text-gray-800"> {{ t('dns.dnsResult') }}
|
||||
<span
|
||||
class="text-gray-300 text-sm font-normal ml-2">
|
||||
|
||||
</span>
|
||||
</h2>
|
||||
<NTooltip
|
||||
placement="top">
|
||||
<template #trigger>
|
||||
<DnsApiChanges/>
|
||||
</template>
|
||||
{{ t('popper.dnsChange') }}
|
||||
</NTooltip>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class=" "> <!-- 使用 min-h-screen 确保占满至少一个屏幕高度 -->
|
||||
<div
|
||||
class="p-8 ">
|
||||
<!-- 增加内边距、使用更大的最大宽度、更大的圆角和阴影 -->
|
||||
<h2 class="mb-6 text-xl font-bold text-gray-900 dark:text-white w-full">提示</h2> <!-- 增大标题文字和下边距 -->
|
||||
<p class="text-center my-2 text-lg text-gray-700 dark:text-gray-400 w-full">当前没有可用的 DNS 服务器。</p>
|
||||
<!-- 增大正文文字尺寸,并添加更多说明 -->
|
||||
<p class="text-center my-2 text-lg text-gray-700 dark:text-gray-400 w-full">请检查您的Dns设置或稍后再试。</p>
|
||||
<!-- 增大正文文字尺寸,并添加更多说明 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DnsInfoList
|
||||
:data="data"
|
||||
/>
|
||||
|
||||
<!-- <DnsCloudflareList-->
|
||||
<!-- v-if="timeStore.getDnsServer == 'cloudflare'"-->
|
||||
<!-- :data="data"-->
|
||||
<!-- />-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,86 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {AdjustTimeToUTCOffset} from "~/utils/utc";
|
||||
|
||||
const {t} = useI18n()
|
||||
const localePath = useLocalePath()
|
||||
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
const route = useRoute();
|
||||
const {domain} = route.params;
|
||||
const domainData = typeof domain === "string" ? domain?.replace(/_/g, '.') : "";
|
||||
|
||||
const {data: domainInfo, pending, error, refresh} = await useAsyncData(
|
||||
'domain',
|
||||
() => $fetch('/api/domain', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
domain: domainData,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
if (!error.value && settingsStore.getIsHistory) {
|
||||
settingsStore.addOrUpdateHistory(
|
||||
{
|
||||
id: domainData,
|
||||
type: 'domain',
|
||||
domain: domainData,
|
||||
path: localePath(`/domain/${domain}.html`),
|
||||
date: AdjustTimeToUTCOffset(new Date().toString(), settingsStore.timeZones)
|
||||
}
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mt-5 mx-auto mb-5">
|
||||
<div
|
||||
v-if="domainStore.getHasDomainShown"
|
||||
class="bg-white dark:bg-gray-900 shadow rounded-lg overflow-hidden">
|
||||
<div class="p-6 space-y-4">
|
||||
<div
|
||||
class="flex justify-between items-center">
|
||||
<n-tag type="info" size="medium">域名信息</n-tag>
|
||||
<n-tag v-if="domainStore.getHasDomainShown" type="success" size="medium">
|
||||
Api来源:{{ domainStore.getFirstNewDomainShown?.name }}
|
||||
</n-tag>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4 text-sm">
|
||||
<p class="text-gray-800 dark:text-gray-200">域名: <span class="font-medium">{{ domainInfo.domain }}</span></p>
|
||||
<p class="text-gray-800 dark:text-gray-200">货币: <span class="font-medium">{{
|
||||
domainInfo.currency
|
||||
}} ({{ domainInfo.currency_symbol }})</span></p>
|
||||
<p class="text-gray-800 dark:text-gray-200">新注册价格: <span
|
||||
class="font-medium">{{ domainInfo.currency_symbol }}{{ domainInfo.new }}</span>
|
||||
</p>
|
||||
<p class="text-gray-800 dark:text-gray-200">续费价格: <span class="font-medium">{{
|
||||
domainInfo.currency_symbol
|
||||
}}{{ domainInfo.renew }}</span></p>
|
||||
<p v-if="domainInfo.premium" class="text-gray-800 dark:text-gray-200">溢价:<span
|
||||
class="font-medium">{{ domainInfo.premium ? '支持' : '不支持' }}</span></p>
|
||||
<p v-else class="text-gray-800 dark:text-gray-200">溢价功能:<span
|
||||
class="font-medium">{{ domainInfo.premium ? '支持' : '不支持' }}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="bg-white shadow-lg rounded-lg overflow-hidden"> <!-- 使用 min-h-screen 确保占满至少一个屏幕高度 -->
|
||||
<div
|
||||
class="p-8 ">
|
||||
<!-- 增加内边距、使用更大的最大宽度、更大的圆角和阴影 -->
|
||||
<h2 class="mb-6 text-xl font-bold text-gray-900 dark:text-white w-full">提示</h2> <!-- 增大标题文字和下边距 -->
|
||||
<p class="text-center my-2 text-lg text-gray-700 dark:text-gray-400 w-full">当前没有可用的 Domain 服务器。</p>
|
||||
<!-- 增大正文文字尺寸,并添加更多说明 -->
|
||||
<p class="text-center my-2 text-lg text-gray-700 dark:text-gray-400 w-full">请检查您的Domain设置或稍后再试。</p>
|
||||
<!-- 增大正文文字尺寸,并添加更多说明 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
const settingsStore = useSettingsStore()
|
||||
|
||||
const {t} = useI18n()
|
||||
|
||||
useHead({
|
||||
title: `${t('index.title')} - ${t('app.title')}`,
|
||||
meta: [
|
||||
{
|
||||
name: 'description',
|
||||
content: t('index.description')
|
||||
}, {
|
||||
name: 'keywords',
|
||||
content: t('index.keywords')
|
||||
}
|
||||
]
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,278 @@
|
||||
<script setup lang="ts">
|
||||
import draggable from "vuedraggable";
|
||||
|
||||
definePageMeta({
|
||||
stylePage: true,
|
||||
})
|
||||
|
||||
const message = useMessage();
|
||||
const configStore = useConfigStore()
|
||||
const {configServer} = storeToRefs(configStore)
|
||||
|
||||
// 恢复默认排序
|
||||
const restoreDefault = (arr: any, name: string) => {
|
||||
arr.sort((a, b) => a.order - b.order);
|
||||
message.success(`恢复默认${name}榜单排序成功`);
|
||||
|
||||
if (name === 'whois') {
|
||||
updateConfigWithFirstVisibleItem(arr, 'Whois');
|
||||
} else if (name === 'dns') {
|
||||
updateConfigWithFirstVisibleItem(arr, 'Dns');
|
||||
} else if (name === 'domain') {
|
||||
updateConfigWithFirstVisibleItem(arr, 'Domain');
|
||||
}
|
||||
};
|
||||
|
||||
const updateConfigWithFirstVisibleItem = (arr, configKey) => {
|
||||
const visibleItems = arr.filter(item => item.show);
|
||||
const firstItemName = visibleItems.length > 0 ? visibleItems[0].name : '';
|
||||
configStore[`setCurrentServer${configKey}`](firstItemName);
|
||||
};
|
||||
|
||||
// 将排序结果写入
|
||||
const saveSoreData = (name = null, open = false, rank = "whois") => {
|
||||
message.success(
|
||||
name ? `${name}${rank}榜单已${open ? "开启" : "关闭"}` : `${rank}榜单排序成功`
|
||||
);
|
||||
switch (rank) {
|
||||
case 'whois':
|
||||
updateConfigWithFirstVisibleItem(configServer.value.whoisArr, 'Whois');
|
||||
break;
|
||||
case 'dns':
|
||||
updateConfigWithFirstVisibleItem(configServer.value.dnsArr, 'Dns');
|
||||
break;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<div class="setting mt-5">
|
||||
<n-card class="set-item">
|
||||
<div class="top">
|
||||
<div class="name">
|
||||
<n-text class="text">Whois第三方API</n-text>
|
||||
<n-text class="tip" depth="3">
|
||||
拖拽以排序,开关用以控制在页面中的显示状态
|
||||
</n-text>
|
||||
</div>
|
||||
<n-popconfirm @positive-click="restoreDefault(configServer.whoisArr,'whois')">
|
||||
<template #trigger>
|
||||
<n-button class="control" size="small"> 恢复默认</n-button>
|
||||
</template>
|
||||
确认将排序恢复到默认状态?
|
||||
</n-popconfirm>
|
||||
</div>
|
||||
<draggable
|
||||
:list="configServer.whoisArr"
|
||||
:animation="200"
|
||||
class="mews-group"
|
||||
item-key="order"
|
||||
@end="saveSoreData(null,false,'whois')"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<n-card
|
||||
class="item"
|
||||
embedded
|
||||
:content-style="{ display: 'flex', alignItems: 'center' }"
|
||||
>
|
||||
<div class="desc" :style="{ opacity: element.show ? null : 0.6 }">
|
||||
<img class="logo" :src="`/logo/${element.name}.png`" alt="logo"/>
|
||||
<n-text class="news-name" v-html="element.label"/>
|
||||
</div>
|
||||
<n-switch
|
||||
class="switch"
|
||||
:round="false"
|
||||
:disabled="element.disabled"
|
||||
v-model:value="element.show"
|
||||
@update:value="saveSoreData(element.label, element.show,'whois')"
|
||||
/>
|
||||
</n-card>
|
||||
</template>
|
||||
</draggable>
|
||||
</n-card>
|
||||
|
||||
<n-card class="set-item">
|
||||
<div class="top">
|
||||
<div class="name">
|
||||
<n-text class="text">Dns第三方API</n-text>
|
||||
</div>
|
||||
<n-popconfirm
|
||||
@positive-click="restoreDefault(configServer.dnsArr,'dns')"
|
||||
negative-text="取消"
|
||||
positive-text="确认"
|
||||
>
|
||||
<template #trigger>
|
||||
<n-button class="control" size="small"> 恢复默认</n-button>
|
||||
</template>
|
||||
确认将Dns排序恢复到默认状态?
|
||||
</n-popconfirm>
|
||||
</div>
|
||||
<draggable
|
||||
:list="configServer.dnsArr"
|
||||
:animation="200"
|
||||
class="mews-group"
|
||||
item-key="order"
|
||||
@end="saveSoreData(null,false,'dns')"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<n-card
|
||||
class="item"
|
||||
embedded
|
||||
:content-style="{ display: 'flex', alignItems: 'center' }"
|
||||
>
|
||||
<div class="desc" :style="{ opacity: element.show ? null : 0.6 }">
|
||||
<img class="logo" :src="`/logo/${element.name}.png`" alt="logo"/>
|
||||
<n-text class="news-name" v-html="element.label"/>
|
||||
</div>
|
||||
<n-switch
|
||||
class="switch"
|
||||
:disabled="element.disabled"
|
||||
:round="false"
|
||||
v-model:value="element.show"
|
||||
@update:value="saveSoreData(element.label, element.show,'dns')"
|
||||
/>
|
||||
</n-card>
|
||||
</template>
|
||||
</draggable>
|
||||
</n-card>
|
||||
|
||||
<n-card class="set-item">
|
||||
<div class="top">
|
||||
<div class="name">
|
||||
<n-text class="text">Domain第三方API</n-text>
|
||||
</div>
|
||||
<n-popconfirm
|
||||
@positive-click="restoreDefault(configServer.domainArr,'domain')"
|
||||
negative-text="取消"
|
||||
positive-text="确认"
|
||||
>
|
||||
<template #trigger>
|
||||
<n-button class="control" size="small"> 恢复默认</n-button>
|
||||
</template>
|
||||
确认将Domain排序恢复到默认状态?
|
||||
</n-popconfirm>
|
||||
</div>
|
||||
<draggable
|
||||
:list="configServer.domainArr"
|
||||
:animation="200"
|
||||
class="mews-group"
|
||||
item-key="order"
|
||||
@end="saveSoreData(null,false,'domain')"
|
||||
>
|
||||
<template #item="{ element }">
|
||||
<n-card
|
||||
class="item"
|
||||
embedded
|
||||
:content-style="{ display: 'flex', alignItems: 'center' }"
|
||||
>
|
||||
<div class="desc" :style="{ opacity: element.show ? null : 0.6 }">
|
||||
<img class="logo" :src="`/logo/${element.name}.png`" alt="logo"/>
|
||||
<n-text class="news-name" v-html="element.label"/>
|
||||
</div>
|
||||
<n-switch
|
||||
class="switch"
|
||||
:disabled="element.disabled"
|
||||
:round="false"
|
||||
v-model:value="element.show"
|
||||
@update:value="saveSoreData(element.label, element.show,'domain')"
|
||||
/>
|
||||
</n-card>
|
||||
</template>
|
||||
</draggable>
|
||||
</n-card>
|
||||
</div>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.setting {
|
||||
.title {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 40px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.n-h {
|
||||
padding-left: 16px;
|
||||
font-size: 20px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.set-item {
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.name {
|
||||
font-size: 18px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.tip {
|
||||
font-size: 12px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.set {
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.mews-group {
|
||||
margin-top: 16px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, minmax(0px, 1fr));
|
||||
gap: 24px;
|
||||
|
||||
@media (max-width: 1666px) {
|
||||
grid-template-columns: repeat(4, minmax(0px, 1fr));
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
grid-template-columns: repeat(3, minmax(0px, 1fr));
|
||||
}
|
||||
|
||||
@media (max-width: 890px) {
|
||||
grid-template-columns: repeat(2, minmax(0px, 1fr));
|
||||
}
|
||||
|
||||
@media (max-width: 620px) {
|
||||
grid-template-columns: repeat(1, minmax(0px, 1fr));
|
||||
}
|
||||
|
||||
.item {
|
||||
cursor: pointer;
|
||||
|
||||
.desc {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
transition: all 0.3s;
|
||||
|
||||
.logo {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.news-name {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.switch {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,218 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
definePageMeta({
|
||||
stylePage: true,
|
||||
})
|
||||
|
||||
const settingsStore = useSettingsStore()
|
||||
const {t} = useI18n()
|
||||
const {isObj} = storeToRefs(settingsStore)
|
||||
|
||||
|
||||
const handleReset = async () => {
|
||||
|
||||
}
|
||||
|
||||
const styleStore = useStyleStore();
|
||||
const localCommon = ref({...styleStore.common});
|
||||
const modalVisible = ref(false)
|
||||
const swatches = ['#FFFFFF', '#18A058', '#2080F0', '#F0A020', 'rgba(208, 48, 80, 1)'];
|
||||
const updateColors = () => {
|
||||
styleStore.updatePrimaryColor(localCommon.value);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ClientOnly>
|
||||
<div class="setting mt-8 settings-grid">
|
||||
<n-h6 prefix="bar"> 基础设置</n-h6>
|
||||
<n-card class="set-item">
|
||||
<div class="top grid grid-cols-2 gap-4">
|
||||
<div class="name">
|
||||
<n-text class="text">{{ t('settings.title') }}</n-text>
|
||||
<n-text class="tip" depth="3">{{ t('settings.history') }}</n-text>
|
||||
</div>
|
||||
<n-switch v-model:value="isObj.isHistory" :round="false"/>
|
||||
|
||||
<div class="name">
|
||||
<n-text class="text">公告设置</n-text>
|
||||
<n-text class="tip" depth="3">是否开启首页公告功能</n-text>
|
||||
</div>
|
||||
<n-switch v-model:value="isObj.isBulletin" :round="false"/>
|
||||
|
||||
<div class="name">
|
||||
<n-text class="text">支持列表</n-text>
|
||||
<n-text class="tip" depth="3">是否开启支持列表功能</n-text>
|
||||
</div>
|
||||
<n-switch v-model:value="isObj.isDomainList" :round="false"/>
|
||||
|
||||
<div class="name">
|
||||
<n-text class="text">支持列表</n-text>
|
||||
<n-text class="tip" depth="3">是否显示 Logo</n-text>
|
||||
</div>
|
||||
<n-switch v-model:value="isObj.isLogo" :round="false"/>
|
||||
|
||||
</div>
|
||||
</n-card>
|
||||
|
||||
<n-modal v-model:show="modalVisible" title="颜色设置" :style="{ width: '480px' }">
|
||||
<div class="p-8">
|
||||
<div class="max-w-4xl mx-auto">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div>
|
||||
<div class="text-sm font-semibold mb-2">Primary Color</div>
|
||||
<n-color-picker v-model:value="localCommon.primaryColor" :swatches="swatches"/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-sm font-semibold mb-2">Info Color</div>
|
||||
<n-color-picker v-model:value="localCommon.infoColor" :swatches="swatches"/>
|
||||
</div>
|
||||
<!-- 重复上述结构以添加更多颜色选择器 -->
|
||||
<div>
|
||||
<div class="text-sm font-semibold mb-2">Success Color</div>
|
||||
<n-color-picker v-model:value="localCommon.successColor" :swatches="swatches"/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-sm font-semibold mb-2">Warning Color</div>
|
||||
<n-color-picker v-model:value="localCommon.warningColor" :swatches="swatches"/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-sm font-semibold mb-2">Error Color</div>
|
||||
<n-color-picker v-model:value="localCommon.errorColor" :swatches="swatches"/>
|
||||
</div>
|
||||
</div>
|
||||
<n-button @click="updateColors" class="mt-6">Update Colors</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</n-modal>
|
||||
|
||||
<div class="mx-auto mt-10">
|
||||
<n-h6 class="mb-4 text-lg font-bold text-gray-800" prefix="bar">颜色设置</n-h6>
|
||||
<n-card class="set-item p-4 shadow-lg">
|
||||
<div class="top flex justify-between items-center">
|
||||
<div class="name">
|
||||
<n-text class="text text-gray-600">自定义所有颜色</n-text>
|
||||
</div>
|
||||
<n-button type="warning" @click="modalVisible = true" class="bg-orange-500 hover:bg-orange-600">确认
|
||||
</n-button>
|
||||
</div>
|
||||
</n-card>
|
||||
</div>
|
||||
|
||||
<n-h6 prefix="bar"> 杂项设置</n-h6>
|
||||
<n-card class="set-item">
|
||||
<div class="top">
|
||||
<div class="name">
|
||||
<n-text class="text">重置所有数据</n-text>
|
||||
<n-text class="tip" depth="3">
|
||||
重置所有数据,你的自定义设置都将会丢失
|
||||
</n-text>
|
||||
</div>
|
||||
<n-popconfirm
|
||||
@positive-click="handleReset"
|
||||
:negative-text="t('common.actions.cancel')"
|
||||
:positive-text="t('common.actions.confirm')"
|
||||
>
|
||||
<template #trigger>
|
||||
<n-button type="warning"> {{ t('common.actions.reset') }}</n-button>
|
||||
</template>
|
||||
确认重置所有数据?你的自定义设置都将会丢失!
|
||||
</n-popconfirm>
|
||||
</div>
|
||||
</n-card>
|
||||
</div>
|
||||
</ClientOnly>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.setting {
|
||||
.title {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 40px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.n-h {
|
||||
padding-left: 16px;
|
||||
font-size: 20px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.set-item {
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.name {
|
||||
font-size: 18px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.tip {
|
||||
font-size: 12px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.set {
|
||||
max-width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.mews-group {
|
||||
margin-top: 16px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, minmax(0px, 1fr));
|
||||
gap: 24px;
|
||||
|
||||
@media (max-width: 1666px) {
|
||||
grid-template-columns: repeat(4, minmax(0px, 1fr));
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
grid-template-columns: repeat(3, minmax(0px, 1fr));
|
||||
}
|
||||
|
||||
@media (max-width: 890px) {
|
||||
grid-template-columns: repeat(2, minmax(0px, 1fr));
|
||||
}
|
||||
|
||||
@media (max-width: 620px) {
|
||||
grid-template-columns: repeat(1, minmax(0px, 1fr));
|
||||
}
|
||||
|
||||
.item {
|
||||
cursor: pointer;
|
||||
|
||||
.desc {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
transition: all 0.3s;
|
||||
|
||||
.logo {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.news-name {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.switch {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,69 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
definePageMeta({
|
||||
stylePage: true,
|
||||
})
|
||||
|
||||
const {t} = useI18n()
|
||||
const route = useRoute();
|
||||
const {domain}: any = route.params;
|
||||
const domainData = typeof domain === "string" ? domain?.replace(/_/g, '.') : "";
|
||||
const settingsStore = useSettingsStore()
|
||||
const localePath = useLocalePath()
|
||||
const configStore = useConfigStore()
|
||||
const {currentServer} = storeToRefs(configStore)
|
||||
|
||||
const {data, pending, error, refresh} = await useAsyncData(
|
||||
'whois',
|
||||
() => $fetch('/api/server/whois', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
domain: domainData,
|
||||
serverName: currentServer.value.whois,
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
if (!error.value && settingsStore.getIsHistory) {
|
||||
settingsStore.addOrUpdateHistory(
|
||||
{
|
||||
id: domainData,
|
||||
type: 'whois',
|
||||
domain: domainData,
|
||||
path: localePath(`/whois/${domain}.html`),
|
||||
date: AdjustTimeToUTCOffset(new Date().toString(), settingsStore.timeZones)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
useHead({
|
||||
title: `${domainData} - ${t('whois.title')}`,
|
||||
meta: [
|
||||
{
|
||||
name: 'description',
|
||||
content: t('whois.description', {domain: domainData})
|
||||
}, {
|
||||
name: 'keywords',
|
||||
content: t('whois.keywords', {domain: domainData})
|
||||
}
|
||||
]
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
class="w-full bg-[#fffffe] mt-5 p-4 shadow-lg rounded-lg whitespace-pre-wrap dark:text-gray-200 dark:bg-gray-800"
|
||||
>
|
||||
<WhoisNuxt
|
||||
v-if="currentServer.whois == 'nuxt'"
|
||||
:data="data"
|
||||
/>
|
||||
<WhoisTianHu
|
||||
v-if="currentServer.whois == 'tianhu'"
|
||||
:data="data"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user