🚩 网站设置功能完成

This commit is contained in:
7836246 2024-03-03 18:29:15 +08:00
parent 263ad0a4df
commit c29c6790a1
12 changed files with 313 additions and 8 deletions

1
.dockerignore Normal file
View File

@ -0,0 +1 @@
node_modules

43
Dockerfile Normal file
View 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"]

View File

@ -48,6 +48,8 @@ yarn
pnpm dev
# Nuxt-Whois
# Nuxt-Whois
# Nuxt-Whois
```
# 免责声明
本项目开源仅供学习使用,不得用于任何违法用途,否则后果自负,与本人无关。使用请保留项目地址谢谢。

View 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>

View File

@ -1,5 +1,6 @@
<script setup lang="ts">
const timeStore = useTimeStore()
const settingsStore = useSettingsStore()
const emit = defineEmits(['action'])
const handleActionFromDnsList = (urlParam:string) => {
@ -17,7 +18,9 @@ const {t} = useI18n()
<CommonDomainList />
</UTooltip>
<UTooltip :text="t('popper.history')" :popper="{ placement: 'top' }">
<UTooltip
v-if="settingsStore.getHistory"
:text="t('popper.history')" :popper="{ placement: 'top' }">
<CommonHistory />
</UTooltip>

View File

@ -6,6 +6,8 @@ export default defineI18nLocale(async locale => {
common: {
actions: {
delete: 'Delete',
reset: 'Reset',
confirm: 'Confirm',
}
},
whois:{
@ -123,6 +125,28 @@ export default defineI18nLocale(async locale => {
theme: 'Theme',
language: 'Language',
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',
},
}
})

View File

@ -6,6 +6,10 @@ export default defineI18nLocale(async locale => {
common: {
actions: {
delete: '刪除',
//重置
reset: '重置',
//確定
confirm: '確定',
}
},
whois: {
@ -127,6 +131,28 @@ export default defineI18nLocale(async locale => {
language: '語言設定',
//切換DNS伺服器
dnsChange: '切換DNS伺服器',
},
settings: {
//全域性設定
title: '全域性設定',
//歷史記錄保留
history: '歷史記錄保留',
//連結跳轉方式
linkOpenType: '連結跳轉方式',
//選擇榜單列表內容的跳轉方式
linkOpenTypeDesc: '選擇榜單列表內容的跳轉方式',
//雜項設定
miscellaneous: '雜項設定',
//重置所有資料
reset: '重置所有資料',
//重置所有資料,你的自定義設定都將會丟失
resetDesc: '重置所有資料,你的自定義設定都將會丟失',
//確認重置所有資料?你的自定義設定都將會丟失!
resetConfirm: '確認重置所有資料?你的自定義設定都將會丟失!',
// 當前視窗
currentWindow: '當前視窗',
// 新視窗
newWindow: '新視窗',
}
}
})

View File

@ -7,6 +7,10 @@ export default defineI18nLocale(async locale => {
actions: {
//删除
delete: '删除',
//重置
reset: '重置',
//确定
confirm: '确定',
}
},
whois:{
@ -133,6 +137,28 @@ export default defineI18nLocale(async locale => {
language: '切换语言',
//dnsChange
dnsChange: '切换DNS服务器',
},
settings: {
//全局设置
title: '全局设置',
//历史记录保留
history: '历史记录保留',
//链接跳转方式
linkOpenType: '链接跳转方式',
//选择榜单列表内容的跳转方式
linkOpenTypeDesc: '选择榜单列表内容的跳转方式',
//杂项设置
miscellaneous: '杂项设置',
//重置所有数据
reset: '重置所有数据',
//重置所有数据,你的自定义设置都将会丢失
resetDesc: '重置所有数据,你的自定义设置都将会丢失',
//确认重置所有数据?你的自定义设置都将会丢失!
resetConfirm: '确认重置所有数据?你的自定义设置都将会丢失!',
// 当前窗口
currentWindow: '当前窗口',
// 新窗口
newWindow: '新窗口',
}
}
})

View File

@ -14,7 +14,7 @@ const domainData = domain.replace(/_/g, '.')
const timeStore = useTimeStore()
const styleStore = useStyleStore()
const localePath = useLocalePath()
const settingsStore = useSettingsStore()
styleStore.setIsPage(true)
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(
{
id: domainData,

View File

@ -1,9 +1,107 @@
<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>
<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>
<style scoped>

View File

@ -14,6 +14,7 @@ const domainData = domain.replace(/_/g, '.')
const showRawData = ref(false);
const timeStore = useTimeStore()
const styleStore = useStyleStore()
const settingsStore = useSettingsStore()
const localePath = useLocalePath()
@ -25,7 +26,7 @@ const {data, pending, error, refresh} = await useAsyncData(
})
)
if (!error.value) {
if (!error.value && settingsStore.getHistory) {
styleStore.addOrUpdateHistory(
{
id: domainData,

28
stores/settings.ts Normal file
View 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',
}),
},
})