This commit is contained in:
taoran
2023-11-28 16:40:53 +08:00
commit 5621838cf4
42 changed files with 8376 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
<script setup lang="ts">
import Header from '@/components/Header/Header.vue'
import Floor from '@/components/Floor/Floor.vue'
import Right from '@/components/Right/Right.vue'
</script>
<template>
<Header></Header>
<div class="w-full flex flex-col justify-between mb-20">
<el-row>
<el-col :xl="4" :md="1" class=""></el-col>
<el-col :xl="16" :md="22" class="mt-4">
<el-row class="flex-1">
<el-col :xl="19" :md="19" class=""><router-view></router-view></el-col>
<el-col :xl="5" :md="5" class="">
<Right/>
</el-col>
</el-row>
</el-col>
<!-- <el-col :xl="4" :md="1" class="flex bg-gray-200"></el-col> -->
<el-col :xl="4" :md="1"></el-col>
</el-row>
</div>
<Floor></Floor>
</template>
<style scoped>
</style>
+15
View File
@@ -0,0 +1,15 @@
//统一管理咱们项目用户相关的接口
import request from '@/utils/request'
import type {
getIpResponseData
} from './type'
//项目用户相关的请求地址
enum API {
//获取ip
GET_IP = '/api/v1/ip/get?ip='
}
//获取ip
export const getIp = (ip:string) => request.get<any, getIpResponseData>(API.GET_IP + ip)
+8
View File
@@ -0,0 +1,8 @@
//响应格式
export interface getIpResponseData {
code: number,
ip: string,
address: string
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

+12
View File
@@ -0,0 +1,12 @@
<template>
<div>
<el-empty :image-size="200" />
</div>
</template>
<script setup lang="ts">
</script>
<style scoped lang="scss">
</style>
+15
View File
@@ -0,0 +1,15 @@
<script setup lang="ts">
import { ref } from 'vue'
const beianNo = ref('xxxxx备案号')
const copyright = ref('ranblogs.com')
</script>
<template>
<div class="bg-black w-full fixed left-0 bottom-0 text-white text-center h-8 leading-8 z-10">
Copyright © 2023 · {{ copyright }} &nbsp;&nbsp; <a href="https://beian.miit.gov.cn/" target="_blank">{{ beianNo }}</a>
</div>
</template>
<style scoped>
</style>
+47
View File
@@ -0,0 +1,47 @@
<script setup lang="ts">
import { ref } from 'vue'
// import { Search } from '@element-plus/icons-vue'
// const searchInput = ref('')
const appName = ref('工具坊')
const appNet = ref('tools.ranblogs.com')
</script>
<template>
<header class="">
<nav class="h-20 w-full leading-10 flex flex-col justify-between pt-2 pb-2">
<el-row>
<el-col :xl="4" :md="1"></el-col>
<el-col :xl="16" :md="22">
<div class="flex items-center justify-between">
<!-- left -->
<router-link class="ml-4 flex flex-row" to="/">
<img class="h-10 w-auto rounded-lg lg:h-16 mr-2 mt-auto mb-auto" src="../../assets/logo.png" :alt="appNet">
<div class="flex flex-col mt-auto mb-auto">
<div class="text-xl">{{ appName }}</div>
<div class="text-sm text-gray-500">{{ appNet }}</div>
</div>
</router-link>
<!-- right -->
<div class=" w-full md:w-auto flex hidden md:block">
<ul class="flex mt-4 flex-col md:flex-row md:mt-0 justify-end">
<!-- <li class="mr-5">
<el-input v-model="searchInput" placeholder="搜索工具" class="w-full max-w-xl h-10" :prefix-icon="Search"/>
</li> -->
<li class="hover:text-blue-500">语言</li>
<li class="ml-5 hover:text-blue-500">加入收藏</li>
<li class="ml-5 hover:text-blue-500">常见问题</li>
</ul>
</div>
</div>
</el-col>
<el-col :xl="4" :md="1" class="flex"></el-col>
</el-row>
</nav>
</header>
</template>
<style scoped>
</style>
+148
View File
@@ -0,0 +1,148 @@
<script setup lang="ts">
import { reactive } from 'vue';
import { RouterLink } from "vue-router"
const info = reactive({
cates: [
{
id: 1,
value: '全部工具',
},
{
id: 2,
value: '文本处理'
},
{
id: 3,
value: '查询相关'
},
{
id: 4,
value: '教育学术'
},
{
id: 5,
value: '设计工具'
},
{
id: 6,
value: '开发运维'
}
],
tools: [
{
id: 1,
value: '时间戳转换',
img: 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
desc: '在线时间戳转换工具以及获取当前时间戳',
url: '/timetran',
cate: '开发运维',
},
{
id: 1,
value: 'MD5在线加密',
img: 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
desc: 'MD5在线加密,长度包含32位、16位',
url: '/MD5',
cate: '开发运维',
},
{
id: 1,
value: 'Json在线转换',
img: 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
desc: '在线编辑json, 语法检查',
url: '/json',
cate: '开发运维',
},
{
id: 1,
value: '正则测试工具',
img: 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
desc: '正则表达式测试工具, 常用正则表达式',
url: '/reg',
cate: '开发运维',
},
{
id: 1,
value: 'Unicode转中文',
img: 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
desc: 'Unicode和中文的相互转换',
url: '/unicode',
cate: '开发运维',
},
{
id: 1,
value: '字数统计',
img: 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
desc: '在线统计字符串的字数、段落、标点符号数量',
url: '/wordCount',
cate: '文本处理',
},
{
id: 1,
value: 'IP查询',
img: 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
desc: '在线查询ip地址、ip归属地',
url: '/ip',
cate: '查询相关',
},
{
id: 1,
value: '常用进制转换',
img: 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
desc: '制转换器是一个在线进制转换工具,可在二进制、八进制、十进制、十六进制、三十二进制以及36以内任意进制之间相互转换',
url: '/scaletran',
cate: '教育学术',
},
{
id: 1,
value: '在线图片处理',
img: 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
desc: '在线图片处理',
url: '/signimage',
cate: '设计工具',
}
]
})
</script>
<template>
<div>
<!-- cates -->
<div class="mr-3">
<ul class="flex bg-gray-100 h-10 rounded leading-10 pl-3">
<li v-for="(item, index) in info.cates" :key="index" class="flex items-center">
<el-text>{{ index != 0 ? '&nbsp;&nbsp;&nbsp;&nbsp;' : '' }} </el-text>
<el-text class="hover:text-blue-400 hover:underline hover:decoration-4 hover:underline-offset-[6px]">{{ item.value }}</el-text>
</li>
</ul>
</div>
<!-- card -->
<div class="mr-3 mt-3 flex justify-between flex-wrap self-card-div" :gutter="10">
<router-link v-for="(item, index) in info.tools" :key="index" :to="item.url" class="flex flex-col mt-3 border-solid rounded border-gray border w-[32.5%] p-2 bg-gray-50 hover:shadow-md">
<div class="flex items-center">
<el-image :src="item.img" class="w-10 h-10 min-h-[2.5rem] min-w-[2.5rem] rounded-full"></el-image>
<div class="flex flex-col ml-2">
<div class="flex">
<div class="font-semibold text-lg ">{{ item.value }}</div>
<!-- <div>加入收藏</div> -->
</div>
<el-text line-clamp="2">{{ item.desc }}</el-text>
</div>
</div>
<el-divider border-style="double" class="m-1" />
<div class="flex items-center justify-between">
<el-text size="small">http://tools.ranblogs.com/timeTran</el-text>
</div>
</router-link>
</div>
</div>
</template>
<style scoped>
.self-card-div:after{
content: "";
width: 32.5%
}
</style>
+81
View File
@@ -0,0 +1,81 @@
<script setup lang="ts">
import { onMounted, reactive } from 'vue'
import { Back } from '@element-plus/icons-vue'
import { getIp } from '@/api/ip'
import { isIp } from '@/utils/verify'
import { ElMessage } from 'element-plus'
const info = reactive({
title: "IP查询",
content: '',
ip: '',
address: '',
})
//查询
const search = async (type: string) => {
try {
//通过点击获取ip信息
if (type == 'click') {
//验证是否ip地址
if (!isIp(info.content)) {
ElMessage({
message: "无效的ip地址",
type: "warning",
duration: 1500
})
return
}
}
//获取信息
const result: any = await getIp(info.content)
info.ip = result.data.ip
info.address = result.data.address
} catch (error) {
console.log(error)
}
}
onMounted(() => {
search('')
})
</script>
<template>
<div class="flex flex-col mt-3 ml-4 flex-1 mr-10">
<div class="text-gray-600">
<a href="/" class="flex items-center">
<el-icon color="blue"><Back /></el-icon>
<div>返回首页</div>
</a>
</div>
<div class="text-xl mt-5 mb-5">
{{ info.title }}
</div>
<div>
<div class="flex">
<div class="mr-2 w-60">
<el-input v-model="info.content" ></el-input>
</div>
<el-button type="primary" @click="search('click')">查询</el-button>
</div>
<div class="mt-3 min-h-md bg-gray-100 p-3 mb-3 flex flex-col">
<div class="bg-white p-2 font-bold text-xl">您的IP信息</div>
<div class="bg-white p-2 flex">
<div class="mr-1">域名解析地址: </div>
<div>{{ info.ip }}</div>
</div>
<div class="bg-white p-2 flex">
<div class="mr-1">所在地理位置: </div>
<div>{{ info.address }}</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
</style>
+139
View File
@@ -0,0 +1,139 @@
<script setup lang="ts">
import { reactive } from 'vue'
import { Back } from '@element-plus/icons-vue'
import { transferred, copy } from '@/utils/string';
import { Codemirror } from "vue-codemirror";
import { json } from '@codemirror/lang-json';
import '@codemirror/search';
import '@codemirror/view';
import '@codemirror/state';
import '@codemirror/commands';
// import { syntaxTree } from '@codemirror/language';
// import { linter, Diagnostic } from '@codemirror/lint';
// const regexpLinter = linter(view => {
// let diagnostics: Diagnostic[] = []
// syntaxTree(view.state).cursor().iterate(node => {
// if (node.name == "number") diagnostics.push({
// from: node.from,
// to: node.to,
// severity: "warning",
// message: "Regular expressions are FORBIDDEN",
// actions: [{
// name: "Remove",
// apply(view, from, to) { view.dispatch({changes: {from, to}}) }
// }]
// })
// })
// return diagnostics
// })
const info = reactive({
title: "Json在线转换",
code: '',
extensions: [json()],
isParseErr: false,
parseErr: ''
})
//格式化json
const formatJson = () => {
try {
//初始化错误信息
info.isParseErr = false;
info.parseErr = ''
// 1、JSON.parse:把JSON字符串转换为JSON对象
// 2、JSON.stringify:把JSON对象 转换为 有缩进的 JSON字符串格式
info.code = JSON.stringify(JSON.parse(info.code), null, '\t');
} catch(error) {
info.isParseErr = true;
// info.parseErr = (error as Error).message
info.parseErr = '无效的JSON'
}
}
//压缩
const compress = () => {
info.code = info.code.replace(/[\r\n\t]/g, "")
}
//转义
const tran = () => {
info.code = transferred(info.code, "\"")
}
//去转义
const unTransferred = () => {
info.code = info.code.replace(/[\\]/g, ``)
}
//清空输入框
const clear = () => {
info.code = ''
}
const copyRes = async () => {
copy(info.code)
}
</script>
<template>
<div class="flex flex-col mt-3 ml-4 flex-1 mr-10">
<div class="text-gray-600">
<a href="/" class="flex items-center">
<el-icon color="blue"><Back /></el-icon>
<div>返回首页</div>
</a>
</div>
<div class="text-xl mt-5 mb-5">
{{ info.title }}
</div>
<div class="">
<div>
<!--
tabSize: tab键前进的个数
style 自定义样式
autofocus 挂载后立即聚焦在编辑器
indent-with-tab绑定键盘tab事件
extensions 扩展传数组
@ready="console.log('ready', $event)"
@change="console.log('change', $event)"
@focus="console.log('focus', $event)"
@blur="console.log('blur', $event)"
-->
<codemirror
v-model="info.code"
placeholder="这里是代码..."
:style="{ height: '400px' }"
:autofocus="true"
:indent-with-tab="true"
:tabSize="2"
:extensions="info.extensions"
/>
</div>
<div class="mt-4">
<el-button type="primary" @click="formatJson">校验/格式化</el-button>
<el-button type="primary" @click="compress">压缩</el-button>
<el-button type="primary" @click="tran">转义</el-button>
<el-button type="primary" @click="unTransferred">去转义</el-button>
<!-- <el-button type="primary">unicode转中文</el-button> -->
<!-- <el-button type="primary">中文转unicode</el-button> -->
<el-button type="primary" @click="copyRes">复制</el-button>
<el-button type="primary" @click="clear">清空</el-button>
</div>
<div class="mt-3 min-h-md bg-red-100 p-3 mb-3" v-show="info.isParseErr">
<el-text type="danger">{{ info.parseErr }}</el-text>
</div>
</div>
</div>
</template>
<style scoped>
</style>
+126
View File
@@ -0,0 +1,126 @@
<script setup lang="ts">
import { reactive } from 'vue'
import { Back } from '@element-plus/icons-vue'
import clipboard3 from 'vue-clipboard3'
import { ElMessage } from 'element-plus';
import { Md5 } from 'ts-md5'
const info = reactive({
title: "MD5在线加密",
encryptStr: '',
encryptUpper32: '',
encryptLower32: '',
encryptUpper16: '',
encryptLower16: '',
})
//加密
const encrypt = () => {
info.encryptLower32 = Md5.hashStr(info.encryptStr)
info.encryptUpper32 = info.encryptLower32.toUpperCase()
info.encryptUpper16 = info.encryptUpper32.substring(8, 24)
info.encryptLower16 = info.encryptLower32.substring(8, 24)
}
//清空输入框
const clear = () => {
info.encryptStr = ''
}
const {toClipboard} = clipboard3()
const copyRes = async (resStr: string) => {
try {
//check
if (resStr == '') {
ElMessage({
message: "无可复制内容",
type: "warning",
duration: 1500
})
return
}
//copy
await toClipboard(resStr)
ElMessage({
message: "复制成功",
type: "success",
duration: 1500
})
} catch (error) {
console.log("复制失败")
console.log(error)
ElMessage({
message: "复制失败",
type: "error",
duration: 1500
})
}
}
</script>
<template>
<div class="flex flex-col mt-3 ml-4 flex-1 mr-10">
<div class="text-gray-600">
<a href="/" class="flex items-center">
<el-icon color="blue"><Back /></el-icon>
<div>返回首页</div>
</a>
</div>
<div class="text-xl mt-5 mb-5">
{{ info.title }}
</div>
<div class="">
<div>
<el-input v-model="info.encryptStr" :rows="5" type="textarea" placeholder="请输入加密内容"></el-input>
</div>
<div class="mt-4">
<el-button type="primary" @click="encrypt()" >加密</el-button>
<el-button type="primary" @click="clear()">清空输入框</el-button>
</div>
<div class="w-[30rem]">
<el-input :value="info.encryptUpper32" class="mt-4" readonly>
<template #prepend>
<el-text>32位大写</el-text>
</template>
<template #append>
<el-button @click="copyRes(info.encryptUpper32)">复制</el-button>
</template>
</el-input>
<el-input :value="info.encryptLower32" class="mt-2" readonly>
<template #prepend>
<el-text>32位小写</el-text>
</template>
<template #append>
<el-button @click="copyRes(info.encryptLower32)">复制</el-button>
</template>
</el-input>
<el-input :value="info.encryptUpper16" class="mt-2" readonly>
<template #prepend>
<el-text>16位大写</el-text>
</template>
<template #append>
<el-button @click="copyRes(info.encryptUpper16)">复制</el-button>
</template>
</el-input>
<el-input :value="info.encryptLower16" class="mt-2" readonly>
<template #prepend>
<el-text>16位小写</el-text>
</template>
<template #append>
<el-button @click="copyRes(info.encryptLower16)">复制</el-button>
</template>
</el-input>
</div>
</div>
</div>
</template>
<style scoped>
</style>
+173
View File
@@ -0,0 +1,173 @@
<script setup lang="ts">
import { reactive } from 'vue'
import { Back } from '@element-plus/icons-vue'
import { Codemirror } from "vue-codemirror"
const info = reactive({
title: "正则表达式测试",
extensions: [],
reg: '',
content: `一些测试实例:
邮箱1[email protected]
邮箱2[email protected]
手机号:13012341234
url: http://tools.ranblogs.com、https://ranblogs.com
IP: 192.168.0.1
时间:2023-11-24
`,
matchRes: '',
matchNum: 0,
isAll: true,
isCase: false,
isParseErr: false,
parseErr: '',
regList: [
{
id: 1,
title: '匹配手机号',
reg: '(13\\d|14[579]|15[^4\\D]|17[^49\\D]|18\\d)\\d{8}'
},
{
id: 1,
title: '匹配网址URL',
reg: '(http|ftp|https):\\\/\\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%&:/~\\+#]*[\\w\\-\\@?^=%&/~\\+#])?'
},
{
id: 1,
title: '匹配Email',
reg: `\\w[-\\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\\.)+[A-Za-z]{2,14}`
},
{
id: 1,
title: '匹配中文字符',
reg: "[\u4e00-\u9fa5]"
},
{
id: 1,
title: '匹配双字节字符(包含汉字)',
reg: "[^\x00-\xff]+"
},
{
id: 1,
title: '匹配18位身份证号',
reg: "\\d{17}[0-9Xx]|\\d{15}"
},
{
id: 1,
title: '匹配整数',
reg: "[1-9]\\d*"
},
{
id: 1,
title: '匹配IP(IPV4)',
reg: "\\d{0,3}\\.\\d{0,3}\\.\\d{0,3}\\.\\d{0,3}"
},
{
id: 1,
title: '匹配日期(年-月-日)',
reg: "(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)"
},
]
})
//使用正则
const useReg = (reg: string) => {
info.reg = reg
}
const clearOldRes = () => {
info.matchNum = 0
info.matchRes = ''
}
//测试匹配(执行匹配)
const execMatch = () => {
//清除旧结果
clearOldRes()
//check
if (info.reg == '') {
return
}
let rule = ''
//是否全局
rule = info.isAll === true ? 'g' : ''
//是否区分大小写
rule += info.isCase === true ? 'i' : ''
let pattern = new RegExp(info.reg, rule);
let matchArr = info.content.match(pattern)
if (info.isAll === true) {
if (matchArr?.length) {
info.matchNum = matchArr?.length
for (let index in matchArr) {
info.matchRes += matchArr[index] + `\n`
}
}
} else {
if (matchArr?.[0]) {
info.matchRes = matchArr[0]
info.matchNum = 1
}
}
}
</script>
<template>
<div class="flex flex-col mt-3 ml-4 flex-1 mr-10">
<div class="text-gray-600">
<a href="/" class="flex items-center">
<el-icon color="blue"><Back /></el-icon>
<div>返回首页</div>
</a>
</div>
<div class="text-xl mt-5 mb-5">
{{ info.title }}
</div>
<div>
<div>
<codemirror
v-model="info.content"
placeholder=""
:style="{ height: '300px' }"
:autofocus="true"
:indent-with-tab="true"
:tabSize="2"
:extensions="info.extensions"
/>
</div>
<div class="mt-4">
<el-text class="font-bold">常用正则</el-text>
<ul class="flex flex-wrap">
<li v-for="(item, index) in info.regList" :key="index">
<el-button link type="primary" @click="useReg(item.reg)" class="mr-2 mt-2">{{ item.title }}</el-button>
</li>
</ul>
<div class="flex items-center mt-4">
<el-input v-model="info.reg" class="mr-2" placeholder="正则表达式"></el-input>
<div class="mr-2">
<el-checkbox v-model="info.isAll" label="全局搜索" size="large"/>
</div>
<div class="mr-2">
<el-checkbox v-model="info.isCase" label="忽略大小写" size="large" />
</div>
<el-button type="primary" @click="execMatch">测试匹配</el-button>
</div>
</div>
<div class="mt-3 min-h-md bg-gray-100 p-3 mb-3">
<el-text>共找到 {{ info.matchNum }} 处匹配结果</el-text>
<el-input type="textarea" :rows="4" readonly v-model="info.matchRes"></el-input>
</div>
</div>
</div>
</template>
<style scoped>
</style>
+54
View File
@@ -0,0 +1,54 @@
<script setup lang="ts">
import { Search } from '@element-plus/icons-vue';
import { Tools } from '@element-plus/icons-vue'
import { reactive } from 'vue';
const info = reactive({
searchInput: '',
hotTools: [
{
id: 1,
value: '时间戳转换'
},
{
id: 1,
value: 'MD5在线加密'
},
{
id: 1,
value: 'Json在线转换'
},
{
id: 1,
value: '正则测试工具'
},
{
id: 1,
value: '在线代码运行'
}
]
})
</script>
<template>
<div>
<!-- search -->
<div>
<el-input v-model="info.searchInput" placeholder="搜索工具" class="w-full max-w-xl h-10" :suffix-icon="Search"/>
</div>
<!-- hot tools -->
<div class="mt-3 border-solid rounded border-gray border p-3">
<div class="text-xl text-gray-400 font-bold">热门工具</div>
<ul class="mt-3">
<li class="flex items-center hover:bg-gray-200 p-1 rounded" v-for="(item, index) in info.hotTools" :key="index">
<el-icon class="mr-1"><Tools /></el-icon>
<div>{{ item.value }}</div>
</li>
</ul>
</div>
</div>
</template>
<style scoped>
</style>
+262
View File
@@ -0,0 +1,262 @@
<script setup lang="ts">
import { reactive } from 'vue'
import { Back } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { copy } from '@/utils/string'
import { isBinary, isOctal, isDecimal, isHexadecimal } from '@/utils/verify'
import { InfoFilled } from '@element-plus/icons-vue'
import { scale58ToBase, scale62ToBase, scale64ToBase, baseToScale58, baseToScale62, baseToScale64 } from '@/utils/math'
const info = reactive({
title: "常用进制转换",
content: '',
chooseTranOptions: '10',
tranOptions: [
{
value: '2',
label: '2进制',
tranValue: '',
desc: '',
},
{
value: '8',
label: '8进制',
tranValue: '',
desc: '',
},
{
value: '10',
label: '10进制',
tranValue: '',
desc: '',
},
{
value: '16',
label: '16进制',
tranValue: '',
desc: '',
},
{
value: '32',
label: '32进制',
tranValue: '',
desc: '数字 + 大写字母,不包含 ILOU 字符',
},
{
value: '58',
label: '58进制',
tranValue: '',
desc: '数字 + 大小写字母,不包含 0OlI 字符',
},
{
value: '62',
label: '62进制',
tranValue: '',
desc: '数字 + 大小写字母',
},
{
value: '64',
label: '64进制',
tranValue: '',
desc: '数字 + 大小写字母以及两个特殊字符 +/',
},
]
})
//高进制转换10进制 - int
const tailScaleToBase = (content: string, scale: number): number => {
let res = 0
switch(scale) {
case 58:
res = scale58ToBase(content)
break;
case 62:
res = scale62ToBase(content)
break;
case 64:
res = scale64ToBase(content)
break;
default:
ElMessage({
message: "参数错误",
type: "warning",
duration: 1500
})
}
return res
}
//10进制转换高进制
const baseToTailScale = (num: number, scale: number): string => {
let res = ''
switch(scale) {
case 58:
res = baseToScale58(num)
break;
case 62:
res = baseToScale62(num)
break;
case 64:
res = baseToScale64(num)
break;
default:
ElMessage({
message: "参数错误",
type: "warning",
duration: 1500
})
}
return res
}
//转换前 - check
const tranCheck = () => {
if (!info.content) {
ElMessage({
message: "请输入转换数值",
type: "warning",
duration: 1500
})
return false
}
if (info.chooseTranOptions == '2' && isBinary(info.content) == false) {
ElMessage({
message: "不是二进制值",
type: "warning",
duration: 1500
})
return false
}
if (info.chooseTranOptions == '8' && isOctal(info.content) == false) {
ElMessage({
message: "不是八进制值",
type: "warning",
duration: 1500
})
return false
}
if (info.chooseTranOptions == '10' && isDecimal(info.content) == false) {
ElMessage({
message: "不是十进制值",
type: "warning",
duration: 1500
})
return false
}
if (info.chooseTranOptions == '16' && isHexadecimal(info.content) == false) {
ElMessage({
message: "不是十六进制值",
type: "warning",
duration: 1500
})
return false
}
return true
}
//转换
const tran = () => {
//转换前 - check
if (!tranCheck()) {
return
}
//当前输入的内容转换成10进制
let tranContent
let chooseTranInt = parseInt(info.chooseTranOptions, 10)
if (chooseTranInt <= 36) {
tranContent = parseInt(info.content, chooseTranInt)
} else {
tranContent = tailScaleToBase(info.content, chooseTranInt)
}
//用上面转换的10进制内容转换对应进制
for (let index in info.tranOptions) {
let valueInt = parseInt(info.tranOptions[index].value, 10)
if (valueInt <= 36) {
info.tranOptions[index].tranValue = tranContent.toString(valueInt)
} else {
info.tranOptions[index].tranValue = baseToTailScale(tranContent, valueInt)
}
}
}
//copy
const copyRes = async (index: any) => {
copy(info.tranOptions[index].tranValue)
}
</script>
<template>
<div class="flex flex-col mt-3 ml-4 flex-1 mr-10">
<div class="text-gray-600">
<a href="/" class="flex items-center">
<el-icon color="blue"><Back /></el-icon>
<div>返回首页</div>
</a>
</div>
<div class="text-xl mt-5 mb-5">
{{ info.title }}
</div>
<div class="flex">
<div class="mr-2 w-full">
<el-input v-model="info.content">
<template #prepend>
<el-select v-model="info.chooseTranOptions" placeholder="Select" class="w-24">
<el-option
v-for="item in info.tranOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-input>
</div>
<el-button type="primary" @click="tran()">转换</el-button>
</div>
<div class="mt-3 min-h-md bg-gray-100 p-3 mb-3 flex flex-col">
<el-table :data="info.tranOptions" border style="width: 100%">
<el-table-column prop="label" label="进制" width="120">
<template #default="scope">
<div class="flex items-center">
<span class="mr-1">{{ scope.row.label }}</span>
<el-tooltip
v-if="scope.row.desc != ''"
class="box-item"
effect="dark"
:content="scope.row.desc"
placement="top-start"
><el-icon ><InfoFilled /></el-icon></el-tooltip>
</div>
</template>
</el-table-column>
<el-table-column prop="tranValue" label="结果"/>
<el-table-column prop="" label="操作" width="60">
<template #default="scope">
<el-button
link
type="primary"
size="small"
@click.prevent="copyRes(scope.$index)"
>
复制
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<style scoped>
</style>
+86
View File
@@ -0,0 +1,86 @@
<script setup lang="ts">
import { ref, reactive } from "vue"
import SignImageCore from './SignImageCore.vue'
import { Back } from '@element-plus/icons-vue'
import { UploadProps, UploadInstance, UploadRawFile, genFileId } from 'element-plus'
// 图片处理
// const newImg = ref("")
const info = reactive({
title:"在线编辑图片", // 弹框标题
previewsImgUrl:"https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg", //图片地址
//获取处理完的图片
getNewImg:(val: string) => {
console.log('getNewImg:' + val)
//newImg.value = window.serverUrl.IMG_SERVER + val
},
cKey: 1, //用于刷新组件视图
})
//使用 upload ref
const refSignImageCore = ref<UploadInstance>()
//上传文件发生变化
const uploadChange: UploadProps['onChange'] = (file) => {
//UploadFile转换url
info.previewsImgUrl = URL.createObjectURL(file.raw!)
// 更新组件
info.cKey++
}
//钩子 - 文件上传超出限制
const uploadExceed: UploadProps['onExceed'] = (files) => {
refSignImageCore.value!.clearFiles()
const file = files[0] as UploadRawFile
file.uid = genFileId()
refSignImageCore.value!.handleStart(file)
}
</script>
<template>
<div class="flex flex-col mt-3 ml-4 flex-1 mr-10">
<div class="text-gray-600">
<a href="/" class="flex items-center">
<el-icon color="blue"><Back /></el-icon>
<div>返回首页</div>
</a>
</div>
<div class="text-xl mt-5 mb-5">
{{ info.title }}
</div>
<div class="flex flex-col mb-3 h-[38rem]">
<div class="mb-3 flex flex-row-reverse">
<!-- save -->
<el-button type="primary" class="ml-2">保存</el-button>
<!-- upload -->
<el-upload
ref="refSignImageCore"
:limit="1"
@exceed="uploadExceed"
accept="image/*"
@change="uploadChange"
:auto-upload="false"
:show-file-list="false"
>
<template #trigger>
<el-button type="primary">选择图片</el-button>
</template>
</el-upload>
</div>
<!-- 图片处理框 -->
<SignImageCore
:key="info.cKey"
:dialogVisible.sync="true"
:title="info.title"
:imgUrl="info.previewsImgUrl"
@getNewImg="info.getNewImg"
></SignImageCore>
</div>
</div>
</template>
<style scoped>
</style>
+237
View File
@@ -0,0 +1,237 @@
<script setup>
import { ref, onMounted, nextTick } from 'vue'
import 'tui-image-editor/dist/tui-image-editor.css'
// import 'tui-color-picker/dist/tui-color-picker.css'
import ImageEditor from 'tui-image-editor'
// 中文菜单
const locale_zh = {
ZoomIn: '放大',
ZoomOut: '缩小',
Hand: '手掌',
History: '历史',
Resize: '调整宽高',
Crop: '裁剪',
DeleteAll: '全部删除',
Delete: '删除',
Undo: '撤销',
Redo: '反撤销',
Reset: '重置',
Flip: '镜像',
Rotate: '旋转',
Draw: '画',
Shape: '形状标注',
Icon: '图标标注',
Text: '文字标注',
Mask: '遮罩',
Filter: '滤镜',
Bold: '加粗',
Italic: '斜体',
Underline: '下划线',
Left: '左对齐',
Center: '居中',
Right: '右对齐',
Color: '颜色',
'Text size': '字体大小',
Custom: '自定义',
Square: '正方形',
Apply: '应用',
Cancel: '取消',
'Flip X': 'X 轴',
'Flip Y': 'Y 轴',
Range: '区间',
Stroke: '描边',
Fill: '填充',
Circle: '圆',
Triangle: '三角',
Rectangle: '矩形',
Free: '曲线',
Straight: '直线',
Arrow: '箭头',
'Arrow-2': '箭头2',
'Arrow-3': '箭头3',
'Star-1': '星星1',
'Star-2': '星星2',
Polygon: '多边形',
Location: '定位',
Heart: '心形',
Bubble: '气泡',
'Custom icon': '自定义图标',
'Load Mask Image': '加载蒙层图片',
Grayscale: '灰度',
Blur: '模糊',
Sharpen: '锐化',
Emboss: '浮雕',
'Remove White': '除去白色',
Distance: '距离',
Brightness: '亮度',
Noise: '噪音',
'Color Filter': '彩色滤镜',
Sepia: '棕色',
Sepia2: '棕色2',
Invert: '负片',
Pixelate: '像素化',
Threshold: '阈值',
Tint: '色调',
Multiply: '正片叠底',
Blend: '混合色',
Width: '宽度',
Height: '高度',
'Lock Aspect Ratio': '锁定宽高比例'
}
// 画布组件自定义样式
const customTheme = {
"common.bi.image": "", // 左上角logo图片
"common.bisize.width": "0px",
"common.bisize.height": "0px",
"common.backgroundImage": "none",
"common.backgroundColor": "#f3f4f6",
"common.border": "1px solid #333",
// header
"header.backgroundImage": "none",
"header.backgroundColor": "#f3f4f6",
"header.border": "0px",
// load button
"loadButton.backgroundColor": "#fff",
"loadButton.border": "1px solid #ddd",
"loadButton.color": "#222",
"loadButton.fontFamily": "NotoSans, sans-serif",
"loadButton.fontSize": "12px",
"loadButton.display": "none", // 可以直接隐藏掉
// download button
"downloadButton.backgroundColor": "#fdba3b",
"downloadButton.border": "1px solid #fdba3b",
"downloadButton.color": "#fff",
"downloadButton.fontFamily": "NotoSans, sans-serif",
"downloadButton.fontSize": "12px",
"downloadButton.display": "none", // 可以直接隐藏掉
// icons default
"menu.normalIcon.color": "#8a8a8a",
"menu.activeIcon.color": "#555555",
"menu.disabledIcon.color": "#ccc",
"menu.hoverIcon.color": "#e9e9e9",
"submenu.normalIcon.color": "#8a8a8a",
"submenu.activeIcon.color": "#e9e9e9",
"menu.iconSize.width": "24px",
"menu.iconSize.height": "24px",
"submenu.iconSize.width": "32px",
"submenu.iconSize.height": "32px",
// submenu primary color
"submenu.backgroundColor": "#1e1e1e",
"submenu.partition.color": "#858585",
// submenu labels
"submenu.normalLabel.color": "#858585",
"submenu.normalLabel.fontWeight": "lighter",
"submenu.activeLabel.color": "#fff",
"submenu.activeLabel.fontWeight": "lighter",
// checkbox style
"checkbox.border": "1px solid #ccc",
"checkbox.backgroundColor": "#fff",
// rango style
"range.pointer.color": "#fff",
"range.bar.color": "#666",
"range.subbar.color": "#d1d1d1",
"range.disabledPointer.color": "#414141",
"range.disabledBar.color": "#282828",
"range.disabledSubbar.color": "#414141",
"range.value.color": "#fff",
"range.value.fontWeight": "lighter",
"range.value.fontSize": "11px",
"range.value.border": "1px solid #353535",
"range.value.backgroundColor": "#151515",
"range.title.color": "#fff",
"range.title.fontWeight": "lighter",
// colorpicker style
"colorpicker.button.border": "1px solid #1e1e1e",
"colorpicker.title.color": "#fff",
};
// props
const props = defineProps({
dialogVisible: {
type: Boolean,
default: () => {
return false;
},
},
title: {
type: String,
default: "",
},
imgUrl: {
type: String,
default: "",
},
});
const emit = defineEmits()
const instance = ref()
onMounted(() => {
nextTick(() => {
init() // 页面创建完成后调用
})
})
const init = ()=> {
instance.value = new ImageEditor(document.querySelector('#tui-image-editor'), {
includeUI: {
loadImage: {
path: props.imgUrl,
name: 'image'
},
menu: ['resize', 'crop', 'rotate', 'draw', 'shape', 'icon', 'text', 'filter'], // 底部菜单按钮列表 隐藏镜像flip和遮罩mask
initMenu: 'draw', // 默认打开的菜单项
menuBarPosition: 'bottom', // 菜单所在的位置
locale: locale_zh, // 本地化语言为中文
theme: customTheme // 自定义样式
},
cssMaxWidth: 400, // canvas 最大宽度
cssMaxHeight: 500 // canvas 最大高度
})
document.getElementsByClassName('tui-image-editor-main')[0].style.top = '45px' // 调整图片显示位置
document.getElementsByClassName(
'tie-btn-reset tui-image-editor-item help'
)[0].style.display = 'none' // 隐藏顶部重置按钮
}
// 保存图片,并上传
const save = ()=> {
const base64String = instance.value.toDataURL() // base64 文件
const data = window.atob(base64String.split(',')[1])
const ia = new Uint8Array(data.length)
for (let i = 0; i < data.length; i++) {
ia[i] = data.charCodeAt(i)
}
const blob = new Blob([ia], { type: 'image/png' }) // blob 文件
const form = new FormData()
form.append("avatarfile", blob);
uploadAvatar(form).then(res=>{
emit('getNewImg',res.imgUrl);
closeDialog()
})
}
</script>
<template>
<div id="tui-image-editor"></div>
</template>
<style lang="scss" scoped>
</style>
+167
View File
@@ -0,0 +1,167 @@
<script setup lang="ts">
import { onMounted, onUnmounted, reactive } from 'vue'
import { VideoPause,VideoPlay,Back,CopyDocument } from '@element-plus/icons-vue'
import { Jh_getTimeStamp,Jh_timeStampToTime,Jh_convertTimeStamp } from '@/utils/time'
import clipboard3 from 'vue-clipboard3'
import { ElMessage } from 'element-plus';
const info = reactive({
title: "时间戳转换",
nowTime: Jh_getTimeStamp(),
isPlay: true,
waitTimeStamp: Jh_getTimeStamp(),//待转换的时间戳
tranTimeStamp: 0, //转换后的时间戳
waitDate: Jh_timeStampToTime(Jh_getTimeStamp(), '{y}-{m}-{d} {h}:{i}:{s}'), //待转换时间
tranDate: '', //转换后时间
chooseTranStampOption: '0',
chooseTranDateOption: '0',
tranOptions: [
{
value: '0',
label: '毫秒(ms)'
},
{
value: '1',
label: '秒(s)'
}
]
})
let timer: any
//开始定时任务
const start = () => {
if (!timer) {
timer = setInterval(() => {
info.nowTime = Jh_getTimeStamp()
}, 1000)
}
}
//停止定时任务
const stop = () => {
clearInterval(timer)
timer = null
}
//进入
onMounted(() => {
start()
})
//消亡
onUnmounted(() => {
stop()
})
const isPlayChange = () => {
if (info.isPlay == true) {
info.isPlay = false
stop()
} else {
info.isPlay = true
start()
}
}
//时间日期相互转换
const timeTran = (type: string) => {
if (type == 'toStamp') {
info.tranTimeStamp = info.chooseTranStampOption == '0' ? Jh_convertTimeStamp(info.waitDate) : Jh_convertTimeStamp(info.waitDate) / 1000
} else {
//转换number类型
if (typeof(info.waitTimeStamp) === 'string') info.waitTimeStamp = parseInt(info.waitTimeStamp as string)
info.tranDate = Jh_timeStampToTime(info.waitTimeStamp, '{y}-{m}-{d} {h}:{i}:{s}')
}
}
//复制时间戳
const {toClipboard} = clipboard3()
const copyStamp = async () => {
try {
await toClipboard('' + info.nowTime)
ElMessage({
message: "复制成功",
type: "success",
duration: 1500
})
} catch (error) {
console.log("复制失败")
console.log(error)
ElMessage({
message: "复制失败",
type: "error",
duration: 1500
})
}
}
</script>
<template>
<div class="flex flex-col mt-3 ml-4 flex-1">
<div class="text-gray-600">
<a href="/" class="flex items-center">
<el-icon color="blue"><Back /></el-icon>
<div>返回首页</div>
</a>
</div>
<div class="text-xl mt-5 mb-5">
{{ info.title }}
</div>
<div class="flex flex-col">
<div class="flex flex-direction">
<el-text class="mr-2 w-12">现在</el-text>
<el-button class="mr-3" link @click="copyStamp()">{{ info.nowTime }} <el-icon class="ml-1 mr-1"><CopyDocument /></el-icon></el-button>
<el-button v-if="info.isPlay" type="danger" link class="flex items-center" @click="isPlayChange()"><el-icon class="mr-1" size="16"><VideoPlay/></el-icon>停止</el-button>
<el-button v-else="info.isPlay" type="primary" link class="flex items-center" @click="isPlayChange()"><el-icon class="mr-1" size="16"><VideoPause /></el-icon>开始</el-button>
</div>
<div class="flex flex-direction mt-4 justify-start">
<el-text class="mr-2 w-12">时间戳</el-text>
<el-input v-model="info.waitTimeStamp" class="h-8 mr-2 w-60 max-w-[30%]" placeholder="毫秒/秒">
<!-- <template #append>
<el-select v-model="info.chooseTranDateOption" placeholder="Select" class="w-24">
<el-option
v-for="item in info.tranOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template> -->
</el-input>
<el-button class="mr-2 max-w-[25%]" @click="timeTran('toDate')">转换</el-button>
<el-input v-model="info.tranDate" class="h-8 w-72 mr-2 max-w-[30%]" placeholder="北京时间"></el-input>
</div>
<div class="flex flex-direction mt-4 justify-start">
<el-text class="mr-2 w-12">时间</el-text>
<el-input v-model="info.waitDate" class="h-8 mr-2 w-60 max-w-[30%]"></el-input>
<el-button class="mr-2 max-w-[25%]" @click="timeTran('toStamp')">转换</el-button>
<el-input v-model="info.tranTimeStamp" :value="info.tranTimeStamp == 0 ? '' : info.tranTimeStamp" class="h-8 w-72 mr-2 max-w-[30%]" placeholder="时间戳">
<template #prepend>
<el-select v-model="info.chooseTranStampOption" placeholder="Select" class="w-24">
<el-option
v-for="item in info.tranOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-input>
</div>
</div>
<div>
<div class="text-xl mt-5 mb-5">时间戳</div>
<el-text>Unix 时间戳是从1970年1月1日UTC/GMT的午夜开始所经过的秒数不考虑闰秒</el-text>
</div>
</div>
</template>
<style scoped>
</style>
+90
View File
@@ -0,0 +1,90 @@
<script setup lang="ts">
import { reactive } from 'vue'
import { Back } from '@element-plus/icons-vue'
import { Codemirror } from "vue-codemirror"
import { copy } from '@/utils/string'
const info = reactive({
title: "Unicode转中文",
extensions: [],
content: '',
tranRes: '',
})
const clearRes = () => {
info.tranRes = ''
}
//to zh
//值转换中文
const toZH = () => {
//clear
clearRes()
let str = info.content
str = str.replace(/\\/g, "%");
// 转换中文
str = unescape(str);
info.tranRes = str
}
//to unicode
const toUnicode = () => {
//clear
clearRes()
//只转换中文
for (let i = 0; i < info.content.length; i++) {
if (/^[\u4E00-\u9FA5\uF900-\uFA2D]+$/.test(info.content[i])) {
let code = info.content.charCodeAt(i).toString(16)
info.tranRes += '\\u' + code
} else {
info.tranRes += info.content[i]
}
}
}
//copy
const copyRes = async () => {
copy(info.tranRes)
}
</script>
<template>
<div class="flex flex-col mt-3 ml-4 flex-1 mr-10">
<div class="text-gray-600">
<a href="/" class="flex items-center">
<el-icon color="blue"><Back /></el-icon>
<div>返回首页</div>
</a>
</div>
<div class="text-xl mt-5 mb-5">
{{ info.title }}
</div>
<div>
<div>
<codemirror
v-model="info.content"
placeholder=""
:style="{ height: '200px' }"
:autofocus="true"
:indent-with-tab="true"
:tabSize="2"
:extensions="info.extensions"
/>
</div>
<div class="mt-4">
<el-button type="primary" @click="toZH">unicode转中文</el-button>
<el-button type="primary" @click="toUnicode">中文转unicode</el-button>
<el-button type="primary" @click="copyRes">复制结果</el-button>
</div>
<div class="mt-3 min-h-md bg-gray-100 p-3 mb-3">
<el-input type="textarea" :rows="8" v-model="info.tranRes"></el-input>
</div>
</div>
</div>
</template>
<style scoped>
</style>
+68
View File
@@ -0,0 +1,68 @@
<script setup lang="ts">
import { reactive } from 'vue'
import { Back } from '@element-plus/icons-vue'
const info = reactive({
title: "在线字数统计",
content: '',
tranRes: '',
chinaCharNum: 0,
letterNum: 0,
punctuationHalfNum: 0,
punctuationFullNum: 0,
numberNum: 0,
emptyNum: 0,
sum: 0,
})
//内容发生变化,重新统计
const changeContent = () => {
//汉字
info.chinaCharNum = (info.content.match(/[\u4e00-\u9fa5]/g) || []).length;
//字母
info.letterNum = (info.content.match(/[a-zA-Z]/g) || []).length;
//符号 - 半角
info.punctuationHalfNum = (info.content.match(/[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]/g) || []).length;
//符号 - 全角
info.punctuationFullNum = (info.content.match(/[\u3002|\uff1f|\uff01|\uff0c|\u3001|\uff1b|\uff1a|\u201c|\u201d|\u2018|\u2019|\uff08|\uff09|\u300a|\u300b|\u3008|\u3009|\u3010|\u3011|\u300e|\u300f|\u300c|\u300d|\ufe43|\ufe44|\u3014|\u3015|\u2026|\u2014|\uff5e|\ufe4f|\uffe5]/g) || []).length;
//数字
info.numberNum = (info.content.match(/[\d+]/g) || []).length;
//空白字符
info.emptyNum = (info.content.match(/[\s*]/g) || []).length;
//合计
info.sum = info.chinaCharNum + info.letterNum + info.punctuationHalfNum + info.punctuationFullNum + info.numberNum + info.emptyNum;
}
</script>
<template>
<div class="flex flex-col mt-3 ml-4 flex-1 mr-10">
<div class="text-gray-600">
<a href="/" class="flex items-center">
<el-icon color="blue"><Back /></el-icon>
<div>返回首页</div>
</a>
</div>
<div class="text-xl mt-5 mb-5">
{{ info.title }}
</div>
<div>
<div>
<el-input type="textarea" :rows="16" v-model="info.content" @input="changeContent"></el-input>
</div>
<div class="mt-3 min-h-md bg-gray-100 p-3 mb-3 flex">
<div><el-text>总字数{{ info.sum }}</el-text></div>
<div class="ml-3"><el-text>汉字{{ info.chinaCharNum }}</el-text></div>
<div class="ml-3"><el-text>字母{{ info.letterNum }}</el-text></div>
<div class="ml-3"><el-text>数字{{ info.numberNum }}</el-text></div>
<div class="ml-3"><el-text>标点符号半角{{ info.punctuationHalfNum }}</el-text></div>
<div class="ml-3"><el-text>标点符号全角{{ info.punctuationFullNum }}</el-text></div>
<div class="ml-3"><el-text>空白字符{{ info.emptyNum }}</el-text></div>
</div>
</div>
</div>
</template>
<style scoped>
</style>
+20
View File
@@ -0,0 +1,20 @@
import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus';
//@ts-ignore忽略当前文件ts类型的检测否则有红色提示(打包会失败)
//入口文件main.ts全局安装element-plus,element-plus默认支持语言英语设置为中文
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
//vite-plugin-svg-icons
import 'virtual:svg-icons-register'
import router from './router'
//styles
import './styles/tailwind.css'
//element-plus css
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(router)
app.use(ElementPlus, {
locale: zhCn
})
app.mount('#app')
+17
View File
@@ -0,0 +1,17 @@
//通过vue-router插件实现模板路由配置
import { createRouter, createWebHashHistory } from 'vue-router'
import { constantRoute } from './router'
//创建路由器
const router = createRouter({
//路由模式hash
history: createWebHashHistory(),
routes: constantRoute,
//滚动行为
scrollBehavior() {
return {
left: 0,
top: 0,
}
},
})
export default router
+64
View File
@@ -0,0 +1,64 @@
//对外暴露配置路由(常量路由)
export const constantRoute = [
{
path: '/',
name: 'home',
component: () => import('@/components/Home/Home.vue'),
},
{
path: '/timetran',
component: () => import('@/components/TimeTran/TimeTran.vue'),
name: 'timetran'
},
{
path: '/MD5',
component: () => import('@/components/MD5/MD5.vue'),
name: 'MD5'
},
{
path: '/json',
component: () => import('@/components/JsonTran/JsonTran.vue'),
name: 'json'
},
{
path: '/reg',
component: () => import('@/components/RegTest/RegTest.vue'),
name: 'reg'
},
{
path: '/unicode',
component: () => import('@/components/Unicode/Unicode.vue'),
name: 'unicode'
},
{
path: '/wordcount',
component: () => import('@/components/WordCount/WordCount.vue'),
name: 'wordcount'
},
{
path: '/ip',
component: () => import('@/components/Ip/Ip.vue'),
name: 'ip'
},
{
path: '/scaletran',
component: () => import('@/components/ScaleTran/ScaleTran.vue'),
name: 'scaletran'
},
{
path: '/signimage',
component: () => import('@/components/SignImage/SignImage.vue'),
name: 'signimage'
},
{
path: '/404',
component: () => import('@/components/404/404.vue'),
name: '404',
},
{
//重定向
path: '/:pathMatch(.*)*',
redirect: '/404',
name: 'Any',
},
]
+3
View File
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
+93
View File
@@ -0,0 +1,93 @@
/**
* 10进制转58进制
*/
export function baseToScale58(num: number) {
var base = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
var result = '';
while (num > 0) {
var mod = num % 58;
result = base[mod] + result;
num = Math.floor(num / 58);
}
return result;
}
/**
* 58进制转10进制
* @param base58
*/
export function scale58ToBase(base58: string) {
var base = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
var result = 0;
var digitValue;
for (var i = 0; i < base58.length; i++) {
digitValue = base.indexOf(base58[i]);
result = result * 58 + digitValue; }
return result;
}
/**
* 10进制转62进制
*
* @param num
* @returns
*/
export function baseToScale62(num: number) {
var base62 = ''
while(num > 0){
var remainder = num % 62;
base62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".charAt(remainder) + base62;
var num = Math.floor(num / 62);
}
return base62
}
/**
* 62转10进制
*/
export function scale62ToBase(base62: string) {
var num = 0;
for(var i = 0; i < base62.length; i++){
num += "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".indexOf(base62.charAt(i)) * Math.pow(62, base62.length - i - 1);
}
return num
}
/**
* 10转64进制
* @returns
*/
export function baseToScale64(num: number) {
var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/';
var result = '';
do {
result = chars[num % 64] + result;
num = Math.floor(num / 64);
} while (num > 0);
return result;
}
/**
* 64转10进制
* @param base64
*/
export function scale64ToBase(base64: string) {
var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/';
var result = 0;
var index;
for (var i = 0; i < base64.length; i++) {
index = chars.indexOf(base64[i]);
result += index * Math.pow(64, base64.length - i - 1);
}
return result;
}
const StringUtils = {
baseToScale58,
scale58ToBase,
baseToScale62,
scale62ToBase,
baseToScale64,
scale64ToBase
}
export default StringUtils
+43
View File
@@ -0,0 +1,43 @@
import axios from "axios";
import { ElMessage } from "element-plus";
//创建axios实例
let request = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API,
timeout: 5000
})
//请求拦截器
request.interceptors.request.use(config => {
console.log(config)
return config;
});
//响应拦截器
request.interceptors.response.use((response) => {
return response.data;
}, (error) => {
//处理网络错误
let msg = '';
let status = error.response.status;
switch (status) {
case 401:
msg = "token过期";
break;
case 403:
msg = '无权访问';
break;
case 404:
msg = "请求地址错误";
break;
case 500:
msg = "服务器出现问题";
break;
default:
msg = "无网络";
}
ElMessage({
type: 'error',
message: msg
})
return Promise.reject(error);
});
export default request;
+60
View File
@@ -0,0 +1,60 @@
import { ElMessage } from 'element-plus';
import clipboard3 from 'vue-clipboard3'
/**
* 转义特殊字符
* str: string: 需处理的字符串
* reg: string: 需转义的特殊字符
*
* @param reg
*/
export function transferred(str: string, reg: string = "`~!@#$^&*()=|{}':;',\\[\\].<>/?~@#¥……&*();—|【】‘;:”“'。,、?") {
let pattern = new RegExp("[" + reg + "]");
let res = '';
Array.from(str).forEach((char: any) => {
res += char.replace(pattern, `\\${char}`);
});
return res;
}
/**
* 复制文本
* @param resStr 文本内容
* @returns
*/
export function copy(resStr: string) {
try {
//check
if (resStr == '') {
ElMessage({
message: "无可复制内容",
type: "warning",
duration: 1500
})
return
}
//copy
const {toClipboard} = clipboard3()
toClipboard(resStr)
ElMessage({
message: "复制成功",
type: "success",
duration: 1500
})
} catch (error) {
console.log("复制失败")
console.log(error)
ElMessage({
message: "复制失败",
type: "error",
duration: 1500
})
}
}
const StringUtils = {
transferred,
copy
}
export default StringUtils
+328
View File
@@ -0,0 +1,328 @@
// 时间转换工具类
const TimeUtils = {
Jh_getTimeStamp,
Jh_timeStampToTime,
Jh_convertTimeStamp,
Jh_timeStampToYMD,
Jh_isToday,
Jh_getYearMonth,
Jh_getPrevYear,
Jh_getNextYear,
Jh_getPrevYearMonth,
Jh_getNextYearMonth,
Jh_compareTimes,
Jh_isBetweenTimes,
Jh_isBetweenTimesByCurrent,
Jh_isBetweenTimesByCurrentAndEndTime,
getEndTime,
startOfDay,
endOfDay
}
export default TimeUtils
/**
* 获取当前日期0点(iso格式)
* @returns {string}
*/
export function startOfDay(isoDateStr: string): string {
if (!isoDateStr) return ''
return isoDateStr.substring(0, 10) + ' 00:00:00'
}
/**
* 获取当前日期的午夜(iso格式)
* @returns {string}
*/
export function endOfDay(isoDateStr: string): string {
if (!isoDateStr) return ''
return isoDateStr.substring(0, 10) + ' 23:59:59'
}
/**
* @description: 获取当前毫秒级时间戳(13位)
* @return {*}
*/
export function Jh_getTimeStamp(): number {
// let timestamp2 = Date.parse(new Date())
// const timestamp = Date.now()
const timestamp = new Date().getTime()
return timestamp
}
/**
* 将某个时间戳转化成 指定格式时间
* @param {date} time 时间 new Date().getTime()
* @param {string} cFormat {y}-{m}-{d} {h}:{i}:{s} {w}
*/
export function Jh_timeStampToTime(time: string | number | Date, cFormat: string): string {
if (arguments.length === 0) {
return ''
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
let date: Date
if (typeof time === 'object') {
date = time as Date
} else {
if (('' + time).length === 10) time = parseInt(time as string) * 1000
date = new Date(time)
}
const formatObj: any = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
w: date.getDay()
}
const time_str = format.replace(/{(y|m|d|h|i|s|w)+}/g, (result, key) => {
let value = formatObj[key]
if (key === 'w') {
return ['日', '一', '二', '三', '四', '五', '六'][value]
}
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
/**
* 将某个时间转化成时间戳
* 时间格式:2019-05-20 00:00:00 或 2019年5月1日 00:00:00
* 返回值:155664000000013位时间戳
*/
export function Jh_convertTimeStamp(time: string): number {
// 用正则主要是把“2019-05-20 00:00:00”转换成“2019/05/20 00:00:00”兼容ios
let newTime = time.replace(/-|\./g, '/')
// console.log(newTime);
// newTime = newTime.replace(/\./g, "-");
newTime = newTime.replace(/年/g, '/')
newTime = newTime.replace(/月/g, '/')
newTime = newTime.replace(/日/g, '')
if (newTime.length === 4) {
newTime = newTime + '/01/01 00:00:00'
}
if (newTime.length === 7) {
newTime = newTime + '/01 00:00:00'
}
if (newTime.length === 10) {
newTime = newTime + ' 00:00:00'
}
if (newTime.length === 16) {
newTime = newTime + ':00'
}
return Date.parse(newTime)
}
/**
* 毫秒级时间戳(13位)转年月日,不传time默认当前时间戳
* @param time 毫秒级时间戳(13位),不传time默认当前时间戳
* @param format 指定format,不传format默认:'{y}/{m}/{d}'
* @return 指定format时间,默认格式:2020/02/02
*/
export function Jh_timeStampToYMD(time?: number | null, format?: string | null) {
time = time ? time : Jh_getTimeStamp()
if (format) {
return Jh_timeStampToTime(time, format)
}
return Jh_timeStampToTime(time, '{y}/{m}/{d}')
}
// 某个时间是否是今天 time格式:2020-07-19 20:33:00
export function Jh_isToday(time: string): boolean {
let newTime = time.replace(/-/g, '')
newTime = newTime.substring(0, 8)
let currentTime: number | string = new Date().getTime()
currentTime = Jh_timeStampToTime(currentTime, '{y}{m}{d}')
return newTime === currentTime.toString()
}
// 获取当前年月 time格式:2020-07
export function Jh_getYearMonth(): string {
const timestamp = new Date().getTime()
return Jh_timeStampToTime(timestamp, '{y}-{m}')
}
// 获取 指定年的上一年 time格式:2020 | 2020年
export function Jh_getPrevYear(time: string): string {
let tempYear: number | string = time.substring(0, 4)
tempYear = parseInt(tempYear)
tempYear = tempYear - 1
const text = time.substring(4, 5)
let prevTime = ''
if (text === '年') {
prevTime = tempYear + '年'
} else {
prevTime = tempYear + text
}
return prevTime
}
// 获取 指定年的下一年 time格式:2020 | 2020年
export function Jh_getNextYear(time: string): string {
let tempYear: number | string = time.substring(0, 4)
tempYear = parseInt(tempYear)
tempYear = tempYear + 1
const text = time.substring(4, 5)
let nextTime = ''
if (text === '年') {
nextTime = tempYear + '年'
} else {
nextTime = tempYear + text
}
return nextTime
}
// 获取 指定年月的上一年月 time格式:2020-07 | 2020年07月
export function Jh_getPrevYearMonth(time: string): string {
let tempYear: number | string = time.substring(0, 4)
let tempMonth: number | string = time.substring(5, 7)
tempYear = parseInt(tempYear)
tempMonth = parseInt(tempMonth)
tempMonth = tempMonth - 1
if (tempMonth === 0) {
tempYear = tempYear - 1
tempMonth = 12
}
if (tempMonth < 10) {
tempMonth = '0' + tempMonth
}
const text = time.substring(4, 5)
let prevTime = ''
if (text === '年') {
prevTime = tempYear + '年' + tempMonth + '月'
} else {
prevTime = tempYear + text + tempMonth
}
return prevTime
}
// 获取 指定年月的下一年月 time格式:2020-07 | 2020年07月
export function Jh_getNextYearMonth(time: string): string {
let tempYear: number | string = time.substring(0, 4)
let tempMonth: number | string = time.substring(5, 7)
tempYear = parseInt(tempYear)
tempMonth = parseInt(tempMonth)
tempMonth = tempMonth + 1
if (tempMonth === 13) {
tempYear = tempYear + 1
tempMonth = 1
}
if (tempMonth < 10) {
tempMonth = '0' + tempMonth
}
const text = time.substring(4, 5)
let nextTime = ''
if (text === '年') {
nextTime = tempYear + '年' + tempMonth + '月'
} else {
nextTime = tempYear + text + tempMonth
}
return nextTime
}
/**
* @param time Date/String/Number
* @description getEndTime('2021-02-12')
* @returns '距离2021年2月12日还有118天0小时30分12秒'
*/
export function getEndTime(time: string): string {
const year = new Date(time).getFullYear()
const month = new Date(time).getMonth() + 1
const date = new Date(time).getDate()
const now = new Date()
const endDate = new Date(new Date(time).toLocaleDateString())
const leftTime = endDate.getTime() - now.getTime()
const leftsecond = parseInt((leftTime / 1000).toString())
const day = Math.floor(leftsecond / (60 * 60 * 24))
const hour = Math.floor((leftsecond - day * 24 * 60 * 60) / 3600)
const minute = Math.floor((leftsecond - day * 24 * 60 * 60 - hour * 3600) / 60)
const second = Math.floor(leftsecond - day * 60 * 60 * 24 - hour * 60 * 60 - minute * 60)
return `距离${year}${month}${date}日还有${day}${hour}小时${minute}${second}`
}
/**
* 判断某个时间是否在开始时间和结束时间范围内
* @param time 2020-07-19 20:33:00 | 2020/07/19 20:33:00
* @return true | false
*/
export function Jh_isBetweenTimes(time: string, startTime: string, endTime: string): boolean {
time = time.replace(/-/g, '/')
startTime = startTime.replace(/-/g, '/')
endTime = endTime.replace(/-/g, '/')
const time2 = new Date(time)
const startTime2 = new Date(startTime)
const endTime2 = new Date(endTime)
if (startTime2 <= time2 && time2 <= endTime2) {
return true
}
return false
}
/**
* 判断当前时间是否在某个时间段内
* @param time 2020-07-19 20:33:00 | 2020/07/19 20:33:00
* @return true | false
*/
export function Jh_isBetweenTimesByCurrent(beginTime: string, endTime: string): boolean {
beginTime = beginTime.replace(/-/g, '/')
endTime = endTime.replace(/-/g, '/')
const beginTime2 = new Date(beginTime)
const endTime2 = new Date(endTime)
const currentTime = new Date()
if (beginTime2 <= currentTime && currentTime <= endTime2) {
return true
}
return false
}
/**
* 判断某个时间是否在当前时间和结束时间范围内
* @param time 2020-07-19 20:33:00 | 2020/07/19 20:33:00
* @return true | false
*/
export function Jh_isBetweenTimesByCurrentAndEndTime(time: string, endTime: string): boolean {
const currentTime = new Date()
time = time.replace(/-/g, '/')
endTime = endTime.replace(/-/g, '/')
const time2 = new Date(time)
const endTime2 = new Date(endTime)
if (currentTime <= time2 && time2 <= endTime2) {
return true
}
return false
}
/**
* 比较两个时间大小
* @param time1 2019-02-02 || 2019-02-02 00:00:00
* @param time2 2019-02-02 || 2019-02-02 00:00:00
* @return time1>time2 为true
*/
export function Jh_compareTimes(time1: string, time2: string): boolean {
const newTime1 = Jh_convertTimeStamp(time1)
const newTime2 = Jh_convertTimeStamp(time2)
if (newTime1 > newTime2) {
return true // 第一个大
} else {
return false // 第二个大
}
}
/*
使用方法:
import { Jh_getTimeStamp, Jh_timeStampToTime } from '@/utils/timeUtils2'
import TimeUtils2 from '@/utils/timeUtils2'
// 时间戳转指定格式时间
TimeUtils2.Jh_timeStampToTime(1554803832, '{y}年{m}月{d}日 {h}:{i}:{s} 星期{w}') 1487065320000
TimeUtils2.Jh_timeStampToTime(new Date().getTime(), '{y}年{m}月{d}日 {h}:{i}:{s} 星期{w}')
*/
+53
View File
@@ -0,0 +1,53 @@
/**
* 是否ip地址
* @param ip
* @returns
*/
export function isIp(ip: string): boolean {
return /^((\d|[1-9]\d|1\d\d|2([0-4]\d|5[0-5]))(\.|$)){4}$/.test(ip) ? true : false;
}
/**
* 是否二进制
* @param data
*/
export function isBinary(data: string): boolean {
return /^([0-1]+)$/g.test(data) ? true : false;
}
/**
* 是否八进制
* @param data
* @returns
*/
export function isOctal(data: string): boolean {
return /^([0-7]+)$/g.test(data) ? true : false;
}
/**
* 是否十进制
* @param data
* @returns
*/
export function isDecimal(data: string): boolean {
return /^([0-9]+)$/g.test(data) ? true : false;
}
/**
* 是否十六进制
* @param data
* @returns
*/
export function isHexadecimal(data: string): boolean {
return /^([0-9a-fA-F]+)$/g.test(data) ? true : false;
}
const StringUtils = {
isIp,
isBinary,
isOctal,
isDecimal,
isHexadecimal
}
export default StringUtils
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />