init
This commit is contained in:
@@ -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>
|
||||
Reference in New Issue
Block a user