1
0
Fork 0
mirror of https://github.com/CorentinTh/it-tools.git synced 2025-08-07 06:25:18 +02:00

feat(new tool): ipinfo

This commit is contained in:
guihouchang 2025-06-05 17:49:37 +08:00
parent bbf73747e4
commit 133b08dc83
6 changed files with 173 additions and 1 deletions

3
components.d.ts vendored
View file

@ -89,9 +89,11 @@ declare module '@vue/runtime-core' {
HtmlWysiwygEditor: typeof import('./src/tools/html-wysiwyg-editor/html-wysiwyg-editor.vue')['default']
HttpStatusCodes: typeof import('./src/tools/http-status-codes/http-status-codes.vue')['default']
IbanValidatorAndParser: typeof import('./src/tools/iban-validator-and-parser/iban-validator-and-parser.vue')['default']
'IconMdi:brushVariant': typeof import('~icons/mdi/brush-variant')['default']
'IconMdi:kettleSteamOutline': typeof import('~icons/mdi/kettle-steam-outline')['default']
InputCopyable: typeof import('./src/components/InputCopyable.vue')['default']
IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default']
IpInfo: typeof import('./src/tools/ip-info/ip-info.vue')['default']
Ipv4AddressConverter: typeof import('./src/tools/ipv4-address-converter/ipv4-address-converter.vue')['default']
Ipv4RangeExpander: typeof import('./src/tools/ipv4-range-expander/ipv4-range-expander.vue')['default']
Ipv4SubnetCalculator: typeof import('./src/tools/ipv4-subnet-calculator/ipv4-subnet-calculator.vue')['default']
@ -121,6 +123,7 @@ declare module '@vue/runtime-core' {
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
NH1: typeof import('naive-ui')['NH1']
NIcon: typeof import('naive-ui')['NIcon']
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']

View file

@ -407,3 +407,25 @@ tools:
notification:
success: Export successful!
error: Export failed, please try again.
ip-info:
title: 'IP Info'
description: 'Query IP address information including location, ISP, and more'
placeholder: 'Enter IP address (e.g. 117.25.96.225)'
query: 'Query'
loading: 'Loading...'
copied: 'Copied to clipboard!'
errors:
emptyIp: 'Please enter an IP address'
fetchFailed: 'Failed to fetch IP information'
generic: 'An error occurred'
fields:
ip: 'IP Address'
hostname: 'Hostname'
city: 'City'
region: 'Region'
country: 'Country'
loc: 'Location'
org: 'Organization'
postal: 'Postal Code'
timezone: 'Timezone'

View file

@ -403,3 +403,25 @@ tools:
notification:
success: 导出成功!
error: 导出失败,请重试。
ip-info:
title: 'IP 信息查询'
description: '查询 IP 地址信息包括位置、ISP 等详细信息'
placeholder: '请输入 IP 地址例如117.25.96.225'
query: '查询'
loading: '加载中...'
copied: '已复制到剪贴板!'
errors:
emptyIp: '请输入 IP 地址'
fetchFailed: '获取 IP 信息失败'
generic: '发生错误'
fields:
ip: 'IP 地址'
hostname: '主机名'
city: '城市'
region: '地区'
country: '国家'
loc: '位置'
org: '组织'
postal: '邮政编码'
timezone: '时区'

View file

@ -88,6 +88,7 @@ import { tool as macAddressLookup } from './mac-address-lookup';
import { tool as xmlFormatter } from './xml-formatter';
import { tool as yamlViewer } from './yaml-viewer';
import { tool as markdownWord } from './markdown-to-word';
import { tool as ipInfo } from './ip-info';
export const toolsByCategory: ToolCategory[] = [
{
@ -166,7 +167,7 @@ export const toolsByCategory: ToolCategory[] = [
},
{
name: 'Network',
components: [ipv4SubnetCalculator, ipv4AddressConverter, ipv4RangeExpander, macAddressLookup, macAddressGenerator, ipv6UlaGenerator],
components: [ipv4SubnetCalculator, ipv4AddressConverter, ipv4RangeExpander, macAddressLookup, macAddressGenerator, ipv6UlaGenerator, ipInfo],
},
{
name: 'Math',

View file

@ -0,0 +1,19 @@
/*
* @Author: guihouchang guihouchang@163.com
* @Date: 2025-06-05 17:06:24
* @LastEditors: guihouchang guihouchang@163.com
* @LastEditTime: 2025-06-05 17:42:33
* @FilePath: /it-tools/src/tools/ip-info/index.ts
* @Description: ,`customMade`, koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
import { ArrowLeftBar } from '@vicons/tabler';
import { defineTool } from '../tool';
export const tool = defineTool({
name: 'ip-info',
path: '/ip-info',
component: () => import('./ip-info.vue'),
icon: ArrowLeftBar,
keywords: ['ip', 'query', 'info', 'location', 'isp'],
createdAt: new Date('2024-08-25'),
});

View file

@ -0,0 +1,105 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useMessage } from 'naive-ui';
import { useCopy } from '@/composable/copy';
const { t } = useI18n();
const message = useMessage();
const { copy } = useCopy();
interface IpInfo {
ip: string
hostname?: string
city?: string
region?: string
country?: string
loc?: string
org?: string
postal?: string
timezone?: string
}
const ipAddress = ref('');
const ipInfo = ref<IpInfo | null>(null);
const loading = ref(false);
const error = ref('');
function fetchIpInfo() {
if (!ipAddress.value) {
error.value = t('tools.ip-info.errors.emptyIp');
return;
}
loading.value = true;
error.value = '';
return fetch(`https://ipinfo.io/${ipAddress.value}`, {
headers: {
Authorization: 'Bearer 7a8a124a501f25',
},
})
.then((response) => {
if (!response.ok) {
throw new Error(t('tools.ip-info.errors.fetchFailed'));
}
return response.json();
})
.then((data) => {
ipInfo.value = data;
})
.catch((e) => {
error.value = e instanceof Error ? e.message : t('tools.ip-info.errors.generic');
})
.finally(() => {
loading.value = false;
});
}
function copyToClipboard(text: string | undefined) {
if (text) {
copy(text);
message.success(t('tools.ip-info.copied'));
}
}
</script>
<template>
<div class="flex flex-col gap-4 p-4">
<div class="flex gap-4">
<input
v-model="ipAddress"
type="text"
:placeholder="t('tools.ip-info.placeholder')"
class="flex-1 border rounded px-4 py-2"
@keyup.enter="fetchIpInfo"
>
<button
class="rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600"
:disabled="loading"
@click="fetchIpInfo"
>
{{ loading ? t('tools.ip-info.loading') : t('tools.ip-info.query') }}
</button>
</div>
<div v-if="error" class="rounded bg-red-100 p-4 text-red-500">
{{ error }}
</div>
<div v-if="ipInfo" class="grid gap-4 border rounded p-4">
<div v-for="(value, key) in ipInfo" :key="key" class="flex items-center justify-between">
<span class="font-medium capitalize">{{ t(`tools.ip-info.fields.${key}`) }}:</span>
<div class="flex items-center gap-2">
<span>{{ value }}</span>
<button
class="p-1 text-gray-500 hover:text-gray-700"
@click="copyToClipboard(value)"
>
<i class="i-mdi-content-copy" />
</button>
</div>
</div>
</div>
</div>
</template>