init
This commit is contained in:
+28
@@ -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>
|
||||
@@ -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)
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
|
||||
//响应格式
|
||||
export interface getIpResponseData {
|
||||
code: number,
|
||||
ip: string,
|
||||
address: string
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-empty :image-size="200" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
||||
@@ -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 }} <a href="https://beian.miit.gov.cn/" target="_blank">{{ beianNo }}</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -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>
|
||||
@@ -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 ? ' ' : '' }} </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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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
@@ -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')
|
||||
@@ -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
|
||||
@@ -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',
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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
|
||||
* 返回值:1556640000000,13位时间戳
|
||||
*/
|
||||
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}')
|
||||
|
||||
*/
|
||||
@@ -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
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
Reference in New Issue
Block a user