Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3106e47ad1 | ||
|
|
4b955d7e18 | ||
|
|
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 |
@@ -12,5 +12,7 @@ VITE_SERVE = ''
|
||||
VITE_HOST = '127.0.0.1'
|
||||
# 第三方反馈互动地址
|
||||
VITE_FEEDBACK_URL = ''
|
||||
# 广告位开关
|
||||
VITE_ADV_SHOW = 'false'
|
||||
# 广告位开关(废弃)
|
||||
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
|
||||
@@ -1,5 +1,5 @@
|
||||
# build stage
|
||||
FROM node:18-alpine3.19 AS build-stage
|
||||
FROM node:22-alpine3.22 AS build-stage
|
||||
# Set environment variables for non-interactive npm installs
|
||||
ENV NPM_CONFIG_LOGLEVEL warn
|
||||
WORKDIR /app
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
</pre>
|
||||
<p> 只需简单几步,即可快速搭建属于自己的在线工具箱。</p>
|
||||
|
||||
[](node)
|
||||
[](node)
|
||||
[](vue)
|
||||
[](tailwindcss)
|
||||
[](elementplus)
|
||||
[](LICENSE)
|
||||
[](LICENSE)
|
||||
|
||||
</div>
|
||||
|
||||
@@ -51,7 +51,7 @@ 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
|
||||
```
|
||||
|
||||
安装
|
||||
@@ -76,6 +76,11 @@ pnpm dev
|
||||
pnpm build
|
||||
```
|
||||
|
||||
打包seo静态页面:复制`.env.development`文件,并将文件名修改为`.env.production`,将里面的`NODE_ENV`的值改为`productio`,然后运行下面打包命令
|
||||
```
|
||||
pnpm build:pro
|
||||
```
|
||||
|
||||
## 工具列表
|
||||
|
||||
- 开发运维
|
||||
@@ -93,6 +98,12 @@ pnpm build
|
||||
- js代码格式化/压缩
|
||||
- Html代码格式化
|
||||
- Css代码格式化/压缩
|
||||
- Base64加解密
|
||||
- 进制转换计算器
|
||||
- 数据存储单位换算
|
||||
- Hash计算器
|
||||
- XML格式化
|
||||
- SQL格式化
|
||||
|
||||
- 文本处理
|
||||
- 文本对比
|
||||
@@ -101,6 +112,9 @@ pnpm build
|
||||
- 文本去重
|
||||
- 在线文本编辑/HTML获取
|
||||
- 字数统计
|
||||
- 文本替换
|
||||
- 文本处理工作流
|
||||
- 词频统计
|
||||
|
||||
- 教育学术
|
||||
- 单位换算
|
||||
@@ -121,24 +135,35 @@ pnpm build
|
||||
- 在线图片处理
|
||||
- 文本转图片
|
||||
- 图片分割
|
||||
- 传图取色
|
||||
- 图片水印添加
|
||||
|
||||
- 数据图表
|
||||
- 柱状图
|
||||
- 折线图
|
||||
- 饼图
|
||||
- 散点图
|
||||
- 词云生成
|
||||
|
||||
- 选择随机
|
||||
- 生成随机数
|
||||
- 帮我决定
|
||||
- 抛硬币
|
||||
- 投骰子
|
||||
- 抽签工具
|
||||
- 转盘工具
|
||||
|
||||
- 其他工具
|
||||
- 数字转金额大写
|
||||
- 手持弹幕
|
||||
- 色板
|
||||
- Color选择器
|
||||
- 番茄时钟
|
||||
- 反应速度测试
|
||||
- m3u8在线播放
|
||||
- 剪刀石头布
|
||||
- Emoji表情大全
|
||||
- 简易计算器
|
||||
|
||||
## 其他
|
||||
|
||||
@@ -147,7 +172,7 @@ Q: 我应该如何添加新功能?
|
||||
A:
|
||||
- 在`components/Tools/tools.ts`文件中添加工具信息
|
||||
- 在`router/router.ts`中添加路由
|
||||
- 拷贝示例目录`components/Tools/Example`修改名称,在这个拷贝出来的目录中开发工具即可
|
||||
- 目录`components/Tools`中添加新的工具组件
|
||||
|
||||
<br/>
|
||||
|
||||
|
||||
@@ -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,19 +14,9 @@
|
||||
<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 class="bg-[--base-gray]">
|
||||
<body>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "rbl-tools-web",
|
||||
"name": "tools-web",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
@@ -26,10 +26,13 @@
|
||||
"@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",
|
||||
"echarts-wordcloud": "^2.1.0",
|
||||
"element-plus": "^2.4.3",
|
||||
"figlet": "^1.7.0",
|
||||
"highlight.js": "^11.9.0",
|
||||
@@ -39,9 +42,10 @@
|
||||
"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",
|
||||
@@ -56,6 +60,7 @@
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@vitejs/plugin-vue": "^4.5.1",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"fast-glob": "^3.3.2",
|
||||
@@ -63,11 +68,11 @@
|
||||
"path": "^0.12.7",
|
||||
"pinia": "^2.1.7",
|
||||
"tailwindcss": "^3.3.5",
|
||||
"typescript": "^5.3.2",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^4.5.1",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vue-router": "^4.2.5",
|
||||
"vue-tsc": "^1.8.24"
|
||||
"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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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"
|
||||
@@ -27,7 +29,7 @@ const componentStore = useComponentStore()
|
||||
</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>
|
||||
|
||||
@@ -6,16 +6,16 @@
|
||||
<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/taoran1401/tools-web" target="_blank" type="primary">Tools-Web</el-link> 是一款免费开源的轻量在线工具箱,包含开发、文本、媒体、图表、生活、查询等实用工具;如果对您有帮助,请将其分享给您的朋友,并且添加到收藏夹中!顺便再点个⭐️吧!</el-text>
|
||||
<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/taoran1401/tools-web/blob/master/package.json" target="_blank" type="primary">package.json</el-link> 文件中找到完整的列表。</el-text>
|
||||
<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/taoran1401/tools-web/issues/new" class="">issues</el-link> 中提交错误报告。</el-text>
|
||||
<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>
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
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 { ElMessage } from 'element-plus'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useRoute } from "vue-router"
|
||||
//store
|
||||
const toolsStore = useToolsStore()
|
||||
const route = useRoute()
|
||||
// const getToolsCate = async () => {
|
||||
// try {
|
||||
// await toolsStore.getToolCate()
|
||||
// } catch (error: any) {
|
||||
// ElMessage.error(error.message)
|
||||
// }
|
||||
// }
|
||||
|
||||
// 取消收藏
|
||||
const removeCollect = (url: string) => {
|
||||
const success = toolsStore.removeCollect(url)
|
||||
if (success) {
|
||||
ElMessage.success('已取消收藏');
|
||||
} else {
|
||||
ElMessage.error('取消收藏失败');
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// getToolsCate()
|
||||
toolsStore.loadCollectedTools()
|
||||
if (route.query && route.query.value) {//底部导航跳转过来的则定位到响应位置
|
||||
document?.querySelector('#' + `${route.query.value}`)?.scrollIntoView();
|
||||
} else {//其他位置跳转过来不需要定位的则定位到顶部
|
||||
@@ -29,15 +31,54 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<div class="md:mr-6 c-xs:mr-0">
|
||||
<!-- list -->
|
||||
<!-- 收藏工具 -->
|
||||
<div id="collect">
|
||||
<!-- 收藏标题 -->
|
||||
<div class="mt-8 mb-3 text-xl font-bold text-[--base-black] tech-title">
|
||||
收藏工具
|
||||
</div>
|
||||
<!-- 收藏卡片 -->
|
||||
<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 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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between mt-2">
|
||||
<el-text line-clamp="2">{{ item.desc }}</el-text>
|
||||
</div>
|
||||
</router-link>
|
||||
<!-- 占位 div -->
|
||||
<div class="w-[24%] c-md:w-[24%] c-sm:w-[32%] "></div>
|
||||
</div>
|
||||
<div v-else class="mt-5 p-5 bg-white rounded-2xl">
|
||||
<el-empty description="暂无收藏工具" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 工具分类列表 -->
|
||||
<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">
|
||||
@@ -46,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>
|
||||
@@ -69,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,14 +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 { ElMessageBox } from 'element-plus'
|
||||
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({
|
||||
@@ -18,44 +15,136 @@ const searchParam = reactive({
|
||||
})
|
||||
//store
|
||||
const toolsStore = useToolsStore()
|
||||
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 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('取消收藏失败');
|
||||
}
|
||||
} else {
|
||||
// 添加收藏
|
||||
const success = toolsStore.addCollect(toolInfo)
|
||||
if (success) {
|
||||
isCollected.value = true
|
||||
ElMessage.success('收藏成功');
|
||||
} else {
|
||||
ElMessage.error('收藏失败,可能已经收藏过了');
|
||||
}
|
||||
}
|
||||
}
|
||||
//收藏
|
||||
// const collect = () => {
|
||||
// ElMessageBox({
|
||||
// title: '提示',
|
||||
// message: '请使用快捷键`Ctrl+D`进行收藏'
|
||||
// })
|
||||
// }
|
||||
|
||||
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
|
||||
: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,14 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
const appDesc = ref(import.meta.env.VITE_APP_DESC || '')
|
||||
const currentYear: number = new Date().getFullYear();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="w-full rounded-2xl z-10 p-5 text-center">
|
||||
<el-text>{{ appDesc }} © 2024 <a href="https://github.com/taoran1401/tools-web" target="_blank" class="text-blue-700">Tools-Web</a></el-text>
|
||||
<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>
|
||||
@@ -40,11 +40,10 @@ 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
|
||||
}
|
||||
@@ -75,25 +74,20 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<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">
|
||||
<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">
|
||||
<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="ml-3 mr-1">
|
||||
<router-link to="/">
|
||||
@@ -104,7 +98,7 @@ onMounted(() => {
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<div class="c-xs:w-[85%] w-full mr-3">
|
||||
<!-- <el-input
|
||||
v-model="searchParam.title"
|
||||
placeholder="搜索工具"
|
||||
@@ -129,7 +123,7 @@ onMounted(() => {
|
||||
size="large"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in toolsStore.list"
|
||||
v-for="item in options"
|
||||
:key="item.id"
|
||||
:label="item.title + ' - ' + item.desc"
|
||||
:value="item.id"
|
||||
@@ -143,7 +137,7 @@ 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">
|
||||
<!-- about -->
|
||||
<li class="ml-3">
|
||||
<li class="ml-3 c-xs:hidden">
|
||||
<router-link to="/about">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
@@ -163,13 +157,13 @@ onMounted(() => {
|
||||
content="GitHub仓库"
|
||||
placement="bottom"
|
||||
>
|
||||
<a href="https://github.com/taoran1401/tools-web" target="_blank">
|
||||
<a href="https://github.com/naroat/tools-web" target="_blank">
|
||||
<svg t="1715594665374" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4334" width="33" height="33"><path d="M512 85.333333C276.266667 85.333333 85.333333 276.266667 85.333333 512a426.410667 426.410667 0 0 0 291.754667 404.821333c21.333333 3.712 29.312-9.088 29.312-20.309333 0-10.112-0.554667-43.690667-0.554667-79.445333-107.178667 19.754667-134.912-26.112-143.445333-50.133334-4.821333-12.288-25.6-50.133333-43.733333-60.288-14.933333-7.978667-36.266667-27.733333-0.554667-28.245333 33.621333-0.554667 57.6 30.933333 65.621333 43.733333 38.4 64.512 99.754667 46.378667 124.245334 35.2 3.754667-27.733333 14.933333-46.378667 27.221333-57.045333-94.933333-10.666667-194.133333-47.488-194.133333-210.688 0-46.421333 16.512-84.778667 43.733333-114.688-4.266667-10.666667-19.2-54.4 4.266667-113.066667 0 0 35.712-11.178667 117.333333 43.776a395.946667 395.946667 0 0 1 106.666667-14.421333c36.266667 0 72.533333 4.778667 106.666666 14.378667 81.578667-55.466667 117.333333-43.690667 117.333334-43.690667 23.466667 58.666667 8.533333 102.4 4.266666 113.066667 27.178667 29.866667 43.733333 67.712 43.733334 114.645333 0 163.754667-99.712 200.021333-194.645334 210.688 15.445333 13.312 28.8 38.912 28.8 78.933333 0 57.045333-0.554667 102.912-0.554666 117.333334 0 11.178667 8.021333 24.490667 29.354666 20.224A427.349333 427.349333 0 0 0 938.666667 512c0-235.733333-190.933333-426.666667-426.666667-426.666667z" fill="#000000" p-id="4335"></path></svg>
|
||||
</a>
|
||||
</el-tooltip>
|
||||
</li>
|
||||
|
||||
<li class="hover:text-blue-500">
|
||||
<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>
|
||||
@@ -178,25 +172,64 @@ onMounted(() => {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.fold-enter-active {
|
||||
transition: all 1s ease-out;
|
||||
}
|
||||
|
||||
.fold-enter-from,
|
||||
.fold-leave-to {
|
||||
transform: translateX(20px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
:deep(.el-select__wrapper) {
|
||||
box-shadow: 0 0 0 0px var(--el-input-border-color, var(--el-border-color)) inset;
|
||||
cursor: default;
|
||||
@apply md:w-[40rem] c-xs:w-60;
|
||||
}
|
||||
|
||||
/* 科技感搜索框 */
|
||||
.el-select :deep(.el-select__wrapper){
|
||||
background-color: rgba(46, 51, 56, 0.05);
|
||||
background-color: rgb(255, 255, 255);
|
||||
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);
|
||||
}
|
||||
|
||||
/* 搜索框容器 */
|
||||
.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>
|
||||
@@ -8,6 +8,7 @@ const route = useRoute()
|
||||
|
||||
const appName = ref(import.meta.env.VITE_APP_TITLE || '工具坊')
|
||||
const appNet = ref(import.meta.env.VITE_APP_DESC || '')
|
||||
const leftAdv = ref(import.meta.env.VITE_LEFT_ADV || '')
|
||||
//菜单选中
|
||||
const defaultActive = ref('')
|
||||
//默认展开的菜单
|
||||
@@ -50,7 +51,7 @@ const gotoAnchor = (anchor: string) => {
|
||||
}
|
||||
}
|
||||
const gotoAbout = () => {
|
||||
router.push('about')
|
||||
router.push('/about')
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -64,7 +65,7 @@ onMounted(async () => {
|
||||
<el-scrollbar>
|
||||
<!-- logo -->
|
||||
<div class="flex justify-center">
|
||||
<router-link class="flex flex-row h-24" 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>
|
||||
@@ -112,6 +113,9 @@ onMounted(async () => {
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
|
||||
<!-- 广告位 -->
|
||||
<div class="ad-container" v-if="leftAdv != ''" v-html="leftAdv"></div>
|
||||
</el-scrollbar>
|
||||
<!-- </div> -->
|
||||
</template>
|
||||
@@ -122,17 +126,17 @@ onMounted(async () => {
|
||||
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 */
|
||||
@@ -140,12 +144,17 @@ onMounted(async () => {
|
||||
/* 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{
|
||||
@@ -154,15 +163,77 @@ onMounted(async () => {
|
||||
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>
|
||||
@@ -13,8 +13,8 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<!-- desc -->
|
||||
<div class="mt-3 rounded-2xl bg-white p-4">
|
||||
<el-divider content-position="left">{{ props.title }}</el-divider>
|
||||
<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>
|
||||
@@ -22,5 +22,32 @@ onMounted(() => {
|
||||
</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>
|
||||
@@ -113,7 +113,7 @@ const gen = () => {
|
||||
console.log(';----', err)
|
||||
return;
|
||||
}
|
||||
contentRes.value = data
|
||||
contentRes.value = data ?? ''
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -87,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">
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
@@ -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,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>
|
||||
@@ -3,6 +3,7 @@ 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';
|
||||
|
||||
@@ -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,90 +0,0 @@
|
||||
<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: "色板",
|
||||
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>
|
||||
|
||||
|
||||
<!-- desc -->
|
||||
<ToolDetail title="描述">
|
||||
<el-text>
|
||||
在线复制颜色,好看的颜色组合色板
|
||||
</el-text>
|
||||
</ToolDetail>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.color-palette:hover .color-text{
|
||||
display: flex;
|
||||
filter: grayscale(1) contrast(999) invert(1)
|
||||
}
|
||||
</style>
|
||||
@@ -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>
|
||||
@@ -63,7 +63,7 @@ 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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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,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>
|
||||
@@ -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>
|
||||
@@ -44,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>
|
||||
@@ -67,4 +67,8 @@ const copyRes = async () => {
|
||||
</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>
|
||||
@@ -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>
|
||||
@@ -13,7 +13,7 @@ export function getToolsCate() {
|
||||
title: '随机密码生成',
|
||||
logo: '/images/logo/keywords.png',
|
||||
desc: '密码生成器、随机字符串生成,批量生成',
|
||||
url: '/random_password/',
|
||||
url: '/randompassword/',
|
||||
cateId: 2,
|
||||
cate: '开发运维',
|
||||
},
|
||||
@@ -49,7 +49,7 @@ export function getToolsCate() {
|
||||
title: 'MD5在线加密',
|
||||
logo: '/images/logo/md5.png',
|
||||
desc: 'MD5在线加密,长度包含32位、16位',
|
||||
url: '/MD5/',
|
||||
url: '/md5/',
|
||||
cateId: 2,
|
||||
cate: '开发运维',
|
||||
},
|
||||
@@ -85,7 +85,7 @@ export function getToolsCate() {
|
||||
title: 'HTTP状态码',
|
||||
logo: '/images/logo/http_code.png',
|
||||
desc: 'http状态对应的名称和含义解释',
|
||||
url: '/HttpStatusCode/',
|
||||
url: '/httpstatuscode/',
|
||||
cateId: 2,
|
||||
cate: '开发运维',
|
||||
},
|
||||
@@ -103,7 +103,7 @@ export function getToolsCate() {
|
||||
title: 'html实体转义',
|
||||
logo: '/images/logo/HtmlEntity.png',
|
||||
desc: 'html实体转义,实体转义成html',
|
||||
url: '/HtmlEntity/',
|
||||
url: '/htmlentity/',
|
||||
cateId: 2,
|
||||
cate: '开发运维',
|
||||
},
|
||||
@@ -112,7 +112,7 @@ export function getToolsCate() {
|
||||
title: 'js代码格式化/压缩',
|
||||
logo: '/images/logo/JSForamt.png',
|
||||
desc: 'JS格式化/压缩工具,提供在线JS格式化、JS压缩、JS混淆、JS解密',
|
||||
url: '/JSForamt/',
|
||||
url: '/jsforamt/',
|
||||
cateId: 2,
|
||||
cate: '开发运维',
|
||||
},
|
||||
@@ -121,7 +121,7 @@ export function getToolsCate() {
|
||||
title: 'Html代码格式化',
|
||||
logo: '/images/logo/HtmlFormat.png',
|
||||
desc: '提供在线html、xml格式化',
|
||||
url: '/HtmlFormat/',
|
||||
url: '/htmlformat/',
|
||||
cateId: 2,
|
||||
cate: '开发运维',
|
||||
},
|
||||
@@ -130,9 +130,64 @@ export function getToolsCate() {
|
||||
title: 'Css代码格式化/压缩',
|
||||
logo: '/images/logo/CssFormat.png',
|
||||
desc: 'css格式化/压缩工具,提供在线css格式化、css压缩',
|
||||
url: '/CssFormat/',
|
||||
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: '开发运维'
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -164,7 +219,7 @@ export function getToolsCate() {
|
||||
title: '字数统计',
|
||||
logo: '/images/logo/wordCount.png',
|
||||
desc: '在线统计字符串的字数、段落、标点符号数量',
|
||||
url: '/wordCount/',
|
||||
url: '/wordcount/',
|
||||
cateId: 3,
|
||||
cate: '文本处理',
|
||||
},
|
||||
@@ -173,7 +228,7 @@ export function getToolsCate() {
|
||||
title: '文本去重',
|
||||
logo: '/images/logo/textRemoveDuplicate.png',
|
||||
desc: '可以删除或去除文本或字符串中的重复行',
|
||||
url: '/textRemoveDuplicate/',
|
||||
url: '/textremoveduplicate/',
|
||||
cateId: 3,
|
||||
cate: '文本处理',
|
||||
},
|
||||
@@ -182,7 +237,7 @@ export function getToolsCate() {
|
||||
title: 'ASCII字形生成器',
|
||||
logo: '/images/logo/ascii_word_pic.png',
|
||||
desc: '在线生成字形ASCII画',
|
||||
url: '/asciiWordPic/',
|
||||
url: '/asciiwordpic/',
|
||||
cateId: 3,
|
||||
cate: '文本处理',
|
||||
},
|
||||
@@ -191,10 +246,37 @@ export function getToolsCate() {
|
||||
title: '在线文本编辑/HTML获取',
|
||||
logo: '/images/logo/richtextEditor.png',
|
||||
desc: '在线富文本编辑, html实时预览,在线编辑文本,文本编辑获取html',
|
||||
url: '/textEdit/',
|
||||
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: '文本处理',
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -340,7 +422,7 @@ export function getToolsCate() {
|
||||
title: '文本转图片',
|
||||
logo: '/images/logo/text_to_img.png',
|
||||
desc: '把文本转换成图片,生成长图,具有超多个性文字排版',
|
||||
url: '/textToImg/',
|
||||
url: '/texttoimg/',
|
||||
cateId: 5,
|
||||
cate: '图片处理'
|
||||
},
|
||||
@@ -349,10 +431,28 @@ export function getToolsCate() {
|
||||
title: '图片分割',
|
||||
logo: '/images/logo/imgCut.png',
|
||||
desc: '将图片分割成四宫格、九宫格、十六宫格,支持自定义行与列',
|
||||
url: '/imgCut/',
|
||||
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: '图片处理',
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -394,7 +494,16 @@ export function getToolsCate() {
|
||||
url: '/scatter/',
|
||||
cateId: 8,
|
||||
cate: '数据图表',
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '词云生成',
|
||||
logo: '/images/logo/wordcloud.svg',
|
||||
desc: '根据输入文本生成词云图,支持自定义词云形状和颜色',
|
||||
url: '/wordcloud/',
|
||||
cateId: 8,
|
||||
cate: '数据图表',
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -438,6 +547,24 @@ export function getToolsCate() {
|
||||
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: '选择随机',
|
||||
},
|
||||
]
|
||||
},
|
||||
// {
|
||||
@@ -475,7 +602,7 @@ export function getToolsCate() {
|
||||
title: '数字转金额大写',
|
||||
logo: '/images/logo/numberToChinese.png',
|
||||
desc: '在线数字一键转换成人民币大写,中文大写转换数字',
|
||||
url: '/numberToChinese/',
|
||||
url: '/numbertochinese/',
|
||||
cateId: 7,
|
||||
cate: '其他工具'
|
||||
},
|
||||
@@ -492,20 +619,74 @@ export function getToolsCate() {
|
||||
id: 1,
|
||||
title: '色板',
|
||||
logo: '/images/logo/palettes.png',
|
||||
desc: '包含纯色、渐变与阶梯色和常用色彩组合',
|
||||
url: '/palettes/',
|
||||
desc: '提供各种颜色的色板,点击颜色可复制颜色值',
|
||||
url: '/colorpalette/',
|
||||
cateId: 7,
|
||||
cate: '其他工具'
|
||||
cate: '其他工具',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: 'Color选择器',
|
||||
logo: '/images/logo/color_picker.png',
|
||||
desc: '颜色选择器、在各种颜色空间如十六进制、rgb、hsl、css等等之间转换颜色',
|
||||
url: '/ColorPicker/',
|
||||
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: '其他工具',
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -539,16 +720,15 @@ export function urlKeyMap() {
|
||||
//获取工具
|
||||
export function getTools(data: ToolsReqData) {
|
||||
//接收参数
|
||||
const { cateId, title } = data
|
||||
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()))
|
||||
// console.log(tmpValue.indexOf(title.toLowerCase()))
|
||||
return tmpValue.indexOf(title.toLowerCase()) !== -1 || tmpDesc.indexOf(title.toLowerCase()) !== -1;
|
||||
});
|
||||
}
|
||||
@@ -558,6 +738,15 @@ export function getTools(data: ToolsReqData) {
|
||||
return item.cateId == cateId;
|
||||
});
|
||||
}
|
||||
//路由筛选
|
||||
if (route) {
|
||||
list = list.filter(item => {
|
||||
// 移除末尾的斜杠后比较
|
||||
const itemUrl = item.url.replace(/\/$/, '');
|
||||
const routeUrl = route.replace(/\/$/, '');
|
||||
return itemUrl === routeUrl;
|
||||
});
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,7 +22,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/MD5',
|
||||
path: '/md5',
|
||||
component: () => import('@/components/Tools/MD5/MD5.vue'),
|
||||
name: 'MD5',
|
||||
meta: {
|
||||
@@ -71,16 +71,16 @@ export const constantRoute = [
|
||||
description: '在线字数计数器,主要是方便计算字数,以控制文字数量的小工具(小说作者、论文、文案文档、文字编辑必备工具)。',
|
||||
}
|
||||
},
|
||||
{
|
||||
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: '/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/Tools/ScaleTran/ScaleTran.vue'),
|
||||
@@ -102,7 +102,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/random_password',
|
||||
path: '/randompassword',
|
||||
component: () => import('@/components/Tools/RandomPassword/RandomPassword.vue'),
|
||||
name: 'random_password',
|
||||
meta: {
|
||||
@@ -151,16 +151,6 @@ export const constantRoute = [
|
||||
description: '手持弹幕是一种新型的互动沟通工具,可以方便地为各种户外活动、演出嘉年华等活动增加趣味性和互动性。手持弹幕具有轻便、易携带、易操作等优点,可以让每个参与者都变成活动的一部分。同时,手持弹幕还可以通过预先编写的文本、表情等形式,表达参与者的情感和想法,实现沟通互动。在社交媒体时代,手持弹幕的使用也带来了更广泛的社交效应,增加了活动的互动性和传播度。无论是举办方还是参与者,手持弹幕都是一个非常有价值的互动工具。',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/palettes',
|
||||
component: () => import('@/components/Tools/Palettes/Palettes.vue'),
|
||||
name: 'palettes',
|
||||
meta: {
|
||||
title: "色板",
|
||||
keywords: '色板,颜色板',
|
||||
description: '在线复制颜色,好看的颜色组合色板',
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/unit',
|
||||
component: () => import('@/components/Tools/Unit/Unit.vue'),
|
||||
@@ -212,7 +202,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/numberToChinese',
|
||||
path: '/numbertochinese',
|
||||
component: () => import('@/components/Tools/NumberToChinese/NumberToChinese.vue'),
|
||||
name: 'numberToChinese',
|
||||
meta: {
|
||||
@@ -242,7 +232,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/textToImg',
|
||||
path: '/texttoimg',
|
||||
component: () => import('@/components/Tools/TextToImg/TextToImg.vue'),
|
||||
name: 'textToImg',
|
||||
meta: {
|
||||
@@ -252,7 +242,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/webInfo',
|
||||
path: '/webinfo',
|
||||
component: () => import('@/components/Tools/WebInfo/WebInfo.vue'),
|
||||
name: 'webInfo',
|
||||
meta: {
|
||||
@@ -282,7 +272,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/Pie',
|
||||
path: '/pie',
|
||||
component: () => import('@/components/Tools/Chart/Pie/Pie.vue'),
|
||||
name: 'pie',
|
||||
meta: {
|
||||
@@ -292,7 +282,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/Scatter',
|
||||
path: '/scatter',
|
||||
component: () => import('@/components/Tools/Chart/Scatter/Scatter.vue'),
|
||||
name: 'scatter',
|
||||
meta: {
|
||||
@@ -302,7 +292,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/Coin',
|
||||
path: '/coin',
|
||||
component: () => import('@/components/Tools/Coin/Coin.vue'),
|
||||
name: 'coin',
|
||||
meta: {
|
||||
@@ -312,7 +302,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/Dice',
|
||||
path: '/dice',
|
||||
component: () => import('@/components/Tools/Dice/Dice.vue'),
|
||||
name: 'dice',
|
||||
meta: {
|
||||
@@ -322,7 +312,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/TextRemoveDuplicate',
|
||||
path: '/textremoveduplicate',
|
||||
component: () => import('@/components/Tools/TextRemoveDuplicate/TextRemoveDuplicate.vue'),
|
||||
name: 'textRemoveDuplicate',
|
||||
meta: {
|
||||
@@ -332,7 +322,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/ImgCut',
|
||||
path: '/imgcut',
|
||||
component: () => import('@/components/Tools/ImgCut/ImgCut.vue'),
|
||||
name: 'imgCut',
|
||||
meta: {
|
||||
@@ -342,7 +332,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/HttpStatusCode',
|
||||
path: '/httpstatuscode',
|
||||
component: () => import('@/components/Tools/HttpStatusCode/HttpStatusCode.vue'),
|
||||
name: 'HttpStatusCode',
|
||||
meta: {
|
||||
@@ -362,7 +352,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/HtmlEntity',
|
||||
path: '/htmlentity',
|
||||
component: () => import('@/components/Tools/HtmlEntity/HtmlEntity.vue'),
|
||||
name: 'HtmlEntity',
|
||||
meta: {
|
||||
@@ -372,7 +362,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/ColorPicker',
|
||||
path: '/colorpicker',
|
||||
component: () => import('@/components/Tools/ColorPicker/ColorPicker.vue'),
|
||||
name: 'ColorPicker',
|
||||
meta: {
|
||||
@@ -382,7 +372,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/asciiWordPic',
|
||||
path: '/asciiwordpic',
|
||||
component: () => import('@/components/Tools/ASCIIWordPic/ASCIIWordPic.vue'),
|
||||
name: 'asciiWordPic',
|
||||
meta: {
|
||||
@@ -392,7 +382,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/JSForamt',
|
||||
path: '/jsforamt',
|
||||
component: () => import('@/components/Tools/JSFormat/JSFormat.vue'),
|
||||
name: 'JSForamt',
|
||||
meta: {
|
||||
@@ -402,7 +392,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/HtmlFormat',
|
||||
path: '/htmlformat',
|
||||
component: () => import('@/components/Tools/HtmlFormat/HtmlFormat.vue'),
|
||||
name: 'HtmlFormat',
|
||||
meta: {
|
||||
@@ -412,7 +402,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/CssFormat',
|
||||
path: '/cssformat',
|
||||
component: () => import('@/components/Tools/CssFormat/CssFormat.vue'),
|
||||
name: 'CssFormat',
|
||||
meta: {
|
||||
@@ -422,7 +412,7 @@ export const constantRoute = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/TextEdit',
|
||||
path: '/textedit',
|
||||
component: () => import('@/components/Tools/TextEdit/TextEdit.vue'),
|
||||
name: 'TextEdit',
|
||||
meta: {
|
||||
@@ -431,6 +421,218 @@ export const constantRoute = [
|
||||
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',
|
||||
|
||||
@@ -7,17 +7,19 @@ import type { IpReqData, IpInfo } from '@/api/ip/type'
|
||||
import type { WebInfo, WebInfoReqData } from '@/api/webinfo/type'
|
||||
import { fetchWebInfo } from '@/api/webinfo'
|
||||
|
||||
// 收藏工具列表存储键名
|
||||
const COLLECTED_TOOLS_KEY = 'collected_tools';
|
||||
|
||||
export const useToolsStore = defineStore('tools', {
|
||||
//用来存放变量
|
||||
state: () => ({
|
||||
list: [] as ToolsInfo[],
|
||||
toolInfo: {} as ToolsInfo,
|
||||
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: {
|
||||
@@ -32,7 +34,7 @@ export const useToolsStore = defineStore('tools', {
|
||||
async getToolInfo(data: ToolsReqData) {
|
||||
//发送请求
|
||||
const result: any = await getTools(data)
|
||||
this.toolInfo = result
|
||||
this.currentTool = result[0] || {}
|
||||
return result
|
||||
},
|
||||
//获取tools cate
|
||||
@@ -60,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,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",
|
||||
|
||||