🚩 网站设置功能完成
This commit is contained in:
parent
263ad0a4df
commit
c29c6790a1
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
node_modules
|
43
Dockerfile
Normal file
43
Dockerfile
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
FROM node:lts-alpine AS base
|
||||||
|
|
||||||
|
# Set the working directory
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
# Install pnpm
|
||||||
|
RUN apk add --no-cache curl && \
|
||||||
|
curl -f https://get.pnpm.io/v6.16.js | node - add --global pnpm
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Create a stage for installing dependencies
|
||||||
|
FROM base as dependencies
|
||||||
|
# Copy the package.json and package-lock.json (or pnpm-lock.yaml if available)
|
||||||
|
COPY package*.json pnpm-lock.yaml* ./
|
||||||
|
# Install dependencies
|
||||||
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Create a stage for running the application in development mode
|
||||||
|
FROM dependencies as development
|
||||||
|
# Copy the source code
|
||||||
|
COPY . .
|
||||||
|
# Expose the port
|
||||||
|
EXPOSE 3000
|
||||||
|
# Run the application
|
||||||
|
CMD ["pnpm", "run", "dev"]
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Create a stage for building the application
|
||||||
|
FROM dependencies as build
|
||||||
|
# Copy the source code
|
||||||
|
COPY . .
|
||||||
|
# Build the application
|
||||||
|
RUN pnpm run build
|
||||||
|
# Remove extraneous packages
|
||||||
|
RUN pnpm prune --prod
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Create a stage for running the application in production mode
|
||||||
|
FROM base AS production
|
||||||
|
# Copy the built application
|
||||||
|
COPY --from=build /usr/src/app/.output /usr/src/app/.output
|
||||||
|
# Run the application
|
||||||
|
CMD ["node", ".output/server/index.mjs"]
|
@ -48,6 +48,8 @@ yarn
|
|||||||
|
|
||||||
pnpm dev
|
pnpm dev
|
||||||
|
|
||||||
# Nuxt-Whois
|
|
||||||
# Nuxt-Whois
|
```
|
||||||
# Nuxt-Whois
|
|
||||||
|
# 免责声明
|
||||||
|
本项目开源仅供学习使用,不得用于任何违法用途,否则后果自负,与本人无关。使用请保留项目地址谢谢。
|
||||||
|
53
components/common/LinkChange.vue
Normal file
53
components/common/LinkChange.vue
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import {useSettingsStore} from "~/stores/settings";
|
||||||
|
|
||||||
|
const availableTimeZones = ref([
|
||||||
|
{ id: 1, name: 'currentWindow' },
|
||||||
|
{ id: 2, name: 'newWindow' },
|
||||||
|
]);
|
||||||
|
const {t} = useI18n()
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<HeadlessListbox
|
||||||
|
v-model="settingsStore.linkOpenType"
|
||||||
|
as="div"
|
||||||
|
class="relative flex items-center"
|
||||||
|
>
|
||||||
|
<HeadlessListboxLabel class="sr-only">
|
||||||
|
Theme
|
||||||
|
</HeadlessListboxLabel>
|
||||||
|
<HeadlessListboxButton type="button" title="Change Color">
|
||||||
|
<div
|
||||||
|
class="flex h-10 w-10 items-center justify-center rounded-lg bg-gray-100 dark:bg-gray-700"
|
||||||
|
>
|
||||||
|
<Icon name="material-symbols:link" class=" text-lg dark:text-white" />
|
||||||
|
</div>
|
||||||
|
</HeadlessListboxButton>
|
||||||
|
<HeadlessListboxOptions
|
||||||
|
class="absolute top-full right-0 z-[999] mt-2 w-40 max-h-60 overflow-y-auto rounded-lg bg-white text-sm font-semibold text-gray-700 shadow-lg shadow-gray-300 outline-none dark:bg-gray-800 dark:text-white dark:shadow-gray-500 dark:ring-0"
|
||||||
|
>
|
||||||
|
<HeadlessListboxOption
|
||||||
|
v-for="item in availableTimeZones"
|
||||||
|
:key="item.id"
|
||||||
|
:value="item.name"
|
||||||
|
class="flex w-full cursor-pointer items-center justify-between py-2 px-3 hover:bg-gray-100 dark:hover:bg-gray-600"
|
||||||
|
:class="{
|
||||||
|
'text-white-500 bg-gray-200 dark:bg-gray-500/50':
|
||||||
|
settingsStore.linkOpenType === item.name,
|
||||||
|
'hover:bg-gray-200 dark:hover:bg-gray-700/30':
|
||||||
|
settingsStore.linkOpenType !== item.name,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<span class="truncate">
|
||||||
|
{{ t(`settings.${item.name}`) }}
|
||||||
|
</span>
|
||||||
|
<span class="flex items-center justify-center text-sm">
|
||||||
|
</span>
|
||||||
|
</HeadlessListboxOption>
|
||||||
|
</HeadlessListboxOptions>
|
||||||
|
</HeadlessListbox>
|
||||||
|
</div>
|
||||||
|
</template>
|
@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const timeStore = useTimeStore()
|
const timeStore = useTimeStore()
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
const emit = defineEmits(['action'])
|
const emit = defineEmits(['action'])
|
||||||
|
|
||||||
const handleActionFromDnsList = (urlParam:string) => {
|
const handleActionFromDnsList = (urlParam:string) => {
|
||||||
@ -17,7 +18,9 @@ const {t} = useI18n()
|
|||||||
<CommonDomainList />
|
<CommonDomainList />
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
|
|
||||||
<UTooltip :text="t('popper.history')" :popper="{ placement: 'top' }">
|
<UTooltip
|
||||||
|
v-if="settingsStore.getHistory"
|
||||||
|
:text="t('popper.history')" :popper="{ placement: 'top' }">
|
||||||
<CommonHistory />
|
<CommonHistory />
|
||||||
</UTooltip>
|
</UTooltip>
|
||||||
|
|
||||||
|
26
lang/en.ts
26
lang/en.ts
@ -6,6 +6,8 @@ export default defineI18nLocale(async locale => {
|
|||||||
common: {
|
common: {
|
||||||
actions: {
|
actions: {
|
||||||
delete: 'Delete',
|
delete: 'Delete',
|
||||||
|
reset: 'Reset',
|
||||||
|
confirm: 'Confirm',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
whois:{
|
whois:{
|
||||||
@ -123,6 +125,28 @@ export default defineI18nLocale(async locale => {
|
|||||||
theme: 'Theme',
|
theme: 'Theme',
|
||||||
language: 'Language',
|
language: 'Language',
|
||||||
dnsChange: 'DNS Change',
|
dnsChange: 'DNS Change',
|
||||||
}
|
},
|
||||||
|
settings: {
|
||||||
|
//全局设置
|
||||||
|
title: 'Global Settings',
|
||||||
|
//历史记录保留
|
||||||
|
history: 'History Retention',
|
||||||
|
//链接跳转方式
|
||||||
|
linkOpenType: 'Link Open Type',
|
||||||
|
//选择榜单列表内容的跳转方式
|
||||||
|
linkOpenTypeDesc: 'Select the jump method for the list content',
|
||||||
|
//杂项设置
|
||||||
|
miscellaneous: 'Miscellaneous Settings',
|
||||||
|
//重置所有数据
|
||||||
|
reset: 'Reset All Data',
|
||||||
|
//重置所有数据,你的自定义设置都将会丢失
|
||||||
|
resetDesc: 'Reset all data, your custom settings will be lost',
|
||||||
|
//确认重置所有数据?你的自定义设置都将会丢失!
|
||||||
|
resetConfirm: 'Confirm reset all data? Your custom settings will be lost!',
|
||||||
|
// 当前窗口
|
||||||
|
currentWindow: 'Current Window',
|
||||||
|
//新窗口
|
||||||
|
newWindow: 'New Window',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
26
lang/tw.ts
26
lang/tw.ts
@ -6,6 +6,10 @@ export default defineI18nLocale(async locale => {
|
|||||||
common: {
|
common: {
|
||||||
actions: {
|
actions: {
|
||||||
delete: '刪除',
|
delete: '刪除',
|
||||||
|
//重置
|
||||||
|
reset: '重置',
|
||||||
|
//確定
|
||||||
|
confirm: '確定',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
whois: {
|
whois: {
|
||||||
@ -127,6 +131,28 @@ export default defineI18nLocale(async locale => {
|
|||||||
language: '語言設定',
|
language: '語言設定',
|
||||||
//切換DNS伺服器
|
//切換DNS伺服器
|
||||||
dnsChange: '切換DNS伺服器',
|
dnsChange: '切換DNS伺服器',
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
//全域性設定
|
||||||
|
title: '全域性設定',
|
||||||
|
//歷史記錄保留
|
||||||
|
history: '歷史記錄保留',
|
||||||
|
//連結跳轉方式
|
||||||
|
linkOpenType: '連結跳轉方式',
|
||||||
|
//選擇榜單列表內容的跳轉方式
|
||||||
|
linkOpenTypeDesc: '選擇榜單列表內容的跳轉方式',
|
||||||
|
//雜項設定
|
||||||
|
miscellaneous: '雜項設定',
|
||||||
|
//重置所有資料
|
||||||
|
reset: '重置所有資料',
|
||||||
|
//重置所有資料,你的自定義設定都將會丟失
|
||||||
|
resetDesc: '重置所有資料,你的自定義設定都將會丟失',
|
||||||
|
//確認重置所有資料?你的自定義設定都將會丟失!
|
||||||
|
resetConfirm: '確認重置所有資料?你的自定義設定都將會丟失!',
|
||||||
|
// 當前視窗
|
||||||
|
currentWindow: '當前視窗',
|
||||||
|
// 新視窗
|
||||||
|
newWindow: '新視窗',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
26
lang/zh.ts
26
lang/zh.ts
@ -7,6 +7,10 @@ export default defineI18nLocale(async locale => {
|
|||||||
actions: {
|
actions: {
|
||||||
//删除
|
//删除
|
||||||
delete: '删除',
|
delete: '删除',
|
||||||
|
//重置
|
||||||
|
reset: '重置',
|
||||||
|
//确定
|
||||||
|
confirm: '确定',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
whois:{
|
whois:{
|
||||||
@ -133,6 +137,28 @@ export default defineI18nLocale(async locale => {
|
|||||||
language: '切换语言',
|
language: '切换语言',
|
||||||
//dnsChange
|
//dnsChange
|
||||||
dnsChange: '切换DNS服务器',
|
dnsChange: '切换DNS服务器',
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
//全局设置
|
||||||
|
title: '全局设置',
|
||||||
|
//历史记录保留
|
||||||
|
history: '历史记录保留',
|
||||||
|
//链接跳转方式
|
||||||
|
linkOpenType: '链接跳转方式',
|
||||||
|
//选择榜单列表内容的跳转方式
|
||||||
|
linkOpenTypeDesc: '选择榜单列表内容的跳转方式',
|
||||||
|
//杂项设置
|
||||||
|
miscellaneous: '杂项设置',
|
||||||
|
//重置所有数据
|
||||||
|
reset: '重置所有数据',
|
||||||
|
//重置所有数据,你的自定义设置都将会丢失
|
||||||
|
resetDesc: '重置所有数据,你的自定义设置都将会丢失',
|
||||||
|
//确认重置所有数据?你的自定义设置都将会丢失!
|
||||||
|
resetConfirm: '确认重置所有数据?你的自定义设置都将会丢失!',
|
||||||
|
// 当前窗口
|
||||||
|
currentWindow: '当前窗口',
|
||||||
|
// 新窗口
|
||||||
|
newWindow: '新窗口',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -14,7 +14,7 @@ const domainData = domain.replace(/_/g, '.')
|
|||||||
const timeStore = useTimeStore()
|
const timeStore = useTimeStore()
|
||||||
const styleStore = useStyleStore()
|
const styleStore = useStyleStore()
|
||||||
const localePath = useLocalePath()
|
const localePath = useLocalePath()
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
styleStore.setIsPage(true)
|
styleStore.setIsPage(true)
|
||||||
|
|
||||||
const {data, pending, error, refresh} = await useAsyncData(
|
const {data, pending, error, refresh} = await useAsyncData(
|
||||||
@ -28,7 +28,7 @@ const {data, pending, error, refresh} = await useAsyncData(
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!error.value) {
|
if (!error.value && settingsStore.getHistory) {
|
||||||
styleStore.addOrUpdateHistory(
|
styleStore.addOrUpdateHistory(
|
||||||
{
|
{
|
||||||
id: domainData,
|
id: domainData,
|
||||||
|
@ -1,9 +1,107 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import {useStyleStore} from "~/stores/style";
|
||||||
|
import {useSettingsStore} from "~/stores/settings";
|
||||||
|
|
||||||
|
const styleStore = useStyleStore()
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
const {t} = useI18n()
|
||||||
|
const timeStore = useTimeStore()
|
||||||
|
styleStore.setIsPage(true)
|
||||||
|
|
||||||
|
const {isHistory} = storeToRefs(settingsStore)
|
||||||
|
const isOpen = ref(false)
|
||||||
|
|
||||||
|
|
||||||
|
const handleReset = async () => {
|
||||||
|
settingsStore.setHistory(true)
|
||||||
|
settingsStore.setLinkOpenType('currentWindow')
|
||||||
|
styleStore.setHistory([])
|
||||||
|
timeStore.setTimeZones('UTC+8')
|
||||||
|
timeStore.setDnsServer('')
|
||||||
|
await refreshNuxtData()
|
||||||
|
isOpen.value = false
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<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>
|
||||||
|
</UCard>
|
||||||
|
</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>
|
||||||
|
</div>
|
||||||
|
</UCard>
|
||||||
|
</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>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -14,6 +14,7 @@ const domainData = domain.replace(/_/g, '.')
|
|||||||
const showRawData = ref(false);
|
const showRawData = ref(false);
|
||||||
const timeStore = useTimeStore()
|
const timeStore = useTimeStore()
|
||||||
const styleStore = useStyleStore()
|
const styleStore = useStyleStore()
|
||||||
|
const settingsStore = useSettingsStore()
|
||||||
|
|
||||||
const localePath = useLocalePath()
|
const localePath = useLocalePath()
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ const {data, pending, error, refresh} = await useAsyncData(
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!error.value) {
|
if (!error.value && settingsStore.getHistory) {
|
||||||
styleStore.addOrUpdateHistory(
|
styleStore.addOrUpdateHistory(
|
||||||
{
|
{
|
||||||
id: domainData,
|
id: domainData,
|
||||||
|
28
stores/settings.ts
Normal file
28
stores/settings.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
|
||||||
|
export const useSettingsStore = defineStore('settings', {
|
||||||
|
state: () => {
|
||||||
|
const {t} = useI18n()
|
||||||
|
return {
|
||||||
|
isHistory: true,
|
||||||
|
linkOpenType: 'currentWindow',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
setHistory(value: boolean) {
|
||||||
|
this.isHistory = value
|
||||||
|
},
|
||||||
|
setLinkOpenType(value: string ) {
|
||||||
|
this.linkOpenType = value
|
||||||
|
},
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
getHistory: (state) => state.isHistory,
|
||||||
|
},
|
||||||
|
persist: {
|
||||||
|
storage: persistedState.cookiesWithOptions({
|
||||||
|
sameSite: 'strict',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
Loading…
x
Reference in New Issue
Block a user