49 Commits
Author SHA1 Message Date
taoran 1dde7fa425 Merge branch 'feature/06' into pro/online 2024-08-23 14:43:05 +08:00
taoran 8b450b91cf docs 2024-08-23 14:42:23 +08:00
taoran d812590e4e Merge branch 'feature/06' into pro/online 2024-08-22 15:20:44 +08:00
taoran 6d471fef84 fix: 搜索工具异常 2024-08-21 18:08:54 +08:00
taoran dd38597204 docs 2024-08-21 12:02:33 +08:00
taoran d984af3ebe fix: 修复多次搜索后无结果问题,修复骰子工具骰子图片丢失问题 2024-07-27 00:24:12 +08:00
taoran 3604407dd7 fix: 路由名称seo优化 2024-07-09 18:25:17 +08:00
taoran ae62d59fa3 docs 2024-07-05 18:29:18 +08:00
taoran ba71938f01 fix 2024-07-04 10:38:48 +08:00
taoran c90524bac1 feat: add favicon 2024-06-28 12:12:50 +08:00
taoran 06219aba9a Merge branch 'feature/06' into pro/online 2024-06-21 11:48:16 +08:00
taoran 4e1354e5d2 fix: 首页小屏错位样式修复 2024-06-21 11:47:48 +08:00
taoran 9e9176fb68 fix 2024-06-21 11:16:48 +08:00
taoran 6082f1d789 feat: 工具详情页添加描述 2024-06-20 17:12:02 +08:00
taoran 95961dc05a docs 2024-06-20 15:01:17 +08:00
taoran c4bff93b44 feat: 新增工具:在线文本编辑/HTML获取; 回到顶部 2024-06-20 14:29:26 +08:00
taoran 6e8787a2d0 fix: debug 2024-06-18 18:25:21 +08:00
taoran 527b7f2f5d fix: add logo 2024-06-18 17:45:18 +08:00
taoran 95b7fec3b5 feat: 新增3个工具,样式修复,修改logo,保存桌面
新增功能:
 - js代码格式化/压缩
 - html代码格式化
 - css代码格式化/压缩
 - 保存到桌面

其他修改:
 - 修改logo
 - 修复一些样式问题
 - 修复一些已知的问题
2024-06-18 17:40:32 +08:00
osiris f05ee53440 Merge branch 'feature/06' 2024-06-05 09:30:36 +08:00
osiris 50a6e0be2a docs 2024-06-05 09:30:29 +08:00
osiris dd0022e219 Merge branch 'feature/06' 2024-06-05 09:29:07 +08:00
osiris 8ca5623b6b docs 2024-06-05 09:29:00 +08:00
osiris 18f9196212 Merge branch 'feature/06' 2024-06-05 09:24:42 +08:00
osiris 81145a70cd docs 2024-06-05 09:24:34 +08:00
osiris 2d9fb23cea Merge branch 'feature/06' 2024-06-05 09:12:35 +08:00
osiris 2090d61140 docs 2024-06-05 09:12:28 +08:00
osiris 4952fb94d6 Merge branch 'feature/06' 2024-06-05 09:11:38 +08:00
osiris 5ad691258b docs 2024-06-05 09:11:31 +08:00
osiris 90136ccbd2 feat: 单位换算 2024-05-31 11:11:42 +08:00
osiris 5a06a7aef3 fix: debug 2024-05-30 18:01:23 +08:00
osiris 144c22eb56 feat: 新增5个工具
- WT解析器
- Http状态码
- html实体转义(#)
- 颜色选择、转换器
- ASCII字形生成器
2024-05-30 17:54:06 +08:00
osiris a10c36cf73 style 2024-05-16 10:40:02 +08:00
osiris 6ef16b1da0 doc 2024-05-15 18:04:36 +08:00
osiris a4dc95ba38 doc 2024-05-15 17:39:15 +08:00
osiris a15c293322 doc 2024-05-15 17:36:39 +08:00
osiris 2c914205d7 doc 2024-05-15 17:31:59 +08:00
osiris 34c601c400 fix: debug 2024-05-15 17:28:59 +08:00
osiris 097d8fc0b8 feat: docker部署方案 2024-05-15 17:21:54 +08:00
osiris ad8a1179cd fix: debug 2024-05-14 15:18:57 +08:00
osiris c860a01416 feat: 搜索时允许根据描述搜索和一些样式调整 2024-05-14 15:05:57 +08:00
osiris efc1cb52eb style 2024-05-14 11:00:42 +08:00
osiris c0488db4e8 feat: 0514版本更新
feat:整理出一套纯前端
style: 关于本站内容调整,头部样式修改
fix: 简化mock数据格式
2024-05-14 10:37:21 +08:00
osiris a882cfeac9 feat: floor edit 2024-05-13 16:47:24 +08:00
osiris 9a55817794 feat: 只保留前端部分 2024-05-13 16:23:33 +08:00
osiris 68951056b1 fix: 添加隐私权政策,工具组件目录调整 2024-05-11 18:25:00 +08:00
osiris 4d72590f6f fix: dbeug 2024-04-30 11:07:28 +08:00
osiris 4bb328e5f6 docs 2024-04-11 16:24:48 +08:00
osiris de2ae48d58 feat: logo存储到本地 2024-04-11 15:55:30 +08:00
137 changed files with 7035 additions and 8247 deletions
+4
View File
@@ -0,0 +1,4 @@
node_modules
dist
.env.development
.env.production
+4 -6
View File
@@ -1,17 +1,15 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
# 网站名称
VITE_APP_TITLE = '工具坊'
# 网站域名
VITE_APP_WEBSITE = 'xxx.xxx.com'
VITE_APP_TITLE = 'Tools-Web'
# 简短描述
VITE_APP_DESC = '一个轻量的在线工具箱'
# base api
VITE_APP_BASE_API = '/v1'
# 服务端地址: https://xxx.xxx.com
# 服务端地址: https://xxx.xxx.com;
VITE_SERVE = ''
# 自定义启动host
VITE_HOST = '127.0.0.1'
# 是否使用mock: bool
VITE_IS_MOCK = 'true'
# 第三方反馈互动地址
VITE_FEEDBACK_URL = ''
# 广告位开关
+2
View File
@@ -25,3 +25,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?
public/sitemap.txt
+16
View File
@@ -0,0 +1,16 @@
# build stage
FROM node:18-alpine3.19 AS build-stage
# Set environment variables for non-interactive npm installs
ENV NPM_CONFIG_LOGLEVEL warn
WORKDIR /app
COPY . .
COPY .env.example .env.development
RUN npm install -g pnpm && pnpm i
RUN pnpm build
# production stage
FROM nginx:stable-alpine AS production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
+123 -41
View File
@@ -1,50 +1,57 @@
# 基于Vite4.x + Vue3.x + ElementPlus + Typescript + Tailwind的工具站
<div align="center">
<pre>
_______ _ __ __ _
|__ __| | | \ \ / / | |
| | ___ ___ | |___ _____\ \ /\ / /__| |__
| |/ _ \ / _ \| / __|______\ \/ \/ / _ \ '_ \
| | (_) | (_) | \__ \ \ /\ / __/ |_) |
|_|\___/ \___/|_|___/ \/ \/ \___|_.__/
[![license](https://img.shields.io/github/license/taoran1401/tools-web)](LICENSE)
</pre>
<p> 只需简单几步,即可快速搭建属于自己的在线工具箱。</p>
> 演示站点:https://tools.ranblogs.com
[![node](https://img.shields.io/badge/any_text-18.14.2-red?label=node)](node)
[![vue](https://img.shields.io/badge/any_text-3.3.10-origin?label=vue)](vue)
[![tailwindcss](https://img.shields.io/badge/any_text-3.3.5-yellow?label=tailwindcss)](tailwindcss)
[![elementplus](https://img.shields.io/badge/any_text-2.7-blue?label=element-plus)](elementplus)
[![license](https://img.shields.io/github/license/naroat/tools-web)](LICENSE)
# 介绍
</div>
免费开源的工具站,目前已开发数十种工具,对平板和手机适配进行了响应式设计,同时为了让seo更为友好做了ssr处理,搭建简单使用方便
## 目录
已有工具:
- 时间戳转换
- MD5在线加密
- Json在线转换
- 正则测试工具
- Unicode转中文
- IP查询
- 常用进制转换
- 在线图片处理
- 字数统计
- 随机密码生成
- URL编码/解码
- UUID生成器
- 手持弹幕
- ASCII码表
- 单位换算
- 色板
- 二维码生成
- 帮我决定
- 摩斯电码
- 生成随机数
- 数字转金额大写
- 文本对比
- markdown编辑器
- 文本转图片
- 网站favicon获取
- [功能展览](#功能展览)
- [开始使用](#开始使用)
- [Docker部署](#Docker部署)
- [手动部署](#手动部署)
- [工具列表](#工具列表)
- [其他](#其他)
# 快速使用
## 功能展示
安装`pnpm`, 为避免因为不同包管理器工具下载同一个依赖,可能版本不一样的问题,本项目强制使用`pnpm`
在线站点:<a href="https://tools.ranblogs.com" target="_blank">Tools-Web</a>
## 开始使用
### Docker部署
```
docker run -d --name tools-web --restart unless-stopped -p 8080:80 docker0796/tools-web:latest
```
访问:`http://127.0.0.1:8080`
### 手动部署
安装`pnpm`
```
npm install pnpm -g
```
克隆
```
git clone --depth=1 https://github.com/taoran1401/tools-web.git
git clone --depth=1 https://github.com/naroat/tools-web.git
```
安装
@@ -66,17 +73,92 @@ pnpm dev
打包
```
pnpm build:dev
pnpm build
```
# 后端功能(非必须)
打包seo静态页面:复制`.env.development`文件,并将文件名修改为`.env.production`,将里面的`NODE_ENV`的值改为`productio`,然后运行下面打包命令
```
pnpm build:pro
```
主要包含登录后的收藏功能,若只用工具可以忽略该部分
## 工具列表
项目地址:https://github.com/taoran1401/tools-web-api
- 开发运维
- 随机密码生成
- URL编码/解码
- UUID生成器
- 时间戳转换
- MD5在线加密
- Json在线转换
- 正则测试工具
- Unicode转中文
- HTTP状态码
- JWT解析
- html实体转义
- js代码格式化/压缩
- Html代码格式化
- Css代码格式化/压缩
- 文本处理
- 文本对比
- markdown编辑器
- ASCII字形生成器
- 文本去重
- 在线文本编辑/HTML获取
- 字数统计
- 教育学术
- 单位换算
- 摩斯电码
- 常用进制转换
- ASCII码表
- 长度单位转换
- 面积单位转换
- 重量单位转换
- 时间单位转换
- 温度单位转换
- 压力单位转换
- 热量单位转换
- 功率单位转换
- 图片处理
- 二维码生成
- 在线图片处理
- 文本转图片
- 图片分割
- 数据图表
- 柱状图
- 折线图
- 饼图
- 散点图
- 选择随机
- 生成随机数
- 帮我决定
- 抛硬币
- 投骰子
- 其他工具
- 数字转金额大写
- 手持弹幕
- 色板
- Color选择器
## 其他
Q: 我应该如何添加新功能?
A:
-`components/Tools/tools.ts`文件中添加工具信息
-`router/router.ts`中添加路由
- 拷贝示例目录`components/Tools/Example`修改名称,在这个拷贝出来的目录中开发工具即可
<br/>
Q: 我可以参与开发 `Tools-Web` 吗?
A: 当然可以,随时欢迎提交 `PR`
使用方法见内`README`
# 其他
有问题或建议请提`issue`
+5 -3
View File
@@ -3,7 +3,8 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/assets/logo.png" />
<link rel="shortcut icon" type="image/svg+xml" href="/src/assets/logo.png" />
<link rel="shortcut icon" type="image/svg+xml" href="/public/favicon.ico" />
<link rel="canonical" href="https://tools.ranblogs.com/">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="renderer" content="webkit">
<meta name="keywords" content="">
@@ -13,7 +14,7 @@
<meta property="og:site_name" content="">
<meta property="og:description" content="">
<meta property="og:type" content="website">
<meta property="og:image" content="https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/logo.png">
<meta property="og:image" content="/src/assets/logo.png">
<meta property="og:url" content="https://tools.ranblogs.com/">
<title></title>
<!-- 百度统计 -->
@@ -27,7 +28,8 @@
})();
</script>
</head>
<body style="background-color: var(--base-gray)">
<body class="bg-[--base-gray]">
<body>
<!--[if lt IE 9]>
<div class="notice chromeframe">您的浏览器版本<strong>很旧很旧</strong>,为了正常地访问网站,请升级您的浏览器</div>
<![endif]-->
-718
View File
@@ -1,718 +0,0 @@
//获取tools
function getTools() {
return [
{
id: 1,
title: '时间戳转换',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/Time.png',
desc: '在线时间戳转换工具以及获取当前时间戳',
url: '/timetran',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'MD5在线加密',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/Password%20fishing.png',
desc: 'MD5在线加密,长度包含32位、16位',
url: '/MD5',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'Json在线转换',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/json.png',
desc: '在线编辑json, 语法检查',
url: '/json',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: '正则测试工具',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/reg.png',
desc: '正则表达式测试工具, 常用正则表达式',
url: '/reg',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'Unicode转中文',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/union.png',
desc: 'Unicode和中文的相互转换',
url: '/unicode',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: '字数统计',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/ico_%E6%95%B0%E6%8D%AE%E7%BB%9F%E8%AE%A1_%E5%B7%A5%E4%BD%9C%E9%87%8F%E7%BB%9F%E8%AE%A1.png',
desc: '在线统计字符串的字数、段落、标点符号数量',
url: '/wordCount',
cateId: 3,
cate: '文本处理',
},
{
id: 1,
title: 'IP查询',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/IP.png',
desc: '在线查询ip地址、ip归属地',
url: '/ip',
cateId: 6,
cate: '查询相关',
},
{
id: 1,
title: '常用进制转换',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/%E8%BF%9B%E5%88%B6%E8%BD%AC%E6%8D%A2.png',
desc: '在线进制转换工具,可在2到64进制之间相互转换',
url: '/scaletran',
cateId: 4,
cate: '教育学术',
},
{
id: 1,
title: '在线图片处理',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/img.png',
desc: '在线图片裁剪,标注,滤镜,画等操作',
url: '/signimage',
cateId: 5,
cate: '图片处理',
},
{
id: 1,
title: '随机密码生成',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/keywords.png',
desc: '密码生成器、随机字符串生成,批量生成',
url: '/random_password',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'URL编码/解码',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/url.png',
desc: 'URL在线编码解码工具(UrlEncode编码 和 UrlDecode解码)',
url: '/urlencode',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'UUID生成器',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/%E7%BA%BF%E6%80%A7-%E7%94%9F%E6%88%90.png',
desc: '批量生成UUID',
url: '/uuid',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: '手持弹幕',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/%E5%BC%B9%E5%B9%95.png',
desc: '手持滚动弹幕',
url: '/barrage',
cateId: 7,
cate: '其他',
},
{
id: 1,
title: 'ASCII码表',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/icon_%E7%BC%96%E7%A0%81%E5%AF%B9%E7%85%A7.png',
desc: 'ASCII码表,控制代码、标准ASCII字符和非标准ASCII字符对照表',
url: '/ascii',
cateId: 4,
cate: '教育学术'
},
{
id: 1,
title: '单位换算',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/%E5%8D%95%E4%BD%8D%E6%8D%A2%E7%AE%97.png',
desc: '在线重量、长度、面积、时间、角度、速度、温度、压力、热量、功率等换算',
url: '/unit',
cateId: 4,
cate: '教育学术'
},
{
id: 1,
title: '色板',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/%E8%B0%83%E8%89%B2%E6%9D%BF.png',
desc: '包含纯色、渐变与阶梯色和常用色彩组合',
url: '/palettes',
cateId: 7,
cate: '其他工具'
},
{
id: 1,
title: '二维码生成',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/%E4%BA%8C%E7%BB%B4%E7%A0%81.png',
desc: '在线生成带logo、透明、艺术的二维码',
url: '/qrcode',
cateId: 5,
cate: '图片处理'
},
{
id: 1,
title: '帮我决定',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/choose.png',
desc: '选择困难,难以决定,今天吃什么,现在做什么,自定义选项都给你安排的明明白白',
url: '/decision',
cateId: 9,
cate: '选择随机'
},
{
id: 1,
title: '摩斯电码',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/medium.png',
desc: '支持中文的摩斯电码编码解码',
url: '/morse',
cateId: 4,
cate: '教育学术'
},
{
id: 1,
title: '生成随机数',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/randomnum.png',
desc: '可定制范围内进行随机数字,可用于抽奖、点名等用途',
url: '/random',
cateId: 9,
cate: '选择随机'
},
{
id: 1,
title: '数字转金额大写',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/text%20recognition.png',
desc: '在线数字一键转换成人民币大写,中文大写转换数字',
url: '/numberToChinese',
cateId: 7,
cate: '其他工具'
},
{
id: 1,
title: '文本对比',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/diff.png',
desc: '文本差异比对支持中文、英文、代码比对',
url: '/diff',
cateId: 3,
cate: '文本处理'
},
{
id: 1,
title: 'markdown编辑器',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/file-markdown-fill.png',
desc: '在线创建或编辑markdown, 实时预览,导出markdown',
url: '/markdown',
cateId: 3,
cate: '文本处理'
},
{
id: 1,
title: '文本转图片',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/text_to_img.png',
desc: '把文本转换成图片,生成长图,具有超多个性文字排版',
url: '/textToImg',
cateId: 5,
cate: '图片处理'
},
{
id: 1,
title: '网站favicon获取',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/IP.png',
desc: '获取网站logo、icon、favicon、标题、关键词、描述等信息',
url: '/webInfo',
cateId: 6,
cate: '查询相关',
},
{
id: 1,
title: '柱状图',
logo: '/images/logo/bar.png',
desc: '在线制作柱状图,像做表格一样制作可视化图表,支持导出静态或动态图表',
url: '/bar',
cateId: 8,
cate: '数据图表',
},
{
id: 1,
title: '折线图',
logo: '/images/logo/bar.png',
desc: '在线制作柱状图,像做表格一样制作可视化图表,支持导出静态或动态图表',
url: '/line',
cateId: 8,
cate: '数据图表',
},
{
id: 1,
title: '饼图',
logo: '/images/logo/bar.png',
desc: '在线制作柱状图,像做表格一样制作可视化图表,支持导出静态或动态图表',
url: '/pie',
cateId: 8,
cate: '数据图表',
},
{
id: 1,
title: '散点图',
logo: '/images/logo/bar.png',
desc: '在线制作柱状图,像做表格一样制作可视化图表,支持导出静态或动态图表',
url: '/scatter',
cateId: 8,
cate: '数据图表',
},
{
id: 1,
title: '抛硬币',
logo: '/images/logo/bar.png',
desc: '在线抛硬币,选择困难那么交给硬币来帮你选择吧',
url: '/coin',
cateId: 9,
cate: '选择随机',
},
{
id: 1,
title: '投骰子',
logo: '/images/logo/bar.png',
desc: '在线投骰子,可自定义骰子数量,简单好用的骰子工具',
url: '/dice',
cateId: 9,
cate: '选择随机',
},
{
id: 1,
title: '文本去重',
logo: '/images/logo/bar.png',
desc: '可以删除或去除文本或字符串中的重复行',
url: '/textRemoveDuplicate',
cateId: 3,
cate: '文本处理',
},
{
id: 1,
title: '图片分割',
logo: '/images/logo/bar.png',
desc: '将图片分割成四宫格、九宫格、十六宫格,支持自定义行与列',
url: '/imgCut',
cateId: 5,
cate: '图片处理',
},
]
}
//获取tools分类已经对应的工具
function getToolsCate() {
return [
{
id: 2,
title: '开发运维',
icon: '',
list: [
{
id: 1,
title: '随机密码生成',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/keywords.png',
desc: '密码生成器、随机字符串生成,批量生成',
url: '/random_password',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'URL编码/解码',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/url.png',
desc: 'URL在线编码解码工具(UrlEncode编码 和 UrlDecode解码)',
url: '/urlencode',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'UUID生成器',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/%E7%BA%BF%E6%80%A7-%E7%94%9F%E6%88%90.png',
desc: '批量生成UUID',
url: '/uuid',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: '时间戳转换',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/Time.png',
desc: '在线时间戳转换工具以及获取当前时间戳',
url: '/timetran',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'MD5在线加密',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/Password%20fishing.png',
desc: 'MD5在线加密,长度包含32位、16位',
url: '/MD5',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'Json在线转换',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/json.png',
desc: '在线编辑json, 语法检查',
url: '/json',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: '正则测试工具',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/reg.png',
desc: '正则表达式测试工具, 常用正则表达式',
url: '/reg',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'Unicode转中文',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/union.png',
desc: 'Unicode和中文的相互转换',
url: '/unicode',
cateId: 2,
cate: '开发运维',
}
]
},
{
id: 3,
title: '文本处理',
icon: '',
list: [
{
id: 1,
title: '文本对比',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/diff.png',
desc: '文本差异比对支持中文、英文、代码比对',
url: '/diff',
cateId: 3,
cate: '文本处理'
},
{
id: 1,
title: 'markdown编辑器',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/file-markdown-fill.png',
desc: '在线创建或编辑markdown, 实时预览,导出markdown',
url: '/markdown',
cateId: 3,
cate: '文本处理'
},
{
id: 1,
title: '字数统计',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/ico_%E6%95%B0%E6%8D%AE%E7%BB%9F%E8%AE%A1_%E5%B7%A5%E4%BD%9C%E9%87%8F%E7%BB%9F%E8%AE%A1.png',
desc: '在线统计字符串的字数、段落、标点符号数量',
url: '/wordCount',
cateId: 3,
cate: '文本处理',
},
{
id: 1,
title: '文本去重',
logo: '/images/logo/bar.png',
desc: '可以删除或去除文本或字符串中的重复行',
url: '/textRemoveDuplicate',
cateId: 3,
cate: '文本处理',
},
]
},
{
id: 4,
title: '教育学术',
icon: '',
list: [
{
id: 1,
title: '单位换算',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/%E5%8D%95%E4%BD%8D%E6%8D%A2%E7%AE%97.png',
desc: '在线重量、长度、面积、时间、角度、速度、温度、压力、热量、功率等换算',
url: '/unit',
cateId: 4,
cate: '教育学术'
},
{
id: 1,
title: '摩斯电码',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/medium.png',
desc: '支持中文的摩斯电码编码解码',
url: '/morse',
cateId: 4,
cate: '教育学术'
},
{
id: 1,
title: '常用进制转换',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/%E8%BF%9B%E5%88%B6%E8%BD%AC%E6%8D%A2.png',
desc: '在线进制转换工具,可在2到64进制之间相互转换',
url: '/scaletran',
cateId: 4,
cate: '教育学术',
},
{
id: 1,
title: 'ASCII码表',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/icon_%E7%BC%96%E7%A0%81%E5%AF%B9%E7%85%A7.png',
desc: 'ASCII码表,控制代码、标准ASCII字符和非标准ASCII字符对照表',
url: '/ascii',
cateId: 4,
cate: '教育学术'
},
]
},
{
id: 5,
title: '图片处理',
icon: '',
list: [
{
id: 1,
title: '二维码生成',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/%E4%BA%8C%E7%BB%B4%E7%A0%81.png',
desc: '在线生成带logo、透明、艺术的二维码',
url: '/qrcode',
cateId: 5,
cate: '图片处理'
},
{
id: 1,
title: '在线图片处理',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/img.png',
desc: '在线图片裁剪,标注,滤镜,画等操作',
url: '/signimage',
cateId: 5,
cate: '图片处理',
},
{
id: 1,
title: '文本转图片',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/text_to_img.png',
desc: '把文本转换成图片,生成长图,具有超多个性文字排版',
url: '/textToImg',
cateId: 5,
cate: '图片处理'
},
{
id: 1,
title: '图片分割',
logo: '/images/logo/bar.png',
desc: '将图片分割成四宫格、九宫格、十六宫格,支持自定义行与列',
url: '/imgCut',
cateId: 5,
cate: '图片处理',
},
]
},
{
id: 6,
title: '查询相关',
icon: '',
list: [
{
id: 1,
title: 'IP查询',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/IP.png',
desc: '在线查询ip地址、ip归属地',
url: '/ip',
cateId: 6,
cate: '查询相关',
},
{
id: 1,
title: '网站favicon获取',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/text_to_img.png',
desc: '获取网站logo、icon、favicon、标题、关键词、描述等信息',
url: '/webInfo',
cateId: 6,
cate: '查询相关',
}
]
},
{
id: 8,
title: '数据图表',
icon: '',
list: [
{
id: 1,
title: '柱状图',
logo: '/images/logo/bar.png',
desc: '在线制作柱状图,像做表格一样制作可视化图表,支持导出静态或动态图表',
url: '/bar',
cateId: 8,
cate: '数据图表',
},{
id: 1,
title: '折线图',
logo: '/images/logo/bar.png',
desc: '在线制作折线图,像做表格一样制作可视化图表,支持导出静态或动态图表',
url: '/line',
cateId: 8,
cate: '数据图表',
},
{
id: 1,
title: '饼图',
logo: '/images/logo/bar.png',
desc: '在线制作饼图,像做表格一样制作可视化图表,支持导出静态或动态图表',
url: '/pie',
cateId: 8,
cate: '数据图表',
},
{
id: 1,
title: '散点图',
logo: '/images/logo/bar.png',
desc: '在线制作散点图,像做表格一样制作可视化图表,支持导出静态或动态图表',
url: '/scatter',
cateId: 8,
cate: '数据图表',
}
]
},
{
id: 9,
title: '选择随机',
icon: '',
list: [
{
id: 1,
title: '生成随机数',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/randomnum.png',
desc: '可定制范围内进行随机数字,可用于抽奖、点名等用途',
url: '/random',
cateId: 9,
cate: '选择随机'
},
{
id: 1,
title: '帮我决定',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/choose.png',
desc: '选择困难,难以决定,今天吃什么,现在做什么,自定义选项都给你安排的明明白白',
url: '/decision',
cateId: 9,
cate: '选择随机'
},
{
id: 1,
title: '抛硬币',
logo: '/images/logo/bar.png',
desc: '在线抛硬币,选择困难那么交给硬币来帮你选择吧',
url: '/coin',
cateId: 9,
cate: '选择随机',
},
{
id: 1,
title: '投骰子',
logo: '/images/logo/bar.png',
desc: '在线投骰子,可自定义骰子数量,简单好用的骰子工具',
url: '/dice',
cateId: 9,
cate: '选择随机',
},
]
},
{
id: 7,
title: '其他工具',
icon: '',
list: [
{
id: 1,
title: '数字转金额大写',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/text%20recognition.png',
desc: '在线数字一键转换成人民币大写,中文大写转换数字',
url: '/numberToChinese',
cateId: 7,
cate: '其他工具'
},
{
id: 1,
title: '手持弹幕',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/%E5%BC%B9%E5%B9%95.png',
desc: '手持滚动弹幕',
url: '/barrage',
cateId: 7,
cate: '其他工具',
},
{
id: 1,
title: '色板',
logo: 'https://baseran2.oss-cn-shenzhen.aliyuncs.com/toools-web/icon/%E8%B0%83%E8%89%B2%E6%9D%BF.png',
desc: '包含纯色、渐变与阶梯色和常用色彩组合',
url: '/palettes',
cateId: 7,
cate: '其他工具'
}
]
}
]
}
export default [
{
url: '/v1/tools',
method: 'get',//请求方式
response: (request) => {
//接收参数
const { cateId, title } = request.query
//调用获取用户信息函数,用于判断是否有此用户
let list = getTools()
//标题筛选
if (title != '') {
list = list.filter(item => {
let tmpValue = item.title.toLowerCase()
return tmpValue.indexOf(title.toLowerCase()) !== -1;
});
}
//分类筛选
if (cateId > 0) {
list = list.filter(item => {
return item.cateId == cateId;
});
}
return { code: 200, message: 'ok', data: list }
},
},
{
url: '/v1/tool/cates',
method: 'get',//请求方式
response: ({ body }) => {
const list = getToolsCate()
return { code: 200, message: 'ok', data: list }
},
},
{
url: '/v1/tool/recommends',
method: 'get',//请求方式
response: () => {
let list = [] as any
//获取推荐数量
let num = 10
const toolList = getTools()
//使用Set数据结构来存储已经生成的随机数
const set = new Set();
while (set.size < num) {
// 生成随机数
const randomNum = Math.floor(Math.random() * toolList.length);
// 如果随机数不在Set中,则添加到Set中并赋值给randomNumber变量
if (!set.has(randomNum)) {
set.add(randomNum);
list.push(toolList[randomNum])
}
}
return { code: 200, message: 'ok', data: list }
},
}
]
+10
View File
@@ -0,0 +1,10 @@
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
+23 -18
View File
@@ -5,43 +5,50 @@
"type": "module",
"scripts": {
"dev": "vite",
"build:dev": "vue-tsc && vite build --mode development",
"build:pro": "vue-tsc && vite build --mode production",
"preview": "vite preview",
"preinstall": "node ./scripts/preinstall.js"
"build": "vue-tsc --noEmit && node --max_old_space_size=8192 node_modules/vite/bin/vite.js build --mode development",
"build:pro": "vue-tsc --noEmit && node --max_old_space_size=8192 node_modules/vite/bin/vite.js build --mode production",
"preview": "vite preview"
},
"dependencies": {
"@codemirror/commands": "^6.3.0",
"@codemirror/commands": "^6.3.2",
"@codemirror/lang-javascript": "^6.2.1",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/language": "^6.9.2",
"@codemirror/language": "^6.9.3",
"@codemirror/lint": "^6.4.2",
"@codemirror/search": "^6.5.4",
"@codemirror/state": "^6.3.3",
"@codemirror/search": "^6.5.5",
"@codemirror/state": "^6.4.1",
"@codemirror/theme-one-dark": "^6.1.2",
"@codemirror/view": "^6.22.0",
"@element-plus/icons-vue": "^2.1.0",
"@codemirror/view": "^6.22.1",
"@element-plus/icons-vue": "^2.3.1",
"@kangc/v-md-editor": "^2.3.18",
"@toast-ui/editor": "^3.2.2",
"@vueuse/core": "^10.9.0",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.6.2",
"colord": "^2.9.3",
"csso": "^5.0.5",
"default-passive-events": "^2.0.0",
"echarts": "^5.5.0",
"element-plus": "^2.4.2",
"element-plus": "^2.4.3",
"figlet": "^1.7.0",
"highlight.js": "^11.9.0",
"html2canvas": "^1.4.1",
"imorse": "^1.0.0",
"jwt-decode": "^4.0.0",
"lodash": "^4.17.21",
"prettier": "^3.3.2",
"prismjs": "^1.29.0",
"qrcode-vue3": "^1.6.8",
"sass": "^1.69.5",
"sass-loader": "^13.3.2",
"terser": "^5.31.1",
"ts-md5": "^1.3.1",
"tui-image-editor": "^3.15.3",
"uuid": "^9.0.1",
"v-code-diff": "^1.8.0",
"vite-plugin-seo-prerender": "^0.5.0",
"vue": "^3.3.4",
"vue": "^3.3.10",
"vue-clipboard3": "^2.0.0",
"vue-codemirror": "^6.1.1",
"vue3-danmaku": "^1.6.0",
@@ -49,20 +56,18 @@
"xlsx": "^0.18.5"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue": "^4.5.1",
"autoprefixer": "^10.4.16",
"fast-glob": "^3.3.2",
"less": "^4.2.0",
"mockjs": "^1.1.0",
"path": "^0.12.7",
"pinia": "^2.1.7",
"tailwindcss": "^3.3.5",
"typescript": "^5.0.2",
"vite": "^4.4.5",
"typescript": "^5.3.2",
"vite": "^4.5.1",
"vite-plugin-html": "^3.2.0",
"vite-plugin-mock": "2.9.6",
"vite-plugin-svg-icons": "^2.0.1",
"vue-router": "^4.2.5",
"vue-tsc": "^1.8.5"
"vue-tsc": "^1.8.24"
}
}
+4431 -6313
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 795 KiB

+1 -1
View File
@@ -23,7 +23,7 @@ const componentStore = useComponentStore()
v-model="componentStore.leftComDrawer"
direction="ltr"
>
<Left bgColor="#fff"></Left>
<Left></Left>
</el-drawer>
<!-- right -->
-38
View File
@@ -1,38 +0,0 @@
//统一管理咱们项目用户相关的接口
import request from '@/utils/request'
import type {
ToolsReqData,
ToolsResponseData,
ToolCateResponseData,
ToolRecommendResponseData,
ToolCollectResponseData,
CollectReqData,
GetToolCollectResponseData
} from './type'
//项目用户相关的请求地址
enum API {
//获取tools
GET_TOOLS = '/tools',
//获取tool cate
GET_TOOL_CATES = '/tool/cates',
//获取推荐
GET_TOOL_RECOMMEND = '/tool/recommends',
//收藏工具
COLLECT = '/tool/collects',
//我的收藏
GET_TOOL_COLLECTS = '/get/tool/collects',
}
//获取tools
export const getTools = (data: ToolsReqData) => request.get<any, ToolsResponseData>(API.GET_TOOLS, {
params: data
})
//获取tool cate
export const getToolCate = () => request.get<any, ToolCateResponseData>(API.GET_TOOL_CATES)
//获取推荐
export const getToolRecommend = () => request.get<any, ToolRecommendResponseData>(API.GET_TOOL_RECOMMEND)
//收藏工具
export const toolCollect = (data: CollectReqData) => request.post<any, ToolCollectResponseData>(API.COLLECT + '/' + data.toolId)
//我的收藏
export const getToolCollect = () => request.get<any, GetToolCollectResponseData>(API.GET_TOOL_COLLECTS)
-53
View File
@@ -1,53 +0,0 @@
import request from '@/utils/request'
import type {
LoginReqData,
LoginResponseData,
LogoutResponseData,
RegisterReqData,
RegisterResponseData,
SendCodeEmailResponseData,
sendCodeEmailReq,
UserInfoResponseData,
ForgetReqData,
ForgetResponseData
} from './type'
//项目用户相关的请求地址
enum API {
//login
LOGIN = '/login',
//logout
LOGOUT = '/logout',
//register
REGISTER = '/register',
//forget
FORGET = '/forget/password',
//注册 - 发送邮箱验证码
SEND_CODE_EMAIL = '/send/register/code',
//忘记密码 - 发送邮箱验证码
SEND_FORGET_CODE_EMAIL = '/send/forget/password/code',
//获取用户信息
USER_INFO = '/user/info'
}
//login
export const login = (data: LoginReqData) => request.post<any, LoginResponseData>(API.LOGIN, data)
//logout
export const logout = () => request.post<any, LogoutResponseData>(API.LOGOUT)
//register
export const register = (data: RegisterReqData) => request.post<any, RegisterResponseData>(API.REGISTER, data)
//forget
export const forget = (data: ForgetReqData) => request.post<any, ForgetResponseData>(API.FORGET, data)
//register - send code
export const sendCodeEmail = (data: sendCodeEmailReq) => request.post<any, SendCodeEmailResponseData>(API.SEND_CODE_EMAIL, data)
//forget - send code
export const sendForgetCodeEmail = (data: sendCodeEmailReq) => request.post<any, SendCodeEmailResponseData>(API.SEND_FORGET_CODE_EMAIL, data)
//获取用户信息
export const userInfo = () => request.get<any, UserInfoResponseData>(API.USER_INFO)
-49
View File
@@ -1,49 +0,0 @@
import { ResponseData } from "../common"
export interface LoginReqData {
email: string,
password: string
}
export interface RegisterReqData {
email: string,
code: string,
password: string,
password_confirmation: string
}
export interface ForgetReqData {
email: string,
code: string,
password: string,
password_confirmation: string
}
export interface sendCodeEmailReq {
email: string
}
export interface LoginResponseData extends ResponseData {
}
export interface LogoutResponseData extends ResponseData {
}
export interface RegisterResponseData extends ResponseData {
}
export interface ForgetResponseData extends ResponseData {
}
export interface SendCodeEmailResponseData extends ResponseData {
}
export interface UserInfoResponseData extends ResponseData {
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

+18 -24
View File
@@ -1,34 +1,28 @@
<script setup lang="ts">
// import { ref } from 'vue'
// import { copy } from '@/utils/string'
// const info = reactive({
// title: "关于本站",
// })
const feedBackUrl = import.meta.env.VITE_FEEDBACK_URL
//copy
// const copyRes = async (resStr: string) => {
// copy(resStr)
// }
</script>
<template>
<div class="flex flex-col mt-3 flex-1">
<div class="bg-white rounded-md p-5">
<br>
<p>本项目基于 <el-link class="" href="https://github.com/taoran1401/tools-web" target="_blank" type="primary">tools-web</el-link> 构建</p>
<br>
<p class=""><span class="bg-gray-200 p-1">ctrl + D</span>收藏本站以便以后使用</p>
<br>
<p class="font-bold">联系作者/问题反馈</p>
<br>
<p>反馈中心<el-link :href="feedBackUrl">{{ feedBackUrl }}</el-link></p>
<br>
<p>邮箱taoran0796@163.com</p>
<br>
<div class="flex flex-col mt-8 flex-1 items-center bg-white rounded-md p-10">
<div class="p-5 w-2/3">
<h1 class="text-2xl font-bold">关于 Tools-Web</h1>
<p class="mt-6">
<el-text><el-link href="https://github.com/naroat/tools-web" target="_blank" type="primary">Tools-Web</el-link> 是一款免费开源的轻量在线工具箱包含开发文本媒体图表生活查询等实用工具如果对您有帮助请将其分享给您的朋友并且添加到收藏夹中顺便再点个</el-text>
</p>
<h1 class="text-2xl font-bold mt-6 mb-6">技术</h1>
<p>
<el-text>主要基于Vite + Vue + ElementPlus + Typescript + TailwindCss开发某些工具使用了第三方开源库您可以在仓库的 <el-link href="https://github.com/naroat/tools-web/blob/master/package.json" target="_blank" type="primary">package.json</el-link> 文件中找到完整的列表</el-text>
</p>
<h1 class="text-2xl font-bold mt-6 mb-6">发现了 Bug</h1>
<p>
<el-text>如果您发现了 Bug或者某些功能未能按预期工作请在 GitHub 仓库的 <el-link type="primary" target="_blank" href="https://github.com/naroat/tools-web/issues/new" class="">issues</el-link> 中提交错误报告</el-text>
</p>
</div>
</div>
</template>
<style scoped>
.title{
/* rgb(51, 54, 57) */
}
</style>
+7 -58
View File
@@ -1,14 +1,12 @@
<script setup lang="ts">
import { onMounted,reactive } from 'vue';
import { onMounted } from 'vue';
import { RouterLink } from "vue-router"
import { Star } from '@element-plus/icons-vue'
// import { Star } from '@element-plus/icons-vue'
import { useToolsStore } from '@/store/modules/tools'
import { useUserStore } from '@/store/modules/user'
import { ElMessage } from 'element-plus'
// import { ElMessage } from 'element-plus'
import { useRoute } from "vue-router"
//store
const toolsStore = useToolsStore()
const userStore = useUserStore()
const route = useRoute()
// const getToolsCate = async () => {
// try {
@@ -18,25 +16,6 @@ const route = useRoute()
// }
// }
//收藏
const toolCollect = reactive({
toolId: 0
})
const collect = async (toolId) => {
try {
if (!userStore.isLogin()) {
//未登录,弹出登录窗口
ElMessage.error('请登录')
return
}
toolCollect.toolId = toolId
await toolsStore.toolCollect(toolCollect)
// ElMessage.success('收藏成功')
} catch (error: any) {
ElMessage.error(error.message)
}
return
}
onMounted(() => {
// getToolsCate()
@@ -50,39 +29,6 @@ onMounted(() => {
<template>
<div class="md:mr-6 c-xs:mr-0">
<!-- collect -->
<div id="collect">
<!-- cate title -->
<div class="md:mt-8 md:mb-8 c-xs:mt-1 c-xs:mb-4 text-xl font-bold text-[--base-black]">
我的收藏
</div>
<!-- card -->
<div class="flex justify-between flex-wrap self-card-div c-xs:ml-3" :gutter="10" v-if="toolsStore.collect.length > 0">
<router-link v-for="(item, index) in toolsStore.collect" :key="index" :to="item.url" class="flex flex-col mt-5 border-solid rounded-2xl border-gray w-[24%] p-2 bg-white hover:shadow-md c-xs:w-[99.5%] c-md:w-[24%] c-sm:w-[32%] p-5">
<div class="flex items-center border-b pb-2">
<el-image :src="item.logo" class="w-10 h-10 min-h-[2.5rem] min-w-[2.5rem] rounded-full"></el-image>
<div class="flex flex-col ml-2 w-full">
<div class="flex">
<div class="font-semibold text-lg line-clamp-1">{{ item.title }}</div>
</div>
<div class="flex justify-between">
<el-text size="small">{{ item.cate }}</el-text>
<el-button :icon="Star" circle size="small" @click.prevent="collect(item.id)" type="warning"/>
</div>
</div>
</div>
<div class="flex items-center justify-between mt-2">
<el-text line-clamp="2">{{ item.desc }}</el-text>
</div>
</router-link>
<!-- 占位 div -->
<div class="w-[24%] c-md:w-[24%] c-sm:w-[32%]"></div>
</div>
<div v-else class="bg-white rounded-2xl">
<el-empty description="暂无收藏" />
</div>
</div>
<!-- list -->
<div v-for="(cate, index) in toolsStore.cates" :key="index">
<!-- cate title -->
@@ -100,7 +46,7 @@ onMounted(() => {
</div>
<div class="flex justify-between">
<el-text size="small">{{ item.cate }}</el-text>
<el-button :icon="Star" circle size="small" @click.prevent="collect(item.id)" type="warning" :plain="!toolsStore.collectIds.includes(item.id)"/>
<!-- <el-button :icon="Star" circle size="small" @click.prevent="collect(item.id)" type="warning" :plain="!toolsStore.collectIds.includes(item.id)"/> -->
</div>
</div>
</div>
@@ -112,6 +58,9 @@ onMounted(() => {
<div class="w-[24%] c-md:w-[24%] c-sm:w-[32%] "></div>
</div>
</div>
<!-- 返回顶部 -->
<el-backtop :right="10" :bottom="50" />
</div>
</template>
@@ -3,8 +3,7 @@
import { onMounted, reactive } from 'vue';
import { useRoute } from 'vue-router'
import { useToolsStore } from '@/store/modules/tools'
import { useUserStore } from '@/store/modules/user'
import { ElMessage } from 'element-plus'
// import { ElMessageBox } from 'element-plus'
import {rtrim} from '@/utils/string'
const props = defineProps({
title: String,
@@ -19,7 +18,6 @@ const searchParam = reactive({
})
//store
const toolsStore = useToolsStore()
const userStore = useUserStore()
//根据路由查询tool id
const getToolInfo = async () => {
@@ -27,26 +25,13 @@ const getToolInfo = async () => {
searchParam.route = rtrim(routeStr, '/')
await toolsStore.getToolInfo(searchParam)
}
//收藏
const toolCollect = reactive({
toolId: 0
})
const collect = async (toolId) => {
try {
if (!userStore.isLogin()) {
//未登录,弹出登录窗口
ElMessage.error('请登录')
return
}
toolCollect.toolId = toolId
await toolsStore.toolCollect(toolCollect)
// ElMessage.success('收藏成功')
} catch (error: any) {
ElMessage.error(error.message)
}
return
}
// const collect = () => {
// ElMessageBox({
// title: '提示',
// message: '请使用快捷键`Ctrl+D`进行收藏'
// })
// }
onMounted(() => {
getToolInfo()
@@ -68,10 +53,6 @@ onMounted(() => {
<div class="text-xl">
{{ props.title }}
</div>
<div>
<el-button type="warning" :plain="!toolsStore.collectIds.includes(toolsStore.toolInfo.id)" @click="collect(toolsStore.toolInfo.id)">收藏</el-button>
<!-- <el-button type="danger">取消收藏</el-button> -->
</div>
</div>
</template>
+4 -5
View File
@@ -1,12 +1,11 @@
<script setup lang="ts">
import { ref } from 'vue'
const beianNo = ref('粤ICP备19038804号-1')
const copyright = ref('ranblogs.com')
import { ref } from 'vue';
const appDesc = ref(import.meta.env.VITE_APP_DESC || '')
</script>
<template>
<div class="bg-white w-full rounded-2xl z-10 p-5 text-center">
Copyright © 2023 · {{ copyright }} &nbsp;&nbsp; <a href="https://beian.miit.gov.cn/" target="_blank">{{ beianNo }}</a>
<div class="w-full rounded-2xl z-10 p-5 text-center">
<el-text>{{ appDesc }} © 2024 <a href="https://github.com/naroat/tools-web" target="_blank" class="text-blue-700">Tools-Web</a></el-text>
</div>
</template>
+75 -84
View File
@@ -3,24 +3,16 @@ import { ref, reactive, onMounted } from 'vue'
import { Search } from '@element-plus/icons-vue';
import { useToolsStore } from '@/store/modules/tools'
import { useComponentStore } from '@/store/modules/component'
import { ElMessage } from 'element-plus'
import 'element-plus/theme-chalk/display.css'
import { ToolsInfo } from '@/api/tools/type';
import Login from '@/components/Layout/Login/Login.vue'
import { useUserStore } from '@/store/modules/user'
import defaultAvatar from '@/assets/default_avatar.png'
import { ToolsInfo } from '@/components/Tools/tools.type.ts';
import router from '@/router';
// const isNavDrawer = ref(false)
const loading = ref(false)
const options = ref<ToolsInfo[]>([])
const loginRef = ref()
const userPopRef = ref()
const defAvatar = ref(defaultAvatar)
const feedBackUrl = import.meta.env.VITE_FEEDBACK_URL
//store
const toolsStore = useToolsStore()
const componentStore = useComponentStore()
const userStore = useUserStore()
//查询参数
const searchParam = reactive({
cateId: 0,
@@ -48,72 +40,41 @@ const searchParam = reactive({
//搜索工具
const searchTools = async (query: string) => {
loading.value = true
options.value = []
if (query) {
searchParam.title = query
await toolsStore.getTools(searchParam)
} else {
options.value = []
options.value = await toolsStore.getTools(searchParam)
}
loading.value = false
}
//保存到桌面
const createUrlShortcut = async () => {
try {
const blob = new Blob(
[`[InternetShortcut]\nURL=${encodeURI(window.location.href)}`],
{ type: 'text/plain' }
);
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'Tools-Web.url';
a.click();
} catch (error) {
console.error('创建URL快捷方式失败:', error);
}
}
const optionClick = (url: string) => {
router.push(url)
}
//收藏
// const collect = () => {
// ElMessageBox({
// title: '提示',
// message: '请使用快捷键`Ctrl+D`进行收藏'
// })
// }
const openLogin = () => {
loginRef.value.show()
}
//获取用户信息
const userInfo = async () => {
try {
//提交
await userStore.userInfo()
} catch (error: any) {
ElMessage.error(error.message)
}
}
//退出登录
const logout = async () => {
try {
//提交
await userStore.logout()
location.reload()
} catch (error: any) {
ElMessage.error(error.message)
}
}
//获取收藏
const getToolCollect = async () => {
try {
await toolsStore.getCollect()
// ElMessage.success('收藏成功')
} catch (error: any) {
ElMessage.error(error.message)
}
return
}
onMounted(() => {
getToolCollect(),
userInfo()
})
</script>
<template>
<header class="h-20 w-full flex justify-between pt-2 pb-2 c-xs:h-16 c-xs:border-b-[1px] items-center">
<div class="flex items-center">
<header class="h-24 w-full flex justify-between pt-2 pb-2 c-xs:h-16 c-xs:border-b-[1px] items-center">
<div class="flex items-center w-full">
<Transition name="fold" class="hidden c-sm:block c-md:hidden c-xs:block">
<svg v-if="!componentStore.leftComDrawer" @click="componentStore.setleftComDrawerStatus(true)" t="1702978210636" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7618" width="30" height="30">
<path fill="#444" fill-opacity=".9" d="M895.936 256l-768-0.896 0.128-64L896 192l-0.064 64zM179.2 689.152l202.688-152a32 32 0 0 0 0-51.2L179.2 333.952a32 32 0 0 0-51.2 25.6v304a32 32 0 0 0 51.2 25.6z m12.8-89.6v-176l117.312 88L192 599.552zM896 544H480v-64H896v64z m-0.064 288l-768-0.896 0.128-64L896 768l-0.064 64z" p-id="7619"></path>
@@ -133,7 +94,16 @@ onMounted(() => {
</svg>
</Transition>
<div class="md:w-80 c-xs:72">
<div class="ml-3 mr-1">
<router-link to="/">
<svg t="1715590310537" class="icon" viewBox="0 0 1053 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4287" width="25" height="25">
<path d="M526.63454 58.067422a21.503527 21.503527 0 0 1-27.501109 0.175539l474.979265 381.50475a31.538506 31.538506 0 1 0 39.496274-49.150919L538.629705 9.004273a41.602742 41.602742 0 0 0-51.988799 0.234052L11.573871 398.671586a31.538506 31.538506 0 1 0 39.993635 48.79984L526.63454 58.067422z" fill="#2c2c2c" p-id="4288"></path><path d="M1013.60897 564.087832L538.629705 182.583083a41.602742 41.602742 0 0 0-51.988799 0.204795L11.573871 572.191883a31.538506 31.538506 0 1 0 39.993635 48.79984l66.763331-54.709653v361.61033A94.644775 94.644775 0 0 0 213.004869 1022.537175H441.732179v-247.275931a15.79851 15.79851 0 0 1 15.739997-15.79851h110.472541c8.68918 0 15.769253 7.080073 15.769253 15.79851v247.275931H812.441281a94.615518 94.615518 0 0 0 94.644775-94.615518V559.465305l66.997383 53.831959a31.538506 31.538506 0 1 0 39.525531-49.209432z m-169.629183 363.804568c0 17.436874-14.101633 31.538506-31.567763 31.538506h-165.591785v-184.198919a78.875522 78.875522 0 0 0-78.875522-78.904778h-110.472541a78.875522 78.875522 0 0 0-78.846265 78.904778v184.198919h-165.591786c-17.46613 0-31.597019-14.130889-31.597019-31.538506V514.527323L512.883986 242.851471l331.095801 265.941578v419.128608z" fill="#2c2c2c" p-id="4289" stroke="black" stroke-width="10"></path>
</svg>
</router-link>
</div>
<div class="c-xs:w-[85%] w-full mr-3">
<!-- <el-input
v-model="searchParam.title"
placeholder="搜索工具"
@@ -154,12 +124,13 @@ onMounted(() => {
placeholder="输入关键词搜索,如文本、json、图片等"
:remote-method="searchTools"
:loading="loading"
class="ml-3 mr-3 md:w-80 c-xs:w-60"
class="ml-3"
size="large"
>
<el-option
v-for="item in toolsStore.list"
v-for="item in options"
:key="item.id"
:label="item.title"
:label="item.title + ' - ' + item.desc"
:value="item.id"
@click="optionClick(item.url)"
>
@@ -170,30 +141,38 @@ onMounted(() => {
<div class=" w-full md:w-auto flex md:block c-xs:w-auto">
<ul class="flex mt-4 flex-col md:flex-row md:mt-0 justify-end items-center c-xs:mt-0">
<li class="hover:text-blue-500">
<el-link :href="feedBackUrl" size="large" target="_blank">建议/问题</el-link>
</li>
<li class="ml-3" v-if="userStore.isLogin()">
<!-- userStore.avatar -->
<el-image class="w-10 h-10 cursor-pointer" fit="cover" :src="(userStore.avatar == '' || userStore.avatar == null || userStore.avatar == undefined) ? defAvatar : userStore.avatar" v-popover="userPopRef"></el-image>
<el-popover
ref="userPopRef"
trigger="hover"
virtual-triggering
popper-style="padding-left: 0; padding-right: 0;"
<!-- about -->
<li class="ml-3 c-xs:hidden">
<router-link to="/about">
<el-tooltip
class="box-item"
effect="dark"
content="关于tools-web"
placement="bottom"
>
<div class="flex flex-col">
<el-button link class="mb-3">{{ userStore.email }}</el-button>
<el-button link type="danger" class="ml-0" style="margin-left: 0;" @click="logout">退出登录</el-button>
</div>
</el-popover>
<svg t="1715594765484" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5578" width="33" height="33"><path d="M511.899716 948.506609c-241.310951 0-437.636339-196.318224-437.636339-437.636339 0-241.323231 196.325387-437.639408 437.636339-437.639408s437.636339 196.316178 437.636339 437.639408C949.536055 752.188384 753.210667 948.506609 511.899716 948.506609zM511.899716 113.944122c-218.866776 0-396.926148 178.064488-396.926148 396.926148 0 218.856543 178.059372 396.926148 396.926148 396.926148 218.868823 0 396.926148-178.069605 396.926148-396.926148C908.825864 292.00861 730.768539 113.944122 511.899716 113.944122zM561.15656 335.324138c-29.853935 0-54.03773-24.189935-54.03773-54.047963 0-29.855982 24.184819-54.047963 54.03773-54.047963 29.838585 0 54.0408 24.191982 54.0408 54.047963C615.19736 311.134203 590.995145 335.324138 561.15656 335.324138zM424.962691 430.321746c0-4.394077 0-8.806573 0-13.19758 42.878576-17.016559 108.943224-10.793834 153.201218-26.418696 1.75804 0 3.510964 0 5.27719 0-21.329794 108.134813-66.391083 206.496028-76.599585 316.955792 2.425236 1.850138 2.086521 1.473561 5.287423 2.623757 33.247218 11.155061 52.320623-66.110697 73.948199-60.727083 21.644973 5.38873-13.548574 43.733037-18.47784 50.193169-19.400862 25.362644-56.465013 68.439742-100.376105 68.657706-31.008224 0.181125-63.159482-19.378349-58.101279-71.301929 5.056156-51.981908 34.219359-124.319423 50.172703-182.263114C472.184179 468.050022 488.801648 429.049776 424.962691 430.321746z" fill="#515151" p-id="5579"></path></svg>
</el-tooltip>
</router-link>
</li>
<li class="ml-3 hover:text-blue-500" v-else>
<el-button size="large" type="primary" @click="openLogin">登录</el-button>
<!-- github -->
<li class="ml-3">
<el-tooltip
class="box-item"
effect="dark"
content="GitHub仓库"
placement="bottom"
>
<a href="https://github.com/naroat/tools-web" target="_blank">
<svg t="1715594665374" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4334" width="33" height="33"><path d="M512 85.333333C276.266667 85.333333 85.333333 276.266667 85.333333 512a426.410667 426.410667 0 0 0 291.754667 404.821333c21.333333 3.712 29.312-9.088 29.312-20.309333 0-10.112-0.554667-43.690667-0.554667-79.445333-107.178667 19.754667-134.912-26.112-143.445333-50.133334-4.821333-12.288-25.6-50.133333-43.733333-60.288-14.933333-7.978667-36.266667-27.733333-0.554667-28.245333 33.621333-0.554667 57.6 30.933333 65.621333 43.733333 38.4 64.512 99.754667 46.378667 124.245334 35.2 3.754667-27.733333 14.933333-46.378667 27.221333-57.045333-94.933333-10.666667-194.133333-47.488-194.133333-210.688 0-46.421333 16.512-84.778667 43.733333-114.688-4.266667-10.666667-19.2-54.4 4.266667-113.066667 0 0 35.712-11.178667 117.333333 43.776a395.946667 395.946667 0 0 1 106.666667-14.421333c36.266667 0 72.533333 4.778667 106.666666 14.378667 81.578667-55.466667 117.333333-43.690667 117.333334-43.690667 23.466667 58.666667 8.533333 102.4 4.266666 113.066667 27.178667 29.866667 43.733333 67.712 43.733334 114.645333 0 163.754667-99.712 200.021333-194.645334 210.688 15.445333 13.312 28.8 38.912 28.8 78.933333 0 57.045333-0.554667 102.912-0.554666 117.333334 0 11.178667 8.021333 24.490667 29.354666 20.224A427.349333 427.349333 0 0 0 938.666667 512c0-235.733333-190.933333-426.666667-426.666667-426.666667z" fill="#000000" p-id="4335"></path></svg>
</a>
</el-tooltip>
</li>
<li class="hover:text-blue-500 c-xs:hidden">
<el-button type="primary" class="ml-3 bg-gradient-to-r from-cyan-500 to-blue-500" @click="createUrlShortcut">保存到桌面</el-button>
</li>
</ul>
</div>
<Login ref="loginRef"/>
</header>
</template>
@@ -207,4 +186,16 @@ onMounted(() => {
transform: translateX(20px);
opacity: 0;
}
:deep(.el-select__wrapper) {
box-shadow: 0 0 0 0px var(--el-input-border-color, var(--el-border-color)) inset;
cursor: default;
@apply md:w-full;
}
.el-select :deep(.el-select__wrapper){
background-color: rgba(46, 51, 56, 0.05);
background-color: rgb(255, 255, 255);
}
</style>
+7 -17
View File
@@ -3,16 +3,13 @@
import { onMounted, ref, reactive } from 'vue';
import { useToolsStore } from '@/store/modules/tools'
import { useRouter, useRoute } from "vue-router"
const props = defineProps({
bgColor: String,
})
const router = useRouter()
const route = useRoute()
const appName = ref(import.meta.env.VITE_APP_TITLE || '工具坊')
const appNet = ref(import.meta.env.VITE_APP_WEBSITE || '')
const appNet = ref(import.meta.env.VITE_APP_DESC || '')
//菜单选中
const defaultActive = ref('collect')
const defaultActive = ref('')
//默认展开的菜单
const defaultOpeneds = ['cate']
//store
@@ -56,8 +53,9 @@ const gotoAbout = () => {
router.push('about')
}
onMounted(() => {
getToolCates()
onMounted(async () => {
await getToolCates()
})
</script>
@@ -66,7 +64,7 @@ onMounted(() => {
<el-scrollbar>
<!-- logo -->
<div class="flex justify-center">
<router-link class="flex flex-row h-20" to="/">
<router-link class="flex flex-row h-24" to="/">
<img class="h-12 w-auto rounded-lg mr-2 mt-auto mb-auto" src="@/assets/logo.png" :alt="appNet">
<div class="flex flex-col mt-auto mb-auto">
<div class="text-2xl">{{ appName }}</div>
@@ -81,18 +79,10 @@ onMounted(() => {
class="w-[200px]"
:default-active="defaultActive"
:default-openeds="defaultOpeneds"
:background-color="props.bgColor ?? '#f5f5fb'"
background-color="transparent"
@open="handleOpen"
@close="handleClose"
>
<el-menu-item index="collect" @click="gotoAnchor('collect')">
<template #title>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" data-v-ea893728="" width="20" height="20">
<path fill="currentColor" d="m512 747.84 228.16 119.936a6.4 6.4 0 0 0 9.28-6.72l-43.52-254.08 184.512-179.904a6.4 6.4 0 0 0-3.52-10.88l-255.104-37.12L517.76 147.904a6.4 6.4 0 0 0-11.52 0L392.192 379.072l-255.104 37.12a6.4 6.4 0 0 0-3.52 10.88L318.08 606.976l-43.584 254.08a6.4 6.4 0 0 0 9.28 6.72zM313.6 924.48a70.4 70.4 0 0 1-102.144-74.24l37.888-220.928L88.96 472.96A70.4 70.4 0 0 1 128 352.896l221.76-32.256 99.2-200.96a70.4 70.4 0 0 1 126.208 0l99.2 200.96 221.824 32.256a70.4 70.4 0 0 1 39.04 120.064L774.72 629.376l37.888 220.928a70.4 70.4 0 0 1-102.144 74.24L512 820.096l-198.4 104.32z"></path>
</svg>
<span class="ml-2">我的收藏</span>
</template>
</el-menu-item>
<el-sub-menu index="cate">
<template #title>
<svg t="1702896224109" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4239" width="20" height="20">
-406
View File
@@ -1,406 +0,0 @@
<script setup lang="ts">
import { ref, reactive } from 'vue'
import { ElMessage, FormInstance, FormRules } from 'element-plus'
import { useUserStore } from '@/store/modules/user'
import { Md5 } from 'ts-md5'
import 'element-plus/theme-chalk/display.css'
// import router from '@/router';
//visible
const visible = ref(false)
const reSendCount = ref(0)
const reForgetSendCount = ref(0)
//login select: 0 login; 1 reg; 2 forget
const loginSelect = ref(0)
//store
const userStore = useUserStore()
//interface
interface loginFormRule {
email: string,
password: string
}
interface registerFormRule {
email: string,
code: string,
password: string,
password_confirmation: string,
}
interface forgetFormRule {
email: string,
code: string,
password: string,
password_confirmation: string,
}
//ref
const ruleLoginFormRef = ref<FormInstance>()
const ruleRegisterFormRef = ref<FormInstance>()
const ruleForgetFormRef = ref<FormInstance>()
//login from data
const loginFrom = reactive<loginFormRule>({
email: '',
password: ''
})
//register from data
const registerFrom = reactive({
email: '',
code: '',
password: '',
password_confirmation: '',
})
//forget from data
const forgetFrom = reactive({
email: '',
code: '',
password: '',
password_confirmation: '',
})
//keep login check
const keepLogin = ref(true)
//显示弹窗
const show = () => {
visible.value = true
}
//关闭弹窗
// const close = () => {
// visible.value = false
// }
//发送验证码
const sendCode = async () => {
try {
//发送
await userStore.sendCodeEmail({
email: registerFrom.email
})
//设置倒计时
reSendCount.value = 180
const intervalId = setInterval(() => {
if (reSendCount.value == 0) {
clearInterval(intervalId)
} else {
reSendCount.value--
}
}, 1000)
ElMessage.success('发送成功')
} catch (error: any) {
ElMessage.error(error.message)
}
}
//忘记密码 - 发送验证码
const sendForgetCode = async () => {
try {
//发送
await userStore.sendForgetCodeEmail({
email: forgetFrom.email
})
//设置倒计时
reForgetSendCount.value = 180
const intervalId = setInterval(() => {
if (reForgetSendCount.value == 0) {
clearInterval(intervalId)
} else {
reForgetSendCount.value--
}
}, 1000)
ElMessage.success('发送成功')
} catch (error: any) {
ElMessage.error(error.message)
}
}
//登录 - 提交
const loginSubmit = async (formEl: FormInstance|undefined) => {
formEl?.validate(async (valid) => {
if (valid) {
loginSubmitCore()
} else {
return false
}
})
}
//登录core
const loginSubmitCore = async (isReg: boolean = false) => {
try {
//提交
await userStore.login({
email: loginFrom.email,
password: Md5.hashStr(loginFrom.password),
})
//登录成功(通过注册调用的登录不用提示文字)
if (isReg == false) {
ElMessage.success('登录成功')
}
//关闭弹窗
// close()
location.reload()
} catch (error: any) {
ElMessage.error(error.message)
}
}
//注册 - 提交
const registerSubmit = (formEl: FormInstance|undefined) => {
formEl?.validate(async (valid) => {
if (valid) {
try {
//提交注册
await userStore.register({
email: registerFrom.email,
code: registerFrom.code,
password: Md5.hashStr(registerFrom.password),
password_confirmation: Md5.hashStr(registerFrom.password_confirmation)
})
ElMessage.success('注册成功')
//模拟登录
loginFrom.email = registerFrom.email
loginFrom.password = registerFrom.password
loginSubmitCore(true)
} catch (error: any) {
ElMessage.error(error.message)
}
} else {
return false
}
})
}
//忘记密码 - 提交
const forgetSubmit = (formEl: FormInstance|undefined) => {
formEl?.validate(async (valid) => {
if (valid) {
try {
//提交注册
await userStore.forget({
email: forgetFrom.email,
code: forgetFrom.code,
password: Md5.hashStr(forgetFrom.password),
password_confirmation: Md5.hashStr(forgetFrom.password_confirmation)
})
ElMessage.success('操作成功')
//模拟登录
loginFrom.email = forgetFrom.email
loginFrom.password = forgetFrom.password
loginSubmitCore(true)
} catch (error: any) {
ElMessage.error(error.message)
}
} else {
return false
}
})
}
//登录 - 规则验证
const loginRules = reactive<FormRules<loginFormRule>>({
email: [
{required: true, message: '请输入邮箱', trigger: 'blur'},
{type: 'email', message: '邮箱格式错误', trigger: 'blur' },
],
password: [
{required: true, message: '请输入密码', trigger: 'blur'},
{min: 6, max: 20, message: '密码长度范围6~20', trigger: 'blur'},
]
})
//自定义验证 - 重复密码验证
const confirmPassword = (_rule: any, value: any, callback: any) => {
if (registerFrom.password !== value) {
callback(new Error('重复密码不一致'))
} else {
callback()
}
}
//忘记密码 - 自定义验证 - 重复密码验证
const confirmForgetPassword = (_rule: any, value: any, callback: any) => {
if (forgetFrom.password !== value) {
callback(new Error('重复密码不一致'))
} else {
callback()
}
}
//注册 - 规则验证
const registerRules = reactive<FormRules<registerFormRule>>({
email: [
{required: true, message: '请输入邮箱', trigger: 'blur'},
{type: 'email', message: '邮箱格式错误', trigger: 'blur' },
],
code: [
{required: true, message: '请输入验证码', trigger: 'blur'}
],
password: [
{required: true, message: '请输入密码', trigger: 'blur'},
{min: 6, max: 20, message: '密码长度范围6~20', trigger: 'blur'},
],
password_confirmation: [
{required: true, message: '请重复输入密码', trigger: 'blur'},
{validator: confirmPassword, trigger: 'blur'},
]
})
//忘记密码 - 规则验证
const forgetRules = reactive<FormRules<forgetFormRule>>({
email: [
{required: true, message: '请输入邮箱', trigger: 'blur'},
{type: 'email', message: '邮箱格式错误', trigger: 'blur' },
],
code: [
{required: true, message: '请输入验证码', trigger: 'blur'}
],
password: [
{required: true, message: '请输入密码', trigger: 'blur'},
{min: 6, max: 20, message: '密码长度范围6~20', trigger: 'blur'},
],
password_confirmation: [
{required: true, message: '请重复输入密码', trigger: 'blur'},
{validator: confirmForgetPassword, trigger: 'blur'},
]
})
defineExpose({
show
})
</script>
<template>
<!-- 登录弹窗 -->
<el-dialog v-model="visible" title="" width="450" center class="pl-6 pr-6 pb-6" style="border-radius: 1rem">
<!-- 登录 -->
<el-form
v-if="loginSelect == 0"
ref="ruleLoginFormRef"
:model="loginFrom"
:rules="loginRules"
>
<el-divider><div class="text-xl font-normal">邮箱登录</div></el-divider>
<div>
<el-form-item prop="email">
<el-input class="" v-model="loginFrom.email" size="large" placeholder="请输入邮箱"></el-input>
</el-form-item>
<el-form-item prop="password" style="margin-bottom: 0;">
<el-input class="" v-model="loginFrom.password" size="large" placeholder="请输入密码" type="password" show-password></el-input>
</el-form-item>
<div class="flex items-center mt-1">
<el-checkbox v-model="keepLogin" label="" size="large" />
<div class="">保持长时间登录</div>
</div>
<el-button class="w-full" type="primary" size="large" @click="loginSubmit(ruleLoginFormRef)">登录</el-button>
<el-divider border-style="dashed" />
<div class="flex justify-between">
<el-button link @click="loginSelect = 1">还没有账号去注册</el-button>
<!-- <el-button type="danger">忘记密码</el-button> -->
<el-button link @click="loginSelect = 2">忘记密码</el-button>
</div>
</div>
</el-form>
<!-- 注册 -->
<el-form
v-if="loginSelect == 1"
ref="ruleRegisterFormRef"
:model="registerFrom"
:rules="registerRules"
>
<el-divider><div class="text-xl font-normal">邮箱注册</div></el-divider>
<div>
<el-form-item prop="email">
<el-input class="" v-model="registerFrom.email" size="large" placeholder="请输入邮箱"></el-input>
</el-form-item>
<el-form-item prop="code">
<el-input
v-model="registerFrom.code"
placeholder="请输入验证码"
class=""
size="large"
>
<template #append>
<el-button v-if="reSendCount == 0" @click="sendCode">发送验证码</el-button>
<el-button v-else>{{reSendCount}}秒后重发</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input class="" v-model="registerFrom.password" size="large" placeholder="请输入密码" type="password" show-password></el-input>
</el-form-item>
<el-form-item prop="password_confirmation">
<el-input class="" v-model="registerFrom.password_confirmation" size="large" placeholder="请重复输入密码" type="password" show-password></el-input>
</el-form-item>
<el-button class="w-full" type="primary" size="large" @click="registerSubmit(ruleRegisterFormRef)">注册</el-button>
<el-divider border-style="dashed" />
<div class="flex justify-between">
<el-button link @click="loginSelect = 0">已经有账号去登录</el-button>
</div>
</div>
</el-form>
<!-- 忘记密码 -->
<el-form
v-if="loginSelect == 2"
ref="ruleForgetFormRef"
:model="forgetFrom"
:rules="forgetRules"
>
<el-divider><div class="text-xl font-normal">忘记密码</div></el-divider>
<div>
<el-form-item prop="email">
<el-input class="" v-model="forgetFrom.email" size="large" placeholder="请输入邮箱"></el-input>
</el-form-item>
<el-form-item prop="code">
<el-input
v-model="forgetFrom.code"
placeholder="请输入验证码"
class=""
size="large"
>
<template #append>
<el-button v-if="reForgetSendCount == 0" @click="sendForgetCode">发送验证码</el-button>
<el-button v-else>{{reForgetSendCount}}秒后重发</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input class="" v-model="forgetFrom.password" size="large" placeholder="请输入密码" type="password" show-password></el-input>
</el-form-item>
<el-form-item prop="password_confirmation">
<el-input class="" v-model="forgetFrom.password_confirmation" size="large" placeholder="请重复输入密码" type="password" show-password></el-input>
</el-form-item>
<el-button class="w-full" type="primary" size="large" @click="forgetSubmit(ruleForgetFormRef)">提交</el-button>
<el-divider border-style="dashed" />
<div class="flex justify-between">
<el-button link @click="loginSelect = 0">已经有账号去登录</el-button>
</div>
</div>
</el-form>
</el-dialog>
</template>
<style scoped>
.fold-enter-active {
transition: all 1s ease-out;
}
.fold-enter-from,
.fold-leave-to {
transform: translateX(20px);
opacity: 0;
}
</style>
-25
View File
@@ -1,6 +1,5 @@
<script setup lang="ts">
import { Tools } from '@element-plus/icons-vue'
import { Message } from '@element-plus/icons-vue'
import { reactive, onMounted } from 'vue';
import { useToolsStore } from '@/store/modules/tools'
const info = reactive({
@@ -21,42 +20,18 @@ const info = reactive({
//store
const toolsStore = useToolsStore()
//获取随机推荐
const getToolRecommend = async () => {
try {
await toolsStore.getToolRecommend()
} catch (e) {
console.log(e)
}
}
//点击走马灯
const clickCarousel = (url: string) => {
let w = window.open('')
w?.document.write(`<!DOCTYPE html><html><body ><img src='${url}'/></body></html>`)
}
//反馈
const feedback = () => {
window.open(info.feedbackUrl)
}
onMounted(() => {
getToolRecommend()
})
</script>
<template>
<div>
<!-- feedback -->
<div class="flex flex-col text-center mt-3 border-solid rounded border-gray border p-3 c-xs:mr-3 c-xs:ml-3">
<div @click="feedback()" class="text-2xl flex items-center justify-center cursor-pointer">
<el-icon><Message /></el-icon>
<div class="ml-2">
建议反馈
</div>
</div>
</div>
<!-- adv -->
<div class="mt-3" v-if="info.advShow == 'true'">
<el-carousel
@@ -0,0 +1,26 @@
<script setup lang="ts">
import { onMounted } from 'vue';
const props = defineProps({
title: {
type: String,
default: '描述'
}
})
onMounted(() => {
})
</script>
<template>
<!-- desc -->
<div class="mt-3 rounded-2xl bg-white p-4">
<el-divider content-position="left">{{ props.title }}</el-divider>
<div class="m-4">
<slot></slot>
</div>
</div>
</template>
<style scoped>
</style>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
// import { copy } from '@/utils/string'
const info = reactive({
title: "ASCII码表",
@@ -311,6 +312,14 @@ const tableDataNonStandard = reactive([
</el-table-column>
</el-table>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
ASCIIAmerican Standard Code for Information Interchange美国信息交换标准代码是一种字符编码标准它使用一个字节8位二进制数来表示128个不同的字符这些字符包括33个控制字符具有特殊功能但不可直接显示的字符和95个可打印字符ASCII码表中的字符从0到127编号其中0代表空字符NULL1代表开始头SOH2代表正文开始STX依此类推ASCII码表的最小值是0代表空字符
</el-text>
</ToolDetail>
</div>
</template>
@@ -0,0 +1,183 @@
<script setup lang="ts">
import { reactive,ref,onMounted } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { copy } from '@/utils/string'
import figlet from 'figlet';
const info = reactive({
title: "ASCII字形生成器",
})
figlet.defaults({ fontPath: '//unpkg.com/[email protected]/fonts/' });
const content = ref('Tools-Web')
const contentRes = ref('')
const fontStyle = ref('Big')
const options = ref([
{
value: 'Alpha',
label: 'Alpha',
},
{
value: 'Avatar',
label: 'Avatar',
},
{
value: 'Banner',
label: 'Banner',
},
{
value: 'Banner3-D',
label: 'Banner3-D',
},
{
value: 'Basic',
label: 'Basic',
},
{
value: 'Bear',
label: 'Bear',
},
{
value: 'Big Money-ne',
label: 'Big Money-ne',
},
{
value: 'Big',
label: 'Big',
},
{
value: 'Block',
label: 'Block',
},
{
value: 'Epic',
label: 'Epic',
},
{
value: 'Ghost',
label: 'Ghost',
},
{
value: 'Knob',
label: 'Knob',
},
{
value: 'Linux',
label: 'Linux',
},
{
value: 'Mini',
label: 'Mini',
},
{
value: 'Mirror',
label: 'Mirror',
},
{
value: 'Peaks',
label: 'Peaks',
},
{
value: 'Slant',
label: 'Slant',
},
{
value: 'Small',
label: 'Small',
},
{
value: 'Stellar',
label: 'Stellar',
},
{
value: 'Thin',
label: 'Thin',
},
{
value: 'Wow',
label: 'Wow',
},
])
const gen = () => {
figlet(content.value,
{
font: fontStyle.value,
width: 120,
horizontalLayout: "default",
verticalLayout: "default",
whitespaceBreak: true,
},
function (err, data) {
if (err) {
console.log(';----', err)
return;
}
contentRes.value = data
})
}
//清空输入框
const clear = () => {
content.value = ''
contentRes.value = ''
}
onMounted(() => {
gen()
})
</script>
<template>
<div class="flex flex-col mt-3 flex-1">
<DetailHeader :title="info.title"></DetailHeader>
<div class="p-4 rounded-2xl bg-white ">
<div class="mb-6">
<el-input v-model="content" :rows="4" type="textarea" placeholder="请输入内容"></el-input>
<div class="mt-3 flex items-center">
<div class="flex items-center mr-3 w-36">
<div class="w-10">
<el-text class="">风格</el-text>
</div>
<el-select
v-model="fontStyle"
size="default"
class="ml-2"
@change="gen"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
<el-button type="primary" @click="gen()">生成</el-button>
<el-button type="primary" @click="copy(contentRes)">复制结果</el-button>
<el-button type="primary" @click="clear">清除</el-button>
</div>
</div>
<div>
<pre tabindex="0"><code>{{ contentRes }}</code></pre>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
好用的ASCII字形生成器输入框中输入需要生成的字母提供多种风格选择
</el-text>
</ToolDetail>
</div>
</template>
<style scoped>
</style>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { onBeforeMount, reactive,ref } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
// import { ElMessage } from 'element-plus'
import vueDanmaku from 'vue3-danmaku' //https://www.npmjs.com/package/vue3-danmaku
const info = reactive({
@@ -114,6 +115,14 @@ onBeforeMount(() => {
<el-text>双击可退出弹幕</el-text>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
手持弹幕是一种新型的互动沟通工具可以方便地为各种户外活动演出嘉年华等活动增加趣味性和互动性手持弹幕具有轻便易携带易操作等优点可以让每个参与者都变成活动的一部分同时手持弹幕还可以通过预先编写的文本表情等形式表达参与者的情感和想法实现沟通互动在社交媒体时代手持弹幕的使用也带来了更广泛的社交效应增加了活动的互动性和传播度无论是举办方还是参与者手持弹幕都是一个非常有价值的互动工具
</el-text>
</ToolDetail>
</div>
</template>
@@ -4,6 +4,7 @@ import Spreadsheet from 'x-data-spreadsheet'
import 'x-data-spreadsheet/dist/locale/zh-cn';
import { UploadProps,UploadRawFile,genFileId } from 'element-plus'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
// import { copy } from '@/utils/string'
import { toEchartsData, toSpreadsheetData } from '@/utils/echarts'
import * as echarts from 'echarts'
@@ -29,7 +30,7 @@ const attrColor = ref('#5470c6')
//
const titlePos = ref('center')
//
const title = ref('然部落在线工具')
const title = ref('Tools-Web')
//
const subTitle = ref('在线图表制作工具')
// -
@@ -39,7 +40,7 @@ const subTitleSwitch = ref(true)
/** 水印 */
const watermarkSwitch = ref(false) //
const waterMarkText = ref('然部落工具');
const waterMarkText = ref('Tools-Web');
//
const createWatermark = () => {
@@ -459,6 +460,17 @@ onMounted(() => {
<el-drawer id="x-spreadsheet" v-model="drawer" direction="btt" custom-class="sheet" style="">
</el-drawer>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
在线图表制作工具在线制作柱状图<br>
在线编辑表格生成柱状图支持png和jpeg格式导出<br>
柱状图是采用垂直柱子,即矩形块的宽度一样,用高度来代表数值大小<br>
能让使用者更为直观地看到不同类别数据或不同时间阶段的比较情况及发展趋势通常利用于较小的数据集分析<br>
无需任何基础即可上手并且支持超全的自定义配置轻松实现你的个性化图表需求<br>
</el-text>
</ToolDetail>
</div>
</template>
@@ -4,6 +4,7 @@ import Spreadsheet from 'x-data-spreadsheet'
import 'x-data-spreadsheet/dist/locale/zh-cn';
import { UploadProps,UploadRawFile,genFileId } from 'element-plus'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
// import { copy } from '@/utils/string'
import { toEchartsData, toSpreadsheetData } from '@/utils/echarts'
import * as echarts from 'echarts'
@@ -29,7 +30,7 @@ const attrColor = ref('#5470c6')
//
const titlePos = ref('center')
//
const title = ref('然部落在线工具')
const title = ref('Tools-Web')
//
const subTitle = ref('在线图表制作工具')
// -
@@ -39,7 +40,7 @@ const subTitleSwitch = ref(true)
/** 水印 */
const watermarkSwitch = ref(false) //
const waterMarkText = ref('然部落工具');
const waterMarkText = ref('Tools-Web');
//
const createWatermark = () => {
@@ -459,6 +460,16 @@ onMounted(() => {
<el-drawer id="x-spreadsheet" v-model="drawer" direction="btt" class="sheet" style="">
</el-drawer>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
折线图为日常最常用的图表之一当必须呈现与时间相关的数据时它是首选它最适合分析趋势<br>
在线图表制作工具在线制作折线图<br>
支持导入表格并在线编辑表格生成折线图支持png和jpeg格式导出<br>
支持超全的自定义配置轻松实现你的个性化图表需求<br>
</el-text>
</ToolDetail>
</div>
</template>
@@ -4,6 +4,7 @@ import Spreadsheet from 'x-data-spreadsheet'
import 'x-data-spreadsheet/dist/locale/zh-cn';
import { UploadProps,UploadRawFile,genFileId } from 'element-plus'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
// import { copy } from '@/utils/string'
import { toEchartsPieData, toSpreadsheetData, tranObjAndColumn } from '@/utils/echarts'
import * as echarts from 'echarts'
@@ -29,7 +30,7 @@ const attrColor = ref('#5470c6')
//
const titlePos = ref('center')
//
const title = ref('然部落在线工具')
const title = ref('Tools-Web')
//
const subTitle = ref('在线图表制作工具')
// -
@@ -39,7 +40,7 @@ const subTitleSwitch = ref(true)
/** 水印 */
const watermarkSwitch = ref(false) //
const waterMarkText = ref('然部落工具');
const waterMarkText = ref('Tools-Web');
//
const createWatermark = () => {
@@ -467,6 +468,17 @@ onMounted(() => {
<el-drawer id="x-spreadsheet" v-model="drawer" direction="btt" custom-class="sheet" style="">
</el-drawer>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
饼图是通过一个圆来展示数据用圆内各扇形的角度来表示数据之间的占比关系<br>
能够很好地展示不同分类的变量之间或单个分类变量与整体之间的占比情况常用来强调某个突出的分类变量或表示表示占比关系<br>
在线图表制作工具在线制作饼图<br>
支持导入表格并在线编辑表格生成饼图支持png和jpeg格式导出<br>
支持超全的自定义配置轻松实现你的个性化图表需求<br>
</el-text>
</ToolDetail>
</div>
</template>
@@ -4,6 +4,7 @@ import Spreadsheet from 'x-data-spreadsheet'
import 'x-data-spreadsheet/dist/locale/zh-cn';
import { UploadProps,UploadRawFile,genFileId } from 'element-plus'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
// import { copy } from '@/utils/string'
import { toEchartsData, toSpreadsheetData, tranObjAndColumn } from '@/utils/echarts'
import * as echarts from 'echarts'
@@ -29,7 +30,7 @@ const attrColor = ref('#5470c6')
//
const titlePos = ref('center')
//
const title = ref('然部落在线工具')
const title = ref('Tools-Web')
//
const subTitle = ref('在线图表制作工具')
// -
@@ -39,7 +40,7 @@ const subTitleSwitch = ref(true)
/** 水印 */
const watermarkSwitch = ref(false) //
const waterMarkText = ref('然部落工具');
const waterMarkText = ref('Tools-Web');
//
const createWatermark = () => {
@@ -462,6 +463,16 @@ onMounted(() => {
<el-drawer id="x-spreadsheet" v-model="drawer" direction="btt" custom-class="sheet" style="">
</el-drawer>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
散点图更偏向于研究型图表通过直观醒目的图形方式反映出变量随自变量而变化的大致趋势可以帮助我们推断出不同维度数据之间的相关性也经常用在地图的标注上<br>
在线图表制作工具在线制作散点图<br>
支持导入表格并在线编辑表格生成散点图支持png和jpeg格式导出<br>
支持超全的自定义配置轻松实现你的个性化图表需求<br>
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { ref,reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
// import { copy } from '@/utils/string'
const info = reactive({
title: "抛硬币",
@@ -39,6 +40,14 @@ const throwCoin = () => {
<el-button type="primary" class="w-48" size="large" disabled v-else>抛硬币...</el-button>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
面临艰难的选择 我们邀请您在线掷硬币
</el-text>
</ToolDetail>
</div>
</template>
@@ -0,0 +1,263 @@
<script setup lang="ts">
import { reactive,ref,onMounted } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { copy } from '@/utils/string'
import { CopyDocument } from '@element-plus/icons-vue'
import { colord, extend } from 'colord';
import cmykPlugin from 'colord/plugins/cmyk';
import hwbPlugin from 'colord/plugins/hwb';
import namesPlugin from 'colord/plugins/names';
import lchPlugin from 'colord/plugins/lch';
import labPlugin from 'colord/plugins/lab';
extend([cmykPlugin, hwbPlugin, namesPlugin, lchPlugin, labPlugin]);
const info = reactive({
title: "Color选择器",
})
const color = ref('#409eff')
const hex = ref()
const rgb = ref()
const hsl = ref()
const hsv = ref()
const hwb = ref()
const lab = ref()
const lch = ref()
const cmyk = ref()
const name = ref()
const change = () => {
let colorObj = colord(color.value)
hex.value = colorObj.toHex()
rgb.value = colorObj.toRgbString()
hsl.value = colorObj.toHslString()
hsv.value = colorObj.toHsv()
hwb.value = colorObj.toHwbString()
lab.value = colorObj.toLab()
lch.value = colorObj.toLchString()
cmyk.value = colorObj.toCmykString()
name.value = colorObj.toName({ closest: true }) //{ closest: true } 接近的
console.log(colorObj.toHslString())
}
//copy
const copyRes = async (resStr: string) => {
copy(resStr)
}
onMounted(() => {
change()
})
</script>
<template>
<div class="flex flex-col mt-3 flex-1">
<DetailHeader :title="info.title"></DetailHeader>
<div class="p-4 rounded-2xl bg-white text-center">
<ul class="w-3/4">
<li class='flex items-center mb-3'>
<div class="w-36 text-right pr-3">选择颜色:</div>
<div class="w-full text-left">
<el-color-picker v-model="color" size="large" @change="change"/>
</div>
</li>
<li class='flex items-center mb-3'>
<div class="w-36 text-right pr-3">Hex:</div>
<div class="w-full pl-3 p-1 text-left bg-[#f5f5fb] flex items-center justify-between">
<div>
{{ hex }}
</div>
<el-tooltip
class="box-item"
effect="dark"
content="复制到剪贴板"
placement="top"
>
<el-button :icon="CopyDocument" text bg type="primary" @click="copyRes(hex)"> </el-button>
</el-tooltip>
</div>
</li>
<li class='flex items-center mb-3'>
<div class="w-36 text-right pr-3">RGB:</div>
<div class="w-full pl-3 p-1 text-left bg-[#f5f5fb] flex items-center justify-between">
<div>
{{ rgb }}
</div>
<el-tooltip
class="box-item"
effect="dark"
content="复制到剪贴板"
placement="top"
>
<el-button :icon="CopyDocument" text bg type="primary" @click="copyRes(rgb)"> </el-button>
</el-tooltip>
</div>
</li>
<li class='flex items-center mb-3'>
<div class="w-36 text-right pr-3">HSL:</div>
<div class="w-full pl-3 p-1 text-left bg-[#f5f5fb] flex items-center justify-between">
<div>
{{ hsl }}
</div>
<el-tooltip
class="box-item"
effect="dark"
content="复制到剪贴板"
placement="top"
>
<el-button :icon="CopyDocument" text bg type="primary" @click="copyRes(hsl)"> </el-button>
</el-tooltip>
</div>
</li>
<li class='flex items-center mb-3'>
<div class="w-36 text-right pr-3">HSV:</div>
<div class="w-full pl-3 p-1 text-left bg-[#f5f5fb] flex items-center justify-between">
<div>
{{ hsv }}
</div>
<el-tooltip
class="box-item"
effect="dark"
content="复制到剪贴板"
placement="top"
>
<el-button :icon="CopyDocument" text bg type="primary" @click="copyRes(JSON.stringify(hsv))"> </el-button>
</el-tooltip>
</div>
</li>
<li class='flex items-center mb-3'>
<div class="w-36 text-right pr-3">HWB:</div>
<div class="w-full pl-3 p-1 text-left bg-[#f5f5fb] flex items-center justify-between">
<div>
{{ hwb }}
</div>
<el-tooltip
class="box-item"
effect="dark"
content="复制到剪贴板"
placement="top"
>
<el-button :icon="CopyDocument" text bg type="primary" @click="copyRes(hwb)"> </el-button>
</el-tooltip>
</div>
</li>
<li class='flex items-center mb-3'>
<div class="w-36 text-right pr-3">LAB:</div>
<div class="w-full pl-3 p-1 text-left bg-[#f5f5fb] flex items-center justify-between">
<div>
{{ lab }}
</div>
<el-tooltip
class="box-item"
effect="dark"
content="复制到剪贴板"
placement="top"
>
<el-button :icon="CopyDocument" text bg type="primary" @click="copyRes(JSON.stringify(lab))"> </el-button>
</el-tooltip>
</div>
</li>
<li class='flex items-center mb-3'>
<div class="w-36 text-right pr-3">LCH:</div>
<div class="w-full pl-3 p-1 text-left bg-[#f5f5fb] flex items-center justify-between">
<div>
{{ lch }}
</div>
<el-tooltip
class="box-item"
effect="dark"
content="复制到剪贴板"
placement="top"
>
<el-button :icon="CopyDocument" text bg type="primary" @click="copyRes(lch)"> </el-button>
</el-tooltip>
</div>
</li>
<li class='flex items-center mb-3'>
<div class="w-36 text-right pr-3">CMYK:</div>
<div class="w-full pl-3 p-1 text-left bg-[#f5f5fb] flex items-center justify-between">
<div>
{{ cmyk }}
</div>
<el-tooltip
class="box-item"
effect="dark"
content="复制到剪贴板"
placement="top"
>
<el-button :icon="CopyDocument" text bg type="primary" @click="copyRes(cmyk)"> </el-button>
</el-tooltip>
</div>
</li>
<li class='flex items-center mb-3'>
<div class="w-36 text-right pr-3">Name:</div>
<div class="w-full pl-3 p-1 text-left bg-[#f5f5fb] flex items-center justify-between">
<div>
{{ name }}
</div>
<el-tooltip
class="box-item"
effect="dark"
content="复制到剪贴板"
placement="top"
>
<el-button :icon="CopyDocument" text bg type="primary" @click="copyRes(name)"> </el-button>
</el-tooltip>
</div>
</li>
</ul>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
在不同格式十六进制rgbhsl和css名称之间转换颜色<br>
<br>
CSS 颜色无处不在它可以作为文字背景阴影表格边框链接等属性我们在 CSS 中使用的大多数颜色都是 Hex RGB其实表示颜色的方式有很多<br>
<div class="indent-8">
- RGB: RGB 值也是我们常用的颜色表示方式RGB 指的就是红-绿-这个顺序非常重要每种颜色使用 0 255 之间的数字指定最常见的 RGB 值黑色rgb(0,0,0) 和白色rgb(255,255,255)RGB表示法使我们以更易读的形式来访问与十六进制值相同的颜色范围
</div>
<div class="indent-8">
- Hex: 可以使用十六进制值来表示 CSS 中的颜色这也是我们用的最多的颜色表示方式十六进制使用 16 个符号表示使用 0 - 9 表示值 0 9A - F 表示值 10 15<br/>
</div>
<div class="indent-8">
- HSL: HSL 全称是 Hue-Saturation-Lightness分别表示色调饱和度和亮度它基于 RGB 色轮的每种颜色都有一个角度以及饱和度和亮度值的百分比值<br/>
</div>
<div class="indent-8">
- HSV: HSV色彩空间是对传统RGB模型的一种改进引入了三个主要组件来定义颜色色调H饱和度S和明度V与RGB模型不同后者基于颜色的主导波长饱和度和亮度提供了更直观的颜色描述方式
</div>
<div class="indent-8">
- HWB: HWB 全称为Hue-Whiteness-Blackness表示色调白度和黑度<br/>
</div>
<div class="indent-8">
- LAB: LAB 是一个可以在 Photoshop 等软件中访问的颜色空间它代表了人类可以看到的整个颜色范围它使用三个轴表示亮度a 轴和b <br/>
</div>
<div class="indent-8">
- LCH: LCH 代表亮度色度和色调它与 Lab 具有相同的 L 但不是使用坐标 a* b*而是使用 C色度 H色调色调可以是 0 360 之间的值色度代表颜色的量它类似于 HSL 中的饱和度但是色度值可以超过 100理论上它是无上限的<br/>
</div>
<div class="indent-8">
- CMYK: CMYK 代表 CyanMagentaYellow Key它们与打印机中的墨水颜色相匹配虽然屏幕通常以 RGB 来显示颜色而打印机通常使用青色品红色黄色和黑色的组合来表示颜色这些是最常见的墨水颜色<br/>
</div>
</el-text>
</ToolDetail>
</div>
</template>
<style scoped>
</style>
@@ -0,0 +1,97 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { copy } from '@/utils/string';
import { Codemirror } from "vue-codemirror";
import '@codemirror/search';
import '@codemirror/state';
import '@codemirror/commands';
import * as prettier from "prettier/standalone";
import * as parserCss from 'prettier/parser-postcss';
import { ElMessage } from 'element-plus'
import { minify } from "csso"
const info = reactive({
title: "css代码格式化/压缩",
code: '',
isParseErr: false,
parseErr: '',
})
//格式化
const formatCode = async () => {
try {
info.code = await prettier.format(info.code, { parser: "css", plugins: [parserCss]})
} catch (error) {
console.log(error)
ElMessage({
showClose: true,
message: '请填入正确代码格式',
type: 'error',
})
}
}
//压缩
const compress = async () => {
try {
info.code = minify(info.code, {
restructure: true,
}).css
} catch (error) {
console.log(error)
}
}
//清空输入框
const clear = () => {
info.code = ''
}
const copyRes = async () => {
copy(info.code)
}
</script>
<template>
<div class="flex flex-col mt-3 flex-1">
<DetailHeader :title="info.title"></DetailHeader>
<div class="p-4 rounded-2xl bg-white ">
<div>
<codemirror
v-model="info.code"
placeholder="这里是代码..."
:style="{ height: '400px' }"
:autofocus="true"
:indent-with-tab="true"
:tabSize="2"
/>
</div>
<div class="mt-4">
<el-button type="primary" @click="formatCode">格式化</el-button>
<el-button type="primary" @click="compress">压缩</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>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
在线css格式化在线压缩css代码
</el-text>
</ToolDetail>
</div>
</template>
<style scoped>
</style>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { reactive, onMounted,ref } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
// import { copy } from '@/utils/string'
const info = reactive({
title: "帮我决定",
@@ -86,6 +87,14 @@ onMounted(() => {
<el-button @click="clear">清空</el-button>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
选择困难症患者的福音由我来帮你决定可自定义决定内容随机选择决定
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,7 +1,8 @@
<script setup lang="ts">
import { ref,reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import DiceCore from "@/components/Dice/DiceCore.vue"
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import DiceCore from "@/components/Tools/Dice/DiceCore.vue"
// import { copy } from '@/utils/string'
const info = reactive({
title: "投骰子",
@@ -62,6 +63,14 @@ const resetDice = () => {
<el-button type="primary" class="w-48" size="large" @click="resetDice">重置</el-button>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
在线投骰子骰子工具自定义骰子数量
</el-text>
</ToolDetail>
</div>
</template>
@@ -9,25 +9,25 @@ const isDicing = ref(false)
const aniIndex = ref(0)
//(4)
const diceAnimationImages = ref([
'/public/images/dice/dice_run1.png',
'/public/images/dice/dice_run2.png',
'/public/images/dice/dice_run3.png',
'/public/images/dice/dice_run4.png'
'/images/dice/dice_run1.png',
'/images/dice/dice_run2.png',
'/images/dice/dice_run3.png',
'/images/dice/dice_run4.png'
])
//
const diceImages = ref({
//1
1: '/public/images/dice/dice1.png',
1: '/images/dice/dice1.png',
//2
2: '/public/images/dice/dice2.png',
2: '/images/dice/dice2.png',
//3
3: '/public/images/dice/dice3.png',
3: '/images/dice/dice3.png',
//4
4: '/public/images/dice/dice4.png',
4: '/images/dice/dice4.png',
//5
5: '/public/images/dice/dice5.png',
5: '/images/dice/dice5.png',
//6
6: '/public/images/dice/dice6.png'
6: '/images/dice/dice6.png'
})
//1
const currentPoint = ref(1)
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { CodeDiff } from "v-code-diff";
// import { copy } from '@/utils/string'
const info = reactive({
@@ -31,6 +32,14 @@ const info = reactive({
theme="dark"
/>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
在线文本差异比对支持中文英文代码比对不限制字数轻松比对文本
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
// import { copy } from '@/utils/string'
const info = reactive({
title: "tool name",
@@ -19,6 +20,14 @@ const info = reactive({
<div class="p-4 rounded-2xl bg-white">
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
示例...
</el-text>
</ToolDetail>
</div>
</template>
@@ -0,0 +1,68 @@
<script setup lang="ts">
import { reactive,ref,onMounted } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { escape, unescape } from 'lodash';
import { copy } from '@/utils/string'
const info = reactive({
title: "HTML实体转义",
})
const content = ref('')
const cleanContent = ref('')
const parser = (type: string) => {
if (type == 'toHTML') {
//转html
cleanContent.value = unescape(content.value)
} else {
//转实体
cleanContent.value = escape(content.value)
}
}
//清空输入框
const clear = () => {
content.value = ''
cleanContent.value = ''
}
onMounted(() => {
})
</script>
<template>
<div class="flex flex-col mt-3 flex-1">
<DetailHeader :title="info.title"></DetailHeader>
<div class="p-4 rounded-2xl bg-white ">
<div class="mb-6">
<el-input v-model="content" :rows="10" type="textarea" placeholder="请输入内容" @change="parser"></el-input>
<div class="mt-3">
<el-button type="primary" @click="parser('toEntity')">HTML转实体</el-button>
<el-button type="primary" @click="parser('toHTML')">实体转HTML</el-button>
<el-button type="primary" @click="copy(cleanContent)">复制结果</el-button>
<el-button type="primary" @click="clear">清除</el-button>
</div>
</div>
<div>
<el-input v-html="cleanContent" v-model="cleanContent" :rows="10" type="textarea" placeholder="" @change="parser"></el-input>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
html实体转义实体转义成html
</el-text>
</ToolDetail>
</div>
</div>
</template>
<style scoped>
</style>
@@ -0,0 +1,82 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { copy } from '@/utils/string';
import { Codemirror } from "vue-codemirror";
import '@codemirror/search';
import '@codemirror/state';
import '@codemirror/commands';
import * as prettier from "prettier/standalone";
import * as parserHtml from 'prettier/parser-html';
import { ElMessage } from 'element-plus';
const info = reactive({
title: "html格式化/压缩",
code: '',
isParseErr: false,
parseErr: '',
})
//格式化
const formatCode = async () => {
try {
info.code = await prettier.format(info.code, { parser: "html", plugins: [parserHtml]})
} catch (error) {
ElMessage({
showClose: true,
message: '请填入正确代码格式',
type: 'error',
})
}
}
//清空输入框
const clear = () => {
info.code = ''
}
const copyRes = async () => {
copy(info.code)
}
</script>
<template>
<div class="flex flex-col mt-3 flex-1">
<DetailHeader :title="info.title"></DetailHeader>
<div class="p-4 rounded-2xl bg-white ">
<div>
<codemirror
v-model="info.code"
placeholder="这里是代码..."
:style="{ height: '400px' }"
:autofocus="true"
:indent-with-tab="true"
:tabSize="2"
/>
</div>
<div class="mt-4">
<el-button type="primary" @click="formatCode">格式化</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>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
提供在线htmlxml格式化
</el-text>
</ToolDetail>
</div>
</template>
<style scoped>
</style>
@@ -0,0 +1,145 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
// import { copy } from '@/utils/string'
const info = reactive({
title: "HTTP状态码",
})
// ascii: https://ascii.co.uk/table
const tableDataControlOne = reactive([
{code:"100 Continue",desc:"请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分"},
{code:"101 Switching Protocols",desc:"请求者已要求服务器切换协议,服务器已确认并准备切换"},
{code:"102 Processing",desc:"由WebDAVRFC 2518)扩展的状态码,代表处理将被继续执行。"},
])
const tableDataControlTwo = reactive([
{code:"200 OK",desc:"服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页"},
{code:"201 Created",desc:"请求成功并且服务器创建了新的资源"},
{code:"202 Accepted",desc:"服务器已接受请求,但尚未处理"},
{code:"203 Non-Authoritative Information",desc:"服务器已成功处理了请求,但返回的信息可能来自另一来源"},
{code:"204 No Content",desc:"服务器成功处理了请求,但没有返回任何内容"},
{code:"205 Reset Content",desc:"服务器成功处理了请求,且没有返回任何内容。但是与204响应不同,返回此状态码的响应要求请求者重置文档视图。该响应主要是被用于接受用户输入后,立即重置表单,以便用户能够轻松地开始另一次输入"},
{code:"206 Partial Content",desc:"服务器成功处理了部分 GET 请求"},
{code:"207 Multi-Status",desc:"由WebDAV(RFC 2518)扩展的状态码,代表之后的消息体将是一个XML消息,并且可能依照之前子请求数量的不同,包含一系列独立的响应代码"},
{code:"208 Already Reported",desc:"一个DAV的绑定成员被前一个请求枚举,并且没有被再一次包括"},
{code:"226 IM Used",desc:"服务器已经满足了请求所要的资源,并且响应是一个或者多个实例操作应用于当前实例的结果"},
])
const tableDataControlThree = reactive([
{code:"300 Multiple Choices",desc:"针对请求,服务器可执行多种操作。 服务器可根据请求者 (user agent) 选择一项操作,或提供操作列表供请求者选择。"},
{code:"301 Moved Permanently",desc:"请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。"},
{code:"302 Move Temporarily",desc:"服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求"},
{code:"303 See Other",desc:"请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。"},
{code:"304 Not Modified",desc:"自从上次请求后,请求的网页未修改过。 服务器返回此响应时,不会返回网页内容。"},
{code:"305 Use Proxy",desc:"请求者只能使用代理访问请求的网页。 如果服务器返回此响应,还表示请求者应使用代理"},
{code:"306 Switch Proxy",desc:"在最新版的规范中,306状态码已经不再被使用"},
{code:"307 Temporary Redirect",desc:"服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。"},
{code:"308 Permanent Redirect",desc:"这个请求和以后的请求都应该被另一个URI地址重新发送。307、308和302、301有相同的表现,但是不允许HTTP方法改变。例如,请求表单到一个永久转移的资源将会继续顺利地执行。"},
])
const tableDataControlFour = reactive([
{code:"400 Bad Request",desc:"服务器不理解请求的语法"},
{code:"401 Unauthorized",desc:"请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应"},
{code:"402 Payment Required",desc:"该状态码是为了将来可能的需求而预留的"},
{code:"403 Forbidden",desc:"请求有效,但服务器拒绝操作。用户可能没有资源的必要权限。"},
{code:"404 Not Found",desc:"服务器找不到请求的网页"},
{code:"405 Method Not Allowed",desc:"禁用请求中指定的方法"},
{code:"406 Not Acceptable",desc:"无法使用请求的内容特性响应请求的网页"},
{code:"407 Proxy Authentication Required",desc:"此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理"},
{code:"408 Request Timeout",desc:"服务器等候请求时发生超时"},
{code:"409 Conflict",desc:"服务器在完成请求时发生冲突。 服务器必须在响应中包含有关冲突的信息"},
{code:"410 Gone",desc:"如果请求的资源已永久删除,服务器就会返回此响应"},
{code:"411 Length Required",desc:"服务器不接受不含有效内容长度标头字段的请求"},
{code:"412 Precondition Failed",desc:"服务器未满足请求者在请求中设置的其中一个前提条件"},
{code:"413 Request Entity Too Large",desc:"服务器无法处理请求,因为请求实体过大,超出服务器的处理能力"},
{code:"414 Request-URI Too Long",desc:"请求的 URI(通常为网址)过长,服务器无法处理"},
{code:"415 Unsupported Media Type",desc:"请求的格式不受请求页面的支持"},
{code:"416 Requested Range Not Satisfiable",desc:"如果页面无法提供请求的范围,则服务器会返回此状态代码"},
{code:"417 Expectation Failed",desc:"服务器未满足”期望“请求标头字段的要求"},
{code:"418 I'm a teapot",desc:"这个代码是在1998年作为传统的IETF April Fools jokes被定义的在RFC2324,超文本咖啡罐控制协议,但是并没有被实际的HTTP服务器实现。RFC指定了这个代码应该是由茶罐返回给速溶咖啡"},
{code:"421 Misdirected Request",desc:"请求被指向到无法生成响应的服务器(比如由于连接重复使用)"},
{code:"422 Unprocessable Entity",desc:"请求格式正确,但是由于含有语义错误,无法响应。(RFC 4918 WebDAV"},
{code:"423 Locked",desc:"当前资源被锁定"},
{code:"424 Failed Dependency",desc:"由于之前的某个请求发生的错误,导致当前请求失败,例如 PROPPATCH"},
{code:"425 Too Early",desc:"状态码 425 Too Early 代表服务器不愿意冒风险来处理该请求,原因是处理该请求可能会被“重放”,从而造成潜在的重放攻击。"},
{code:"426 Upgrade Required",desc:"客户端应当切换到TLS/1.0"},
{code:"429 Too Many Requests",desc:"用户在给定的时间内发送了太多请求"},
{code:"431 Request Header Fields Too Large",desc:"服务器不愿意处理请求,因为单个报头字段或所有报头字段都太大了。"},
{code:"449 Retry With",desc:"由微软扩展,代表请求应当在执行完适当的操作后进行重试。"},
{code:"451 Unavailable For Legal Reasons",desc:"该请求因法律原因不可用"},
])
const tableDataControlFive = reactive([
{code:"500 Internal Server Error",desc:"服务器遇到错误,无法完成请求;一般来说,这个问题都会在服务器端的源代码出现错误时出现。"},
{code:"501 Not Implemented",desc:"服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求。"},
{code:"502 Bad Gateway",desc:"作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应"},
{code:"503 Service Unavailable",desc:"服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态"},
{code:"504 Gateway Timeout",desc:"服务器作为网关或代理,但是没有及时从上游服务器收到请求"},
{code:"505 HTTP Version Not Supported",desc:"服务器不支持请求中所用的 HTTP 协议版本"},
{code:"506 Variant Also Negotiates",desc:"由《透明内容协商协议》(RFC 2295)扩展,代表服务器存在内部配置错误:被请求的协商变元资源被配置为在透明内容协商中使用自己,因此在一个协商处理中不是一个合适的重点"},
{code:"507 Insufficient Storage",desc:"服务器无法存储完成请求所必须的内容。这个状况被认为是临时的"},
{code:"509 Bandwidth Limit Exceeded",desc:"服务器达到带宽限制。这不是一个官方的状态码,但是仍被广泛使用。"},
{code:"510 Not Extended",desc:"获取资源所需要的策略并没有被满足"},
{code:"511 Network Authentication Required",desc:"客户端需要进行身份验证才能获得网络访问权限。"},
])
//copy
// const copyRes = async (resStr: string) => {
// copy(resStr)
// }
</script>
<template>
<div class="flex flex-col mt-3 flex-1">
<DetailHeader :title="info.title"></DetailHeader>
<div class="p-4 rounded-2xl bg-white">
<el-table :data="tableDataControlOne" class="w-full mb-3">
<el-table-column label="1xx 信息,表示临时响应并需要请求者继续执行操作" align="center">
<el-table-column prop="code" label="状态码" width="270" />
<el-table-column prop="desc" label="含义解释" />
</el-table-column>
</el-table>
<el-table :data="tableDataControlTwo" class="w-full mb-3">
<el-table-column label="2xx 成功,操作被成功接收并处理" align="center">
<el-table-column prop="code" label="状态码" width="270" />
<el-table-column prop="desc" label="含义解释"/>
</el-table-column>
</el-table>
<el-table :data="tableDataControlThree" class="w-full mb-3">
<el-table-column label="3xx 表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向" align="center">
<el-table-column prop="code" label="状态码" width="270" />
<el-table-column prop="desc" label="含义解释"/>
</el-table-column>
</el-table>
<el-table :data="tableDataControlFour" class="w-full mb-3">
<el-table-column label="4xx 客户端错误,请求包含语法错误或无法完成请求" align="center">
<el-table-column prop="code" label="状态码" width="270" />
<el-table-column prop="desc" label="含义解释"/>
</el-table-column>
</el-table>
<el-table :data="tableDataControlFive" class="w-full mb-3">
<el-table-column label="5xx 这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错" align="center">
<el-table-column prop="code" label="状态码" width="270" />
<el-table-column prop="desc" label="含义解释"/>
</el-table-column>
</el-table>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
超全面http状态对应的名称和含义解释
</el-text>
</ToolDetail>
</div>
</template>
<style scoped>
</style>
@@ -2,6 +2,7 @@
import { onMounted, reactive, ref,computed, watch } from 'vue'
import { UploadProps,UploadRawFile,genFileId } from 'element-plus'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
const info = reactive({
title: "图片切割",
@@ -136,6 +137,14 @@ onMounted(() => {
<el-empty :image-size="200" description="无预览"/>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
将图片分割成四宫格九宫格十六宫格支持自定义行与列<br>
比如九宫格切图广泛应用于微信朋友圈微博等社交媒体
</el-text>
</ToolDetail>
</div>
</template>
+110
View File
@@ -0,0 +1,110 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { copy } from '@/utils/string';
import { Codemirror } from "vue-codemirror";
import '@codemirror/search';
import '@codemirror/state';
import '@codemirror/commands';
import * as prettier from "prettier/standalone";
import * as parserBabel from 'prettier/parser-babel';
import * as prettierPluginEstree from "prettier/plugins/estree";
import { ElMessage } from 'element-plus'
import { minify } from "terser"
const info = reactive({
title: "js代码格式化/压缩",
code: '',
isParseErr: false,
parseErr: '',
})
interface Error {
name: string;
message: string;
stack?: string;
}
//格式化
const formatCode = async () => {
try {
info.code = await prettier.format(info.code, { parser: "babel", plugins: [parserBabel, prettierPluginEstree]})
} catch (error) {
ElMessage({
showClose: true,
message: '请填入正确的js代码',
type: 'error',
})
}
}
//混淆压缩
const confuseCompress = async () => {
try {
let res = await minify(info.code, {
mangle: {
toplevel: true,
}
})
info.code = res.code != undefined ? res.code : info.code
} catch(error) {
ElMessage({
showClose: true,
message: '请填入正确的js代码: ' + (error as Error).message,
type: 'error',
})
}
}
//清空输入框
const clear = () => {
info.code = ''
}
const copyRes = async () => {
copy(info.code)
}
</script>
<template>
<div class="flex flex-col mt-3 flex-1">
<DetailHeader :title="info.title"></DetailHeader>
<div class="p-4 rounded-2xl bg-white ">
<div>
<codemirror
v-model="info.code"
placeholder="这里是代码..."
:style="{ height: '400px' }"
:autofocus="true"
:indent-with-tab="true"
:tabSize="2"
/>
</div>
<div class="mt-4">
<el-button type="primary" @click="formatCode">格式化</el-button>
<el-button type="primary" @click="confuseCompress">混淆压缩</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>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
JS格式化/压缩工具,提供在线JS格式化JS压缩JS混淆
</el-text>
</ToolDetail>
</div>
</template>
<style scoped>
</style>
+103
View File
@@ -0,0 +1,103 @@
<script setup lang="ts">
import { reactive,ref,onMounted } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import {jwtDecode} from 'jwt-decode'
const info = reactive({
title: "JWT解析",
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0b29scy5yYW5ibG9ncy5jb20iLCJzdWIiOiJhcnlhIiwiYXVkIjoidGFvcmFuIiwiaWF0IjoxNzE3MDM2MzMzLCJleHAiOjE3MTcwMzY5MzN9.9v_eDdsPnvcY7qZatNnKvN9VEf7t7o72OgwhLS6gy6w',
})
const decodeHeader = ref()
const decodePayload = ref()
const invalidToken = ref(false)
//解析
const parser = () => {
try {
decodePayload.value = JSON.stringify(jwtDecode(info.token), null, '\t')
decodeHeader.value = JSON.stringify(jwtDecode(info.token, {header: true}), null, '\t')
} catch (e) {
console.log('Invalid token', e)
invalidToken.value = true
}
}
//清空输入框
const clear = () => {
info.token = ''
decodeHeader.value = ''
decodePayload.value = ''
}
onMounted(() => {
parser()
})
</script>
<template>
<div class="flex flex-col mt-3 flex-1">
<DetailHeader :title="info.title"></DetailHeader>
<div class="p-4 rounded-2xl bg-white ">
<div class="mb-6">
<el-input v-model="info.token" :rows="5" type="textarea" placeholder="请输入Token" @change="parser"></el-input>
<el-text type="danger" v-if="invalidToken">Invalid token</el-text>
<div class="mt-3">
<el-button type="primary" @click="parser">解析</el-button>
<el-button type="primary" @click="clear">清除</el-button>
</div>
</div>
<!-- header -->
<div v-if="decodeHeader">
<div class="mb-3">Header(头部)</div>
<el-input
v-model="decodeHeader"
type="textarea"
:autosize="true"
class="w-full"
/>
</div>
<!-- payload -->
<div v-if="decodePayload">
<div class="mb-3 mt-3">Payload(载荷)</div>
<el-input
v-model="decodePayload"
type="textarea"
:autosize="true"
v-if="decodePayload"
class="w-full"
/>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
解析和解码JSON Web Tokenjwt并显示其内容<br>
<br>
1. JWT JSON Web Tokens 的缩写是目前最流行的跨域认证解决方案是一个开放式标准(RFC 7519)用于在各方之间以JSON对象安全传输信息<br/>
2. Header(头部)<br/>
&nbsp;&nbsp;- typ(type): 用来标识整个token字符串是一个JWT字符串<br/>
&nbsp;&nbsp;- alg(algorithm): 用来说明这个JWT签发的时候所使用的签名和摘要算法<br/>
3. Payload(载荷)<br/>
&nbsp;&nbsp;- iss (Issuer)签发者<br/>
&nbsp;&nbsp;- sub (Subject)主题<br/>
&nbsp;&nbsp;- aud (Audience)接收者<br/>
&nbsp;&nbsp;- exp (Expiration time)过期时间<br/>
&nbsp;&nbsp;- nbf (Not Before)生效时间<br/>
&nbsp;&nbsp;- iat (Issued At)签发时间<br/>
&nbsp;&nbsp;- jti (JWT ID)编号<br/>
</el-text>
</ToolDetail>
</div>
</template>
<style scoped>
</style>
@@ -1,11 +1,11 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { transferred, copy } from '@/utils/string';
import { Codemirror } from "vue-codemirror";
import { json } from '@codemirror/lang-json';
import '@codemirror/search';
import { lineNumbers } from '@codemirror/view';
import '@codemirror/state';
import '@codemirror/commands';
// import { syntaxTree } from '@codemirror/language';
@@ -31,7 +31,7 @@ import '@codemirror/commands';
const info = reactive({
title: "Json在线转换",
code: '',
extensions: [json(), lineNumbers()],
extensions: [json()], //[json(), lineNumbers()],
isParseErr: false,
parseErr: ''
})
@@ -123,6 +123,14 @@ const copyRes = async () => {
<el-text type="danger">{{ info.parseErr }}</el-text>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
JSONJavaScript Object Notation是一种轻量级的数据交换格式它基于JavaScript的一个子集但与语言无关因此在多种编程环境中广泛使用JSON格式易于人阅读和编写同时也易于机器解析和生成它通常用于网络应用程序中服务器与客户端之间的数据传输<br>
JSON 工具提供实时编辑和预览JSON 数据语法高亮校验格式化转义去转义压缩等功能可以提高阅读修改的效率和准确性
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { Md5 } from 'ts-md5'
import { copy } from '@/utils/string'
const info = reactive({
@@ -83,6 +84,14 @@ const copyRes = async (resStr: string) => {
</el-input>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
在线md5加密,md5加密算法,md5加密工具,免费MD5在线加密,可生成32位md5,16位md5
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
const info = reactive({
title: "在线markdown编辑器",
content: '',
@@ -21,6 +21,14 @@ const info = reactive({
<div class="">
<v-md-editor v-model="info.content" height="500px"></v-md-editor>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
在线编辑markdown
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { copy } from '@/utils/string'
import { toMorse, toText } from '@/utils/morse'
const info = reactive({
@@ -49,6 +50,14 @@ const copyRes = async (resStr: string) => {
<el-input type="textarea" :rows="8" v-model="info.tranRes"></el-input>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
摩尔斯电码或摩斯电码Morse code是一种编码系统通过不同顺序的信号表示英文字母数字和标点符号由美国人艾尔菲德·维尔在协助Samuel Morse发明摩尔斯电报机1835时创造今天国际摩尔斯电码仍在使用
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { copy, numberToChinese } from '@/utils/string'
const info = reactive({
title: "数字转金额大写",
@@ -42,6 +43,13 @@ const copyRes = async (resStr: string) => {
<el-input type="textarea" :rows="8" v-model="info.tranRes"></el-input>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
可以将阿拉伯数字金额转换为中文大写金额的在线工具;用户输入阿拉伯数字金额如100会立即转换成壹佰的中文
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { copy } from '@/utils/string'
const info = reactive({
title: "色板",
@@ -70,6 +71,14 @@ const copyRes = async (resStr: string) => {
</div>
<div></div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
在线复制颜色好看的颜色组合色板
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { reactive,ref } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import QRCodeVue3 from 'qrcode-vue3'
import { Delete, Plus } from '@element-plus/icons-vue'
import { ElMessage, type UploadFile } from 'element-plus'
@@ -68,7 +69,7 @@ const gen = () => {
<div class="w-16 mr-2"><el-text>尺寸</el-text></div>
<el-input v-model="info.size" class="">
<template #append>
<el-select v-model="info.size" class="w-32">
<el-select v-model="info.size" class="size-select">
<el-option label="常规 200px" value="200" />
<el-option label="常规 300px" value="300" />
<el-option label="适中 350px" value="350" />
@@ -85,8 +86,10 @@ const gen = () => {
</el-input>
</div>
<div class="flex mb-3">
<el-text class="w-16">纠错级别</el-text>
<el-select v-model="info.errorCorrectionLevel" class="w-36">
<div class="w-16">
<el-text>纠错级别</el-text>
</div>
<el-select v-model="info.errorCorrectionLevel" class="corr-select">
<el-option label="L 可遮挡 7%" value="L" />
<el-option label="M 可遮挡 15%" value="M" />
<el-option label="Q 可遮挡 25%" value="Q" />
@@ -171,6 +174,14 @@ const gen = () => {
/>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
根据内容生成二维码自定义尺寸纠错级别颜色等带logo二维码
</el-text>
</ToolDetail>
</div>
</template>
@@ -178,4 +189,10 @@ const gen = () => {
.my-button{
background-color:red;
}
.size-select{
@apply w-36
}
.corr-select{
@apply w-36
}
</style>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
// import { copy } from '@/utils/string'
const info = reactive({
title: "生成随机数",
@@ -48,6 +49,13 @@ const random = () => {
<el-button @click="random" type="primary" size="large" disabled v-else>生成...</el-button>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
可定制范围内进行随机数字可用于抽奖点名等用途
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { onMounted, reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { copy, genRandomStrByChars } from '@/utils/string'
import { ElMessage } from 'element-plus'
const info = reactive({
@@ -148,6 +149,14 @@ onMounted(() => {
<el-input type="textarea" :autosize="info.autosize" v-model="info.resStr"></el-input>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
在线生成密码支持开启或关闭大小写 数字 特殊符号支持自定义字符长度批量生成密码
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,12 +1,13 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { Codemirror } from "vue-codemirror"
import { lineNumbers } from '@codemirror/view';
// import { lineNumbers } from '@codemirror/view';
const info = reactive({
title: "正则表达式测试",
extensions: [lineNumbers()],
// extensions: [lineNumbers()],
reg: '',
content: `一些测试实例:
邮箱1demo@163.com
@@ -128,7 +129,6 @@ const execMatch = () => {
:autofocus="true"
:indent-with-tab="true"
:tabSize="2"
:extensions="info.extensions"
/>
</div>
@@ -158,6 +158,14 @@ const execMatch = () => {
<el-input type="textarea" :rows="4" readonly v-model="info.matchRes"></el-input>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
正则表达式是对字符串包括普通字符例如a z 之间的字母和特殊字符称为元字符操作的一种逻辑公式就是用事先定义好的一些特定字符及这些特定字符的组合组成一个规则字符串这个规则字符串用来表达对字符串的一种过滤逻辑正则表达式是一种文本模式该模式描述在搜索文本时要匹配的一个或多个字符串
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { ElMessage } from 'element-plus'
import { copy } from '@/utils/string'
import { isBinary, isOctal, isDecimal, isHexadecimal } from '@/utils/verify'
@@ -200,7 +201,7 @@ const copyRes = async (index: any) => {
<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-select v-model="info.chooseTranOptions" placeholder="Select" class="choosetranoptions-select">
<el-option
v-for="item in info.tranOptions"
:key="item.value"
@@ -248,8 +249,18 @@ const copyRes = async (index: any) => {
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
进位制其实是一种记数的方式所以也称为进位记数法/位值计数法可以用有限的数字符号代表所有的数值 可使用数字符号的数目称为基数英文radix或底数基数为n即可称n进位制简称n进制 例如平常生活中我们经常用到的十进制就是使用10个阿拉伯数字0-9进行记数所以它的基数就是10称为十进制
</el-text>
</ToolDetail>
</div>
</template>
<style scoped>
.choosetranoptions-select{
@apply w-24
}
</style>
@@ -2,6 +2,7 @@
import { ref, reactive } from "vue"
import SignImageCore from './SignImageCore.vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { UploadProps, UploadInstance, UploadRawFile, genFileId } from 'element-plus'
import { autoDown } from '@/utils/file'
import { Jh_getTimeStamp } from '@/utils/time'
@@ -80,6 +81,14 @@ const saveImg = () => {
@getNewImg="info.getNewImg"
></SignImageCore>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
在线图片裁剪图片标注图片滤镜图片画笔图片旋转图片文字图片尺寸调整等操作
</el-text>
</ToolDetail>
</div>
</template>
+109
View File
@@ -0,0 +1,109 @@
<script setup lang="ts">
import { reactive, ref, shallowRef, onBeforeUnmount, onMounted } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import '@wangeditor/editor/dist/css/style.css' // 引入富文本 css
import { Editor, Toolbar } from '@wangeditor/editor-for-vue' //富文本组件
import { copy } from '@/utils/string'
const info = reactive({
title: "在线文本编辑/HTML获取",
mode: 'default',
})
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()
// 内容
const content = ref(``)
// html
const html = ref('')
// 工具栏配置
const toolbarConfig = {
excludeKeys: [
"uploadImage",
"group-video",
"insertLink",
]
}
//编辑器配置
const editorConfig = { placeholder: '请输入内容...' }
const handleCreated = (editor) => {
editorRef.value = editor // 记录 editor 实例,重要!
// html = editor.getHtml()
}
const handleChange = (editor) => {
html.value = editor.getHtml()
}
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
onMounted(() => {
})
//copy
const copyRes = async (resStr: string) => {
copy(resStr)
}
</script>
<template>
<div class="flex flex-col mt-3 flex-1">
<DetailHeader :title="info.title"></DetailHeader>
<div class="p-4 rounded-2xl bg-white h-[510px]">
<div class="border z-10 mt-3 h-96">
<Toolbar
class=""
:editor="editorRef"
:defaultConfig="toolbarConfig"
:mode="info.mode"
/>
<Editor
class="border"
v-model="content"
:defaultConfig="editorConfig"
:mode="info.mode"
@onCreated="handleCreated"
@onChange="handleChange"
/>
</div>
</div>
<div class="p-4 mt-3 rounded-2xl bg-white">
<el-button type="primary" @click="copyRes(html)">复制HTML</el-button>
<el-input
v-model="html"
disabled
class="mt-3"
type="textarea"
autosize
placeholder="html预览处"
/>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
在线富文本编辑, html实时预览在线编辑文本文本编辑获取html
</el-text>
</ToolDetail>
</div>
</template>
<style scoped>
.downext-select{
@apply w-24
}
</style>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { reactive, computed } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { copy } from '@/utils/string'
const info = reactive({
@@ -48,6 +49,14 @@ const copyRes = async () => {
<el-input type="textarea" :rows="8" v-model="removeDuplicate"></el-input>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
可以删除或去除文本或字符串中的重复行
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { reactive, ref, shallowRef, onBeforeUnmount } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import html2canvas from "html2canvas";
import '@wangeditor/editor/dist/css/style.css' // css
import { Editor, Toolbar } from '@wangeditor/editor-for-vue' //
@@ -104,8 +105,8 @@ onBeforeUnmount(() => {
<el-text>背景颜色</el-text>
<el-color-picker v-model="info.convasBackgroundColor" size="large" />
</div>
<div>
<el-select v-model="info.downExt" class="" placeholder="Select">
<div class="flex">
<el-select v-model="info.downExt" class="downext-select" placeholder="Select">
<el-option
v-for="item in info.downExts"
:key="item.value"
@@ -132,11 +133,19 @@ onBeforeUnmount(() => {
/>
<div ref="poster" class="absolute top-0 -z-10" v-html="valueHtml"></div>
</div>
<!-- v-html="valueHtml" -->
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
把文本转换成图片生成长图富文本自定义文字排版可导出pngjpeg格式可更换背景图设置宽度是好用的文本转图片工具
</el-text>
</ToolDetail>
</div>
</template>
<style scoped>
.downext-select{
@apply w-24
}
</style>
@@ -3,6 +3,7 @@ import { onMounted, onUnmounted, reactive } from 'vue'
import { VideoPause,VideoPlay,CopyDocument } from '@element-plus/icons-vue'
import { Jh_getTimeStamp,Jh_timeStampToTime,Jh_convertTimeStamp } from '@/utils/time'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { copy } from '@/utils/string'
const info = reactive({
title: "时间戳转换",
@@ -95,16 +96,6 @@ const copyRes = async () => {
<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>
@@ -116,7 +107,7 @@ const copyRes = async () => {
<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-select v-model="info.chooseTranStampOption" placeholder="Select" class="trantimestamp-select">
<el-option
v-for="item in info.tranOptions"
:key="item.value"
@@ -129,13 +120,18 @@ const copyRes = async () => {
</div>
</div>
<div class="p-4 rounded-2xl bg-white mt-5">
<div class="text-xl mt-5 mb-5">时间戳</div>
<el-text>Unix 时间戳是从1970年1月1日UTC/GMT的午夜开始所经过的秒数不考虑闰秒</el-text>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
时间戳是从1970年1月1日UTC/GMT的午夜开始所经过的秒数不考虑闰秒用于表示一个时间点然而这种格式对于人类阅读并不友好因此需要转换成可读的日期和时间格式这个工具能够将时间戳快速转换为人类可读的日期时间格式同时也支持反向转换即将日期时间转换为时间戳<br>
</el-text>
</ToolDetail>
</div>
</template>
<style scoped>
.trantimestamp-select{
@apply w-24
}
</style>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { copy } from '@/utils/string'
import { ElMessage } from 'element-plus'
import { v4 as uuidv4 } from 'uuid'
@@ -62,6 +63,14 @@ const copyRes = async (resStr: string) => {
<el-input type="textarea" :autosize="info.autosize" v-model="info.resStr"></el-input>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
在线UUID批量生成
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,14 +1,15 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { Codemirror } from "vue-codemirror"
import { lineNumbers } from '@codemirror/view';
// import { lineNumbers } from '@codemirror/view';
import { copy } from '@/utils/string'
const info = reactive({
title: "Unicode转中文",
extensions: [lineNumbers()],
// extensions: [lineNumbers()],
content: '',
tranRes: '',
})
@@ -62,7 +63,6 @@ const copyRes = async () => {
:autofocus="true"
:indent-with-tab="true"
:tabSize="2"
:extensions="info.extensions"
/>
</div>
@@ -76,6 +76,14 @@ const copyRes = async () => {
<el-input type="textarea" :rows="8" v-model="info.tranRes"></el-input>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
Unicode是计算机科学领域里的一项业界标准有时候我们需要对一段文本或者一段内容进行重新排版编译的时候就需要将获取的值进行转码
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,6 +1,8 @@
<script setup lang="ts">
import { reactive } from 'vue'
import { reactive, onMounted } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { useRoute } from "vue-router"
import Length from './Length.vue'
import Area from './Area.vue'
import Heat from './Heat.vue'
@@ -14,9 +16,42 @@ const info = reactive({
activeName: 'lengthTab',
})
const route = useRoute()
const handleClick = () => {
}
onMounted(() => {
if (route.query.active) {
switch (route.query.active) {
case 'area':
info.activeName = 'areaTab'
break;
case 'weight':
info.activeName = 'weightTab'
break;
case 'time':
info.activeName = 'timeTab'
break;
case 'temperature':
info.activeName = 'temperatureTab'
break;
case 'pressure':
info.activeName = 'pressureTab'
break;
case 'heat':
info.activeName = 'heatTab'
break;
case 'power':
info.activeName = 'powerTab'
break
default:
info.activeName = 'lengthTab'
break;
}
}
})
</script>
<template>
@@ -56,6 +91,14 @@ const handleClick = () => {
</el-tabs>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
在线单位换算长度单位换算面积单位换算时间单位换算温度单位换算压力单位换算热量单位换算功率单位换算
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,6 +1,7 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
import { copy } from '@/utils/string'
const info = reactive({
@@ -54,6 +55,14 @@ const copyRes = async () => {
<el-input type="textarea" :rows="8" v-model="info.tranRes"></el-input>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
在线url编码在线url解码工具
</el-text>
</ToolDetail>
</div>
</template>
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { reactive } from 'vue'
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
const info = reactive({
title: "在线字数统计",
content: '',
@@ -52,6 +52,14 @@ const changeContent = () => {
<div class="ml-3"><el-text>空白字符{{ info.emptyNum }}</el-text></div>
</div>
</div>
<!-- desc -->
<ToolDetail title="描述">
<el-text>
在线统计字数汉字标点数字字母字符
</el-text>
</ToolDetail>
</div>
</template>
+569
View File
@@ -0,0 +1,569 @@
import type { ToolsReqData } from '@/components/Tools/tools.type.ts'
//获取tools分类与对应的工具
export function getToolsCate() {
return [
{
id: 2,
title: '开发运维',
icon: '',
list: [
{
id: 1,
title: '随机密码生成',
logo: '/images/logo/keywords.png',
desc: '密码生成器、随机字符串生成,批量生成',
url: '/randompassword/',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'URL编码/解码',
logo: '/images/logo/url.png',
desc: 'URL在线编码解码工具(UrlEncode编码 和 UrlDecode解码)',
url: '/urlencode/',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'UUID生成器',
logo: '/images/logo/uuid.png',
desc: '批量生成UUID',
url: '/uuid/',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: '时间戳转换',
logo: '/images/logo/Time.png',
desc: '在线时间戳转换工具以及获取当前时间戳',
url: '/timetran/',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'MD5在线加密',
logo: '/images/logo/md5.png',
desc: 'MD5在线加密,长度包含32位、16位',
url: '/md5/',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'Json在线转换',
logo: '/images/logo/json.png',
desc: '提供实时编辑和预览JSON 数据,语法高亮、校验、格式化、转义,去转义、压缩等功能,可以提高阅读修改的效率和准确性',
url: '/json/',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: '正则测试工具',
logo: '/images/logo/reg.png',
desc: '正则表达式测试工具, 常用正则表达式',
url: '/reg/',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'Unicode转中文',
logo: '/images/logo/union.png',
desc: 'Unicode和中文的相互转换',
url: '/unicode/',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'HTTP状态码',
logo: '/images/logo/http_code.png',
desc: 'http状态对应的名称和含义解释',
url: '/httpstatuscode/',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'JWT解析',
logo: '/images/logo/jwt_parse.png',
desc: '解析和解码JSON Web Tokenjwt',
url: '/jwt/',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'html实体转义',
logo: '/images/logo/HtmlEntity.png',
desc: 'html实体转义,实体转义成html',
url: '/htmlentity/',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'js代码格式化/压缩',
logo: '/images/logo/JSForamt.png',
desc: 'JS格式化/压缩工具,提供在线JS格式化、JS压缩、JS混淆、JS解密',
url: '/jsforamt/',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'Html代码格式化',
logo: '/images/logo/HtmlFormat.png',
desc: '提供在线html、xml格式化',
url: '/htmlformat/',
cateId: 2,
cate: '开发运维',
},
{
id: 1,
title: 'Css代码格式化/压缩',
logo: '/images/logo/CssFormat.png',
desc: 'css格式化/压缩工具,提供在线css格式化、css压缩',
url: '/cssformat/',
cateId: 2,
cate: '开发运维',
}
]
},
{
id: 3,
title: '文本处理',
icon: '',
list: [
{
id: 1,
title: '文本对比',
logo: '/images/logo/diff.png',
desc: '文本差异比对支持中文、英文、代码比对',
url: '/diff/',
cateId: 3,
cate: '文本处理'
},
{
id: 1,
title: 'markdown编辑器',
logo: '/images/logo/file-markdown-fill.png',
desc: '在线创建或编辑markdown, 实时预览,导出markdown',
url: '/markdown/',
cateId: 3,
cate: '文本处理'
},
{
id: 1,
title: '字数统计',
logo: '/images/logo/wordCount.png',
desc: '在线统计字符串的字数、段落、标点符号数量',
url: '/wordcount/',
cateId: 3,
cate: '文本处理',
},
{
id: 1,
title: '文本去重',
logo: '/images/logo/textRemoveDuplicate.png',
desc: '可以删除或去除文本或字符串中的重复行',
url: '/textremoveduplicate/',
cateId: 3,
cate: '文本处理',
},
{
id: 1,
title: 'ASCII字形生成器',
logo: '/images/logo/ascii_word_pic.png',
desc: '在线生成字形ASCII画',
url: '/asciiwordpic/',
cateId: 3,
cate: '文本处理',
},
{
id: 1,
title: '在线文本编辑/HTML获取',
logo: '/images/logo/richtextEditor.png',
desc: '在线富文本编辑, html实时预览,在线编辑文本,文本编辑获取html',
url: '/textedit/',
cateId: 3,
cate: '文本处理'
},
]
},
{
id: 4,
title: '教育学术',
icon: '',
list: [
{
id: 1,
title: '单位换算',
logo: '/images/logo/unit.png',
desc: '在线重量、长度、面积、时间、角度、速度、温度、压力、热量、功率等换算',
url: '/unit/',
cateId: 4,
cate: '教育学术'
},
{
id: 1,
title: '摩斯电码',
logo: '/images/logo/medium.png',
desc: '支持中文的摩斯电码编码解码',
url: '/morse/',
cateId: 4,
cate: '教育学术'
},
{
id: 1,
title: '常用进制转换',
logo: '/images/logo/scaletran.png',
desc: '在线进制转换工具,可在2到64进制之间相互转换',
url: '/scaletran/',
cateId: 4,
cate: '教育学术',
},
{
id: 1,
title: 'ASCII码表',
logo: '/images/logo/ascii.png',
desc: 'ASCII码表,控制代码、标准ASCII字符和非标准ASCII字符对照表',
url: '/ascii/',
cateId: 4,
cate: '教育学术'
},
{
id: 1,
title: '长度单位转换',
logo: '/images/logo/length.png',
desc: '长度转换工具-支持国际长度单位,中国传统长度单位,英制长度单位',
url: '/unit/?active=length',
cateId: 4,
cate: '教育学术'
},
{
id: 1,
title: '面积单位转换',
logo: '/images/logo/area.png',
desc: '面积转换工具-支持国际面积单位,中国传统面积单位,英制面积单位',
url: '/unit/?active=area',
cateId: 4,
cate: '教育学术'
},
{
id: 1,
title: '重量单位转换',
logo: '/images/logo/weight.png',
desc: '重量转换工具-支持国际重量单位,中国传统重量单位,英制重量单位(常衡制和金衡制)',
url: '/unit/?active=weight',
cateId: 4,
cate: '教育学术'
},
{
id: 1,
title: '时间单位转换',
logo: '/images/logo/time_unit.png',
desc: '时间单位转换工具-支持国际时间单位',
url: '/unit/?active=time',
cateId: 4,
cate: '教育学术'
},
{
id: 1,
title: '温度单位转换',
logo: '/images/logo/temperature.png',
desc: '温度单位转换工具-支持国际温度单位',
url: '/unit/?active=temperature',
cateId: 4,
cate: '教育学术'
},
{
id: 1,
title: '压力单位转换',
logo: '/images/logo/pressure.png',
desc: '压力单位转换工具-Pa/kPa/hPa/MPa/bar/torr/psi等',
url: '/unit/?active=pressure',
cateId: 4,
cate: '教育学术'
},
{
id: 1,
title: '热量单位转换',
logo: '/images/logo/heat.png',
desc: '热量单位转换工具-Wh/mWh/kWh/MWh/J/kJ等',
url: '/unit/?active=heat',
cateId: 4,
cate: '教育学术'
},
{
id: 1,
title: '功率单位转换',
logo: '/images/logo/power.png',
desc: '功率单位转换工具-W/mW/kW/MW/GW等',
url: '/unit/?active=power',
cateId: 4,
cate: '教育学术'
},
]
},
{
id: 5,
title: '图片处理',
icon: '',
list: [
{
id: 1,
title: '二维码生成',
logo: '/images/logo/qrcode.png',
desc: '在线生成带logo、透明、艺术的二维码',
url: '/qrcode/',
cateId: 5,
cate: '图片处理'
},
{
id: 1,
title: '在线图片处理',
logo: '/images/logo/img.png',
desc: '在线图片裁剪,图片标注,图片滤镜,图片画笔、图片旋转、图片文字等操作',
url: '/signimage/',
cateId: 5,
cate: '图片处理',
},
{
id: 1,
title: '文本转图片',
logo: '/images/logo/text_to_img.png',
desc: '把文本转换成图片,生成长图,具有超多个性文字排版',
url: '/texttoimg/',
cateId: 5,
cate: '图片处理'
},
{
id: 1,
title: '图片分割',
logo: '/images/logo/imgCut.png',
desc: '将图片分割成四宫格、九宫格、十六宫格,支持自定义行与列',
url: '/imgcut/',
cateId: 5,
cate: '图片处理',
},
]
},
{
id: 8,
title: '数据图表',
icon: '',
list: [
{
id: 1,
title: '柱状图',
logo: '/images/logo/bar.png',
desc: '在线制作柱状图,像做表格一样制作可视化图表,支持导出静态或动态图表',
url: '/bar/',
cateId: 8,
cate: '数据图表',
},{
id: 1,
title: '折线图',
logo: '/images/logo/line.png',
desc: '在线制作折线图,像做表格一样制作可视化图表,支持导出静态或动态图表',
url: '/line/',
cateId: 8,
cate: '数据图表',
},
{
id: 1,
title: '饼图',
logo: '/images/logo/pie.png',
desc: '在线制作饼图,像做表格一样制作可视化图表,支持导出静态或动态图表',
url: '/pie/',
cateId: 8,
cate: '数据图表',
},
{
id: 1,
title: '散点图',
logo: '/images/logo/scatter.png',
desc: '在线制作散点图,像做表格一样制作可视化图表,支持导出静态或动态图表',
url: '/scatter/',
cateId: 8,
cate: '数据图表',
}
]
},
{
id: 9,
title: '选择随机',
icon: '',
list: [
{
id: 1,
title: '生成随机数',
logo: '/images/logo/random.png',
desc: '可定制范围内进行随机数字,可用于抽奖、点名等用途',
url: '/random/',
cateId: 9,
cate: '选择随机'
},
{
id: 1,
title: '帮我决定',
logo: '/images/logo/choose.png',
desc: '选择困难,难以决定,今天吃什么,现在做什么,自定义选项都给你安排的明明白白',
url: '/decision/',
cateId: 9,
cate: '选择随机'
},
{
id: 1,
title: '抛硬币',
logo: '/images/logo/coin.png',
desc: '在线抛硬币,选择困难那么交给硬币来帮你选择吧',
url: '/coin/',
cateId: 9,
cate: '选择随机',
},
{
id: 1,
title: '投骰子',
logo: '/images/logo/dice.png',
desc: '在线投骰子,可自定义骰子数量,简单好用的骰子工具',
url: '/dice/',
cateId: 9,
cate: '选择随机',
},
]
},
// {
// id: 6,
// title: '查询相关',
// icon: '',
// list: [
// {
// id: 1,
// title: 'IP查询',
// logo: '/images/logo/IP.png',
// desc: '在线查询ip地址、ip归属地',
// url: '/ip',
// cateId: 6,
// cate: '查询相关',
// },
// {
// id: 1,
// title: '网站favicon获取',
// logo: '/images/logo/text_to_img.png',
// desc: '获取网站logo、icon、favicon、标题、关键词、描述等信息',
// url: '/webInfo',
// cateId: 6,
// cate: '查询相关',
// }
// ]
// },
{
id: 7,
title: '其他工具',
icon: '',
list: [
{
id: 1,
title: '数字转金额大写',
logo: '/images/logo/numberToChinese.png',
desc: '在线数字一键转换成人民币大写,中文大写转换数字',
url: '/numbertochinese/',
cateId: 7,
cate: '其他工具'
},
{
id: 1,
title: '手持弹幕',
logo: '/images/logo/dm.png',
desc: '手持滚动弹幕',
url: '/barrage/',
cateId: 7,
cate: '其他工具',
},
{
id: 1,
title: '色板',
logo: '/images/logo/palettes.png',
desc: '包含纯色、渐变与阶梯色和常用色彩组合',
url: '/palettes/',
cateId: 7,
cate: '其他工具'
},
{
id: 1,
title: 'Color选择器',
logo: '/images/logo/color_picker.png',
desc: '颜色选择器、在各种颜色空间如十六进制、rgb、hsl、css等等之间转换颜色',
url: '/colorpicker/',
cateId: 7,
cate: '其他工具'
}
]
}
]
}
//工具list
export function toolsList() {
let list = [] as any[]
let toolsCate = getToolsCate()
for (let item in toolsCate) {
for (let _item in toolsCate[item].list) {
list.push(toolsCate[item].list[_item])
}
}
return list
}
/**
* url为键名的工具list map
* @returns
*/
export function urlKeyMap() {
// let toolsMapByUrlKey = new Map()
// let list = toolsList()
// for (let item in list) {
// toolsMapByUrlKey.set(list[item].url, list[item])
// }
// return toolsMapByUrlKey
}
//获取工具
export function getTools(data: ToolsReqData) {
//接收参数
const { cateId, title } = data
//获取工具list
let list = toolsList()
//标题筛选
if (title != '') {
list = list.filter(item => {
let tmpValue = item.title.toLowerCase()
let tmpDesc = item.desc.toLowerCase()
// console.log(tmpValue.indexOf(title.toLowerCase()))
return tmpValue.indexOf(title.toLowerCase()) !== -1 || tmpDesc.indexOf(title.toLowerCase()) !== -1;
});
}
//分类筛选
if (cateId > 0) {
list = list.filter(item => {
return item.cateId == cateId;
});
}
return list
}
const ToolsExport = {
getTools,
getToolsCate,
toolsList,
};
export default ToolsExport;
@@ -1,5 +1,3 @@
import { ResponseData } from "../common"
export interface ToolsReqData {
cateId: number,
title: string,
@@ -32,26 +30,16 @@ export interface CollectReqData {
}
//tools list response
export interface ToolsResponseData extends ResponseData {
export interface ToolsResponseData {
data: ToolsInfo[]
}
//tools cate response
export interface ToolCateResponseData extends ResponseData {
export interface ToolCateResponseData{
data: ToolCate[]
}
//tools recommend response
export interface ToolRecommendResponseData extends ResponseData {
export interface ToolRecommendResponseData{
data: ToolsInfo[]
}
//tools collect response
export interface ToolCollectResponseData extends ResponseData {
}
//get collect response
export interface GetToolCollectResponseData extends ResponseData {
}
+1 -1
View File
@@ -33,7 +33,7 @@ router.afterEach((to) => {
if (detailTitle) {
document.title = detailTitle + '-' + import.meta.env.VITE_APP_TITLE
} else {
document.title = import.meta.env.VITE_APP_TITLE
document.title = import.meta.env.VITE_APP_TITLE + '-' + import.meta.env.VITE_APP_DESC
}
//设置meta
+140 -50
View File
@@ -6,14 +6,14 @@ export const constantRoute = [
component: () => import('@/components/Home/Home.vue'),
name: 'home',
meta: {
keywords: '然部落,在线工具,开发人员工具,时间戳转换,加密,解密,md5,进制转换,二维码,正则表达式,json格式化,照片处理,字数统计',
description: '然部落,在线工具,在线工具大全,开发人员工具,日常生活工具,办公助手,时间戳转换,加密,解密,md5,进制转换,二维码,正则表达式,json格式化,照片处理,字数统计',
keywords: 'tools-web,在线工具,开发人员工具,时间戳转换,加密,解密,md5,进制转换,二维码,正则表达式,json格式化,照片处理,字数统计',
description: 'tools-web,在线工具,在线工具大全,开发人员工具,日常生活工具,办公助手,时间戳转换,加密,解密,md5,进制转换,二维码,正则表达式,json格式化,照片处理,字数统计',
}
},
//工具
{
path: '/timetran',
component: () => import('@/components/TimeTran/TimeTran.vue'),
component: () => import('@/components/Tools/TimeTran/TimeTran.vue'),
name: 'timetran',
meta: {
title: "时间戳转换",
@@ -22,8 +22,8 @@ export const constantRoute = [
}
},
{
path: '/MD5',
component: () => import('@/components/MD5/MD5.vue'),
path: '/md5',
component: () => import('@/components/Tools/MD5/MD5.vue'),
name: 'MD5',
meta: {
title: "MD5在线加密",
@@ -33,17 +33,17 @@ export const constantRoute = [
},
{
path: '/json',
component: () => import('@/components/JsonTran/JsonTran.vue'),
component: () => import('@/components/Tools/JsonTran/JsonTran.vue'),
name: 'json',
meta: {
title: "Json在线转换",
keywords: 'json编辑器,json格式化,json压缩,json转义,json去转义',
description: 'json编辑器,json解析,json格式化,json压缩,json转义,json去转义',
description: '提供实时编辑和预览JSON 数据,语法高亮、校验、格式化、转义,去转义、压缩等功能,可以提高阅读修改的效率和准确性',
}
},
{
path: '/reg',
component: () => import('@/components/RegTest/RegTest.vue'),
component: () => import('@/components/Tools/RegTest/RegTest.vue'),
name: 'reg',
meta: {
title: "正则表达式测试",
@@ -53,7 +53,7 @@ export const constantRoute = [
},
{
path: '/unicode',
component: () => import('@/components/Unicode/Unicode.vue'),
component: () => import('@/components/Tools/Unicode/Unicode.vue'),
name: 'unicode',
meta: {
title: "Unicode转中文",
@@ -63,7 +63,7 @@ export const constantRoute = [
},
{
path: '/wordcount',
component: () => import('@/components/WordCount/WordCount.vue'),
component: () => import('@/components/Tools/WordCount/WordCount.vue'),
name: 'wordcount',
meta: {
title: "在线字数统计",
@@ -73,7 +73,7 @@ export const constantRoute = [
},
{
path: '/ip',
component: () => import('@/components/Ip/Ip.vue'),
component: () => import('@/components/Tools/Ip/Ip.vue'),
name: 'ip',
meta: {
title: "IP查询",
@@ -83,7 +83,7 @@ export const constantRoute = [
},
{
path: '/scaletran',
component: () => import('@/components/ScaleTran/ScaleTran.vue'),
component: () => import('@/components/Tools/ScaleTran/ScaleTran.vue'),
name: 'scaletran',
meta: {
title: "常用进制转换",
@@ -93,17 +93,17 @@ export const constantRoute = [
},
{
path: '/signimage',
component: () => import('@/components/SignImage/SignImage.vue'),
component: () => import('@/components/Tools/SignImage/SignImage.vue'),
name: 'signimage',
meta: {
title: "在线编辑图片",
keywords: '图片裁剪,图片滤镜,图片旋转,图片编辑,美化图片',
description: '图片裁剪,图片滤镜,图片旋转图片编辑,美化图片',
keywords: '在线图片裁剪,图片标注,图片滤镜,图片画笔、图片旋转、图片文字,图片美,图片尺寸调整化',
description: '在线图片裁剪,图片标注,图片滤镜,图片画笔、图片旋转图片文字,图片尺寸调整等操作',
}
},
{
path: '/random_password',
component: () => import('@/components/RandomPassword/RandomPassword.vue'),
path: '/randompassword',
component: () => import('@/components/Tools/RandomPassword/RandomPassword.vue'),
name: 'random_password',
meta: {
title: "随机密码生成",
@@ -113,7 +113,7 @@ export const constantRoute = [
},
{
path: '/urlencode',
component: () => import('@/components/UrlEncode/UrlEncode.vue'),
component: () => import('@/components/Tools/UrlEncode/UrlEncode.vue'),
name: 'urlencode',
meta: {
title: "URL编码/解码",
@@ -123,7 +123,7 @@ export const constantRoute = [
},
{
path: '/ascii',
component: () => import('@/components/ASCII/ASCII.vue'),
component: () => import('@/components/Tools/ASCII/ASCII.vue'),
name: 'ascii',
meta: {
title: "ASCII码表",
@@ -133,7 +133,7 @@ export const constantRoute = [
},
{
path: '/uuid',
component: () => import('@/components/UUID/UUID.vue'),
component: () => import('@/components/Tools/UUID/UUID.vue'),
name: 'uuid',
meta: {
title: "UUID生成器",
@@ -143,7 +143,7 @@ export const constantRoute = [
},
{
path: '/barrage',
component: () => import('@/components/Barrage/Barrage.vue'),
component: () => import('@/components/Tools/Barrage/Barrage.vue'),
name: 'barrage',
meta: {
title: "手持弹幕",
@@ -153,7 +153,7 @@ export const constantRoute = [
},
{
path: '/palettes',
component: () => import('@/components/Palettes/Palettes.vue'),
component: () => import('@/components/Tools/Palettes/Palettes.vue'),
name: 'palettes',
meta: {
title: "色板",
@@ -163,7 +163,7 @@ export const constantRoute = [
},
{
path: '/unit',
component: () => import('@/components/Unit/Unit.vue'),
component: () => import('@/components/Tools/Unit/Unit.vue'),
name: 'unit',
meta: {
title: "单位转换",
@@ -173,7 +173,7 @@ export const constantRoute = [
},
{
path: '/qrcode',
component: () => import('@/components/Qrcode/Qrcode.vue'),
component: () => import('@/components/Tools/Qrcode/Qrcode.vue'),
name: 'qrcode',
meta: {
title: "二维码生成",
@@ -183,7 +183,7 @@ export const constantRoute = [
},
{
path: '/decision',
component: () => import('@/components/Decision/Decision.vue'),
component: () => import('@/components/Tools/Decision/Decision.vue'),
name: 'decision',
meta: {
title: "帮我决定",
@@ -193,7 +193,7 @@ export const constantRoute = [
},
{
path: '/morse',
component: () => import('@/components/Morse/Morse.vue'),
component: () => import('@/components/Tools/Morse/Morse.vue'),
name: 'morse',
meta: {
title: "摩斯电码",
@@ -203,7 +203,7 @@ export const constantRoute = [
},
{
path: '/random',
component: () => import('@/components/Random/Random.vue'),
component: () => import('@/components/Tools/Random/Random.vue'),
name: 'random',
meta: {
title: "生成随机数",
@@ -212,8 +212,8 @@ export const constantRoute = [
}
},
{
path: '/numberToChinese',
component: () => import('@/components/NumberToChinese/NumberToChinese.vue'),
path: '/numbertochinese',
component: () => import('@/components/Tools/NumberToChinese/NumberToChinese.vue'),
name: 'numberToChinese',
meta: {
title: "数字转金额大写",
@@ -223,7 +223,7 @@ export const constantRoute = [
},
{
path: '/diff',
component: () => import('@/components/Diff/Diff.vue'),
component: () => import('@/components/Tools/Diff/Diff.vue'),
name: 'diff',
meta: {
title: "文本对比",
@@ -233,7 +233,7 @@ export const constantRoute = [
},
{
path: '/markdown',
component: () => import('@/components/Markdown/Markdown.vue'),
component: () => import('@/components/Tools/Markdown/Markdown.vue'),
name: 'markdown',
meta: {
title: "markdown编辑器",
@@ -242,8 +242,8 @@ export const constantRoute = [
}
},
{
path: '/textToImg',
component: () => import('@/components/TextToImg/TextToImg.vue'),
path: '/texttoimg',
component: () => import('@/components/Tools/TextToImg/TextToImg.vue'),
name: 'textToImg',
meta: {
title: "文本转图片",
@@ -252,8 +252,8 @@ export const constantRoute = [
}
},
{
path: '/webInfo',
component: () => import('@/components/WebInfo/WebInfo.vue'),
path: '/webinfo',
component: () => import('@/components/Tools/WebInfo/WebInfo.vue'),
name: 'webInfo',
meta: {
title: "网站favicon获取",
@@ -263,7 +263,7 @@ export const constantRoute = [
},
{
path: '/bar',
component: () => import('@/components/Chart/Bar/Bar.vue'),
component: () => import('@/components/Tools/Chart/Bar/Bar.vue'),
name: 'bar',
meta: {
title: "柱状图",
@@ -273,7 +273,7 @@ export const constantRoute = [
},
{
path: '/line',
component: () => import('@/components/Chart/Line/Line.vue'),
component: () => import('@/components/Tools/Chart/Line/Line.vue'),
name: 'line',
meta: {
title: "折线图",
@@ -282,8 +282,8 @@ export const constantRoute = [
}
},
{
path: '/Pie',
component: () => import('@/components/Chart/Pie/Pie.vue'),
path: '/pie',
component: () => import('@/components/Tools/Chart/Pie/Pie.vue'),
name: 'pie',
meta: {
title: "饼图",
@@ -292,8 +292,8 @@ export const constantRoute = [
}
},
{
path: '/Scatter',
component: () => import('@/components/Chart/Scatter/Scatter.vue'),
path: '/scatter',
component: () => import('@/components/Tools/Chart/Scatter/Scatter.vue'),
name: 'scatter',
meta: {
title: "散点图",
@@ -302,8 +302,8 @@ export const constantRoute = [
}
},
{
path: '/Coin',
component: () => import('@/components/Coin/Coin.vue'),
path: '/coin',
component: () => import('@/components/Tools/Coin/Coin.vue'),
name: 'coin',
meta: {
title: "抛硬币",
@@ -312,8 +312,8 @@ export const constantRoute = [
}
},
{
path: '/Dice',
component: () => import('@/components/Dice/Dice.vue'),
path: '/dice',
component: () => import('@/components/Tools/Dice/Dice.vue'),
name: 'dice',
meta: {
title: "投骰子",
@@ -322,8 +322,8 @@ export const constantRoute = [
}
},
{
path: '/TextRemoveDuplicate',
component: () => import('@/components/TextRemoveDuplicate/TextRemoveDuplicate.vue'),
path: '/textremoveduplicate',
component: () => import('@/components/Tools/TextRemoveDuplicate/TextRemoveDuplicate.vue'),
name: 'textRemoveDuplicate',
meta: {
title: "文本去重",
@@ -332,15 +332,105 @@ export const constantRoute = [
}
},
{
path: '/ImgCut',
component: () => import('@/components/ImgCut/ImgCut.vue'),
path: '/imgcut',
component: () => import('@/components/Tools/ImgCut/ImgCut.vue'),
name: 'imgCut',
meta: {
title: "图片割",
title: "图片割",
keywords: '图片分割,图片切割,四宫格,九宫格,十六宫格',
description: '将图片分割成四宫格、九宫格、十六宫格,支持自定义行与列',
}
},
{
path: '/httpstatuscode',
component: () => import('@/components/Tools/HttpStatusCode/HttpStatusCode.vue'),
name: 'HttpStatusCode',
meta: {
title: "HTTP状态码",
keywords: 'http状态码',
description: '所有http状态对应的名称和含义解释',
}
},
{
path: '/jwt',
component: () => import('@/components/Tools/JWT/JWT.vue'),
name: 'jwt',
meta: {
title: "JWT解析",
keywords: 'jwt解析,jwt解码,JSON Web Token解析',
description: '解析和解码JSON Web Tokenjwt',
}
},
{
path: '/htmlentity',
component: () => import('@/components/Tools/HtmlEntity/HtmlEntity.vue'),
name: 'HtmlEntity',
meta: {
title: "html实体转义",
keywords: 'html实体转义',
description: 'html实体转义,实体转义成html',
}
},
{
path: '/colorpicker',
component: () => import('@/components/Tools/ColorPicker/ColorPicker.vue'),
name: 'ColorPicker',
meta: {
title: "Color选择器",
keywords: '颜色选择器,Color选择器',
description: '颜色选择器、在各种颜色空间如十六进制、rgb、hsl、css等等之间转换颜色',
}
},
{
path: '/asciiwordpic',
component: () => import('@/components/Tools/ASCIIWordPic/ASCIIWordPic.vue'),
name: 'asciiWordPic',
meta: {
title: "ASCII字形生成器",
keywords: 'ASCII画,字形画,字形生成器',
description: '在线生成字形ASCII画',
}
},
{
path: '/jsforamt',
component: () => import('@/components/Tools/JSFormat/JSFormat.vue'),
name: 'JSForamt',
meta: {
title: "js代码格式化/压缩",
keywords: 'js代码格式化,js格式化,js压缩,javascript格式化',
description: 'JS格式化/压缩工具,提供在线JS格式化、JS压缩、JS混淆、JS解密',
}
},
{
path: '/htmlformat',
component: () => import('@/components/Tools/HtmlFormat/HtmlFormat.vue'),
name: 'HtmlFormat',
meta: {
title: "html代码格式化",
keywords: 'html格式化,xml格式化',
description: '提供在线html、xml格式化',
}
},
{
path: '/cssformat',
component: () => import('@/components/Tools/CssFormat/CssFormat.vue'),
name: 'CssFormat',
meta: {
title: "css代码格式化/压缩",
keywords: 'css格式化,css压缩',
description: '提供在线css格式化,在线css压缩工具',
}
},
{
path: '/textedit',
component: () => import('@/components/Tools/TextEdit/TextEdit.vue'),
name: 'TextEdit',
meta: {
title: "在线文本编辑/HTML获取",
keywords: '文本编辑,富文本预览,在线编辑文本,文本编辑获取html',
description: '在线富文本编辑, html实时预览,在线编辑文本,文本编辑获取html',
}
},
// 关于
{
path: '/about',
-21
View File
@@ -1,21 +0,0 @@
import { defineStore } from 'pinia'
export const useUserStore = defineStore('linux', {
//用来存放变量
state: () => ({
}),
//方法
actions: {
//获取tools
async getCmds() {
//发送请求
// const result: any = await getTools(data)
// if (result.code == 200) {
// return result.message
// } else {
// return Promise.reject(new Error(result.message))
// }
},
}
})
+8 -64
View File
@@ -1,11 +1,10 @@
//创建tools相关的小工具
import { defineStore } from 'pinia'
import { getTools, getToolCate, getToolRecommend, toolCollect, getToolCollect } from '@/api/tools'
import { getTools, getToolsCate } from '@/components/Tools/tools.ts'
import { getIp } from '@/api/ip'
import type { ToolsReqData, ToolsInfo, ToolCate, CollectReqData } from '@/api/tools/type'
import type { ToolsReqData, ToolsInfo } from '@/components/Tools/tools.type.ts'
import type { IpReqData, IpInfo } from '@/api/ip/type'
import type { WebInfo, WebInfoReqData } from '@/api/webinfo/type'
import { useUserStore } from '@/store/modules/user'
import { fetchWebInfo } from '@/api/webinfo'
export const useToolsStore = defineStore('tools', {
@@ -13,7 +12,7 @@ export const useToolsStore = defineStore('tools', {
state: () => ({
list: [] as ToolsInfo[],
toolInfo: {} as ToolsInfo,
cates: [] as ToolCate[],
cates: [] as any[],
recommends: [] as ToolsInfo[],
ipData: {} as IpInfo,
webInfo: {} as WebInfo,
@@ -26,44 +25,20 @@ export const useToolsStore = defineStore('tools', {
async getTools(data: ToolsReqData) {
//发送请求
const result: any = await getTools(data)
if (result.code == 200) {
this.list = result.data
return result.message
} else {
return Promise.reject(new Error(result.message))
}
this.list = result
return result
},
//获取tool info
async getToolInfo(data: ToolsReqData) {
//发送请求
const result: any = await getTools(data)
if (result.code == 200) {
this.toolInfo = result.data[0]
return result.message
} else {
return Promise.reject(new Error(result.message))
}
this.toolInfo = result
return result
},
//获取tools cate
async getToolCate() {
//发送请求
const result: any = await getToolCate()
if (result.code == 200) {
this.cates = result.data
return result.message
} else {
return Promise.reject(new Error(result.message))
}
},
//获取推荐
async getToolRecommend() {
const result: any = await getToolRecommend()
if (result.code == 200) {
this.recommends = result.data
return result.message
} else {
return Promise.reject(new Error(result.message))
}
this.cates = await getToolsCate()
},
//获取ip
async getIp(data: IpReqData) {
@@ -75,37 +50,6 @@ export const useToolsStore = defineStore('tools', {
return Promise.reject(new Error(result.message))
}
},
//收藏工具
async toolCollect(data: CollectReqData) {
if (!useUserStore().isLogin()) {
//未登录
return
}
const result: any = await toolCollect(data)
if (result.code == 200) {
//收藏成功
this.getCollect();
return result.message
} else {
return Promise.reject(new Error(result.message))
}
},
//我的收藏
async getCollect() {
if (!useUserStore().isLogin()) {
//未登录
return
}
const result: any = await getToolCollect()
if (result.code == 200) {
this.collect = result.data
this.collectIds = this.collect.map(item => item.id)
console.log(this.collectIds)
return result.message
} else {
return Promise.reject(new Error(result.message))
}
},
//获取网站信息
async getWebInfo(data: WebInfoReqData) {
const result: any = await fetchWebInfo(data)
-105
View File
@@ -1,105 +0,0 @@
import { defineStore } from 'pinia'
import { login, register, sendCodeEmail, userInfo, logout, sendForgetCodeEmail, forget} from '@/api/user'
import type { LoginReqData, RegisterReqData, sendCodeEmailReq, ForgetReqData } from '@/api/user/type'
export const useUserStore = defineStore('user', {
//用来存放变量
state: () => ({
token: import.meta.env.VITE_IS_MOCK == 'true' ? localStorage.setItem('TOKEN', '') : localStorage.getItem('TOKEN'),//token
username: '',
avatar: '',
email: '',
}),
//方法
actions: {
//获取登录状态
isLogin() {
let flag = false;
if (this.token != null && this.token != undefined && this.token != '') {
flag = true;
}
return flag;
},
//login
async login(data: LoginReqData) {
//发送请求
const result: any = await login(data)
if (result.code == 200) {
//仓库存储token
this.token = result.data.token
//本地存储token
localStorage.setItem('TOKEN', result.data.token)
return result.message
} else {
return Promise.reject(new Error(result.message))
}
},
//logout
async logout() {
//发送请求
const result: any = await logout()
if (result.code == 200) {
//本地存储token
localStorage.setItem('TOKEN', '')
return result.message
} else {
return Promise.reject(new Error(result.message))
}
},
//register
async register(data: RegisterReqData) {
//发送请求
const result: any = await register(data)
if (result.code == 200) {
return result.message
} else {
return Promise.reject(new Error(result.message))
}
},
//forget
async forget(data: ForgetReqData) {
//发送请求
const result: any = await forget(data)
if (result.code == 200) {
return result.message
} else {
return Promise.reject(new Error(result.message))
}
},
//register - send code email
async sendCodeEmail(data: sendCodeEmailReq) {
//发送请求
const result: any = await sendCodeEmail(data)
if (result.code == 200) {
return result.message
} else {
return Promise.reject(new Error(result.message))
}
},
//forget - send code email
async sendForgetCodeEmail(data: sendCodeEmailReq) {
//发送请求
const result: any = await sendForgetCodeEmail(data)
if (result.code == 200) {
return result.message
} else {
return Promise.reject(new Error(result.message))
}
},
//获取用户信息
async userInfo() {
if (!this.isLogin()) {
return
}
//发送请求
const result: any = await userInfo()
if (result.code == 200) {
this.username = result.data.nick_name
this.avatar = result.data.avatar
this.email = result.data.email
return result.message
} else {
return Promise.reject(new Error(result.message))
}
}
}
})
+2 -33
View File
@@ -1,44 +1,13 @@
import axios from "axios";
import { ElMessage } from "element-plus";
import { useUserStore } from '@/store/modules/user'
//store
import pinia from '../store' //这里需要引入pinia
const userStore = useUserStore(pinia)
//创建axios实例
let request = axios.create({
baseURL: import.meta.env.VITE_IS_MOCK == 'true' ? import.meta.env.VITE_APP_BASE_API : import.meta.env.VITE_SERVE + import.meta.env.VITE_APP_BASE_API,
timeout: 5000
})
//创建一个存储CancelToken源的映射,用于管理请求和取消操作
const cancelSources = new Map();
//mock环境下无法使用的接口
const mockBanUrl: string[] = [
'/login',
'/user/info',
'/get/tool/collects',
'/send/forget/password/code',
'/send/register/code',
'/forget/password',
'/register',
'/logout'
];
});
//请求拦截器
request.interceptors.request.use(config => {
if (import.meta.env.VITE_IS_MOCK == 'true' && config.url != undefined && mockBanUrl.includes(config.url)) {
//mock环境不能使用登录后相关功能
const source = axios.CancelToken.source()
//将source与请求的url或某种唯一标识符关联起来
cancelSources.set(config.url, source);
config.cancelToken = source.token;
//取消
source.cancel('Request canceled by the user.');
} else {
//获取token
if (userStore.token) {
// config.headers.token = userStore.token
config.headers.Authorization = 'Bearer ' + userStore.token
}
}
return config;
});
//响应拦截器
-6
View File
@@ -1,9 +1,7 @@
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import {viteMockServe} from 'vite-plugin-mock'
import path from 'path'
import { createHtmlPlugin } from 'vite-plugin-html'
import {seoperender} from "./ssr.config";
// https://vitejs.dev/config/
@@ -21,10 +19,6 @@ export default defineConfig(({command, mode}) => {
// Specify symbolId format
symbolId: 'icon-[dir]-[name]',
}),
//mock
viteMockServe({
localEnabled: env.VITE_IS_MOCK == 'true' ? command === 'serve' : false,
}),
seoperender()
],
resolve: {