Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c23f06df50 | ||
|
|
94d3bee845 | ||
|
|
e861236a9a | ||
|
|
226997fb25 | ||
|
|
f492194fab | ||
|
|
5bbe38f6fe | ||
|
|
68b493c725 | ||
|
|
5187935888 | ||
|
|
0701b6a286 | ||
|
|
071d7fb5e1 | ||
|
|
71e3e59856 | ||
|
|
3d9380c575 | ||
|
|
8b450b91cf | ||
|
|
ce3df800d3 | ||
|
|
6d471fef84 | ||
|
|
b45580dfdc | ||
|
|
dd38597204 | ||
|
|
4fa19c1aed | ||
|
|
d984af3ebe | ||
|
|
f289facdba | ||
|
|
3604407dd7 | ||
|
|
a5233c55c6 | ||
|
|
ae62d59fa3 | ||
|
|
ba71938f01 | ||
|
|
84f42c6825 | ||
|
|
c90524bac1 | ||
|
|
c53bda5706 | ||
|
|
4e1354e5d2 | ||
|
|
f822d17e6e | ||
|
|
9e9176fb68 | ||
|
|
6082f1d789 | ||
|
|
95961dc05a | ||
|
|
c4bff93b44 | ||
|
|
6e8787a2d0 | ||
|
|
527b7f2f5d | ||
|
|
95b7fec3b5 | ||
|
|
f05ee53440 | ||
|
|
50a6e0be2a | ||
|
|
dd0022e219 | ||
|
|
8ca5623b6b | ||
|
|
18f9196212 | ||
|
|
81145a70cd | ||
|
|
2d9fb23cea | ||
|
|
2090d61140 | ||
|
|
4952fb94d6 | ||
|
|
5ad691258b | ||
|
|
90136ccbd2 | ||
|
|
5a06a7aef3 | ||
|
|
144c22eb56 | ||
|
|
a10c36cf73 | ||
|
|
6ef16b1da0 | ||
|
|
a4dc95ba38 | ||
|
|
a15c293322 | ||
|
|
2c914205d7 | ||
|
|
34c601c400 | ||
|
|
097d8fc0b8 | ||
|
|
ad8a1179cd | ||
|
|
c860a01416 | ||
|
|
efc1cb52eb | ||
|
|
c0488db4e8 | ||
|
|
a882cfeac9 | ||
|
|
9a55817794 | ||
|
|
68951056b1 | ||
|
|
4d72590f6f | ||
|
|
4bb328e5f6 | ||
|
|
de2ae48d58 |
@@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
dist
|
||||
.env.development
|
||||
.env.production
|
||||
@@ -1,18 +1,18 @@
|
||||
# 变量必须以 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 = ''
|
||||
# 广告位开关
|
||||
# 广告位开关(废弃)
|
||||
VITE_ADV_SHOW = 'false'
|
||||
# 左侧底部广告位
|
||||
VITE_LEFT_ADV = ''
|
||||
@@ -13,6 +13,7 @@ dist-ssr
|
||||
*.local
|
||||
.env.development
|
||||
.env.production
|
||||
.env
|
||||
.vscode
|
||||
|
||||
# Editor directories and files
|
||||
@@ -25,3 +26,5 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
public/sitemap.txt
|
||||
@@ -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;"]
|
||||
@@ -1,50 +1,57 @@
|
||||
# 基于Vite4.x + Vue3.x + ElementPlus + Typescript + Tailwind的工具站
|
||||
<div align="center">
|
||||
<pre>
|
||||
_______ _ __ __ _
|
||||
|__ __| | | \ \ / / | |
|
||||
| | ___ ___ | |___ _____\ \ /\ / /__| |__
|
||||
| |/ _ \ / _ \| / __|______\ \/ \/ / _ \ '_ \
|
||||
| | (_) | (_) | \__ \ \ /\ / __/ |_) |
|
||||
|_|\___/ \___/|_|___/ \/ \/ \___|_.__/
|
||||
|
||||
[](LICENSE)
|
||||
</pre>
|
||||
<p> 只需简单几步,即可快速搭建属于自己的在线工具箱。</p>
|
||||
|
||||
> 演示站点:https://tools.ranblogs.com
|
||||
[](node)
|
||||
[](vue)
|
||||
[](tailwindcss)
|
||||
[](elementplus)
|
||||
[](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,112 @@ 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代码格式化/压缩
|
||||
- Base64加解密
|
||||
- 进制转换计算器
|
||||
- 数据存储单位换算
|
||||
- Hash计算器
|
||||
- XML格式化
|
||||
- SQL格式化
|
||||
|
||||
- 文本处理
|
||||
- 文本对比
|
||||
- markdown编辑器
|
||||
- ASCII字形生成器
|
||||
- 文本去重
|
||||
- 在线文本编辑/HTML获取
|
||||
- 字数统计
|
||||
- 文本替换
|
||||
- 文本处理工作流
|
||||
- 词频统计
|
||||
|
||||
- 教育学术
|
||||
- 单位换算
|
||||
- 摩斯电码
|
||||
- 常用进制转换
|
||||
- ASCII码表
|
||||
- 长度单位转换
|
||||
- 面积单位转换
|
||||
- 重量单位转换
|
||||
- 时间单位转换
|
||||
- 温度单位转换
|
||||
- 压力单位转换
|
||||
- 热量单位转换
|
||||
- 功率单位转换
|
||||
|
||||
- 图片处理
|
||||
- 二维码生成
|
||||
- 在线图片处理
|
||||
- 文本转图片
|
||||
- 图片分割
|
||||
- 传图取色
|
||||
- 图片水印添加
|
||||
|
||||
- 数据图表
|
||||
- 柱状图
|
||||
- 折线图
|
||||
- 饼图
|
||||
- 散点图
|
||||
- 词云生成
|
||||
|
||||
- 选择随机
|
||||
- 生成随机数
|
||||
- 帮我决定
|
||||
- 抛硬币
|
||||
- 投骰子
|
||||
- 抽签工具
|
||||
- 转盘工具
|
||||
|
||||
- 其他工具
|
||||
- 数字转金额大写
|
||||
- 手持弹幕
|
||||
- 色板
|
||||
- Color选择器
|
||||
- 番茄时钟
|
||||
- 反应速度测试
|
||||
- m3u8在线播放
|
||||
- 剪刀石头布
|
||||
- Emoji表情大全
|
||||
- 简易计算器
|
||||
|
||||
## 其他
|
||||
|
||||
Q: 我应该如何添加新功能?
|
||||
|
||||
A:
|
||||
- 在`components/Tools/tools.ts`文件中添加工具信息
|
||||
- 在`router/router.ts`中添加路由
|
||||
- 目录`components/Tools`中添加新的工具组件
|
||||
|
||||
<br/>
|
||||
|
||||
Q: 我可以参与开发 `Tools-Web` 吗?
|
||||
|
||||
A: 当然可以,随时欢迎提交 `PR`
|
||||
|
||||
使用方法见内`README`
|
||||
|
||||
# 其他
|
||||
|
||||
有问题或建议请提`issue`
|
||||
|
||||
@@ -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,21 +14,12 @@
|
||||
<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:url" content="https://tools.ranblogs.com/">
|
||||
<meta property="og:image" content="/src/assets/logo.png">
|
||||
<meta property="og:url" content=""><!-- 可选:这里填写自己的访问地址 -->
|
||||
<title></title>
|
||||
<!-- 百度统计 -->
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
(function() {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?a94ed52bb5ed2bd9fdbad5b7468a9338";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</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]-->
|
||||
|
||||
@@ -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 }
|
||||
},
|
||||
}
|
||||
]
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +1,58 @@
|
||||
{
|
||||
"name": "rbl-tools-web",
|
||||
"name": "tools-web",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"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",
|
||||
"codemirror": "^6.0.2",
|
||||
"colord": "^2.9.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
"csso": "^5.0.5",
|
||||
"default-passive-events": "^2.0.0",
|
||||
"echarts": "^5.5.0",
|
||||
"element-plus": "^2.4.2",
|
||||
"echarts-wordcloud": "^2.1.0",
|
||||
"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",
|
||||
"qrcode-vue3": "^1.7.1",
|
||||
"sass": "^1.69.5",
|
||||
"sass-loader": "^13.3.2",
|
||||
"sql-formatter": "^15.7.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 +60,19 @@
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@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.9.3",
|
||||
"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": "^3.2.5"
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772429681651" class="icon" viewBox="0 0 1303 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5445" xmlns:xlink="http://www.w3.org/1999/xlink" width="254.4921875" height="200"><path d="M1282.786896 270.896107L1000.908905 18.527799a73.076031 73.076031 0 0 0-49.337958-18.525006H105.750792S0 5.960584 0 95.141269v833.066759c1.117086 53.433939 47.9416 96.162472 105.006068 95.79011h1093.254667S1303.266803 1019.995247 1303.266803 928.114938V316.510445a64.232435 64.232435 0 0 0-21.13154-45.614338h0.651633z m-277.688919-162.535989l182.550443 166.445789h-148.013873s-35.188204 0-35.188204-32.302398v-132.18849l0.651634-1.954901z m229.095686 819.84791c0 8.657415-3.723619 16.942468-10.426135 22.90026a35.095113 35.095113 0 0 1-24.855159 8.843596H105.192249a34.257299 34.257299 0 0 1-34.629661-31.743856V95.141269c0-29.044232 34.53657-29.044232 34.536571-29.044231h830.367134v176.406471c0 89.180686 104.261344 96.441743 104.261345 96.441743h194.559115v589.262776z" fill="#1296db" p-id="5446"></path><path d="M344.993341 287.373123c68.235326 0 91.88031 22.527898 91.88031 61.25354a53.71321 53.71321 0 0 1-39.191095 53.71321c27.089331 3.258167 47.476148 26.344608 47.196876 53.71321 0 45.148886-29.509684 65.535702-90.763223 65.535703H255.254112V287.373123h89.739229z m-45.614338 95.603929h41.890719c30.62677 0 53.71321-9.681411 53.71321-30.068227 0-20.479907-17.687192-29.044232-53.71321-29.044232h-41.890719v59.112459z m0 98.862096h45.614338c37.050013 0 53.71321-9.122868 53.71321-31.650765 0-22.620988-23.086441-30.71986-53.71321-30.719861h-45.614338v62.370626z m294.910659-74.65857V401.222787c0-12.846487-9.681411-24.668979-36.584561-24.668979a42.449262 42.449262 0 0 0-41.332175 21.410812l-39.191095-5.306158a83.315985 83.315985 0 0 1 82.198899-48.872505c53.71321 0 77.265103 22.527898 77.265103 58.553916v82.198899c0 9.681411 5.957791 11.822492 18.897369 9.122868v25.786064a90.297771 90.297771 0 0 1-26.90315 3.72362 37.608556 37.608556 0 0 1-29.509684-12.287944 78.47528 78.47528 0 0 1 0-10.798497 96.162472 96.162472 0 0 1-64.511707 23.086441c-27.927146 0-59.577911-13.963573-59.577911-44.590343-2.792715-41.890719 39.098004-63.30153 119.155822-71.400403z m-53.71321 83.222895a92.438853 92.438853 0 0 0 53.71321-17.687193V439.389886c-40.30818 3.723619-79.033823 12.939578-79.033822 34.44348 2.792715 11.729401 9.681411 16.570106 26.344607 16.570107h-1.023995z m171.286494-19.828274a44.0318 44.0318 0 0 0 43.007805 21.969355c19.362821 0 31.650765-9.122868 31.650765-20.386817s-18.152645-18.804278-41.332176-25.786064c-45.614338-12.381035-67.11824-26.81006-67.11824-53.713211s29.509684-49.337958 74.658569-49.337957a75.775656 75.775656 0 0 1 75.775656 42.821623l-39.749638 5.957792a38.167099 38.167099 0 0 0-36.026018-17.687193c-19.828274 0-32.209308 8.564325-32.209308 19.362821 0 10.705406 19.828274 19.269731 36.491471 23.551893 49.989591 11.357039 74.75166 25.320612 74.75166 53.806301 0 28.392598-31.185313 53.71321-78.47528 53.713211a85.922519 85.922519 0 0 1-81.640356-45.614339l40.308181-8.657415z m235.891291-126.789242a81.081813 81.081813 0 0 1 85.922519 87.039605v17.128649H902.139899a42.449262 42.449262 0 0 0 47.196877 42.449262 49.337958 49.337958 0 0 0 44.683433-21.410812l39.098004 5.864701a84.33998 84.33998 0 0 1-83.781437 48.407052 83.781437 83.781437 0 0 1-90.297772-89.739228 83.781437 83.781437 0 0 1 88.715233-89.739229z m44.0318 73.541484a41.890719 41.890719 0 0 0-44.0318-40.773633 42.449262 42.449262 0 0 0-45.148885 40.773633h89.180685zM536.759742 560.407518a82.198899 82.198899 0 0 1 78.47528 47.755419l-43.007805 6.516334a39.191095 39.191095 0 0 0-36.49147-21.503902c-35.467475 0-49.431048 38.632552-53.713211 76.334199a67.11824 67.11824 0 0 1 56.878287-26.996241 71.493493 71.493493 0 0 1 77.358194 74.193117c0 48.407053-30.068227 78.382189-87.505057 78.382189-57.52992 0-86.015609-33.791846-86.015609-100.910087 0-82.850532 30.71986-133.771028 94.021391-133.771028zM531.546675 763.344778a41.332176 41.332176 0 0 0 43.473257-44.0318 41.890719 41.890719 0 0 0-43.473257-44.0318 44.0318 44.0318 0 1 0 0 88.0636zM752.171126 564.59659h62.370626v144.476434h29.509684v34.44348H814.541752v47.196876h-43.007804v-47.289966H650.143954v-26.81006L752.171126 564.59659z m19.362822 144.476434v-104.261344l-69.259322 104.261344h69.259322z" fill="#1296db" p-id="5447"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772432797191" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="31778" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M753.999941 483.999962c148.879988 0 269.999979 121.119991 269.999979 269.999979S902.879929 1023.99992 753.999941 1023.99992a268.703979 268.703979 0 0 1-169.495987-59.999995H89.999993C40.375997 963.999925 0 923.623928 0 873.999932v-783.999939C0 40.375997 40.375997 0 89.999993 0h543.999957C683.623947 0 723.999943 40.375997 723.999943 89.999993v395.679969a271.743979 271.743979 0 0 1 29.999998-1.68zM529.607959 903.999929A268.431979 268.431979 0 0 1 483.999962 753.999941a268.431979 268.431979 0 0 1 45.607997-149.999988H513.99996a29.999998 29.999998 0 0 1 0-59.999995h59.999995c3.08 0 6.056 0.464 8.855999 1.327999A269.967979 269.967979 0 0 1 663.999948 499.439961V89.999993a30.031998 30.031998 0 0 0-29.999998-29.999998h-543.999957a30.031998 30.031998 0 0 0-29.999998 29.999998v783.999939a30.031998 30.031998 0 0 0 29.999998 29.999997h439.607966z m344.391973-179.999986a29.999998 29.999998 0 0 1 0 59.999996h-119.999991a29.999998 29.999998 0 0 1-29.999998-29.999998v-119.999991a29.999998 29.999998 0 0 1 59.999996 0V723.999943h89.999993z m-119.999991 239.999982c115.791991 0 209.999984-94.207993 209.999984-209.999984S869.791932 543.999958 753.999941 543.999958 543.999958 638.20795 543.999958 753.999941s94.207993 209.999984 209.999983 209.999984zM573.999955 119.999991a29.999998 29.999998 0 0 1 29.999998 29.999997v183.999986a29.999998 29.999998 0 0 1-29.999998 29.999998h-423.999967A29.999998 29.999998 0 0 1 119.999991 333.999974v-183.999986A29.999998 29.999998 0 0 1 149.999988 119.999991h423.999967zM543.999958 303.999976V179.999986H179.999986V303.999976H543.999958zM393.999969 423.999967a29.999998 29.999998 0 0 1 0 59.999995h-243.999981a29.999998 29.999998 0 0 1 0-59.999995h243.999981z m-183.999985 119.999991a29.999998 29.999998 0 0 1 0 59.999995h-59.999996a29.999998 29.999998 0 0 1 0-59.999995h59.999996z m0 119.99999a29.999998 29.999998 0 0 1 0 59.999995h-59.999996a29.999998 29.999998 0 0 1 0-59.999995h59.999996z m0 119.999991a29.999998 29.999998 0 0 1 0 59.999995h-59.999996a29.999998 29.999998 0 0 1 0-59.999995h59.999996z m183.999985-239.999981a29.999998 29.999998 0 0 1 0 59.999995h-63.999995a29.999998 29.999998 0 0 1 0-59.999995h63.999995z m0 119.99999a29.999998 29.999998 0 0 1 0 59.999995h-63.999995a29.999998 29.999998 0 0 1 0-59.999995h63.999995z m0 119.999991a29.999998 29.999998 0 0 1 0 59.999995h-63.999995a29.999998 29.999998 0 0 1 0-59.999995h63.999995z m179.999986-359.999972a29.999998 29.999998 0 0 1 0 59.999995h-59.999995a29.999998 29.999998 0 0 1 0-59.999995h59.999995z" fill="#20A0FF" p-id="31779"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772431477669" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="19654" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M1024 512C1024 229.230208 794.769792 0 512 0 229.230208 0 0 229.230208 0 512 0 794.769792 229.230208 1024 512 1024 629.410831 1024 740.826187 984.331046 830.768465 912.686662 841.557579 904.092491 843.33693 888.379234 834.742758 877.590121 826.148587 866.801009 810.43533 865.021658 799.646219 873.615827 718.470035 938.277495 618.001779 974.048781 512 974.048781 256.817504 974.048781 49.951219 767.182496 49.951219 512 49.951219 256.817504 256.817504 49.951219 512 49.951219 767.182496 49.951219 974.048781 256.817504 974.048781 512 974.048781 599.492834 949.714859 683.336764 904.470807 755.960693 897.177109 767.668243 900.755245 783.071797 912.462793 790.365493 924.170342 797.659191 939.573897 794.081058 946.867595 782.373508 997.013826 701.880796 1024 608.898379 1024 512ZM337.170731 499.512194C371.654852 499.512194 399.609756 471.557291 399.609756 437.073171 399.609756 402.58905 371.654852 374.634146 337.170731 374.634146 302.686611 374.634146 274.731708 402.58905 274.731708 437.073171 274.731708 471.557291 302.686611 499.512194 337.170731 499.512194ZM711.804879 499.512194C746.288998 499.512194 774.243902 471.557291 774.243902 437.073171 774.243902 402.58905 746.288998 374.634146 711.804879 374.634146 677.320757 374.634146 649.365854 402.58905 649.365854 437.073171 649.365854 471.557291 677.320757 499.512194 711.804879 499.512194ZM352.788105 768.770967C396.165222 803.472661 453.151987 824.195121 524.487806 824.195121 595.823622 824.195121 652.810387 803.472661 696.187505 768.770967 722.700531 747.560546 738.882517 725.984565 746.631548 710.486505 752.800254 698.149092 747.799529 683.146916 735.462114 676.978208 723.124702 670.809502 708.122526 675.810227 701.953818 688.147642 701.03616 689.982957 698.492224 694.172969 694.165854 699.941463 686.602473 710.025971 676.927317 720.210345 664.983226 729.765617 630.311565 757.502948 584.273939 774.243902 524.487806 774.243902 464.70167 774.243902 418.664045 757.502948 383.992384 729.765617 372.048292 720.210345 362.373137 710.025971 354.809756 699.941463 350.483386 694.172969 347.93945 689.982957 347.021792 688.147642 340.853084 675.810227 325.850908 670.809502 313.513495 676.978208 301.176081 683.146916 296.175356 698.149092 302.344062 710.486505 310.093092 725.984565 326.275078 747.560546 352.788105 768.770967Z" fill="#f4ea29" p-id="19655"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772430030253" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7514" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M217.6 768l-25.472 92.586667a61.696 61.696 0 0 0 119.04 32.725333L345.6 768h170.666667l-25.472 92.586667a61.696 61.696 0 0 0 119.04 32.725333L644.266667 768h187.733333a64 64 0 0 0 0-128h-152.533333l70.4-256H874.666667a64 64 0 0 0 0-128h-89.6l25.472-92.586667a61.696 61.696 0 1 0-119.04-32.725333L657.066667 256h-170.666667l25.472-92.586667a61.696 61.696 0 1 0-119.04-32.725333L358.4 256H192a64 64 0 1 0 0 128h131.2l-70.4 256H149.333333a64 64 0 0 0 0 128h68.266667z m163.2-128l70.4-256h170.666667l-70.4 256h-170.666667z" fill="#000000" p-id="7515"></path></svg>
|
||||
|
After Width: | Height: | Size: 893 B |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772432585106" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="29440" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M222.976 910.208l-109.248-109.184 458.496-459.584 110.336 110.336m324.736-302.08L874.24 16.64a56.64 56.64 0 0 0-80.192 0L616.576 194.176 506.816 85.568 426.624 165.76 507.392 246.4 0 753.792V1024h270.208l507.328-507.392 80.768 80.768 80.192-80.192-109.184-109.184 177.472-177.472a56.832 56.832 0 0 0 0.512-80.768z" fill="#173E67" p-id="29441"></path></svg>
|
||||
|
After Width: | Height: | Size: 690 B |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772435612733" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="40056" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M735.78125 579.9374999999999c-141.46875 0-256.21875-114.65625-256.21875-256.03125S594.3125 67.78125 735.78125 67.78125 992 182.4375 992 323.8125 877.25 579.9374999999999 735.78125 579.9374999999999z m0-486.5625c-127.31250000000001 0-230.625 103.21875-230.625 230.4375 0 127.31250000000001 103.21875 230.4375 230.625 230.4375 127.31250000000001 0 230.625-103.21875 230.625-230.4375 0-127.21874999999999-103.21875-230.4375-230.625-230.4375z m85.78125 358.5v-46.59375h22.6875V242.34375H798.875v209.53125h-45.28125V195.84375h135.9375v256.03125h-67.96875zM594.875 195.84375h136.03125v46.59375h-90.65625v58.21875h90.65625v46.59375h-90.65625v58.21875h90.65625v46.59375H594.875V195.84375zM273.59375 388.3125c0-37.59375 30.375-68.25 67.78125-68.25s67.78125 30.656250000000004 67.78125 68.25c0 37.59375-30.375 68.25-67.78125 68.25s-67.78125-30.5625-67.78125-68.25zM493.71875 532.3125c15.28125 17.71875 32.53125 33.84375 51.375 47.8125L421.8125 756.1874999999999l-80.625-113.34375-81 111h-78.9375l159.84375-231.65625 80.4375 113.15625 72.1875-103.03125z m206.0625 108.84375l59.34375 112.59375h-71.53125l-69.46875-132.9375c26.15625 10.40625 53.53125 17.25 81.65625 20.34375zM117.21875 207.65625v623.625h711.09375V629.6249999999999c30.281249999999996-9.1875 58.875-22.6875 85.21875-40.3125v272.90625c0 29.8125-24.09375 54-53.71875 54H85.71875C56.09375 916.21875 32 891.9375 32 862.21875V176.71875C32 146.90625 56.09375 122.625 85.71875 122.625H487.625c-20.90625 25.6875-37.5 54.1875-49.5 84.9375H117.21875z" p-id="40057" fill="#1296db"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772431789265" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="22192" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M1007.19 263.95c0 5.07-1.61 9.41-4.96 12.63L825.28 453.65c-3.34 3.34-7.55 4.95-12.63 4.95-4.7 0-8.91-1.73-12.38-5.2-3.46-3.47-5.2-7.68-5.2-12.38V334.9H653.41c-17.7 0-33.68 2.85-48.17 8.29-14.36 5.57-27.12 13.87-38.13 24.89-11.02 11.02-20.44 22.41-28.11 33.93-7.81 11.64-16.1 25.88-24.89 42.84-11.76 22.91-26.13 54.36-43.09 94.48-10.65 24.27-19.81 44.83-27.37 61.42-7.55 16.6-17.58 35.91-29.84 58.07-12.26 22.17-24.14 40.62-35.29 55.23-11.27 14.74-24.89 29.97-40.99 45.95-16.1 15.85-32.69 28.48-49.78 37.88-17.09 9.41-36.78 17.09-58.82 23.28-22.04 6.06-45.69 9.04-70.83 9.04H34.28c-5.08 0-9.41-1.61-12.63-4.95-3.34-3.22-4.95-7.43-4.95-12.51V706.63c0-5.08 1.61-9.41 4.95-12.63 3.34-3.34 7.55-4.95 12.63-4.95H158.1c17.71 0 33.68-2.85 48.17-8.3 14.37-5.57 27.12-13.86 38.14-24.89 11.02-11.02 20.43-22.41 28.11-33.93 7.8-11.64 16.1-25.88 24.89-42.84 11.77-22.78 26.13-54.36 43.09-94.48 10.65-24.27 19.81-44.83 27.37-61.42 7.56-16.59 17.58-35.91 29.84-58.07 12.38-22.05 24.15-40.62 35.29-55.23 11.26-14.74 24.89-29.96 40.99-45.94 16.1-15.85 32.69-28.48 49.78-37.89 17.09-9.41 36.77-17.09 58.82-23.28 22.04-6.07 45.69-9.04 70.82-9.04h141.54V87.61c0-5.08 1.61-9.41 4.95-12.63 3.34-3.34 7.55-4.95 12.63-4.95 4.46 0 8.79 1.86 13.25 5.58l176.45 175.96c3.34 2.96 4.96 7.3 4.96 12.38z m-622.48 53.61c-22.04 33.93-47.31 84.2-75.66 150.94-8.17-16.6-14.86-29.84-20.43-40.12-5.58-10.15-13-21.92-22.41-35.17s-18.82-23.77-28.11-31.33c-9.41-7.55-21.05-13.99-34.92-19.32-13.87-5.32-28.85-8.05-45.07-8.05H34.4c-5.08 0-9.41-1.61-12.63-4.95-3.34-3.34-4.96-7.55-4.96-12.63V210.82c0-5.08 1.61-9.41 4.96-12.63 3.34-3.34 7.55-4.95 12.63-4.95h123.83c91.88-0.12 167.53 41.36 226.48 124.32z m622.48 441.69c0 5.07-1.61 9.41-4.96 12.63L825.28 948.95c-3.34 3.34-7.55 4.95-12.63 4.95-4.7 0-8.91-1.73-12.38-5.2-3.46-3.47-5.2-7.68-5.2-12.38V830.2c-11.76 0-27.49 0.12-46.93 0.25-19.56 0.25-34.42 0.37-44.82 0.62-10.41 0.25-23.78 0-40.37-0.62-16.59-0.62-29.6-1.49-39.25-2.85-9.53-1.24-21.42-3.22-35.29-5.82-13.99-2.6-25.63-5.94-34.92-10.28-9.17-4.21-19.93-9.53-32.08-15.85-12.26-6.32-23.03-13.62-32.69-22.04a548.445 548.445 0 0 1-30.46-29.47c-10.65-11.27-21.05-24.02-30.96-38.39 21.79-34.3 46.8-84.58 75.29-150.94 8.17 16.6 14.86 29.97 20.43 40.12 5.57 10.15 13 21.92 22.41 35.17s18.82 23.77 28.11 31.33c9.41 7.56 21.05 14.12 34.92 19.32 13.87 5.33 28.85 8.05 45.07 8.05h141.53V582.67c0-5.07 1.61-9.41 4.95-12.63 3.34-3.34 7.56-4.95 12.63-4.95 4.46 0 8.8 1.86 13.25 5.57l176.33 176.33c3.35 2.85 4.97 7.18 4.97 12.26z m0 0" p-id="22193" fill="#13227a"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772435561727" class="icon" viewBox="0 0 1026 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="36421" xmlns:xlink="http://www.w3.org/1999/xlink" width="200.390625" height="200"><path d="M47.104 258.048h501.76c26.624 0 47.104-20.48 47.104-47.104s-20.48-47.104-47.104-47.104h-501.76C20.48 162.816 0 184.32 0 209.92s20.48 48.128 47.104 48.128z m0 239.616h501.76c26.624 0 47.104-20.48 47.104-47.104s-20.48-47.104-47.104-47.104h-501.76C20.48 403.456 0 423.936 0 450.56s20.48 47.104 47.104 47.104z m240.64 131.072H47.104C20.48 628.736 0 649.216 0 675.84c0 26.624 20.48 47.104 47.104 47.104h242.688c26.624 0 47.104-20.48 47.104-47.104-2.048-26.624-22.528-47.104-49.152-47.104z m588.8-384c-33.792-17.408-80.896-44.032-98.304-80.896 0-18.432-17.408-35.84-35.84-35.84S706.56 145.408 706.56 163.84v468.992c-37.888-18.432-106.496-13.312-160.768 17.408-82.944 45.056-118.784 132.096-80.896 193.536 35.84 60.416 134.144 71.68 216.064 26.624 58.368-31.744 96.256-87.04 98.304-138.24V325.632c118.784 0 189.44 47.104 164.864 164.864-6.144 20.48-11.264 44.032-20.48 62.464-4.096 7.168-4.096 15.36 2.048 20.48 6.144 6.144 17.408 6.144 26.624-4.096 45.056-44.032 71.68-102.4 73.728-162.816-2.048-80.896-81.92-130.048-149.504-161.792z" fill="#CC66FF" p-id="36422"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772432655407" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="30609" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 0c282.7776 0 512 229.2224 512 512s-229.2224 512-512 512S0 794.7776 0 512 229.2224 0 512 0z m0 837.8368a46.592 46.592 0 0 0-46.2336 41.1136l-0.3072 5.4272v46.5408a46.592 46.592 0 0 0 92.7744 5.4272l0.3072-5.4272v-46.592a46.592 46.592 0 0 0-46.5408-46.4896z m418.9184-372.3776h-46.592a46.592 46.592 0 0 0-5.376 92.7744l5.4272 0.3072h46.5408a46.592 46.592 0 0 0 0-93.0816z m-791.296 0h-46.5408a46.592 46.592 0 0 0-5.4272 92.7744l5.4272 0.3072h46.592a46.592 46.592 0 0 0 0-93.0816zM512 139.6224a46.592 46.592 0 0 0-46.592 46.592V512c0 25.7024 20.8896 46.592 46.592 46.592h232.704a46.592 46.592 0 1 0 0-93.184H558.592V186.1632A46.592 46.592 0 0 0 512 139.6224z" fill="#d81e06" p-id="30610"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772432906138" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="33999" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M617.890909 258.327273c56.180364 0 103.726545 47.546182 103.726546 103.749818v216.087273c0 17.291636-12.962909 30.254545-30.254546 30.254545s-30.254545-12.986182-30.254545-25.949091v-185.832727c0-8.634182-8.657455-17.291636-17.291637-17.291636-12.962909 0-17.291636 8.657455-17.291636 17.291636v514.327273c0 25.925818-21.597091 43.217455-43.217455 43.217454-21.597091 0-43.217455-21.620364-43.217454-43.217454V595.456c0-8.634182-8.657455-17.291636-17.291637-17.291636h-21.620363c-8.634182 0-17.268364 8.657455-17.268364 17.291636v315.508364c0 25.925818-21.620364 43.217455-43.217454 43.217454-25.949091 0-43.240727-21.620364-43.240728-43.217454 4.328727 0 4.328727-513.000727 4.328728-514.327273 0-8.634182-4.328727-17.291636-17.268364-17.291636-12.986182 0-17.314909 8.657455-17.314909 17.291636v181.527273c0 17.291636-12.962909 25.925818-30.254546 25.925818h-4.305454a29.556364 29.556364 0 0 1-30.254546-30.254546v-216.110545c0-56.180364 47.522909-99.397818 99.374546-99.397818h216.133818zM67.653818 493.847273h185.716364v36.305454h-185.716364V581.818182L0 512l67.653818-69.818182v51.665455zM1024 512l-67.653818 69.818182v-51.665455h-185.693091v-36.305454h185.693091V442.181818L1024 512zM522.798545 46.545455c51.851636 0 95.092364 43.217455 95.092364 95.092363 0 51.851636-43.240727 99.397818-95.092364 99.397818-51.874909 0-99.421091-43.217455-99.42109-95.092363 0-51.851636 43.240727-99.374545 99.42109-99.397818z" fill="#1296db" p-id="34000"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772431887131" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="23324" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M431.7 323s13.9 29 21 62.2c15.1 71.2-3.7 100.5-26.9 99.1-24-1.4-37.1-37.8-37.1-37.8S377 404.2 374.2 390c-2.6-12.9-7.2-52.1-7.2-52.1s5.6-14.8 4.9-28.8c-0.6-11.9-3.2-29.1-3.2-29.1s19.4-8.1 26.6-10.4c30-9.8 58.8-24.8 70-38.6 10.1-12.3 20.9-39.9 20.9-39.9s7.1 11.1 9 15.6c2.3 5.5 8 24.3 8 24.3s55.6 35.1 79.7 57c46.7 42.4 40.6 73 23.9 87.3-20.6 17.6-49.2 2.4-78.7-16-22.4-14-54.6-51.7-54.6-51.7s-23.8-33.2-38.8-27.6c-18.1 6.9-3 43-3 43zM285.2 341.2s-19.3 17.6-40.3-7.8c-12-14.5-11.1-31.2-8-41.2 5.4-17.5 23-47.8 52.3-51.2 13.2-1.5 18.1 9.5 18.1 9.5s-18.4 19-24.1 35.3c-6.1 17.8 2 55.4 2 55.4z" fill="#d81e06" p-id="23325"></path><path d="M292.5 338c3.9 11.7 37 24.9 62.1 4.4 20.5-16.7 8.1-59.8 8.1-59.8s-16.9 25.7-41.1 13.2c-15.7-8.1-9.6-41.3-9.6-41.3s-10.9 4.2-21.3 34.1c-8.6 25.1-1 41 1.8 49.4z" fill="#d81e06" p-id="23326"></path><path d="M317.5 258.8s-3 31.5 10.7 32.9c16 1.7 32.5-15.1 32.5-15.1s4.2-3.1 20.9-10.8c24.8-11.4 57.2-13.1 79-40.2 16.3-20.3 18.2-45.1 18.2-45.1s-21.9-32.9-30.9-41.3c-9.9-9.4-44.6-29.9-44.6-29.9s-22.8 2.9-30 4.2c-8.3 1.5-32 7.2-32 7.2s-4.5 0-5.3-4.3c-0.8-4.4 5.7-5.9 5.7-5.9s28-5 37.4-6.2c5.7-0.8 22.9-1.8 22.9-1.8s4.4-25.9-42.4-38.5c-39-10.6-78.9 21.4-78.9 21.4s-26 20.3-37.2 41.4c-14.3 26.9 3.6 47.6 3.6 47.6s13.8-15.4 19.3-19.9c6.2-5.1 26.5-19.3 26.5-19.3s3.8-1.1 4-0.7c1.7 3.4-3.5 7.7-3.5 7.7s-14.6 10.4-18.5 13.8c-7.1 6-22.4 15.3-25.8 23.3-0.2 0.5 0.2 1.7 0 2.2-3.1 9.5-12.1 12.2-12 38 0 11.5 6.9 47.8 6.9 47.8s23.3-31.1 40.5-33.4c25.6-3.4 25.3 12 26.6 11 1.5-1.1 14-22.7 29.2-29.5 15.3-6.8 24.4-5.8 24.4-5.8s-18.3-14.4-24-21.2c-4.6-5.5-9.2-22.7-9.2-22.7s0.8-3.9 5.2-4.3c4.2-0.4 5.9 3.8 5.9 3.8s-0.6 16 9.3 25.3c9.4 8.8 32.1 15.7 33.7 19.1 1.8 3.8-40.5 8.4-49 15-15.2 12.2-19.1 34.2-19.1 34.2z" fill="#d81e06" p-id="23327"></path><path d="M342.7 863.7c31.5-15.2 111.2-9.5 97.7-55.6-8.1-27.7-92.1 1.8-95.7-5.3-6.4-12.6 59.2-36.4 79.4-47.7 37.1-20.7 39.1-45.1 32.7-58.2C431 644.3 300.6 754.2 296 751c-16.1-11.3 152.1-109.3 92.6-157.9-43.2-35.4-129.4 117.3-141.4 110.7-17.2-9.4 78.4-145.2 17.2-169.8-49-19.7-97 188.8-102.9 180.8-15.2-20.7-53.5-79.3-92.8-47.5-21 17.2 23.3 50.7 28.6 68.4 3.3 10.9-2 36.5-1.4 45.9 0.7 11.4 0.2 28.8 3.2 37.7 0.2 0.4 6 14.2 15.5 26.3 9.6 12.2 18.8 18.1 18.2 20.1-0.4 1.4-12.2-8.6-25.8-23.5-12.3-13.5-9.1-21.1-13.8-20.5-4 0.5-34.6 37.9 29.8 94.5 51.7 45.4 101.4 9.4 94.7 7-0.6-0.2-7.4-0.9-14.3-4.7-11.2-6.3-26.4-14.1-26.2-14 26.8 10.9 58.3 23.5 94.5 5.7 21.3-10.4 49.5-36.2 71-46.5z" fill="#d81e06" p-id="23328"></path><path d="M713.2 867.6s26.5-12.6 37.2-29.9c12.6-20.5-0.2-47.4-0.2-47.4s16.4-10.7 21.6-23.4c6.6-16.3 0.4-34 0.4-34s4.2 0.4 10.2-1.5c5.7-1.8 9.9-5.3 9.9-5.3s10.1 23.2 14.7 28.2c9.1 9.8 23.2 10.7 24.6 8.5 4.2-6.8-13.6-6.1-23.6-22.9-3.8-6.4-7.2-12.3-5.6-16.3 2.6-6.5 25.6-20.2 31.7-27.8 9.4-11.9 13.5-18.7 15.2-27.8 0.3-1.8 0.1-5.8-5.1-5.3-1 0.1-3 3-3 3s-13.2 24-22.8 34.6c-13.7 15.2-37.3 22.4-37.3 22.4s-12.7 4.6-26.7-7.8c-10.4-9.2-13.9-22-14.2-30.1-0.6-13.9 7.2-37.7 12.9-51.2 6.2-14.7 22.5-40.2 32.3-56.9 4.1-6.9 23.4-9.5 31.4-9 26.1 1.6 63.1 33.4 63.1 33.4s38.9 26.6 48.3 89.5c7.2 48.3-20.8 81.2-33 102.2-0.8 1.5-0.1 4 1.8 4.4 3.7 0.7 13.3-10.7 23.5-25.8 13.9-20.7 14.4-53.2 14.4-53.2s59.2 47.9-6.7 129.1c-48.5 59.8-94.9 32.3-94.9 32.3s13.8-16.4 18.3-22.5c6.7-9.2 10.5-16.7 10.1-23-0.1-1-1.3-5.2-2.3-3.3-2.2 4-7.7 13.7-7.7 13.7s-7 10.7-9.2 14c-5.9 9-15.1 16.3-15.1 16.3S807.3 889.2 773 890c-37.6 0.7-59.8-22.4-59.8-22.4z" fill="#d81e06" p-id="23329"></path><path d="M621.6 773.7s-12.8 8-17.3 17.1c-2.9 5.7-3.7 20.2-0.8 26 5.3 10.7 26.7 23.9 37.8 29.3 13.6 6.6 47.3 18.5 63 16.2 11.6-1.7 24-10.1 35.6-23.8 12.8-15.1 5.7-45.2 5.7-45.2s-21.7 14.4-57.2 5.1c-22.8-5.9-66.8-24.7-66.8-24.7z" fill="#d81e06" p-id="23330"></path><path d="M625 689.7s-17 12.4-22.8 28.7c-6.3 17.9 13.6 36.6 13.6 36.6s18.8 16.7 49.2 29c26.7 10.8 58.4 8.5 58.4 8.5s15.9 0.3 32.7-17c12.3-12.7 5.6-34.7 5.6-34.7s-26.9 5.6-68.9-13.6c-17.7-8-67.8-37.5-67.8-37.5z" fill="#d81e06" p-id="23331"></path><path d="M649.6 612.6c-6.8-0.6-22.8 3.9-31.1 22.8-8.7 19.7 6.1 37.6 6.1 37.6s21.7 21.4 57.8 42c50.1 28.6 84.9 18.8 84.9 18.8s-25.9-12-34.2-35.8c-7.7-22.1 3.8-45.9 3.8-45.9s-22.2-15.9-30.8-19.9c-13.3-5.8-41.6-18.4-56.5-19.6zM740.9 645.5s11.1-28.1 15.4-37c3.9-8 18.4-32.9 18.4-32.9s-39.8-28.9-66.1-27.9c-15.3 0.6-29.6 6-37 12.4-5.3 4.5-10.5 12.9-14.9 23.7-2.1 5.1-3.3 21.7-3.3 21.7s35.8 11.5 46.1 16.2c11.5 5.2 41.4 23.8 41.4 23.8z" fill="#d81e06" p-id="23332"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772430169211" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8596" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M858.938 121.599H653.556C632.92 64.605 578.877 23.333 515 23.333c-63.875 0-117.92 41.272-138.556 98.266H171.065c-54.046 0-98.266 44.22-98.266 98.266v687.872c0 54.043 44.22 98.263 98.266 98.263h687.872c54.043 0 98.263-44.22 98.263-98.263V219.865c0-54.046-44.22-98.266-98.262-98.266z m-343.938 0c27.027 0 49.138 22.11 49.138 49.134 0 27.021-22.11 49.131-49.138 49.131-27.024 0-49.134-22.11-49.134-49.131 0-27.024 22.11-49.134 49.134-49.134z m98.263 687.864H269.334V711.2h343.929v98.263z m147.4-196.527H269.334v-98.274h491.329v98.274z m0-196.537H269.334v-98.266h491.329v98.266z" p-id="8597" fill="#13227a"></path></svg>
|
||||
|
After Width: | Height: | Size: 949 B |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772430285877" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11595" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M879 113c17.673 0 32 14.327 32 32v734c0 17.673-14.327 32-32 32H145c-17.673 0-32-14.327-32-32V145c0-17.673 14.327-32 32-32h734z m-40 72H185v654h654V185zM651 644v56H287v-56h364z m123.699-256v152h-124.14v61.278l-89-89 89-89V484h68.139v-96h56zM527 484v56H287v-56h240z m124-168v56H287v-56h364z" fill="#d81e06" p-id="11596"></path></svg>
|
||||
|
After Width: | Height: | Size: 665 B |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772429959597" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6511" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M96 228.064c0-70.72 93.12-124.128 208-124.128S512 157.376 512 228.064v181.44c0 70.688-93.12 123.136-208 123.136S96 480.192 96 409.504v-181.44z m416 384.96c0-70.656 93.12-124.096 208-124.096s208 53.44 208 124.128v177.856c0 70.72-93.12 123.136-208 123.136S512 861.6 512 790.912v-177.856z" fill="#FFFFFF" p-id="6512"></path><path d="M240 700.352v122.24C277.888 871.552 306.144 896 320 896v32c-28.416 0-62.912-30.176-108.736-90.304l-3.264-4.288v-133.056L151.104 752l-17.6-16L224 653.824 314.496 736l-17.6 16L240 700.352z m544-376.704v-122.24C746.112 152.448 717.856 128 704 128V96c28.416 0 62.912 30.176 108.736 90.304l3.264 4.288v133.056L872.896 272l17.6 16L800 370.176 709.504 288l17.6-16 56.896 51.648z" fill="#27A2DF" p-id="6513"></path><path d="M480 344.096V279.968C443.168 313.632 378.112 336 304 336S164.832 313.632 128 280v64.096c18.624 32.512 90.336 62.816 176 62.816s157.376-30.304 176-62.816z m-176 197.632c-114.88 0-208-53.696-208-119.936V216C96 149.728 189.12 96 304 96S512 149.728 512 216v205.76c0 66.24-93.12 119.968-208 119.968z m176-152.864c-37.248 30.144-103.68 49.984-176 49.984s-138.752-19.84-176-49.984v45.28c13.504 37.024 85.504 73.28 176 73.28s162.496-36.256 176-73.28v-45.28zM304 301.76c100.576 0 176-44.864 176-85.728s-75.424-85.728-176-85.728S128 175.136 128 216s75.424 85.728 176 85.728zM896 728.096v-64.128c-36.832 33.664-101.888 56.032-176 56.032s-139.168-22.368-176-56v64.096c18.624 32.512 90.336 62.816 176 62.816s157.376-30.304 176-62.816z m-176 197.632c-114.88 0-208-53.696-208-119.936V600c0-66.272 93.12-120 208-120s208 53.728 208 120v205.76c0 66.24-93.12 119.968-208 119.968z m176-152.864c-37.248 30.144-103.68 49.984-176 49.984s-138.752-19.84-176-49.984v45.28c13.504 37.024 85.504 73.28 176 73.28s162.496-36.256 176-73.28v-45.28z m-176-87.136c100.576 0 176-44.864 176-85.728s-75.424-85.728-176-85.728S544 559.136 544 600s75.424 85.728 176 85.728z" fill="#5D6D7E" p-id="6514"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772430510391" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13643" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M414.427429 445.878857h81.334857L344.795429 39.789714H241.810286l-152.137143 406.089143h78.994286l38.619428-107.666286h169.691429l37.449143 107.666286z m-184.32-172.032l59.099428-165.595428h7.021714l57.929143 165.595428H230.107429z m645.266285 673.645714h81.334857l-150.966857-406.089142H702.756571l-152.137142 406.089142h78.994285l38.619429-107.666285h169.691428l37.449143 107.666285z m-184.32-172.032l59.099429-165.595428h7.021714l57.929143 165.595428H691.053714z" p-id="13644" fill="#d4237a"></path><path d="M599.771429 118.272c119.222857 5.705143 218.185143 72.045714 244.955428 193.389714l-68.754286 3.657143 119.515429 159.012572L997.668571 303.542857l-76.507428 4.022857C892.342857 143.798857 761.782857 43.227429 603.428571 36.278857c-21.942857-0.658286-41.179429 19.090286-39.936 42.934857 2.56 20.406857 17.554286 38.253714 36.425143 39.058286zM439.588571 900.096c-133.339429-0.292571-242.249143-79.579429-259.657142-225.060571l73.142857-6.436572-124.854857-154.770286-96.182858 174.08 73.142858-6.363428c19.529143 187.977143 162.523429 298.496 333.824 300.617143 21.942857-0.219429 40.521143-20.626286 38.473142-44.397715a40.740571 40.740571 0 0 0-37.888-37.668571z" p-id="13645" fill="#d4237a"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772430578915" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18392" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M73.138103 0a36.569052 36.569052 0 0 0-36.569051 36.569052v219.414309A36.569052 36.569052 0 0 0 73.138103 292.552413h219.41431a36.569052 36.569052 0 0 0 36.569051-36.569052v-54.853577h365.690516v50.465291L251.595075 694.81198H73.138103a36.569052 36.569052 0 0 0-36.569051 36.569052v219.414309a36.569052 36.569052 0 0 0 36.569051 36.569052h219.41431a36.569052 36.569052 0 0 0 36.569051-36.569052v-54.853577h438.828619v91.422629a36.569052 36.569052 0 0 0 62.45994 25.890888l146.276207-146.276206a36.569052 36.569052 0 0 0 0-51.781777l-146.276207-146.276206a36.569052 36.569052 0 0 0-62.45994 25.890888v91.422629h-438.828619v-13.89624L808.907421 292.552413H950.795341a36.569052 36.569052 0 0 0 36.569052-36.569052v-219.414309A36.569052 36.569052 0 0 0 950.795341 0h-219.414309a36.569052 36.569052 0 0 0-36.569052 36.569052v54.853577h-365.690516V36.569052A36.569052 36.569052 0 0 0 292.552413 0H73.138103z" fill="#000000" p-id="18393"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772431978344" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="24906" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M539 374l-27-131.6-25 131.2c-27.2 4.9-53.4 17.9-74.4 38.9-54.9 54.9-54.9 143.9 0 198.8 54.9 54.9 143.9 54.9 198.8 0 54.9-54.9 54.9-143.9 0-198.8C590.8 392 565.5 379.2 539 374z" p-id="24907" fill="#ea986c"></path><path d="M512 0C229.2 0 0 229.2 0 512s229.2 512 512 512 512-229.2 512-512S794.8 0 512 0zM828.8 828.8 629.5 629.4c-21.4 21.3-47.2 35.7-74.4 43l73 272.5c-37.6 10.1-76.4 15.1-116 15.1s-78.4-5.1-116-15.1l73-272.5c-27.2-7.2-53-21.6-74.4-43L195.2 828.8c0 0 0 0 0 0-41.2-41.2-73.5-89.1-96-142.4-8.1-19.1-14.8-38.6-20.1-58.4L351.5 555c-7.5-28.2-7.5-57.9 0-86L79.1 396c5.3-19.8 12-39.3 20.1-58.4 22.6-53.3 54.9-101.3 96-142.4 0 0 0 0 0 0l199.3 199.3c21.4-21.3 47.2-35.7 74.4-43L396 79.1C433.6 69.1 472.4 64 512 64s78.4 5.1 116 15.1l-73 272.4c27.2 7.3 53 21.6 74.4 43l199.3-199.4c0 0 0 0 0 0 41.2 41.2 73.5 89.1 96 142.4 8.1 19.1 14.8 38.6 20.1 58.4l-272.5 73c7.5 28.1 7.5 57.8 0 86l272.5 73c-5.3 19.8-12 39.3-20.1 58.4C902.3 739.7 869.9 787.6 828.8 828.8 828.8 828.8 828.8 828.8 828.8 828.8z" p-id="24908" fill="#ea986c"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772432300485" class="icon" viewBox="0 0 1562 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="28422" xmlns:xlink="http://www.w3.org/1999/xlink" width="305.078125" height="200"><path d="M41.714526 700.631579H150.366316c105.256421 0 178.014316-56.751158 178.014316-182.37979S255.622737 339.752421 145.515789 339.752421H41.714526V700.631579z m86.824421-69.847579V408.629895h11.641264c58.206316 0 99.435789 23.767579 99.435789 109.621894 0 85.854316-41.229474 112.532211-99.435789 112.532211h-11.641264z m379.796211 76.638316c69.847579 0 134.844632-53.355789 134.844631-144.545684 0-91.674947-64.997053-145.030737-134.844631-145.030737-70.332632 0-135.329684 53.355789-135.329684 145.030737 0 91.189895 64.997053 144.545684 135.329684 144.545684z m0-69.847579c-32.498526 0-47.050105-29.103158-47.050105-74.698105 0-46.08 14.551579-75.183158 47.050105-75.183158 32.013474 0 46.565053 29.103158 46.565053 75.183158 0 45.594947-14.551579 74.698105-46.565053 74.698105z m267.749053 69.847579c18.917053 0 33.468632-3.395368 42.684631-7.27579l-10.186105-63.541894c-4.850526 0.970105-6.790737 0.970105-9.701053 0.970105-6.790737 0-15.521684-5.335579-15.521684-23.282526V313.074526h-85.854316v297.822316c0 57.721263 19.402105 96.525474 78.578527 96.525474z m202.266947 0c69.847579 0 134.844632-53.355789 134.844631-144.545684 0-91.674947-64.997053-145.030737-134.844631-145.030737-70.332632 0-135.329684 53.355789-135.329684 145.030737 0 91.189895 64.997053 144.545684 135.329684 144.545684z m0-69.847579c-32.498526 0-47.050105-29.103158-47.050105-74.698105 0-46.08 14.551579-75.183158 47.050105-75.183158 32.013474 0 46.565053 29.103158 46.565053 75.183158 0 45.594947-14.551579 74.698105-46.565053 74.698105z m189.170526 63.056842h85.854316v-157.642105c14.551579-36.378947 39.289263-49.475368 59.661474-49.475369 12.126316 0 20.372211 1.940211 30.073263 4.365474l14.551579-74.213053c-7.760842-3.395368-16.976842-5.820632-33.468632-5.820631-27.648 0-58.206316 17.946947-78.578526 54.810947h-1.940211l-5.820631-48.02021h-70.332632V700.631579z" fill="#5182E4" p-id="28423"></path><path d="M243.388632 269.473684h156.186947v-48.505263H301.271579v-51.09221h80.518737v-48.505264H301.271579v-44.624842h94.746947V28.887579H243.388632V269.473684z m260.958315 4.527158c18.755368 0 32.013474-3.880421 41.714527-6.790737l-9.054316-41.391158c-4.527158 1.293474-10.994526 3.233684-16.815158 3.233685-13.581474 0-23.282526-8.084211-23.282526-28.456421V130.425263h43.331368v-44.94821H496.909474v-48.505264h-47.858527l-6.467368 48.505264-28.133053 2.263579v42.684631h24.899369v70.817684c0 43.654737 18.755368 72.757895 64.997052 72.757895z m144.222316 0c46.565053 0 89.896421-35.570526 89.896421-96.363789 0-61.116632-43.331368-96.687158-89.896421-96.687158-46.888421 0-90.219789 35.570526-90.219789 96.687158 0 60.793263 43.331368 96.363789 90.219789 96.363789z m0-46.565053c-21.665684 0-31.366737-19.402105-31.366737-49.798736 0-30.72 9.701053-50.122105 31.366737-50.122106 21.342316 0 31.043368 19.402105 31.043369 50.122106 0 30.396632-9.701053 49.798737-31.043369 49.798736z m209.219369 46.565053c21.665684 0 46.888421-7.437474 65.967157-21.018947l-19.078736-34.600421a74.913684 74.913684 0 0 1-38.804211 11.641263c-23.282526 0-40.744421-10.347789-45.918316-35.570526h108.005053c1.832421-8.084211 2.694737-16.330105 2.586947-24.576 0-50.445474-25.869474-88.926316-78.901894-88.926316-44.624842 0-87.309474 36.864-87.309474 96.687158 0 60.793263 40.744421 96.363789 93.453474 96.363789z m-38.804211-117.059368c3.880421-21.989053 17.785263-32.013474 33.630316-32.013474 21.018947 0 29.103158 13.581474 29.103158 32.013474h-62.733474z" fill="#5182E4" fill-opacity=".3" p-id="28424"></path><path d="M914.324211 973.877895c54.703158 0 91.890526-39.343158 91.890526-105.094737 0-65.482105-37.187368-102.938947-91.890526-102.938947-54.703158 0-91.890526 37.187368-91.890527 102.938947s37.187368 105.094737 91.890527 105.094737z m0-41.498948c-26.408421 0-42.576842-24.522105-42.576843-63.595789 0-38.804211 16.168421-61.709474 42.576843-61.709474 26.408421 0 42.846316 22.905263 42.846315 61.709474 0 39.073684-16.437895 63.595789-42.846315 63.595789z m180.277894 41.498948c14.551579 0 29.372632-7.814737 40.151579-18.593684h1.077895l3.503158 14.821052h39.073684v-215.309474h-48.235789v51.2l1.886315 22.905264c-9.970526-9.701053-19.941053-15.898947-36.917894-15.898948-31.797895 0-63.326316 30.181053-63.326316 80.572632 0 49.852632 24.522105 80.303158 62.787368 80.303158z m12.665263-39.343158c-16.437895 0-26.138947-12.395789-26.138947-41.768421 0-28.294737 12.126316-40.690526 25.330526-40.690527a33.953684 33.953684 0 0 1 23.713685 9.162106v60.362105c-6.467368 9.970526-13.743158 12.934737-22.905264 12.934737z m109.67579 35.570526h47.696842v-153.330526h-47.696842V970.105263z m23.983158-174.618947c15.898947 0 27.216842-10.24 27.216842-24.522105 0-14.551579-11.317895-24.791579-27.216842-24.791579-16.168421 0-27.486316 10.24-27.486316 24.791579 0 14.282105 11.317895 24.522105 27.486316 24.522105z m129.077895 178.391579c38.804211 0 74.913684-29.642105 74.913684-80.303158 0-50.930526-36.109474-80.572632-74.913684-80.572632-39.073684 0-75.183158 29.642105-75.183158 80.572632 0 50.661053 36.109474 80.303158 75.183158 80.303158z m0-38.804211c-18.054737 0-26.138947-16.168421-26.138948-41.498947 0-25.6 8.084211-41.768421 26.138948-41.768421 17.785263 0 25.869474 16.168421 25.869473 41.768421 0 25.330526-8.084211 41.498947-25.869473 41.498947z" fill="#5182E4" fill-opacity=".504" p-id="28425"></path><path d="M238.753684 970.105263h45.810527v-66.56c0-24.252632-4.042105-51.738947-6.197895-74.105263h1.347368l19.671579 43.924211 49.583158 96.741052h49.044211v-200.488421h-45.810527v66.290526c0 23.983158 3.772632 52.816842 6.467369 74.105264H357.322105l-19.671579-44.463158-49.852631-95.932632h-49.044211V970.105263z m268.665263 3.772632c38.804211 0 74.913684-29.642105 74.913685-80.303158 0-50.930526-36.109474-80.572632-74.913685-80.572632-39.073684 0-75.183158 29.642105-75.183158 80.572632 0 50.661053 36.109474 80.303158 75.183158 80.303158z m0-38.804211c-18.054737 0-26.138947-16.168421-26.138947-41.498947 0-25.6 8.084211-41.768421 26.138947-41.768421 17.785263 0 25.869474 16.168421 25.869474 41.768421 0 25.330526-8.084211 41.498947-25.869474 41.498947z m105.094737 35.031579h47.696842v-102.4c9.431579-8.892632 15.898947-13.743158 26.677895-13.743158 12.126316 0 17.785263 6.197895 17.785263 27.216842V970.105263h47.966316v-94.854737c0-38.265263-14.282105-62.248421-47.696842-62.248421-20.749474 0-36.378947 10.509474-49.044211 22.905263h-1.077894l-3.233685-19.132631h-39.073684V970.105263zM1047.174737 269.473684h45.810526V202.913684c0-24.252632-4.042105-51.738947-6.197895-74.105263h1.347369l19.671579 43.924211 49.583158 96.741052h49.04421V68.985263h-45.810526v66.290526c0 23.983158 3.772632 52.816842 6.467368 74.105264h-1.347368l-19.671579-44.463158-49.852632-95.932632h-49.04421V269.473684z m202.105263 0h47.696842V116.143158h-47.696842V269.473684z m23.983158-174.618947c15.898947 0 27.216842-10.24 27.216842-24.522105 0-14.551579-11.317895-24.791579-27.216842-24.791579-16.168421 0-27.486316 10.24-27.486316 24.791579 0 14.282105 11.317895 24.522105 27.486316 24.522105z m108.328421 178.391579c41.768421 0 63.865263-21.827368 63.865263-50.122105 0-27.755789-21.288421-38.804211-40.421053-46.08-15.898947-5.658947-29.911579-9.162105-29.911578-18.324211 0-7.275789 5.389474-11.048421 16.168421-11.048421 10.24 0 21.018947 5.389474 31.797894 12.934737l21.557895-28.564211c-13.473684-9.970526-30.72-19.671579-54.972632-19.671579-35.570526 0-59.284211 19.671579-59.28421 48.774737 0 25.330526 21.557895 37.726316 39.882105 45.002105 15.629474 6.197895 30.450526 10.509474 30.450527 19.671579 0 7.545263-5.389474 12.126316-18.054737 12.126316-12.126316 0-24.791579-5.389474-38.804211-16.168421l-21.288421 29.911579c15.36 12.934737 39.073684 21.557895 59.014737 21.557895z" fill="#5182E4" p-id="28426"></path><path d="M1482.428632 636.928l-13.581474-3.449263c-15.952842-4.096-34.708211-8.407579-51.523369-12.288v-0.862316c16.384-4.311579 35.570526-8.407579 51.523369-12.719158l13.581474-3.449263v32.768z m66.613894-49.367579v-40.528842l-160.390737 49.798737v46.133895l160.390737 49.798736v-39.235368l-36.864-9.269895v-47.427368l36.864-9.269895z m0-54.325895v-38.157473h-70.063158c-16.168421-6.467368-21.989053-17.461895-21.989052-26.516211 0-5.389474 0.862316-9.054316 1.94021-13.365895l-32.983579-6.467368a34.654316 34.654316 0 0 0-2.586947 14.874947c0 12.288 7.976421 25.869474 24.360421 34.92379v0.862316l-21.342316 2.586947v31.258947h122.664421z m3.018106-151.552c0-12.934737-3.880421-28.456421-14.44379-40.313263l-25.222737 14.659369c4.958316 6.251789 8.623158 13.581474 8.623158 21.342315 0 15.090526-12.934737 26.085053-33.199158 26.085053-20.48 0-33.414737-10.563368-33.414737-27.378526 0-5.389474 1.940211-10.132211 6.682948-15.737263l-24.576-17.893053a53.086316 53.086316 0 0 0-13.150316 35.786105c0 34.277053 23.713684 64.458105 64.458105 64.458105 40.528842 0 64.242526-26.516211 64.242527-61.008842z" fill="#5182E4" fill-opacity=".504" p-id="28427"></path></svg>
|
||||
|
After Width: | Height: | Size: 9.0 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772431544365" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="21079" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M68.557 512.41a450.816 450.816 0 1 0 901.632 0 450.816 450.816 0 1 0-901.632 0z" fill="#E9ECFF" p-id="21080"></path><path d="M748.34 771.686H322.866c-40.653 0-73.728-33.075-73.728-73.728V273.357c0-13.466 10.906-24.371 24.371-24.371s24.372 10.905 24.372 24.37v424.551c0 13.773 11.212 24.986 24.985 24.986H748.34c13.466 0 24.371 10.905 24.371 24.371a24.371 24.371 0 0 1-24.37 24.422z" fill="#6C6CEA" p-id="21081"></path><path d="M433.766 693.3h-77.004a16.691 16.691 0 0 1-16.692-16.692V483.021c0-9.216 7.476-16.691 16.692-16.691h77.056c9.216 0 16.69 7.475 16.69 16.69v193.588a16.773 16.773 0 0 1-16.742 16.691z m154.42 0H511.13a16.691 16.691 0 0 1-16.692-16.692V276.122c0-9.216 7.476-16.692 16.692-16.692h77.056c9.216 0 16.69 7.476 16.69 16.692v400.486a16.727 16.727 0 0 1-16.69 16.691z" fill="#757BF2" p-id="21082"></path><path d="M742.554 693.3h-77.056a16.691 16.691 0 0 1-16.692-16.692V330.445c0-9.216 7.476-16.691 16.692-16.691h77.056c9.216 0 16.69 7.475 16.69 16.69v346.164c0 9.216-7.474 16.691-16.69 16.691z" fill="#6C6CEA" p-id="21083"></path><path d="M634.214 722.893H322.918c-13.772 0-24.985-11.213-24.985-24.986v-424.55c0-13.466-10.906-24.371-24.371-24.371s-24.372 10.905-24.372 24.37v424.551c0 40.653 33.076 73.728 73.728 73.728h205.62a445.798 445.798 0 0 0 105.676-48.742z m125.03-392.448c0-9.216-7.474-16.691-16.69-16.691h-77.056a16.691 16.691 0 0 0-16.692 16.69v346.164c0 9.216 7.476 16.691 16.692 16.691h10.342a454.129 454.129 0 0 0 83.405-87.347V330.445z" fill="#757BF2" p-id="21084"></path><path d="M271.053 631.86c9.011 0 17.971-0.308 26.88-0.82V273.357c0-13.466-10.906-24.371-24.371-24.371s-24.372 10.905-24.372 24.37v357.94c7.22 0.41 14.541 0.563 21.863 0.563z m162.713-165.53h-77.004a16.691 16.691 0 0 0-16.692 16.69v143.566a446.413 446.413 0 0 0 110.388-31.898V483.021c0-9.216-7.476-16.691-16.692-16.691z m171.06 17.766V276.122c0-9.216-7.476-16.692-16.692-16.692H511.13a16.691 16.691 0 0 0-16.692 16.692v296.55a450.995 450.995 0 0 0 110.388-88.576z m97.177-170.342h-36.505a16.691 16.691 0 0 0-16.692 16.69v96.666a446.095 446.095 0 0 0 53.197-113.356z" fill="#8486F8" p-id="21085"></path><path d="M297.933 450.048V273.357c0-13.466-10.906-24.371-24.371-24.371s-24.372 10.905-24.372 24.37v189.646a431.748 431.748 0 0 0 48.743-12.954z m214.374-150.22a452.463 452.463 0 0 0 28.314-40.346H511.13a16.691 16.691 0 0 0-16.692 16.69v45.006a419.386 419.386 0 0 0 17.87-21.35" fill="#8D92F8" p-id="21086"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1772430213579" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9712" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M145.6 0C100.8 0 64 35.2 64 80v862.4C64 987.2 100.8 1024 145.6 1024h732.8c44.8 0 81.6-36.8 81.6-81.6V324.8L657.6 0h-512z" fill="#FC7B24" p-id="9713"></path><path d="M960 326.4v16H755.2s-100.8-20.8-99.2-108.8c0 0 4.8 92.8 97.6 92.8H960z" fill="#FB5C1B" p-id="9714"></path><path d="M657.6 0v233.6c0 25.6 17.6 92.8 97.6 92.8H960L657.6 0z" fill="#FFFFFF" p-id="9715"></path><path d="M374.4 860.8c-3.2 0-6.4 0-9.6-3.2l-59.2-80-59.2 80c-3.2 3.2-6.4 3.2-9.6 3.2-6.4 0-11.2-4.8-11.2-11.2 0-1.6 0-4.8 1.6-6.4l62.4-81.6-57.6-78.4c-1.6-1.6-1.6-3.2-1.6-6.4 0-4.8 4.8-11.2 11.2-11.2 3.2 0 6.4 1.6 9.6 4.8l54.4 73.6 54.4-73.6c3.2-3.2 6.4-4.8 9.6-4.8 6.4 0 11.2 4.8 11.2 11.2 0 3.2 0 4.8-1.6 6.4l-57.6 76.8 62.4 83.2c1.6 1.6 1.6 4.8 1.6 6.4 0 6.4-4.8 11.2-11.2 11.2z m230.4 0c-6.4 0-11.2-4.8-11.2-11.2v-148.8l-64 156.8c-1.6 1.6-3.2 3.2-6.4 3.2s-4.8-1.6-6.4-3.2l-64-156.8v148.8c0 6.4-4.8 11.2-11.2 11.2-8 0-12.8-4.8-12.8-11.2V684.8c0-9.6 9.6-19.2 20.8-19.2 8 0 16 4.8 19.2 12.8l54.4 134.4 54.4-134.4c3.2-8 11.2-12.8 19.2-12.8 11.2 0 20.8 9.6 20.8 19.2v164.8c0 6.4-4.8 11.2-12.8 11.2z m169.6-1.6h-88c-9.6 0-17.6-8-17.6-17.6V676.8c0-6.4 6.4-11.2 12.8-11.2s11.2 4.8 11.2 11.2v161.6h81.6c4.8 0 9.6 4.8 9.6 9.6 0 6.4-4.8 11.2-9.6 11.2z" fill="#FFFFFF" p-id="9716"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 795 KiB |
@@ -13,9 +13,11 @@ const componentStore = useComponentStore()
|
||||
<template>
|
||||
<el-container>
|
||||
<!-- left -->
|
||||
<el-aside class="fixed top-0 left-0 h-full z-10 c-md:block c-sm:hidden c-xs:hidden" width="240px" v-show="!componentStore.leftCom">
|
||||
<transition name="left-sidebar">
|
||||
<el-aside v-if="!componentStore.leftCom" class="fixed top-0 left-0 h-full z-10 c-md:block c-sm:hidden c-xs:hidden" width="240px">
|
||||
<Left></Left>
|
||||
</el-aside>
|
||||
</el-aside>
|
||||
</transition>
|
||||
<el-drawer
|
||||
show-close
|
||||
size="240px"
|
||||
@@ -23,11 +25,11 @@ const componentStore = useComponentStore()
|
||||
v-model="componentStore.leftComDrawer"
|
||||
direction="ltr"
|
||||
>
|
||||
<Left bgColor="#fff"></Left>
|
||||
<Left></Left>
|
||||
</el-drawer>
|
||||
|
||||
<!-- right -->
|
||||
<el-container :class="!componentStore.leftCom ? 'c-md:ml-[240px]' : ''">
|
||||
<el-container :class="!componentStore.leftCom ? 'c-md:ml-[240px]' : ''" class="main-container">
|
||||
<el-header>
|
||||
<Header/>
|
||||
</el-header>
|
||||
@@ -63,4 +65,61 @@ const componentStore = useComponentStore()
|
||||
.animation-leave-active {
|
||||
transition: all 0.3s cubic-bezier(1, 0.6, 0.6, 1);
|
||||
}
|
||||
|
||||
/* 左侧菜单折叠动画 */
|
||||
.left-sidebar-enter-from,
|
||||
.left-sidebar-leave-to {
|
||||
transform: translateX(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.left-sidebar-enter-to,
|
||||
.left-sidebar-leave-from {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.left-sidebar-enter-active,
|
||||
.left-sidebar-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* 主内容区域动画 */
|
||||
.main-container {
|
||||
transition: margin-left 0.3s ease;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
/* 全局科技感样式 */
|
||||
body {
|
||||
background-color: #f0f2f5;
|
||||
background-image:
|
||||
radial-gradient(circle at 10% 20%, rgba(64, 158, 255, 0.05) 0%, transparent 20%),
|
||||
radial-gradient(circle at 80% 30%, rgba(118, 75, 162, 0.05) 0%, transparent 25%),
|
||||
radial-gradient(circle at 40% 70%, rgba(102, 126, 234, 0.05) 0%, transparent 20%),
|
||||
radial-gradient(circle at 90% 90%, rgba(64, 158, 255, 0.05) 0%, transparent 15%);
|
||||
background-attachment: fixed;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 科技感滚动条 */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: linear-gradient(180deg, #409EFF, #667eea);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: linear-gradient(180deg, #667eea, #764ba2);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -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>
|
||||
@@ -1,45 +1,26 @@
|
||||
<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 { StarFilled } from '@element-plus/icons-vue'
|
||||
import { useToolsStore } from '@/store/modules/tools'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useRoute } from "vue-router"
|
||||
//store
|
||||
const toolsStore = useToolsStore()
|
||||
const userStore = useUserStore()
|
||||
const route = useRoute()
|
||||
// const getToolsCate = async () => {
|
||||
// try {
|
||||
// await toolsStore.getToolCate()
|
||||
// } catch (error: any) {
|
||||
// ElMessage.error(error.message)
|
||||
// }
|
||||
// }
|
||||
|
||||
//收藏
|
||||
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)
|
||||
// 取消收藏
|
||||
const removeCollect = (url: string) => {
|
||||
const success = toolsStore.removeCollect(url)
|
||||
if (success) {
|
||||
ElMessage.success('已取消收藏');
|
||||
} else {
|
||||
ElMessage.error('取消收藏失败');
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// getToolsCate()
|
||||
toolsStore.loadCollectedTools()
|
||||
if (route.query && route.query.value) {//底部导航跳转过来的则定位到响应位置
|
||||
document?.querySelector('#' + `${route.query.value}`)?.scrollIntoView();
|
||||
} else {//其他位置跳转过来不需要定位的则定位到顶部
|
||||
@@ -50,24 +31,30 @@ 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 class="mt-8 mb-3 text-xl font-bold text-[--base-black] tech-title">
|
||||
收藏工具
|
||||
</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 v-if="toolsStore.collectedTools.length > 0" class="flex justify-between flex-wrap self-card-div c-xs:ml-0" :gutter="10">
|
||||
<router-link v-for="(item, index) in toolsStore.collectedTools" :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 hover:-translate-y-2 duration-300 tech-card">
|
||||
<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="flex justify-between">
|
||||
<div class="font-semibold text-lg line-clamp-1">{{ item.title }}</div>
|
||||
<el-button
|
||||
:icon="StarFilled"
|
||||
circle
|
||||
size="small"
|
||||
@click.prevent="removeCollect(item.url)"
|
||||
type="warning"
|
||||
/>
|
||||
</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>
|
||||
@@ -76,22 +63,22 @@ onMounted(() => {
|
||||
</div>
|
||||
</router-link>
|
||||
<!-- 占位 div -->
|
||||
<div class="w-[24%] c-md:w-[24%] c-sm:w-[32%]"></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 v-else class="mt-5 p-5 bg-white rounded-2xl">
|
||||
<el-empty description="暂无收藏工具" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- list -->
|
||||
<!-- 工具分类列表 -->
|
||||
<div v-for="(cate, index) in toolsStore.cates" :key="index">
|
||||
<!-- cate title -->
|
||||
<div class="mt-8 mb-3 text-xl font-bold text-[--base-black]" :id="'cate_' + cate.id">
|
||||
<div class="mt-8 mb-3 text-xl font-bold text-[--base-black] tech-title" :id="'cate_' + cate.id">
|
||||
{{ cate.title }}
|
||||
</div>
|
||||
<!-- card -->
|
||||
<div class="flex justify-between flex-wrap self-card-div c-xs:ml-0" :gutter="10">
|
||||
<router-link v-for="(item, index) in cate.list" :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 hover:-translate-y-2 duration-300">
|
||||
<router-link v-for="(item, index) in cate.list" :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 hover:-translate-y-2 duration-300 tech-card">
|
||||
<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">
|
||||
@@ -100,7 +87,6 @@ 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)"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -112,6 +98,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>
|
||||
|
||||
@@ -120,4 +109,59 @@ onMounted(() => {
|
||||
content: "";
|
||||
width: 24%
|
||||
}
|
||||
|
||||
/* 科技感卡片样式 */
|
||||
.router-link-active .tech-card {
|
||||
border-color: #409EFF;
|
||||
box-shadow: 0 0 20px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
.tech-card {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid transparent;
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8f9ff 100%);
|
||||
}
|
||||
|
||||
.tech-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 30px rgba(64, 158, 255, 0.2);
|
||||
border-color: #409EFF;
|
||||
}
|
||||
|
||||
.tech-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, #409EFF, #667eea, #764ba2);
|
||||
transform: scaleX(0);
|
||||
transform-origin: left;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.tech-card:hover::before {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
|
||||
/* 科技感标题样式 */
|
||||
.tech-title {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.tech-title::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
left: 0;
|
||||
width: 60px;
|
||||
height: 3px;
|
||||
background: linear-gradient(90deg, #409EFF, #667eea);
|
||||
border-radius: 3px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,15 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
// import { Star } from '@element-plus/icons-vue'
|
||||
import { onMounted, reactive } from 'vue';
|
||||
import { Star, StarFilled } from '@element-plus/icons-vue'
|
||||
import { onMounted, reactive, ref } 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 {rtrim} from '@/utils/string'
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
id: Number
|
||||
})
|
||||
const props = defineProps({ title: String, id: Number})
|
||||
const route = useRoute()
|
||||
//查询参数
|
||||
const searchParam = reactive({
|
||||
@@ -19,62 +15,136 @@ const searchParam = reactive({
|
||||
})
|
||||
//store
|
||||
const toolsStore = useToolsStore()
|
||||
const userStore = useUserStore()
|
||||
const isCollected = ref(false)
|
||||
|
||||
//根据路由查询tool id
|
||||
const getToolInfo = async () => {
|
||||
let routeStr = route.path
|
||||
searchParam.route = rtrim(routeStr, '/')
|
||||
await toolsStore.getToolInfo(searchParam)
|
||||
// 检查收藏状态
|
||||
isCollected.value = toolsStore.isCollected(searchParam.route)
|
||||
}
|
||||
|
||||
//收藏
|
||||
const toolCollect = reactive({
|
||||
toolId: 0
|
||||
})
|
||||
const collect = async (toolId) => {
|
||||
try {
|
||||
if (!userStore.isLogin()) {
|
||||
//未登录,弹出登录窗口
|
||||
ElMessage.error('请登录')
|
||||
return
|
||||
// 收藏/取消收藏
|
||||
const toggleCollect = () => {
|
||||
const toolInfo = toolsStore.currentTool
|
||||
if (!toolInfo) {
|
||||
ElMessage.warning('获取工具信息失败');
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCollected.value) {
|
||||
// 取消收藏
|
||||
const success = toolsStore.removeCollect(toolInfo.url)
|
||||
if (success) {
|
||||
isCollected.value = false
|
||||
ElMessage.success('已取消收藏');
|
||||
} else {
|
||||
ElMessage.error('取消收藏失败');
|
||||
}
|
||||
toolCollect.toolId = toolId
|
||||
await toolsStore.toolCollect(toolCollect)
|
||||
// ElMessage.success('收藏成功')
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error.message)
|
||||
} else {
|
||||
// 添加收藏
|
||||
const success = toolsStore.addCollect(toolInfo)
|
||||
if (success) {
|
||||
isCollected.value = true
|
||||
ElMessage.success('收藏成功');
|
||||
} else {
|
||||
ElMessage.error('收藏失败,可能已经收藏过了');
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
toolsStore.loadCollectedTools()
|
||||
getToolInfo()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex justify-between rounded-2xl bg-white p-4 mt-5 mb-5">
|
||||
<!-- <div class="text-gray-600">
|
||||
<div class="flex">
|
||||
<RouterLink to="/" class="flex items-center">
|
||||
<el-icon color="blue"><Back /></el-icon>
|
||||
<div>返回首页</div>
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="text-xl">
|
||||
<div class="flex justify-between items-center rounded-2xl p-4 mt-5 mb-5 tech-detail-header">
|
||||
<div class="text-xl tech-title">
|
||||
{{ 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> -->
|
||||
<el-button
|
||||
:icon="isCollected ? StarFilled : Star"
|
||||
:type="isCollected ? 'warning' : 'default'"
|
||||
@click="toggleCollect"
|
||||
class="tech-button"
|
||||
size="default"
|
||||
>
|
||||
{{ isCollected ? '已收藏' : '收藏' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 科技感详情页头部 */
|
||||
.tech-detail-header {
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8f9ff 100%);
|
||||
border: 1px solid rgba(64, 158, 255, 0.2);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tech-detail-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, #409EFF, #667eea, #764ba2);
|
||||
}
|
||||
|
||||
/* 科技感标题 */
|
||||
.tech-title {
|
||||
position: relative;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 科技感按钮 */
|
||||
.tech-button {
|
||||
position: relative;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tech-button:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
|
||||
}
|
||||
|
||||
.tech-button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* 收藏状态按钮样式 */
|
||||
.tech-button.is-warning {
|
||||
background: linear-gradient(135deg, #ffc107, #ff9800);
|
||||
border-color: #ffc107;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tech-button.is-warning:hover {
|
||||
background: linear-gradient(135deg, #ffca28, #ff9800);
|
||||
box-shadow: 0 4px 12px rgba(255, 193, 7, 0.4);
|
||||
}
|
||||
|
||||
/* 未收藏状态按钮样式 */
|
||||
.tech-button.is-default {
|
||||
background: linear-gradient(135deg, #f8f9fa, #e9ecef);
|
||||
border-color: #dee2e6;
|
||||
color: #495057;
|
||||
}
|
||||
|
||||
.tech-button.is-default:hover {
|
||||
background: linear-gradient(135deg, #ffffff, #f8f9fa);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
</style>
|
||||
@@ -1,15 +1,30 @@
|
||||
<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 || '')
|
||||
const currentYear: number = new Date().getFullYear();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-white w-full rounded-2xl z-10 p-5 text-center">
|
||||
Copyright © 2023 · {{ copyright }} <a href="https://beian.miit.gov.cn/" target="_blank">{{ beianNo }}</a>
|
||||
<div class="w-full rounded-2xl z-10 p-5 text-center tech-footer">
|
||||
<el-text>{{ appDesc }} © {{ currentYear }} <a href="https://github.com/naroat/tools-web" target="_blank" class="text-blue-700 tech-link">Tools-Web</a></el-text>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 简约页脚 */
|
||||
.tech-footer {
|
||||
background: transparent;
|
||||
border: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 简约链接 */
|
||||
.tech-link {
|
||||
transition: all 0.2s ease;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.tech-link:hover {
|
||||
color: #409EFF;
|
||||
}
|
||||
</style>
|
||||
@@ -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,92 +40,65 @@ 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">
|
||||
<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">
|
||||
<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">
|
||||
<div class="hidden c-sm:block c-md:hidden c-xs:block">
|
||||
<svg @click="componentStore.setleftComDrawerStatus(!componentStore.leftComDrawer)" :class="['icon', { 'rotate': componentStore.leftComDrawer }]" t="1702978210636" 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>
|
||||
</svg>
|
||||
<svg v-else @click="componentStore.setleftComDrawerStatus(false)" t="1702978577170" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1587" width="30" height="30">
|
||||
<path fill="#444" fill-opacity=".9" d="M128.064 192l768 0.896-0.128 64L128 256l0.064-64z m514.048 294.848a32 32 0 0 0 0 51.2l202.688 152a32 32 0 0 0 51.2-25.6v-304a32 32 0 0 0-51.2-25.6l-202.688 152zM832 424.448v176l-117.312-88L832 424.448zM128 480h416v64H128v-64z m0.064 288l768 0.896-0.128 64L128 832l0.064-64z" p-id="1588"></path>
|
||||
</svg>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<!-- c-md:block -->
|
||||
<Transition name="fold" class="hidden c-md:block">
|
||||
<svg v-if="!componentStore.leftCom" @click="componentStore.setLeftComStatus(true)" t="1702978577170" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1587" width="30" height="30">
|
||||
<path fill="#444" fill-opacity=".9" d="M128.064 192l768 0.896-0.128 64L128 256l0.064-64z m514.048 294.848a32 32 0 0 0 0 51.2l202.688 152a32 32 0 0 0 51.2-25.6v-304a32 32 0 0 0-51.2-25.6l-202.688 152zM832 424.448v176l-117.312-88L832 424.448zM128 480h416v64H128v-64z m0.064 288l768 0.896-0.128 64L128 832l0.064-64z" p-id="1588"></path>
|
||||
</svg>
|
||||
<svg v-else @click="componentStore.setLeftComStatus(false)" 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">
|
||||
<div class="hidden c-md:block">
|
||||
<svg @click="componentStore.setLeftComStatus(!componentStore.leftCom)" :class="['icon', { 'rotate': componentStore.leftCom }]" t="1702978210636" 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>
|
||||
</svg>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<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 +119,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,41 +136,100 @@ 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>
|
||||
<!-- about -->
|
||||
<li class="ml-3 c-xs:hidden">
|
||||
<router-link to="/about">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="关于tools-web"
|
||||
placement="bottom"
|
||||
>
|
||||
<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" 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;"
|
||||
>
|
||||
<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>
|
||||
<!-- 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="ml-3 hover:text-blue-500" v-else>
|
||||
<el-button size="large" type="primary" @click="openLogin">登录</el-button>
|
||||
|
||||
<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>
|
||||
|
||||
<style scoped>
|
||||
.fold-enter-active {
|
||||
transition: all 1s ease-out;
|
||||
|
||||
|
||||
/* 科技感搜索框 */
|
||||
.el-select :deep(.el-select__wrapper){
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8f9ff 100%);
|
||||
border: 1px solid rgba(64, 158, 255, 0.2);
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.1);
|
||||
}
|
||||
|
||||
.fold-enter-from,
|
||||
.fold-leave-to {
|
||||
transform: translateX(20px);
|
||||
opacity: 0;
|
||||
/* 搜索框容器 */
|
||||
.el-select {
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.el-select :deep(.el-select__wrapper):hover {
|
||||
border-color: #409EFF;
|
||||
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2);
|
||||
}
|
||||
|
||||
.el-select :deep(.el-select__wrapper.is-focus) {
|
||||
border-color: #409EFF;
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
||||
}
|
||||
|
||||
/* 科技感按钮 */
|
||||
.el-button {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.el-button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 科技感图标 */
|
||||
.icon {
|
||||
transition: all 0.3s ease;
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
.icon:hover {
|
||||
transform: scale(1.1);
|
||||
filter: drop-shadow(0 0 8px rgba(64, 158, 255, 0.5));
|
||||
}
|
||||
|
||||
/* 旋转动画 */
|
||||
.icon.rotate {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* 简约Header */
|
||||
header {
|
||||
background: transparent;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
@@ -3,16 +3,14 @@
|
||||
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 leftAdv = ref(import.meta.env.VITE_LEFT_ADV || '')
|
||||
//菜单选中
|
||||
const defaultActive = ref('collect')
|
||||
const defaultActive = ref('')
|
||||
//默认展开的菜单
|
||||
const defaultOpeneds = ['cate']
|
||||
//store
|
||||
@@ -53,11 +51,12 @@ const gotoAnchor = (anchor: string) => {
|
||||
}
|
||||
}
|
||||
const gotoAbout = () => {
|
||||
router.push('about')
|
||||
router.push('/about')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getToolCates()
|
||||
onMounted(async () => {
|
||||
await getToolCates()
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -66,7 +65,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 logo-container p-4" 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 +80,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">
|
||||
@@ -122,6 +113,9 @@ onMounted(() => {
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
|
||||
<!-- 广告位 -->
|
||||
<div class="ad-container" v-if="leftAdv != ''" v-html="leftAdv"></div>
|
||||
</el-scrollbar>
|
||||
<!-- </div> -->
|
||||
</template>
|
||||
@@ -132,17 +126,17 @@ onMounted(() => {
|
||||
list-style: none !important;
|
||||
padding-left: 0 !important;
|
||||
margin-top: 1rem;
|
||||
background-color: none !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.el-menu:hover{
|
||||
background-color: none !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.el-sub-menu{
|
||||
border-radius: 10rem !important;
|
||||
list-style: none !important;
|
||||
margin: 0 !important;
|
||||
padding-left: 0 !important;
|
||||
background-color: none !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
/* 深选择器获取menu__title */
|
||||
@@ -150,12 +144,17 @@ onMounted(() => {
|
||||
/* padding-left: 0 !important; */
|
||||
font-size: 1rem;
|
||||
border-radius: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.el-sub-menu :deep(.el-sub-menu__title:hover){
|
||||
/* padding-left: 0 !important; */
|
||||
color: #fff;
|
||||
background-color: #409EFF;
|
||||
background: linear-gradient(135deg, #409EFF, #667eea);
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 4px 15px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
.el-menu-item{
|
||||
@@ -164,15 +163,77 @@ onMounted(() => {
|
||||
height: 38px!important;
|
||||
margin: 10px 0;
|
||||
border-radius: 1rem !important;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.el-menu-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 3px;
|
||||
height: 100%;
|
||||
background: linear-gradient(180deg, #409EFF, #667eea);
|
||||
transform: scaleY(0);
|
||||
transform-origin: center;
|
||||
transition: transform 0.3s ease;
|
||||
border-radius: 0 1rem 1rem 0;
|
||||
}
|
||||
|
||||
.el-menu-item:hover::before,
|
||||
.el-menu-item.is-active::before {
|
||||
transform: scaleY(1);
|
||||
}
|
||||
|
||||
.el-menu-item.is-active{
|
||||
color: #fff;
|
||||
background-color: #409EFF;
|
||||
background: linear-gradient(135deg, #409EFF, #667eea);
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 4px 15px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
.el-menu-item:hover{
|
||||
color: #fff;
|
||||
background-color: #409EFF;
|
||||
background: linear-gradient(135deg, #409EFF, #667eea);
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 4px 15px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 简约Logo区域 */
|
||||
.logo-container {
|
||||
position: relative;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
/* 广告位样式 */
|
||||
.ad-container {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
width: 200px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.ad-container:hover {
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.ad-link {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ad-image {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -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>
|
||||
@@ -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,53 @@
|
||||
<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 p-4 tech-detail-section">
|
||||
<el-divider content-position="left" class="tech-divider">{{ props.title }}</el-divider>
|
||||
<div class="m-4">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 科技感详情区块 */
|
||||
.tech-detail-section {
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8f9ff 100%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 科技感分割线 */
|
||||
.tech-divider {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tech-divider .el-divider__text {
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8f9ff 100%);
|
||||
padding: 0 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.tech-divider::before {
|
||||
background: linear-gradient(90deg, #409EFF, transparent);
|
||||
}
|
||||
|
||||
.tech-divider::after {
|
||||
background: linear-gradient(90deg, transparent, #667eea);
|
||||
}
|
||||
</style>
|
||||
@@ -1,81 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import { copy } from '@/utils/string'
|
||||
const info = reactive({
|
||||
title: "色板",
|
||||
palettes: [
|
||||
{
|
||||
theme: 'last summer day', //
|
||||
item: ['#A7C5C5', '#DEE0D5', '#E2AC48', '#B96028', '#983C2D'],
|
||||
},
|
||||
{
|
||||
theme: 'Hazy Summer Afternoon', //
|
||||
item: ['#26C4A5', '#54D99F', '#ACE08C', '#E8E284', '#F2B652'],
|
||||
},
|
||||
{
|
||||
theme: 'Summering', //
|
||||
item: ['#FFBE4C', '#B33127', '#93EDFF', '#648FAD', '#FFFFFF'],
|
||||
},
|
||||
{
|
||||
theme: 'Bare August — Footcare Essentials', //
|
||||
item: ['#F2636F', '#F2858E', '#F2AEB4', '#F27999', '#F2D9D0'],
|
||||
},
|
||||
{
|
||||
theme: 'Big Vilnius Summer Loto', //
|
||||
item: ['#D90416', '#BF2A45', '#528C49', '#BF7B54', '#F28D8D'],
|
||||
},
|
||||
{
|
||||
theme: 'Illustration Design', //
|
||||
item: ['#D9D2D6', '#010326', '#4C5D73', '#808C65', '#F2E750'],
|
||||
},
|
||||
{
|
||||
theme: 'Beautiful tropical beach with blue sky and white clouds abstract texture background', //
|
||||
item: ['#04B2D9', '#CEECF2', '#04C4D9', '#15BFBF', '#D9D0C7'],
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
//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="">
|
||||
<div class="flex flex-col">
|
||||
<div v-for="(item, index) in info.palettes" :key="index" class="flex flex-col mb-3">
|
||||
<div class="flex h-28 rounded-lg overflow-hidden items-center">
|
||||
<span
|
||||
v-for="(color, index) in item.item"
|
||||
:key="index"
|
||||
class="w-1/5 h-full color-palette"
|
||||
:style="'background-color:' + color + ';'"
|
||||
@click="copyRes(color)"
|
||||
>
|
||||
<div class="color-text hidden flex items-center justify-center h-full w-full" :style="'color:' + color + ';'" >{{ color }}</div>
|
||||
</span>
|
||||
<!-- @change="(val: any) => (changeCheckBox(val, 3)) -->
|
||||
</div>
|
||||
<!-- <div class="">
|
||||
<el-text>主题:{{ item.theme }}</el-text>
|
||||
</div> -->
|
||||
<el-divider v-if="index != item.item.length + 1"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.color-palette:hover .color-text{
|
||||
display: flex;
|
||||
filter: grayscale(1) contrast(999) invert(1)
|
||||
}
|
||||
</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>
|
||||
ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是一种字符编码标准,它使用一个字节(8位二进制数)来表示128个不同的字符。这些字符包括33个控制字符(具有特殊功能但不可直接显示的字符)和95个可打印字符。ASCII码表中的字符从0到127编号,其中0代表空字符(NULL),1代表开始头(SOH),2代表正文开始(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({
|
||||
@@ -86,7 +87,7 @@ onBeforeMount(() => {
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div class="flex mb-2">
|
||||
<el-text class="w-20">弹幕内容:</el-text>
|
||||
<div class="w-72"><el-input v-model="info.content" type="textarea" rows="3"></el-input></div>
|
||||
<div class="w-72"><el-input v-model="info.content" type="textarea" :rows="3" placeholder="可在此输入弹幕内容"></el-input></div>
|
||||
</div>
|
||||
|
||||
<div class="flex mb-2">
|
||||
@@ -114,6 +115,14 @@ onBeforeMount(() => {
|
||||
<el-text>双击可退出弹幕</el-text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
手持弹幕是一种新型的互动沟通工具,可以方便地为各种户外活动、演出嘉年华等活动增加趣味性和互动性。手持弹幕具有轻便、易携带、易操作等优点,可以让每个参与者都变成活动的一部分。同时,手持弹幕还可以通过预先编写的文本、表情等形式,表达参与者的情感和想法,实现沟通互动。在社交媒体时代,手持弹幕的使用也带来了更广泛的社交效应,增加了活动的互动性和传播度。无论是举办方还是参与者,手持弹幕都是一个非常有价值的互动工具。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
<script setup lang="ts">
|
||||
import { 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 { copy } from '@/utils/string'
|
||||
|
||||
// 状态管理
|
||||
const title = "Base64加解密工具"
|
||||
const activeTab = ref('encode')
|
||||
const encodeInput = ref('')
|
||||
const encodeOutput = ref('')
|
||||
const decodeInput = ref('')
|
||||
const decodeOutput = ref('')
|
||||
|
||||
// 加密处理
|
||||
const encodeText = () => {
|
||||
if (!encodeInput.value) {
|
||||
ElMessage.warning('请输入要加密的文本')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
encodeOutput.value = btoa(unescape(encodeURIComponent(encodeInput.value)))
|
||||
ElMessage.success('加密成功')
|
||||
} catch (error) {
|
||||
ElMessage.error('加密失败:' + error)
|
||||
}
|
||||
}
|
||||
|
||||
// 解密处理
|
||||
const decodeText = () => {
|
||||
if (!decodeInput.value) {
|
||||
ElMessage.warning('请输入要解密的Base64字符串')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
decodeOutput.value = decodeURIComponent(escape(atob(decodeInput.value)))
|
||||
ElMessage.success('解密成功')
|
||||
} catch (error) {
|
||||
ElMessage.error('解密失败:请输入有效的Base64字符串')
|
||||
}
|
||||
}
|
||||
|
||||
// 复制结果
|
||||
const copyEncodeResult = () => {
|
||||
if (!encodeOutput.value) {
|
||||
ElMessage.warning('没有可复制的内容')
|
||||
return
|
||||
}
|
||||
|
||||
copy(encodeOutput.value)
|
||||
}
|
||||
|
||||
const copyDecodeResult = () => {
|
||||
if (!decodeOutput.value) {
|
||||
ElMessage.warning('没有可复制的内容')
|
||||
return
|
||||
}
|
||||
|
||||
copy(decodeOutput.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="加密" name="encode">
|
||||
<div class="mb-4">
|
||||
<el-input
|
||||
v-model="encodeInput"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
placeholder="请输入要加密的文本"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<el-button type="primary" @click="encodeText">加密</el-button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<el-input
|
||||
v-model="encodeOutput"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
placeholder="加密结果"
|
||||
readonly
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="copyEncodeResult">复制</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="解密" name="decode">
|
||||
<div class="mb-4">
|
||||
<el-input
|
||||
v-model="decodeInput"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
placeholder="请输入要解密的Base64字符串"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<el-button type="primary" @click="decodeText">解密</el-button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<el-input
|
||||
v-model="decodeOutput"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
placeholder="解密结果"
|
||||
readonly
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="copyDecodeResult">复制</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线Base64加解密工具,支持文本的Base64编码和解码,可用于URL编码、数据传输等场景。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,132 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } 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'
|
||||
|
||||
const title = "进制转换计算器"
|
||||
|
||||
// 状态管理
|
||||
const inputValue = ref('')
|
||||
const fromBase = ref(10)
|
||||
const toBase = ref(2)
|
||||
|
||||
// 进制选项
|
||||
const baseOptions = [
|
||||
{ label: '2 (二进制)', value: 2 },
|
||||
{ label: '8 (八进制)', value: 8 },
|
||||
{ label: '10 (十进制)', value: 10 },
|
||||
{ label: '16 (十六进制)', value: 16 },
|
||||
]
|
||||
|
||||
// 转换结果
|
||||
const convertedValue = computed(() => {
|
||||
if (!inputValue.value) return ''
|
||||
|
||||
try {
|
||||
// 先转换为十进制
|
||||
const decimalValue = parseInt(inputValue.value, fromBase.value)
|
||||
if (isNaN(decimalValue)) return '无效输入'
|
||||
|
||||
// 再转换为目标进制
|
||||
if (toBase.value === 10) {
|
||||
return decimalValue.toString()
|
||||
} else if (toBase.value === 16) {
|
||||
return decimalValue.toString(16).toUpperCase()
|
||||
} else {
|
||||
return decimalValue.toString(toBase.value)
|
||||
}
|
||||
} catch (error) {
|
||||
return '转换失败'
|
||||
}
|
||||
})
|
||||
|
||||
// 复制结果
|
||||
const copyResult = () => {
|
||||
if (!convertedValue.value || convertedValue.value === '无效输入' || convertedValue.value === '转换失败') {
|
||||
ElMessage.warning('没有可复制的内容')
|
||||
return
|
||||
}
|
||||
|
||||
copy(convertedValue.value)
|
||||
}
|
||||
|
||||
// 清空输入
|
||||
const clearInput = () => {
|
||||
inputValue.value = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<!-- 输入部分 -->
|
||||
<div class="mb-4">
|
||||
<el-input
|
||||
v-model="inputValue"
|
||||
placeholder="请输入要转换的数值"
|
||||
class="mb-2"
|
||||
/>
|
||||
|
||||
<div class="flex gap-4 items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<el-text class="">从进制:</el-text>
|
||||
<el-select v-model="fromBase" style="width: 140px;">
|
||||
<el-option
|
||||
v-for="option in baseOptions"
|
||||
:key="option.value"
|
||||
:label="option.label"
|
||||
:value="option.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<el-text>到进制:</el-text>
|
||||
<el-select v-model="toBase" style="width: 140px;">
|
||||
<el-option
|
||||
v-for="option in baseOptions"
|
||||
:key="option.value"
|
||||
:label="option.label"
|
||||
:value="option.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="mb-4">
|
||||
<el-button type="primary" @click="clearInput">清空</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 结果部分 -->
|
||||
<div>
|
||||
<el-input
|
||||
:value="convertedValue"
|
||||
placeholder="转换结果"
|
||||
readonly
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="copyResult">复制</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线进制转换计算器,支持二进制、八进制、十进制、十六进制等多种进制之间的相互转换。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div class="tool-content">
|
||||
<div class="calculator">
|
||||
<div class="calculator-display">
|
||||
<div class="display-text">{{ display }}</div>
|
||||
<div class="display-history">{{ history }}</div>
|
||||
</div>
|
||||
|
||||
<div class="calculator-buttons">
|
||||
<div class="button-row">
|
||||
<el-button type="info" @click="clear">C</el-button>
|
||||
<el-button type="info" @click="backspace"><</el-button>
|
||||
<el-button type="info" @click="append('%')">%</el-button>
|
||||
<el-button type="info" @click="append('/')">÷</el-button>
|
||||
</div>
|
||||
|
||||
<div class="button-row">
|
||||
<el-button @click="append('7')">7</el-button>
|
||||
<el-button @click="append('8')">8</el-button>
|
||||
<el-button @click="append('9')">9</el-button>
|
||||
<el-button type="info" @click="append('*')">×</el-button>
|
||||
</div>
|
||||
|
||||
<div class="button-row">
|
||||
<el-button @click="append('4')">4</el-button>
|
||||
<el-button @click="append('5')">5</el-button>
|
||||
<el-button @click="append('6')">6</el-button>
|
||||
<el-button type="info" @click="append('-')">-</el-button>
|
||||
</div>
|
||||
|
||||
<div class="button-row">
|
||||
<el-button @click="append('1')">1</el-button>
|
||||
<el-button @click="append('2')">2</el-button>
|
||||
<el-button @click="append('3')">3</el-button>
|
||||
<el-button type="info" @click="append('+')">+</el-button>
|
||||
</div>
|
||||
|
||||
<div class="button-row">
|
||||
<el-button @click="append('0')">0</el-button>
|
||||
<el-button @click="append('.')">.</el-button>
|
||||
<el-button type="success" @click="calculate">=</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线简易计算器,支持基本的加减乘除和取模运算,具有历史记录显示功能,可用于日常简单计算。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
|
||||
|
||||
const title = "简易计算器"
|
||||
|
||||
const display = ref('0');
|
||||
const history = ref('');
|
||||
const firstOperand = ref<number | null>(null);
|
||||
const operator = ref<string | null>(null);
|
||||
const waitingForSecondOperand = ref(false);
|
||||
|
||||
// 清空
|
||||
const clear = () => {
|
||||
display.value = '0';
|
||||
history.value = '';
|
||||
firstOperand.value = null;
|
||||
operator.value = null;
|
||||
waitingForSecondOperand.value = false;
|
||||
};
|
||||
|
||||
// 退格
|
||||
const backspace = () => {
|
||||
if (waitingForSecondOperand.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (display.value.length > 1) {
|
||||
display.value = display.value.slice(0, -1);
|
||||
} else {
|
||||
display.value = '0';
|
||||
}
|
||||
};
|
||||
|
||||
// 添加数字或操作符
|
||||
const append = (value: string) => {
|
||||
if (['+', '-', '*', '/', '%'].includes(value)) {
|
||||
setOperator(value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (waitingForSecondOperand.value) {
|
||||
display.value = value;
|
||||
waitingForSecondOperand.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (value === '.') {
|
||||
if (!display.value.includes('.')) {
|
||||
display.value += value;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (display.value === '0' && value !== '.') {
|
||||
display.value = value;
|
||||
} else {
|
||||
display.value += value;
|
||||
}
|
||||
};
|
||||
|
||||
// 计算
|
||||
const calculate = () => {
|
||||
if (operator.value === null || firstOperand.value === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const secondOperand = parseFloat(display.value);
|
||||
let result: number;
|
||||
|
||||
switch (operator.value) {
|
||||
case '+':
|
||||
result = firstOperand.value + secondOperand;
|
||||
break;
|
||||
case '-':
|
||||
result = firstOperand.value - secondOperand;
|
||||
break;
|
||||
case '*':
|
||||
result = firstOperand.value * secondOperand;
|
||||
break;
|
||||
case '/':
|
||||
if (secondOperand === 0) {
|
||||
display.value = 'Error';
|
||||
history.value = '';
|
||||
firstOperand.value = null;
|
||||
operator.value = null;
|
||||
waitingForSecondOperand.value = false;
|
||||
return;
|
||||
}
|
||||
result = firstOperand.value / secondOperand;
|
||||
break;
|
||||
case '%':
|
||||
result = firstOperand.value % secondOperand;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理结果显示
|
||||
if (Number.isInteger(result)) {
|
||||
display.value = result.toString();
|
||||
} else {
|
||||
display.value = result.toFixed(2);
|
||||
}
|
||||
|
||||
history.value = `${firstOperand.value} ${operator.value} ${secondOperand} =`;
|
||||
firstOperand.value = result;
|
||||
operator.value = null;
|
||||
waitingForSecondOperand.value = true;
|
||||
};
|
||||
|
||||
// 监听操作符点击
|
||||
const setOperator = (op: string) => {
|
||||
if (operator.value !== null) {
|
||||
calculate();
|
||||
}
|
||||
|
||||
firstOperand.value = parseFloat(display.value);
|
||||
operator.value = op;
|
||||
waitingForSecondOperand.value = true;
|
||||
history.value = `${firstOperand.value} ${op}`;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tool-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.calculator {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.calculator-display {
|
||||
background-color: #f5f7fa;
|
||||
padding: 20px;
|
||||
text-align: right;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.display-text {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.display-history {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.calculator-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.button-row .el-button {
|
||||
flex: 1;
|
||||
height: 60px;
|
||||
font-size: 18px;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
border-right: 1px solid #e4e7ed;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.button-row .el-button:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.button-row:last-child .el-button {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.button-row .el-button:nth-child(1) {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.button-row .el-button.success {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.calculator {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.button-row .el-button {
|
||||
height: 50px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -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,601 @@
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div class="tool-content">
|
||||
<div class="input-section">
|
||||
<el-tabs v-model="activeCategory">
|
||||
<el-tab-pane label="基础颜色" name="basic">
|
||||
<div class="color-grid">
|
||||
<div
|
||||
v-for="color in basicColors"
|
||||
:key="color.hex"
|
||||
class="color-item"
|
||||
:style="{ backgroundColor: color.hex }"
|
||||
@click="copyColor(color)"
|
||||
>
|
||||
<div class="color-info">
|
||||
<span class="color-name">{{ color.name }}</span>
|
||||
<span class="color-hex">{{ color.hex }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="渐变色" name="gradient">
|
||||
<div class="gradient-grid">
|
||||
<div
|
||||
v-for="(gradient, index) in gradients"
|
||||
:key="index"
|
||||
class="gradient-item"
|
||||
:style="{ background: gradient.value }"
|
||||
@click="copyGradient(gradient)"
|
||||
>
|
||||
<div class="gradient-info">
|
||||
<span class="gradient-name">{{ gradient.name }}</span>
|
||||
<span class="gradient-value">{{ gradient.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="Material Design" name="material">
|
||||
<div class="color-grid">
|
||||
<div
|
||||
v-for="color in materialColors"
|
||||
:key="color.hex"
|
||||
class="color-item"
|
||||
:style="{ backgroundColor: color.hex }"
|
||||
@click="copyColor(color)"
|
||||
>
|
||||
<div class="color-info">
|
||||
<span class="color-name">{{ color.name }}</span>
|
||||
<span class="color-hex">{{ color.hex }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="扁平化" name="flat">
|
||||
<div class="color-grid">
|
||||
<div
|
||||
v-for="color in flatColors"
|
||||
:key="color.hex"
|
||||
class="color-item"
|
||||
:style="{ backgroundColor: color.hex }"
|
||||
@click="copyColor(color)"
|
||||
>
|
||||
<div class="color-info">
|
||||
<span class="color-name">{{ color.name }}</span>
|
||||
<span class="color-hex">{{ color.hex }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="霓虹色" name="neon">
|
||||
<div class="color-grid">
|
||||
<div
|
||||
v-for="color in neonColors"
|
||||
:key="color.hex"
|
||||
class="color-item"
|
||||
:style="{ backgroundColor: color.hex }"
|
||||
@click="copyColor(color)"
|
||||
>
|
||||
<div class="color-info">
|
||||
<span class="color-name">{{ color.name }}</span>
|
||||
<span class="color-hex">{{ color.hex }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="自然色" name="nature">
|
||||
<div class="color-grid">
|
||||
<div
|
||||
v-for="color in natureColors"
|
||||
:key="color.hex"
|
||||
class="color-item"
|
||||
:style="{ backgroundColor: color.hex }"
|
||||
@click="copyColor(color)"
|
||||
>
|
||||
<div class="color-info">
|
||||
<span class="color-name">{{ color.name }}</span>
|
||||
<span class="color-hex">{{ color.hex }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="金属色" name="metal">
|
||||
<div class="color-grid">
|
||||
<div
|
||||
v-for="color in metalColors"
|
||||
:key="color.hex"
|
||||
class="color-item"
|
||||
:style="{ backgroundColor: color.hex }"
|
||||
@click="copyColor(color)"
|
||||
>
|
||||
<div class="color-info">
|
||||
<span class="color-name">{{ color.name }}</span>
|
||||
<span class="color-hex">{{ color.hex }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="复古色" name="vintage">
|
||||
<div class="color-grid">
|
||||
<div
|
||||
v-for="color in vintageColors"
|
||||
:key="color.hex"
|
||||
class="color-item"
|
||||
:style="{ backgroundColor: color.hex }"
|
||||
@click="copyColor(color)"
|
||||
>
|
||||
<div class="color-info">
|
||||
<span class="color-name">{{ color.name }}</span>
|
||||
<span class="color-hex">{{ color.hex }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="季节色" name="seasonal">
|
||||
<div class="color-grid">
|
||||
<div
|
||||
v-for="color in seasonalColors"
|
||||
:key="color.hex"
|
||||
class="color-item"
|
||||
:style="{ backgroundColor: color.hex }"
|
||||
@click="copyColor(color)"
|
||||
>
|
||||
<div class="color-info">
|
||||
<span class="color-name">{{ color.name }}</span>
|
||||
<span class="color-hex">{{ color.hex }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="莫兰迪色" name="morandi">
|
||||
<div class="color-grid">
|
||||
<div
|
||||
v-for="color in morandiColors"
|
||||
:key="color.hex"
|
||||
class="color-item"
|
||||
:style="{ backgroundColor: color.hex }"
|
||||
@click="copyColor(color)"
|
||||
>
|
||||
<div class="color-info">
|
||||
<span class="color-name">{{ color.name }}</span>
|
||||
<span class="color-hex">{{ color.hex }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="马卡龙色" name="macaron">
|
||||
<div class="color-grid">
|
||||
<div
|
||||
v-for="color in macaronColors"
|
||||
:key="color.hex"
|
||||
class="color-item"
|
||||
:style="{ backgroundColor: color.hex }"
|
||||
@click="copyColor(color)"
|
||||
>
|
||||
<div class="color-info">
|
||||
<span class="color-name">{{ color.name }}</span>
|
||||
<span class="color-hex">{{ color.hex }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="工业风" name="industrial">
|
||||
<div class="color-grid">
|
||||
<div
|
||||
v-for="color in industrialColors"
|
||||
:key="color.hex"
|
||||
class="color-item"
|
||||
:style="{ backgroundColor: color.hex }"
|
||||
@click="copyColor(color)"
|
||||
>
|
||||
<div class="color-info">
|
||||
<span class="color-name">{{ color.name }}</span>
|
||||
<span class="color-hex">{{ color.hex }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="中国传统色" name="chinese">
|
||||
<div class="color-grid">
|
||||
<div
|
||||
v-for="color in chineseColors"
|
||||
:key="color.hex"
|
||||
class="color-item"
|
||||
:style="{ backgroundColor: color.hex }"
|
||||
@click="copyColor(color)"
|
||||
>
|
||||
<div class="color-info">
|
||||
<span class="color-name">{{ color.name }}</span>
|
||||
<span class="color-hex">{{ color.hex }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线色板工具,提供基础颜色、渐变色、Material Design 颜色、扁平化颜色、霓虹色、自然色、金属色、复古色、季节色、莫兰迪色、马卡龙色、工业风和中国传统色,点击颜色可复制颜色值,方便设计和开发使用。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
|
||||
|
||||
const title = "色板工具"
|
||||
|
||||
const activeCategory = ref('basic');
|
||||
|
||||
// 基础颜色
|
||||
const basicColors = [
|
||||
{ name: '红色', hex: '#FF0000' },
|
||||
{ name: '绿色', hex: '#00FF00' },
|
||||
{ name: '蓝色', hex: '#0000FF' },
|
||||
{ name: '黄色', hex: '#FFFF00' },
|
||||
{ name: '紫色', hex: '#800080' },
|
||||
{ name: '橙色', hex: '#FFA500' },
|
||||
{ name: '黑色', hex: '#000000' },
|
||||
{ name: '白色', hex: '#FFFFFF' },
|
||||
{ name: '灰色', hex: '#808080' },
|
||||
{ name: '棕色', hex: '#A52A2A' },
|
||||
{ name: '粉色', hex: '#FFC0CB' },
|
||||
{ name: '青色', hex: '#00FFFF' },
|
||||
{ name: '品红', hex: '#FF00FF' },
|
||||
{ name: '橄榄绿', hex: '#808000' },
|
||||
{ name: '海军蓝', hex: '#000080' },
|
||||
{ name: '银色', hex: '#C0C0C0' },
|
||||
];
|
||||
|
||||
// 渐变色
|
||||
const gradients = [
|
||||
{ name: '蓝到紫', value: 'linear-gradient(90deg, #3B82F6, #8B5CF6)' },
|
||||
{ name: '红到黄', value: 'linear-gradient(90deg, #EF4444, #F59E0B)' },
|
||||
{ name: '绿到蓝', value: 'linear-gradient(90deg, #10B981, #3B82F6)' },
|
||||
{ name: '紫到粉', value: 'linear-gradient(90deg, #8B5CF6, #EC4899)' },
|
||||
{ name: '黄到绿', value: 'linear-gradient(90deg, #F59E0B, #10B981)' },
|
||||
{ name: '红到橙', value: 'linear-gradient(90deg, #EF4444, #F97316)' },
|
||||
{ name: '蓝到青', value: 'linear-gradient(90deg, #3B82F6, #06B6D4)' },
|
||||
{ name: '紫到红', value: 'linear-gradient(90deg, #8B5CF6, #EF4444)' },
|
||||
];
|
||||
|
||||
// Material Design颜色
|
||||
const materialColors = [
|
||||
{ name: 'Material Red', hex: '#F44336' },
|
||||
{ name: 'Material Pink', hex: '#E91E63' },
|
||||
{ name: 'Material Purple', hex: '#9C27B0' },
|
||||
{ name: 'Material Deep Purple', hex: '#673AB7' },
|
||||
{ name: 'Material Indigo', hex: '#3F51B5' },
|
||||
{ name: 'Material Blue', hex: '#2196F3' },
|
||||
{ name: 'Material Light Blue', hex: '#03A9F4' },
|
||||
{ name: 'Material Cyan', hex: '#00BCD4' },
|
||||
{ name: 'Material Teal', hex: '#009688' },
|
||||
{ name: 'Material Green', hex: '#4CAF50' },
|
||||
{ name: 'Material Light Green', hex: '#8BC34A' },
|
||||
{ name: 'Material Lime', hex: '#CDDC39' },
|
||||
{ name: 'Material Yellow', hex: '#FFEB3B' },
|
||||
{ name: 'Material Amber', hex: '#FFC107' },
|
||||
{ name: 'Material Orange', hex: '#FF9800' },
|
||||
{ name: 'Material Deep Orange', hex: '#FF5722' },
|
||||
];
|
||||
|
||||
// 扁平化颜色
|
||||
const flatColors = [
|
||||
{ name: 'Flat Turquoise', hex: '#1ABC9C' },
|
||||
{ name: 'Flat Green Sea', hex: '#16A085' },
|
||||
{ name: 'Flat Peter River', hex: '#3498DB' },
|
||||
{ name: 'Flat Belize Hole', hex: '#2980B9' },
|
||||
{ name: 'Flat Amethyst', hex: '#9B59B6' },
|
||||
{ name: 'Flat Wisteria', hex: '#8E44AD' },
|
||||
{ name: 'Flat Wet Asphalt', hex: '#34495E' },
|
||||
{ name: 'Flat Midnight Blue', hex: '#2C3E50' },
|
||||
{ name: 'Flat Sun Flower', hex: '#F1C40F' },
|
||||
{ name: 'Flat Orange', hex: '#F39C12' },
|
||||
{ name: 'Flat Carrot', hex: '#E67E22' },
|
||||
{ name: 'Flat Alizarin', hex: '#E74C3C' },
|
||||
{ name: 'Flat Clouds', hex: '#ECF0F1' },
|
||||
{ name: 'Flat Silver', hex: '#BDC3C7' },
|
||||
{ name: 'Flat Concrete', hex: '#95A5A6' },
|
||||
{ name: 'Flat Asbestos', hex: '#7F8C8D' },
|
||||
];
|
||||
|
||||
// 霓虹色
|
||||
const neonColors = [
|
||||
{ name: 'Neon Pink', hex: '#FF1493' },
|
||||
{ name: 'Neon Blue', hex: '#00FFFF' },
|
||||
{ name: 'Neon Green', hex: '#39FF14' },
|
||||
{ name: 'Neon Yellow', hex: '#FFFF00' },
|
||||
{ name: 'Neon Orange', hex: '#FF6600' },
|
||||
{ name: 'Neon Purple', hex: '#9932CC' },
|
||||
{ name: 'Neon Red', hex: '#FF073A' },
|
||||
{ name: 'Neon Cyan', hex: '#00FFFF' },
|
||||
{ name: 'Neon Magenta', hex: '#FF00FF' },
|
||||
{ name: 'Neon Lime', hex: '#32CD32' },
|
||||
{ name: 'Neon Turquoise', hex: '#40E0D0' },
|
||||
{ name: 'Neon Lavender', hex: '#E6E6FA' },
|
||||
];
|
||||
|
||||
// 自然色
|
||||
const natureColors = [
|
||||
{ name: 'Forest Green', hex: '#228B22' },
|
||||
{ name: 'Earth Brown', hex: '#8B4513' },
|
||||
{ name: 'Sky Blue', hex: '#87CEEB' },
|
||||
{ name: 'Sunset Orange', hex: '#FF8C00' },
|
||||
{ name: 'Ocean Blue', hex: '#1E90FF' },
|
||||
{ name: 'Moss Green', hex: '#8A9A5B' },
|
||||
{ name: 'Sandy Beige', hex: '#F5DEB3' },
|
||||
{ name: 'Leaf Green', hex: '#008000' },
|
||||
{ name: 'Clay Brown', hex: '#B22222' },
|
||||
{ name: 'Dolphin Gray', hex: '#878787' },
|
||||
{ name: 'Coral Pink', hex: '#FF7F50' },
|
||||
{ name: 'Pine Green', hex: '#01796F' },
|
||||
];
|
||||
|
||||
// 金属色
|
||||
const metalColors = [
|
||||
{ name: 'Gold', hex: '#FFD700' },
|
||||
{ name: 'Silver', hex: '#C0C0C0' },
|
||||
{ name: 'Bronze', hex: '#CD7F32' },
|
||||
{ name: 'Copper', hex: '#B87333' },
|
||||
{ name: 'Platinum', hex: '#E5E4E2' },
|
||||
{ name: 'Brass', hex: '#B5A642' },
|
||||
{ name: 'Steel Blue', hex: '#4682B4' },
|
||||
{ name: 'Aluminum', hex: '#A9A9A9' },
|
||||
{ name: 'Chrome', hex: '#E0E0E0' },
|
||||
{ name: 'Titanium', hex: '#818589' },
|
||||
{ name: 'Zinc', hex: '#E8E8E8' },
|
||||
{ name: 'Lead', hex: '#696969' },
|
||||
];
|
||||
|
||||
// 复古色
|
||||
const vintageColors = [
|
||||
{ name: 'Sepia', hex: '#704214' },
|
||||
{ name: 'Mustard', hex: '#FFDB58' },
|
||||
{ name: 'Burgundy', hex: '#800020' },
|
||||
{ name: 'Teal', hex: '#008080' },
|
||||
{ name: 'Cream', hex: '#FFFDD0' },
|
||||
{ name: 'Olive', hex: '#808000' },
|
||||
{ name: 'Mauve', hex: '#E0B0FF' },
|
||||
{ name: 'Peach', hex: '#FFDAB9' },
|
||||
{ name: 'Sage', hex: '#9CAF88' },
|
||||
{ name: 'Navy', hex: '#000080' },
|
||||
{ name: 'Coral', hex: '#FF7F50' },
|
||||
{ name: 'Lavender', hex: '#E6E6FA' },
|
||||
];
|
||||
|
||||
// 季节色
|
||||
const seasonalColors = [
|
||||
{ name: 'Spring Green', hex: '#00FF7F' },
|
||||
{ name: 'Summer Yellow', hex: '#FFFF00' },
|
||||
{ name: 'Autumn Orange', hex: '#FF8C00' },
|
||||
{ name: 'Winter Blue', hex: '#1E90FF' },
|
||||
{ name: 'Cherry Blossom', hex: '#FFB7C5' },
|
||||
{ name: 'Beach Sand', hex: '#F4A460' },
|
||||
{ name: 'Fall Leaf', hex: '#8B4513' },
|
||||
{ name: 'Snow White', hex: '#FFFFFF' },
|
||||
{ name: 'Tulip Red', hex: '#FF0000' },
|
||||
{ name: 'Sunflower Yellow', hex: '#FFD700' },
|
||||
{ name: 'Pumpkin Orange', hex: '#FF7F50' },
|
||||
{ name: 'Icicle Blue', hex: '#B0E0E6' },
|
||||
];
|
||||
|
||||
// 莫兰迪色
|
||||
const morandiColors = [
|
||||
{ name: '莫兰迪灰', hex: '#9B9B9B' },
|
||||
{ name: '莫兰迪粉', hex: '#E8D8D1' },
|
||||
{ name: '莫兰迪蓝', hex: '#A8B8C8' },
|
||||
{ name: '莫兰迪绿', hex: '#A8B8A8' },
|
||||
{ name: '莫兰迪黄', hex: '#E8D8A8' },
|
||||
{ name: '莫兰迪紫', hex: '#B8A8C8' },
|
||||
{ name: '莫兰迪棕', hex: '#B8A89B' },
|
||||
{ name: '莫兰迪橙', hex: '#E8C8A8' },
|
||||
{ name: '莫兰迪青', hex: '#A8C8C8' },
|
||||
{ name: '莫兰迪红', hex: '#C8A8A8' },
|
||||
{ name: '莫兰迪灰蓝', hex: '#9B9BA8' },
|
||||
{ name: '莫兰迪灰绿', hex: '#9BA89B' },
|
||||
];
|
||||
|
||||
// 马卡龙色
|
||||
const macaronColors = [
|
||||
{ name: '马卡龙粉', hex: '#FFB6C1' },
|
||||
{ name: '马卡龙蓝', hex: '#87CEFA' },
|
||||
{ name: '马卡龙绿', hex: '#98FB98' },
|
||||
{ name: '马卡龙黄', hex: '#FFFACD' },
|
||||
{ name: '马卡龙紫', hex: '#E6E6FA' },
|
||||
{ name: '马卡龙橙', hex: '#FFA07A' },
|
||||
{ name: '马卡龙青', hex: '#E0FFFF' },
|
||||
{ name: '马卡龙红', hex: '#FFC0CB' },
|
||||
{ name: '马卡龙灰', hex: '#F0F0F0' },
|
||||
{ name: '马卡龙棕', hex: '#F5DEB3' },
|
||||
{ name: '马卡龙天蓝', hex: '#87CEEB' },
|
||||
{ name: '马卡龙薄荷', hex: '#98FB98' },
|
||||
];
|
||||
|
||||
// 工业风
|
||||
const industrialColors = [
|
||||
{ name: '工业黑', hex: '#2C2C2C' },
|
||||
{ name: '工业灰', hex: '#666666' },
|
||||
{ name: '工业银', hex: '#999999' },
|
||||
{ name: '工业红', hex: '#B22222' },
|
||||
{ name: '工业蓝', hex: '#4682B4' },
|
||||
{ name: '工业黄', hex: '#DAA520' },
|
||||
{ name: '工业绿', hex: '#228B22' },
|
||||
{ name: '工业橙', hex: '#FF8C00' },
|
||||
{ name: '工业棕', hex: '#8B4513' },
|
||||
{ name: '工业青', hex: '#008080' },
|
||||
{ name: '工业紫', hex: '#8A2BE2' },
|
||||
{ name: '工业白', hex: '#F5F5F5' },
|
||||
];
|
||||
|
||||
// 中国传统色
|
||||
const chineseColors = [
|
||||
{ name: '胭脂红', hex: '#E44B4E' },
|
||||
{ name: '黛青', hex: '#4A6C8C' },
|
||||
{ name: '琉璃黄', hex: '#FFD700' },
|
||||
{ name: '玉脂白', hex: '#F7F7F7' },
|
||||
{ name: '水墨黑', hex: '#1A1A1A' },
|
||||
{ name: '竹青', hex: '#9DB47E' },
|
||||
{ name: '朱砂红', hex: '#C93756' },
|
||||
{ name: '石青', hex: '#557799' },
|
||||
{ name: '金黄', hex: '#E6B422' },
|
||||
{ name: '藕荷粉', hex: '#E8B4B8' },
|
||||
{ name: '松绿', hex: '#3A5F0B' },
|
||||
{ name: '琥珀色', hex: '#E6C200' },
|
||||
];
|
||||
|
||||
// 复制颜色
|
||||
const copyColor = (color: { name: string; hex: string }) => {
|
||||
navigator.clipboard.writeText(color.hex).then(() => {
|
||||
ElMessage.success(`已复制 ${color.name} 的颜色值: ${color.hex}`);
|
||||
}).catch(() => {
|
||||
ElMessage.error('复制失败');
|
||||
});
|
||||
};
|
||||
|
||||
// 复制渐变色
|
||||
const copyGradient = (gradient: { name: string; value: string }) => {
|
||||
navigator.clipboard.writeText(gradient.value).then(() => {
|
||||
ElMessage.success(`已复制 ${gradient.name} 的渐变值: ${gradient.value}`);
|
||||
}).catch(() => {
|
||||
ElMessage.error('复制失败');
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tool-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.color-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||
gap: 15px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.color-item {
|
||||
height: 120px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.color-item:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.color-info {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.color-name {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.color-hex {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.gradient-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 15px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.gradient-item {
|
||||
height: 120px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gradient-item:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.gradient-info {
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.gradient-name {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.gradient-value {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.color-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
}
|
||||
|
||||
.gradient-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -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>
|
||||
在不同格式(十六进制、rgb、hsl和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 到 9,A - 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 代表 Cyan、Magenta、Yellow 和 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>
|
||||
|
||||
@@ -0,0 +1,737 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, 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 title = "Emoji表情大全"
|
||||
|
||||
// 状态管理
|
||||
const searchKeyword = ref('')
|
||||
const selectedCategory = ref('all')
|
||||
|
||||
// Emoji分类
|
||||
const emojiCategories = [
|
||||
{ id: 'all', name: '全部' },
|
||||
{ id: 'smileys', name: '表情符号与情感' },
|
||||
{ id: 'people', name: '人物与身体' },
|
||||
{ id: 'animals', name: '动物与自然' },
|
||||
{ id: 'food', name: '食物与饮料' },
|
||||
{ id: 'travel', name: '旅行与地点' },
|
||||
{ id: 'activities', name: '活动' },
|
||||
{ id: 'objects', name: '物体' },
|
||||
{ id: 'symbols', name: '符号' },
|
||||
{ id: 'flags', name: '旗帜' }
|
||||
]
|
||||
|
||||
// Emoji数据
|
||||
const emojis = [
|
||||
// 表情符号与情感
|
||||
{ emoji: '😀', name: '笑脸', category: 'smileys' },
|
||||
{ emoji: '😂', name: '笑哭', category: 'smileys' },
|
||||
{ emoji: '😍', name: '爱心眼', category: 'smileys' },
|
||||
{ emoji: '😎', name: '墨镜', category: 'smileys' },
|
||||
{ emoji: '🤔', name: '思考', category: 'smileys' },
|
||||
{ emoji: '😢', name: '哭泣', category: 'smileys' },
|
||||
{ emoji: '😡', name: '愤怒', category: 'smileys' },
|
||||
{ emoji: '😱', name: '惊讶', category: 'smileys' },
|
||||
{ emoji: '😴', name: '睡觉', category: 'smileys' },
|
||||
{ emoji: '😵', name: '晕', category: 'smileys' },
|
||||
{ emoji: '🤢', name: '恶心', category: 'smileys' },
|
||||
{ emoji: '🤠', name: '牛仔', category: 'smileys' },
|
||||
{ emoji: '😈', name: '恶魔', category: 'smileys' },
|
||||
{ emoji: '👼', name: '天使', category: 'smileys' },
|
||||
{ emoji: '👻', name: '幽灵', category: 'smileys' },
|
||||
{ emoji: '💩', name: '便便', category: 'smileys' },
|
||||
{ emoji: '😊', name: '微笑', category: 'smileys' },
|
||||
{ emoji: '😃', name: '大笑', category: 'smileys' },
|
||||
{ emoji: '😄', name: '露齿笑', category: 'smileys' },
|
||||
{ emoji: '😁', name: '开心', category: 'smileys' },
|
||||
{ emoji: '😆', name: '眯眼笑', category: 'smileys' },
|
||||
{ emoji: '😅', name: '汗笑', category: 'smileys' },
|
||||
{ emoji: '🤣', name: '大笑', category: 'smileys' },
|
||||
{ emoji: '😇', name: '天使笑', category: 'smileys' },
|
||||
{ emoji: '🥰', name: '可爱', category: 'smileys' },
|
||||
{ emoji: '😘', name: '飞吻', category: 'smileys' },
|
||||
{ emoji: '😗', name: '亲吻', category: 'smileys' },
|
||||
{ emoji: '😙', name: '亲亲', category: 'smileys' },
|
||||
{ emoji: '😚', name: '闭眼亲吻', category: 'smileys' },
|
||||
{ emoji: '😋', name: '美味', category: 'smileys' },
|
||||
{ emoji: '😛', name: '调皮', category: 'smileys' },
|
||||
{ emoji: '😝', name: '吐舌', category: 'smileys' },
|
||||
{ emoji: '😜', name: '眨眼吐舌', category: 'smileys' },
|
||||
{ emoji: '🤪', name: '疯狂', category: 'smileys' },
|
||||
{ emoji: '😏', name: '得意', category: 'smileys' },
|
||||
{ emoji: '😒', name: '无奈', category: 'smileys' },
|
||||
{ emoji: '🙄', name: '白眼', category: 'smileys' },
|
||||
{ emoji: '😬', name: '尴尬', category: 'smileys' },
|
||||
{ emoji: '😌', name: '放松', category: 'smileys' },
|
||||
{ emoji: '😔', name: '难过', category: 'smileys' },
|
||||
{ emoji: '😪', name: '疲惫', category: 'smileys' },
|
||||
{ emoji: '🤤', name: '流口水', category: 'smileys' },
|
||||
{ emoji: '😷', name: '口罩', category: 'smileys' },
|
||||
{ emoji: '🤒', name: '发烧', category: 'smileys' },
|
||||
{ emoji: '🤕', name: '受伤', category: 'smileys' },
|
||||
{ emoji: '🤢', name: '呕吐', category: 'smileys' },
|
||||
{ emoji: '🤮', name: '恶心', category: 'smileys' },
|
||||
{ emoji: '🤧', name: '打喷嚏', category: 'smileys' },
|
||||
{ emoji: '🥵', name: '热', category: 'smileys' },
|
||||
{ emoji: '🥶', name: '冷', category: 'smileys' },
|
||||
{ emoji: '🥴', name: '醉', category: 'smileys' },
|
||||
{ emoji: '😵💫', name: '头晕', category: 'smileys' },
|
||||
{ emoji: '🤯', name: '震惊', category: 'smileys' },
|
||||
{ emoji: '🤠', name: '牛仔', category: 'smileys' },
|
||||
{ emoji: '🥳', name: '庆祝', category: 'smileys' },
|
||||
{ emoji: '😎', name: '酷', category: 'smileys' },
|
||||
{ emoji: '🤓', name: '书呆子', category: 'smileys' },
|
||||
{ emoji: '😕', name: '困惑', category: 'smileys' },
|
||||
{ emoji: '😟', name: '担忧', category: 'smileys' },
|
||||
{ emoji: '🙁', name: '不高兴', category: 'smileys' },
|
||||
{ emoji: '😮', name: '惊讶', category: 'smileys' },
|
||||
{ emoji: '😯', name: '震惊', category: 'smileys' },
|
||||
{ emoji: '😲', name: '惊讶', category: 'smileys' },
|
||||
{ emoji: '😳', name: '害羞', category: 'smileys' },
|
||||
{ emoji: '🥺', name: '恳求', category: 'smileys' },
|
||||
{ emoji: '😦', name: '震惊', category: 'smileys' },
|
||||
{ emoji: '😧', name: '担心', category: 'smileys' },
|
||||
{ emoji: '😨', name: '害怕', category: 'smileys' },
|
||||
{ emoji: '😰', name: '恐慌', category: 'smileys' },
|
||||
{ emoji: '😥', name: '难过', category: 'smileys' },
|
||||
{ emoji: '😢', name: '哭泣', category: 'smileys' },
|
||||
{ emoji: '😭', name: '大哭', category: 'smileys' },
|
||||
{ emoji: '😱', name: '恐惧', category: 'smileys' },
|
||||
{ emoji: '😖', name: '痛苦', category: 'smileys' },
|
||||
{ emoji: '😣', name: '压力', category: 'smileys' },
|
||||
{ emoji: '😞', name: '失望', category: 'smileys' },
|
||||
{ emoji: '😓', name: '汗', category: 'smileys' },
|
||||
{ emoji: '😩', name: '疲惫', category: 'smileys' },
|
||||
{ emoji: '😫', name: '无奈', category: 'smileys' },
|
||||
{ emoji: '🥱', name: '打哈欠', category: 'smileys' },
|
||||
{ emoji: '😤', name: '愤怒', category: 'smileys' },
|
||||
{ emoji: '😡', name: '生气', category: 'smileys' },
|
||||
{ emoji: '😠', name: '愤怒', category: 'smileys' },
|
||||
{ emoji: '🤬', name: '暴怒', category: 'smileys' },
|
||||
{ emoji: '😈', name: '恶魔', category: 'smileys' },
|
||||
{ emoji: '👿', name: '魔鬼', category: 'smileys' },
|
||||
{ emoji: '💀', name: '骷髅', category: 'smileys' },
|
||||
{ emoji: '💩', name: '便便', category: 'smileys' },
|
||||
{ emoji: '🤡', name: '小丑', category: 'smileys' },
|
||||
{ emoji: '👹', name: '妖怪', category: 'smileys' },
|
||||
{ emoji: '👺', name: '小鬼', category: 'smileys' },
|
||||
{ emoji: '👻', name: '幽灵', category: 'smileys' },
|
||||
{ emoji: '💋', name: '吻', category: 'smileys' },
|
||||
{ emoji: '💌', name: '情书', category: 'smileys' },
|
||||
{ emoji: '💘', name: '爱心箭', category: 'smileys' },
|
||||
{ emoji: '💝', name: '礼物', category: 'smileys' },
|
||||
{ emoji: '💖', name: '爱心', category: 'smileys' },
|
||||
{ emoji: '💗', name: '爱心', category: 'smileys' },
|
||||
{ emoji: '💓', name: '心跳', category: 'smileys' },
|
||||
{ emoji: '💞', name: '双心', category: 'smileys' },
|
||||
{ emoji: '💕', name: '双心', category: 'smileys' },
|
||||
{ emoji: '💟', name: '心形', category: 'smileys' },
|
||||
{ emoji: '💔', name: '破碎的心', category: 'smileys' },
|
||||
{ emoji: '❤️', name: '红心', category: 'smileys' },
|
||||
{ emoji: '🧡', name: '橙心', category: 'smileys' },
|
||||
{ emoji: '💛', name: '黄心', category: 'smileys' },
|
||||
{ emoji: '💚', name: '绿心', category: 'smileys' },
|
||||
{ emoji: '💙', name: '蓝心', category: 'smileys' },
|
||||
{ emoji: '💜', name: '紫心', category: 'smileys' },
|
||||
{ emoji: '🖤', name: '黑心', category: 'smileys' },
|
||||
|
||||
// 人物与身体
|
||||
{ emoji: '👍', name: '点赞', category: 'people' },
|
||||
{ emoji: '👎', name: '踩', category: 'people' },
|
||||
{ emoji: '👋', name: '挥手', category: 'people' },
|
||||
{ emoji: '🤝', name: '握手', category: 'people' },
|
||||
{ emoji: '🙏', name: '祈祷', category: 'people' },
|
||||
{ emoji: '👌', name: 'OK', category: 'people' },
|
||||
{ emoji: '✌️', name: '胜利', category: 'people' },
|
||||
{ emoji: '🤘', name: '摇滚', category: 'people' },
|
||||
{ emoji: '🤙', name: '打电话', category: 'people' },
|
||||
{ emoji: '👊', name: '拳头', category: 'people' },
|
||||
{ emoji: '✊', name: '举起拳头', category: 'people' },
|
||||
{ emoji: '👆', name: '向上指', category: 'people' },
|
||||
{ emoji: '👇', name: '向下指', category: 'people' },
|
||||
{ emoji: '👈', name: '向左指', category: 'people' },
|
||||
{ emoji: '👉', name: '向右指', category: 'people' },
|
||||
|
||||
// 动物与自然
|
||||
{ emoji: '🐶', name: '狗', category: 'animals' },
|
||||
{ emoji: '🐱', name: '猫', category: 'animals' },
|
||||
{ emoji: '🐭', name: '老鼠', category: 'animals' },
|
||||
{ emoji: '🐹', name: '仓鼠', category: 'animals' },
|
||||
{ emoji: '🐰', name: '兔子', category: 'animals' },
|
||||
{ emoji: '🦊', name: '狐狸', category: 'animals' },
|
||||
{ emoji: '🐻', name: '熊', category: 'animals' },
|
||||
{ emoji: '🐼', name: '熊猫', category: 'animals' },
|
||||
{ emoji: '🐨', name: '考拉', category: 'animals' },
|
||||
{ emoji: '🐯', name: '老虎', category: 'animals' },
|
||||
{ emoji: '🦁', name: '狮子', category: 'animals' },
|
||||
{ emoji: '🐮', name: '牛', category: 'animals' },
|
||||
{ emoji: '🐷', name: '猪', category: 'animals' },
|
||||
{ emoji: '🐸', name: '青蛙', category: 'animals' },
|
||||
{ emoji: '🐵', name: '猴子', category: 'animals' },
|
||||
{ emoji: '🐔', name: '鸡', category: 'animals' },
|
||||
{ emoji: '🐧', name: '企鹅', category: 'animals' },
|
||||
{ emoji: '🐦', name: '鸟', category: 'animals' },
|
||||
{ emoji: '🐤', name: '小鸡', category: 'animals' },
|
||||
{ emoji: '🐣', name: '破壳小鸡', category: 'animals' },
|
||||
{ emoji: '🐥', name: '黄小鸡', category: 'animals' },
|
||||
{ emoji: '🦆', name: '鸭子', category: 'animals' },
|
||||
{ emoji: '🦅', name: '鹰', category: 'animals' },
|
||||
{ emoji: '🦉', name: '猫头鹰', category: 'animals' },
|
||||
{ emoji: '🦇', name: '蝙蝠', category: 'animals' },
|
||||
{ emoji: '🐺', name: '狼', category: 'animals' },
|
||||
{ emoji: '🐗', name: '野猪', category: 'animals' },
|
||||
{ emoji: '🐴', name: '马', category: 'animals' },
|
||||
{ emoji: '🦄', name: '独角兽', category: 'animals' },
|
||||
{ emoji: '🐝', name: '蜜蜂', category: 'animals' },
|
||||
{ emoji: '🐛', name: '毛毛虫', category: 'animals' },
|
||||
{ emoji: '🦋', name: '蝴蝶', category: 'animals' },
|
||||
{ emoji: '🐌', name: '蜗牛', category: 'animals' },
|
||||
{ emoji: '🐞', name: '瓢虫', category: 'animals' },
|
||||
{ emoji: '🐜', name: '蚂蚁', category: 'animals' },
|
||||
{ emoji: '🐢', name: '乌龟', category: 'animals' },
|
||||
{ emoji: '🐍', name: '蛇', category: 'animals' },
|
||||
{ emoji: '🦎', name: '蜥蜴', category: 'animals' },
|
||||
{ emoji: '🦖', name: '霸王龙', category: 'animals' },
|
||||
{ emoji: '🦕', name: '雷龙', category: 'animals' },
|
||||
{ emoji: '🐙', name: '章鱼', category: 'animals' },
|
||||
{ emoji: '🐚', name: '贝壳', category: 'animals' },
|
||||
{ emoji: '🐌', name: '蜗牛', category: 'animals' },
|
||||
{ emoji: '🌱', name: '发芽', category: 'animals' },
|
||||
{ emoji: '🌿', name: '草', category: 'animals' },
|
||||
{ emoji: '🍃', name: '叶子', category: 'animals' },
|
||||
{ emoji: '🍂', name: '落叶', category: 'animals' },
|
||||
{ emoji: '🍁', name: '枫叶', category: 'animals' },
|
||||
{ emoji: '🌾', name: '稻穗', category: 'animals' },
|
||||
{ emoji: '🌴', name: '棕榈树', category: 'animals' },
|
||||
{ emoji: '🌵', name: '仙人掌', category: 'animals' },
|
||||
{ emoji: '🌷', name: '郁金香', category: 'animals' },
|
||||
{ emoji: '🌺', name: '花朵', category: 'animals' },
|
||||
{ emoji: '🌸', name: '樱花', category: 'animals' },
|
||||
{ emoji: '🌹', name: '玫瑰', category: 'animals' },
|
||||
{ emoji: '🌻', name: '向日葵', category: 'animals' },
|
||||
{ emoji: '🌼', name: '雏菊', category: 'animals' },
|
||||
|
||||
// 食物与饮料
|
||||
{ emoji: '🍎', name: '苹果', category: 'food' },
|
||||
{ emoji: '🍏', name: '青苹果', category: 'food' },
|
||||
{ emoji: '🍐', name: '梨', category: 'food' },
|
||||
{ emoji: '🍊', name: '橘子', category: 'food' },
|
||||
{ emoji: '🍋', name: '柠檬', category: 'food' },
|
||||
{ emoji: '🍌', name: '香蕉', category: 'food' },
|
||||
{ emoji: '🍉', name: '西瓜', category: 'food' },
|
||||
{ emoji: '🍇', name: '葡萄', category: 'food' },
|
||||
{ emoji: '🍓', name: '草莓', category: 'food' },
|
||||
{ emoji: '🍈', name: '瓜', category: 'food' },
|
||||
{ emoji: '🍒', name: '樱桃', category: 'food' },
|
||||
{ emoji: '🍑', name: '桃子', category: 'food' },
|
||||
{ emoji: '🍍', name: '菠萝', category: 'food' },
|
||||
{ emoji: '🥥', name: '椰子', category: 'food' },
|
||||
{ emoji: '🥝', name: '猕猴桃', category: 'food' },
|
||||
{ emoji: '🍅', name: '番茄', category: 'food' },
|
||||
{ emoji: '🥑', name: '牛油果', category: 'food' },
|
||||
{ emoji: '🥦', name: '西兰花', category: 'food' },
|
||||
{ emoji: '🥬', name: '生菜', category: 'food' },
|
||||
{ emoji: '🥒', name: '黄瓜', category: 'food' },
|
||||
{ emoji: '🥜', name: '花生', category: 'food' },
|
||||
{ emoji: '🌰', name: '栗子', category: 'food' },
|
||||
{ emoji: '🍞', name: '面包', category: 'food' },
|
||||
{ emoji: '🥐', name: '羊角面包', category: 'food' },
|
||||
{ emoji: '🥖', name: '法棍', category: 'food' },
|
||||
{ emoji: '🥨', name: '椒盐卷饼', category: 'food' },
|
||||
{ emoji: '🥯', name: '百吉饼', category: 'food' },
|
||||
{ emoji: '🧀', name: '奶酪', category: 'food' },
|
||||
{ emoji: '🍖', name: '肉', category: 'food' },
|
||||
{ emoji: '🍗', name: '鸡肉', category: 'food' },
|
||||
{ emoji: '🍔', name: '汉堡', category: 'food' },
|
||||
{ emoji: '🍟', name: '薯条', category: 'food' },
|
||||
{ emoji: '🍕', name: '披萨', category: 'food' },
|
||||
{ emoji: '🌭', name: '热狗', category: 'food' },
|
||||
{ emoji: '🥪', name: '三明治', category: 'food' },
|
||||
{ emoji: '🌮', name: '墨西哥卷', category: 'food' },
|
||||
{ emoji: '🌯', name: '墨西哥饼', category: 'food' },
|
||||
{ emoji: '🥙', name: '皮塔饼', category: 'food' },
|
||||
{ emoji: '🍱', name: '便当', category: 'food' },
|
||||
{ emoji: '🍘', name: '米饼', category: 'food' },
|
||||
{ emoji: '🍙', name: '饭团', category: 'food' },
|
||||
{ emoji: '🍚', name: '米饭', category: 'food' },
|
||||
{ emoji: '🍛', name: '咖喱饭', category: 'food' },
|
||||
{ emoji: '🍜', name: '拉面', category: 'food' },
|
||||
{ emoji: '🍝', name: '意大利面', category: 'food' },
|
||||
{ emoji: '🍠', name: '红薯', category: 'food' },
|
||||
{ emoji: '🍢', name: '烤串', category: 'food' },
|
||||
{ emoji: '🍣', name: '寿司', category: 'food' },
|
||||
{ emoji: '🍤', name: '虾', category: 'food' },
|
||||
{ emoji: '🍥', name: '鱼板', category: 'food' },
|
||||
{ emoji: '🥮', name: '月饼', category: 'food' },
|
||||
{ emoji: '🍡', name: '丸子', category: 'food' },
|
||||
{ emoji: '🍧', name: '刨冰', category: 'food' },
|
||||
{ emoji: '🍨', name: '冰淇淋', category: 'food' },
|
||||
{ emoji: '🍩', name: '甜甜圈', category: 'food' },
|
||||
{ emoji: '🍪', name: '饼干', category: 'food' },
|
||||
{ emoji: '🎂', name: '蛋糕', category: 'food' },
|
||||
{ emoji: '🍰', name: '甜点', category: 'food' },
|
||||
{ emoji: '🧁', name: '纸杯蛋糕', category: 'food' },
|
||||
{ emoji: '🍫', name: '巧克力', category: 'food' },
|
||||
{ emoji: '🍬', name: '糖果', category: 'food' },
|
||||
{ emoji: '🍭', name: '棒棒糖', category: 'food' },
|
||||
{ emoji: '🍮', name: '布丁', category: 'food' },
|
||||
{ emoji: '🍯', name: '蜂蜜', category: 'food' },
|
||||
{ emoji: '🥛', name: '牛奶', category: 'food' },
|
||||
{ emoji: '🍼', name: '奶瓶', category: 'food' },
|
||||
{ emoji: '☕', name: '咖啡', category: 'food' },
|
||||
{ emoji: '🍵', name: '茶', category: 'food' },
|
||||
{ emoji: '🍶', name: '清酒', category: 'food' },
|
||||
{ emoji: '🍾', name: '香槟', category: 'food' },
|
||||
{ emoji: '🍷', name: '红酒', category: 'food' },
|
||||
{ emoji: '🍸', name: '鸡尾酒', category: 'food' },
|
||||
{ emoji: '🍹', name: '果汁', category: 'food' },
|
||||
{ emoji: '🍺', name: '啤酒', category: 'food' },
|
||||
{ emoji: '🍻', name: '干杯', category: 'food' },
|
||||
{ emoji: '🥂', name: '碰杯', category: 'food' },
|
||||
|
||||
// 旅行与地点
|
||||
{ emoji: '🌍', name: '地球', category: 'travel' },
|
||||
{ emoji: '🌎', name: '西半球', category: 'travel' },
|
||||
{ emoji: '🌏', name: '东半球', category: 'travel' },
|
||||
{ emoji: '🌐', name: '全球', category: 'travel' },
|
||||
{ emoji: '🗺️', name: '地图', category: 'travel' },
|
||||
{ emoji: '🗾', name: '日本地图', category: 'travel' },
|
||||
{ emoji: '🧭', name: '指南针', category: 'travel' },
|
||||
{ emoji: '🏔️', name: '雪山', category: 'travel' },
|
||||
{ emoji: '🏕️', name: '露营', category: 'travel' },
|
||||
{ emoji: '🏖️', name: '沙滩', category: 'travel' },
|
||||
{ emoji: '🏜️', name: '沙漠', category: 'travel' },
|
||||
{ emoji: '🏝️', name: '荒岛', category: 'travel' },
|
||||
{ emoji: '🏞️', name: '风景', category: 'travel' },
|
||||
{ emoji: '🌋', name: '火山', category: 'travel' },
|
||||
{ emoji: '💧', name: '水滴', category: 'travel' },
|
||||
{ emoji: '🌊', name: '海浪', category: 'travel' },
|
||||
{ emoji: '🌅', name: '日出', category: 'travel' },
|
||||
{ emoji: '🌄', name: '日落', category: 'travel' },
|
||||
{ emoji: '🌠', name: '流星', category: 'travel' },
|
||||
{ emoji: '⭐', name: '星星', category: 'travel' },
|
||||
{ emoji: '🌟', name: '闪亮星星', category: 'travel' },
|
||||
{ emoji: '🌙', name: '月亮', category: 'travel' },
|
||||
{ emoji: '🌝', name: '满月', category: 'travel' },
|
||||
{ emoji: '🌚', name: '新月', category: 'travel' },
|
||||
{ emoji: '🌛', name: '弯月', category: 'travel' },
|
||||
{ emoji: '🌜', name: '残月', category: 'travel' },
|
||||
{ emoji: '🌞', name: '太阳', category: 'travel' },
|
||||
{ emoji: '🌡️', name: '温度计', category: 'travel' },
|
||||
{ emoji: '🌤️', name: '晴', category: 'travel' },
|
||||
{ emoji: '⛅', name: '多云', category: 'travel' },
|
||||
{ emoji: '🌥️', name: '阴天', category: 'travel' },
|
||||
{ emoji: '☁️', name: '云', category: 'travel' },
|
||||
{ emoji: '🌧️', name: '下雨', category: 'travel' },
|
||||
{ emoji: '⛈️', name: '雷雨', category: 'travel' },
|
||||
{ emoji: '🌩️', name: '闪电', category: 'travel' },
|
||||
{ emoji: '❄️', name: '雪花', category: 'travel' },
|
||||
{ emoji: '☃️', name: '雪人', category: 'travel' },
|
||||
{ emoji: '⛄', name: '雪⼈', category: 'travel' },
|
||||
{ emoji: '🌬️', name: '风', category: 'travel' },
|
||||
{ emoji: '💨', name: '大风', category: 'travel' },
|
||||
{ emoji: '🕐', name: '1点', category: 'travel' },
|
||||
{ emoji: '🕑', name: '2点', category: 'travel' },
|
||||
{ emoji: '🕒', name: '3点', category: 'travel' },
|
||||
{ emoji: '🕓', name: '4点', category: 'travel' },
|
||||
{ emoji: '🕔', name: '5点', category: 'travel' },
|
||||
{ emoji: '🕕', name: '6点', category: 'travel' },
|
||||
{ emoji: '🕖', name: '7点', category: 'travel' },
|
||||
{ emoji: '🕗', name: '8点', category: 'travel' },
|
||||
{ emoji: '🕘', name: '9点', category: 'travel' },
|
||||
{ emoji: '🕙', name: '10点', category: 'travel' },
|
||||
{ emoji: '🕚', name: '11点', category: 'travel' },
|
||||
{ emoji: '🕛', name: '12点', category: 'travel' },
|
||||
|
||||
// 活动
|
||||
{ emoji: '⚽', name: '足球', category: 'activities' },
|
||||
{ emoji: '🏀', name: '篮球', category: 'activities' },
|
||||
{ emoji: '🏈', name: '橄榄球', category: 'activities' },
|
||||
{ emoji: '⚾', name: '棒球', category: 'activities' },
|
||||
{ emoji: '🥎', name: '垒球', category: 'activities' },
|
||||
{ emoji: '🏐', name: '排球', category: 'activities' },
|
||||
{ emoji: '🏉', name: '橄榄球', category: 'activities' },
|
||||
{ emoji: '🎾', name: '网球', category: 'activities' },
|
||||
{ emoji: '🥏', name: '飞盘', category: 'activities' },
|
||||
{ emoji: '🏐', name: '排球', category: 'activities' },
|
||||
{ emoji: '🏏', name: '板球', category: 'activities' },
|
||||
{ emoji: '🏑', name: '曲棍球', category: 'activities' },
|
||||
{ emoji: '🥍', name: '长曲棍球', category: 'activities' },
|
||||
{ emoji: '🏒', name: '冰球', category: 'activities' },
|
||||
{ emoji: '🥅', name: '球门', category: 'activities' },
|
||||
{ emoji: '⛳', name: '高尔夫', category: 'activities' },
|
||||
{ emoji: '⛸️', name: '滑冰', category: 'activities' },
|
||||
{ emoji: '🎿', name: '滑雪', category: 'activities' },
|
||||
{ emoji: '🛷', name: '雪橇', category: 'activities' },
|
||||
{ emoji: '🏂', name: '单板滑雪', category: 'activities' },
|
||||
{ emoji: '🏋️', name: '举重', category: 'activities' },
|
||||
{ emoji: '🏌️', name: '高尔夫', category: 'activities' },
|
||||
{ emoji: '🚣', name: '划船', category: 'activities' },
|
||||
{ emoji: '🏊', name: '游泳', category: 'activities' },
|
||||
{ emoji: '🏄', name: '冲浪', category: 'activities' },
|
||||
{ emoji: '🏇', name: '赛马', category: 'activities' },
|
||||
{ emoji: '🚴', name: '骑行', category: 'activities' },
|
||||
{ emoji: '🚵', name: '山地自行车', category: 'activities' },
|
||||
{ emoji: '🏹', name: '射箭', category: 'activities' },
|
||||
{ emoji: '🎣', name: '钓鱼', category: 'activities' },
|
||||
{ emoji: '🤿', name: '潜水', category: 'activities' },
|
||||
{ emoji: '🎿', name: '滑雪', category: 'activities' },
|
||||
{ emoji: '🎽', name: '运动衫', category: 'activities' },
|
||||
{ emoji: '🎯', name: '靶心', category: 'activities' },
|
||||
{ emoji: '🎱', name: '台球', category: 'activities' },
|
||||
{ emoji: '🎮', name: '游戏手柄', category: 'activities' },
|
||||
{ emoji: '🎰', name: '老虎机', category: 'activities' },
|
||||
{ emoji: '🎲', name: '骰子', category: 'activities' },
|
||||
{ emoji: '🎯', name: '靶心', category: 'activities' },
|
||||
{ emoji: '🎭', name: '面具', category: 'activities' },
|
||||
{ emoji: '🎪', name: '马戏团', category: 'activities' },
|
||||
{ emoji: '🎫', name: '票', category: 'activities' },
|
||||
{ emoji: '🎟️', name: '门票', category: 'activities' },
|
||||
{ emoji: '🎨', name: '调色板', category: 'activities' },
|
||||
{ emoji: '🎬', name: '电影', category: 'activities' },
|
||||
{ emoji: '🎤', name: '麦克风', category: 'activities' },
|
||||
{ emoji: '🎧', name: '耳机', category: 'activities' },
|
||||
{ emoji: '🎼', name: '乐谱', category: 'activities' },
|
||||
{ emoji: '🎵', name: '音符', category: 'activities' },
|
||||
{ emoji: '🎶', name: '音乐', category: 'activities' },
|
||||
{ emoji: '🎹', name: '钢琴', category: 'activities' },
|
||||
{ emoji: '🥁', name: '鼓', category: 'activities' },
|
||||
{ emoji: '🎷', name: '萨克斯', category: 'activities' },
|
||||
{ emoji: '🎺', name: '小号', category: 'activities' },
|
||||
{ emoji: '🎸', name: '吉他', category: 'activities' },
|
||||
{ emoji: '🎻', name: '小提琴', category: 'activities' },
|
||||
{ emoji: '🎪', name: '马戏团', category: 'activities' },
|
||||
{ emoji: '🎭', name: '戏剧', category: 'activities' },
|
||||
{ emoji: '🎨', name: '艺术', category: 'activities' },
|
||||
{ emoji: '🎯', name: '目标', category: 'activities' },
|
||||
|
||||
// 物体
|
||||
{ emoji: '💻', name: '电脑', category: 'objects' },
|
||||
{ emoji: '📱', name: '手机', category: 'objects' },
|
||||
{ emoji: '📞', name: '电话', category: 'objects' },
|
||||
{ emoji: '📟', name: '寻呼机', category: 'objects' },
|
||||
{ emoji: '📠', name: '传真', category: 'objects' },
|
||||
{ emoji: '💽', name: '光盘', category: 'objects' },
|
||||
{ emoji: '💾', name: '软盘', category: 'objects' },
|
||||
{ emoji: '💿', name: 'CD', category: 'objects' },
|
||||
{ emoji: '📀', name: 'DVD', category: 'objects' },
|
||||
{ emoji: '📼', name: '录像带', category: 'objects' },
|
||||
{ emoji: '📷', name: '相机', category: 'objects' },
|
||||
{ emoji: '📸', name: '拍照', category: 'objects' },
|
||||
{ emoji: '📹', name: '摄像机', category: 'objects' },
|
||||
{ emoji: '🎥', name: '电影摄像机', category: 'objects' },
|
||||
{ emoji: '📺', name: '电视', category: 'objects' },
|
||||
{ emoji: '📻', name: '收音机', category: 'objects' },
|
||||
{ emoji: '📟', name: '寻呼机', category: 'objects' },
|
||||
{ emoji: '🔋', name: '电池', category: 'objects' },
|
||||
{ emoji: '🔌', name: '插头', category: 'objects' },
|
||||
{ emoji: '💡', name: '灯泡', category: 'objects' },
|
||||
{ emoji: '🔦', name: '手电筒', category: 'objects' },
|
||||
{ emoji: '📔', name: '笔记本', category: 'objects' },
|
||||
{ emoji: '📕', name: '书', category: 'objects' },
|
||||
{ emoji: '📖', name: '打开的书', category: 'objects' },
|
||||
{ emoji: '📗', name: '绿书', category: 'objects' },
|
||||
{ emoji: '📘', name: '蓝书', category: 'objects' },
|
||||
{ emoji: '📙', name: '黄书', category: 'objects' },
|
||||
{ emoji: '📚', name: '书架', category: 'objects' },
|
||||
{ emoji: '📓', name: '笔记本', category: 'objects' },
|
||||
{ emoji: '📒', name: '笔记本', category: 'objects' },
|
||||
{ emoji: '📃', name: '文件', category: 'objects' },
|
||||
{ emoji: '📜', name: '卷轴', category: 'objects' },
|
||||
{ emoji: '📄', name: '纸张', category: 'objects' },
|
||||
{ emoji: '📰', name: '报纸', category: 'objects' },
|
||||
{ emoji: '📞', name: '电话', category: 'objects' },
|
||||
{ emoji: '☎️', name: '电话', category: 'objects' },
|
||||
{ emoji: '📟', name: '寻呼机', category: 'objects' },
|
||||
{ emoji: '📠', name: '传真', category: 'objects' },
|
||||
{ emoji: '📡', name: '天线', category: 'objects' },
|
||||
{ emoji: '💾', name: '软盘', category: 'objects' },
|
||||
{ emoji: '💿', name: 'CD', category: 'objects' },
|
||||
{ emoji: '📀', name: 'DVD', category: 'objects' },
|
||||
{ emoji: '📼', name: '录像带', category: 'objects' },
|
||||
{ emoji: '🔍', name: '放大镜', category: 'objects' },
|
||||
{ emoji: '🔎', name: '搜索', category: 'objects' },
|
||||
{ emoji: '🕯️', name: '蜡烛', category: 'objects' },
|
||||
{ emoji: '💣', name: '炸弹', category: 'objects' },
|
||||
{ emoji: '🔫', name: '枪', category: 'objects' },
|
||||
{ emoji: '🔪', name: '刀', category: 'objects' },
|
||||
{ emoji: '💊', name: '药丸', category: 'objects' },
|
||||
{ emoji: '💉', name: '注射器', category: 'objects' },
|
||||
{ emoji: '🔬', name: '显微镜', category: 'objects' },
|
||||
{ emoji: '🔭', name: '望远镜', category: 'objects' },
|
||||
{ emoji: '🧪', name: '试管', category: 'objects' },
|
||||
{ emoji: '🧫', name: '培养皿', category: 'objects' },
|
||||
{ emoji: '🧬', name: 'DNA', category: 'objects' },
|
||||
|
||||
// 符号
|
||||
{ emoji: '❤️', name: '红心', category: 'symbols' },
|
||||
{ emoji: '🧡', name: '橙心', category: 'symbols' },
|
||||
{ emoji: '💛', name: '黄心', category: 'symbols' },
|
||||
{ emoji: '💚', name: '绿心', category: 'symbols' },
|
||||
{ emoji: '💙', name: '蓝心', category: 'symbols' },
|
||||
{ emoji: '💜', name: '紫心', category: 'symbols' },
|
||||
{ emoji: '🖤', name: '黑心', category: 'symbols' },
|
||||
{ emoji: '💔', name: '破碎的心', category: 'symbols' },
|
||||
{ emoji: '💓', name: '心跳', category: 'symbols' },
|
||||
{ emoji: '💗', name: '爱心', category: 'symbols' },
|
||||
{ emoji: '💖', name: '闪亮的心', category: 'symbols' },
|
||||
{ emoji: '💘', name: '丘比特之箭', category: 'symbols' },
|
||||
{ emoji: '💝', name: '礼物', category: 'symbols' },
|
||||
{ emoji: '💞', name: '双心', category: 'symbols' },
|
||||
{ emoji: '💟', name: '心形', category: 'symbols' },
|
||||
{ emoji: '❣️', name: '感叹号', category: 'symbols' },
|
||||
{ emoji: '💕', name: '双心', category: 'symbols' },
|
||||
{ emoji: '💌', name: '情书', category: 'symbols' },
|
||||
{ emoji: '💋', name: '吻', category: 'symbols' },
|
||||
{ emoji: '💯', name: '满分', category: 'symbols' },
|
||||
{ emoji: '💢', name: '生气', category: 'symbols' },
|
||||
{ emoji: '💥', name: '爆炸', category: 'symbols' },
|
||||
{ emoji: '💫', name: '星星', category: 'symbols' },
|
||||
{ emoji: '💦', name: '水滴', category: 'symbols' },
|
||||
{ emoji: '💨', name: '风', category: 'symbols' },
|
||||
{ emoji: '🕳️', name: '洞', category: 'symbols' },
|
||||
{ emoji: '💣', name: '炸弹', category: 'symbols' },
|
||||
{ emoji: '💬', name: '对话', category: 'symbols' },
|
||||
{ emoji: '💭', name: '思考', category: 'symbols' },
|
||||
{ emoji: '💤', name: '打鼾', category: 'symbols' },
|
||||
{ emoji: '💡', name: '灯泡', category: 'symbols' },
|
||||
{ emoji: '💢', name: '生气', category: 'symbols' },
|
||||
{ emoji: '🔔', name: '铃铛', category: 'symbols' },
|
||||
{ emoji: '🔕', name: '静音', category: 'symbols' },
|
||||
{ emoji: '📣', name: '喇叭', category: 'symbols' },
|
||||
{ emoji: '📢', name: '扩音器', category: 'symbols' },
|
||||
{ emoji: '🔊', name: '音量', category: 'symbols' },
|
||||
{ emoji: '🔉', name: '音量', category: 'symbols' },
|
||||
{ emoji: '🔈', name: '音量', category: 'symbols' },
|
||||
{ emoji: '🔇', name: '静音', category: 'symbols' },
|
||||
{ emoji: '✅', name: '对勾', category: 'symbols' },
|
||||
{ emoji: '❌', name: '叉号', category: 'symbols' },
|
||||
{ emoji: '❓', name: '问号', category: 'symbols' },
|
||||
{ emoji: '❗', name: '感叹号', category: 'symbols' },
|
||||
{ emoji: '❔', name: '问号', category: 'symbols' },
|
||||
{ emoji: '❕', name: '感叹号', category: 'symbols' },
|
||||
{ emoji: '💯', name: '100分', category: 'symbols' },
|
||||
{ emoji: '🔟', name: '10', category: 'symbols' },
|
||||
{ emoji: '🔢', name: '数字', category: 'symbols' },
|
||||
{ emoji: '🔣', name: '符号', category: 'symbols' },
|
||||
{ emoji: '🔤', name: '字母', category: 'symbols' },
|
||||
{ emoji: '🔡', name: '小写', category: 'symbols' },
|
||||
{ emoji: '🔠', name: '大写', category: 'symbols' },
|
||||
{ emoji: '🔸', name: '菱形', category: 'symbols' },
|
||||
{ emoji: '🔹', name: '蓝色菱形', category: 'symbols' },
|
||||
{ emoji: '🔺', name: '三角形', category: 'symbols' },
|
||||
{ emoji: '🔻', name: '倒三角形', category: 'symbols' },
|
||||
{ emoji: '💠', name: '钻石', category: 'symbols' },
|
||||
{ emoji: '🔘', name: '圆形', category: 'symbols' },
|
||||
{ emoji: '🔴', name: '红圆', category: 'symbols' },
|
||||
{ emoji: '🔵', name: '蓝圆', category: 'symbols' },
|
||||
{ emoji: '🔺', name: '三角形', category: 'symbols' },
|
||||
{ emoji: '🔻', name: '倒三角形', category: 'symbols' },
|
||||
{ emoji: '🔸', name: '橙色菱形', category: 'symbols' },
|
||||
{ emoji: '🔹', name: '蓝色菱形', category: 'symbols' },
|
||||
{ emoji: '🔶', name: '橙色六边形', category: 'symbols' },
|
||||
{ emoji: '🔷', name: '蓝色六边形', category: 'symbols' },
|
||||
{ emoji: '🔳', name: '白色正方形', category: 'symbols' },
|
||||
{ emoji: '🔲', name: '黑色正方形', category: 'symbols' },
|
||||
|
||||
// 旗帜
|
||||
{ emoji: '🇨🇳', name: '中国国旗', category: 'flags' },
|
||||
{ emoji: '🇺🇸', name: '美国国旗', category: 'flags' },
|
||||
{ emoji: '🇯🇵', name: '日本国旗', category: 'flags' },
|
||||
{ emoji: '🇰🇷', name: '韩国国旗', category: 'flags' },
|
||||
{ emoji: '🇬🇧', name: '英国国旗', category: 'flags' },
|
||||
{ emoji: '🇫🇷', name: '法国国旗', category: 'flags' },
|
||||
{ emoji: '🇩🇪', name: '德国国旗', category: 'flags' },
|
||||
{ emoji: '🇮🇹', name: '意大利国旗', category: 'flags' },
|
||||
{ emoji: '🇷🇺', name: '俄罗斯国旗', category: 'flags' },
|
||||
{ emoji: '🇺🇾', name: '乌拉圭国旗', category: 'flags' },
|
||||
{ emoji: '🇧🇪', name: '比利时国旗', category: 'flags' },
|
||||
{ emoji: '🇳🇱', name: '荷兰国旗', category: 'flags' },
|
||||
{ emoji: '🇪🇸', name: '西班牙国旗', category: 'flags' },
|
||||
{ emoji: '🇵🇹', name: '葡萄牙国旗', category: 'flags' },
|
||||
{ emoji: '🇮🇪', name: '爱尔兰国旗', category: 'flags' },
|
||||
{ emoji: '🇦🇺', name: '澳大利亚国旗', category: 'flags' },
|
||||
{ emoji: '🇨🇦', name: '加拿大国旗', category: 'flags' },
|
||||
{ emoji: '🇮🇱', name: '以色列国旗', category: 'flags' },
|
||||
{ emoji: '🇵🇰', name: '巴基斯坦国旗', category: 'flags' },
|
||||
{ emoji: '🇮🇳', name: '印度国旗', category: 'flags' },
|
||||
{ emoji: '🇮🇩', name: '印度尼西亚国旗', category: 'flags' },
|
||||
{ emoji: '🇹🇭', name: '泰国国旗', category: 'flags' },
|
||||
{ emoji: '🇻🇳', name: '越南国旗', category: 'flags' },
|
||||
{ emoji: '🇸🇦', name: '沙特阿拉伯国旗', category: 'flags' },
|
||||
{ emoji: '🇦🇪', name: '阿联酋国旗', category: 'flags' },
|
||||
{ emoji: '🇹🇷', name: '土耳其国旗', category: 'flags' },
|
||||
{ emoji: '🇲🇾', name: '马来西亚国旗', category: 'flags' },
|
||||
{ emoji: '🇸🇬', name: '新加坡国旗', category: 'flags' },
|
||||
{ emoji: '🇭🇰', name: '香港特别行政区区旗', category: 'flags' },
|
||||
{ emoji: '🇲🇴', name: '澳门特别行政区区旗', category: 'flags' },
|
||||
{ emoji: '🇳🇴', name: '挪威国旗', category: 'flags' },
|
||||
{ emoji: '🇸🇪', name: '瑞典国旗', category: 'flags' },
|
||||
{ emoji: '🇫🇮', name: '芬兰国旗', category: 'flags' },
|
||||
{ emoji: '🇩🇰', name: '丹麦国旗', category: 'flags' },
|
||||
{ emoji: '🇮🇸', name: '冰岛国旗', category: 'flags' },
|
||||
{ emoji: '🇨🇭', name: '瑞士国旗', category: 'flags' },
|
||||
{ emoji: '🇦🇹', name: '奥地利国旗', category: 'flags' },
|
||||
{ emoji: '🇭🇺', name: '匈牙利国旗', category: 'flags' },
|
||||
{ emoji: '🇵🇱', name: '波兰国旗', category: 'flags' },
|
||||
{ emoji: '🇨🇿', name: '捷克国旗', category: 'flags' },
|
||||
{ emoji: '🇸🇰', name: '斯洛伐克国旗', category: 'flags' },
|
||||
{ emoji: '🇭🇷', name: '克罗地亚国旗', category: 'flags' },
|
||||
{ emoji: '🇧🇦', name: '波黑国旗', category: 'flags' },
|
||||
{ emoji: '🇸🇮', name: '斯洛文尼亚国旗', category: 'flags' },
|
||||
{ emoji: '🇲🇰', name: '北马其顿国旗', category: 'flags' },
|
||||
{ emoji: '🇧🇬', name: '保加利亚国旗', category: 'flags' },
|
||||
{ emoji: '🇷🇴', name: '罗马尼亚国旗', category: 'flags' },
|
||||
{ emoji: '🇺🇦', name: '乌克兰国旗', category: 'flags' },
|
||||
{ emoji: '🇧🇾', name: '白俄罗斯国旗', category: 'flags' },
|
||||
{ emoji: '🇰🇿', name: '哈萨克斯坦国旗', category: 'flags' },
|
||||
{ emoji: '🇺🇿', name: '乌兹别克斯坦国旗', category: 'flags' },
|
||||
{ emoji: '🇰🇬', name: '吉尔吉斯斯坦国旗', category: 'flags' },
|
||||
{ emoji: '🇹🇯', name: '塔吉克斯坦国旗', category: 'flags' },
|
||||
{ emoji: '🇦🇲', name: '亚美尼亚国旗', category: 'flags' },
|
||||
{ emoji: '🇦🇿', name: '阿塞拜疆国旗', category: 'flags' },
|
||||
{ emoji: '🇮🇶', name: '伊拉克国旗', category: 'flags' },
|
||||
{ emoji: '🇮🇷', name: '伊朗国旗', category: 'flags' },
|
||||
{ emoji: '🇱🇧', name: '黎巴嫩国旗', category: 'flags' },
|
||||
{ emoji: '🇸🇾', name: '叙利亚国旗', category: 'flags' },
|
||||
{ emoji: '🇯🇴', name: '约旦国旗', category: 'flags' },
|
||||
{ emoji: '🇵🇸', name: '巴勒斯坦国旗', category: 'flags' },
|
||||
{ emoji: '🇰🇼', name: '科威特国旗', category: 'flags' },
|
||||
{ emoji: '🇴🇲', name: '阿曼国旗', category: 'flags' },
|
||||
{ emoji: '🇶🇦', name: '卡塔尔国旗', category: 'flags' },
|
||||
{ emoji: '🇧🇭', name: '巴林国旗', category: 'flags' },
|
||||
{ emoji: '🇾🇪', name: '也门国旗', category: 'flags' },
|
||||
{ emoji: '🇲🇦', name: '摩洛哥国旗', category: 'flags' },
|
||||
{ emoji: '🇩🇿', name: '阿尔及利亚国旗', category: 'flags' },
|
||||
{ emoji: '🇹🇳', name: '突尼斯国旗', category: 'flags' },
|
||||
{ emoji: '🇪🇬', name: '埃及国旗', category: 'flags' },
|
||||
{ emoji: '🇸🇩', name: '苏丹国旗', category: 'flags' },
|
||||
{ emoji: '🇪🇷', name: '厄立特里亚国旗', category: 'flags' },
|
||||
{ emoji: '🇩🇯', name: '吉布提国旗', category: 'flags' },
|
||||
{ emoji: '🇸🇴', name: '索马里国旗', category: 'flags' },
|
||||
{ emoji: '🇰🇪', name: '肯尼亚国旗', category: 'flags' },
|
||||
{ emoji: '🇺🇬', name: '乌干达国旗', category: 'flags' },
|
||||
{ emoji: '🇹🇿', name: '坦桑尼亚国旗', category: 'flags' },
|
||||
{ emoji: '🇷🇼', name: '卢旺达国旗', category: 'flags' },
|
||||
{ emoji: '🇧🇮', name: '布隆迪国旗', category: 'flags' },
|
||||
{ emoji: '🇨🇩', name: '刚果民主共和国国旗', category: 'flags' },
|
||||
{ emoji: '🇨🇬', name: '刚果共和国国旗', category: 'flags' },
|
||||
{ emoji: '🇦🇴', name: '安哥拉国旗', category: 'flags' },
|
||||
{ emoji: '🇲🇿', name: '莫桑比克国旗', category: 'flags' },
|
||||
{ emoji: '🇿🇦', name: '南非国旗', category: 'flags' },
|
||||
{ emoji: '🇳🇦', name: '纳米比亚国旗', category: 'flags' },
|
||||
{ emoji: '🇧🇼', name: '博茨瓦纳国旗', category: 'flags' },
|
||||
{ emoji: '🇿🇲', name: '赞比亚国旗', category: 'flags' },
|
||||
{ emoji: '🇲🇼', name: '马拉维国旗', category: 'flags' },
|
||||
{ emoji: '🇲🇺', name: '毛里求斯国旗', category: 'flags' },
|
||||
{ emoji: '🇸🇨', name: '塞舌尔国旗', category: 'flags' },
|
||||
{ emoji: '🇨🇺', name: '古巴国旗', category: 'flags' },
|
||||
{ emoji: '🇲🇽', name: '墨西哥国旗', category: 'flags' },
|
||||
{ emoji: '🇧🇷', name: '巴西国旗', category: 'flags' },
|
||||
{ emoji: '🇦🇷', name: '阿根廷国旗', category: 'flags' },
|
||||
{ emoji: '🇨🇱', name: '智利国旗', category: 'flags' },
|
||||
{ emoji: '🇵🇪', name: '秘鲁国旗', category: 'flags' },
|
||||
{ emoji: '🇪🇨', name: '厄瓜多尔国旗', category: 'flags' },
|
||||
{ emoji: '🇧🇴', name: '玻利维亚国旗', category: 'flags' },
|
||||
{ emoji: '🇵🇾', name: '巴拉圭国旗', category: 'flags' },
|
||||
{ emoji: '🇨🇺', name: '古巴国旗', category: 'flags' },
|
||||
{ emoji: '🇩🇴', name: '多米尼加共和国国旗', category: 'flags' },
|
||||
{ emoji: '🇵🇭', name: '菲律宾国旗', category: 'flags' },
|
||||
{ emoji: '🇻🇺', name: '瓦努阿图国旗', category: 'flags' },
|
||||
{ emoji: '🇫🇯', name: '斐济国旗', category: 'flags' },
|
||||
{ emoji: '🇵🇳', name: '帕劳国旗', category: 'flags' },
|
||||
{ emoji: '🇲🇵', name: '马绍尔群岛国旗', category: 'flags' },
|
||||
{ emoji: '🇫🇲', name: '密克罗尼西亚联邦国旗', category: 'flags' },
|
||||
{ emoji: '🇰🇮', name: '基里巴斯国旗', category: 'flags' },
|
||||
{ emoji: '🇹🇻', name: '图瓦卢国旗', category: 'flags' },
|
||||
{ emoji: '🇨🇰', name: '库克群岛国旗', category: 'flags' },
|
||||
{ emoji: '🇳🇺', name: '纽埃国旗', category: 'flags' },
|
||||
{ emoji: '🇲🇲', name: '缅甸国旗', category: 'flags' },
|
||||
{ emoji: '🇱🇰', name: '斯里兰卡国旗', category: 'flags' },
|
||||
{ emoji: '🇲🇻', name: '马尔代夫国旗', category: 'flags' },
|
||||
{ emoji: '🇧🇩', name: '孟加拉国国旗', category: 'flags' },
|
||||
{ emoji: '🇳🇵', name: '尼泊尔国旗', category: 'flags' },
|
||||
{ emoji: '🇱🇦', name: '老挝国旗', category: 'flags' },
|
||||
{ emoji: '🇰🇭', name: '柬埔寨国旗', category: 'flags' }
|
||||
]
|
||||
|
||||
// 过滤后的Emoji
|
||||
const filteredEmojis = computed(() => {
|
||||
let result = emojis
|
||||
|
||||
// 按分类过滤
|
||||
if (selectedCategory.value !== 'all') {
|
||||
result = result.filter(emoji => emoji.category === selectedCategory.value)
|
||||
}
|
||||
|
||||
// 按关键词搜索
|
||||
if (searchKeyword.value) {
|
||||
const keyword = searchKeyword.value.toLowerCase()
|
||||
result = result.filter(emoji => emoji.name.toLowerCase().includes(keyword))
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
// 复制Emoji
|
||||
const copyEmoji = (emoji: string) => {
|
||||
copy(emoji)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<!-- 搜索和分类 -->
|
||||
<div class="mb-4">
|
||||
<el-input
|
||||
v-model="searchKeyword"
|
||||
placeholder="搜索Emoji"
|
||||
class="mb-4"
|
||||
/>
|
||||
|
||||
<div class="flex flex-wrap gap-2 mb-4">
|
||||
<el-button
|
||||
v-for="category in emojiCategories"
|
||||
:key="category.id"
|
||||
:type="selectedCategory === category.id ? 'primary' : 'default'"
|
||||
@click="selectedCategory = category.id"
|
||||
size="small"
|
||||
>
|
||||
{{ category.name }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Emoji列表 -->
|
||||
<div class="grid grid-cols-6 sm:grid-cols-8 md:grid-cols-10 lg:grid-cols-12 gap-4">
|
||||
<div
|
||||
v-for="emoji in filteredEmojis"
|
||||
:key="emoji.emoji + emoji.name"
|
||||
class="flex flex-col items-center p-2 border rounded hover:bg-gray-50 cursor-pointer"
|
||||
@click="copyEmoji(emoji.emoji)"
|
||||
>
|
||||
<div class="text-3xl mb-1">{{ emoji.emoji }}</div>
|
||||
<div class="text-xs text-center">{{ emoji.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线Emoji表情大全,提供各种分类的Emoji表情,点击即可复制到剪贴板,方便在聊天、文档中使用。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</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: "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,234 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, computed } 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 { Md5 } from 'ts-md5'
|
||||
import CryptoJS from 'crypto-js'
|
||||
|
||||
const title = "Hash计算器"
|
||||
|
||||
// 状态管理
|
||||
const inputText = ref('')
|
||||
const algorithm = ref('MD5')
|
||||
const key = ref('')
|
||||
const hashResult = ref('')
|
||||
|
||||
// 计算属性:是否是Hmac算法
|
||||
const isHmacAlgorithm = computed(() => {
|
||||
return algorithm.value.startsWith('HMAC-')
|
||||
})
|
||||
|
||||
// 哈希算法选项
|
||||
const algorithmOptions = [
|
||||
{ label: 'MD5', value: 'MD5' },
|
||||
{ label: 'SHA-1', value: 'SHA-1' },
|
||||
{ label: 'SHA-256', value: 'SHA-256' },
|
||||
{ label: 'SHA-384', value: 'SHA-384' },
|
||||
{ label: 'SHA-512', value: 'SHA-512' },
|
||||
{ label: 'Hmac-MD5', value: 'HMAC-MD5' },
|
||||
{ label: 'Hmac-SHA1', value: 'HMAC-SHA1' },
|
||||
{ label: 'Hmac-SHA256', value: 'HMAC-SHA256' },
|
||||
{ label: 'Hmac-SHA384', value: 'HMAC-SHA384' },
|
||||
{ label: 'Hmac-SHA512', value: 'HMAC-SHA512' }
|
||||
]
|
||||
|
||||
// 监听输入变化,自动计算哈希
|
||||
watch([inputText, algorithm, key], async () => {
|
||||
if (!inputText.value) {
|
||||
hashResult.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (algorithm.value === 'MD5') {
|
||||
// 计算MD5
|
||||
hashResult.value = Md5.hashStr(inputText.value)
|
||||
} else if (algorithm.value.startsWith('HMAC-')) {
|
||||
// 处理Hmac算法
|
||||
if (!key.value) {
|
||||
hashResult.value = '请输入Hmac密钥'
|
||||
return
|
||||
}
|
||||
|
||||
// 提取Hmac算法中的哈希算法部分
|
||||
const hashAlgorithm = algorithm.value.replace('HMAC-', '')
|
||||
|
||||
if (hashAlgorithm === 'MD5') {
|
||||
// 使用crypto-js计算Hmac-MD5
|
||||
try {
|
||||
const hmac = CryptoJS.HmacMD5(inputText.value, key.value)
|
||||
hashResult.value = hmac.toString()
|
||||
} catch (error) {
|
||||
console.error('Hmac-MD5计算失败:', error)
|
||||
hashResult.value = '计算失败'
|
||||
}
|
||||
} else {
|
||||
// 处理其他Hmac算法
|
||||
// 转换为Web Crypto API支持的标准格式
|
||||
let standardHashAlgorithm
|
||||
switch (hashAlgorithm) {
|
||||
case 'SHA1':
|
||||
standardHashAlgorithm = 'SHA-1'
|
||||
break
|
||||
case 'SHA256':
|
||||
standardHashAlgorithm = 'SHA-256'
|
||||
break
|
||||
case 'SHA384':
|
||||
standardHashAlgorithm = 'SHA-384'
|
||||
break
|
||||
case 'SHA512':
|
||||
standardHashAlgorithm = 'SHA-512'
|
||||
break
|
||||
default:
|
||||
standardHashAlgorithm = hashAlgorithm
|
||||
}
|
||||
|
||||
try {
|
||||
const encoder = new TextEncoder()
|
||||
const data = encoder.encode(inputText.value)
|
||||
const keyData = encoder.encode(key.value)
|
||||
|
||||
// 创建密钥
|
||||
const cryptoKey = await crypto.subtle.importKey(
|
||||
'raw',
|
||||
keyData,
|
||||
{ name: 'HMAC', hash: standardHashAlgorithm },
|
||||
false,
|
||||
['sign']
|
||||
)
|
||||
|
||||
// 计算Hmac
|
||||
const hmacBuffer = await crypto.subtle.sign('HMAC', cryptoKey, data)
|
||||
const hmacArray = Array.from(new Uint8Array(hmacBuffer))
|
||||
const hmacHex = hmacArray.map(b => b.toString(16).padStart(2, '0')).join('')
|
||||
|
||||
hashResult.value = hmacHex
|
||||
} catch (error) {
|
||||
console.error('Hmac计算失败:', error)
|
||||
hashResult.value = '计算失败'
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 使用Web Crypto API计算其他哈希
|
||||
const encoder = new TextEncoder()
|
||||
const data = encoder.encode(inputText.value)
|
||||
const hashBuffer = await crypto.subtle.digest(algorithm.value, data)
|
||||
const hashArray = Array.from(new Uint8Array(hashBuffer))
|
||||
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('')
|
||||
|
||||
hashResult.value = hashHex
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('计算失败:', error)
|
||||
hashResult.value = '计算失败'
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// 计算哈希
|
||||
const calculateHash = async () => {
|
||||
// 由于我们已经在watch中自动计算了哈希,这里只需要显示成功消息
|
||||
if (!inputText.value) {
|
||||
ElMessage.warning('请输入要计算哈希的文本')
|
||||
return
|
||||
}
|
||||
|
||||
if (algorithm.value.startsWith('HMAC-') && !key.value) {
|
||||
ElMessage.warning('请输入Hmac密钥')
|
||||
return
|
||||
}
|
||||
|
||||
ElMessage.success('计算成功')
|
||||
}
|
||||
|
||||
// 复制结果
|
||||
const copyResult = () => {
|
||||
if (!hashResult.value || hashResult.value === '计算失败' || hashResult.value === '请输入Hmac密钥') {
|
||||
ElMessage.warning('没有可复制的内容')
|
||||
return
|
||||
}
|
||||
|
||||
copy(hashResult.value)
|
||||
}
|
||||
|
||||
// 清空输入
|
||||
const clearInput = () => {
|
||||
inputText.value = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<!-- 输入部分 -->
|
||||
<div class="mb-4">
|
||||
<el-input
|
||||
v-model="inputText"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="请输入要计算哈希的文本"
|
||||
class="mb-2"
|
||||
/>
|
||||
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<el-text>哈希算法:</el-text>
|
||||
<el-select v-model="algorithm" style="width: 150px;">
|
||||
<el-option
|
||||
v-for="option in algorithmOptions"
|
||||
:key="option.value"
|
||||
:label="option.label"
|
||||
:value="option.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<!-- 密钥输入(仅Hmac算法需要) -->
|
||||
<div class="flex items-center gap-2 mb-2" v-if="isHmacAlgorithm">
|
||||
<el-text>密钥(HMAC算法必填):</el-text>
|
||||
<el-input
|
||||
v-model="key"
|
||||
placeholder="请输入Hmac密钥"
|
||||
style="width: 500px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="mb-4">
|
||||
<el-button type="primary" @click="calculateHash">计算哈希</el-button>
|
||||
<el-button @click="copyResult">复制结果</el-button>
|
||||
<el-button @click="clearInput">清空</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 结果部分 -->
|
||||
<div>
|
||||
<el-input
|
||||
:value="hashResult"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="哈希结果"
|
||||
readonly
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="copyResult">复制</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线Hash计算器,支持MD5、SHA-1、SHA-256、SHA-384、SHA-512等多种哈希算法,以及Hmac-MD5、Hmac-SHA1、Hmac-SHA256、Hmac-SHA384、Hmac-SHA512等Hmac算法,可用于数据完整性验证、密码加密等场景。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -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>
|
||||
提供在线html、xml格式化
|
||||
</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:"由WebDAV(RFC 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>
|
||||
@@ -0,0 +1,338 @@
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div class="tool-content">
|
||||
<div class="upload-section">
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="上传图片">
|
||||
<el-upload
|
||||
class="upload-demo"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:on-change="handleFileChange"
|
||||
:show-file-list="false"
|
||||
accept="image/*"
|
||||
>
|
||||
<el-button type="primary">点击上传</el-button>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
支持 JPG、PNG、GIF 等格式的图片
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="image-section">
|
||||
<div v-if="imageUrl" class="image-container">
|
||||
<img
|
||||
ref="image"
|
||||
:src="imageUrl"
|
||||
@load="handleImageLoad"
|
||||
@click="handleImageClick"
|
||||
class="preview-image"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<canvas
|
||||
ref="canvas"
|
||||
class="hidden-canvas"
|
||||
></canvas>
|
||||
</div>
|
||||
<div v-else class="no-image">
|
||||
请上传图片
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="color-section">
|
||||
<h3>颜色信息</h3>
|
||||
<div v-if="selectedColor" class="color-info">
|
||||
<div class="color-preview" :style="{ backgroundColor: selectedColor.hex }"></div>
|
||||
<div class="color-details">
|
||||
<div class="color-item">
|
||||
<span class="color-label">HEX:</span>
|
||||
<span class="color-value">{{ selectedColor.hex }}</span>
|
||||
<el-button type="text" size="small" @click="copyColor(selectedColor.hex)">复制</el-button>
|
||||
</div>
|
||||
<div class="color-item">
|
||||
<span class="color-label">RGB:</span>
|
||||
<span class="color-value">{{ selectedColor.rgb }}</span>
|
||||
<el-button type="text" size="small" @click="copyColor(selectedColor.rgb)">复制</el-button>
|
||||
</div>
|
||||
<div class="color-item">
|
||||
<span class="color-label">HSL:</span>
|
||||
<span class="color-value">{{ selectedColor.hsl }}</span>
|
||||
<el-button type="text" size="small" @click="copyColor(selectedColor.hsl)">复制</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="no-color">
|
||||
点击图片获取颜色
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线传图取色工具,上传图片后点击图片任意位置获取颜色值,支持 HEX、RGB、HSL 三种颜色格式,可用于设计和开发中获取颜色参考。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
|
||||
|
||||
const title = "传图取色"
|
||||
|
||||
const imageUrl = ref('');
|
||||
const image = ref<HTMLImageElement | null>(null);
|
||||
const canvas = ref<HTMLCanvasElement | null>(null);
|
||||
const selectedColor = ref<{
|
||||
hex: string;
|
||||
rgb: string;
|
||||
hsl: string;
|
||||
} | null>(null);
|
||||
|
||||
// 处理文件上传
|
||||
const handleFileChange = (file: any) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
imageUrl.value = e.target?.result as string;
|
||||
selectedColor.value = null;
|
||||
};
|
||||
reader.readAsDataURL(file.raw);
|
||||
};
|
||||
|
||||
// 处理图片加载完成
|
||||
const handleImageLoad = () => {
|
||||
if (image.value && canvas.value) {
|
||||
const ctx = canvas.value.getContext('2d');
|
||||
if (ctx) {
|
||||
// 设置canvas尺寸与图片一致
|
||||
canvas.value.width = image.value.width;
|
||||
canvas.value.height = image.value.height;
|
||||
// 绘制图片到canvas
|
||||
ctx.drawImage(image.value, 0, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 处理图片点击事件
|
||||
const handleImageClick = (e: MouseEvent) => {
|
||||
if (!image.value || !canvas.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const rect = image.value.getBoundingClientRect();
|
||||
const x = Math.floor((e.clientX - rect.left) * (image.value.width / rect.width));
|
||||
const y = Math.floor((e.clientY - rect.top) * (image.value.height / rect.height));
|
||||
|
||||
const ctx = canvas.value.getContext('2d');
|
||||
if (ctx) {
|
||||
const pixel = ctx.getImageData(x, y, 1, 1).data;
|
||||
const [r, g, b] = pixel;
|
||||
|
||||
// 转换为各种颜色格式
|
||||
const hex = rgbToHex(r, g, b);
|
||||
const rgb = `rgb(${r}, ${g}, ${b})`;
|
||||
const hsl = rgbToHsl(r, g, b);
|
||||
|
||||
selectedColor.value = {
|
||||
hex,
|
||||
rgb,
|
||||
hsl
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('取色失败:', error);
|
||||
ElMessage.error('取色失败,可能是图片跨域问题');
|
||||
}
|
||||
};
|
||||
|
||||
// RGB转HEX
|
||||
const rgbToHex = (r: number, g: number, b: number) => {
|
||||
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase()}`;
|
||||
};
|
||||
|
||||
// RGB转HSL
|
||||
const rgbToHsl = (r: number, g: number, b: number) => {
|
||||
r /= 255;
|
||||
g /= 255;
|
||||
b /= 255;
|
||||
|
||||
const max = Math.max(r, g, b);
|
||||
const min = Math.min(r, g, b);
|
||||
let h = 0;
|
||||
let s = 0;
|
||||
let l = (max + min) / 2;
|
||||
|
||||
if (max !== min) {
|
||||
const d = max - min;
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
|
||||
switch (max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
|
||||
h /= 6;
|
||||
}
|
||||
|
||||
h = Math.round(h * 360);
|
||||
s = Math.round(s * 100);
|
||||
l = Math.round(l * 100);
|
||||
|
||||
return `hsl(${h}, ${s}%, ${l}%)`;
|
||||
};
|
||||
|
||||
// 复制颜色值
|
||||
const copyColor = (color: string) => {
|
||||
navigator.clipboard.writeText(color).then(() => {
|
||||
ElMessage.success('已复制到剪贴板');
|
||||
}).catch(() => {
|
||||
ElMessage.error('复制失败');
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tool-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.upload-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.image-section {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 300px;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.image-container {
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
.preview-image {
|
||||
max-width: 100%;
|
||||
max-height: 400px;
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.hidden-canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.no-image {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 100px 0;
|
||||
}
|
||||
|
||||
.color-section {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.color-section h3 {
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.color-info {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.color-preview {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e4e7ed;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.color-details {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.color-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.color-label {
|
||||
width: 60px;
|
||||
font-weight: bold;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.color-value {
|
||||
flex: 1;
|
||||
font-family: monospace;
|
||||
background-color: #fff;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.no-color {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 40px 0;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.tool-content {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.upload-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.image-section {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.color-section {
|
||||
width: 40%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,322 @@
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div class="tool-content">
|
||||
<div class="input-section">
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="上传图片">
|
||||
<el-upload
|
||||
class="upload-demo"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:on-change="handleFileChange"
|
||||
:show-file-list="false"
|
||||
accept="image/*"
|
||||
>
|
||||
<el-button type="primary">点击上传</el-button>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
支持 JPG、PNG、GIF 等格式的图片
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="水印文字">
|
||||
<el-input
|
||||
v-model="watermarkText"
|
||||
placeholder="请输入水印文字"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="水印设置">
|
||||
<div class="settings-grid">
|
||||
<el-form-item label="位置" class="setting-item">
|
||||
<el-select v-model="position" size="small">
|
||||
<el-option label="左上角" value="top-left" />
|
||||
<el-option label="右上角" value="top-right" />
|
||||
<el-option label="左下角" value="bottom-left" />
|
||||
<el-option label="右下角" value="bottom-right" />
|
||||
<el-option label="居中" value="center" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="字体大小" class="setting-item">
|
||||
<el-input-number v-model="fontSize" :min="12" :max="100" size="small" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="透明度" class="setting-item">
|
||||
<el-slider v-model="opacity" :min="0" :max="1" :step="0.1" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="颜色" class="setting-item">
|
||||
<el-color-picker v-model="textColor" size="small" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="addWatermark">添加水印</el-button>
|
||||
<el-button @click="clearImage">清空</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="preview-section">
|
||||
<h3>预览</h3>
|
||||
<div class="preview-container">
|
||||
<div v-if="imageUrl" class="image-wrapper">
|
||||
<img :src="imageUrl" class="original-image" />
|
||||
<canvas
|
||||
ref="canvas"
|
||||
class="watermark-canvas"
|
||||
:width="imageWidth"
|
||||
:height="imageHeight"
|
||||
></canvas>
|
||||
</div>
|
||||
<div v-else class="no-image">
|
||||
请上传图片
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="watermarkedUrl" class="download-section">
|
||||
<el-button type="success" @click="downloadImage">下载图片</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线图片水印添加工具,上传图片后添加自定义文字水印,支持设置水印位置、字体大小、透明度和颜色,可下载带水印的图片。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, nextTick } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
|
||||
|
||||
const title = "图片水印添加"
|
||||
|
||||
const imageUrl = ref('');
|
||||
const watermarkedUrl = ref('');
|
||||
const watermarkText = ref('水印');
|
||||
const position = ref('bottom-right');
|
||||
const fontSize = ref(24);
|
||||
const opacity = ref(0.5);
|
||||
const textColor = ref('#ffffff');
|
||||
const imageWidth = ref(0);
|
||||
const imageHeight = ref(0);
|
||||
const canvas = ref<HTMLCanvasElement | null>(null);
|
||||
|
||||
// 处理文件上传
|
||||
const handleFileChange = (file: any) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
imageWidth.value = img.width;
|
||||
imageHeight.value = img.height;
|
||||
imageUrl.value = e.target?.result as string;
|
||||
watermarkedUrl.value = '';
|
||||
};
|
||||
img.src = e.target?.result as string;
|
||||
};
|
||||
reader.readAsDataURL(file.raw);
|
||||
};
|
||||
|
||||
// 添加水印
|
||||
const addWatermark = async () => {
|
||||
if (!imageUrl.value || !watermarkText.value) {
|
||||
ElMessage.warning('请上传图片并输入水印文字');
|
||||
return;
|
||||
}
|
||||
|
||||
await nextTick();
|
||||
|
||||
if (canvas.value) {
|
||||
const ctx = canvas.value.getContext('2d');
|
||||
if (ctx) {
|
||||
// 清除画布
|
||||
ctx.clearRect(0, 0, canvas.value.width, canvas.value.height);
|
||||
|
||||
// 绘制原始图片
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
ctx.drawImage(img, 0, 0);
|
||||
|
||||
// 设置水印样式
|
||||
ctx.font = `${fontSize.value}px Arial`;
|
||||
ctx.fillStyle = `${textColor.value}${Math.floor(opacity.value * 255).toString(16).padStart(2, '0')}`;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
|
||||
// 计算水印位置
|
||||
let x = canvas.value!.width / 2;
|
||||
let y = canvas.value!.height / 2;
|
||||
|
||||
switch (position.value) {
|
||||
case 'top-left':
|
||||
x = fontSize.value * 1.5;
|
||||
y = fontSize.value * 1.5;
|
||||
ctx.textAlign = 'left';
|
||||
ctx.textBaseline = 'top';
|
||||
break;
|
||||
case 'top-right':
|
||||
x = canvas.value!.width - fontSize.value * 1.5;
|
||||
y = fontSize.value * 1.5;
|
||||
ctx.textAlign = 'right';
|
||||
ctx.textBaseline = 'top';
|
||||
break;
|
||||
case 'bottom-left':
|
||||
x = fontSize.value * 1.5;
|
||||
y = canvas.value!.height - fontSize.value * 1.5;
|
||||
ctx.textAlign = 'left';
|
||||
ctx.textBaseline = 'bottom';
|
||||
break;
|
||||
case 'bottom-right':
|
||||
x = canvas.value!.width - fontSize.value * 1.5;
|
||||
y = canvas.value!.height - fontSize.value * 1.5;
|
||||
ctx.textAlign = 'right';
|
||||
ctx.textBaseline = 'bottom';
|
||||
break;
|
||||
}
|
||||
|
||||
// 绘制水印文字
|
||||
ctx.fillText(watermarkText.value, x, y);
|
||||
|
||||
// 生成带水印的图片URL
|
||||
watermarkedUrl.value = canvas.value!.toDataURL('image/png');
|
||||
};
|
||||
img.src = imageUrl.value;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 下载图片
|
||||
const downloadImage = () => {
|
||||
if (!watermarkedUrl.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = watermarkedUrl.value;
|
||||
link.download = `watermarked_${Date.now()}.png`;
|
||||
link.click();
|
||||
};
|
||||
|
||||
// 清空
|
||||
const clearImage = () => {
|
||||
imageUrl.value = '';
|
||||
watermarkedUrl.value = '';
|
||||
watermarkText.value = '水印';
|
||||
position.value = 'bottom-right';
|
||||
fontSize.value = 24;
|
||||
opacity.value = 0.5;
|
||||
textColor.value = '#ffffff';
|
||||
imageWidth.value = 0;
|
||||
imageHeight.value = 0;
|
||||
|
||||
if (canvas.value) {
|
||||
const ctx = canvas.value.getContext('2d');
|
||||
if (ctx) {
|
||||
ctx.clearRect(0, 0, canvas.value.width, canvas.value.height);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tool-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.settings-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.setting-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.preview-section {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.preview-section h3 {
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 8px;
|
||||
min-height: 400px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.image-wrapper {
|
||||
position: relative;
|
||||
max-width: 100%;
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
.original-image {
|
||||
max-width: 100%;
|
||||
max-height: 400px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.watermark-canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.no-image {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 100px 0;
|
||||
}
|
||||
|
||||
.download-section {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.tool-content {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.preview-section {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
</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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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 Token(jwt)并显示其内容。<br>
|
||||
<br>
|
||||
1. JWT 是 JSON Web Tokens 的缩写,是目前最流行的跨域认证解决方案,是一个开放式标准(RFC 7519),用于在各方之间以JSON对象安全传输信息。<br/>
|
||||
2. Header(头部)<br/>
|
||||
- typ(type): 用来标识整个token字符串是一个JWT字符串<br/>
|
||||
- alg(algorithm): 用来说明这个JWT签发的时候所使用的签名和摘要算法<br/>
|
||||
3. Payload(载荷)<br/>
|
||||
- iss (Issuer):签发者<br/>
|
||||
- sub (Subject):主题<br/>
|
||||
- aud (Audience):接收者<br/>
|
||||
- exp (Expiration time):过期时间<br/>
|
||||
- nbf (Not Before):生效时间<br/>
|
||||
- iat (Issued At):签发时间<br/>
|
||||
- jti (JWT ID):编号<br/>
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,11 +1,12 @@
|
||||
<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 +32,7 @@ import '@codemirror/commands';
|
||||
const info = reactive({
|
||||
title: "Json在线转换",
|
||||
code: '',
|
||||
extensions: [json(), lineNumbers()],
|
||||
extensions: [json()], //[json(), lineNumbers()],
|
||||
isParseErr: false,
|
||||
parseErr: ''
|
||||
})
|
||||
@@ -123,6 +124,14 @@ const copyRes = async () => {
|
||||
<el-text type="danger">{{ info.parseErr }}</el-text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。它基于JavaScript的一个子集,但与语言无关,因此在多种编程环境中广泛使用。JSON格式易于人阅读和编写,同时也易于机器解析和生成。它通常用于网络应用程序中服务器与客户端之间的数据传输。<br>
|
||||
JSON 工具提供实时编辑和预览JSON 数据,语法高亮、校验、格式化、转义,去转义、压缩等功能,可以提高阅读修改的效率和准确性
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div class="tool-content">
|
||||
<div class="input-section">
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="抽签选项(每行一个)">
|
||||
<el-input
|
||||
v-model="optionsText"
|
||||
type="textarea"
|
||||
:rows="8"
|
||||
placeholder="请输入抽签选项,每行一个"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="抽取设置">
|
||||
<div class="settings-grid">
|
||||
<el-form-item label="抽取数量" class="setting-item">
|
||||
<el-input-number v-model="drawCount" :min="1" :max="10" size="small" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="是否允许重复" class="setting-item">
|
||||
<el-switch v-model="allowRepeat" size="small" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="drawLottery">开始抽签</el-button>
|
||||
<el-button @click="clearOptions">清空</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="result-section">
|
||||
<h3>抽签结果</h3>
|
||||
<div v-if="results.length > 0" class="result-list">
|
||||
<div v-for="(result, index) in results" :key="index" class="result-item">
|
||||
<span class="result-number">{{ index + 1 }}</span>
|
||||
<span class="result-text">{{ result }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="no-result">
|
||||
点击开始抽签按钮开始抽取
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线抽签工具,输入多个选项后随机抽取一个或多个结果,支持设置抽取数量和是否允许重复抽取,可用于随机选择、抽奖等场景。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
|
||||
|
||||
const title = "抽签工具"
|
||||
|
||||
const optionsText = ref('');
|
||||
const drawCount = ref(1);
|
||||
const allowRepeat = ref(false);
|
||||
const results = ref<string[]>([]);
|
||||
|
||||
// 开始抽签
|
||||
const drawLottery = () => {
|
||||
// 解析输入的选项
|
||||
const options = optionsText.value
|
||||
.split('\n')
|
||||
.map(option => option.trim())
|
||||
.filter(option => option !== '');
|
||||
|
||||
if (options.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算实际抽取数量
|
||||
const actualDrawCount = allowRepeat.value
|
||||
? Math.min(drawCount.value, 10)
|
||||
: Math.min(drawCount.value, options.length);
|
||||
|
||||
// 执行抽签
|
||||
const newResults: string[] = [];
|
||||
const availableOptions = [...options];
|
||||
|
||||
for (let i = 0; i < actualDrawCount; i++) {
|
||||
if (availableOptions.length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
const randomIndex = Math.floor(Math.random() * availableOptions.length);
|
||||
const selectedOption = availableOptions[randomIndex];
|
||||
newResults.push(selectedOption);
|
||||
|
||||
// 如果不允许重复,从可用选项中移除
|
||||
if (!allowRepeat.value) {
|
||||
availableOptions.splice(randomIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
results.value = newResults;
|
||||
};
|
||||
|
||||
// 清空选项
|
||||
const clearOptions = () => {
|
||||
optionsText.value = '';
|
||||
results.value = [];
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tool-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.settings-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.setting-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.result-section {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.result-section h3 {
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.result-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
border-left: 4px solid #409eff;
|
||||
}
|
||||
|
||||
.result-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-color: #409eff;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
margin-right: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.result-text {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.no-result {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 40px 0;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.tool-content {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.result-section {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div class="tool-content">
|
||||
<div class="input-section">
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="m3u8地址">
|
||||
<el-input
|
||||
v-model="m3u8Url"
|
||||
placeholder="请输入m3u8格式的视频地址"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="loadVideo">加载视频</el-button>
|
||||
<el-button @click="clearUrl">清空</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="player-section">
|
||||
<div v-if="m3u8Url" class="player-container">
|
||||
<video
|
||||
ref="videoPlayer"
|
||||
class="video-js vjs-default-skin vjs-big-play-centered"
|
||||
controls
|
||||
preload="auto"
|
||||
width="100%"
|
||||
height="400"
|
||||
>
|
||||
<source :src="m3u8Url" type="application/x-mpegURL">
|
||||
您的浏览器不支持HTML5视频播放。
|
||||
</video>
|
||||
</div>
|
||||
<div v-else class="no-video">
|
||||
请输入m3u8地址并点击加载视频
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线m3u8播放器,支持播放m3u8格式的视频流,可输入自定义m3u8地址进行播放,适用于直播流和视频点播场景。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
|
||||
|
||||
const title = "m3u8在线播放"
|
||||
|
||||
const m3u8Url = ref('');
|
||||
const videoPlayer = ref<HTMLVideoElement | null>(null);
|
||||
|
||||
// 加载视频
|
||||
const loadVideo = () => {
|
||||
if (!m3u8Url.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 重新加载视频元素
|
||||
if (videoPlayer.value) {
|
||||
videoPlayer.value.load();
|
||||
}
|
||||
};
|
||||
|
||||
// 清空地址
|
||||
const clearUrl = () => {
|
||||
m3u8Url.value = '';
|
||||
};
|
||||
|
||||
// 示例地址
|
||||
const exampleUrl = 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8';
|
||||
|
||||
onMounted(() => {
|
||||
// 加载示例地址
|
||||
m3u8Url.value = exampleUrl;
|
||||
loadVideo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tool-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.player-section {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.player-container {
|
||||
width: 100%;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.no-video {
|
||||
text-align: center;
|
||||
color: #999;
|
||||
padding: 100px 0;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* 确保视频播放器占满容器 */
|
||||
:deep(.video-js) {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.tool-content {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.player-section {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</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 { 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>
|
||||
|
||||
@@ -0,0 +1,391 @@
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div class="tool-content">
|
||||
<div class="timer-section">
|
||||
<div class="timer-display">
|
||||
<div class="timer-circle">
|
||||
<svg width="200" height="200" viewBox="0 0 200 200">
|
||||
<!-- 背景圆环 -->
|
||||
<circle
|
||||
cx="100"
|
||||
cy="100"
|
||||
r="90"
|
||||
fill="none"
|
||||
stroke="#e4e7ed"
|
||||
stroke-width="10"
|
||||
/>
|
||||
<!-- 进度圆环 -->
|
||||
<circle
|
||||
cx="100"
|
||||
cy="100"
|
||||
r="90"
|
||||
fill="none"
|
||||
stroke="#409eff"
|
||||
stroke-width="10"
|
||||
stroke-linecap="round"
|
||||
:stroke-dasharray="circumference"
|
||||
:stroke-dashoffset="progressOffset"
|
||||
transform="rotate(-90 100 100)"
|
||||
/>
|
||||
</svg>
|
||||
<div class="timer-text">
|
||||
{{ formattedTime }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="timer-controls">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="startTimer"
|
||||
:disabled="isRunning"
|
||||
>
|
||||
开始
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
@click="pauseTimer"
|
||||
:disabled="!isRunning"
|
||||
>
|
||||
暂停
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
@click="resetTimer"
|
||||
>
|
||||
重置
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<h3>时间设置(分钟)</h3>
|
||||
<el-form label-position="top" class="settings-form">
|
||||
<el-form-item label="工作时间">
|
||||
<el-input-number
|
||||
v-model="workTime"
|
||||
:min="1"
|
||||
:max="60"
|
||||
:step="5"
|
||||
:disabled="isRunning"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="短休息">
|
||||
<el-input-number
|
||||
v-model="shortBreakTime"
|
||||
:min="1"
|
||||
:max="30"
|
||||
:step="5"
|
||||
:disabled="isRunning"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="长休息">
|
||||
<el-input-number
|
||||
v-model="longBreakTime"
|
||||
:min="1"
|
||||
:max="60"
|
||||
:step="5"
|
||||
:disabled="isRunning"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="工作轮数">
|
||||
<el-input-number
|
||||
v-model="workRounds"
|
||||
:min="1"
|
||||
:max="10"
|
||||
:disabled="isRunning"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="status-section">
|
||||
<h3>状态</h3>
|
||||
<div class="status-info">
|
||||
<div class="status-item">
|
||||
<span class="status-label">当前模式:</span>
|
||||
<span class="status-value">{{ currentModeText }}</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-label">已完成轮数:</span>
|
||||
<span class="status-value">{{ completedRounds }}/{{ workRounds }}</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-label">今日完成:</span>
|
||||
<span class="status-value">{{ todayCompleted }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线番茄时钟工具,用于时间管理,支持工作和休息模式切换,可自定义工作时间、休息时间和工作轮数,帮助提高工作效率和专注力。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
|
||||
|
||||
const title = "番茄时钟"
|
||||
|
||||
// 时间设置
|
||||
const workTime = ref(25);
|
||||
const shortBreakTime = ref(5);
|
||||
const longBreakTime = ref(15);
|
||||
const workRounds = ref(4);
|
||||
|
||||
// 状态
|
||||
const isRunning = ref(false);
|
||||
const currentTime = ref(workTime.value * 60);
|
||||
const currentMode = ref<'work' | 'shortBreak' | 'longBreak'>('work');
|
||||
const completedRounds = ref(0);
|
||||
const todayCompleted = ref(0);
|
||||
const timerInterval = ref<number | null>(null);
|
||||
|
||||
// 计算属性
|
||||
const circumference = computed(() => 2 * Math.PI * 90);
|
||||
|
||||
const progressOffset = computed(() => {
|
||||
const totalTime = currentMode.value === 'work' ? workTime.value * 60 :
|
||||
currentMode.value === 'shortBreak' ? shortBreakTime.value * 60 :
|
||||
longBreakTime.value * 60;
|
||||
const progress = currentTime.value / totalTime;
|
||||
return circumference.value * (1 - progress);
|
||||
});
|
||||
|
||||
const formattedTime = computed(() => {
|
||||
const minutes = Math.floor(currentTime.value / 60);
|
||||
const seconds = currentTime.value % 60;
|
||||
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||||
});
|
||||
|
||||
const currentModeText = computed(() => {
|
||||
switch (currentMode.value) {
|
||||
case 'work': return '工作';
|
||||
case 'shortBreak': return '短休息';
|
||||
case 'longBreak': return '长休息';
|
||||
default: return '工作';
|
||||
}
|
||||
});
|
||||
|
||||
// 开始计时器
|
||||
const startTimer = () => {
|
||||
if (isRunning.value) return;
|
||||
|
||||
isRunning.value = true;
|
||||
timerInterval.value = window.setInterval(() => {
|
||||
if (currentTime.value > 0) {
|
||||
currentTime.value--;
|
||||
} else {
|
||||
// 时间到
|
||||
clearInterval(timerInterval.value!);
|
||||
isRunning.value = false;
|
||||
|
||||
// 播放提示音
|
||||
playNotification();
|
||||
|
||||
// 切换模式
|
||||
switchMode();
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
// 暂停计时器
|
||||
const pauseTimer = () => {
|
||||
if (!isRunning.value) return;
|
||||
|
||||
clearInterval(timerInterval.value!);
|
||||
isRunning.value = false;
|
||||
};
|
||||
|
||||
// 重置计时器
|
||||
const resetTimer = () => {
|
||||
clearInterval(timerInterval.value!);
|
||||
isRunning.value = false;
|
||||
currentTime.value = workTime.value * 60;
|
||||
currentMode.value = 'work';
|
||||
completedRounds.value = 0;
|
||||
};
|
||||
|
||||
// 切换模式
|
||||
const switchMode = () => {
|
||||
if (currentMode.value === 'work') {
|
||||
completedRounds.value++;
|
||||
todayCompleted.value++;
|
||||
|
||||
if (completedRounds.value % workRounds.value === 0) {
|
||||
// 长休息
|
||||
currentMode.value = 'longBreak';
|
||||
currentTime.value = longBreakTime.value * 60;
|
||||
} else {
|
||||
// 短休息
|
||||
currentMode.value = 'shortBreak';
|
||||
currentTime.value = shortBreakTime.value * 60;
|
||||
}
|
||||
} else {
|
||||
// 回到工作模式
|
||||
currentMode.value = 'work';
|
||||
currentTime.value = workTime.value * 60;
|
||||
}
|
||||
|
||||
ElMessage.success(`切换到${currentModeText.value}模式`);
|
||||
};
|
||||
|
||||
// 播放提示音
|
||||
const playNotification = () => {
|
||||
try {
|
||||
const audio = new Audio('data:audio/wav;base64,UklGRigAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQQAAAAA');
|
||||
audio.play();
|
||||
} catch (e) {
|
||||
console.log('无法播放提示音');
|
||||
}
|
||||
};
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
// 从本地存储加载今日完成数
|
||||
const today = new Date().toDateString();
|
||||
const storedData = localStorage.getItem('pomodoro');
|
||||
if (storedData) {
|
||||
const data = JSON.parse(storedData);
|
||||
if (data.date === today) {
|
||||
todayCompleted.value = data.completed;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// 保存今日完成数到本地存储
|
||||
const today = new Date().toDateString();
|
||||
localStorage.setItem('pomodoro', JSON.stringify({
|
||||
date: today,
|
||||
completed: todayCompleted.value
|
||||
}));
|
||||
|
||||
// 清除计时器
|
||||
if (timerInterval.value) {
|
||||
clearInterval(timerInterval.value);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tool-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.timer-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.timer-display {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.timer-circle {
|
||||
position: relative;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.timer-text {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.timer-controls {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.settings-section h3 {
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.settings-form {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.status-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.status-section h3 {
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.status-info {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.status-item {
|
||||
padding: 15px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.status-label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.status-value {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.tool-content {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.timer-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.status-section {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -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'
|
||||
@@ -62,31 +63,33 @@ const gen = () => {
|
||||
<div class="w-4/6 ">
|
||||
<div class="flex mb-3">
|
||||
<div class="w-16 mr-2"><el-text>内容</el-text></div>
|
||||
<el-input v-model="info.content" type="textarea" rows="3" class="w-full" placeholder="可在此输入文字或网址"></el-input>
|
||||
<el-input v-model="info.content" type="textarea" :rows="Number(3)" class="w-full" placeholder="可在此输入文字或网址"></el-input>
|
||||
</div>
|
||||
<div class="flex mb-3">
|
||||
<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-option label="常规 200px" value="200" />
|
||||
<el-option label="常规 300px" value="300" />
|
||||
<el-option label="适中 350px" value="350" />
|
||||
<el-option label="较大 500px" value="500" />
|
||||
<!-- 分割线 start -->
|
||||
<div class="flex justify-center bg-gray-200 w-full">
|
||||
<div class="h-[1px] bg-gray-200 w-4/5"></div>
|
||||
</div>
|
||||
<!-- 分割线 end -->
|
||||
<el-option label="超大 1000px" value="1000" />
|
||||
<el-option label="超大 1200px" value="1200" />
|
||||
</el-select>
|
||||
<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" />
|
||||
<el-option label="较大 500px" value="500" />
|
||||
<!-- 分割线 start -->
|
||||
<div class="flex justify-center bg-gray-200 w-full">
|
||||
<div class="h-[1px] bg-gray-200 w-4/5"></div>
|
||||
</div>
|
||||
<!-- 分割线 end -->
|
||||
<el-option label="超大 1000px" value="1000" />
|
||||
<el-option label="超大 1200px" value="1200" />
|
||||
</el-select>
|
||||
</template>
|
||||
</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>
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div class="tool-content">
|
||||
<div class="test-section">
|
||||
<div
|
||||
ref="testArea"
|
||||
class="test-area"
|
||||
:class="testStatus"
|
||||
@click="handleTestClick"
|
||||
>
|
||||
<div v-if="testStatus === 'ready'" class="test-message">
|
||||
点击开始测试
|
||||
</div>
|
||||
<div v-else-if="testStatus === 'waiting'" class="test-message">
|
||||
准备就绪,等待变色...
|
||||
</div>
|
||||
<div v-else-if="testStatus === 'active'" class="test-message">
|
||||
点击!
|
||||
</div>
|
||||
<div v-else-if="testStatus === 'completed'" class="test-message">
|
||||
反应时间: {{ reactionTime }} ms
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="test-controls">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="startTest"
|
||||
:disabled="testStatus === 'waiting' || testStatus === 'active'"
|
||||
>
|
||||
开始测试
|
||||
</el-button>
|
||||
<el-button @click="resetTest">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats-section">
|
||||
<h3>测试统计</h3>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">最佳反应时间</div>
|
||||
<div class="stat-value">{{ bestTime }} ms</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">平均反应时间</div>
|
||||
<div class="stat-value">{{ averageTime }} ms</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">测试次数</div>
|
||||
<div class="stat-value">{{ testCount }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="history-section">
|
||||
<h4>测试历史</h4>
|
||||
<div class="history-list">
|
||||
<div
|
||||
v-for="(time, index) in testHistory"
|
||||
:key="index"
|
||||
class="history-item"
|
||||
>
|
||||
测试 {{ index + 1 }}: {{ time }} ms
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线反应速度测试工具,通过点击变色的方块来测试你的反应速度,记录测试历史和统计数据,帮助你了解自己的反应能力。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
|
||||
|
||||
const title = "反应速度测试"
|
||||
|
||||
// 移除未使用的 testArea 引用
|
||||
const testStatus = ref<'ready' | 'waiting' | 'active' | 'completed'>('ready');
|
||||
const reactionTime = ref(0);
|
||||
const testHistory = ref<number[]>([]);
|
||||
const startTime = ref(0);
|
||||
const waitingTimer = ref<number | null>(null);
|
||||
|
||||
// 计算属性
|
||||
const testCount = computed(() => testHistory.value.length);
|
||||
|
||||
const bestTime = computed(() => {
|
||||
if (testHistory.value.length === 0) return 0;
|
||||
return Math.min(...testHistory.value);
|
||||
});
|
||||
|
||||
const averageTime = computed(() => {
|
||||
if (testHistory.value.length === 0) return 0;
|
||||
const sum = testHistory.value.reduce((acc, time) => acc + time, 0);
|
||||
return Math.round(sum / testHistory.value.length);
|
||||
});
|
||||
|
||||
// 开始测试
|
||||
const startTest = () => {
|
||||
testStatus.value = 'waiting';
|
||||
|
||||
// 随机等待时间(1-5秒)
|
||||
const waitTime = 1000 + Math.random() * 4000;
|
||||
|
||||
waitingTimer.value = window.setTimeout(() => {
|
||||
testStatus.value = 'active';
|
||||
startTime.value = performance.now();
|
||||
}, waitTime);
|
||||
};
|
||||
|
||||
// 处理测试区域点击
|
||||
const handleTestClick = () => {
|
||||
if (testStatus.value === 'ready') {
|
||||
startTest();
|
||||
} else if (testStatus.value === 'active') {
|
||||
const endTime = performance.now();
|
||||
reactionTime.value = Math.round(endTime - startTime.value);
|
||||
testHistory.value.push(reactionTime.value);
|
||||
testStatus.value = 'completed';
|
||||
}
|
||||
};
|
||||
|
||||
// 重置测试
|
||||
const resetTest = () => {
|
||||
if (waitingTimer.value) {
|
||||
clearTimeout(waitingTimer.value);
|
||||
}
|
||||
testStatus.value = 'ready';
|
||||
reactionTime.value = 0;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tool-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.test-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.test-area {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
border: 2px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.test-area.ready {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.test-area.waiting {
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
.test-area.active {
|
||||
background-color: #f0f9eb;
|
||||
border-color: #67c23a;
|
||||
box-shadow: 0 0 20px rgba(103, 194, 58, 0.5);
|
||||
}
|
||||
|
||||
.test-area.completed {
|
||||
background-color: #fef0f0;
|
||||
border-color: #f56c6c;
|
||||
}
|
||||
|
||||
.test-message {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.test-controls {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.stats-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.stats-section h3 {
|
||||
margin-bottom: 15px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.history-section {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.history-section h4 {
|
||||
margin-bottom: 10px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.history-list {
|
||||
background-color: #f9f9f9;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.history-item {
|
||||
margin-bottom: 10px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.history-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.tool-content {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.test-section {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.stats-section {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -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: `一些测试实例:
|
||||
邮箱1:demo@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>
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div class="tool-content">
|
||||
<div class="game-section">
|
||||
<div class="player-section">
|
||||
<h3>你的选择</h3>
|
||||
<div class="choices">
|
||||
<div
|
||||
v-for="choice in choices"
|
||||
:key="choice.value"
|
||||
class="choice-item"
|
||||
:class="{ active: selectedChoice === choice.value }"
|
||||
@click="makeChoice(choice.value)"
|
||||
>
|
||||
<div class="choice-icon">{{ choice.icon }}</div>
|
||||
<div class="choice-name">{{ choice.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="result-section">
|
||||
<h3>对战结果</h3>
|
||||
<div v-if="gameResult" class="result-content">
|
||||
<div class="result-message">{{ gameResult.message }}</div>
|
||||
<div class="result-details">
|
||||
<div class="player-result">你: {{ getChoiceName(selectedChoice) }}</div>
|
||||
<div class="computer-result">电脑: {{ getChoiceName(computerChoice) }}</div>
|
||||
</div>
|
||||
<el-button type="primary" @click="resetGame">再来一局</el-button>
|
||||
</div>
|
||||
<div v-else class="no-result">
|
||||
请选择你的武器开始游戏
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="computer-section">
|
||||
<h3>电脑选择</h3>
|
||||
<div class="computer-choice">
|
||||
<div v-if="computerChoice" class="choice-item computer">
|
||||
<div class="choice-icon">{{ getChoiceIcon(computerChoice) }}</div>
|
||||
<div class="choice-name">{{ getChoiceName(computerChoice) }}</div>
|
||||
</div>
|
||||
<div v-else class="waiting">
|
||||
等待中...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stats-section">
|
||||
<h3>游戏统计</h3>
|
||||
<div class="stats-grid">
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">总游戏数</div>
|
||||
<div class="stat-value">{{ stats.total }}</div>
|
||||
</div>
|
||||
<div class="stat-item win">
|
||||
<div class="stat-label">胜利</div>
|
||||
<div class="stat-value">{{ stats.wins }}</div>
|
||||
</div>
|
||||
<div class="stat-item lose">
|
||||
<div class="stat-label">失败</div>
|
||||
<div class="stat-value">{{ stats.losses }}</div>
|
||||
</div>
|
||||
<div class="stat-item draw">
|
||||
<div class="stat-label">平局</div>
|
||||
<div class="stat-value">{{ stats.draws }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-button @click="resetStats">重置统计</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线剪刀石头布游戏,与电脑对战,支持实时显示对战结果和游戏统计,可用于休闲娱乐和决策参考。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
|
||||
|
||||
const title = "剪刀石头布"
|
||||
|
||||
const choices = [
|
||||
{ name: '剪刀', value: 'scissors', icon: '✂️' },
|
||||
{ name: '石头', value: 'rock', icon: '🪨' },
|
||||
{ name: '布', value: 'paper', icon: '📄' }
|
||||
];
|
||||
|
||||
const selectedChoice = ref<string>('');
|
||||
const computerChoice = ref<string>('');
|
||||
const gameResult = ref<{ message: string; result: 'win' | 'lose' | 'draw' } | null>(null);
|
||||
|
||||
const stats = ref({
|
||||
total: 0,
|
||||
wins: 0,
|
||||
losses: 0,
|
||||
draws: 0
|
||||
});
|
||||
|
||||
// 生成电脑的选择
|
||||
const generateComputerChoice = () => {
|
||||
const randomIndex = Math.floor(Math.random() * choices.length);
|
||||
return choices[randomIndex].value;
|
||||
};
|
||||
|
||||
// 判断游戏结果
|
||||
const determineWinner = (player: string, computer: string) => {
|
||||
if (player === computer) {
|
||||
return { message: '平局!', result: 'draw' as const };
|
||||
}
|
||||
|
||||
if (
|
||||
(player === 'rock' && computer === 'scissors') ||
|
||||
(player === 'scissors' && computer === 'paper') ||
|
||||
(player === 'paper' && computer === 'rock')
|
||||
) {
|
||||
return { message: '你赢了!', result: 'win' as const };
|
||||
}
|
||||
|
||||
return { message: '你输了!', result: 'lose' as const };
|
||||
};
|
||||
|
||||
// 玩家选择
|
||||
const makeChoice = (choice: string) => {
|
||||
if (gameResult.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
selectedChoice.value = choice;
|
||||
computerChoice.value = generateComputerChoice();
|
||||
gameResult.value = determineWinner(choice, computerChoice.value);
|
||||
|
||||
// 更新统计
|
||||
stats.value.total++;
|
||||
if (gameResult.value.result === 'win') {
|
||||
stats.value.wins++;
|
||||
} else if (gameResult.value.result === 'lose') {
|
||||
stats.value.losses++;
|
||||
} else {
|
||||
stats.value.draws++;
|
||||
}
|
||||
};
|
||||
|
||||
// 重置游戏
|
||||
const resetGame = () => {
|
||||
selectedChoice.value = '';
|
||||
computerChoice.value = '';
|
||||
gameResult.value = null;
|
||||
};
|
||||
|
||||
// 重置统计
|
||||
const resetStats = () => {
|
||||
stats.value = {
|
||||
total: 0,
|
||||
wins: 0,
|
||||
losses: 0,
|
||||
draws: 0
|
||||
};
|
||||
resetGame();
|
||||
};
|
||||
|
||||
// 获取选择的名称
|
||||
const getChoiceName = (choice: string) => {
|
||||
const found = choices.find(c => c.value === choice);
|
||||
return found ? found.name : '';
|
||||
};
|
||||
|
||||
// 获取选择的图标
|
||||
const getChoiceIcon = (choice: string) => {
|
||||
const found = choices.find(c => c.value === choice);
|
||||
return found ? found.icon : '';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tool-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.game-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.player-section, .computer-section {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.choices {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.choice-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
border: 2px solid #e4e7ed;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.choice-item:hover {
|
||||
border-color: #409eff;
|
||||
box-shadow: 0 0 10px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
.choice-item.active {
|
||||
border-color: #409eff;
|
||||
background-color: #ecf5ff;
|
||||
box-shadow: 0 0 10px rgba(64, 158, 255, 0.5);
|
||||
}
|
||||
|
||||
.choice-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.choice-name {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.computer-choice {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.computer-choice .choice-item {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.waiting {
|
||||
padding: 40px;
|
||||
font-size: 16px;
|
||||
color: #999;
|
||||
border: 2px dashed #e4e7ed;
|
||||
border-radius: 8px;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.result-section {
|
||||
text-align: center;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
padding: 30px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.result-message {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.result-details {
|
||||
margin-bottom: 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.player-result, .computer-result {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.no-result {
|
||||
padding: 40px;
|
||||
font-size: 16px;
|
||||
color: #999;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.stats-section {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-item.win {
|
||||
background-color: #f0f9eb;
|
||||
border: 1px solid #c2e7b0;
|
||||
}
|
||||
|
||||
.stat-item.lose {
|
||||
background-color: #fef0f0;
|
||||
border: 1px solid #fbc4c4;
|
||||
}
|
||||
|
||||
.stat-item.draw {
|
||||
background-color: #f0f0f0;
|
||||
border: 1px solid #d9d9d9;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.game-section {
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.player-section, .computer-section {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.result-section {
|
||||
width: 40%;
|
||||
}
|
||||
}
|
||||
</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 { 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>
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
<script setup lang="ts">
|
||||
import { 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 { copy } from '@/utils/string'
|
||||
import { format } from 'sql-formatter'
|
||||
|
||||
const title = "SQL格式化"
|
||||
|
||||
// 状态管理
|
||||
const inputText = ref('')
|
||||
const outputText = ref('')
|
||||
const indentSize = ref(4)
|
||||
|
||||
// 格式化SQL
|
||||
const formatSql = () => {
|
||||
if (!inputText.value) {
|
||||
ElMessage.warning('请输入要格式化的SQL')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用sql-formatter库格式化SQL
|
||||
const formatted = format(inputText.value)
|
||||
outputText.value = formatted
|
||||
ElMessage.success('格式化成功')
|
||||
} catch (error) {
|
||||
outputText.value = '格式化失败'
|
||||
ElMessage.error('格式化失败:' + error)
|
||||
}
|
||||
}
|
||||
|
||||
// 压缩SQL
|
||||
const minifySql = () => {
|
||||
if (!inputText.value) {
|
||||
ElMessage.warning('请输入要压缩的SQL')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 压缩SQL(去除空格和换行)
|
||||
const minified = inputText.value
|
||||
.replace(/\s+/g, ' ') // 替换多个空白字符为单个空格
|
||||
.trim()
|
||||
|
||||
outputText.value = minified
|
||||
ElMessage.success('压缩成功')
|
||||
} catch (error) {
|
||||
outputText.value = '压缩失败'
|
||||
ElMessage.error('压缩失败:' + error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 复制结果
|
||||
const copyResult = () => {
|
||||
if (!outputText.value || outputText.value === '格式化失败' || outputText.value === '压缩失败') {
|
||||
ElMessage.warning('没有可复制的内容')
|
||||
return
|
||||
}
|
||||
|
||||
copy(outputText.value)
|
||||
}
|
||||
|
||||
// 清空输入
|
||||
const clearInput = () => {
|
||||
inputText.value = ''
|
||||
outputText.value = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<!-- 输入部分 -->
|
||||
<div class="mb-4">
|
||||
<el-input
|
||||
v-model="inputText"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
placeholder="请输入要格式化的SQL"
|
||||
class="mb-2"
|
||||
/>
|
||||
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<el-text>缩进大小:</el-text>
|
||||
<el-input-number v-model="indentSize" :min="1" :max="8" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="mb-4">
|
||||
<el-button type="primary" @click="formatSql">格式化</el-button>
|
||||
<el-button type="success" @click="minifySql">压缩</el-button>
|
||||
<el-button @click="clearInput">清空</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 结果部分 -->
|
||||
<div>
|
||||
<el-input
|
||||
v-model="outputText"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
placeholder="格式化结果"
|
||||
readonly
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="copyResult">复制</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线SQL格式化工具,用于美化和压缩SQL代码,支持自定义缩进大小,使SQL代码更易于阅读和维护。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,205 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } 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'
|
||||
|
||||
const title = "数据存储单位换算"
|
||||
|
||||
// 状态管理
|
||||
const inputValue = ref('')
|
||||
const fromUnit = ref('B')
|
||||
const toUnit = ref('KB')
|
||||
|
||||
// 存储单位选项
|
||||
const unitOptions = [
|
||||
{ label: 'bit (比特)', value: 'bit' },
|
||||
{ label: 'B (字节)', value: 'B' },
|
||||
{ label: 'KB (千字节)', value: 'KB' },
|
||||
{ label: 'MB (兆字节)', value: 'MB' },
|
||||
{ label: 'GB (吉字节)', value: 'GB' },
|
||||
{ label: 'TB (太字节)', value: 'TB' },
|
||||
{ label: 'PB (拍字节)', value: 'PB' },
|
||||
{ label: 'EB (艾字节)', value: 'EB' }
|
||||
]
|
||||
|
||||
// 单位转换系数
|
||||
const unitFactors = {
|
||||
'bit': 1,
|
||||
'B': 8, // 1字节 = 8比特
|
||||
'KB': 8 * 1024,
|
||||
'MB': 8 * 1024 * 1024,
|
||||
'GB': 8 * 1024 * 1024 * 1024,
|
||||
'TB': 8 * 1024 * 1024 * 1024 * 1024,
|
||||
'PB': 8 * 1024 * 1024 * 1024 * 1024 * 1024,
|
||||
'EB': 8 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024
|
||||
}
|
||||
|
||||
// 单个转换结果
|
||||
const convertedValue = computed(() => {
|
||||
if (!inputValue.value) return ''
|
||||
|
||||
const value = parseFloat(inputValue.value)
|
||||
if (isNaN(value)) return '无效输入'
|
||||
|
||||
try {
|
||||
// 先转换为字节
|
||||
const bytes = value * unitFactors[fromUnit.value]
|
||||
// 再转换为目标单位
|
||||
const result = bytes / unitFactors[toUnit.value]
|
||||
|
||||
// 检查小数点后的位数
|
||||
const resultStr = result.toString()
|
||||
const decimalIndex = resultStr.indexOf('.')
|
||||
|
||||
if (decimalIndex !== -1 && resultStr.length - decimalIndex - 1 > 5) {
|
||||
// 小数点超过5位,使用科学计数法
|
||||
return result.toExponential(5)
|
||||
} else {
|
||||
// 否则使用固定小数位
|
||||
return result.toFixed(6)
|
||||
}
|
||||
} catch (error) {
|
||||
return '转换失败'
|
||||
}
|
||||
})
|
||||
|
||||
// 所有单位的转换结果
|
||||
const allConvertedValues = computed(() => {
|
||||
if (!inputValue.value) return []
|
||||
|
||||
const value = parseFloat(inputValue.value)
|
||||
if (isNaN(value)) return []
|
||||
|
||||
try {
|
||||
// 先转换为字节
|
||||
const bytes = value * unitFactors[fromUnit.value]
|
||||
|
||||
// 计算所有单位的转换结果
|
||||
return unitOptions.map(option => {
|
||||
const result = bytes / unitFactors[option.value]
|
||||
|
||||
// 检查小数点后的位数
|
||||
const resultStr = result.toString()
|
||||
const decimalIndex = resultStr.indexOf('.')
|
||||
|
||||
let formattedResult
|
||||
if (decimalIndex !== -1 && resultStr.length - decimalIndex - 1 > 5) {
|
||||
// 小数点超过5位,使用科学计数法
|
||||
formattedResult = result.toExponential(5)
|
||||
} else {
|
||||
// 否则使用固定小数位
|
||||
formattedResult = result.toFixed(6)
|
||||
}
|
||||
|
||||
return {
|
||||
unit: option.label,
|
||||
value: formattedResult
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
return []
|
||||
}
|
||||
})
|
||||
|
||||
// 复制结果
|
||||
const copyResult = () => {
|
||||
if (!convertedValue.value || convertedValue.value === '无效输入' || convertedValue.value === '转换失败') {
|
||||
ElMessage.warning('没有可复制的内容')
|
||||
return
|
||||
}
|
||||
|
||||
copy(convertedValue.value)
|
||||
}
|
||||
|
||||
// 清空输入
|
||||
const clearInput = () => {
|
||||
inputValue.value = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<!-- 输入部分 -->
|
||||
<div class="mb-4">
|
||||
<el-input
|
||||
v-model="inputValue"
|
||||
type="number"
|
||||
placeholder="请输入数值"
|
||||
class="mb-2"
|
||||
/>
|
||||
|
||||
<div class="flex gap-4 items-center">
|
||||
<div class="flex items-center gap-2">
|
||||
<el-text>从单位:</el-text>
|
||||
<el-select v-model="fromUnit" style="width: 140px;">
|
||||
<el-option
|
||||
v-for="option in unitOptions"
|
||||
:key="option.value"
|
||||
:label="option.label"
|
||||
:value="option.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<el-text>到单位:</el-text>
|
||||
<el-select v-model="toUnit" style="width: 140px;">
|
||||
<el-option
|
||||
v-for="option in unitOptions"
|
||||
:key="option.value"
|
||||
:label="option.label"
|
||||
:value="option.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="mb-4">
|
||||
<el-button type="primary" @click="clearInput">清空</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 结果部分 -->
|
||||
<div class="mb-4">
|
||||
<el-input
|
||||
:value="convertedValue"
|
||||
placeholder="转换结果"
|
||||
readonly
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="copyResult">复制</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<!-- 所有单位转换结果 -->
|
||||
<div v-if="allConvertedValues.length > 0">
|
||||
<el-text class="block mb-2">所有单位转换结果:</el-text>
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-2">
|
||||
<div v-for="item in allConvertedValues" :key="item.unit" class="bg-gray-50 p-2 rounded">
|
||||
<div class="text-sm text-gray-500">{{ item.unit }}</div>
|
||||
<div class="font-medium">{{ item.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线数据存储单位换算工具,支持比特(bit)、字节(B)、KB、MB、GB、TB、PB、EB等存储单位之间的相互转换。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -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>
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } 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'
|
||||
|
||||
const title = "文本替换"
|
||||
|
||||
// 状态管理
|
||||
const inputText = ref('')
|
||||
const findText = ref('')
|
||||
const replaceText = ref('')
|
||||
const useRegex = ref(false)
|
||||
const caseSensitive = ref(true)
|
||||
|
||||
// 替换结果
|
||||
const replaceResult = computed(() => {
|
||||
if (!inputText.value) return ''
|
||||
if (!findText.value) return inputText.value
|
||||
|
||||
try {
|
||||
if (useRegex.value) {
|
||||
// 使用正则表达式替换
|
||||
const regex = new RegExp(findText.value, caseSensitive.value ? 'g' : 'gi')
|
||||
return inputText.value.replace(regex, replaceText.value)
|
||||
} else {
|
||||
// 使用普通文本替换
|
||||
if (caseSensitive.value) {
|
||||
return inputText.value.replace(new RegExp(findText.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), replaceText.value)
|
||||
} else {
|
||||
return inputText.value.replace(new RegExp(findText.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'), replaceText.value)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
return '替换失败:正则表达式语法错误'
|
||||
}
|
||||
})
|
||||
|
||||
// 复制结果
|
||||
const copyResult = () => {
|
||||
if (!replaceResult.value || replaceResult.value === '替换失败:正则表达式语法错误') {
|
||||
ElMessage.warning('没有可复制的内容')
|
||||
return
|
||||
}
|
||||
|
||||
copy(replaceResult.value)
|
||||
}
|
||||
|
||||
// 清空输入
|
||||
const clearInput = () => {
|
||||
inputText.value = ''
|
||||
findText.value = ''
|
||||
replaceText.value = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<!-- 输入部分 -->
|
||||
<div class="mb-4">
|
||||
<el-input
|
||||
v-model="inputText"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
placeholder="请输入要处理的文本"
|
||||
class="mb-2"
|
||||
/>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-2">
|
||||
<div>
|
||||
<el-input
|
||||
v-model="findText"
|
||||
placeholder="请输入要查找的文本"
|
||||
class="mb-2"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<el-input
|
||||
v-model="replaceText"
|
||||
placeholder="请输入要替换的文本"
|
||||
class="mb-2"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4 mb-2">
|
||||
<el-checkbox v-model="useRegex">使用正则表达式</el-checkbox>
|
||||
<el-checkbox v-model="caseSensitive">区分大小写</el-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="mb-4">
|
||||
<el-button @click="copyResult">复制结果</el-button>
|
||||
<el-button @click="clearInput">清空</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 结果部分 -->
|
||||
<div>
|
||||
<el-input
|
||||
:value="replaceResult"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
placeholder="替换结果"
|
||||
readonly
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="copyResult">复制</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线文本替换工具,支持普通文本和正则表达式替换,可用于批量修改文本内容,支持区分大小写选项。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -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>
|
||||
把文本转换成图片,生成长图,富文本自定义文字排版,可导出png,jpeg格式,可更换背景图,设置宽度,是好用的文本转图片工具
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.downext-select{
|
||||
@apply w-24
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,232 @@
|
||||
<script setup lang="ts">
|
||||
import { 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 { ArrowDown, ArrowUp, Delete } from '@element-plus/icons-vue'
|
||||
import { copy } from '@/utils/string'
|
||||
|
||||
const title = "文本处理工作流"
|
||||
|
||||
// 状态管理
|
||||
const inputText = ref('')
|
||||
const workflowSteps = ref([
|
||||
{ id: 1, type: 'trim', name: '去除首尾空格' },
|
||||
{ id: 2, type: 'lowercase', name: '转为小写' }
|
||||
])
|
||||
const outputText = ref('')
|
||||
|
||||
// 可用的处理步骤类型
|
||||
const availableSteps = [
|
||||
{ type: 'trim', name: '去除首尾空格' },
|
||||
{ type: 'lowercase', name: '转为小写' },
|
||||
{ type: 'uppercase', name: '转为大写' },
|
||||
{ type: 'removeEmptyLines', name: '移除空行' },
|
||||
{ type: 'trimLines', name: '去除每行首尾空格' },
|
||||
{ type: 'sort', name: '按字母排序' }
|
||||
]
|
||||
|
||||
// 执行工作流
|
||||
const executeWorkflow = () => {
|
||||
if (!inputText.value) {
|
||||
ElMessage.warning('请输入要处理的文本')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
let result = inputText.value
|
||||
|
||||
// 按顺序执行每个步骤
|
||||
workflowSteps.value.forEach(step => {
|
||||
result = processText(result, step.type)
|
||||
})
|
||||
|
||||
outputText.value = result
|
||||
ElMessage.success('工作流执行成功')
|
||||
} catch (error) {
|
||||
outputText.value = '执行失败'
|
||||
ElMessage.error('执行失败:' + error)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理文本
|
||||
const processText = (text: string, type: string): string => {
|
||||
switch (type) {
|
||||
case 'trim':
|
||||
return text.trim()
|
||||
case 'lowercase':
|
||||
return text.toLowerCase()
|
||||
case 'uppercase':
|
||||
return text.toUpperCase()
|
||||
case 'removeEmptyLines':
|
||||
return text.split('\n').filter(line => line.trim() !== '').join('\n')
|
||||
case 'trimLines':
|
||||
return text.split('\n').map(line => line.trim()).join('\n')
|
||||
case 'sort':
|
||||
return text.split('\n').filter(line => line.trim() !== '').sort().join('\n')
|
||||
default:
|
||||
return text
|
||||
}
|
||||
}
|
||||
|
||||
// 添加步骤
|
||||
const addStep = (type: string, name: string) => {
|
||||
const newId = Math.max(...workflowSteps.value.map(step => step.id), 0) + 1
|
||||
workflowSteps.value.push({ id: newId, type, name })
|
||||
}
|
||||
|
||||
// 删除步骤
|
||||
const removeStep = (id: number) => {
|
||||
workflowSteps.value = workflowSteps.value.filter(step => step.id !== id)
|
||||
}
|
||||
|
||||
// 上移步骤
|
||||
const moveStepUp = (index: number) => {
|
||||
if (index > 0) {
|
||||
const temp = workflowSteps.value[index]
|
||||
workflowSteps.value[index] = workflowSteps.value[index - 1]
|
||||
workflowSteps.value[index - 1] = temp
|
||||
}
|
||||
}
|
||||
|
||||
// 下移步骤
|
||||
const moveStepDown = (index: number) => {
|
||||
if (index < workflowSteps.value.length - 1) {
|
||||
const temp = workflowSteps.value[index]
|
||||
workflowSteps.value[index] = workflowSteps.value[index + 1]
|
||||
workflowSteps.value[index + 1] = temp
|
||||
}
|
||||
}
|
||||
|
||||
// 复制结果
|
||||
const copyResult = () => {
|
||||
if (!outputText.value || outputText.value === '执行失败') {
|
||||
ElMessage.warning('没有可复制的内容')
|
||||
return
|
||||
}
|
||||
|
||||
copy(outputText.value)
|
||||
}
|
||||
|
||||
// 清空输入
|
||||
const clearInput = () => {
|
||||
inputText.value = ''
|
||||
outputText.value = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<!-- 输入部分 -->
|
||||
<div class="mb-4">
|
||||
<el-input
|
||||
v-model="inputText"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="请输入要处理的文本"
|
||||
class="mb-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 工作流步骤 -->
|
||||
<div class="mb-4">
|
||||
<el-card class="mb-4">
|
||||
<template #header>
|
||||
<div class="flex justify-between items-center">
|
||||
<span>工作流步骤</span>
|
||||
<el-dropdown>
|
||||
<el-button type="primary" size="small">
|
||||
添加步骤 <el-icon><ArrowDown /></el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
v-for="step in availableSteps"
|
||||
:key="step.type"
|
||||
@click="addStep(step.type, step.name)"
|
||||
>
|
||||
{{ step.name }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="workflowSteps.length === 0" class="text-center text-gray-500 py-4">
|
||||
暂无步骤,请添加处理步骤
|
||||
</div>
|
||||
|
||||
<div v-else class="space-y-2">
|
||||
<div
|
||||
v-for="(step, index) in workflowSteps"
|
||||
:key="step.id"
|
||||
class="flex items-center gap-2 p-2 border rounded"
|
||||
>
|
||||
<el-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click="moveStepUp(index)"
|
||||
:disabled="index === 0"
|
||||
>
|
||||
<el-icon><ArrowUp /></el-icon>
|
||||
</el-button>
|
||||
<el-button
|
||||
type="text"
|
||||
size="small"
|
||||
@click="moveStepDown(index)"
|
||||
:disabled="index === workflowSteps.length - 1"
|
||||
>
|
||||
<el-icon><ArrowDown /></el-icon>
|
||||
</el-button>
|
||||
<div class="flex-1">{{ step.name }}</div>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
@click="removeStep(step.id)"
|
||||
>
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="mb-4">
|
||||
<el-button type="primary" @click="executeWorkflow">执行工作流</el-button>
|
||||
<el-button @click="clearInput">清空</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 结果部分 -->
|
||||
<div>
|
||||
<el-input
|
||||
v-model="outputText"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="处理结果"
|
||||
readonly
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="copyResult">复制</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线文本处理工作流工具,允许用户定义一系列文本处理步骤并按顺序执行,可用于复杂的文本处理任务,提高工作效率。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</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({
|
||||
@@ -43,7 +44,7 @@ const copyRes = async () => {
|
||||
<el-input type="textarea" :rows="8" v-model="info.content"></el-input>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="mt-4 flex flex-wrap gap-2 button-container">
|
||||
<el-button type="primary" @click="toEncode">UrlEncode编码</el-button>
|
||||
<el-button type="primary" @click="toDecode">UrlDecode解码</el-button>
|
||||
<el-button type="primary" @click="copyRes">复制结果</el-button>
|
||||
@@ -54,8 +55,20 @@ 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>
|
||||
|
||||
<style scoped>
|
||||
.button-container .el-button {
|
||||
margin-right: 12px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,307 @@
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div class="tool-content">
|
||||
<div class="input-section">
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="转盘选项(每行一个)">
|
||||
<el-input
|
||||
v-model="optionsText"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
placeholder="请输入转盘选项,每行一个"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="updateWheel">更新转盘</el-button>
|
||||
<el-button @click="clearOptions">清空</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="wheel-section">
|
||||
<div class="wheel-wrapper">
|
||||
<div
|
||||
ref="wheel"
|
||||
class="wheel"
|
||||
:style="wheelStyle"
|
||||
>
|
||||
<div
|
||||
v-for="(option, index) in options"
|
||||
:key="index"
|
||||
class="wheel-sector"
|
||||
:style="getSectorStyle(index)"
|
||||
>
|
||||
<div class="sector-text">{{ option }}</div>
|
||||
</div>
|
||||
<div class="wheel-center"></div>
|
||||
</div>
|
||||
<div class="wheel-pointer"></div>
|
||||
</div>
|
||||
|
||||
<div class="wheel-controls">
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="isSpinning || options.length < 2"
|
||||
@click="spinWheel"
|
||||
>
|
||||
{{ isSpinning ? '旋转中...' : '开始旋转' }}
|
||||
</el-button>
|
||||
<div v-if="result" class="result">
|
||||
结果: {{ result }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线转盘工具,自定义选项后旋转转盘随机选择结果,支持多个选项和动画效果,可用于随机选择、抽奖等场景。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
|
||||
|
||||
const title = "转盘工具"
|
||||
|
||||
const optionsText = ref('');
|
||||
const options = ref<string[]>([]);
|
||||
const isSpinning = ref(false);
|
||||
const result = ref<string>('');
|
||||
// const wheel = ref<HTMLElement | null>(null);
|
||||
const rotation = ref(0);
|
||||
|
||||
// 颜色列表
|
||||
const colors = [
|
||||
'#ff6b6b', '#4ecdc4', '#45b7d1', '#96ceb4', '#feca57',
|
||||
'#ff9ff3', '#54a0ff', '#5f27cd', '#00d2d3', '#ff9f43'
|
||||
];
|
||||
|
||||
// 解析选项
|
||||
const parseOptions = () => {
|
||||
const newOptions = optionsText.value
|
||||
.split('\n')
|
||||
.map(option => option.trim())
|
||||
.filter(option => option !== '');
|
||||
options.value = newOptions;
|
||||
};
|
||||
|
||||
// 更新转盘
|
||||
const updateWheel = () => {
|
||||
parseOptions();
|
||||
result.value = '';
|
||||
};
|
||||
|
||||
// 清空选项
|
||||
const clearOptions = () => {
|
||||
optionsText.value = '';
|
||||
options.value = [];
|
||||
result.value = '';
|
||||
};
|
||||
|
||||
// 转盘样式
|
||||
const wheelStyle = computed(() => {
|
||||
return {
|
||||
transform: `rotate(${rotation.value}deg)`
|
||||
};
|
||||
});
|
||||
|
||||
// 获取扇形样式
|
||||
const getSectorStyle = (index: number) => {
|
||||
const count = options.value.length;
|
||||
if (count === 0) return {};
|
||||
|
||||
const angle = 360 / count;
|
||||
const startAngle = index * angle;
|
||||
const endAngle = (index + 1) * angle;
|
||||
|
||||
// 计算扇形路径
|
||||
const radius = 150;
|
||||
const centerX = radius;
|
||||
const centerY = radius;
|
||||
|
||||
const startX = centerX + radius * Math.cos((startAngle - 90) * Math.PI / 180);
|
||||
const startY = centerY + radius * Math.sin((startAngle - 90) * Math.PI / 180);
|
||||
const endX = centerX + radius * Math.cos((endAngle - 90) * Math.PI / 180);
|
||||
const endY = centerY + radius * Math.sin((endAngle - 90) * Math.PI / 180);
|
||||
|
||||
// const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1';
|
||||
|
||||
// const path = `M ${centerX} ${centerY} L ${startX} ${startY} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${endX} ${endY} Z`;
|
||||
|
||||
return {
|
||||
backgroundColor: colors[index % colors.length],
|
||||
clipPath: `polygon(50% 50%, ${startX}px ${startY}px, ${endX}px ${endY}px)`
|
||||
};
|
||||
};
|
||||
|
||||
// 旋转转盘
|
||||
const spinWheel = () => {
|
||||
if (isSpinning.value || options.value.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
isSpinning.value = true;
|
||||
result.value = '';
|
||||
|
||||
// 随机旋转角度(3-5圈)
|
||||
const randomRotation = 360 * (3 + Math.random() * 2);
|
||||
const finalRotation = rotation.value + randomRotation;
|
||||
|
||||
// 计算最终指向的选项
|
||||
const anglePerOption = 360 / options.value.length;
|
||||
const normalizedRotation = finalRotation % 360;
|
||||
const selectedIndex = Math.floor((360 - normalizedRotation) / anglePerOption) % options.value.length;
|
||||
|
||||
// 执行旋转动画
|
||||
const duration = 3000 + Math.random() * 2000; // 3-5秒
|
||||
const startTime = performance.now();
|
||||
|
||||
const animate = (currentTime: number) => {
|
||||
const elapsed = currentTime - startTime;
|
||||
const progress = Math.min(elapsed / duration, 1);
|
||||
|
||||
// 使用缓动函数
|
||||
const easeOut = 1 - Math.pow(1 - progress, 3);
|
||||
rotation.value = rotation.value + randomRotation * easeOut;
|
||||
|
||||
if (progress < 1) {
|
||||
requestAnimationFrame(animate);
|
||||
} else {
|
||||
// 旋转结束
|
||||
isSpinning.value = false;
|
||||
result.value = options.value[selectedIndex];
|
||||
}
|
||||
};
|
||||
|
||||
requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
// 初始示例
|
||||
optionsText.value = '选项1\n选项2\n选项3\n选项4\n选项5';
|
||||
parseOptions();
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tool-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.wheel-section {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.wheel-wrapper {
|
||||
position: relative;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.wheel {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
transition: transform 0.3s ease;
|
||||
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.wheel-sector {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.sector-text {
|
||||
max-width: 80px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.wheel-center {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background-color: white;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.wheel-pointer {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 15px solid transparent;
|
||||
border-right: 15px solid transparent;
|
||||
border-bottom: 25px solid #ff6b6b;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.wheel-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.result {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-top: 10px;
|
||||
padding: 10px 20px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.tool-content {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.wheel-section {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,282 @@
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div class="tool-content">
|
||||
<div class="input-section">
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="输入文本">
|
||||
<el-input
|
||||
v-model="inputText"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
placeholder="请输入要生成词云的文本"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="词云设置">
|
||||
<div class="settings-grid">
|
||||
<el-form-item label="形状" class="setting-item">
|
||||
<el-select v-model="shape" size="small">
|
||||
<el-option label="圆形" value="circle" />
|
||||
<el-option label="矩形" value="rect" />
|
||||
<el-option label="三角形" value="triangle" />
|
||||
<el-option label="心形" value="heart" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="颜色方案" class="setting-item">
|
||||
<el-select v-model="colorScheme" size="small">
|
||||
<el-option label="默认" value="default" />
|
||||
<el-option label="暖色" value="warm" />
|
||||
<el-option label="冷色" value="cool" />
|
||||
<el-option label="多彩" value="multi" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="字体大小范围" class="setting-item">
|
||||
<div class="size-range">
|
||||
<el-input-number v-model="minFontSize" :min="10" :max="50" size="small" />
|
||||
<span class="range-separator">-</span>
|
||||
<el-input-number v-model="maxFontSize" :min="20" :max="100" size="small" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="generateWordCloud">生成词云</el-button>
|
||||
<el-button @click="downloadWordCloud">下载图片</el-button>
|
||||
<el-button @click="clearText">清空</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<div class="result-section">
|
||||
<div ref="chartContainer" class="chart-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线词云生成工具,根据输入文本生成词云图,支持自定义词云形状、颜色方案和字体大小范围,可用于文本分析和可视化。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, nextTick } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import 'echarts-wordcloud';
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import ToolDetail from '@/components/Layout/ToolDetail/ToolDetail.vue'
|
||||
|
||||
const title = "词云生成"
|
||||
|
||||
const inputText = ref('');
|
||||
const shape = ref('circle');
|
||||
const colorScheme = ref('default');
|
||||
const minFontSize = ref(12);
|
||||
const maxFontSize = ref(60);
|
||||
const chartContainer = ref<HTMLElement | null>(null);
|
||||
let chart: echarts.ECharts | null = null;
|
||||
|
||||
// 颜色方案
|
||||
const colorSchemes = {
|
||||
default: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'],
|
||||
warm: ['#ff7f50', '#87ceeb', '#da70d6', '#32cd32', '#6495ed', '#ff69b4', '#ba55d3', '#cd5c5c', '#ffa500'],
|
||||
cool: ['#00bfff', '#9370db', '#3cb371', '#ff6347', '#4682b4', '#ff69b4', '#32cd32', '#ff4500', '#9400d3'],
|
||||
multi: ['#ff0000', '#ff7f00', '#ffff00', '#00ff00', '#00ffff', '#0000ff', '#8b00ff', '#ff00ff', '#1e90ff']
|
||||
};
|
||||
|
||||
// 词频统计函数
|
||||
const getWordFrequency = (text: string) => {
|
||||
// 简单的词频统计,实际应用中可能需要更复杂的分词
|
||||
const words = text
|
||||
.toLowerCase()
|
||||
.replace(/[.,?!;:()\[\]{}]/g, ' ')
|
||||
.split(/\s+/)
|
||||
.filter(word => word.length > 1);
|
||||
|
||||
const frequency: Record<string, number> = {};
|
||||
words.forEach(word => {
|
||||
frequency[word] = (frequency[word] || 0) + 1;
|
||||
});
|
||||
|
||||
return Object.entries(frequency)
|
||||
.map(([name, value]) => ({ name, value }))
|
||||
.sort((a, b) => b.value - a.value)
|
||||
.slice(0, 100); // 只取前100个高频词
|
||||
};
|
||||
|
||||
// 生成词云
|
||||
const generateWordCloud = () => {
|
||||
if (!inputText.value.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const words = getWordFrequency(inputText.value);
|
||||
if (words.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
if (chartContainer.value) {
|
||||
if (chart) {
|
||||
chart.dispose();
|
||||
}
|
||||
|
||||
chart = echarts.init(chartContainer.value);
|
||||
|
||||
const option = {
|
||||
tooltip: {},
|
||||
series: [
|
||||
{
|
||||
type: 'wordCloud',
|
||||
shape: shape.value,
|
||||
left: 'center',
|
||||
top: 'center',
|
||||
width: '80%',
|
||||
height: '80%',
|
||||
right: null,
|
||||
bottom: null,
|
||||
sizeRange: [minFontSize.value, maxFontSize.value],
|
||||
rotationRange: [-45, 45],
|
||||
rotationStep: 45,
|
||||
gridSize: 8,
|
||||
drawOutOfBound: false,
|
||||
textStyle: {
|
||||
fontFamily: 'sans-serif',
|
||||
fontWeight: 'bold',
|
||||
color: function () {
|
||||
return colorSchemes[colorScheme.value as keyof typeof colorSchemes][Math.floor(Math.random() * colorSchemes[colorScheme.value as keyof typeof colorSchemes].length)];
|
||||
}
|
||||
},
|
||||
emphasis: {
|
||||
focus: 'self',
|
||||
textStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowColor: '#333'
|
||||
}
|
||||
},
|
||||
data: words
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chart.setOption(option);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 清空文本
|
||||
const clearText = () => {
|
||||
inputText.value = '';
|
||||
if (chart) {
|
||||
chart.clear();
|
||||
}
|
||||
};
|
||||
|
||||
// 下载词云图片
|
||||
const downloadWordCloud = () => {
|
||||
if (!chart) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取图片数据 URL
|
||||
const dataURL = chart.getDataURL({
|
||||
type: 'png',
|
||||
pixelRatio: 2, // 提高图片质量
|
||||
backgroundColor: '#fff'
|
||||
});
|
||||
|
||||
// 创建下载链接
|
||||
const link = document.createElement('a');
|
||||
link.download = `wordcloud-${Date.now()}.png`;
|
||||
link.href = dataURL;
|
||||
link.click();
|
||||
} catch (error) {
|
||||
console.error('下载词云图片失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 响应式调整
|
||||
const handleResize = () => {
|
||||
if (chart) {
|
||||
chart.resize();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', handleResize);
|
||||
// 示例文本
|
||||
inputText.value = '词云 生成 工具 在线 文本 分析 可视化 数据 图表 形状 颜色 字体 大小 频率 统计 单词 词组 自定义 圆形 矩形 三角形 心形 暖色 冷色 多彩';
|
||||
generateWordCloud();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tool-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.settings-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 15px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.setting-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.size-range {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.range-separator {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.result-section {
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.tool-content {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.input-section {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.result-section {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -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>
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
<script setup lang="ts">
|
||||
import { 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 { copy } from '@/utils/string'
|
||||
|
||||
const title = "词频统计"
|
||||
|
||||
// 状态管理
|
||||
const inputText = ref('')
|
||||
const minWordLength = ref(2)
|
||||
const frequencyResult = ref([] as { word: string; count: number }[])
|
||||
|
||||
// 统计词频
|
||||
const calculateFrequency = () => {
|
||||
if (!inputText.value) {
|
||||
ElMessage.warning('请输入要统计词频的文本')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const frequency = countWordFrequency(inputText.value, minWordLength.value)
|
||||
frequencyResult.value = frequency
|
||||
ElMessage.success('统计完成')
|
||||
} catch (error) {
|
||||
ElMessage.error('统计失败:' + error)
|
||||
}
|
||||
}
|
||||
|
||||
// 计算词频
|
||||
const countWordFrequency = (text: string, minLength: number) => {
|
||||
// 分词
|
||||
const words = tokenize(text)
|
||||
|
||||
// 过滤长度
|
||||
const filteredWords = words.filter(word => word.length >= minLength)
|
||||
|
||||
// 统计频率
|
||||
const frequencyMap = new Map<string, number>()
|
||||
filteredWords.forEach(word => {
|
||||
frequencyMap.set(word, (frequencyMap.get(word) || 0) + 1)
|
||||
})
|
||||
|
||||
// 转换为数组并排序
|
||||
const frequencyArray = Array.from(frequencyMap.entries())
|
||||
.map(([word, count]) => ({ word, count }))
|
||||
.sort((a, b) => b.count - a.count)
|
||||
|
||||
return frequencyArray
|
||||
}
|
||||
|
||||
// 文本分词
|
||||
const tokenize = (text: string): string[] => {
|
||||
// 简单的分词实现,去除标点符号,转为小写
|
||||
return text
|
||||
.toLowerCase()
|
||||
.replace(/[.,?!;:"'()\[\]{}]/g, ' ')
|
||||
.split(/\s+/)
|
||||
.filter(word => word.length > 0)
|
||||
}
|
||||
|
||||
// 复制结果
|
||||
const copyResult = () => {
|
||||
if (frequencyResult.value.length === 0) {
|
||||
ElMessage.warning('没有可复制的内容')
|
||||
return
|
||||
}
|
||||
|
||||
const resultText = frequencyResult.value
|
||||
.map(item => `${item.word}: ${item.count}`)
|
||||
.join('\n')
|
||||
|
||||
copy(resultText)
|
||||
ElMessage.success('复制成功')
|
||||
}
|
||||
|
||||
// 清空输入
|
||||
const clearInput = () => {
|
||||
inputText.value = ''
|
||||
frequencyResult.value = []
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<!-- 输入部分 -->
|
||||
<div class="mb-4">
|
||||
<el-input
|
||||
v-model="inputText"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
placeholder="请输入要统计词频的文本"
|
||||
class="mb-2"
|
||||
/>
|
||||
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<el-text>最小单词长度:</el-text>
|
||||
<el-input-number v-model="minWordLength" :min="1" :max="10" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="mb-4">
|
||||
<el-button type="primary" @click="calculateFrequency">统计词频</el-button>
|
||||
<el-button @click="clearInput">清空</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 结果部分 -->
|
||||
<div v-if="frequencyResult.length > 0" class="mb-4">
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="font-bold">统计结果</div>
|
||||
<el-button size="small" @click="copyResult">复制结果</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-table :data="frequencyResult" style="width: 100%">
|
||||
<el-table-column prop="word" label="单词" width="200" />
|
||||
<el-table-column prop="count" label="出现次数" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线词频统计工具,用于统计文本中单词出现的频率,可用于文本分析、关键词提取等场景,支持设置最小单词长度。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,187 @@
|
||||
<script setup lang="ts">
|
||||
import { 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 { copy } from '@/utils/string'
|
||||
|
||||
const title = "XML格式化"
|
||||
|
||||
// 状态管理
|
||||
const inputText = ref('')
|
||||
const outputText = ref('')
|
||||
const indentSize = ref(4)
|
||||
|
||||
// 格式化XML
|
||||
const formatXml = () => {
|
||||
if (!inputText.value) {
|
||||
ElMessage.warning('请输入要格式化的XML文本')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用DOMParser解析XML
|
||||
const parser = new DOMParser()
|
||||
const xmlDoc = parser.parseFromString(inputText.value, 'text/xml')
|
||||
|
||||
// 检查解析是否成功
|
||||
const errorNode = xmlDoc.querySelector('parsererror')
|
||||
if (errorNode) {
|
||||
throw new Error('XML解析错误: ' + errorNode.textContent)
|
||||
}
|
||||
|
||||
// 格式化XML
|
||||
const formatted = formatXmlNode(xmlDoc.documentElement, 0)
|
||||
outputText.value = formatted
|
||||
ElMessage.success('格式化成功')
|
||||
} catch (error) {
|
||||
outputText.value = '格式化失败'
|
||||
ElMessage.error('格式化失败:' + error)
|
||||
}
|
||||
}
|
||||
|
||||
// 递归格式化XML节点
|
||||
const formatXmlNode = (node: Node, indent: number): string => {
|
||||
const indentStr = ' '.repeat(indent * indentSize.value)
|
||||
let result = ''
|
||||
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
result += `${indentStr}<${node.nodeName}`
|
||||
|
||||
// 处理属性
|
||||
if ((node as Element).attributes && (node as Element).attributes.length > 0) {
|
||||
for (let i = 0; i < (node as Element).attributes.length; i++) {
|
||||
const attr = (node as Element).attributes[i]
|
||||
result += ` ${attr.name}="${attr.value}"`
|
||||
}
|
||||
}
|
||||
|
||||
if (node.childNodes.length === 0) {
|
||||
// 空标签
|
||||
result += '/>\n'
|
||||
} else {
|
||||
result += '>\n'
|
||||
|
||||
// 处理子节点
|
||||
for (let i = 0; i < node.childNodes.length; i++) {
|
||||
const childNode = node.childNodes[i]
|
||||
if (childNode.nodeType === Node.TEXT_NODE && childNode.textContent?.trim() === '') {
|
||||
// 跳过空白文本节点
|
||||
continue
|
||||
}
|
||||
result += formatXmlNode(childNode, indent + 1)
|
||||
}
|
||||
|
||||
result += `${indentStr}</${node.nodeName}>\n`
|
||||
}
|
||||
} else if (node.nodeType === Node.TEXT_NODE) {
|
||||
const text = node.textContent?.trim()
|
||||
if (text) {
|
||||
result += `${indentStr}${text}\n`
|
||||
}
|
||||
} else if (node.nodeType === Node.CDATA_SECTION_NODE) {
|
||||
result += `${indentStr}<![CDATA[${node.textContent}]]>\n`
|
||||
} else if (node.nodeType === Node.COMMENT_NODE) {
|
||||
result += `${indentStr}<!--${node.textContent}-->\n`
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 压缩XML
|
||||
const minifyXml = () => {
|
||||
if (!inputText.value) {
|
||||
ElMessage.warning('请输入要压缩的XML文本')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 压缩XML(去除空格和换行)
|
||||
const minified = inputText.value
|
||||
.replace(/\s+/g, ' ') // 替换多个空白字符为单个空格
|
||||
.replace(/\s*<\s*/g, '<') // 去除标签周围的空格
|
||||
.replace(/\s*>\s*/g, '>') // 去除标签周围的空格
|
||||
.trim()
|
||||
|
||||
outputText.value = minified
|
||||
ElMessage.success('压缩成功')
|
||||
} catch (error) {
|
||||
outputText.value = '压缩失败'
|
||||
ElMessage.error('压缩失败:' + error)
|
||||
}
|
||||
}
|
||||
|
||||
// 复制结果
|
||||
const copyResult = () => {
|
||||
if (!outputText.value || outputText.value === '格式化失败' || outputText.value === '压缩失败') {
|
||||
ElMessage.warning('没有可复制的内容')
|
||||
return
|
||||
}
|
||||
|
||||
copy(outputText.value)
|
||||
}
|
||||
|
||||
// 清空输入
|
||||
const clearInput = () => {
|
||||
inputText.value = ''
|
||||
outputText.value = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<!-- 输入部分 -->
|
||||
<div class="mb-4">
|
||||
<el-input
|
||||
v-model="inputText"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
placeholder="请输入要格式化的XML文本"
|
||||
class="mb-2"
|
||||
/>
|
||||
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<el-text>缩进大小:</el-text>
|
||||
<el-input-number v-model="indentSize" :min="1" :max="8" size="small" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="mb-4">
|
||||
<el-button type="primary" @click="formatXml">格式化</el-button>
|
||||
<el-button type="success" @click="minifyXml">压缩</el-button>
|
||||
<el-button @click="clearInput">清空</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 结果部分 -->
|
||||
<div>
|
||||
<el-input
|
||||
v-model="outputText"
|
||||
type="textarea"
|
||||
:rows="6"
|
||||
placeholder="格式化结果"
|
||||
readonly
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="copyResult">复制</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线XML格式化工具,用于美化和压缩XML代码,支持自定义缩进大小,使XML代码更易于阅读和维护。
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,759 @@
|
||||
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 Token(jwt)',
|
||||
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: 1,
|
||||
title: 'Base64加解密',
|
||||
logo: '/images/logo/Base64.svg',
|
||||
desc: '在线Base64加解密工具,支持文本的Base64编码和解码',
|
||||
url: '/base64/',
|
||||
cateId: 2,
|
||||
cate: '开发运维',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '进制转换计算器',
|
||||
logo: '/images/logo/scaletran.png',
|
||||
desc: '在线进制转换计算器,支持二进制、八进制、十进制、十六进制等多种进制之间的相互转换',
|
||||
url: '/baseconverter/',
|
||||
cateId: 2,
|
||||
cate: '开发运维',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '数据存储单位换算',
|
||||
logo: '/images/logo/storageconverter.svg',
|
||||
desc: '在线数据存储单位换算工具,支持字节、KB、MB、GB、TB、PB、EB等存储单位之间的相互转换',
|
||||
url: '/storageconverter/',
|
||||
cateId: 2,
|
||||
cate: '开发运维',
|
||||
},
|
||||
|
||||
{
|
||||
id: 1,
|
||||
title: 'Hash计算器',
|
||||
logo: '/images/logo/hashcalculator.svg',
|
||||
desc: '在线Hash计算器,支持MD5、SHA-1、SHA-256、SHA-384、SHA-512等多种哈希算法',
|
||||
url: '/hashcalculator/',
|
||||
cateId: 2,
|
||||
cate: '开发运维',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: 'XML格式化',
|
||||
logo: '/images/logo/xmlformat.svg',
|
||||
desc: '在线XML格式化工具,用于美化和压缩XML代码,支持自定义缩进大小',
|
||||
url: '/xmlformat/',
|
||||
cateId: 2,
|
||||
cate: '开发运维',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: 'SQL格式化',
|
||||
logo: '/images/logo/sqlformat.svg',
|
||||
desc: '在线SQL格式化工具,用于美化和压缩SQL代码,支持自定义缩进大小',
|
||||
url: '/sqlformat/',
|
||||
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: 1,
|
||||
title: '文本替换',
|
||||
logo: '/images/logo/textreplace.svg',
|
||||
desc: '在线文本替换工具,支持普通文本和正则表达式替换,可用于批量修改文本内容',
|
||||
url: '/textreplace/',
|
||||
cateId: 3,
|
||||
cate: '文本处理',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '文本处理工作流',
|
||||
logo: '/images/logo/textworkflow.svg',
|
||||
desc: '在线文本处理工作流工具,允许用户定义一系列文本处理步骤并按顺序执行',
|
||||
url: '/textworkflow/',
|
||||
cateId: 3,
|
||||
cate: '文本处理',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '词频统计',
|
||||
logo: '/images/logo/wordfrequency.svg',
|
||||
desc: '在线词频统计工具,用于统计文本中单词出现的频率,可用于文本分析、关键词提取等场景',
|
||||
url: '/wordfrequency/',
|
||||
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: 1,
|
||||
title: '传图取色',
|
||||
logo: '/images/logo/imagecolorpicker.svg',
|
||||
desc: '上传图片并点击图片获取颜色值',
|
||||
url: '/imagecolorpicker/',
|
||||
cateId: 5,
|
||||
cate: '图片处理',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '图片水印添加',
|
||||
logo: '/images/logo/imagewatermark.svg',
|
||||
desc: '上传图片并添加文字水印',
|
||||
url: '/imagewatermark/',
|
||||
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: 1,
|
||||
title: '词云生成',
|
||||
logo: '/images/logo/wordcloud.svg',
|
||||
desc: '根据输入文本生成词云图,支持自定义词云形状和颜色',
|
||||
url: '/wordcloud/',
|
||||
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: 1,
|
||||
title: '抽签工具',
|
||||
logo: '/images/logo/lottery.svg',
|
||||
desc: '输入多个选项,随机抽取一个或多个结果',
|
||||
url: '/lottery/',
|
||||
cateId: 9,
|
||||
cate: '选择随机',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '转盘工具',
|
||||
logo: '/images/logo/wheel.svg',
|
||||
desc: '自定义选项,旋转转盘随机选择结果',
|
||||
url: '/wheel/',
|
||||
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: '/colorpalette/',
|
||||
cateId: 7,
|
||||
cate: '其他工具',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: 'Color选择器',
|
||||
logo: '/images/logo/color_picker.png',
|
||||
desc: '颜色选择器、在各种颜色空间如十六进制、rgb、hsl、css等等之间转换颜色',
|
||||
url: '/colorpicker/',
|
||||
cateId: 7,
|
||||
cate: '其他工具'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '番茄时钟',
|
||||
logo: '/images/logo/pomodoro.svg',
|
||||
desc: '专注工作和休息的时间管理工具',
|
||||
url: '/pomodoro/',
|
||||
cateId: 7,
|
||||
cate: '其他工具',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '反应速度测试',
|
||||
logo: '/images/logo/reactiontest.svg',
|
||||
desc: '测试你的反应速度,点击变色的方块',
|
||||
url: '/reactiontest/',
|
||||
cateId: 7,
|
||||
cate: '其他工具',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: 'm3u8在线播放',
|
||||
logo: '/images/logo/m3u8player.svg',
|
||||
desc: '播放m3u8格式的视频流',
|
||||
url: '/m3u8player/',
|
||||
cateId: 7,
|
||||
cate: '其他工具',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '剪刀石头布',
|
||||
logo: '/images/logo/rockpaperscissors.svg',
|
||||
desc: '与电脑对战的剪刀石头布游戏',
|
||||
url: '/rockpaperscissors/',
|
||||
cateId: 7,
|
||||
cate: '其他工具',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: 'Emoji表情大全',
|
||||
logo: '/images/logo/emoji.svg',
|
||||
desc: '在线Emoji表情大全,提供各种分类的Emoji表情,点击即可复制到剪贴板',
|
||||
url: '/emoji/',
|
||||
cateId: 7,
|
||||
cate: '其他工具',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '简易计算器',
|
||||
logo: '/images/logo/calculator.svg',
|
||||
desc: '基本的加减乘除计算工具',
|
||||
url: '/calculator/',
|
||||
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, route } = 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;
|
||||
});
|
||||
}
|
||||
//路由筛选
|
||||
if (route) {
|
||||
list = list.filter(item => {
|
||||
// 移除末尾的斜杠后比较
|
||||
const itemUrl = item.url.replace(/\/$/, '');
|
||||
const routeUrl = route.replace(/\/$/, '');
|
||||
return itemUrl === routeUrl;
|
||||
});
|
||||
}
|
||||
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 {
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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: "在线字数统计",
|
||||
@@ -71,19 +71,19 @@ export const constantRoute = [
|
||||
description: '在线字数计数器,主要是方便计算字数,以控制文字数量的小工具(小说作者、论文、文案文档、文字编辑必备工具)。',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/ip',
|
||||
component: () => import('@/components/Ip/Ip.vue'),
|
||||
name: 'ip',
|
||||
meta: {
|
||||
title: "IP查询",
|
||||
keywords: 'ip,ip查询,ip地址查询,ip138,查ip,我的ip,公网ip,ip归属地查询',
|
||||
description: '公网ip地址归属地查询',
|
||||
}
|
||||
},
|
||||
// {
|
||||
// path: '/ip',
|
||||
// component: () => import('@/components/Tools/Ip/Ip.vue'),
|
||||
// name: 'ip',
|
||||
// meta: {
|
||||
// title: "IP查询",
|
||||
// keywords: 'ip,ip查询,ip地址查询,ip138,查ip,我的ip,公网ip,ip归属地查询',
|
||||
// description: '公网ip地址归属地查询',
|
||||
// }
|
||||
// },
|
||||
{
|
||||
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: "手持弹幕",
|
||||
@@ -151,19 +151,9 @@ export const constantRoute = [
|
||||
description: '手持弹幕是一种新型的互动沟通工具,可以方便地为各种户外活动、演出嘉年华等活动增加趣味性和互动性。手持弹幕具有轻便、易携带、易操作等优点,可以让每个参与者都变成活动的一部分。同时,手持弹幕还可以通过预先编写的文本、表情等形式,表达参与者的情感和想法,实现沟通互动。在社交媒体时代,手持弹幕的使用也带来了更广泛的社交效应,增加了活动的互动性和传播度。无论是举办方还是参与者,手持弹幕都是一个非常有价值的互动工具。',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/palettes',
|
||||
component: () => import('@/components/Palettes/Palettes.vue'),
|
||||
name: 'palettes',
|
||||
meta: {
|
||||
title: "色板",
|
||||
keywords: '色板,颜色板',
|
||||
description: '在线复制颜色,好看的颜色组合色板',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/unit',
|
||||
component: () => import('@/components/Unit/Unit.vue'),
|
||||
component: () => import('@/components/Tools/Unit/Unit.vue'),
|
||||
name: 'unit',
|
||||
meta: {
|
||||
title: "单位转换",
|
||||
@@ -173,7 +163,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 +173,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 +183,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 +193,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 +202,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 +213,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 +223,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 +232,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 +242,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 +253,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 +263,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 +272,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 +282,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 +292,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 +302,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 +312,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 +322,317 @@ 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 Token(jwt)',
|
||||
}
|
||||
},
|
||||
{
|
||||
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: '/base64',
|
||||
component: () => import('@/components/Tools/Base64/Base64.vue'),
|
||||
name: 'Base64',
|
||||
meta: {
|
||||
title: "Base64加解密",
|
||||
keywords: 'Base64,base64加密,base64解密,base64编码,base64解码',
|
||||
description: '在线Base64加解密工具,支持文本的Base64编码和解码',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/baseconverter',
|
||||
component: () => import('@/components/Tools/BaseConverter/BaseConverter.vue'),
|
||||
name: 'baseconverter',
|
||||
meta: {
|
||||
title: "进制转换计算器",
|
||||
keywords: '进制转换,二进制,八进制,十进制,十六进制,进制计算器',
|
||||
description: '在线进制转换计算器,支持二进制、八进制、十进制、十六进制等多种进制之间的相互转换',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/storageconverter',
|
||||
component: () => import('@/components/Tools/StorageConverter/StorageConverter.vue'),
|
||||
name: 'storageconverter',
|
||||
meta: {
|
||||
title: "数据存储单位换算",
|
||||
keywords: '存储单位换算,字节,KB,MB,GB,TB,PB,EB',
|
||||
description: '在线数据存储单位换算工具,支持字节、KB、MB、GB、TB、PB、EB等存储单位之间的相互转换',
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
path: '/hashcalculator',
|
||||
component: () => import('@/components/Tools/HashCalculator/HashCalculator.vue'),
|
||||
name: 'hashcalculator',
|
||||
meta: {
|
||||
title: "Hash计算器",
|
||||
keywords: 'Hash计算器,MD5,SHA-1,SHA-256,SHA-384,SHA-512',
|
||||
description: '在线Hash计算器,支持MD5、SHA-1、SHA-256、SHA-384、SHA-512等多种哈希算法',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/xmlformat',
|
||||
component: () => import('@/components/Tools/XmlFormat/XmlFormat.vue'),
|
||||
name: 'xmlformat',
|
||||
meta: {
|
||||
title: "XML格式化",
|
||||
keywords: 'XML格式化,XML压缩,XML美化',
|
||||
description: '在线XML格式化工具,用于美化和压缩XML代码,支持自定义缩进大小',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/sqlformat',
|
||||
component: () => import('@/components/Tools/SqlFormat/SqlFormat.vue'),
|
||||
name: 'sqlformat',
|
||||
meta: {
|
||||
title: "SQL格式化",
|
||||
keywords: 'SQL格式化,SQL压缩,SQL美化',
|
||||
description: '在线SQL格式化工具,用于美化和压缩SQL代码,支持自定义缩进大小',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/textreplace',
|
||||
component: () => import('@/components/Tools/TextReplace/TextReplace.vue'),
|
||||
name: 'textreplace',
|
||||
meta: {
|
||||
title: "文本替换",
|
||||
keywords: '文本替换,正则表达式替换,批量替换',
|
||||
description: '在线文本替换工具,支持普通文本和正则表达式替换,可用于批量修改文本内容',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/textworkflow',
|
||||
component: () => import('@/components/Tools/TextWorkflow/TextWorkflow.vue'),
|
||||
name: 'textworkflow',
|
||||
meta: {
|
||||
title: "文本处理工作流",
|
||||
keywords: '文本处理,工作流,批量处理',
|
||||
description: '在线文本处理工作流工具,允许用户定义一系列文本处理步骤并按顺序执行',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/emoji',
|
||||
component: () => import('@/components/Tools/Emoji/Emoji.vue'),
|
||||
name: 'emoji',
|
||||
meta: {
|
||||
title: "Emoji表情大全",
|
||||
keywords: 'Emoji,表情符号,表情大全',
|
||||
description: '在线Emoji表情大全,提供各种分类的Emoji表情,点击即可复制到剪贴板',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/wordfrequency',
|
||||
component: () => import('@/components/Tools/WordFrequency/WordFrequency.vue'),
|
||||
name: 'wordfrequency',
|
||||
meta: {
|
||||
title: "词频统计",
|
||||
keywords: '词频统计,文本分析,关键词提取',
|
||||
description: '在线词频统计工具,用于统计文本中单词出现的频率,可用于文本分析、关键词提取等场景',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/wordcloud',
|
||||
component: () => import('@/components/Tools/WordCloud/WordCloud.vue'),
|
||||
name: 'wordcloud',
|
||||
meta: {
|
||||
title: "词云生成",
|
||||
keywords: '词云,文本可视化,词频分析',
|
||||
description: '根据输入文本生成词云图,支持自定义词云形状和颜色',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/lottery',
|
||||
component: () => import('@/components/Tools/Lottery/Lottery.vue'),
|
||||
name: 'lottery',
|
||||
meta: {
|
||||
title: "抽签工具",
|
||||
keywords: '抽签,随机选择,抽奖',
|
||||
description: '输入多个选项,随机抽取一个或多个结果',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/rockpaperscissors',
|
||||
component: () => import('@/components/Tools/RockPaperScissors/RockPaperScissors.vue'),
|
||||
name: 'rockpaperscissors',
|
||||
meta: {
|
||||
title: "剪刀石头布",
|
||||
keywords: '剪刀石头布,游戏,对战',
|
||||
description: '与电脑对战的剪刀石头布游戏',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/wheel',
|
||||
component: () => import('@/components/Tools/Wheel/Wheel.vue'),
|
||||
name: 'wheel',
|
||||
meta: {
|
||||
title: "转盘工具",
|
||||
keywords: '转盘,随机选择,抽奖',
|
||||
description: '自定义选项,旋转转盘随机选择结果',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/imagecolorpicker',
|
||||
component: () => import('@/components/Tools/ImageColorPicker/ImageColorPicker.vue'),
|
||||
name: 'imagecolorpicker',
|
||||
meta: {
|
||||
title: "传图取色",
|
||||
keywords: '图片取色,颜色提取,色彩分析',
|
||||
description: '上传图片并点击图片获取颜色值',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/pomodoro',
|
||||
component: () => import('@/components/Tools/Pomodoro/Pomodoro.vue'),
|
||||
name: 'pomodoro',
|
||||
meta: {
|
||||
title: "番茄时钟",
|
||||
keywords: '番茄工作法,时间管理,专注',
|
||||
description: '专注工作和休息的时间管理工具',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/calculator',
|
||||
component: () => import('@/components/Tools/Calculator/Calculator.vue'),
|
||||
name: 'calculator',
|
||||
meta: {
|
||||
title: "简易计算器",
|
||||
keywords: '计算器,加减乘除,数学计算',
|
||||
description: '基本的加减乘除计算工具',
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
path: '/reactiontest',
|
||||
component: () => import('@/components/Tools/ReactionTest/ReactionTest.vue'),
|
||||
name: 'reactiontest',
|
||||
meta: {
|
||||
title: "反应速度测试",
|
||||
keywords: '反应速度,测试,游戏',
|
||||
description: '测试你的反应速度,点击变色的方块',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/m3u8player',
|
||||
component: () => import('@/components/Tools/M3U8Player/M3U8Player.vue'),
|
||||
name: 'm3u8player',
|
||||
meta: {
|
||||
title: "m3u8在线播放",
|
||||
keywords: 'm3u8,视频播放,流媒体',
|
||||
description: '播放m3u8格式的视频流',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/imagewatermark',
|
||||
component: () => import('@/components/Tools/ImageWatermark/ImageWatermark.vue'),
|
||||
name: 'imagewatermark',
|
||||
meta: {
|
||||
title: "图片水印添加",
|
||||
keywords: '图片水印,水印添加,图片处理',
|
||||
description: '上传图片并添加文字水印',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/colorpalette',
|
||||
component: () => import('@/components/Tools/ColorPalette/ColorPalette.vue'),
|
||||
name: 'colorpalette',
|
||||
meta: {
|
||||
title: "色板工具",
|
||||
keywords: '色板,颜色,渐变色,Material Design',
|
||||
description: '提供各种颜色的色板,点击颜色可复制颜色值',
|
||||
}
|
||||
},
|
||||
// 关于
|
||||
{
|
||||
path: '/about',
|
||||
|
||||
@@ -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))
|
||||
// }
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -1,24 +1,25 @@
|
||||
//创建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'
|
||||
|
||||
// 收藏工具列表存储键名
|
||||
const COLLECTED_TOOLS_KEY = 'collected_tools';
|
||||
|
||||
export const useToolsStore = defineStore('tools', {
|
||||
//用来存放变量
|
||||
state: () => ({
|
||||
list: [] as ToolsInfo[],
|
||||
toolInfo: {} as ToolsInfo,
|
||||
cates: [] as ToolCate[],
|
||||
currentTool: {} as ToolsInfo,
|
||||
cates: [] as any[],
|
||||
recommends: [] as ToolsInfo[],
|
||||
ipData: {} as IpInfo,
|
||||
webInfo: {} as WebInfo,
|
||||
collect: [] as ToolsInfo[],
|
||||
collectIds: [] as number[],
|
||||
collectedTools: [] as ToolsInfo[],
|
||||
}),
|
||||
//方法
|
||||
actions: {
|
||||
@@ -26,44 +27,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.currentTool = result[0] || {}
|
||||
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 +52,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)
|
||||
@@ -116,5 +62,54 @@ export const useToolsStore = defineStore('tools', {
|
||||
return Promise.reject(new Error(result.message))
|
||||
}
|
||||
},
|
||||
// 加载收藏工具
|
||||
loadCollectedTools() {
|
||||
try {
|
||||
const stored = localStorage.getItem(COLLECTED_TOOLS_KEY);
|
||||
if (stored) {
|
||||
this.collectedTools = JSON.parse(stored);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载收藏工具失败:', error);
|
||||
this.collectedTools = [];
|
||||
}
|
||||
},
|
||||
// 保存收藏工具到本地存储
|
||||
saveCollectedTools() {
|
||||
try {
|
||||
localStorage.setItem(COLLECTED_TOOLS_KEY, JSON.stringify(this.collectedTools));
|
||||
} catch (error) {
|
||||
console.error('保存收藏工具失败:', error);
|
||||
}
|
||||
},
|
||||
// 添加收藏
|
||||
addCollect(tool: ToolsInfo) {
|
||||
// 检查是否已经收藏
|
||||
const isCollected = this.collectedTools.some(t => t.url === tool.url);
|
||||
if (!isCollected) {
|
||||
this.collectedTools.push(tool);
|
||||
this.saveCollectedTools();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
// 移除收藏
|
||||
removeCollect(url: string) {
|
||||
const index = this.collectedTools.findIndex(tool => tool.url === url);
|
||||
if (index > -1) {
|
||||
this.collectedTools.splice(index, 1);
|
||||
this.saveCollectedTools();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
// 检查是否已收藏
|
||||
isCollected(url: string) {
|
||||
const normalizedUrl = url.replace(/\/$/, '');
|
||||
return this.collectedTools.some(tool => {
|
||||
const toolUrl = tool.url.replace(/\/$/, '');
|
||||
return toolUrl === normalizedUrl;
|
||||
});
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -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;
|
||||
});
|
||||
//响应拦截器
|
||||
|
||||
@@ -1,10 +1,76 @@
|
||||
import {constantRoute} from "./src/router/router"
|
||||
import vitePluginSeoPrerender from "vite-plugin-seo-prerender";
|
||||
// import vitePluginSeoPrerender from "vite-plugin-seo-prerender";
|
||||
|
||||
|
||||
import seoPrerender from "vite-plugin-seo-prerender";
|
||||
|
||||
export function seoperender(){
|
||||
const filterPath = ['/:pathMatch(.*)*', '/404']; // 排除的路径
|
||||
return vitePluginSeoPrerender({
|
||||
routes: constantRoute.map(routeConfig => routeConfig.path).filter(path => !filterPath.includes(path)),
|
||||
network: true,
|
||||
return seoPrerender({
|
||||
routes: [
|
||||
'/',
|
||||
'/timetran',
|
||||
'/md5',
|
||||
'/json',
|
||||
'/reg',
|
||||
'/unicode',
|
||||
'/wordcount',
|
||||
'/scaletran',
|
||||
'/signimage',
|
||||
'/randompassword',
|
||||
'/urlencode',
|
||||
'/ascii',
|
||||
'/uuid',
|
||||
'/barrage',
|
||||
'/unit',
|
||||
'/qrcode',
|
||||
'/decision',
|
||||
'/morse',
|
||||
'/random',
|
||||
'/numbertochinese',
|
||||
'/diff',
|
||||
'/markdown',
|
||||
'/texttoimg',
|
||||
'/webinfo',
|
||||
'/bar',
|
||||
'/line',
|
||||
'/pie',
|
||||
'/scatter',
|
||||
'/coin',
|
||||
'/dice',
|
||||
'/textremoveduplicate',
|
||||
'/imgcut',
|
||||
'/httpstatuscode',
|
||||
'/jwt',
|
||||
'/htmlentity',
|
||||
'/colorpicker',
|
||||
'/asciiwordpic',
|
||||
'/jsforamt',
|
||||
'/htmlformat',
|
||||
'/cssformat',
|
||||
'/textedit',
|
||||
'/base64',
|
||||
'/baseconverter',
|
||||
'/storageconverter',
|
||||
'/hashcalculator',
|
||||
'/xmlformat',
|
||||
'/sqlformat',
|
||||
'/textreplace',
|
||||
'/textworkflow',
|
||||
'/emoji',
|
||||
'/wordfrequency',
|
||||
'/wordcloud',
|
||||
'/lottery',
|
||||
'/rockpaperscissors',
|
||||
'/wheel',
|
||||
'/imagecolorpicker',
|
||||
'/pomodoro',
|
||||
'/calculator',
|
||||
'/reactiontest',
|
||||
'/m3u8player',
|
||||
'/imagewatermark',
|
||||
'/colorpalette',
|
||||
'/about'
|
||||
],
|
||||
// network: true,
|
||||
// removeStyle: true
|
||||
})
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
|
||||
@@ -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: {
|
||||
|
||||