forked from zguiy/utils
		
	
		
			
				
	
	
		
			654 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			654 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
<template>
 | 
						|
  <div class="space-y-6">
 | 
						|
    <!-- 工具栏 -->
 | 
						|
    <div class="card p-4">
 | 
						|
      <div class="flex flex-wrap gap-2">
 | 
						|
        <button
 | 
						|
          @click="updateCurrentTime"
 | 
						|
          class="btn-primary"
 | 
						|
        >
 | 
						|
          <FontAwesomeIcon :icon="['fas', 'sync']" class="mr-2" />
 | 
						|
          刷新时间
 | 
						|
        </button>
 | 
						|
        
 | 
						|
        <button
 | 
						|
          @click="copyResult"
 | 
						|
          :disabled="!selectedTime"
 | 
						|
          class="btn-secondary"
 | 
						|
        >
 | 
						|
          <FontAwesomeIcon 
 | 
						|
            :icon="copied ? ['fas', 'check'] : ['fas', 'copy']" 
 | 
						|
            :class="['mr-2', copied && 'text-success']"
 | 
						|
          />
 | 
						|
          复制时间
 | 
						|
        </button>
 | 
						|
        
 | 
						|
        <button
 | 
						|
          @click="resetToNow"
 | 
						|
          class="btn-secondary"
 | 
						|
        >
 | 
						|
          <FontAwesomeIcon :icon="['fas', 'clock']" class="mr-2" />
 | 
						|
          当前时间
 | 
						|
        </button>
 | 
						|
        
 | 
						|
        <button
 | 
						|
          @click="addCustomTimezone"
 | 
						|
          class="btn-secondary"
 | 
						|
        >
 | 
						|
          <FontAwesomeIcon :icon="['fas', 'plus']" class="mr-2" />
 | 
						|
          添加时区
 | 
						|
        </button>
 | 
						|
      </div>
 | 
						|
    </div>
 | 
						|
 | 
						|
    <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
 | 
						|
      <!-- 输入区域 -->
 | 
						|
      <div class="space-y-4">
 | 
						|
        <!-- 时间输入 -->
 | 
						|
        <div class="card p-4">
 | 
						|
          <h3 class="text-lg font-semibold text-primary mb-3">输入时间</h3>
 | 
						|
          
 | 
						|
          <div class="space-y-4">
 | 
						|
            <!-- 日期时间输入 -->
 | 
						|
            <div>
 | 
						|
              <label class="block text-sm font-medium text-secondary mb-2">选择日期时间</label>
 | 
						|
              <input
 | 
						|
                v-model="inputDateTime"
 | 
						|
                type="datetime-local"
 | 
						|
                class="input-field"
 | 
						|
                @change="convertTimezones"
 | 
						|
              >
 | 
						|
            </div>
 | 
						|
            
 | 
						|
            <!-- 源时区 -->
 | 
						|
            <div>
 | 
						|
              <label class="block text-sm font-medium text-secondary mb-2">源时区</label>
 | 
						|
              <select v-model="sourceTimezone" class="select-field" @change="convertTimezones">
 | 
						|
                <option v-for="timezone in commonTimezones" :key="timezone.value" :value="timezone.value">
 | 
						|
                  {{ timezone.label }}
 | 
						|
                </option>
 | 
						|
              </select>
 | 
						|
            </div>
 | 
						|
            
 | 
						|
            <!-- 快速时间选择 -->
 | 
						|
            <div>
 | 
						|
              <label class="block text-sm font-medium text-secondary mb-2">快速选择</label>
 | 
						|
              <div class="grid grid-cols-2 gap-2">
 | 
						|
                <button
 | 
						|
                  v-for="quickTime in quickTimes"
 | 
						|
                  :key="quickTime.label"
 | 
						|
                  @click="setQuickTime(quickTime)"
 | 
						|
                  class="btn-sm btn-secondary text-xs"
 | 
						|
                >
 | 
						|
                  {{ quickTime.label }}
 | 
						|
                </button>
 | 
						|
              </div>
 | 
						|
            </div>
 | 
						|
          </div>
 | 
						|
        </div>
 | 
						|
 | 
						|
        <!-- 常用时区 -->
 | 
						|
        <div class="card p-4">
 | 
						|
          <h3 class="text-lg font-semibold text-primary mb-3">常用时区</h3>
 | 
						|
          
 | 
						|
          <div class="space-y-2">
 | 
						|
            <div
 | 
						|
              v-for="timezone in commonTimezones.slice(0, 8)"
 | 
						|
              :key="timezone.value"
 | 
						|
              class="flex items-center justify-between p-2 bg-block rounded"
 | 
						|
            >
 | 
						|
              <div>
 | 
						|
                <div class="font-medium text-primary">{{ timezone.name }}</div>
 | 
						|
                <div class="text-sm text-secondary">{{ timezone.value }}</div>
 | 
						|
              </div>
 | 
						|
              <div class="text-sm text-primary font-mono">
 | 
						|
                {{ getTimezoneTime(timezone.value) }}
 | 
						|
              </div>
 | 
						|
            </div>
 | 
						|
          </div>
 | 
						|
        </div>
 | 
						|
 | 
						|
        <!-- 时区偏移计算 -->
 | 
						|
        <div class="card p-4">
 | 
						|
          <h3 class="text-lg font-semibold text-primary mb-3">时差计算</h3>
 | 
						|
          
 | 
						|
          <div class="space-y-3">
 | 
						|
            <div class="flex space-x-2">
 | 
						|
              <select v-model="timezone1" class="select-field flex-1">
 | 
						|
                <option v-for="timezone in commonTimezones" :key="timezone.value" :value="timezone.value">
 | 
						|
                  {{ timezone.name }}
 | 
						|
                </option>
 | 
						|
              </select>
 | 
						|
              <span class="self-center text-secondary">vs</span>
 | 
						|
              <select v-model="timezone2" class="select-field flex-1">
 | 
						|
                <option v-for="timezone in commonTimezones" :key="timezone.value" :value="timezone.value">
 | 
						|
                  {{ timezone.name }}
 | 
						|
                </option>
 | 
						|
              </select>
 | 
						|
            </div>
 | 
						|
            
 | 
						|
            <div class="bg-block rounded p-3 text-center">
 | 
						|
              <div class="text-sm text-secondary">时差</div>
 | 
						|
              <div class="text-lg font-medium text-primary">{{ getTimeDifference() }}</div>
 | 
						|
            </div>
 | 
						|
          </div>
 | 
						|
        </div>
 | 
						|
      </div>
 | 
						|
 | 
						|
      <!-- 结果区域 -->
 | 
						|
      <div class="lg:col-span-2 space-y-4">
 | 
						|
        <!-- 世界时钟 -->
 | 
						|
        <div class="card p-4">
 | 
						|
          <h3 class="text-lg font-semibold text-primary mb-3">世界时钟</h3>
 | 
						|
          
 | 
						|
          <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
 | 
						|
            <div
 | 
						|
              v-for="timezone in displayTimezones"
 | 
						|
              :key="timezone.value"
 | 
						|
              class="bg-block rounded-lg p-4"
 | 
						|
            >
 | 
						|
              <div class="flex items-center justify-between mb-2">
 | 
						|
                <div>
 | 
						|
                  <div class="font-medium text-primary">{{ timezone.name }}</div>
 | 
						|
                  <div class="text-xs text-tertiary">{{ timezone.value }}</div>
 | 
						|
                </div>
 | 
						|
                <button
 | 
						|
                  @click="removeTimezone(timezone.value)"
 | 
						|
                  class="text-error hover:bg-error hover:bg-opacity-10 p-1 rounded"
 | 
						|
                >
 | 
						|
                  <FontAwesomeIcon :icon="['fas', 'times']" />
 | 
						|
                </button>
 | 
						|
              </div>
 | 
						|
              
 | 
						|
              <div class="text-center">
 | 
						|
                <div class="text-2xl font-mono font-bold text-primary mb-1">
 | 
						|
                  {{ getTimezoneTime(timezone.value, 'HH:mm:ss') }}
 | 
						|
                </div>
 | 
						|
                <div class="text-sm text-secondary">
 | 
						|
                  {{ getTimezoneTime(timezone.value, 'yyyy-MM-dd EEEE') }}
 | 
						|
                </div>
 | 
						|
                <div class="text-xs text-tertiary mt-1">
 | 
						|
                  UTC{{ getTimezoneOffset(timezone.value) }}
 | 
						|
                </div>
 | 
						|
              </div>
 | 
						|
            </div>
 | 
						|
          </div>
 | 
						|
        </div>
 | 
						|
 | 
						|
        <!-- 转换结果 -->
 | 
						|
        <div v-if="conversionResults.length > 0" class="card p-4">
 | 
						|
          <h3 class="text-lg font-semibold text-primary mb-3">转换结果</h3>
 | 
						|
          
 | 
						|
          <div class="space-y-3">
 | 
						|
            <div
 | 
						|
              v-for="result in conversionResults"
 | 
						|
              :key="result.timezone"
 | 
						|
              class="flex items-center justify-between p-3 bg-block rounded-lg"
 | 
						|
            >
 | 
						|
              <div class="flex-1">
 | 
						|
                <div class="font-medium text-primary">{{ result.name }}</div>
 | 
						|
                <div class="text-sm text-secondary">{{ result.timezone }}</div>
 | 
						|
              </div>
 | 
						|
              
 | 
						|
              <div class="text-right">
 | 
						|
                <div class="text-lg font-mono text-primary">{{ result.time }}</div>
 | 
						|
                <div class="text-xs text-secondary">{{ result.date }}</div>
 | 
						|
              </div>
 | 
						|
              
 | 
						|
              <button
 | 
						|
                @click="copySpecificTime(result)"
 | 
						|
                class="ml-3 p-2 text-secondary hover:text-primary transition-colors"
 | 
						|
                :title="'复制 ' + result.name + ' 时间'"
 | 
						|
              >
 | 
						|
                <FontAwesomeIcon :icon="['fas', 'copy']" />
 | 
						|
              </button>
 | 
						|
            </div>
 | 
						|
          </div>
 | 
						|
        </div>
 | 
						|
 | 
						|
        <!-- 时区信息 -->
 | 
						|
        <div class="card p-4">
 | 
						|
          <h3 class="text-lg font-semibold text-primary mb-3">时区信息</h3>
 | 
						|
          
 | 
						|
          <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
 | 
						|
            <!-- UTC时间 -->
 | 
						|
            <div class="bg-block rounded-lg p-4">
 | 
						|
              <div class="text-sm text-secondary mb-1">协调世界时 (UTC)</div>
 | 
						|
              <div class="text-xl font-mono text-primary">{{ utcTime }}</div>
 | 
						|
              <div class="text-xs text-tertiary mt-1">Coordinated Universal Time</div>
 | 
						|
            </div>
 | 
						|
            
 | 
						|
            <!-- 本地时间 -->
 | 
						|
            <div class="bg-block rounded-lg p-4">
 | 
						|
              <div class="text-sm text-secondary mb-1">本地时间</div>
 | 
						|
              <div class="text-xl font-mono text-primary">{{ localTime }}</div>
 | 
						|
              <div class="text-xs text-tertiary mt-1">{{ localTimezone }}</div>
 | 
						|
            </div>
 | 
						|
            
 | 
						|
            <!-- Unix时间戳 -->
 | 
						|
            <div class="bg-block rounded-lg p-4">
 | 
						|
              <div class="text-sm text-secondary mb-1">Unix时间戳</div>
 | 
						|
              <div class="text-lg font-mono text-primary">{{ unixTimestamp }}</div>
 | 
						|
              <div class="text-xs text-tertiary mt-1">秒 / 毫秒</div>
 | 
						|
            </div>
 | 
						|
            
 | 
						|
            <!-- ISO 8601 -->
 | 
						|
            <div class="bg-block rounded-lg p-4">
 | 
						|
              <div class="text-sm text-secondary mb-1">ISO 8601</div>
 | 
						|
              <div class="text-sm font-mono text-primary break-all">{{ isoTime }}</div>
 | 
						|
              <div class="text-xs text-tertiary mt-1">国际标准时间格式</div>
 | 
						|
            </div>
 | 
						|
          </div>
 | 
						|
        </div>
 | 
						|
 | 
						|
        <!-- 日程助手 -->
 | 
						|
        <div class="card p-4">
 | 
						|
          <h3 class="text-lg font-semibold text-primary mb-3">会议时间建议</h3>
 | 
						|
          
 | 
						|
          <div class="space-y-3">
 | 
						|
            <div class="flex space-x-2">
 | 
						|
              <select v-model="meetingTimezone1" class="select-field flex-1">
 | 
						|
                <option v-for="timezone in commonTimezones" :key="timezone.value" :value="timezone.value">
 | 
						|
                  {{ timezone.name }}
 | 
						|
                </option>
 | 
						|
              </select>
 | 
						|
              <select v-model="meetingTimezone2" class="select-field flex-1">
 | 
						|
                <option v-for="timezone in commonTimezones" :key="timezone.value" :value="timezone.value">
 | 
						|
                  {{ timezone.name }}
 | 
						|
                </option>
 | 
						|
              </select>
 | 
						|
            </div>
 | 
						|
            
 | 
						|
            <div class="bg-block rounded p-4">
 | 
						|
              <div class="text-sm text-secondary mb-2">最佳会议时间段 (工作时间 9:00-18:00)</div>
 | 
						|
              <div class="space-y-2">
 | 
						|
                <div
 | 
						|
                  v-for="suggestion in getMeetingSuggestions()"
 | 
						|
                  :key="suggestion.time"
 | 
						|
                  class="flex justify-between items-center text-sm"
 | 
						|
                >
 | 
						|
                  <span class="text-primary">{{ suggestion.time }}</span>
 | 
						|
                  <span class="text-secondary">{{ suggestion.zones }}</span>
 | 
						|
                </div>
 | 
						|
              </div>
 | 
						|
            </div>
 | 
						|
          </div>
 | 
						|
        </div>
 | 
						|
      </div>
 | 
						|
    </div>
 | 
						|
 | 
						|
    <!-- 状态消息 -->
 | 
						|
    <div v-if="statusMessage" class="card p-4">
 | 
						|
      <div :class="[
 | 
						|
        'flex items-center space-x-2',
 | 
						|
        statusType === 'success' ? 'text-success' : 'text-error'
 | 
						|
      ]">
 | 
						|
        <FontAwesomeIcon 
 | 
						|
          :icon="statusType === 'success' ? ['fas', 'check'] : ['fas', 'times']" 
 | 
						|
        />
 | 
						|
        <span>{{ statusMessage }}</span>
 | 
						|
      </div>
 | 
						|
    </div>
 | 
						|
  </div>
 | 
						|
</template>
 | 
						|
 | 
						|
<script setup lang="ts">
 | 
						|
import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
 | 
						|
import { useLanguage } from '@/composables/useLanguage'
 | 
						|
 | 
						|
const { t } = useLanguage()
 | 
						|
 | 
						|
// 响应式状态
 | 
						|
const inputDateTime = ref('')
 | 
						|
const sourceTimezone = ref('Asia/Shanghai')
 | 
						|
const selectedTime = ref('')
 | 
						|
const copied = ref(false)
 | 
						|
const statusMessage = ref('')
 | 
						|
const statusType = ref<'success' | 'error'>('success')
 | 
						|
const currentTime = ref(new Date())
 | 
						|
 | 
						|
// 时区比较
 | 
						|
const timezone1 = ref('Asia/Shanghai')
 | 
						|
const timezone2 = ref('America/New_York')
 | 
						|
 | 
						|
// 会议时间建议
 | 
						|
const meetingTimezone1 = ref('Asia/Shanghai')
 | 
						|
const meetingTimezone2 = ref('America/New_York')
 | 
						|
 | 
						|
// 显示的时区列表
 | 
						|
const displayTimezones = ref([
 | 
						|
  { name: '北京', value: 'Asia/Shanghai' },
 | 
						|
  { name: '纽约', value: 'America/New_York' },
 | 
						|
  { name: '伦敦', value: 'Europe/London' },
 | 
						|
  { name: '东京', value: 'Asia/Tokyo' }
 | 
						|
])
 | 
						|
 | 
						|
// 转换结果
 | 
						|
const conversionResults = ref<Array<{
 | 
						|
  name: string
 | 
						|
  timezone: string
 | 
						|
  time: string
 | 
						|
  date: string
 | 
						|
  fullTime: string
 | 
						|
}>>([])
 | 
						|
 | 
						|
// 常用时区
 | 
						|
const commonTimezones = [
 | 
						|
  { name: '北京 (CST)', value: 'Asia/Shanghai', label: 'Asia/Shanghai (UTC+8)' },
 | 
						|
  { name: '东京 (JST)', value: 'Asia/Tokyo', label: 'Asia/Tokyo (UTC+9)' },
 | 
						|
  { name: '首尔 (KST)', value: 'Asia/Seoul', label: 'Asia/Seoul (UTC+9)' },
 | 
						|
  { name: '新加坡 (SGT)', value: 'Asia/Singapore', label: 'Asia/Singapore (UTC+8)' },
 | 
						|
  { name: '香港 (HKT)', value: 'Asia/Hong_Kong', label: 'Asia/Hong_Kong (UTC+8)' },
 | 
						|
  { name: '悉尼 (AEDT)', value: 'Australia/Sydney', label: 'Australia/Sydney (UTC+11)' },
 | 
						|
  { name: '伦敦 (GMT)', value: 'Europe/London', label: 'Europe/London (UTC+0)' },
 | 
						|
  { name: '巴黎 (CET)', value: 'Europe/Paris', label: 'Europe/Paris (UTC+1)' },
 | 
						|
  { name: '莫斯科 (MSK)', value: 'Europe/Moscow', label: 'Europe/Moscow (UTC+3)' },
 | 
						|
  { name: '纽约 (EST)', value: 'America/New_York', label: 'America/New_York (UTC-5)' },
 | 
						|
  { name: '洛杉矶 (PST)', value: 'America/Los_Angeles', label: 'America/Los_Angeles (UTC-8)' },
 | 
						|
  { name: '芝加哥 (CST)', value: 'America/Chicago', label: 'America/Chicago (UTC-6)' },
 | 
						|
  { name: '丹佛 (MST)', value: 'America/Denver', label: 'America/Denver (UTC-7)' },
 | 
						|
  { name: 'UTC', value: 'UTC', label: 'UTC (UTC+0)' }
 | 
						|
]
 | 
						|
 | 
						|
// 快速时间选择
 | 
						|
const quickTimes = [
 | 
						|
  { label: '现在', offset: 0 },
 | 
						|
  { label: '1小时后', offset: 1 },
 | 
						|
  { label: '明天此时', offset: 24 },
 | 
						|
  { label: '下周此时', offset: 24 * 7 }
 | 
						|
]
 | 
						|
 | 
						|
// 计算属性
 | 
						|
const utcTime = computed(() => {
 | 
						|
  return formatTime(currentTime.value, 'UTC', 'yyyy-MM-dd HH:mm:ss')
 | 
						|
})
 | 
						|
 | 
						|
const localTime = computed(() => {
 | 
						|
  return formatTime(currentTime.value, Intl.DateTimeFormat().resolvedOptions().timeZone, 'yyyy-MM-dd HH:mm:ss')
 | 
						|
})
 | 
						|
 | 
						|
const localTimezone = computed(() => {
 | 
						|
  return Intl.DateTimeFormat().resolvedOptions().timeZone
 | 
						|
})
 | 
						|
 | 
						|
const unixTimestamp = computed(() => {
 | 
						|
  const seconds = Math.floor(currentTime.value.getTime() / 1000)
 | 
						|
  const milliseconds = currentTime.value.getTime()
 | 
						|
  return `${seconds} / ${milliseconds}`
 | 
						|
})
 | 
						|
 | 
						|
const isoTime = computed(() => {
 | 
						|
  return currentTime.value.toISOString()
 | 
						|
})
 | 
						|
 | 
						|
// 定时器
 | 
						|
let timeInterval: number | undefined
 | 
						|
 | 
						|
// 格式化时间
 | 
						|
const formatTime = (date: Date, timezone: string, format: string): string => {
 | 
						|
  try {
 | 
						|
    const options: Intl.DateTimeFormatOptions = {
 | 
						|
      timeZone: timezone
 | 
						|
    }
 | 
						|
    
 | 
						|
    if (format.includes('yyyy')) {
 | 
						|
      options.year = 'numeric'
 | 
						|
    }
 | 
						|
    if (format.includes('MM')) {
 | 
						|
      options.month = '2-digit'
 | 
						|
    }
 | 
						|
    if (format.includes('dd')) {
 | 
						|
      options.day = '2-digit'
 | 
						|
    }
 | 
						|
    if (format.includes('HH')) {
 | 
						|
      options.hour = '2-digit'
 | 
						|
      options.hour12 = false
 | 
						|
    }
 | 
						|
    if (format.includes('mm')) {
 | 
						|
      options.minute = '2-digit'
 | 
						|
    }
 | 
						|
    if (format.includes('ss')) {
 | 
						|
      options.second = '2-digit'
 | 
						|
    }
 | 
						|
    if (format.includes('EEEE')) {
 | 
						|
      options.weekday = 'long'
 | 
						|
    }
 | 
						|
    
 | 
						|
    const formatter = new Intl.DateTimeFormat('zh-CN', options)
 | 
						|
    
 | 
						|
    if (format === 'HH:mm:ss') {
 | 
						|
      return date.toLocaleTimeString('zh-CN', {
 | 
						|
        timeZone: timezone,
 | 
						|
        hour12: false,
 | 
						|
        hour: '2-digit',
 | 
						|
        minute: '2-digit',
 | 
						|
        second: '2-digit'
 | 
						|
      })
 | 
						|
    } else if (format === 'yyyy-MM-dd EEEE') {
 | 
						|
      return date.toLocaleDateString('zh-CN', {
 | 
						|
        timeZone: timezone,
 | 
						|
        year: 'numeric',
 | 
						|
        month: '2-digit',
 | 
						|
        day: '2-digit',
 | 
						|
        weekday: 'long'
 | 
						|
      })
 | 
						|
    } else {
 | 
						|
      return formatter.format(date)
 | 
						|
    }
 | 
						|
  } catch (error) {
 | 
						|
    return date.toISOString()
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// 获取时区时间
 | 
						|
const getTimezoneTime = (timezone: string, format: string = 'yyyy-MM-dd HH:mm:ss'): string => {
 | 
						|
  return formatTime(currentTime.value, timezone, format)
 | 
						|
}
 | 
						|
 | 
						|
// 获取时区偏移
 | 
						|
const getTimezoneOffset = (timezone: string): string => {
 | 
						|
  try {
 | 
						|
    const date = new Date()
 | 
						|
    const utc = date.getTime() + (date.getTimezoneOffset() * 60000)
 | 
						|
    const targetTime = new Date(utc + getTimezoneOffsetMinutes(timezone) * 60000)
 | 
						|
    const offset = getTimezoneOffsetMinutes(timezone) / 60
 | 
						|
    return offset >= 0 ? `+${offset}` : `${offset}`
 | 
						|
  } catch {
 | 
						|
    return '+0'
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// 获取时区偏移分钟数
 | 
						|
const getTimezoneOffsetMinutes = (timezone: string): number => {
 | 
						|
  try {
 | 
						|
    const date = new Date()
 | 
						|
    const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }))
 | 
						|
    const targetDate = new Date(date.toLocaleString('en-US', { timeZone: timezone }))
 | 
						|
    return (targetDate.getTime() - utcDate.getTime()) / (1000 * 60)
 | 
						|
  } catch {
 | 
						|
    return 0
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// 获取时差
 | 
						|
const getTimeDifference = (): string => {
 | 
						|
  const offset1 = getTimezoneOffsetMinutes(timezone1.value)
 | 
						|
  const offset2 = getTimezoneOffsetMinutes(timezone2.value)
 | 
						|
  const diffMinutes = Math.abs(offset1 - offset2)
 | 
						|
  const hours = Math.floor(diffMinutes / 60)
 | 
						|
  const minutes = diffMinutes % 60
 | 
						|
  
 | 
						|
  if (hours === 0) {
 | 
						|
    return `${minutes} 分钟`
 | 
						|
  } else if (minutes === 0) {
 | 
						|
    return `${hours} 小时`
 | 
						|
  } else {
 | 
						|
    return `${hours} 小时 ${minutes} 分钟`
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// 获取会议建议
 | 
						|
const getMeetingSuggestions = (): Array<{ time: string; zones: string }> => {
 | 
						|
  const suggestions = []
 | 
						|
  
 | 
						|
  for (let hour = 9; hour <= 18; hour++) {
 | 
						|
    const time1 = `${hour.toString().padStart(2, '0')}:00`
 | 
						|
    const date = new Date()
 | 
						|
    date.setHours(hour, 0, 0, 0)
 | 
						|
    
 | 
						|
    const time2 = formatTime(date, meetingTimezone2.value, 'HH:mm')
 | 
						|
    const hour2 = parseInt(time2.split(':')[0])
 | 
						|
    
 | 
						|
    if (hour2 >= 9 && hour2 <= 18) {
 | 
						|
      suggestions.push({
 | 
						|
        time: `${time1} - ${time2}`,
 | 
						|
        zones: `${getTimezoneName(meetingTimezone1.value)} - ${getTimezoneName(meetingTimezone2.value)}`
 | 
						|
      })
 | 
						|
    }
 | 
						|
  }
 | 
						|
  
 | 
						|
  return suggestions.slice(0, 3)
 | 
						|
}
 | 
						|
 | 
						|
// 获取时区名称
 | 
						|
const getTimezoneName = (timezone: string): string => {
 | 
						|
  const found = commonTimezones.find(tz => tz.value === timezone)
 | 
						|
  return found ? found.name : timezone
 | 
						|
}
 | 
						|
 | 
						|
// 设置快速时间
 | 
						|
const setQuickTime = (quickTime: any) => {
 | 
						|
  const date = new Date()
 | 
						|
  date.setHours(date.getHours() + quickTime.offset)
 | 
						|
  
 | 
						|
  const year = date.getFullYear()
 | 
						|
  const month = (date.getMonth() + 1).toString().padStart(2, '0')
 | 
						|
  const day = date.getDate().toString().padStart(2, '0')
 | 
						|
  const hours = date.getHours().toString().padStart(2, '0')
 | 
						|
  const minutes = date.getMinutes().toString().padStart(2, '0')
 | 
						|
  
 | 
						|
  inputDateTime.value = `${year}-${month}-${day}T${hours}:${minutes}`
 | 
						|
  convertTimezones()
 | 
						|
}
 | 
						|
 | 
						|
// 转换时区
 | 
						|
const convertTimezones = () => {
 | 
						|
  if (!inputDateTime.value) return
 | 
						|
  
 | 
						|
  const inputDate = new Date(inputDateTime.value)
 | 
						|
  if (isNaN(inputDate.getTime())) return
 | 
						|
  
 | 
						|
  conversionResults.value = displayTimezones.value.map(timezone => {
 | 
						|
    const time = formatTime(inputDate, timezone.value, 'HH:mm:ss')
 | 
						|
    const date = formatTime(inputDate, timezone.value, 'yyyy-MM-dd EEEE')
 | 
						|
    const fullTime = formatTime(inputDate, timezone.value, 'yyyy-MM-dd HH:mm:ss')
 | 
						|
    
 | 
						|
    return {
 | 
						|
      name: timezone.name,
 | 
						|
      timezone: timezone.value,
 | 
						|
      time,
 | 
						|
      date,
 | 
						|
      fullTime
 | 
						|
    }
 | 
						|
  })
 | 
						|
}
 | 
						|
 | 
						|
// 添加自定义时区
 | 
						|
const addCustomTimezone = () => {
 | 
						|
  const timezone = prompt('请输入时区标识符 (如: Asia/Shanghai):')
 | 
						|
  if (!timezone) return
 | 
						|
  
 | 
						|
  try {
 | 
						|
    // 验证时区是否有效
 | 
						|
    formatTime(new Date(), timezone, 'HH:mm:ss')
 | 
						|
    
 | 
						|
    const name = prompt('请输入时区显示名称:', timezone) || timezone
 | 
						|
    
 | 
						|
    displayTimezones.value.push({
 | 
						|
      name,
 | 
						|
      value: timezone
 | 
						|
    })
 | 
						|
    
 | 
						|
    convertTimezones()
 | 
						|
    showStatus('时区添加成功', 'success')
 | 
						|
  } catch (error) {
 | 
						|
    showStatus('无效的时区标识符', 'error')
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// 移除时区
 | 
						|
const removeTimezone = (timezone: string) => {
 | 
						|
  displayTimezones.value = displayTimezones.value.filter(tz => tz.value !== timezone)
 | 
						|
  convertTimezones()
 | 
						|
}
 | 
						|
 | 
						|
// 更新当前时间
 | 
						|
const updateCurrentTime = () => {
 | 
						|
  currentTime.value = new Date()
 | 
						|
}
 | 
						|
 | 
						|
// 重置到现在
 | 
						|
const resetToNow = () => {
 | 
						|
  const now = new Date()
 | 
						|
  const year = now.getFullYear()
 | 
						|
  const month = (now.getMonth() + 1).toString().padStart(2, '0')
 | 
						|
  const day = now.getDate().toString().padStart(2, '0')
 | 
						|
  const hours = now.getHours().toString().padStart(2, '0')
 | 
						|
  const minutes = now.getMinutes().toString().padStart(2, '0')
 | 
						|
  
 | 
						|
  inputDateTime.value = `${year}-${month}-${day}T${hours}:${minutes}`
 | 
						|
  convertTimezones()
 | 
						|
}
 | 
						|
 | 
						|
// 复制结果
 | 
						|
const copyResult = async () => {
 | 
						|
  if (!selectedTime.value) return
 | 
						|
  
 | 
						|
  try {
 | 
						|
    await navigator.clipboard.writeText(selectedTime.value)
 | 
						|
    copied.value = true
 | 
						|
    setTimeout(() => {
 | 
						|
      copied.value = false
 | 
						|
    }, 2000)
 | 
						|
  } catch (error) {
 | 
						|
    showStatus('复制失败', 'error')
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// 复制特定时间
 | 
						|
const copySpecificTime = async (result: any) => {
 | 
						|
  try {
 | 
						|
    await navigator.clipboard.writeText(result.fullTime)
 | 
						|
    showStatus(`已复制 ${result.name} 时间`, 'success')
 | 
						|
  } catch (error) {
 | 
						|
    showStatus('复制失败', 'error')
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// 显示状态消息
 | 
						|
const showStatus = (message: string, type: 'success' | 'error') => {
 | 
						|
  statusMessage.value = message
 | 
						|
  statusType.value = type
 | 
						|
  setTimeout(() => {
 | 
						|
    statusMessage.value = ''
 | 
						|
  }, 3000)
 | 
						|
}
 | 
						|
 | 
						|
// 组件挂载
 | 
						|
onMounted(() => {
 | 
						|
  resetToNow()
 | 
						|
  updateCurrentTime()
 | 
						|
  
 | 
						|
  // 每秒更新时间
 | 
						|
  timeInterval = setInterval(() => {
 | 
						|
    updateCurrentTime()
 | 
						|
  }, 1000)
 | 
						|
})
 | 
						|
 | 
						|
// 组件卸载
 | 
						|
onUnmounted(() => {
 | 
						|
  if (timeInterval) {
 | 
						|
    clearInterval(timeInterval)
 | 
						|
  }
 | 
						|
})
 | 
						|
</script>  |