forked from zguiy/utils
		
	
		
			
				
	
	
		
			323 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			323 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
<template>
 | 
						|
  <div class="space-y-6">
 | 
						|
    <!-- 转换工具栏 -->
 | 
						|
    <div class="card p-4">
 | 
						|
      <div class="flex flex-wrap gap-2">
 | 
						|
        <button
 | 
						|
          @click="() => convertTo('unicode')"
 | 
						|
          class="btn-primary"
 | 
						|
        >
 | 
						|
          <FontAwesomeIcon :icon="['fas', 'arrow-right']" class="mr-2" />
 | 
						|
          转为Unicode编码
 | 
						|
        </button>
 | 
						|
        
 | 
						|
        <button
 | 
						|
          @click="() => convertTo('text')"
 | 
						|
          class="btn-secondary"
 | 
						|
        >
 | 
						|
          <FontAwesomeIcon :icon="['fas', 'arrow-left']" class="mr-2" />
 | 
						|
          解码为文本
 | 
						|
        </button>
 | 
						|
        
 | 
						|
        <button
 | 
						|
          @click="() => convertTo('hex')"
 | 
						|
          class="btn-secondary"
 | 
						|
        >
 | 
						|
          <FontAwesomeIcon :icon="['fas', 'hashtag']" class="mr-2" />
 | 
						|
          转为16进制
 | 
						|
        </button>
 | 
						|
        
 | 
						|
        <button
 | 
						|
          @click="clearAll"
 | 
						|
          class="btn-secondary"
 | 
						|
        >
 | 
						|
          <FontAwesomeIcon :icon="['fas', 'trash']" class="mr-2" />
 | 
						|
          清空
 | 
						|
        </button>
 | 
						|
      </div>
 | 
						|
    </div>
 | 
						|
 | 
						|
    <!-- 输入输出区域 -->
 | 
						|
    <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
 | 
						|
      <!-- 输入区域 -->
 | 
						|
      <div class="card p-4">
 | 
						|
        <div class="flex items-center justify-between mb-3">
 | 
						|
          <h3 class="text-lg font-semibold text-primary">输入</h3>
 | 
						|
          <button
 | 
						|
            @click="pasteFromClipboard"
 | 
						|
            class="p-2 rounded text-secondary hover:text-primary transition-colors"
 | 
						|
            title="粘贴"
 | 
						|
          >
 | 
						|
            <FontAwesomeIcon :icon="['fas', 'clipboard']" />
 | 
						|
          </button>
 | 
						|
        </div>
 | 
						|
        
 | 
						|
        <textarea
 | 
						|
          v-model="inputText"
 | 
						|
          placeholder="输入要转换的文本或Unicode编码..."
 | 
						|
          class="textarea-field h-80"
 | 
						|
        />
 | 
						|
        
 | 
						|
        <div class="mt-3 text-sm text-tertiary">
 | 
						|
          <p>字符数量: {{ inputText.length }}</p>
 | 
						|
        </div>
 | 
						|
      </div>
 | 
						|
 | 
						|
      <!-- 输出区域 -->
 | 
						|
      <div class="card p-4">
 | 
						|
        <div class="flex items-center justify-between mb-3">
 | 
						|
          <h3 class="text-lg font-semibold text-primary">输出</h3>
 | 
						|
          <button
 | 
						|
            @click="copyToClipboard"
 | 
						|
            :disabled="!outputText"
 | 
						|
            class="p-2 rounded text-secondary hover:text-primary transition-colors disabled:opacity-50"
 | 
						|
            title="复制"
 | 
						|
          >
 | 
						|
            <FontAwesomeIcon 
 | 
						|
              :icon="copied ? ['fas', 'check'] : ['fas', 'copy']" 
 | 
						|
              :class="copied && 'text-success'"
 | 
						|
            />
 | 
						|
          </button>
 | 
						|
        </div>
 | 
						|
        
 | 
						|
        <textarea
 | 
						|
          v-model="outputText"
 | 
						|
          placeholder="转换结果将显示在这里..."
 | 
						|
          class="textarea-field h-80"
 | 
						|
          readonly
 | 
						|
        />
 | 
						|
      </div>
 | 
						|
    </div>
 | 
						|
 | 
						|
    <!-- 字符信息 -->
 | 
						|
    <div v-if="charInfo.length > 0" 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 lg:grid-cols-3 gap-4">
 | 
						|
        <div
 | 
						|
          v-for="(char, index) in charInfo"
 | 
						|
          :key="index"
 | 
						|
          class="bg-block p-3 rounded"
 | 
						|
        >
 | 
						|
          <div class="text-center space-y-2">
 | 
						|
            <div class="text-2xl font-bold text-primary">{{ char.char }}</div>
 | 
						|
            <div class="text-sm text-secondary space-y-1">
 | 
						|
              <div><strong>Unicode:</strong> {{ char.unicode }}</div>
 | 
						|
              <div><strong>UTF-8:</strong> {{ char.utf8 }}</div>
 | 
						|
              <div><strong>十进制:</strong> {{ char.decimal }}</div>
 | 
						|
              <div><strong>十六进制:</strong> {{ char.hex }}</div>
 | 
						|
              <div v-if="char.description"><strong>描述:</strong> {{ char.description }}</div>
 | 
						|
            </div>
 | 
						|
          </div>
 | 
						|
        </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">
 | 
						|
        <div>
 | 
						|
          <h4 class="font-medium text-secondary mb-2">常用字符</h4>
 | 
						|
          <div class="space-y-2">
 | 
						|
            <button
 | 
						|
              v-for="example in examples"
 | 
						|
              :key="example.text"
 | 
						|
              @click="() => useExample(example.text)"
 | 
						|
              class="block w-full text-left p-2 bg-block hover:bg-hover rounded text-sm"
 | 
						|
            >
 | 
						|
              <span class="font-mono">{{ example.text }}</span>
 | 
						|
              <span class="text-secondary ml-2">{{ example.unicode }}</span>
 | 
						|
            </button>
 | 
						|
          </div>
 | 
						|
        </div>
 | 
						|
        
 | 
						|
        <div>
 | 
						|
          <h4 class="font-medium text-secondary mb-2">转换格式说明</h4>
 | 
						|
          <div class="space-y-2 text-sm">
 | 
						|
            <div class="bg-block p-3 rounded">
 | 
						|
              <div><strong>Unicode编码格式:</strong></div>
 | 
						|
              <div class="font-mono text-xs">
 | 
						|
                \\u4E2D (JavaScript格式)<br>
 | 
						|
                U+4E2D (标准格式)<br>
 | 
						|
                中 (HTML实体)
 | 
						|
              </div>
 | 
						|
            </div>
 | 
						|
            <div class="bg-block p-3 rounded">
 | 
						|
              <div><strong>支持的输入格式:</strong></div>
 | 
						|
              <div class="text-xs">
 | 
						|
                • 普通文本<br>
 | 
						|
                • Unicode编码序列<br>
 | 
						|
                • 十六进制编码
 | 
						|
              </div>
 | 
						|
            </div>
 | 
						|
          </div>
 | 
						|
        </div>
 | 
						|
      </div>
 | 
						|
    </div>
 | 
						|
  </div>
 | 
						|
</template>
 | 
						|
 | 
						|
<script setup lang="ts">
 | 
						|
import { ref, watch } from 'vue'
 | 
						|
 | 
						|
// 响应式状态
 | 
						|
const inputText = ref('')
 | 
						|
const outputText = ref('')
 | 
						|
const copied = ref(false)
 | 
						|
const charInfo = ref<Array<{
 | 
						|
  char: string
 | 
						|
  unicode: string
 | 
						|
  utf8: string
 | 
						|
  decimal: number
 | 
						|
  hex: string
 | 
						|
  description?: string
 | 
						|
}>>([])
 | 
						|
 | 
						|
// 示例数据
 | 
						|
const examples = [
 | 
						|
  { text: '中', unicode: '\\u4E2D' },
 | 
						|
  { text: '文', unicode: '\\u6587' },
 | 
						|
  { text: '😀', unicode: '\\uD83D\\uDE00' },
 | 
						|
  { text: '©', unicode: '\\u00A9' },
 | 
						|
  { text: '™', unicode: '\\u2122' },
 | 
						|
  { text: '€', unicode: '\\u20AC' }
 | 
						|
]
 | 
						|
 | 
						|
// 文本转Unicode
 | 
						|
const textToUnicode = (text: string): string => {
 | 
						|
  return text.split('').map(char => {
 | 
						|
    const code = char.charCodeAt(0)
 | 
						|
    if (code > 127) {
 | 
						|
      return '\\u' + code.toString(16).toUpperCase().padStart(4, '0')
 | 
						|
    }
 | 
						|
    return char
 | 
						|
  }).join('')
 | 
						|
}
 | 
						|
 | 
						|
// Unicode转文本
 | 
						|
const unicodeToText = (unicode: string): string => {
 | 
						|
  try {
 | 
						|
    // 处理不同格式的Unicode编码
 | 
						|
    let processedUnicode = unicode
 | 
						|
      .replace(/\\u([0-9a-fA-F]{4})/g, '\\u$1')
 | 
						|
      .replace(/U\+([0-9a-fA-F]{4})/g, '\\u$1')
 | 
						|
      .replace(/&#(\d+);/g, (match, dec) => {
 | 
						|
        const hex = parseInt(dec).toString(16).toUpperCase().padStart(4, '0')
 | 
						|
        return '\\u' + hex
 | 
						|
      })
 | 
						|
    
 | 
						|
    return JSON.parse('"' + processedUnicode + '"')
 | 
						|
  } catch (error) {
 | 
						|
    throw new Error('Unicode格式错误')
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// 文本转十六进制
 | 
						|
const textToHex = (text: string): string => {
 | 
						|
  return text.split('').map(char => {
 | 
						|
    const code = char.charCodeAt(0)
 | 
						|
    return '0x' + code.toString(16).toUpperCase().padStart(4, '0')
 | 
						|
  }).join(' ')
 | 
						|
}
 | 
						|
 | 
						|
// 转换函数
 | 
						|
const convertTo = (type: 'unicode' | 'text' | 'hex') => {
 | 
						|
  if (!inputText.value.trim()) return
 | 
						|
  
 | 
						|
  try {
 | 
						|
    switch (type) {
 | 
						|
      case 'unicode':
 | 
						|
        outputText.value = textToUnicode(inputText.value)
 | 
						|
        break
 | 
						|
      case 'text':
 | 
						|
        outputText.value = unicodeToText(inputText.value)
 | 
						|
        break
 | 
						|
      case 'hex':
 | 
						|
        outputText.value = textToHex(inputText.value)
 | 
						|
        break
 | 
						|
    }
 | 
						|
  } catch (error) {
 | 
						|
    outputText.value = '转换失败: ' + (error instanceof Error ? error.message : '未知错误')
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// 分析字符信息
 | 
						|
const analyzeCharacters = (text: string) => {
 | 
						|
  if (!text || text.length > 20) {
 | 
						|
    charInfo.value = []
 | 
						|
    return
 | 
						|
  }
 | 
						|
  
 | 
						|
  charInfo.value = text.split('').map(char => {
 | 
						|
    const code = char.charCodeAt(0)
 | 
						|
    const unicode = '\\u' + code.toString(16).toUpperCase().padStart(4, '0')
 | 
						|
    
 | 
						|
    // UTF-8 编码
 | 
						|
    const utf8Bytes = new TextEncoder().encode(char)
 | 
						|
    const utf8 = Array.from(utf8Bytes).map(b => '0x' + b.toString(16).toUpperCase()).join(' ')
 | 
						|
    
 | 
						|
    return {
 | 
						|
      char,
 | 
						|
      unicode,
 | 
						|
      utf8,
 | 
						|
      decimal: code,
 | 
						|
      hex: '0x' + code.toString(16).toUpperCase(),
 | 
						|
      description: getCharDescription(char, code)
 | 
						|
    }
 | 
						|
  })
 | 
						|
}
 | 
						|
 | 
						|
// 获取字符描述
 | 
						|
const getCharDescription = (char: string, code: number): string | undefined => {
 | 
						|
  if (code >= 0x4E00 && code <= 0x9FFF) return 'CJK统一汉字'
 | 
						|
  if (code >= 0x3040 && code <= 0x309F) return '平假名'
 | 
						|
  if (code >= 0x30A0 && code <= 0x30FF) return '片假名'
 | 
						|
  if (code >= 0x1F600 && code <= 0x1F64F) return 'Emoji表情'
 | 
						|
  if (code >= 0x0020 && code <= 0x007F) return 'ASCII字符'
 | 
						|
  if (code >= 0x00A0 && code <= 0x00FF) return 'Latin-1补充'
 | 
						|
  return undefined
 | 
						|
}
 | 
						|
 | 
						|
// 使用示例
 | 
						|
const useExample = (text: string) => {
 | 
						|
  inputText.value = text
 | 
						|
  convertTo('unicode')
 | 
						|
}
 | 
						|
 | 
						|
// 清空所有内容
 | 
						|
const clearAll = () => {
 | 
						|
  inputText.value = ''
 | 
						|
  outputText.value = ''
 | 
						|
  charInfo.value = []
 | 
						|
}
 | 
						|
 | 
						|
// 粘贴功能
 | 
						|
const pasteFromClipboard = async () => {
 | 
						|
  try {
 | 
						|
    const text = await navigator.clipboard.readText()
 | 
						|
    inputText.value = text
 | 
						|
  } catch (error) {
 | 
						|
    console.error('粘贴失败:', error)
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// 复制功能
 | 
						|
const copyToClipboard = async () => {
 | 
						|
  if (!outputText.value) return
 | 
						|
  
 | 
						|
  try {
 | 
						|
    await navigator.clipboard.writeText(outputText.value)
 | 
						|
    copied.value = true
 | 
						|
    setTimeout(() => {
 | 
						|
      copied.value = false
 | 
						|
    }, 2000)
 | 
						|
  } catch (error) {
 | 
						|
    console.error('复制失败:', error)
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// 监听输入变化,自动分析字符
 | 
						|
watch(inputText, (newValue) => {
 | 
						|
  analyzeCharacters(newValue)
 | 
						|
}, { immediate: true })
 | 
						|
</script>  |