forked from zguiy/utils
工具完成
This commit is contained in:
323
src/components/tools/UnicodeConverter.vue
Normal file
323
src/components/tools/UnicodeConverter.vue
Normal file
@ -0,0 +1,323 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user