1
0
forked from zguiy/utils

工具完成

This commit is contained in:
2025-06-28 22:38:49 +08:00
parent 2c668fedd0
commit 8400dbfab9
60 changed files with 23197 additions and 144 deletions

View File

@ -0,0 +1,464 @@
<template>
<div class="space-y-6">
<!-- 进制选择 -->
<div class="card p-6">
<h2 class="text-lg font-medium text-primary mb-4">数字进制转换器</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- 源进制选择 -->
<div>
<label class="block text-sm font-medium text-secondary mb-2">从进制</label>
<div class="grid grid-cols-2 gap-2 mb-3">
<button
v-for="base in baseOptions"
:key="'from-' + base.id"
:class="[
'px-3 py-2 text-sm rounded transition-all',
fromBase === base.id ? 'bg-primary text-white' : 'btn-secondary'
]"
@click="setFromBase(base.id)"
>
{{ base.name }}
</button>
</div>
<!-- 自定义进制输入 -->
<div v-if="fromBase === 'custom'" class="flex items-center gap-2">
<span class="text-sm text-secondary">自定义进制:</span>
<input
v-model.number="customFromBase"
type="number"
min="2"
max="36"
class="w-20 p-1 bg-block border border-primary/20 rounded text-center input-field"
/>
</div>
</div>
<!-- 目标进制选择 -->
<div>
<label class="block text-sm font-medium text-secondary mb-2">到进制</label>
<div class="grid grid-cols-2 gap-2 mb-3">
<button
v-for="base in baseOptions"
:key="'to-' + base.id"
:class="[
'px-3 py-2 text-sm rounded transition-all',
toBase === base.id ? 'bg-primary text-white' : 'btn-secondary'
]"
@click="setToBase(base.id)"
>
{{ base.name }}
</button>
</div>
<!-- 自定义进制输入 -->
<div v-if="toBase === 'custom'" class="flex items-center gap-2">
<span class="text-sm text-secondary">自定义进制:</span>
<input
v-model.number="customToBase"
type="number"
min="2"
max="36"
class="w-20 p-1 bg-block border border-primary/20 rounded text-center input-field"
/>
</div>
</div>
</div>
</div>
<!-- 输入和输出区域 -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- 输入区域 -->
<div class="card p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-md font-medium text-primary">输入数字</h3>
<div class="flex gap-2">
<button @click="loadExample" class="btn-secondary text-sm">
<FontAwesomeIcon :icon="['fas', 'sync-alt']" class="mr-1" />
示例
</button>
<button @click="clearAll" class="btn-secondary text-sm">
<FontAwesomeIcon :icon="['fas', 'eraser']" class="mr-1" />
清空
</button>
</div>
</div>
<div class="space-y-4">
<div>
<label class="block text-sm text-secondary font-medium mb-2">
{{ getBaseLabel(getCurrentFromBase()) }} 数字
</label>
<textarea
v-model="inputValue"
class="textarea-field h-32 w-full resize-y font-mono"
:placeholder="getInputPlaceholder()"
/>
</div>
<!-- 错误消息 -->
<div v-if="error" class="p-3 bg-red-900/20 border border-red-700/30 text-error rounded-lg">
<FontAwesomeIcon :icon="['fas', 'exclamation-triangle']" class="mr-2" />
{{ error }}
</div>
</div>
</div>
<!-- 输出区域 -->
<div class="card p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-md font-medium text-primary">转换结果</h3>
<button
v-if="outputValue"
@click="copyToClipboard"
class="btn-secondary text-sm"
>
<FontAwesomeIcon :icon="copied ? ['fas', 'check'] : ['fas', 'copy']" class="mr-1" />
{{ copied ? '已复制' : '复制' }}
</button>
</div>
<div class="space-y-4">
<div>
<label class="block text-sm text-secondary font-medium mb-2">
{{ getBaseLabel(getCurrentToBase()) }} 数字
</label>
<textarea
v-model="outputValue"
readonly
class="textarea-field h-32 w-full resize-y font-mono bg-block"
placeholder="转换结果将在这里显示..."
/>
</div>
<!-- 转换信息 -->
<div v-if="outputValue" class="text-sm text-tertiary">
<div>十进制值: {{ getDecimalValue() }}</div>
<div>字符长度: {{ outputValue.length }}</div>
</div>
</div>
</div>
</div>
<!-- 高级选项 -->
<div class="card p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-md font-medium text-primary">高级选项</h3>
<button
@click="showAdvancedOptions = !showAdvancedOptions"
class="btn-secondary text-sm"
>
<FontAwesomeIcon :icon="['fas', 'cog']" class="mr-1" />
{{ showAdvancedOptions ? '隐藏选项' : '显示选项' }}
</button>
</div>
<div v-if="showAdvancedOptions" class="grid grid-cols-1 md:grid-cols-3 gap-4">
<label class="flex items-center">
<input
v-model="useUppercase"
type="checkbox"
class="w-4 h-4 text-primary bg-block rounded border-primary/20 focus:ring-primary focus:ring-opacity-25"
/>
<span class="ml-2 text-sm text-secondary">使用大写字母</span>
</label>
<label class="flex items-center">
<input
v-model="addPrefix"
type="checkbox"
class="w-4 h-4 text-primary bg-block rounded border-primary/20 focus:ring-primary focus:ring-opacity-25"
/>
<span class="ml-2 text-sm text-secondary">添加进制前缀</span>
</label>
<label class="flex items-center">
<input
v-model="groupDigits"
type="checkbox"
class="w-4 h-4 text-primary bg-block rounded border-primary/20 focus:ring-primary focus:ring-opacity-25"
/>
<span class="ml-2 text-sm text-secondary">数字分组</span>
</label>
</div>
</div>
<!-- 快速转换面板 -->
<div class="card p-6">
<h3 class="text-md font-medium text-primary mb-4">快速转换</h3>
<div v-if="inputValue && !error" class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div
v-for="quickBase in quickBases"
:key="quickBase.id"
class="bg-block rounded-lg p-4"
>
<div class="flex items-center justify-between mb-2">
<h4 class="text-sm font-medium text-secondary">{{ quickBase.name }}</h4>
<button
@click="() => copyQuickResult(quickBase.id)"
class="text-tertiary hover:text-primary transition-colors"
>
<FontAwesomeIcon :icon="['fas', 'copy']" />
</button>
</div>
<code class="text-sm text-primary font-mono break-all">
{{ getQuickConversion(quickBase.id) }}
</code>
</div>
</div>
<div v-else class="text-center py-8 text-tertiary">
请输入有效数字以查看快速转换结果
</div>
</div>
<!-- 使用说明 -->
<div class="card p-6">
<h3 class="text-md font-medium text-primary mb-4">
<FontAwesomeIcon :icon="['fas', 'info-circle']" class="mr-2" />
使用说明
</h3>
<div class="text-sm text-secondary space-y-2">
<p> 支持 2-36 进制之间的任意转换</p>
<p> 可以输入带前缀的数字 0x, 0b, 0o</p>
<p> 支持大写/小写字母添加前缀数字分组等格式选项</p>
<p> 十六进制以上的进制使用字母 A-Z 表示 10-35</p>
<p> 输入时可以使用空格或下划线分隔系统会自动忽略</p>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
// 响应式状态
const inputValue = ref('')
const outputValue = ref('')
const fromBase = ref('10')
const toBase = ref('2')
const customFromBase = ref(10)
const customToBase = ref(2)
const error = ref('')
const copied = ref(false)
const showAdvancedOptions = ref(false)
const useUppercase = ref(true)
const addPrefix = ref(false)
const groupDigits = ref(false)
// 进制选项
const baseOptions = [
{ id: '2', name: '二进制' },
{ id: '8', name: '八进制' },
{ id: '10', name: '十进制' },
{ id: '16', name: '十六进制' },
{ id: 'custom', name: '自定义' }
]
// 快速转换进制
const quickBases = [
{ id: '2', name: '二进制' },
{ id: '8', name: '八进制' },
{ id: '10', name: '十进制' },
{ id: '16', name: '十六进制' }
]
// 获取当前源进制
const getCurrentFromBase = (): number => {
return fromBase.value === 'custom' ? customFromBase.value : parseInt(fromBase.value)
}
// 获取当前目标进制
const getCurrentToBase = (): number => {
return toBase.value === 'custom' ? customToBase.value : parseInt(toBase.value)
}
// 获取进制标签
const getBaseLabel = (base: number): string => {
const labels: Record<number, string> = {
2: '二进制',
8: '八进制',
10: '十进制',
16: '十六进制'
}
return labels[base] || `${base}进制`
}
// 获取输入提示
const getInputPlaceholder = (): string => {
const base = getCurrentFromBase()
if (base === 2) return '例如: 1010, 0b1010'
if (base === 8) return '例如: 755, 0o755'
if (base === 10) return '例如: 42, 255'
if (base === 16) return '例如: FF, 0xFF'
return `请输入${base}进制数字...`
}
// 获取十进制值
const getDecimalValue = (): string => {
if (!inputValue.value.trim() || error.value) return '-'
try {
const cleanValue = inputValue.value.replace(/^0[bxo]|[\s_]/gi, '')
const decimal = parseInt(cleanValue, getCurrentFromBase())
return isNaN(decimal) ? '-' : decimal.toString()
} catch {
return '-'
}
}
// 进制转换函数
const convertBase = (value: string, from: number, to: number): string => {
// 验证进制范围
if (from < 2 || from > 36 || to < 2 || to > 36) {
throw new Error('进制范围必须在 2-36 之间')
}
// 移除输入中可能存在的前缀和格式化字符
const cleanValue = value.replace(/^0[bxo]|[\s_]/gi, '')
// 转换为十进制
let decimalValue
try {
decimalValue = parseInt(cleanValue, from)
if (isNaN(decimalValue)) {
throw new Error()
}
} catch {
throw new Error('输入的数字格式无效')
}
// 转换为目标进制
let result = decimalValue.toString(to)
// 大写字母
if (useUppercase.value && to > 10) {
result = result.toUpperCase()
}
// 添加前缀
if (addPrefix.value) {
if (to === 2) result = '0b' + result
else if (to === 8) result = '0o' + result
else if (to === 16) result = '0x' + result
}
// 数字分组
if (groupDigits.value) {
const prefix = result.match(/^0[bxo]/i)?.[0] || ''
const digits = result.replace(/^0[bxo]/i, '')
let grouped = ''
if (to === 2) {
// 二进制每8位分组
grouped = digits.match(/.{1,8}/g)?.join('_') || digits
} else if (to === 16) {
// 十六进制每4位分组
grouped = digits.match(/.{1,4}/g)?.join('_') || digits
} else {
// 其他进制每4位分组
grouped = digits.match(/.{1,4}/g)?.join('_') || digits
}
result = prefix + grouped
}
return result
}
// 获取快速转换结果
const getQuickConversion = (baseId: string): string => {
if (!inputValue.value.trim() || error.value) return '-'
try {
return convertBase(inputValue.value, getCurrentFromBase(), parseInt(baseId))
} catch {
return '错误'
}
}
// 设置源进制
const setFromBase = (base: string) => {
fromBase.value = base
}
// 设置目标进制
const setToBase = (base: string) => {
toBase.value = base
}
// 复制输出内容
const copyToClipboard = async () => {
if (!outputValue.value) return
try {
await navigator.clipboard.writeText(outputValue.value)
copied.value = true
setTimeout(() => {
copied.value = false
}, 2000)
} catch (err) {
console.error('复制失败:', err)
error.value = '复制失败'
}
}
// 复制快速转换结果
const copyQuickResult = async (baseId: string) => {
const result = getQuickConversion(baseId)
if (result === '-' || result === '错误') return
try {
await navigator.clipboard.writeText(result)
} catch (err) {
console.error('复制失败:', err)
}
}
// 清空所有内容
const clearAll = () => {
inputValue.value = ''
outputValue.value = ''
error.value = ''
}
// 加载示例
const loadExample = () => {
const examples: Record<number, string> = {
2: '1010',
8: '755',
10: '42',
16: 'FF'
}
const currentFromBase = getCurrentFromBase()
const example = examples[currentFromBase] || examples[10]
inputValue.value = example
}
// 监听输入变化并自动转换
watch([inputValue, fromBase, toBase, customFromBase, customToBase, useUppercase, addPrefix, groupDigits], () => {
if (inputValue.value.trim() === '') {
outputValue.value = ''
error.value = ''
return
}
try {
const result = convertBase(inputValue.value, getCurrentFromBase(), getCurrentToBase())
outputValue.value = result
error.value = ''
} catch (err) {
if (err instanceof Error) {
error.value = err.message
} else {
error.value = '转换错误'
}
outputValue.value = ''
}
})
</script>