198 lines
7.0 KiB
Vue
198 lines
7.0 KiB
Vue
<template>
|
|
<div class="min-h-screen bg-main">
|
|
<!-- 导航栏 -->
|
|
<nav class="fixed top-0 left-0 w-full bg-card border-b border-opacity-15 border-primary-500 shadow-lg" style="z-index: 999;">
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div class="flex justify-between items-center h-16">
|
|
<!-- 返回按钮和标题 -->
|
|
<div class="flex items-center space-x-4">
|
|
<button
|
|
@click="goBack"
|
|
class="p-2 rounded-lg bg-card hover:bg-block-hover text-secondary hover:text-primary transition-all duration-200"
|
|
title="返回首页"
|
|
>
|
|
<FontAwesomeIcon :icon="['fas', 'arrow-left']" class="w-5 h-5" />
|
|
</button>
|
|
|
|
<div class="flex items-center space-x-3">
|
|
<div v-if="currentTool" class="icon-container">
|
|
<FontAwesomeIcon
|
|
:icon="currentTool.icon"
|
|
class="text-2xl text-primary-light"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<h1 class="text-xl font-bold text-primary">
|
|
{{ toolTitle }}
|
|
</h1>
|
|
<p class="text-sm text-secondary hidden sm:block">
|
|
{{ toolDescription }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 主题和语言切换 -->
|
|
<div class="flex items-center space-x-2">
|
|
<ThemeToggle />
|
|
<LanguageToggle />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- 工具内容区 -->
|
|
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 pt-24">
|
|
<div v-if="isLoading" class="text-center py-16">
|
|
<FontAwesomeIcon :icon="['fas', 'spinner']" class="text-4xl text-primary animate-spin mb-4" />
|
|
<p class="text-secondary">{{ t('common.loading') }}</p>
|
|
</div>
|
|
|
|
<div v-else-if="toolNotFound" class="text-center py-16">
|
|
<FontAwesomeIcon :icon="['fas', 'search']" class="text-6xl text-tertiary mb-4" />
|
|
<h2 class="text-2xl font-semibold text-secondary mb-2">工具未找到</h2>
|
|
<p class="text-tertiary mb-6">您访问的工具不存在或暂未实现</p>
|
|
<button
|
|
@click="goBack"
|
|
class="btn-primary"
|
|
>
|
|
返回首页
|
|
</button>
|
|
</div>
|
|
|
|
<!-- 动态工具组件 -->
|
|
<component
|
|
v-else-if="ToolComponent"
|
|
:is="ToolComponent"
|
|
class="animate-fadeIn"
|
|
/>
|
|
</main>
|
|
|
|
<!-- 返回顶部按钮 -->
|
|
<BackToTop />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, computed, onMounted, defineAsyncComponent, watch } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { useLanguage } from '@/composables/useLanguage'
|
|
import tools from '@/config/tools'
|
|
import type { Tool } from '@/types/tools'
|
|
|
|
// 组件导入
|
|
import ThemeToggle from '@/components/ThemeToggle.vue'
|
|
import LanguageToggle from '@/components/LanguageToggle.vue'
|
|
import BackToTop from '@/components/BackToTop.vue'
|
|
|
|
interface Props {
|
|
toolCode: string
|
|
}
|
|
|
|
const props = defineProps<Props>()
|
|
const router = useRouter()
|
|
const { t } = useLanguage()
|
|
|
|
// 响应式状态
|
|
const isLoading = ref(true)
|
|
const toolNotFound = ref(false)
|
|
const ToolComponent = ref<any>(null)
|
|
|
|
// 当前工具信息
|
|
const currentTool = computed((): Tool | undefined => {
|
|
return tools.find(tool => tool.code === props.toolCode)
|
|
})
|
|
|
|
// 工具标题
|
|
const toolTitle = computed(() => {
|
|
if (!currentTool.value) return '未知工具'
|
|
return t(`tools.${currentTool.value.code}.title`) || currentTool.value.code
|
|
})
|
|
|
|
// 工具描述
|
|
const toolDescription = computed(() => {
|
|
if (!currentTool.value) return ''
|
|
return t(`tools.${currentTool.value.code}.description`) || ''
|
|
})
|
|
|
|
// 返回首页
|
|
const goBack = () => {
|
|
router.push('/')
|
|
}
|
|
|
|
// 工具组件映射表
|
|
const toolComponentMap: Record<string, () => Promise<any>> = {
|
|
json_formatter: () => import('@/components/tools/JsonFormatter.vue'),
|
|
timestamp_converter: () => import('@/components/tools/TimestampConverter.vue'),
|
|
encoding_converter: () => import('@/components/tools/EncodingConverter.vue'),
|
|
regex_tester: () => import('@/components/tools/RegexTester.vue'),
|
|
crypto_tools: () => import('@/components/tools/CryptoTools.vue'),
|
|
color_tools: () => import('@/components/tools/ColorTools.vue'),
|
|
code_formatter: () => import('@/components/tools/CodeFormatter.vue'),
|
|
json_editor: () => import('@/components/tools/JsonEditor.vue'),
|
|
json_converter: () => import('@/components/tools/JsonConverter.vue'),
|
|
url_encoder: () => import('@/components/tools/UrlEncoder.vue'),
|
|
unicode_converter: () => import('@/components/tools/UnicodeConverter.vue'),
|
|
jwt_decoder: () => import('@/components/tools/JwtDecoder.vue'),
|
|
ip_lookup: () => import('@/components/tools/IpLookup.vue'),
|
|
date_calculator: () => import('@/components/tools/DateCalculator.vue'),
|
|
timezone_converter: () => import('@/components/tools/TimezoneConverter.vue'),
|
|
text_counter: () => import('@/components/tools/TextCounter.vue'),
|
|
text_space_stripper: () => import('@/components/tools/TextSpaceStripper.vue'),
|
|
html_markdown_converter: () => import('@/components/tools/HtmlMarkdownConverter.vue'),
|
|
image_compressor: () => import('@/components/tools/ImageCompressor.vue'),
|
|
qrcode_generator: () => import('@/components/tools/QrcodeGenerator.vue'),
|
|
css_gradient_generator: () => import('@/components/tools/CssGradientGenerator.vue'),
|
|
number_base_converter: () => import('@/components/tools/NumberBaseConverter.vue'),
|
|
yml_properties_converter: () => import('@/components/tools/YmlPropertiesConverter.vue'),
|
|
base64_to_image: () => import('@/components/tools/Base64ToImage.vue'),
|
|
image_watermark: () => import('@/components/tools/ImageWatermark.vue'),
|
|
image_to_ico: () => import('@/components/tools/ImageToIco.vue'),
|
|
cron_generator: () => import('@/components/tools/CronGenerator.vue'),
|
|
http_tester: () => import('@/components/tools/HttpTester.vue'),
|
|
chrome_bookmark_recovery: () => import('@/components/tools/ChromeBookmarkRecovery.vue'),
|
|
seal_generator: () => import('@/components/tools/SealGenerator.vue')
|
|
}
|
|
|
|
// 加载工具组件
|
|
const loadToolComponent = async () => {
|
|
try {
|
|
isLoading.value = true
|
|
toolNotFound.value = false
|
|
|
|
// 检查工具是否存在
|
|
if (!currentTool.value) {
|
|
toolNotFound.value = true
|
|
isLoading.value = false
|
|
return
|
|
}
|
|
|
|
// 检查组件是否存在
|
|
const componentLoader = toolComponentMap[props.toolCode]
|
|
if (!componentLoader) {
|
|
toolNotFound.value = true
|
|
isLoading.value = false
|
|
return
|
|
}
|
|
|
|
// 动态加载组件
|
|
const component = defineAsyncComponent(componentLoader)
|
|
ToolComponent.value = component
|
|
|
|
isLoading.value = false
|
|
} catch (error) {
|
|
console.error('加载工具组件失败:', error)
|
|
toolNotFound.value = true
|
|
isLoading.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadToolComponent()
|
|
})
|
|
|
|
// 监听路由变化
|
|
watch(() => props.toolCode, () => {
|
|
loadToolComponent()
|
|
})
|
|
</script> |