💥 破坏性更新 咱不推荐更新

This commit is contained in:
7836246
2024-03-25 06:55:06 +08:00
parent 9bb0fbd9a6
commit 410dc1ac58
52 changed files with 3645 additions and 1337 deletions
+179
View File
@@ -0,0 +1,179 @@
<script setup lang="ts">
definePageMeta({
layout: 'full',
})
</script>
<template>
<CommonAppPage show-footer>
<div class="flex">
<n-card class="min-w-200 w-30%">
<div class="flex items-center">
<!-- <n-avatar round :size="60" :src="userStore.avatar" class="flex-shrink-0" />-->
<div class="ml-20 flex-col">
<span class="text-20 opacity-80">
<!-- Hello, {{ userStore.nickName ?? userStore.username }}-->
</span>
<!-- <span class="mt-4 opacity-50">当前角色{{ userStore.currentRole?.name }}</span>-->
</div>
</div>
<p class="mt-28 text-14 opacity-60">一个人几乎可以在任何他怀有无限热忱的事情上成功</p>
<p class="mt-12 text-right text-12 opacity-40"> 查尔斯·史考伯</p>
</n-card>
<n-card class="ml-12 w-70%" title="✨ 欢迎使用 Vue Naive Admin 2.0">
<template #header-extra>
<a
class="text-14 text-primary text-highlight hover:underline hover:opacity-80"
href="https://isme.top"
target="_blank"
@click.prevent="useMessage()?.info('官网正在火速开发中...')"
>
isme.top
</a>
</template>
<p class="opacity-60">
这是一款极简风格的后台管理模板包含前后端解决方案前端使用 Vite + Vue3 + Pinia +
Unocss后端使用 Nestjs + TypeOrm +
MySql简单易用赏心悦目历经十几次重构和细节打磨诚意满满
</p>
<footer class="mt-12 flex items-center justify-end">
<n-button
type="primary"
ghost
tag="a"
href="https://docs.isme.top/web/#/624306705/188522224"
target="__blank"
>
开发文档
</n-button>
<n-button
type="primary"
class="ml-12"
tag="a"
href="https://github.com/zclzone/vue-naive-admin/tree/2.x"
target="__blank"
>
代码仓库
</n-button>
</footer>
</n-card>
</div>
<div class="mt-12 flex">
<n-card class="w-50%" title="💯 特性" segmented>
<template #header-extra>
<span class="opacity-90 text-highlight">👏 历经十几次重构和细节打磨</span>
</template>
<ul class="opacity-90">
<li class="py-4">
🆒 使用
<b>Vue3</b>
主流技术栈:
<span class="text-highlight">Vite + Vue3 + Pinia</span>
</li>
<li class="py-4">
🍇 使用
<b>原子CSS</b>
框架:
<span class="text-highlight">Unocss</span>
优雅轻量易用
</li>
<li class="py-4">
🤹 使用主流的
<span class="text-highlight">iconify + unocss</span>
图标方案支持自定义图标支持动态渲染
</li>
<li class="py-4">
🎨 使用 Naive UI
<span class="text-highlight">极致简洁的代码风格和清爽的页面设计</span>
审美在线主题轻松定制
</li>
<li class="py-4">
👏 先进且易于理解的文件结构设计多个模块之间
<b>零耦合</b>
单个业务模块删除不影响其他模块
</li>
<li class="py-4">
🚀
<span class="text-highlight">扁平化路由</span>
设计每一个组件都可以是一个页面告别多级路由 KeepAlive 难实现问题
</li>
<li class="py-4">
🍒
<span class="text-highlight">基于权限动态生成路由</span>
无需额外定义路由
<span class="text-highlight">403和404可区分</span>
而不是无权限也跳404
</li>
<li class="py-4">
🔐 基于Redis集成
<span class="text-highlight">无感刷新</span>
用户登录态可控安全与体验缺一不可
</li>
<li class="py-4">
基于 Naive UI 封装
<span class="text-highlight">message</span>
全局工具方法支持批量提醒支持跨页面共享实例
</li>
<li class="py-4">
基于 Naive UI 封装常用的业务组件包含
<span class="text-highlight">Page</span>
组件
<span class="text-highlight">CRUD</span>
表格组件及
<span class="text-highlight">Modal</span>
组件减少大量重复性工作
</li>
</ul>
<n-divider class="mb-0! mt-12!">
<p class="text-14 opacity-60">
👉点击
<b class="mx-2 transition hover:text-primary">
<a href="https://isme.top" target="_blank">更多</a>
</b>
查看更多实用功能持续开发中...
</p>
</n-divider>
</n-card>
<n-card class="ml-12 w-50%" title="🛠️ 技术栈" segmented>
<!-- <VChart :option="skillOption" autoresize />-->
</n-card>
</div>
<n-card class="mt-12" title="⚡️ 趋势" segmented>
<!-- <VChart :option="trendOption" :init-options="{ height: 400 }" autoresize />-->
</n-card>
</CommonAppPage>
</template>
<style lang="less" scoped>
.viewMount {
width: 100%;
height: 300px;
box-shadow: 0 1px 2px -2px rgba(0, 0, 0, 0.08), 0 3px 6px 0 rgba(0, 0, 0, 0.06),
0 5px 12px 4px rgba(0, 0, 0, 0.04);
border: 1px solid #efeff5;
.chart-header {
padding: 0 10px;
border-bottom: 1px solid #e5e6e7;
display: flex;
justify-content: space-between;
align-items: center;
height: 40px;
.left-title {
display: flex;
color: #3a4446;
}
.right-unit {
color: #fff;
padding: 1px 6px;
border-radius: 2px;
}
}
}
</style>
+104
View File
@@ -0,0 +1,104 @@
<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]">
<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="45" height="45">
<path
d="M228.364788 380.401625c0-20.948412 11.370167-40.019574 29.20405-49.277834l235.061667-136.228678 2.346563-1.237279c14.079381-6.762369 30.292002-6.719705 46.525955 1.322608l235.168329 137.103307c18.559184 9.044936 30.398664 28.500081 30.398664 49.832476l0.426648 272.905337c0 21.033742-11.476829 40.232898-28.436084 48.851186l-242.165354 135.290053a50.643107 50.643107 0 0 1-42.792786 1.621262l-3.178527-1.535933L99.239798 624.572225C80.445957 615.697948 68.222494 596.072144 68.222494 574.249104l0.959958-294.643048c0-21.417725 11.903477-40.894202 28.436084-48.851186L492.395848 5.058124c14.100713-6.869031 30.377331-6.869031 46.611285 1.130617l387.609628 223.499509c18.559184 9.023603 30.441329 28.500081 30.441328 49.917806v452.374781c0 21.183069-11.647488 40.467554-28.286756 48.765856l-392.004102 223.563506a50.643107 50.643107 0 0 1-43.048774 1.215946l-2.858541-1.407938-237.57889-130.767585-100.262259 89.638727c-21.076407 20.905748-54.397609 20.052452-74.450061-1.919916A56.424186 56.424186 0 0 1 64.020012 923.033772V754.145196c0-20.137781 15.593981-36.435732 34.835802-36.435732s34.835802 16.29795 34.835802 36.435732v130.340937l81.063104-72.48748a51.005758 51.005758 0 0 1 56.530848-10.900855l2.837209 1.386606 239.754794 131.962199 373.530247-213.025302V290.165591L514.666869 75.284371 138.811391 290.165591l-0.89596 273.331985 90.449357 49.51249V380.401625z m86.716188 173.752362h47.54991l73.511435 93.585219a42.046152 42.046152 0 0 0 60.648001 6.399719c6.954361-5.866409 11.946142-13.823392 14.33537-22.804331l46.077974-173.795027 57.87479 96.635752h95.398473c17.215243 0 31.16663-14.506029 31.16663-32.339911 0-17.876548-13.951387-32.339912-31.145298-32.339912h-60.775995l-62.78124-104.827392a43.304763 43.304763 0 0 0-25.193559-19.519142c-22.953658-6.549045-46.675282 7.466338-52.989671 31.273292l-47.848563 180.472067-68.690314-87.398825H315.102309c-17.215243 0-31.16663 14.463364-31.16663 32.339912 0 17.855215 13.951387 32.318579 31.145297 32.318579z"
></path>
</svg>
<n-gradient-text :gradient="gradient" class="text-[24px]">Admin Pro</n-gradient-text>
</div>
<div class="text-[#5a6f7e]">Admin Pro 中后台前端/设计解决方案</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>
+48 -28
View File
@@ -1,20 +1,18 @@
<script setup lang="ts">
import {useStyleStore} from "~/stores/style";
import {AdjustTimeToUTCOffset} from "~/utils/utc";
import {useTimeStore} from "~/stores/time";
import DnsInfo from "~/components/dns/InfoList.vue";
const {t} = useI18n()
const localePath = useLocalePath()
const route = useRoute();
const {domain} = route.params;
const domainData = domain.replace(/_/g, '.')
const domainData = typeof domain === "string" ? domain?.replace(/_/g, '.') : "";
const timeStore = useTimeStore()
const styleStore = useStyleStore()
const localePath = useLocalePath()
const settingsStore = useSettingsStore()
const dnsStore = useDnsStore()
styleStore.setIsPage(true)
const {data, pending, error, refresh} = await useAsyncData(
@@ -22,8 +20,9 @@ const {data, pending, error, refresh} = await useAsyncData(
() => $fetch('/api/dns', {
method: 'POST',
body: {
domain: domainData,
dnsServer:timeStore.getDnsServer
domain: domainData,
dnsServer: dnsStore.getFirstNewDnsShown?.name,
flag: dnsStore.getHasShownItems
}
})
)
@@ -40,15 +39,16 @@ if (!error.value && settingsStore.getHistory) {
)
}
useHead({
title: `${domainData} - ${t('dns.title')}`,
meta: [
{
name: 'description',
content: t('dns.description', { domain: domainData })
},{
content: t('dns.description', {domain: domainData})
}, {
name: 'keywords',
content: t('dns.keywords', { domain: domainData })
content: t('dns.keywords', {domain: domainData})
}
]
})
@@ -58,40 +58,60 @@ useHead({
<div class="mt-5">
<div class="bg-white shadow-lg rounded-lg overflow-hidden">
<div class="p-6">
<div class="flex justify-between items-center mb-6">
<div class="flex justify-between items-center mb-6"
v-if="dnsStore.getHasShownItems"
>
<h2 class="text-2xl font-bold text-gray-800"> {{ t('dns.dnsResult') }}
<span class="text-gray-300 text-sm font-normal ml-2">{{ timeStore.getDnsServer }}</span>
<ClientOnly>
<span
class="text-gray-300 text-sm font-normal ml-2">
{{ dnsStore.getFirstNewDnsShown.iName }}
</span>
</ClientOnly>
</h2>
<ClientOnly>
<UTooltip :text="t('popper.dnsChange')" :popper="{ placement: 'top' }">
<DnsApiChanges />
</UTooltip>
<NTooltip
placement="top">
<template #trigger>
<DnsApiChanges
/>
</template>
{{ t('popper.dnsChange') }}
</NTooltip>
</ClientOnly>
</div>
<DnsDefaultList v-if="timeStore.getDnsServer == ''"
:data="data" />
<div
v-else
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
v-if="timeStore.getDnsServer != 'cloudflare' && timeStore.getDnsServer != ''"
v-if="dnsStore.getHasShownItems"
:data="data"
/>
<DnsCloudflareList
v-if="timeStore.getDnsServer == 'cloudflare'"
:data="data"
/>
<!-- <DnsCloudflareList-->
<!-- v-if="timeStore.getDnsServer == 'cloudflare'"-->
<!-- :data="data"-->
<!-- />-->
</div>
</div>
</div>
<!-- 公告部分 -->
<CommonBulletin v-if="error" class="mt-5" >
<CommonBulletin v-if="error && apisStore.getHasShownItems" class="mt-5">
<template #text>
<Icon name="bx:error" size="16px" color="red" />
<Icon name="bx:error" size="16px" color="red"/>
{{ t('error.notFound') }}
</template>
</CommonBulletin>
+94
View File
@@ -0,0 +1,94 @@
<script setup lang="ts">
import {AdjustTimeToUTCOffset} from "~/utils/utc";
const {t} = useI18n()
const localePath = useLocalePath()
const timeStore = useTimeStore()
const styleStore = useStyleStore()
const settingsStore = useSettingsStore()
//控制 api 列表
const domainStore = useDomainStore()
styleStore.setIsPage(true)
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,
domainServer: domainStore.getFirstNewDomainShown.name,
flag: domainStore.getHasDomainShown
}
})
)
if (!error.value && settingsStore.getHistory) {
styleStore.addOrUpdateHistory(
{
id: domainData,
type: 'domain',
domain: domainData,
path: localePath(`/domain/${domain}.html`),
date: AdjustTimeToUTCOffset(new Date().toString(), timeStore.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>
+298
View File
@@ -0,0 +1,298 @@
<script setup lang="ts">
import draggable from "vuedraggable";
const styleStore = useStyleStore()
const settingsStore = useSettingsStore();
const whoisStore = useWhoisStore()
const dnsStore = useDnsStore()
const domainStore = useDomainStore()
styleStore.setIsPage(true)
const {
newsDnsArr,
} = storeToRefs(dnsStore);
const {
newsDomainArr,
} = storeToRefs(domainStore);
const {
newsWhoisArr,
} = storeToRefs(whoisStore);
const message = useMessage();
// 恢复默认排序
const restoreDefault = () => {
newsWhoisArr.value = newsWhoisArr.value.sort((a, b) => a.order - b.order);
message.success("恢复默认Whois榜单排序成功");
};
const restoreDnsDefault = () => {
newsDnsArr.value = newsDnsArr.value.sort((a, b) => a.order - b.order);
message.success("恢复Dns Api榜单排序成功");
};
const restoreDomainDefault = () => {
newsDomainArr.value = newsDomainArr.value.sort((a, b) => a.order - b.order);
message.success("恢复Dns Api榜单排序成功");
};
// 将排序结果写入
const saveSoreData = (name = null, open = false) => {
message.success(
name ? `${name}Whois榜单已${open ? "开启" : "关闭"}` : "Whois榜单排序成功"
);
whoisStore.checkNewsWhoisUpdate()
};
const saveDnsSoreData = (name = null, open = false) => {
message.success(
name ? `${name}DnsServer已${open ? "开启" : "关闭"}` : "DnsServer排序成功"
);
dnsStore.checkNewsDnsUpdate()
};
const saveSoreDomainData = (name = null, open = false) => {
message.success(
name ? `${name}DomainServer已${open ? "开启" : "关闭"}` : "DomainServer排序成功"
);
domainStore.checkNewsDomainUpdate()
};
</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">
<template #trigger>
<n-button class="control" size="small"> 恢复默认</n-button>
</template>
确认将排序恢复到默认状态
</n-popconfirm>
</div>
<draggable
:list="newsWhoisArr"
:animation="200"
class="mews-group"
item-key="order"
@end="saveSoreData()"
>
<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)"
/>
</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>
<n-text class="tip" depth="3">
拖拽以排序开关用以控制在页面中的显示状态
</n-text>
</div>
<n-popconfirm
@positive-click="restoreDnsDefault"
negative-text="取消"
positive-text="确认"
>
<template #trigger>
<n-button class="control" size="small"> 恢复默认</n-button>
</template>
确认将Dns排序恢复到默认状态
</n-popconfirm>
</div>
<draggable
:list="newsDnsArr"
:animation="200"
class="mews-group"
item-key="order"
@end="saveDnsSoreData()"
>
<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="saveDnsSoreData(element.label, element.show)"
/>
</n-card>
</template>
</draggable>
</n-card>
<n-card class="set-item">
<div class="top">
<div class="name">
<n-text class="text">域名第三方API</n-text>
<n-text class="tip" depth="3">
拖拽以排序开关用以控制在页面中的显示状态
</n-text>
</div>
<n-popconfirm @positive-click="restoreDomainDefault">
<template #trigger>
<n-button class="control" size="small"> 恢复默认</n-button>
</template>
确认将排序恢复到默认状态
</n-popconfirm>
</div>
<draggable
:list="newsDomainArr"
:animation="200"
class="mews-group"
item-key="order"
@end="saveSoreDomainData()"
>
<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="saveSoreDomainData(element.label, element.show)"
/>
</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>
+239 -116
View File
@@ -1,7 +1,7 @@
<script setup lang="ts">
import {useStyleStore} from "~/stores/style";
import {useSettingsStore} from "~/stores/settings";
import {useDomainStore} from "~/stores/domain";
import {useModal} from 'naive-ui';
const styleStore = useStyleStore()
const settingsStore = useSettingsStore()
@@ -9,7 +9,7 @@ const {t} = useI18n()
const timeStore = useTimeStore()
styleStore.setIsPage(true)
const {isHistory} = storeToRefs(settingsStore)
const {isHistory, isBulletin, isDomainList} = storeToRefs(settingsStore)
const isOpen = ref(false)
const isEditDomainOpen = ref(false)
@@ -27,136 +27,259 @@ const handleReset = async () => {
</script>
<template>
<div class="setting">
<div class="text-2xl font-bold mt-[30px] mb-[20px]">{{ t('settings.title') }}</div>
<UCard>
<div class="flex justify-between items-center">
<div class="text-base ">{{ t('settings.history') }}</div>
<div>
<UToggle v-model="isHistory" />
</div>
<div class="setting mt-5 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>
</UCard>
</div>
<n-switch v-model:value="isHistory" :round="false"/>
<div class="setting">
<div class="text-2xl font-bold mt-[30px] mb-[20px]"> {{ t('settings.suffixSetting') }} </div>
<u-card class="set-item">
<div class="flex justify-between items-center">
<div class="text-base"> {{ t('settings.customSuffix') }} </div>
<div class="text-sm " >
{{ t('settings.suffixDesc') }}
<div class="name">
<n-text class="text">公告设置</n-text>
<n-text class="tip" depth="3">是否开启首页公告功能</n-text>
</div>
<div>
<u-button type="warning"
@click="isEditDomainOpen = true"
> {{ t('settings.manage') }} </u-button>
<n-switch v-model:value="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="isDomainList" :round="false"/>
</div>
</n-card>
<UModal
v-model="isEditDomainOpen"
>
<UCard
:ui="{
base: 'h-full flex flex-col',
rounded: '10',
divide: 'divide-y divide-gray-100 dark:divide-gray-800',
body: {
base: 'grow'
}
}"
<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('settings.cancel')"
:positive-text="t('settings.confirm')"
>
<template #header>
<div class="flex items-center justify-between">
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
{{ t('settings.suffixManage') }}
</h3>
<UButton
color="gray"
variant="ghost"
icon="i-heroicons-x-mark-20-solid"
class="-my-1"
type="button"
@click="isEditDomainOpen = false" />
</div>
<template #trigger>
<n-button type="warning"> {{ t('common.actions.reset') }}</n-button>
</template>
<div class="mx-auto">
<DomainEditor />
</div>
</UCard>
</UModal>
</u-card>
</div>
<div class="setting">
<div class="text-2xl font-bold mt-[30px] mb-[20px]">{{ t('settings.linkOpenType') }}</div>
<UCard>
<div class="flex justify-between items-center">
<div class="text-base "> {{ t('settings.linkOpenTypeDesc') }} </div>
<div>
<ClientOnly>
<CommonLinkChange />
</ClientOnly>
</div>
确认重置所有数据你的自定义设置都将会丢失
</n-popconfirm>
</div>
</UCard>
</n-card>
</div>
<!-- <div class="setting">-->
<!-- <div class="text-2xl font-bold mt-[30px] mb-[20px]"> {{ t('settings.suffixSetting') }} </div>-->
<!-- <u-card class="set-item">-->
<!-- <div class="flex justify-between items-center">-->
<!-- <div class="text-base"> {{ t('settings.customSuffix') }} </div>-->
<!-- <div class="text-sm " >-->
<!-- {{ t('settings.suffixDesc') }}-->
<!-- </div>-->
<!-- <div>-->
<!-- <u-button type="warning"-->
<!-- @click="isEditDomainOpen = true"-->
<!-- > {{ t('settings.manage') }} </u-button>-->
<!-- </div>-->
<!-- </div>-->
<div class="setting">
<div class="text-2xl font-bold mt-[30px] mb-[20px]"> {{ t('settings.miscellaneous') }} </div>
<u-card class="set-item">
<div class="flex justify-between items-center">
<div class="text-base">{{ t('settings.reset') }} </div>
<div class="text-sm " >
{{ t('settings.resetDesc') }}
</div>
<div>
<u-button type="warning"
@click="isOpen = true"
> {{ t('common.actions.reset') }} </u-button>
</div>
</div>
<!-- <UModal-->
<!-- v-model="isEditDomainOpen"-->
<!-- >-->
<!-- <UCard-->
<!-- :ui="{-->
<!-- base: 'h-full flex flex-col',-->
<!-- rounded: '10',-->
<!-- divide: 'divide-y divide-gray-100 dark:divide-gray-800',-->
<!-- body: {-->
<!-- base: 'grow'-->
<!-- }-->
<!-- }"-->
<!-- >-->
<!-- <template #header>-->
<!-- <div class="flex items-center justify-between">-->
<!-- <h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">-->
<!-- {{ t('settings.suffixManage') }}-->
<!-- </h3>-->
<!-- <UButton-->
<!-- color="gray"-->
<!-- variant="ghost"-->
<!-- icon="i-heroicons-x-mark-20-solid"-->
<!-- class="-my-1"-->
<!-- type="button"-->
<!-- @click="isEditDomainOpen = false" />-->
<!-- </div>-->
<!-- </template>-->
<!-- <div class="mx-auto">-->
<!-- <DomainEditor />-->
<!-- </div>-->
<!-- </UCard>-->
<!-- </UModal>-->
<!-- </u-card>-->
<!-- </div>-->
<UModal
v-model="isOpen"
>
<UCard
:ui="{
base: 'h-full flex flex-col',
rounded: '',
divide: 'divide-y divide-gray-100 dark:divide-gray-800',
body: {
base: 'grow'
}
}"
>
<template #header>
<div class="flex items-center justify-between">
<h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
Modal
</h3>
<UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="isOpen = false" />
</div>
</template>
<!-- <div class="setting">-->
<!-- <div class="text-2xl font-bold mt-[30px] mb-[20px]">{{ t('settings.linkOpenType') }}</div>-->
<!-- <UCard>-->
<!-- <div class="flex justify-between items-center">-->
<!-- <div class="text-base "> {{ t('settings.linkOpenTypeDesc') }} </div>-->
<!-- <div>-->
<!-- <ClientOnly>-->
<!-- <CommonLinkChange />-->
<!-- </ClientOnly>-->
<!-- </div>-->
<!-- </div>-->
<!-- </UCard>-->
<!-- </div>-->
<div class="p-4 m-auto text-center">
{{ t('settings.resetConfirm') }}
</div>
<div class="flex justify-end">
<UButton
@click="handleReset"
class="my-1">
{{ t('common.actions.confirm') }}
</UButton>
</div>
</UCard>
</UModal>
</u-card>
</div>
<!-- <div class="setting">-->
<!-- <div class="text-2xl font-bold mt-[30px] mb-[20px]"> {{ t('settings.miscellaneous') }} </div>-->
<!-- <u-card class="set-item">-->
<!-- <div class="flex justify-between items-center">-->
<!-- <div class="text-base">{{ t('settings.reset') }} </div>-->
<!-- <div class="text-sm " >-->
<!-- {{ t('settings.resetDesc') }}-->
<!-- </div>-->
<!-- <div>-->
<!-- <u-button type="warning"-->
<!-- @click="isOpen = true"-->
<!-- > {{ t('common.actions.reset') }} </u-button>-->
<!-- </div>-->
<!-- </div>-->
<!-- <UModal-->
<!-- v-model="isOpen"-->
<!-- >-->
<!-- <UCard-->
<!-- :ui="{-->
<!-- base: 'h-full flex flex-col',-->
<!-- rounded: '',-->
<!-- divide: 'divide-y divide-gray-100 dark:divide-gray-800',-->
<!-- body: {-->
<!-- base: 'grow'-->
<!-- }-->
<!-- }"-->
<!-- >-->
<!-- <template #header>-->
<!-- <div class="flex items-center justify-between">-->
<!-- <h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">-->
<!-- Modal-->
<!-- </h3>-->
<!-- <UButton color="gray" variant="ghost" icon="i-heroicons-x-mark-20-solid" class="-my-1" @click="isOpen = false" />-->
<!-- </div>-->
<!-- </template>-->
<!-- <div class="p-4 m-auto text-center">-->
<!-- {{ t('settings.resetConfirm') }}-->
<!-- </div>-->
<!-- <div class="flex justify-end">-->
<!-- <UButton-->
<!-- @click="handleReset"-->
<!-- class="my-1">-->
<!-- {{ t('common.actions.confirm') }}-->
<!-- </UButton>-->
<!-- </div>-->
<!-- </UCard>-->
<!-- </UModal>-->
<!-- </u-card>-->
<!-- </div>-->
</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>
+1 -3
View File
@@ -1,8 +1,6 @@
<script setup lang="ts">
import {ParseWhois} from "~/utils/whoisToJson";
import {AdjustTimeToUTCOffset} from "~/utils/utc";
import {useTimeStore} from "~/stores/time";
import {useStyleStore} from "~/stores/style";
const route = useRoute();
const {domain} = route.params;
@@ -133,7 +131,7 @@ useHead({
class="hover:bg-gray-100 text-gray-900 dark:hover:bg-gray-700 text-gray-200">
<th class="p-4 text-left font-semibold text-gray-900 dark:text-gray-200">{{ t('result.rawData') }}</th>
<td class="p-4 text-gray-900 dark:text-gray-200">
<UToggle color="sky" v-model="showRawData"/>
<NSwitch v-model:value="showRawData"/>
</td>
</tr>
</tbody>