forked from zguiy/utils
		
	
		
			
				
	
	
		
			197 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			197 lines
		
	
	
		
			6.9 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')
 | 
						|
}
 | 
						|
 | 
						|
// 加载工具组件
 | 
						|
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>  |